From 97d4c0a0d8d0bb0db6a3760531277a5d72caea3c Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 9 Dec 2024 22:52:45 -0800 Subject: [PATCH] Refactored RadioGroup to just use Commands --- Terminal.Gui/Views/RadioGroup.cs | 343 ++++++++++++---------------- UICatalog/Scenarios/MessageBoxes.cs | 4 +- 2 files changed, 154 insertions(+), 193 deletions(-) diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 623ccc4b8..9a1e8af8e 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -16,162 +16,22 @@ public class RadioGroup : View, IDesignable, IOrientation Width = Dim.Auto (DimAutoStyle.Content); Height = Dim.Auto (DimAutoStyle.Content); - // Select (Space key or mouse click) - The default implementation sets focus. RadioGroup does not. - AddCommand ( - Command.Select, - (ctx) => - { - bool cursorChanged = false; - if (SelectedItem == Cursor) - { - cursorChanged = MoveDownRight (); - if (!cursorChanged) - { - cursorChanged = MoveHome (); - } - } + AddCommand (Command.Select, HandleSelectCommand); - bool selectedItemChanged = false; - if (SelectedItem != Cursor) - { - selectedItemChanged = ChangeSelectedItem (Cursor); - } - - if (cursorChanged || selectedItemChanged) - { - if (RaiseSelecting (ctx) == true) - { - return true; - } - } - - return cursorChanged || selectedItemChanged; - }); - - // Accept (Enter key) - Raise Accept event - DO NOT advance state - AddCommand (Command.Accept, RaiseAccepting); + // Accept (Enter key or Doubleclick) - Raise Accept event - DO NOT advance state + AddCommand (Command.Accept, HandleAcceptCommand); // Hotkey - ctx may indicate a radio item hotkey was pressed. Behavior depends on HasFocus // If HasFocus and it's this.HotKey invoke Select command - DO NOT raise Accept // If it's a radio item HotKey select that item and raise Selected event - DO NOT raise Accept // If nothing is selected, select first and raise Selected event - DO NOT raise Accept - AddCommand (Command.HotKey, - ctx => - { - // If the command did not come from a keyboard event, ignore it - if (ctx is not CommandContext keyCommandContext) - { - return false; - } + AddCommand (Command.HotKey, HandleHotKeyCommand); - var item = keyCommandContext.Binding.Data as int?; - - if (HasFocus) - { - if (keyCommandContext is { Binding : { } } && (item is null || HotKey == keyCommandContext.Binding.Key?.NoAlt.NoCtrl.NoShift)) - { - // It's this.HotKey OR Another View (Label?) forwarded the hotkey command to us - Act just like `Space` (Select) - return InvokeCommand (Command.Select); - } - } - - if (item is { } && item < _radioLabels.Count) - { - if (item.Value == SelectedItem) - { - return true; - } - - // If a RadioItem.HotKey is pressed we always set the selected item - never SetFocus - bool selectedItemChanged = ChangeSelectedItem (item.Value); - - if (selectedItemChanged) - { - // Doesn't matter if it's handled - RaiseSelecting (ctx); - return true; - } - - - return false; - } - - if (SelectedItem == -1 && ChangeSelectedItem (0)) - { - if (RaiseSelecting (ctx) == true) - { - return true; - } - return false; - } - - if (RaiseHandlingHotKey () == true) - { - return true; - }; - - // Default Command.Hotkey sets focus - SetFocus (); - - return true; - }); - - AddCommand ( - Command.Up, - () => - { - if (!HasFocus) - { - return false; - } - - return MoveUpLeft (); - } - ); - - AddCommand ( - Command.Down, - () => - { - if (!HasFocus) - { - return false; - } - - return MoveDownRight (); - } - ); - - AddCommand ( - Command.Start, - () => - { - if (!HasFocus) - { - return false; - } - - MoveHome (); - - return true; - } - ); - - AddCommand ( - Command.End, - () => - { - if (!HasFocus) - { - return false; - } - - MoveEnd (); - - return true; - } - ); + AddCommand (Command.Up, () => HasFocus && MoveUpLeft ()); + AddCommand (Command.Down, () => HasFocus && MoveDownRight ()); + AddCommand (Command.Start, () => HasFocus && MoveHome ()); + AddCommand (Command.End, () => HasFocus && MoveEnd ()); // ReSharper disable once UseObjectOrCollectionInitializer _orientationHelper = new (this); @@ -181,11 +41,147 @@ public class RadioGroup : View, IDesignable, IOrientation SetupKeyBindings (); + // By default, single click is already bound to Command.Select + MouseBindings.Add (MouseFlags.Button1DoubleClicked, Command.Accept); + SubviewLayout += RadioGroup_LayoutStarted; HighlightStyle = HighlightStyle.PressedOutside | HighlightStyle.Pressed; + } - MouseClick += RadioGroup_MouseClick; + private bool? HandleHotKeyCommand (ICommandContext? ctx) + { + // If the command did not come from a keyboard event, ignore it + if (ctx is not CommandContext keyCommandContext) + { + return false; + } + + var item = keyCommandContext.Binding.Data as int?; + + if (HasFocus) + { + if ((item is null || HotKey == keyCommandContext.Binding.Key?.NoAlt.NoCtrl.NoShift!)) + { + // It's this.HotKey OR Another View (Label?) forwarded the hotkey command to us - Act just like `Space` (Select) + return InvokeCommand (Command.Select); + } + } + + if (item is { } && item < _radioLabels.Count) + { + if (item.Value == SelectedItem) + { + return true; + } + + // If a RadioItem.HotKey is pressed we always set the selected item - never SetFocus + bool selectedItemChanged = ChangeSelectedItem (item.Value); + + if (selectedItemChanged) + { + // Doesn't matter if it's handled + RaiseSelecting (ctx); + + return true; + } + + return false; + } + + if (SelectedItem == -1 && ChangeSelectedItem (0)) + { + if (RaiseSelecting (ctx) == true) + { + return true; + } + + return false; + } + + if (RaiseHandlingHotKey () == true) + { + return true; + } + + ; + + // Default Command.Hotkey sets focus + SetFocus (); + + return true; + } + + private bool? HandleAcceptCommand (ICommandContext? ctx) + { + if (!DoubleClickAccepts + && ctx is CommandContext mouseCommandContext + && mouseCommandContext.Binding.MouseEventArgs!.Flags.HasFlag (MouseFlags.Button1DoubleClicked)) + { + return false; + } + + return RaiseAccepting (ctx); + } + + private bool? HandleSelectCommand (ICommandContext? ctx) + { + if (ctx is CommandContext mouseCommandContext + && mouseCommandContext.Binding.MouseEventArgs!.Flags.HasFlag (MouseFlags.Button1Clicked)) + { + int viewportX = mouseCommandContext.Binding.MouseEventArgs.Position.X; + int viewportY = mouseCommandContext.Binding.MouseEventArgs.Position.Y; + + int pos = Orientation == Orientation.Horizontal ? viewportX : viewportY; + + int rCount = Orientation == Orientation.Horizontal + ? _horizontal!.Last ().pos + _horizontal!.Last ().length + : _radioLabels.Count; + + if (pos < rCount) + { + int c = Orientation == Orientation.Horizontal + ? _horizontal!.FindIndex (x => x.pos <= viewportX && x.pos + x.length - 2 >= viewportX) + : viewportY; + + if (c > -1) + { + // Just like the user pressing the items' hotkey + return InvokeCommand (Command.HotKey, new KeyBinding ([Command.HotKey], target: this, data: c)) == true; + } + } + + return false; + } + + bool cursorChanged = false; + + if (SelectedItem == Cursor) + { + cursorChanged = MoveDownRight (); + + if (!cursorChanged) + { + cursorChanged = MoveHome (); + } + } + + bool selectedItemChanged = false; + + if (SelectedItem != Cursor) + { + selectedItemChanged = ChangeSelectedItem (Cursor); + } + + if (cursorChanged || selectedItemChanged) + { + if (RaiseSelecting (ctx) == true) + { + return true; + } + } + + return cursorChanged || selectedItemChanged; } // TODO: Fix InvertColorsOnPress - only highlight the selected item @@ -226,48 +222,6 @@ public class RadioGroup : View, IDesignable, IOrientation /// public bool DoubleClickAccepts { get; set; } = true; - private void RadioGroup_MouseClick (object? sender, MouseEventArgs e) - { - if (e.Flags.HasFlag (MouseFlags.Button1Clicked)) - { - int viewportX = e.Position.X; - int viewportY = e.Position.Y; - - int pos = Orientation == Orientation.Horizontal ? viewportX : viewportY; - - int rCount = Orientation == Orientation.Horizontal - ? _horizontal!.Last ().pos + _horizontal!.Last ().length - : _radioLabels.Count; - - if (pos < rCount) - { - int c = Orientation == Orientation.Horizontal - ? _horizontal!.FindIndex (x => x.pos <= viewportX && x.pos + x.length - 2 >= viewportX) - : viewportY; - - if (c > -1) - { - // Just like the user pressing the items' hotkey - e.Handled = InvokeCommand (Command.HotKey, new KeyBinding ([Command.HotKey], target: this, data: c)) == true; - } - } - - return; - } - - if (DoubleClickAccepts && e.Flags.HasFlag (MouseFlags.Button1DoubleClicked)) - { - // NOTE: Drivers ALWAYS generate a Button1Clicked event before Button1DoubleClicked - // NOTE: So, we've already selected an item. - - // Just like the user pressing `Enter` - InvokeCommand (Command.Accept); - } - - // HACK: Always eat so Select is not invoked by base - e.Handled = true; - } - private List<(int pos, int length)>? _horizontal; private int _horizontalSpace = 2; @@ -538,7 +492,12 @@ public class RadioGroup : View, IDesignable, IOrientation return false; } - private void MoveEnd () { Cursor = Math.Max (_radioLabels.Count - 1, 0); } + private bool MoveEnd () + { + Cursor = Math.Max (_radioLabels.Count - 1, 0); + + return true; + } private bool MoveHome () { diff --git a/UICatalog/Scenarios/MessageBoxes.cs b/UICatalog/Scenarios/MessageBoxes.cs index 0e6db7c16..934a70070 100644 --- a/UICatalog/Scenarios/MessageBoxes.cs +++ b/UICatalog/Scenarios/MessageBoxes.cs @@ -185,7 +185,9 @@ public class MessageBoxes : Scenario var styleRadioGroup = new RadioGroup { - X = Pos.Right (label) + 1, Y = Pos.Top (label), RadioLabels = new [] { "_Query", "_Error" } + X = Pos.Right (label) + 1, + Y = Pos.Top (label), + RadioLabels = ["_Query", "_Error"], }; frame.Add (styleRadioGroup);