mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Fixed RadioGroup 2
This commit is contained in:
@@ -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 ());
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user