diff options
Diffstat (limited to 'src/Godosa.Desktop/PadNavigator.cs')
| -rw-r--r-- | src/Godosa.Desktop/PadNavigator.cs | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/Godosa.Desktop/PadNavigator.cs b/src/Godosa.Desktop/PadNavigator.cs new file mode 100644 index 0000000..b4b59d5 --- /dev/null +++ b/src/Godosa.Desktop/PadNavigator.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.VisualTree; +using Avalonia.Media; +using Godosa.Core.Input; + +namespace Godosa.Desktop; + +/// <summary>Gamepad control of an open window: D-pad moves a highlight through its enabled +/// buttons (wrapping), A presses (the game decides B/Start).</summary> +public sealed class PadNavigator +{ + private static readonly Thickness HighlightThickness = new(2); + private readonly MenuCursor _cursor = new(); + private readonly List<Button> _buttons = new(); + private Control? _container; + private Button? _highlighted; + private readonly IBrush _highlight; + + public PadNavigator(IBrush highlight) => _highlight = highlight; + + /// <summary>The window pad input should drive now, or null (pad drives the character).</summary> + public Control? Active { get; private set; } + + /// <summary>Highlight only while the gamepad is the device in use (keyboard/mouse users + /// get no stray gold border).</summary> + public bool ShowHighlight { get; set; } + + public void Refresh(Control? active) + { + Active = active; + if (!ReferenceEquals(active, _container)) + { + _container = active; + _cursor.Clamp(0); + } + Collect(); + _cursor.Clamp(_buttons.Count); + Highlight(); + } + + public void Move(int delta) + { + Collect(); + _cursor.Clamp(_buttons.Count); + _cursor.Move(delta, _buttons.Count); + Highlight(); + } + + public void Press() + { + Collect(); + _cursor.Clamp(_buttons.Count); + if (_cursor.Index >= 0) Buttons.Press(_buttons[_cursor.Index]); + } + + private void Collect() + { + _buttons.Clear(); + if (_container is null) return; + _buttons.AddRange(_container.GetVisualDescendants().OfType<Button>() + .Where(b => b.IsEffectivelyVisible && b.IsEffectivelyEnabled)); + } + + private void Highlight() + { + var target = ShowHighlight && _cursor.Index >= 0 && _cursor.Index < _buttons.Count ? _buttons[_cursor.Index] : null; + if (ReferenceEquals(target, _highlighted)) return; + if (_highlighted is not null) + { + _highlighted.ClearValue(Button.BorderBrushProperty); + _highlighted.ClearValue(Button.BorderThicknessProperty); + } + _highlighted = target; + if (target is not null) + { + target.BorderBrush = _highlight; + target.BorderThickness = HighlightThickness; + target.BringIntoView(); // long option pages scroll + } + } +} |
