mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Refactored RadioGroup to just use Commands
This commit is contained in:
@@ -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<KeyBinding> 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<KeyBinding> 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<MouseBinding> mouseCommandContext
|
||||
&& mouseCommandContext.Binding.MouseEventArgs!.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return RaiseAccepting (ctx);
|
||||
}
|
||||
|
||||
private bool? HandleSelectCommand (ICommandContext? ctx)
|
||||
{
|
||||
if (ctx is CommandContext<MouseBinding> 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<KeyBinding> (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
|
||||
/// </remarks>
|
||||
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<KeyBinding> (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 ()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user