Fixed RadioGroup 2

This commit is contained in:
Tig
2024-10-01 15:34:55 -06:00
parent 66edb36cc7
commit f3b93a58dd
9 changed files with 410 additions and 73 deletions

View File

@@ -394,6 +394,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
/// See the View Navigation Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/navigation.html"/>
/// </para>
/// </remarks>
/// <returns><see langword="true"/> if the focus changed; <see langword="true"/> false otherwise.</returns>
public bool SetFocus ()
{
(bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ());

View File

@@ -1,4 +1,6 @@
#nullable enable
using System.Reflection.Metadata;
namespace Terminal.Gui;
/// <summary>Shows a check box that can be cycled between three states.</summary>
@@ -20,8 +22,13 @@ public class CheckBox : View
CanFocus = true;
// Things this view knows how to do
AddCommand (Command.Accept, AdvanceCheckState);
// Select (Space key and single-click) - Advance state and raise Select event
AddCommand (Command.Select, AdvanceCheckState);
// Accept (Enter key and double-click) - Raise Accept event - DO NOT advance state
AddCommand (Command.Accept, RaiseAcceptEvent);
// Hotkey - Advance state and raise Select event - DO NOT raise Accept
AddCommand (Command.HotKey, AdvanceCheckState);
TitleChanged += Checkbox_TitleChanged;
@@ -161,15 +168,9 @@ public class CheckBox : View
return e.Cancel;
}
// By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the Accept event is fired.
if (RaiseAcceptEvent () == true)
{
return true;
}
CheckedState = e.NewValue;
return true;
return RaiseSelectEvent ();
}
/// <summary>Raised when the <see cref="CheckBox"/> state is changing.</summary>

View File

@@ -17,7 +17,7 @@ public class Label : View
Width = Dim.Auto (DimAutoStyle.Text);
// Things this view knows how to do
AddCommand (Command.HotKey, FocusNext);
AddCommand (Command.HotKey, context => InvokeHotKeyOnNext(context));
TitleChanged += Label_TitleChanged;
MouseClick += Label_MouseClick;
@@ -51,12 +51,12 @@ public class Label : View
set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value;
}
private bool? FocusNext ()
private bool? InvokeHotKeyOnNext (CommandContext context)
{
int me = SuperView?.Subviews.IndexOf (this) ?? -1;
if (me != -1 && me < SuperView?.Subviews.Count - 1)
{
SuperView?.Subviews [me + 1].SetFocus ();
SuperView?.Subviews [me + 1].InvokeCommand(Command.HotKey, context.Key, context.KeyBinding);
}
return true;

View File

@@ -1,4 +1,5 @@
namespace Terminal.Gui;
#nullable enable
namespace Terminal.Gui;
/// <summary>Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time.</summary>
public class RadioGroup : View, IDesignable, IOrientation
@@ -82,16 +83,17 @@ public class RadioGroup : View, IDesignable, IOrientation
}
}
SelectedItem = Cursor;
return true;
return SetSelectedItem (Cursor);
});
AddCommand (
Command.Accept,
() =>
{
SelectedItem = Cursor;
if (!SetSelectedItem (Cursor))
{
return false;
}
return RaiseAcceptEvent () is false;
}
@@ -101,14 +103,41 @@ public class RadioGroup : View, IDesignable, IOrientation
Command.HotKey,
ctx =>
{
if (ctx.KeyBinding?.Context is { } && (int)ctx.KeyBinding?.Context! < _radioLabels.Count)
{
SelectedItem = (int)ctx.KeyBinding?.Context!;
var item = ctx.KeyBinding?.Context as int?;
return RaiseSelectEvent () is true or null;
if (HasFocus)
{
if (ctx is { KeyBinding: { } } && (ctx.KeyBinding.Value.BoundView != this || HotKey == ctx.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, ctx.Key, ctx.KeyBinding);
}
}
return !SetFocus ();
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
if (SetSelectedItem (item.Value))
{
return true;
}
return false;
}
if (SelectedItem == -1 && SetSelectedItem (0))
{
return true;
}
SetFocus ();
return true;
});
_orientationHelper = new (this);
@@ -247,12 +276,36 @@ public class RadioGroup : View, IDesignable, IOrientation
public int SelectedItem
{
get => _selected;
set
set => SetSelectedItem (value);
}
/// <summary>
/// INTERNAL Sets the selected item.
/// </summary>
/// <param name="value"></param>
/// <returns>true if the selection changed.</returns>
private bool SetSelectedItem (int value)
{
if (_selected == value || value > _radioLabels.Count - 1)
{
OnSelectedItemChanged (value, SelectedItem);
Cursor = Math.Max (_selected, 0);
SetNeedsDisplay ();
return false;
}
if (RaiseSelectEvent () == true)
{
return false;
}
int savedSelected = _selected;
_selected = value;
Cursor = Math.Max (_selected, 0);
OnSelectedItemChanged (value, SelectedItem);
SelectedItemChanged?.Invoke (this, new (SelectedItem, savedSelected));
SetNeedsDisplay ();
return true;
}
/// <inheritdoc/>
@@ -370,16 +423,7 @@ public class RadioGroup : View, IDesignable, IOrientation
/// <summary>Called whenever the current selected item changes. Invokes the <see cref="SelectedItemChanged"/> event.</summary>
/// <param name="selectedItem"></param>
/// <param name="previousSelectedItem"></param>
public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem)
{
if (_selected == selectedItem)
{
return;
}
_selected = selectedItem;
SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem));
}
protected virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem) { }
/// <summary>
/// Gets or sets the <see cref="RadioLabels"/> index for the cursor. The cursor may or may not be the selected

View File

@@ -220,7 +220,7 @@ public class Buttons : Scenario
var label = new Label
{
X = 2, Y = Pos.Bottom (computedFrame) + 1,
Text = "Text Alignment (changes the four buttons above): "
Text = "Text Ali_gnment (changes the four buttons above): "
};
main.Add (label);
@@ -229,7 +229,9 @@ public class Buttons : Scenario
X = 4,
Y = Pos.Bottom (label) + 1,
SelectedItem = 2,
RadioLabels = new [] { "Start", "End", "Center", "Fill" }
RadioLabels = new [] { "_Start", "_End", "_Center", "_Fill" },
Title = "_9 RadioGroup",
BorderStyle = LineStyle.Dotted
};
main.Add (radioGroup);

View File

@@ -17,8 +17,8 @@ public sealed class MyScenario : Scenario
Title = GetQuitKeyAndName (),
};
var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "_Press me!" };
button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
var button = new CheckBox() { X = Pos.Center (), Y = Pos.Center (), Text = "_Press me!" };
//button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
appWindow.Add (button);
// Run - Start the application.

View File

@@ -1,5 +1,6 @@
using System.ComponentModel;
using Xunit.Abstractions;
// ReSharper disable AccessToModifiedClosure
namespace Terminal.Gui.ViewsTests;
@@ -172,47 +173,99 @@ public class CheckBoxTests (ITestOutputHelper output)
[Fact]
public void KeyBindings_Command ()
{
var toggled = false;
Application.Navigation = new ApplicationNavigation ();
Application.Top = new Toplevel ();
View otherView = new () { CanFocus = true };
var ckb = new CheckBox ();
ckb.CheckedStateChanging += (s, e) => toggled = true;
Application.Top.Add (ckb, otherView);
Application.Top.SetFocus ();
Assert.True (ckb.HasFocus);
int checkedStateChangingCount = 0;
ckb.CheckedStateChanging += (s, e) => checkedStateChangingCount++;
int selectCount = 0;
ckb.Select += (s, e) => selectCount++;
int acceptCount = 0;
ckb.Accept += (s, e) => acceptCount++;
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
Assert.False (toggled);
Assert.Equal (0, checkedStateChangingCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
Assert.Equal (Key.Empty, ckb.HotKey);
// Test while focused
ckb.Text = "_Test";
Assert.Equal (Key.T, ckb.HotKey);
Assert.True (ckb.NewKeyDownEvent (Key.T));
ckb.NewKeyDownEvent (Key.T);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
Assert.True (toggled);
Assert.Equal (1, checkedStateChangingCount);
Assert.Equal (1, selectCount);
Assert.Equal (0, acceptCount);
ckb.Text = "T_est";
toggled = false;
Assert.Equal (Key.E, ckb.HotKey);
Assert.True (ckb.NewKeyDownEvent (Key.E.WithAlt));
Assert.True (toggled);
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
ckb.NewKeyDownEvent (Key.E.WithAlt);
Assert.Equal (2, checkedStateChangingCount);
Assert.Equal (2, selectCount);
Assert.Equal (0, acceptCount);
toggled = false;
Assert.Equal (Key.E, ckb.HotKey);
Assert.True (ckb.NewKeyDownEvent (Key.E));
Assert.True (toggled);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
ckb.NewKeyDownEvent (Key.Space);
Assert.Equal (3, checkedStateChangingCount);
Assert.Equal (3, selectCount);
Assert.Equal (0, acceptCount);
toggled = false;
Assert.True (ckb.NewKeyDownEvent (Key.Space));
Assert.True (toggled);
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
toggled = false;
Assert.True (ckb.NewKeyDownEvent (Key.Space));
Assert.True (toggled);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
ckb.NewKeyDownEvent (Key.Enter);
Assert.Equal (3, checkedStateChangingCount);
Assert.Equal (3, selectCount);
Assert.Equal (1, acceptCount);
toggled = false;
Assert.False (ckb.NewKeyDownEvent (Key.Enter));
Assert.False (toggled);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
//ckb.Text = "_Test";
//Assert.Equal (Key.T, ckb.HotKey);
//Assert.True (ckb.NewKeyDownEvent (Key.T));
//Assert.Equal (CheckState.Checked, ckb.CheckedState);
//Assert.True (checkedStateChangingCount);
//Assert.True (ckb.HasFocus);
//ckb.Text = "T_est";
//checkedStateChangingCount = false;
//Assert.Equal (Key.E, ckb.HotKey);
//Assert.True (ckb.NewKeyDownEvent (Key.E.WithAlt));
//Assert.True (checkedStateChangingCount);
//Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
//checkedStateChangingCount = false;
//Assert.Equal (Key.E, ckb.HotKey);
//Assert.True (ckb.NewKeyDownEvent (Key.E));
//Assert.True (checkedStateChangingCount);
//Assert.Equal (CheckState.Checked, ckb.CheckedState);
//checkedStateChangingCount = false;
//Assert.False (ckb.NewKeyDownEvent (Key.Space));
//Assert.True (checkedStateChangingCount);
//Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
//ckb.SetFocus ();
//Assert.False (ckb.NewKeyDownEvent (Key.Space));
//Assert.True (checkedStateChangingCount);
//Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
//checkedStateChangingCount = false;
//Assert.True (ckb.NewKeyDownEvent (Key.Space));
//Assert.False (checkedStateChangingCount);
//Assert.Equal (CheckState.Checked, ckb.CheckedState);
//checkedStateChangingCount = false;
//Assert.False (ckb.NewKeyDownEvent (Key.Enter));
//Assert.False (checkedStateChangingCount);
//Assert.Equal (CheckState.Checked, ckb.CheckedState);
Application.Top.Dispose ();
Application.ResetState (false);
}
[Fact]

View File

@@ -76,7 +76,7 @@ public class RadioGroupTests (ITestOutputHelper output)
}
[Fact]
public void KeyBindings_Command ()
public void Commands_HasFocus ()
{
Application.Navigation = new ();
var rg = new RadioGroup { RadioLabels = new [] { "Test", "New Test" } };
@@ -85,29 +85,61 @@ public class RadioGroupTests (ITestOutputHelper output)
rg.SetFocus ();
Assert.Equal (Orientation.Vertical, rg.Orientation);
int selectedItemChangedCount = 0;
rg.SelectedItemChanged += (s, e) => selectedItemChangedCount++;
int selectCount = 0;
rg.Select += (s, e) => selectCount++;
int acceptCount = 0;
rg.Accept += (s, e) => acceptCount++;
// By default the first item is selected
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
Assert.Equal (Key.Empty, rg.HotKey);
// With HasFocus
// Test up/down without Select
Assert.False (Application.OnKeyDown (Key.CursorUp)); // Should not change (should focus prev view if there was one, which there isn't)
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (0, rg.Cursor);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
Assert.True (Application.OnKeyDown (Key.CursorDown));
Assert.Equal (0, rg.SelectedItem); // Cursor changed, but selection didnt
Assert.Equal (1, rg.Cursor);
Assert.False (Application.OnKeyDown (Key.CursorDown)); // Should not change (should focus next view if there was one, which there isn't)
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
Assert.False (Application.OnKeyDown (Key.CursorDown)); // Should not change selection (should focus next view if there was one, which there isn't)
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
// Now test Select (Space) when Cursor != SelectedItem
// Test Select (Space) when Cursor != SelectedItem
Assert.True (Application.OnKeyDown (Key.Space));
Assert.Equal (1, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (1, selectedItemChangedCount);
Assert.Equal (1, selectCount);
Assert.Equal (0, acceptCount);
// Now test Select (Space) when Cursor == SelectedItem - Should cycle
Assert.True (Application.OnKeyDown (Key.Space));
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (0, rg.Cursor);
Assert.Equal (2, selectedItemChangedCount);
Assert.Equal (2, selectCount);
Assert.Equal (0, acceptCount);
Assert.True (Application.OnKeyDown (Key.Space));
Assert.Equal (1, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
@@ -131,12 +163,203 @@ public class RadioGroupTests (ITestOutputHelper output)
Assert.True (Application.OnKeyDown (Key.Space));
Assert.Equal (1, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (7, selectedItemChangedCount);
Assert.Equal (7, selectCount);
Assert.Equal (0, acceptCount);
// Test HotKey
// Selected == Cursor (1) - Advance state and raise Select event - DO NOT raise Accept
rg.HotKey = Key.L;
Assert.Equal (Key.L, rg.HotKey);
Assert.True (Application.OnKeyDown (Key.L));
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (0, rg.Cursor);
Assert.Equal (8, selectedItemChangedCount);
Assert.Equal (8, selectCount);
Assert.Equal (0, acceptCount);
// Make Selected != Cursor
Assert.True (Application.OnKeyDown (Key.CursorDown));
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
// Selected != Cursor - Select Cursor and raise Select event - DO NOT raise Accept
Assert.True (Application.OnKeyDown (Key.L));
Assert.Equal (1, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (9, selectedItemChangedCount);
Assert.Equal (9, selectCount);
Assert.Equal (0, acceptCount);
Application.ResetState (ignoreDisposed: true);
}
[Fact]
public void HotKeys_Select_RadioLabels ()
public void HotKey_HasFocus_False ()
{
Application.Navigation = new ();
var rg = new RadioGroup { RadioLabels = new [] { "Test", "New Test" } };
Application.Top = new Toplevel ();
// With !HasFocus
View otherView = new () { Id = "otherView", CanFocus = true };
Label label = new ()
{
Id = "label",
Title = "_R",
};
Application.Top.Add (label, rg, otherView);
otherView.SetFocus ();
int selectedItemChangedCount = 0;
rg.SelectedItemChanged += (s, e) => selectedItemChangedCount++;
int selectCount = 0;
rg.Select += (s, e) => selectCount++;
int acceptCount = 0;
rg.Accept += (s, e) => acceptCount++;
// By default the first item is selected
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (Orientation.Vertical, rg.Orientation);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
Assert.Equal (Key.Empty, rg.HotKey);
Assert.False (rg.HasFocus);
// Test HotKey
// Selected (0) == Cursor (0) - SetFocus
rg.HotKey = Key.L;
Assert.Equal (Key.L, rg.HotKey);
Assert.True (Application.OnKeyDown (Key.L));
Assert.True (rg.HasFocus);
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (0, rg.Cursor);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
// Make Selected != Cursor
Assert.True (Application.OnKeyDown (Key.CursorDown));
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
otherView.SetFocus ();
// Selected != Cursor - SetFocus
Assert.True (Application.OnKeyDown (Key.L));
Assert.True (rg.HasFocus);
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
Assert.True (Application.OnKeyDown (Key.L));
Assert.True (rg.HasFocus);
Assert.Equal (1, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (1, selectedItemChangedCount);
Assert.Equal (1, selectCount);
Assert.Equal (0, acceptCount);
Application.ResetState (ignoreDisposed: true);
}
[Fact]
public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects ()
{
Application.Navigation = new ();
var rg = new RadioGroup { RadioLabels = new [] { "Item _A", "Item _B" } };
Application.Top = new Toplevel ();
// With !HasFocus
View otherView = new () { Id = "otherView", CanFocus = true };
Label label = new ()
{
Id = "label",
Title = "_R",
};
Application.Top.Add (label, rg, otherView);
otherView.SetFocus ();
int selectedItemChangedCount = 0;
rg.SelectedItemChanged += (s, e) => selectedItemChangedCount++;
int selectCount = 0;
rg.Select += (s, e) => selectCount++;
int acceptCount = 0;
rg.Accept += (s, e) => acceptCount++;
// By default the first item is selected
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (Orientation.Vertical, rg.Orientation);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
Assert.Equal (Key.Empty, rg.HotKey);
Assert.False (rg.HasFocus);
// Test RadioTitem.HotKey - Should never SetFocus
// Selected (0) == Cursor (0)
Assert.True (Application.OnKeyDown (Key.A));
Assert.False (rg.HasFocus);
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (0, rg.Cursor);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
rg.SetFocus ();
// Make Selected != Cursor
Assert.True (Application.OnKeyDown (Key.CursorDown));
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
otherView.SetFocus ();
// Selected != Cursor
Assert.True (Application.OnKeyDown (Key.A));
Assert.False (rg.HasFocus);
Assert.Equal (0, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (0, selectedItemChangedCount);
Assert.Equal (0, selectCount);
Assert.Equal (0, acceptCount);
// Selected != Cursor - Should not set focus
Assert.True (Application.OnKeyDown (Key.B));
Assert.False (rg.HasFocus);
Assert.Equal (1, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (1, selectedItemChangedCount);
Assert.Equal (1, selectCount);
Assert.Equal (0, acceptCount);
Assert.True (Application.OnKeyDown (Key.B));
Assert.False (rg.HasFocus);
Assert.Equal (1, rg.SelectedItem);
Assert.Equal (1, rg.Cursor);
Assert.Equal (1, selectedItemChangedCount);
Assert.Equal (1, selectCount);
Assert.Equal (0, acceptCount);
Application.ResetState (ignoreDisposed: true);
}
[Fact]
public void HotKeys_HasFocus_True_Selects ()
{
var rg = new RadioGroup { RadioLabels = new [] { "_Left", "_Right", "Cen_tered", "_Justified" } };
Application.Top = new Toplevel ();
@@ -251,7 +474,7 @@ public class RadioGroupTests (ITestOutputHelper output)
}
[Fact]
public void Accept_Command_Fires_Accept ()
public void Accept_Command_Does_Not_Fire_Accept ()
{
var group = new RadioGroup { RadioLabels = new [] { "_Left", "_Right", "Cen_tered", "_Justified" } };
var accepted = false;
@@ -259,7 +482,7 @@ public class RadioGroupTests (ITestOutputHelper output)
group.Accept += OnAccept;
group.InvokeCommand (Command.Accept);
Assert.True (accepted);
Assert.False (accepted);
return;

View File

@@ -414,6 +414,19 @@ Same for mouse interaction:
This gets really interesting when there's a View like a `Shortcut` that is a composite of several subviews.
### New Model
| | | | | **Keyboard** | | | | **Mouse** | | | | |
|----------------|-------------------------|------------|---------------|--------------|--------------------------------------------------------------------------------|--------------------------------------------------|---------------------------------------|-------------------------------|------------------------------|-------------------------------|----------------|---------------|
| | **Number<br>of States** | **Static** | **IsDefault** | **Hotkeys** | **Select<br>Command<br>`Space`** | **Accept<br>Command<br>`Enter`** | **Hotkey<br>Command** | **CanFocus<br>Click** | **CanFocus<br>DblCLick** | **!CanFocus<br>Click** | **RightClick** | **GrabMouse** |
| **View** | 1 | Yes | No | 1 | | OnAccept | Focus | Focus | | | | No |
| **Label** | 1 | Yes | No | 1 | | OnAccept | FocusNext | Focus | | FocusNext | | No |
| **Button** | 1 | No | Yes | 1 | Focus<br>OnAccept | Focus<br>OnAccept | Focus<br>OnAccept | Focus<br>OnAccept | | OnAccept | | No |
| **Checkbox** | 3 | No | No | 1 | AdvanceCheckState<br>OnSelect | AdvanceCheckState<br>OnAccept | AdvanceCheckState<br>OnAccept | AdvanceCheckState<br>OnAccept | | AdvanceCheckState<br>OnAccept | | No |
| **RadioGroup** | > 1 | No | No | 2+ | If cursor not selected,<br>select. Else, Advance <br>selected item<br>OnSelect | Set SelectedItem<br>OnAccept | Focus<br>Set SelectedItem<br>OnAccept | SetFocus<br>Set _cursor | | SetFocus<br>Set _cursor | | No |
| **Slider** | > 1 | No | No | 1 | SetFocusedOption<br>OnOptionsChanged | SetFocusedOption<br>OnOptionsChanged<br>OnAccept | Focus | SetFocus<br>SetFocusedOption | | SetFocus<br>SetFocusedOption | | Yes |
| **ListView** | > 1 | No | No | 1 | MarkUnMarkRow | OpenSelectedItem<br>OnAccept | OnAccept | SetMark<br>OnSelectedChanged | OpenSelectedItem<br>OnAccept | | | No |
## `View` - base class
### `!HasFocus`
@@ -627,7 +640,7 @@ In v2_develop it's all kinds of confused. Here's what it SHOULD do:
* `Enter` - `Command.Accept` -> Advances state to selected RadioItem and Raises `Accept`
* `Space` - `Command.Select` -> Advances state
* `Title.Hotkey` - `Command.Hotkey` -> does nothing
* `Title.Hotkey` - `Command.Hotkey` -> Advance state
* `RadioItem.Hotkey` - `Command.Select` -> Advance State to RadioItem with hotkey.
* `Click` - advances state to clicked RadioItem.
* `Double Click` - Advances state to clicked RadioItem and then raises `Accept` (this is what Office does; it's pretty nice. Windows does nothing).