aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/src/Godosa.Desktop/PadNavigator.cs
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
committergodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
commit39066900773e7857faf2d02e7ed51b71d219d97d (patch)
treeffab8e4ddd971626776c3b1f4ce1da2c186c2187 /src/Godosa.Desktop/PadNavigator.cs
downloadgodosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.tar.gz
godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.zip
godosa-engine: initial public history
Diffstat (limited to 'src/Godosa.Desktop/PadNavigator.cs')
-rw-r--r--src/Godosa.Desktop/PadNavigator.cs85
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
+ }
+ }
+}