From 1e993193672e390ee58b18d1dc3b2735d7734b68 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 13 Oct 2024 08:01:57 -0600 Subject: [PATCH 01/15] Fixed --- Terminal.Gui/Views/ListView.cs | 2 +- Terminal.Gui/Views/TableView/TableView.cs | 2 +- Terminal.Gui/Views/TreeView/TreeView.cs | 2 +- UICatalog/UICatalog.cs | 44 +++++++++++------------ 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 0c54aa2612..94d2f82823 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -803,7 +803,7 @@ public bool OnOpenSelectedItem () } /// - public override bool OnProcessKeyDown (Key a) + public override bool OnKeyDown (Key a) { // Enable user to find & select an item by typing text if (CollectionNavigatorBase.IsCompatibleKey (a)) diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index 1717b35d68..5df54ae361 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -988,7 +988,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - public override bool OnProcessKeyDown (Key keyEvent) + public override bool OnKeyDown (Key keyEvent) { if (TableIsNullOrInvisible ()) { diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 6a43a3e1e2..1450a57ba1 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -1182,7 +1182,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View cu } /// - public override bool OnProcessKeyDown (Key keyEvent) + public override bool OnKeyDown (Key keyEvent) { if (!Enabled) { diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 975fcc0482..3bc368f579 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -620,28 +620,28 @@ public UICatalogTopLevel () ); ScenarioList.Style.ColumnStyles.Add (1, new () { MaxWidth = 1 }); - // Enable user to find & select a scenario by typing text - // TableView does not (currently) have built-in CollectionNavigator support (the ability for the - // user to type and the items that match get selected). We implement it in the app instead. - ScenarioList.KeyDown += (s, a) => - { - if (CollectionNavigatorBase.IsCompatibleKey (a)) - { - int? newItem = - _scenarioCollectionNav?.GetNextMatchingItem ( - ScenarioList.SelectedRow, - (char)a - ); - - if (newItem is int v && newItem != -1) - { - ScenarioList.SelectedRow = v; - ScenarioList.EnsureSelectedCellIsVisible (); - ScenarioList.SetNeedsDisplay (); - a.Handled = true; - } - } - }; + //// Enable user to find & select a scenario by typing text + //// TableView does not (currently) have built-in CollectionNavigator support (the ability for the + //// user to type and the items that match get selected). We implement it in the app instead. + //ScenarioList.KeyDown += (s, a) => + // { + // if (CollectionNavigatorBase.IsCompatibleKey (a)) + // { + // int? newItem = + // _scenarioCollectionNav?.GetNextMatchingItem ( + // ScenarioList.SelectedRow, + // (char)a + // ); + + // if (newItem is int v && newItem != -1) + // { + // ScenarioList.SelectedRow = v; + // ScenarioList.EnsureSelectedCellIsVisible (); + // ScenarioList.SetNeedsDisplay (); + // a.Handled = true; + // } + // } + // }; ScenarioList.CellActivated += ScenarioView_OpenSelectedItem; // TableView typically is a grid where nav keys are biased for moving left/right. From 8cb0c84282519e77bd7e7ef863a75d41fe409feb Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 13 Oct 2024 08:40:38 -0600 Subject: [PATCH 02/15] Fixed View.KeyDown/Up event handling --- Terminal.Gui/View/View.Keyboard.cs | 71 ++++++++++++----------- Terminal.Gui/Views/ListView.cs | 2 +- Terminal.Gui/Views/TableView/TableView.cs | 16 ++--- Terminal.Gui/Views/TextView.cs | 2 +- UnitTests/View/KeyboardEventTests.cs | 42 +++++--------- UnitTests/Views/ListViewTests.cs | 12 ++-- 6 files changed, 71 insertions(+), 74 deletions(-) diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index f9c8355127..37f2cf2c08 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -259,9 +259,9 @@ private void SetHotKeyFromTitle () /// /// See for an overview of Terminal.Gui keyboard APIs. /// - /// + /// /// if the event was handled. - public bool NewKeyDownEvent (Key keyEvent) + public bool NewKeyDownEvent (Key key) { if (!Enabled) { @@ -270,28 +270,27 @@ public bool NewKeyDownEvent (Key keyEvent) // By default the KeyBindingScope is View - if (Focused?.NewKeyDownEvent (keyEvent) == true) + if (Focused?.NewKeyDownEvent (key) == true) { return true; } - // Before (fire the cancellable event) - if (OnKeyDown (keyEvent)) + if (RaiseKeyDown (key) || key.Handled) { return true; } // During (this is what can be cancelled) - InvokingKeyBindings?.Invoke (this, keyEvent); + InvokingKeyBindings?.Invoke (this, key); - if (keyEvent.Handled) + if (key.Handled) { return true; } // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommand can be reflected up stack - bool? handled = OnInvokingKeyBindings (keyEvent, KeyBindingScope.HotKey | KeyBindingScope.Focused); + bool? handled = OnInvokingKeyBindings (key, KeyBindingScope.HotKey | KeyBindingScope.Focused); if (handled is { } && (bool)handled) { @@ -302,14 +301,28 @@ public bool NewKeyDownEvent (Key keyEvent) // TODO: But I've moved it outside of the v-function to test something. // After (fire the cancellable event) // fire event - ProcessKeyDown?.Invoke (this, keyEvent); + ProcessKeyDown?.Invoke (this, key); - if (!keyEvent.Handled && OnProcessKeyDown (keyEvent)) + if (!key.Handled && OnProcessKeyDown (key)) { return true; } - return keyEvent.Handled; + return key.Handled; + } + + private bool RaiseKeyDown (Key key) + { + // Before (fire the cancellable event) + if (OnKeyDown (key) || key.Handled) + { + return true; + } + + // fire event + KeyDown?.Invoke (this, key); + + return key.Handled; } /// @@ -330,10 +343,7 @@ public bool NewKeyDownEvent (Key keyEvent) /// public virtual bool OnKeyDown (Key keyEvent) { - // fire event - KeyDown?.Invoke (this, keyEvent); - - return keyEvent.Handled; + return false; } /// @@ -421,35 +431,38 @@ public virtual bool OnProcessKeyDown (Key keyEvent) /// /// See for an overview of Terminal.Gui keyboard APIs. /// - /// + /// /// if the event was handled. - public bool NewKeyUpEvent (Key keyEvent) + public bool NewKeyUpEvent (Key key) { if (!Enabled) { return false; } - if (Focused?.NewKeyUpEvent (keyEvent) == true) + if (RaiseKeyUp (key) || key.Handled) { return true; } + return false; + } + + private bool RaiseKeyUp (Key key) + { // Before (fire the cancellable event) - if (OnKeyUp (keyEvent)) + if (OnKeyUp (key) || key.Handled) { return true; } - // During (this is what can be cancelled) - // TODO: Until there's a clear use-case, we will not define 'during' event (e.g. OnDuringKeyUp). - - // After (fire the cancellable event InvokingKeyBindings) - // TODO: Until there's a clear use-case, we will not define an 'after' event (e.g. OnAfterKeyUp). + // fire event + KeyUp?.Invoke (this, key); - return false; + return key.Handled; } + /// Method invoked when a key is released. This method is called from . /// Contains the details about the key that produced the event. /// @@ -467,14 +480,6 @@ public bool NewKeyUpEvent (Key keyEvent) /// public virtual bool OnKeyUp (Key keyEvent) { - // fire event - KeyUp?.Invoke (this, keyEvent); - - if (keyEvent.Handled) - { - return true; - } - return false; } diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 94d2f82823..fddd0482ff 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -806,7 +806,7 @@ public bool OnOpenSelectedItem () public override bool OnKeyDown (Key a) { // Enable user to find & select an item by typing text - if (CollectionNavigatorBase.IsCompatibleKey (a)) + if (CollectionNavigatorBase.IsCompatibleKey (a) && (!AllowsMarking && a == Key.Space)) { int? newItem = KeystrokeNavigator?.GetNextMatchingItem (SelectedItem, (char)a); diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index 5df54ae361..8e90bd42dd 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -988,7 +988,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - public override bool OnKeyDown (Key keyEvent) + public override bool OnKeyDown (Key key) { if (TableIsNullOrInvisible ()) { @@ -998,12 +998,14 @@ public override bool OnKeyDown (Key keyEvent) if (CollectionNavigator != null && HasFocus && Table.Rows != 0 - && CollectionNavigatorBase.IsCompatibleKey (keyEvent) - && !keyEvent.KeyCode.HasFlag (KeyCode.CtrlMask) - && !keyEvent.KeyCode.HasFlag (KeyCode.AltMask) - && Rune.IsLetterOrDigit ((Rune)keyEvent)) - { - return CycleToNextTableEntryBeginningWith (keyEvent); + && key != KeyBindings.GetKeyFromCommands (Command.Accept) + && key != CellActivationKey + && CollectionNavigatorBase.IsCompatibleKey (key) + && !key.KeyCode.HasFlag (KeyCode.CtrlMask) + && !key.KeyCode.HasFlag (KeyCode.AltMask) + && Rune.IsLetterOrDigit ((Rune)key)) + { + return CycleToNextTableEntryBeginningWith (key); } return false; diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 674d8328ed..a539aadf3a 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3688,7 +3688,7 @@ public override bool OnKeyUp (Key key) return true; } - return base.OnKeyUp (key); + return false; } /// diff --git a/UnitTests/View/KeyboardEventTests.cs b/UnitTests/View/KeyboardEventTests.cs index ec7eec6aa1..70965a8da1 100644 --- a/UnitTests/View/KeyboardEventTests.cs +++ b/UnitTests/View/KeyboardEventTests.cs @@ -108,7 +108,7 @@ public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, boo Assert.Equal (alt, e.IsAlt); Assert.Equal (control, e.IsCtrl); Assert.False (keyDown); - Assert.False (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); keyDown = true; }; view.ProcessKeyDown += (s, e) => { keyPressed = true; }; @@ -120,7 +120,7 @@ public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, boo Assert.Equal (alt, e.IsAlt); Assert.Equal (control, e.IsCtrl); Assert.False (keyUp); - Assert.False (view.OnKeyUpContinued); + Assert.True (view.OnKeyUpCalled); keyUp = true; }; @@ -138,7 +138,7 @@ public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, boo ) ); Assert.True (keyPressed); - Assert.True (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); Assert.True (view.OnKeyPressedContinued); view.NewKeyUpEvent ( @@ -150,7 +150,7 @@ public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, boo ) ); Assert.True (keyUp); - Assert.True (view.OnKeyUpContinued); + Assert.True (view.OnKeyUpCalled); } [Fact] @@ -215,7 +215,7 @@ public void InvokingKeyBindings_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyDown); - Assert.False (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); e.Handled = false; keyDown = true; }; @@ -243,7 +243,7 @@ public void InvokingKeyBindings_Handled_True_Stops_Processing () Assert.True (invokingKeyBindings); Assert.False (keyPressed); - Assert.True (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); Assert.False (view.OnInvokingKeyBindingsContinued); Assert.False (view.OnKeyPressedContinued); } @@ -263,7 +263,7 @@ public void KeyDown_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyDown); - Assert.False (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); e.Handled = true; keyDown = true; }; @@ -291,7 +291,7 @@ public void KeyDown_Handled_True_Stops_Processing () Assert.False (invokingKeyBindings); Assert.False (keyPressed); - Assert.False (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); Assert.False (view.OnInvokingKeyBindingsContinued); Assert.False (view.OnKeyPressedContinued); } @@ -352,7 +352,7 @@ public void KeyPressed_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyDown); - Assert.False (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); e.Handled = false; keyDown = true; }; @@ -380,7 +380,7 @@ public void KeyPressed_Handled_True_Stops_Processing () Assert.True (invokingKeyBindings); Assert.True (keyPressed); - Assert.True (view.OnKeyDownContinued); + Assert.True (view.OnKeyDownCalled); Assert.True (view.OnInvokingKeyBindingsContinued); Assert.False (view.OnKeyPressedContinued); } @@ -406,8 +406,8 @@ public void KeyUp_Handled_True_Stops_Processing () view.NewKeyUpEvent (Key.A); Assert.True (keyUp); - Assert.False (view.OnKeyUpContinued); - Assert.False (view.OnKeyDownContinued); + Assert.True (view.OnKeyUpCalled); + Assert.False (view.OnKeyDownCalled); Assert.False (view.OnInvokingKeyBindingsContinued); Assert.False (view.OnKeyPressedContinued); } @@ -444,9 +444,9 @@ public class OnKeyTestView : View public OnKeyTestView () { CanFocus = true; } public bool CancelVirtualMethods { set; private get; } public bool OnInvokingKeyBindingsContinued { get; set; } - public bool OnKeyDownContinued { get; set; } + public bool OnKeyDownCalled { get; set; } public bool OnKeyPressedContinued { get; set; } - public bool OnKeyUpContinued { get; set; } + public bool OnKeyUpCalled { get; set; } public override string Text { get; set; } public override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) @@ -465,24 +465,14 @@ public class OnKeyTestView : View public override bool OnKeyDown (Key keyEvent) { - if (base.OnKeyDown (keyEvent)) - { - return true; - } - - OnKeyDownContinued = true; + OnKeyDownCalled = true; return CancelVirtualMethods; } public override bool OnKeyUp (Key keyEvent) { - if (base.OnKeyUp (keyEvent)) - { - return true; - } - - OnKeyUpContinued = true; + OnKeyUpCalled = true; return CancelVirtualMethods; } diff --git a/UnitTests/Views/ListViewTests.cs b/UnitTests/Views/ListViewTests.cs index ff84f1b2f7..7714c82040 100644 --- a/UnitTests/Views/ListViewTests.cs +++ b/UnitTests/Views/ListViewTests.cs @@ -539,10 +539,10 @@ public void ListViewSelectThenDown () lv.KeyBindings.Add (Key.Space.WithShift, Command.Select, Command.Down); - Key ev = Key.Space.WithShift; + Key key = Key.Space.WithShift; // view should indicate that it has accepted and consumed the event - Assert.True (lv.NewKeyDownEvent (ev)); + Assert.True (lv.NewKeyDownEvent (key)); // first item should now be selected Assert.Equal (0, lv.SelectedItem); @@ -553,7 +553,7 @@ public void ListViewSelectThenDown () Assert.False (lv.Source.IsMarked (2)); // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); + Assert.True (lv.NewKeyDownEvent (key)); // second item should now be selected Assert.Equal (1, lv.SelectedItem); @@ -564,21 +564,21 @@ public void ListViewSelectThenDown () Assert.False (lv.Source.IsMarked (2)); // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); + Assert.True (lv.NewKeyDownEvent (key)); Assert.Equal (2, lv.SelectedItem); Assert.True (lv.Source.IsMarked (0)); Assert.True (lv.Source.IsMarked (1)); Assert.False (lv.Source.IsMarked (2)); // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); + Assert.True (lv.NewKeyDownEvent (key)); Assert.Equal (2, lv.SelectedItem); // cannot move down any further Assert.True (lv.Source.IsMarked (0)); Assert.True (lv.Source.IsMarked (1)); Assert.True (lv.Source.IsMarked (2)); // but can toggle marked // Press key combo again - Assert.True (lv.NewKeyDownEvent (ev)); + Assert.True (lv.NewKeyDownEvent (key)); Assert.Equal (2, lv.SelectedItem); // cannot move down any further Assert.True (lv.Source.IsMarked (0)); Assert.True (lv.Source.IsMarked (1)); From 61be0615e0ebf7b8f9c0bcc73550f49d8876df49 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 13 Oct 2024 09:25:37 -0600 Subject: [PATCH 03/15] Fixed TableView key handling --- Terminal.Gui/View/View.Keyboard.cs | 100 +++++++++------- Terminal.Gui/Views/DateField.cs | 2 +- Terminal.Gui/Views/FileDialog.cs | 2 +- Terminal.Gui/Views/HexView.cs | 2 +- Terminal.Gui/Views/ListView.cs | 2 +- Terminal.Gui/Views/ScrollView.cs | 2 +- Terminal.Gui/Views/TableView/TableView.cs | 2 +- Terminal.Gui/Views/TextField.cs | 2 +- Terminal.Gui/Views/TextValidateField.cs | 2 +- Terminal.Gui/Views/TextView.cs | 2 +- Terminal.Gui/Views/TileView.cs | 2 +- Terminal.Gui/Views/TimeField.cs | 2 +- Terminal.Gui/Views/TreeView/TreeView.cs | 2 +- Terminal.Gui/Views/Wizard/Wizard.cs | 2 +- UICatalog/Scenarios/LineDrawing.cs | 4 +- UICatalog/Scenarios/Snake.cs | 2 +- UICatalog/Scenarios/VkeyPacketSimulator.cs | 2 +- UnitTests/FileServices/FileDialogTests.cs | 10 +- UnitTests/Input/ResponderTests.cs | 10 +- UnitTests/View/KeyboardEventTests.cs | 4 +- UnitTests/View/ViewTests.cs | 8 +- UnitTests/Views/ScrollViewTests.cs | 130 ++++++++++----------- 22 files changed, 159 insertions(+), 137 deletions(-) diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 37f2cf2c08..3170793581 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -268,63 +268,84 @@ public bool NewKeyDownEvent (Key key) return false; } - // By default the KeyBindingScope is View - + // If there's a Focused subview, give it a chance (this recurses down the hierarchy) if (Focused?.NewKeyDownEvent (key) == true) { return true; } + // Before (fire the cancellable event) if (RaiseKeyDown (key) || key.Handled) { return true; } // During (this is what can be cancelled) - InvokingKeyBindings?.Invoke (this, key); - if (key.Handled) + if (RaiseInvokingKeyBindings (key) || key.Handled) { return true; } - // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommand can be reflected up stack - - bool? handled = OnInvokingKeyBindings (key, KeyBindingScope.HotKey | KeyBindingScope.Focused); - - if (handled is { } && (bool)handled) + if (RaiseProcessKeyDown(key) || key.Handled) { return true; } - // TODO: The below is not right. OnXXX handlers are supposed to fire the events. - // TODO: But I've moved it outside of the v-function to test something. - // After (fire the cancellable event) - // fire event - ProcessKeyDown?.Invoke (this, key); + return key.Handled; - if (!key.Handled && OnProcessKeyDown (key)) + bool RaiseKeyDown (Key key) { - return true; - } + // Before (fire the cancellable event) + if (OnKeyDown (key) || key.Handled) + { + return true; + } - return key.Handled; - } + // fire event + KeyDown?.Invoke (this, key); - private bool RaiseKeyDown (Key key) - { - // Before (fire the cancellable event) - if (OnKeyDown (key) || key.Handled) + return key.Handled; + } + + bool RaiseInvokingKeyBindings (Key key) { - return true; + // BUGBUG: The proper pattern is for the v-method (OnInvokingKeyBindings) to be called first, then the event + InvokingKeyBindings?.Invoke (this, key); + + if (key.Handled) + { + return true; + } + + // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommand can be reflected up stack + + bool? handled = OnInvokingKeyBindings (key, KeyBindingScope.HotKey | KeyBindingScope.Focused); + + if (handled is { } && (bool)handled) + { + return true; + } + + return false; } - // fire event - KeyDown?.Invoke (this, key); + bool RaiseProcessKeyDown (Key key) + { + // BUGBUG: The proper pattern is for the v-method (OnProcessKeyDown) to be called first, then the event + ProcessKeyDown?.Invoke (this, key); - return key.Handled; + if (!key.Handled && OnProcessKeyDown (key)) + { + return true; + } + + return false; + } } + + /// /// Low-level API called when the user presses a key, allowing a view to pre-process the key down event. This is /// called from before . @@ -341,7 +362,7 @@ private bool RaiseKeyDown (Key key) /// /// Fires the event. /// - public virtual bool OnKeyDown (Key keyEvent) + protected virtual bool OnKeyDown (Key keyEvent) { return false; } @@ -384,9 +405,8 @@ public virtual bool OnKeyDown (Key keyEvent) /// KeyUp events. /// /// - public virtual bool OnProcessKeyDown (Key keyEvent) + protected virtual bool OnProcessKeyDown (Key keyEvent) { - //ProcessKeyDown?.Invoke (this, keyEvent); return keyEvent.Handled; } @@ -446,20 +466,20 @@ public bool NewKeyUpEvent (Key key) } return false; - } - private bool RaiseKeyUp (Key key) - { - // Before (fire the cancellable event) - if (OnKeyUp (key) || key.Handled) + bool RaiseKeyUp (Key key) { - return true; - } + // Before (fire the cancellable event) + if (OnKeyUp (key) || key.Handled) + { + return true; + } - // fire event - KeyUp?.Invoke (this, key); + // fire event + KeyUp?.Invoke (this, key); - return key.Handled; + return key.Handled; + } } diff --git a/Terminal.Gui/Views/DateField.cs b/Terminal.Gui/Views/DateField.cs index 2e07f0afa8..43060c0a57 100644 --- a/Terminal.Gui/Views/DateField.cs +++ b/Terminal.Gui/Views/DateField.cs @@ -131,7 +131,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) public virtual void OnDateChanged (DateTimeEventArgs args) { DateChanged?.Invoke (this, args); } /// - public override bool OnProcessKeyDown (Key a) + protected override bool OnProcessKeyDown (Key a) { // Ignore non-numeric characters. if (a >= Key.D0 && a <= Key.D9) diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs index d0022fea00..78ed5b584a 100644 --- a/Terminal.Gui/Views/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialog.cs @@ -233,7 +233,7 @@ internal FileDialog (IFileSystem fileSystem) _tbPath.TextChanged += (s, e) => PathChanged (); _tableView.CellActivated += CellActivate; - _tableView.KeyUp += (s, k) => k.Handled = TableView_KeyUp (k); + _tableView.KeyDown += (s, k) => k.Handled = TableView_KeyUp (k); _tableView.SelectedCellChanged += TableView_SelectedCellChanged; _tableView.KeyBindings.ReplaceCommands (Key.Home, Command.Start); diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index 16a3637559..8f7e3c1a06 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/Terminal.Gui/Views/HexView.cs @@ -452,7 +452,7 @@ void SetAttribute (Attribute attribute) public virtual void OnPositionChanged () { PositionChanged?.Invoke (this, new HexViewEventArgs (Position, CursorPosition, BytesPerLine)); } /// - public override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnProcessKeyDown (Key keyEvent) { if (!AllowEdits) { diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index fddd0482ff..f7e65f843b 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -803,7 +803,7 @@ public bool OnOpenSelectedItem () } /// - public override bool OnKeyDown (Key a) + protected override bool OnKeyDown (Key a) { // Enable user to find & select an item by typing text if (CollectionNavigatorBase.IsCompatibleKey (a) && (!AllowsMarking && a == Key.Space)) diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 576973ed68..6a2c6ffbe9 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -388,7 +388,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - public override bool OnKeyDown (Key a) + protected override bool OnKeyDown (Key a) { if (base.OnKeyDown (a)) { diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index 8e90bd42dd..5da0b11e3e 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -988,7 +988,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - public override bool OnKeyDown (Key key) + protected override bool OnKeyDown (Key key) { if (TableIsNullOrInvisible ()) { diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index c9d6818672..984f151f46 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -1055,7 +1055,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, View previousFocuse /// /// /// - public override bool OnProcessKeyDown (Key a) + protected override bool OnProcessKeyDown (Key a) { // Remember the cursor position because the new calculated cursor position is needed // to be set BEFORE the TextChanged event is triggered. diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs index a6c740d1b7..579b22f562 100644 --- a/Terminal.Gui/Views/TextValidateField.cs +++ b/Terminal.Gui/Views/TextValidateField.cs @@ -597,7 +597,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - public override bool OnProcessKeyDown (Key a) + protected override bool OnProcessKeyDown (Key a) { if (_provider is null) { diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index a539aadf3a..f5436ea963 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3703,7 +3703,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocus } /// - public override bool OnProcessKeyDown (Key a) + protected override bool OnProcessKeyDown (Key a) { if (!CanFocus) { diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index b6e57dd903..4adbb5b1b8 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -286,7 +286,7 @@ public override void OnDrawContent (Rectangle viewport) //// BUGBUG: Why is this not handled by a key binding??? /// - public override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnProcessKeyDown (Key keyEvent) { var focusMoved = false; diff --git a/Terminal.Gui/Views/TimeField.cs b/Terminal.Gui/Views/TimeField.cs index b198d66a00..305a3ce597 100644 --- a/Terminal.Gui/Views/TimeField.cs +++ b/Terminal.Gui/Views/TimeField.cs @@ -177,7 +177,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) } /// - public override bool OnProcessKeyDown (Key a) + protected override bool OnProcessKeyDown (Key a) { // Ignore non-numeric characters. if (a.KeyCode is >= (KeyCode)(int)KeyCode.D0 and <= (KeyCode)(int)KeyCode.D9) diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 1450a57ba1..59fe8e04fe 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -1182,7 +1182,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View cu } /// - public override bool OnKeyDown (Key keyEvent) + protected override bool OnKeyDown (Key keyEvent) { if (!Enabled) { diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index 6c9fe55c12..338328b02d 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -386,7 +386,7 @@ public bool GoToStep (WizardStep newStep) /// /// /// - public override bool OnProcessKeyDown (Key key) + protected override bool OnProcessKeyDown (Key key) { //// BUGBUG: Why is this not handled by a key binding??? if (!Modal) diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs index 5b2887c590..d2fdb78007 100644 --- a/UICatalog/Scenarios/LineDrawing.cs +++ b/UICatalog/Scenarios/LineDrawing.cs @@ -121,7 +121,7 @@ public override void Main () tools.CurrentColor = canvas.GetNormalColor (); canvas.CurrentAttribute = tools.CurrentColor; - win.KeyDown += (s, e) => { e.Handled = canvas.OnKeyDown (e); }; + win.KeyDown += (s, e) => { e.Handled = canvas.NewKeyDownEvent (e); }; Application.Run (win); win.Dispose (); @@ -290,7 +290,7 @@ public override void OnDrawContentComplete (Rectangle viewport) } //// BUGBUG: Why is this not handled by a key binding??? - public override bool OnKeyDown (Key e) + protected override bool OnKeyDown (Key e) { // BUGBUG: These should be implemented with key bindings if (e.KeyCode == (KeyCode.Z | KeyCode.CtrlMask)) diff --git a/UICatalog/Scenarios/Snake.cs b/UICatalog/Scenarios/Snake.cs index 0a16aed953..998969d8da 100644 --- a/UICatalog/Scenarios/Snake.cs +++ b/UICatalog/Scenarios/Snake.cs @@ -357,7 +357,7 @@ public override void OnDrawContent (Rectangle viewport) } // BUGBUG: Should (can) this use key bindings instead. - public override bool OnKeyDown (Key keyEvent) + protected override bool OnKeyDown (Key keyEvent) { if (keyEvent.KeyCode == KeyCode.CursorUp) { diff --git a/UICatalog/Scenarios/VkeyPacketSimulator.cs b/UICatalog/Scenarios/VkeyPacketSimulator.cs index 94a8618fe5..6bea34c910 100644 --- a/UICatalog/Scenarios/VkeyPacketSimulator.cs +++ b/UICatalog/Scenarios/VkeyPacketSimulator.cs @@ -112,7 +112,7 @@ public override void Main () if (handled == null || handled == false) { - if (!tvOutput.OnProcessKeyDown (e)) + if (!tvOutput.NewKeyDownEvent (e)) { Application.Invoke ( () => MessageBox.Query ( diff --git a/UnitTests/FileServices/FileDialogTests.cs b/UnitTests/FileServices/FileDialogTests.cs index f446f5be9d..28e81ef602 100644 --- a/UnitTests/FileServices/FileDialogTests.cs +++ b/UnitTests/FileServices/FileDialogTests.cs @@ -140,7 +140,7 @@ public void DotDot_MovesToRoot_ThenPressBack () AssertIsTheStartingDirectory (dlg.Path); Assert.IsType (dlg.MostFocused); - Send ('v', ConsoleKey.DownArrow); + Application.OnKeyDown (Key.CursorDown); var tv = GetTableView(dlg); tv.SetFocus (); @@ -152,15 +152,17 @@ public void DotDot_MovesToRoot_ThenPressBack () AssertIsTheStartingDirectory (dlg.Path); // Accept navigation up a directory - Send ('\n', ConsoleKey.Enter); + Application.OnKeyDown (Key.Enter); AssertIsTheRootDirectory (dlg.Path); Assert.True (dlg.Canceled); Assert.False (selected); - // Now press the back button (in table view) - Send ('<', ConsoleKey.Backspace); + Assert.IsType (dlg.MostFocused); + + // Now press Backspace (in table view) + Application.OnKeyDown (Key.Backspace); // Should move us back to the root AssertIsTheStartingDirectory (dlg.Path); diff --git a/UnitTests/Input/ResponderTests.cs b/UnitTests/Input/ResponderTests.cs index cd02fa0ed5..cac5c549ef 100644 --- a/UnitTests/Input/ResponderTests.cs +++ b/UnitTests/Input/ResponderTests.cs @@ -205,11 +205,11 @@ public void KeyPressed_Handled_True_Cancels_KeyPress () var r = new View (); var args = new Key { KeyCode = KeyCode.Null }; - Assert.False (r.OnKeyDown (args)); + Assert.False (r.NewKeyDownEvent (args)); Assert.False (args.Handled); r.KeyDown += (s, a) => a.Handled = true; - Assert.True (r.OnKeyDown (args)); + Assert.True (r.NewKeyDownEvent (args)); Assert.True (args.Handled); r.Dispose (); @@ -232,8 +232,8 @@ public void New_Methods_Return_False () var r = new View (); //Assert.False (r.OnKeyDown (new KeyEventArgs () { Key = Key.Unknown })); - Assert.False (r.OnKeyDown (new Key { KeyCode = KeyCode.Null })); - Assert.False (r.OnKeyUp (new Key { KeyCode = KeyCode.Null })); + Assert.False (r.NewKeyDownEvent (new Key { KeyCode = KeyCode.Null })); + Assert.False (r.NewKeyDownEvent (new Key { KeyCode = KeyCode.Null })); Assert.False (r.NewMouseEvent (new MouseEvent { Flags = MouseFlags.AllEvents })); var v = new View (); @@ -293,6 +293,6 @@ public void Responder_Not_Notifying_Dispose () public class DerivedView : View { - public override bool OnKeyDown (Key keyEvent) { return true; } + protected override bool OnKeyDown (Key keyEvent) { return true; } } } diff --git a/UnitTests/View/KeyboardEventTests.cs b/UnitTests/View/KeyboardEventTests.cs index 70965a8da1..d60e4ec354 100644 --- a/UnitTests/View/KeyboardEventTests.cs +++ b/UnitTests/View/KeyboardEventTests.cs @@ -463,7 +463,7 @@ public class OnKeyTestView : View return CancelVirtualMethods; } - public override bool OnKeyDown (Key keyEvent) + protected override bool OnKeyDown (Key keyEvent) { OnKeyDownCalled = true; @@ -477,7 +477,7 @@ public override bool OnKeyUp (Key keyEvent) return CancelVirtualMethods; } - public override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnProcessKeyDown (Key keyEvent) { if (base.OnProcessKeyDown (keyEvent)) { diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index 6f1392a460..54b9b31b55 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -870,10 +870,10 @@ public void New_Methods_Return_False () { var r = new View (); - Assert.False (r.OnKeyDown (new () { KeyCode = KeyCode.Null })); + Assert.False (r.NewKeyDownEvent (Key.Empty)); //Assert.False (r.OnKeyDown (new KeyEventArgs () { Key = Key.Unknown })); - Assert.False (r.OnKeyUp (new () { KeyCode = KeyCode.Null })); + Assert.False (r.NewKeyUpEvent (Key.Empty)); Assert.False (r.NewMouseEvent (new () { Flags = MouseFlags.AllEvents })); r.Dispose (); @@ -1132,7 +1132,7 @@ public override void OnDrawContent (Rectangle viewport) ClearNeedsDisplay (); } - public override bool OnKeyDown (Key keyEvent) + protected override bool OnKeyDown (Key keyEvent) { IsKeyDown = true; @@ -1146,7 +1146,7 @@ public override bool OnKeyUp (Key keyEvent) return true; } - public override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnProcessKeyDown (Key keyEvent) { IsKeyPress = true; diff --git a/UnitTests/Views/ScrollViewTests.cs b/UnitTests/Views/ScrollViewTests.cs index fd207e28fe..b179cd0ca1 100644 --- a/UnitTests/Views/ScrollViewTests.cs +++ b/UnitTests/Views/ScrollViewTests.cs @@ -579,7 +579,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorRight)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorRight)); top.Draw (); expected = @" @@ -603,7 +603,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorRight)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorRight)); top.Draw (); expected = @" @@ -627,7 +627,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorRight)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorRight)); top.Draw (); expected = @" @@ -651,7 +651,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorRight)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorRight)); top.Draw (); expected = @" @@ -675,7 +675,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorRight)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorRight)); top.Draw (); expected = @" @@ -699,7 +699,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorRight)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorRight)); top.Draw (); expected = @" @@ -723,7 +723,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorRight)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorRight)); top.Draw (); expected = @" @@ -746,7 +746,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.End.WithCtrl)); + Assert.True (scrollView.NewKeyDownEvent (Key.End.WithCtrl)); top.Draw (); expected = @" @@ -769,8 +769,8 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.Home.WithCtrl)); - Assert.True (scrollView.OnKeyDown (Key.CursorDown)); + Assert.True (scrollView.NewKeyDownEvent (Key.Home.WithCtrl)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorDown)); top.Draw (); expected = @" @@ -793,7 +793,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorDown)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorDown)); top.Draw (); expected = @" @@ -816,7 +816,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.CursorDown)); + Assert.True (scrollView.NewKeyDownEvent (Key.CursorDown)); top.Draw (); expected = @" @@ -839,7 +839,7 @@ public void DrawTextFormatter_Respects_The_Clip_Bounds () pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); Assert.Equal (new (1, 1, 21, 14), pos); - Assert.True (scrollView.OnKeyDown (Key.End)); + Assert.True (scrollView.NewKeyDownEvent (Key.End)); top.Draw (); expected = @" @@ -940,120 +940,120 @@ public void KeyBindings_Command () Assert.True (sv.KeepContentAlwaysInViewport); Assert.True (sv.AutoHideScrollBars); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorUp)); + Assert.False (sv.NewKeyDownEvent (Key.CursorUp)); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorDown)); + Assert.True (sv.NewKeyDownEvent (Key.CursorDown)); Assert.Equal (new (0, -1), sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorUp)); + Assert.True (sv.NewKeyDownEvent (Key.CursorUp)); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.PageUp)); + Assert.False (sv.NewKeyDownEvent (Key.PageUp)); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.PageDown)); + Assert.True (sv.NewKeyDownEvent (Key.PageDown)); Point point0xMinus10 = new (0, -10); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.PageDown)); + Assert.False (sv.NewKeyDownEvent (Key.PageDown)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorDown)); + Assert.False (sv.NewKeyDownEvent (Key.CursorDown)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.V.WithAlt)); + Assert.True (sv.NewKeyDownEvent (Key.V.WithAlt)); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.V.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.V.WithCtrl)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorLeft)); + Assert.False (sv.NewKeyDownEvent (Key.CursorLeft)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorRight)); + Assert.True (sv.NewKeyDownEvent (Key.CursorRight)); Assert.Equal (new (-1, -10), sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorLeft)); + Assert.True (sv.NewKeyDownEvent (Key.CursorLeft)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.PageUp.WithCtrl)); + Assert.False (sv.NewKeyDownEvent (Key.PageUp.WithCtrl)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.PageDown.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.PageDown.WithCtrl)); Point pointMinus20xMinus10 = new (-20, -10); Assert.Equal (pointMinus20xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorRight)); + Assert.False (sv.NewKeyDownEvent (Key.CursorRight)); Assert.Equal (pointMinus20xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.Home)); + Assert.True (sv.NewKeyDownEvent (Key.Home)); Point pointMinus20x0 = new (-20, 0); Assert.Equal (pointMinus20x0, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.Home)); + Assert.False (sv.NewKeyDownEvent (Key.Home)); Assert.Equal (pointMinus20x0, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.End)); + Assert.True (sv.NewKeyDownEvent (Key.End)); Assert.Equal (pointMinus20xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.End)); + Assert.False (sv.NewKeyDownEvent (Key.End)); Assert.Equal (pointMinus20xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.Home.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.Home.WithCtrl)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.Home.WithCtrl)); + Assert.False (sv.NewKeyDownEvent (Key.Home.WithCtrl)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.End.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.End.WithCtrl)); Assert.Equal (pointMinus20xMinus10, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.End.WithCtrl)); + Assert.False (sv.NewKeyDownEvent (Key.End.WithCtrl)); Assert.Equal (pointMinus20xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.Home)); + Assert.True (sv.NewKeyDownEvent (Key.Home)); Assert.Equal (pointMinus20x0, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.Home.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.Home.WithCtrl)); Assert.Equal (Point.Empty, sv.ContentOffset); sv.KeepContentAlwaysInViewport = false; Assert.False (sv.KeepContentAlwaysInViewport); Assert.True (sv.AutoHideScrollBars); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorUp)); + Assert.False (sv.NewKeyDownEvent (Key.CursorUp)); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorDown)); + Assert.True (sv.NewKeyDownEvent (Key.CursorDown)); Assert.Equal (new (0, -1), sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorUp)); + Assert.True (sv.NewKeyDownEvent (Key.CursorUp)); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.PageUp)); + Assert.False (sv.NewKeyDownEvent (Key.PageUp)); Assert.Equal (Point.Empty, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.PageDown)); + Assert.True (sv.NewKeyDownEvent (Key.PageDown)); Assert.Equal (point0xMinus10, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.PageDown)); + Assert.True (sv.NewKeyDownEvent (Key.PageDown)); Point point0xMinus19 = new (0, -19); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.PageDown)); + Assert.False (sv.NewKeyDownEvent (Key.PageDown)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorDown)); + Assert.False (sv.NewKeyDownEvent (Key.CursorDown)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.V.WithAlt)); + Assert.True (sv.NewKeyDownEvent (Key.V.WithAlt)); Assert.Equal (new (0, -9), sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.V.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.V.WithCtrl)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorLeft)); + Assert.False (sv.NewKeyDownEvent (Key.CursorLeft)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorRight)); + Assert.True (sv.NewKeyDownEvent (Key.CursorRight)); Assert.Equal (new (-1, -19), sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.CursorLeft)); + Assert.True (sv.NewKeyDownEvent (Key.CursorLeft)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.PageUp.WithCtrl)); + Assert.False (sv.NewKeyDownEvent (Key.PageUp.WithCtrl)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.PageDown.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.PageDown.WithCtrl)); Assert.Equal (new (-20, -19), sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.PageDown.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.PageDown.WithCtrl)); Point pointMinus39xMinus19 = new (-39, -19); Assert.Equal (pointMinus39xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.PageDown.WithCtrl)); + Assert.False (sv.NewKeyDownEvent (Key.PageDown.WithCtrl)); Assert.Equal (pointMinus39xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.CursorRight)); + Assert.False (sv.NewKeyDownEvent (Key.CursorRight)); Assert.Equal (pointMinus39xMinus19, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.PageUp.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.PageUp.WithCtrl)); var pointMinus19xMinus19 = new Point (-19, -19); Assert.Equal (pointMinus19xMinus19, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.Home)); + Assert.True (sv.NewKeyDownEvent (Key.Home)); Assert.Equal (new (-19, 0), sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.Home)); + Assert.False (sv.NewKeyDownEvent (Key.Home)); Assert.Equal (new (-19, 0), sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.End)); + Assert.True (sv.NewKeyDownEvent (Key.End)); Assert.Equal (pointMinus19xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.End)); + Assert.False (sv.NewKeyDownEvent (Key.End)); Assert.Equal (pointMinus19xMinus19, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.Home.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.Home.WithCtrl)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.Home.WithCtrl)); + Assert.False (sv.NewKeyDownEvent (Key.Home.WithCtrl)); Assert.Equal (point0xMinus19, sv.ContentOffset); - Assert.True (sv.OnKeyDown (Key.End.WithCtrl)); + Assert.True (sv.NewKeyDownEvent (Key.End.WithCtrl)); Assert.Equal (pointMinus39xMinus19, sv.ContentOffset); - Assert.False (sv.OnKeyDown (Key.End.WithCtrl)); + Assert.False (sv.NewKeyDownEvent (Key.End.WithCtrl)); Assert.Equal (pointMinus39xMinus19, sv.ContentOffset); } From d6852cbbdc7ea07e33f3a4509d27516ed42a8e40 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 13 Oct 2024 09:39:50 -0600 Subject: [PATCH 04/15] KeyUp/Down api docs --- Terminal.Gui/View/View.Keyboard.cs | 103 +++++++++++++++-------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 3170793581..c3ccd8ca96 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -243,7 +243,8 @@ private void SetHotKeyFromTitle () #region Key Down Event /// - /// If the view is enabled, processes a new key down event and returns if the event was + /// If the view is enabled, raises the related key down events on the view, and returns if the + /// event was /// handled. /// /// @@ -252,10 +253,19 @@ private void SetHotKeyFromTitle () /// first. /// /// - /// If the focused sub view does not handle the key press, this method calls to allow the - /// view to pre-process the key press. If returns , this method then - /// calls to invoke any key bindings. Then, only if no key bindings are - /// handled, will be called allowing the view to process the key press. + /// If the focused sub view does not handle the key press, this method raises / + /// to allow the + /// view to pre-process the key press. If / is not handled + /// / will be raised to invoke any key + /// bindings. + /// Then, only if no key bindings are + /// handled, / will be raised allowing the view to + /// process the key press. + /// + /// + /// Callling this method for a key bound to the view via an Application-scoped keybinding will have no effect. + /// Instead, + /// use . /// /// See for an overview of Terminal.Gui keyboard APIs. /// @@ -287,7 +297,8 @@ public bool NewKeyDownEvent (Key key) return true; } - if (RaiseProcessKeyDown(key) || key.Handled) + // After + if (RaiseProcessKeyDown (key) || key.Handled) { return true; } @@ -344,11 +355,11 @@ bool RaiseProcessKeyDown (Key key) } } - - /// - /// Low-level API called when the user presses a key, allowing a view to pre-process the key down event. This is - /// called from before . + /// Called when the user presses a key, allowing subscribers to pre-process the key down event. Called + /// before and are raised. Set + /// to true to + /// stop the key from being processed by other views. /// /// Contains the details about the key that produced the event. /// @@ -362,14 +373,11 @@ bool RaiseProcessKeyDown (Key key) /// /// Fires the event. /// - protected virtual bool OnKeyDown (Key keyEvent) - { - return false; - } + protected virtual bool OnKeyDown (Key keyEvent) { return false; } /// - /// Invoked when the user presses a key, allowing subscribers to pre-process the key down event. This is fired - /// from before . Set to true to + /// Raised when the user presses a key, allowing subscribers to pre-process the key down event. Raised + /// before and . Set to true to /// stop the key from being processed by other views. /// /// @@ -382,47 +390,39 @@ protected virtual bool OnKeyDown (Key keyEvent) public event EventHandler? KeyDown; /// - /// Low-level API called when the user presses a key, allowing views do things during key down events. This is - /// called from after . + /// Called when the user presses a key, allowing views do things during key down events. This is + /// called after the after are raised. /// - /// Contains the details about the key that produced the event. - /// - /// if the key press was not handled. if the keypress was handled - /// and no other view should see it. - /// /// /// - /// Override to override the behavior of how the base class processes key down - /// events. - /// - /// /// For processing s and commands, use and /// instead. /// - /// Fires the event. /// /// Not all terminals support distinct key up notifications; applications should avoid depending on distinct /// KeyUp events. /// /// - protected virtual bool OnProcessKeyDown (Key keyEvent) - { - return keyEvent.Handled; - } + /// Contains the details about the key that produced the event. + /// + /// if the key press was not handled. if the keypress was handled + /// and no other view should see it. + /// + protected virtual bool OnProcessKeyDown (Key keyEvent) { return keyEvent.Handled; } /// - /// Invoked when the user presses a key, allowing subscribers to do things during key down events. Set + /// Raised when the user presses a key, allowing subscribers to do things during key down events. Set /// to true to stop the key from being processed by other views. Invoked after - /// and before . + /// and . /// /// /// - /// SubViews can use the of their super view override the default behavior of when - /// key bindings are invoked. + /// For processing s and commands, use and + /// instead. /// /// - /// Not all terminals support distinct key up notifications; applications should avoid depending on distinct - /// KeyUp events. + /// SubViews can use the of their super view override the default behavior of when + /// key bindings are invoked. /// /// See for an overview of Terminal.Gui keyboard APIs. /// @@ -433,8 +433,9 @@ protected virtual bool OnProcessKeyDown (Key keyEvent) #region KeyUp Event /// - /// If the view is enabled, processes a new key up event and returns if the event was - /// handled. Called before . + /// If the view is enabled, raises the related key up events on the view, and returns if the + /// event was + /// handled. /// /// /// @@ -446,8 +447,9 @@ protected virtual bool OnProcessKeyDown (Key keyEvent) /// first. /// /// - /// If the focused sub view does not handle the key press, this method calls , which is - /// cancellable. + /// If the focused sub view does not handle the key press, this method raises / + /// to allow the + /// view to pre-process the key press. If /. /// /// See for an overview of Terminal.Gui keyboard APIs. /// @@ -460,11 +462,16 @@ public bool NewKeyUpEvent (Key key) return false; } + // Before if (RaiseKeyUp (key) || key.Handled) { return true; } + // During + + // After + return false; bool RaiseKeyUp (Key key) @@ -482,7 +489,6 @@ bool RaiseKeyUp (Key key) } } - /// Method invoked when a key is released. This method is called from . /// Contains the details about the key that produced the event. /// @@ -498,10 +504,7 @@ bool RaiseKeyUp (Key key) /// /// See for an overview of Terminal.Gui keyboard APIs. /// - public virtual bool OnKeyUp (Key keyEvent) - { - return false; - } + public virtual bool OnKeyUp (Key keyEvent) { return false; } /// /// Invoked when a key is released. Set to true to stop the key up event from being processed @@ -537,7 +540,8 @@ public virtual bool OnKeyUp (Key keyEvent) /// The scope. /// /// if no event was raised; input proessing should continue. - /// if the event was raised and was not handled (or cancelled); input proessing should continue. + /// if the event was raised and was not handled (or cancelled); input proessing should + /// continue. /// if the event was raised and handled (or cancelled); input proessing should stop. /// public virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) @@ -715,7 +719,8 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) /// The scope. /// /// if no command was invoked; input proessing should continue. - /// if at least one command was invoked and was not handled (or cancelled); input proessing should continue. + /// if at least one command was invoked and was not handled (or cancelled); input proessing + /// should continue. /// if at least one command was invoked and handled (or cancelled); input proessing should stop. /// protected bool? InvokeKeyBindings (Key key, KeyBindingScope scope) From ee196fec48327b2fc0651d168ea79e8b505dc347 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 08:41:22 -0600 Subject: [PATCH 05/15] WIP: Keyboard event improvements --- Terminal.Gui/View/View.Keyboard.cs | 139 +++++++++------------ Terminal.Gui/Views/Menu/Menu.cs | 4 +- Terminal.Gui/Views/ScrollView.cs | 2 +- Terminal.Gui/Views/TextField.cs | 2 +- Terminal.Gui/Views/TextView.cs | 2 +- UICatalog/Scenarios/VkeyPacketSimulator.cs | 2 +- UnitTests/View/KeyboardEventTests.cs | 28 ++--- 7 files changed, 79 insertions(+), 100 deletions(-) diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index c3ccd8ca96..c1a28a5b93 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -292,17 +292,18 @@ public bool NewKeyDownEvent (Key key) // During (this is what can be cancelled) - if (RaiseInvokingKeyBindings (key) || key.Handled) + if (RaiseInvokingKeyBindingsAndInvokeCommands (key) is not false || key.Handled) { return true; } - // After if (RaiseProcessKeyDown (key) || key.Handled) { return true; } + // After + return key.Handled; bool RaiseKeyDown (Key key) @@ -319,28 +320,6 @@ bool RaiseKeyDown (Key key) return key.Handled; } - bool RaiseInvokingKeyBindings (Key key) - { - // BUGBUG: The proper pattern is for the v-method (OnInvokingKeyBindings) to be called first, then the event - InvokingKeyBindings?.Invoke (this, key); - - if (key.Handled) - { - return true; - } - - // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommand can be reflected up stack - - bool? handled = OnInvokingKeyBindings (key, KeyBindingScope.HotKey | KeyBindingScope.Focused); - - if (handled is { } && (bool)handled) - { - return true; - } - - return false; - } - bool RaiseProcessKeyDown (Key key) { // BUGBUG: The proper pattern is for the v-method (OnProcessKeyDown) to be called first, then the event @@ -529,70 +508,78 @@ bool RaiseKeyUp (Key key) private Dictionary CommandImplementations { get; } = new (); /// - /// Low-level API called when a user presses a key; invokes any key bindings set on the view. This is called - /// during after has returned. + /// /// - /// - /// Fires the event. - /// See for an overview of Terminal.Gui keyboard APIs. - /// - /// Contains the details about the key that produced the event. - /// The scope. - /// - /// if no event was raised; input proessing should continue. - /// if the event was raised and was not handled (or cancelled); input proessing should - /// continue. - /// if the event was raised and handled (or cancelled); input proessing should stop. - /// - public virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + /// + /// + /// + internal bool? RaiseInvokingKeyBindingsAndInvokeCommands (Key key) { + // Before // fire event only if there's a hotkey binding for the key - if (KeyBindings.TryGet (keyEvent, scope, out KeyBinding kb)) + if (!KeyBindings.TryGet (key, KeyBindingScope.Focused | KeyBindingScope.HotKey, out KeyBinding kb)) { - InvokingKeyBindings?.Invoke (this, keyEvent); + return null; + } - if (keyEvent.Handled) - { - return true; - } + KeyBindingScope scope = kb.Scope; + + // During + // BUGBUG: The proper pattern is for the v-method (OnInvokingKeyBindings) to be called first, then the event + InvokingKeyBindings?.Invoke (this, key); + + if (key.Handled) + { + return true; } + // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommand can be reflected up stac + bool? handled = OnInvokingKeyBindings (key, scope); + + if (handled is { } && (bool)handled) + { + return true; + } + + // After + // * If no key binding was found, `InvokeKeyBindings` returns `null`. // Continue passing the event (return `false` from `OnInvokeKeyBindings`). // * If key bindings were found, but none handled the key (all `Command`s returned `false`), // `InvokeKeyBindings` returns `false`. Continue passing the event (return `false` from `OnInvokeKeyBindings`).. // * If key bindings were found, and any handled the key (at least one `Command` returned `true`), // `InvokeKeyBindings` returns `true`. Continue passing the event (return `false` from `OnInvokeKeyBindings`). - bool? handled = InvokeKeyBindings (keyEvent, scope); + handled = InvokeCommands (key, scope); if (handled is { } && (bool)handled) { // Stop processing if any key binding handled the key. // DO NOT stop processing if there are no matching key bindings or none of the key bindings handled the key - return true; + return handled; } - if (Margin is { } && ProcessAdornmentKeyBindings (Margin, keyEvent, scope, ref handled)) + if (Margin is { } && ProcessAdornmentKeyBindings (Margin, key, scope, ref handled)) { return true; } - if (Padding is { } && ProcessAdornmentKeyBindings (Padding, keyEvent, scope, ref handled)) + if (Padding is { } && ProcessAdornmentKeyBindings (Padding, key, scope, ref handled)) { return true; } - if (Border is { } && ProcessAdornmentKeyBindings (Border, keyEvent, scope, ref handled)) + if (Border is { } && ProcessAdornmentKeyBindings (Border, key, scope, ref handled)) { return true; } - if (ProcessSubViewKeyBindings (keyEvent, scope, ref handled)) + if (ProcessSubViewKeyBindings (key, scope, ref handled)) { return true; } return handled; + } private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, KeyBindingScope scope, ref bool? handled) @@ -705,6 +692,28 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) return false; } + + /// + /// Low-level API called when a user presses a key; invokes any key bindings set on the view. This is called + /// during after has returned. + /// + /// + /// Fires the event. + /// See for an overview of Terminal.Gui keyboard APIs. + /// + /// Contains the details about the key that produced the event. + /// The scope. + /// + /// if no event was raised; input proessing should continue. + /// if the event was raised and was not handled (or cancelled); input proessing should + /// continue. + /// if the event was raised and handled (or cancelled); input proessing should stop. + /// + protected virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + { + return false; + } + /// /// Raised when a key is pressed that may be mapped to a key binding. Set to true to /// stop the key from being processed by other views. @@ -712,7 +721,7 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) public event EventHandler? InvokingKeyBindings; /// - /// Invokes any binding that is registered on this and matches the + /// Invokes the Commands bound to . /// See for an overview of Terminal.Gui keyboard APIs. /// /// The key event passed. @@ -723,7 +732,7 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) /// should continue. /// if at least one command was invoked and handled (or cancelled); input proessing should stop. /// - protected bool? InvokeKeyBindings (Key key, KeyBindingScope scope) + protected bool? InvokeCommands (Key key, KeyBindingScope scope) { bool? toReturn = null; @@ -753,30 +762,6 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) #endif return InvokeCommands (binding.Commands, key, binding); - - foreach (Command command in binding.Commands) - { - if (!CommandImplementations.ContainsKey (command)) - { - throw new NotSupportedException ( - @$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by this View ({GetType ().Name})" - ); - } - - // each command has its own return value - bool? thisReturn = InvokeCommand (command, key, binding); - - // if we haven't got anything yet, the current command result should be used - toReturn ??= thisReturn; - - // if ever see a true then that's what we will return - if (thisReturn ?? false) - { - toReturn = true; - } - } - - return toReturn; } #endregion Key Bindings diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs index 1744251f3c..7d7715a53a 100644 --- a/Terminal.Gui/Views/Menu/Menu.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -306,7 +306,7 @@ private bool ExpandCollapse (MenuItem? menuItem) } /// - public override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + protected override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) { bool? handled = base.OnInvokingKeyBindings (keyEvent, scope); @@ -317,7 +317,7 @@ private bool ExpandCollapse (MenuItem? menuItem) // TODO: Determine if there's a cleaner way to handle this. // This supports the case where the menu bar is a context menu - return _host.OnInvokingKeyBindings (keyEvent, scope); + return _host.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent); } private void Current_TerminalResized (object? sender, SizeChangedEventArgs e) diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 6a2c6ffbe9..9ad1a75f1d 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -395,7 +395,7 @@ protected override bool OnKeyDown (Key a) return true; } - bool? result = InvokeKeyBindings (a, KeyBindingScope.HotKey | KeyBindingScope.Focused); + bool? result = InvokeCommands (a, KeyBindingScope.HotKey | KeyBindingScope.Focused); if (result is { }) { diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 984f151f46..b27cd82e0d 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -1014,7 +1014,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - public override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope) + protected override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope) { // Give autocomplete first opportunity to respond to key presses if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (a)) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index f5436ea963..770e4293b5 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3664,7 +3664,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - public override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope) + protected override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope) { if (!a.IsValid) { diff --git a/UICatalog/Scenarios/VkeyPacketSimulator.cs b/UICatalog/Scenarios/VkeyPacketSimulator.cs index 6bea34c910..059203ea7c 100644 --- a/UICatalog/Scenarios/VkeyPacketSimulator.cs +++ b/UICatalog/Scenarios/VkeyPacketSimulator.cs @@ -108,7 +108,7 @@ public override void Main () if (_outputStarted) { // If the key wasn't handled by the TextView will popup a Dialog with the keys pressed. - bool? handled = tvOutput.OnInvokingKeyBindings (e, KeyBindingScope.HotKey | KeyBindingScope.Focused); + bool? handled = tvOutput.NewKeyDownEvent (e); if (handled == null || handled == false) { diff --git a/UnitTests/View/KeyboardEventTests.cs b/UnitTests/View/KeyboardEventTests.cs index d60e4ec354..fc601590c8 100644 --- a/UnitTests/View/KeyboardEventTests.cs +++ b/UnitTests/View/KeyboardEventTests.cs @@ -224,7 +224,7 @@ public void InvokingKeyBindings_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); - Assert.False (view.OnInvokingKeyBindingsContinued); + Assert.False (view.OnInvokingKeyBindingsCalled); e.Handled = true; invokingKeyBindings = true; }; @@ -244,7 +244,7 @@ public void InvokingKeyBindings_Handled_True_Stops_Processing () Assert.False (keyPressed); Assert.True (view.OnKeyDownCalled); - Assert.False (view.OnInvokingKeyBindingsContinued); + Assert.False (view.OnInvokingKeyBindingsCalled); Assert.False (view.OnKeyPressedContinued); } @@ -272,7 +272,7 @@ public void KeyDown_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); - Assert.False (view.OnInvokingKeyBindingsContinued); + Assert.False (view.OnInvokingKeyBindingsCalled); e.Handled = true; invokingKeyBindings = true; }; @@ -292,7 +292,7 @@ public void KeyDown_Handled_True_Stops_Processing () Assert.False (keyPressed); Assert.True (view.OnKeyDownCalled); - Assert.False (view.OnInvokingKeyBindingsContinued); + Assert.False (view.OnInvokingKeyBindingsCalled); Assert.False (view.OnKeyPressedContinued); } @@ -361,7 +361,7 @@ public void KeyPressed_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); - Assert.False (view.OnInvokingKeyBindingsContinued); + Assert.False (view.OnInvokingKeyBindingsCalled); e.Handled = false; invokingKeyBindings = true; }; @@ -381,7 +381,7 @@ public void KeyPressed_Handled_True_Stops_Processing () Assert.True (keyPressed); Assert.True (view.OnKeyDownCalled); - Assert.True (view.OnInvokingKeyBindingsContinued); + Assert.True (view.OnInvokingKeyBindingsCalled); Assert.False (view.OnKeyPressedContinued); } @@ -408,7 +408,7 @@ public void KeyUp_Handled_True_Stops_Processing () Assert.True (view.OnKeyUpCalled); Assert.False (view.OnKeyDownCalled); - Assert.False (view.OnInvokingKeyBindingsContinued); + Assert.False (view.OnInvokingKeyBindingsCalled); Assert.False (view.OnKeyPressedContinued); } @@ -421,7 +421,7 @@ public void OnInvokingKeyBindings_Returns_Nullable_Properly (bool? toReturn, boo var view = new KeyBindingsTestView (); view.CommandReturns = toReturn; - bool? result = view.OnInvokingKeyBindings (Key.A, KeyBindingScope.HotKey | KeyBindingScope.Focused); + bool? result = view.RaiseInvokingKeyBindingsAndInvokeCommands (Key.A); Assert.Equal (expected, result); } @@ -443,22 +443,16 @@ public class OnKeyTestView : View { public OnKeyTestView () { CanFocus = true; } public bool CancelVirtualMethods { set; private get; } - public bool OnInvokingKeyBindingsContinued { get; set; } + public bool OnInvokingKeyBindingsCalled { get; set; } public bool OnKeyDownCalled { get; set; } public bool OnKeyPressedContinued { get; set; } public bool OnKeyUpCalled { get; set; } public override string Text { get; set; } - public override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + protected override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) { - bool? handled = base.OnInvokingKeyBindings (keyEvent, scope); - if (handled != null && (bool)handled) - { - return true; - } - - OnInvokingKeyBindingsContinued = true; + OnInvokingKeyBindingsCalled = true; return CancelVirtualMethods; } From 6df071fbe1e420efee7ffd2fb626d3b0f624cea4 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 12:32:41 -0600 Subject: [PATCH 06/15] Fixed. Added event.md --- Terminal.Gui/View/View.Keyboard.cs | 66 ++++----- Terminal.Gui/Views/Menu/Menu.cs | 16 +-- Terminal.Gui/Views/TextField.cs | 2 +- Terminal.Gui/Views/TextView.cs | 2 +- .../View/{ => Keyboard}/KeyboardEventTests.cs | 76 +++++------ UnitTests/Views/MenuBarTests.cs | 2 +- docfx/docs/events.md | 126 ++++++++++++++++++ 7 files changed, 205 insertions(+), 85 deletions(-) rename UnitTests/View/{ => Keyboard}/KeyboardEventTests.cs (86%) create mode 100644 docfx/docs/events.md diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index c1a28a5b93..cf7f9cdf1b 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -1,5 +1,6 @@ #nullable enable using System.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis; namespace Terminal.Gui; @@ -292,18 +293,21 @@ public bool NewKeyDownEvent (Key key) // During (this is what can be cancelled) - if (RaiseInvokingKeyBindingsAndInvokeCommands (key) is not false || key.Handled) + // TODO: NewKeyDownEvent returns bool. It should be bool? so state of RaiseInvokingKeyBindingsAndInvokeCommands can be reflected up stack + + + if (RaiseInvokingKeyBindingsAndInvokeCommands (key) is true || key.Handled) { return true; } + // After + // TODO: Is ProcessKeyDown really the right name? if (RaiseProcessKeyDown (key) || key.Handled) { return true; } - // After - return key.Handled; bool RaiseKeyDown (Key key) @@ -322,14 +326,13 @@ bool RaiseKeyDown (Key key) bool RaiseProcessKeyDown (Key key) { - // BUGBUG: The proper pattern is for the v-method (OnProcessKeyDown) to be called first, then the event - ProcessKeyDown?.Invoke (this, key); - - if (!key.Handled && OnProcessKeyDown (key)) + if (OnProcessKeyDown (key) || key.Handled) { return true; } + ProcessKeyDown?.Invoke (this, key); + return false; } } @@ -512,17 +515,22 @@ bool RaiseKeyUp (Key key) /// /// /// - /// + /// + /// if no command was invoked or there was no matching key binding; input processing should continue. + /// if a command was invoked and was not handled (or cancelled); input processing should + /// continue. + /// if was handled or a command was invoked and handled (or cancelled); input processing should stop. + /// internal bool? RaiseInvokingKeyBindingsAndInvokeCommands (Key key) { - // Before - // fire event only if there's a hotkey binding for the key - if (!KeyBindings.TryGet (key, KeyBindingScope.Focused | KeyBindingScope.HotKey, out KeyBinding kb)) - { - return null; - } + //// Before + //// fire event only if there's a hotkey binding for the key + //if (!KeyBindings.TryGet (key, KeyBindingScope.Focused | KeyBindingScope.HotKey, out KeyBinding kb)) + //{ + // return null; + //} - KeyBindingScope scope = kb.Scope; + KeyBindingScope scope = KeyBindingScope.Focused | KeyBindingScope.HotKey; // During // BUGBUG: The proper pattern is for the v-method (OnInvokingKeyBindings) to be called first, then the event @@ -533,14 +541,13 @@ bool RaiseKeyUp (Key key) return true; } - // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommand can be reflected up stac - bool? handled = OnInvokingKeyBindings (key, scope); - - if (handled is { } && (bool)handled) + if (OnInvokingKeyBindings (key, scope)) { return true; } + bool? handled; + // After // * If no key binding was found, `InvokeKeyBindings` returns `null`. @@ -636,7 +643,7 @@ private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref return true; } - bool? subViewHandled = subview.OnInvokingKeyBindings (keyEvent, scope); + bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent); if (subViewHandled is { }) { @@ -694,26 +701,25 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) /// - /// Low-level API called when a user presses a key; invokes any key bindings set on the view. This is called - /// during after has returned. + /// Called when a key is pressed that may be mapped to a key binding. Set to true to + /// stop the key from being processed by other views. /// /// - /// Fires the event. /// See for an overview of Terminal.Gui keyboard APIs. /// /// Contains the details about the key that produced the event. /// The scope. /// - /// if no event was raised; input proessing should continue. - /// if the event was raised and was not handled (or cancelled); input proessing should + /// if the event was raised and was not handled (or cancelled); input processing should /// continue. - /// if the event was raised and handled (or cancelled); input proessing should stop. + /// if the event was raised and handled (or cancelled); input processing should stop. /// - protected virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + protected virtual bool OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) { return false; } + // TODO: This does not carry KeyBindingScope, but OnInvokingKeyBindings does /// /// Raised when a key is pressed that may be mapped to a key binding. Set to true to /// stop the key from being processed by other views. @@ -727,10 +733,10 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) /// The key event passed. /// The scope. /// - /// if no command was invoked; input proessing should continue. - /// if at least one command was invoked and was not handled (or cancelled); input proessing + /// if no command was invoked; input processing should continue. + /// if at least one command was invoked and was not handled (or cancelled); input processing /// should continue. - /// if at least one command was invoked and handled (or cancelled); input proessing should stop. + /// if at least one command was invoked and handled (or cancelled); input processing should stop. /// protected bool? InvokeCommands (Key key, KeyBindingScope scope) { diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs index 7d7715a53a..9ee580d49d 100644 --- a/Terminal.Gui/Views/Menu/Menu.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -305,19 +305,11 @@ private bool ExpandCollapse (MenuItem? menuItem) return true; } - /// - protected override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + /// + protected override bool OnProcessKeyDown (Key keyEvent) { - bool? handled = base.OnInvokingKeyBindings (keyEvent, scope); - - if (handled is { } && (bool)handled) - { - return true; - } - - // TODO: Determine if there's a cleaner way to handle this. - // This supports the case where the menu bar is a context menu - return _host.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent); + // We didn't handle the key, pass it on to host + return _host.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent) == true; } private void Current_TerminalResized (object? sender, SizeChangedEventArgs e) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index b27cd82e0d..d1cfed3b1b 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -1014,7 +1014,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - protected override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope) + protected override bool OnInvokingKeyBindings (Key a, KeyBindingScope scope) { // Give autocomplete first opportunity to respond to key presses if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (a)) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 770e4293b5..4cc86bf2f9 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3664,7 +3664,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - protected override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope) + protected override bool OnInvokingKeyBindings (Key a, KeyBindingScope scope) { if (!a.IsValid) { diff --git a/UnitTests/View/KeyboardEventTests.cs b/UnitTests/View/Keyboard/KeyboardEventTests.cs similarity index 86% rename from UnitTests/View/KeyboardEventTests.cs rename to UnitTests/View/Keyboard/KeyboardEventTests.cs index fc601590c8..ae597a5962 100644 --- a/UnitTests/View/KeyboardEventTests.cs +++ b/UnitTests/View/Keyboard/KeyboardEventTests.cs @@ -12,7 +12,7 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews /// [Theory] [MemberData (nameof (AllViewTypes))] - public void AllViews_KeyDown_All_EventsFire (Type viewType) + public void AllViews_NewKeyDownEvent_All_EventsFire (Type viewType) { var view = CreateInstanceIfNotGeneric (viewType); @@ -49,7 +49,8 @@ public void AllViews_KeyDown_All_EventsFire (Type viewType) keyDownProcessed = true; }; - Assert.True (view.NewKeyDownEvent (Key.A)); // this will be true because the ProcessKeyDown event handled it + // Key.Empty is invalid, but it's used here to test that the event is fired + Assert.True (view.NewKeyDownEvent (Key.Empty)); // this will be true because the ProcessKeyDown event handled it Assert.True (keyDown); Assert.True (invokingKeyBindings); Assert.True (keyDownProcessed); @@ -62,7 +63,7 @@ public void AllViews_KeyDown_All_EventsFire (Type viewType) /// [Theory] [MemberData (nameof (AllViewTypes))] - public void AllViews_KeyUp_All_EventsFire (Type viewType) + public void AllViews_NewKeyUpEvent_All_EventsFire (Type viewType) { var view = CreateInstanceIfNotGeneric (viewType); @@ -92,13 +93,13 @@ public void AllViews_KeyUp_All_EventsFire (Type viewType) [InlineData (true, false, false)] [InlineData (true, true, false)] [InlineData (true, true, true)] - public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, bool control) + public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool shift, bool alt, bool control) { var keyDown = false; var keyPressed = false; var keyUp = false; - var view = new OnKeyTestView (); + var view = new OnNewKeyTestView (); view.CancelVirtualMethods = false; view.KeyDown += (s, e) => @@ -139,7 +140,7 @@ public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, boo ); Assert.True (keyPressed); Assert.True (view.OnKeyDownCalled); - Assert.True (view.OnKeyPressedContinued); + Assert.True (view.OnProcessKeyDownCalled); view.NewKeyUpEvent ( new ( @@ -154,7 +155,7 @@ public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, boo } [Fact] - public void InvokingKeyBindings_Handled_Cancels () + public void NewKeyDownEvent_InvokingKeyBindings_Handled_Cancels () { var view = new View (); var keyPressInvoked = false; @@ -201,13 +202,13 @@ public void InvokingKeyBindings_Handled_Cancels () } [Fact] - public void InvokingKeyBindings_Handled_True_Stops_Processing () + public void NewKeyDownEvent_InvokingKeyBindings_Handled_True_Stops_Processing () { var keyDown = false; var invokingKeyBindings = false; var keyPressed = false; - var view = new OnKeyTestView (); + var view = new OnNewKeyTestView (); Assert.True (view.CanFocus); view.CancelVirtualMethods = false; @@ -233,7 +234,7 @@ public void InvokingKeyBindings_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); - Assert.False (view.OnKeyPressedContinued); + Assert.False (view.OnProcessKeyDownCalled); e.Handled = true; keyPressed = true; }; @@ -245,17 +246,17 @@ public void InvokingKeyBindings_Handled_True_Stops_Processing () Assert.True (view.OnKeyDownCalled); Assert.False (view.OnInvokingKeyBindingsCalled); - Assert.False (view.OnKeyPressedContinued); + Assert.False (view.OnProcessKeyDownCalled); } [Fact] - public void KeyDown_Handled_True_Stops_Processing () + public void NewKeyDownEvent_Handled_True_Stops_Processing () { var keyDown = false; var invokingKeyBindings = false; var keyPressed = false; - var view = new OnKeyTestView (); + var view = new OnNewKeyTestView (); Assert.True (view.CanFocus); view.CancelVirtualMethods = false; @@ -281,7 +282,7 @@ public void KeyDown_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); - Assert.False (view.OnKeyPressedContinued); + Assert.False (view.OnProcessKeyDownCalled); e.Handled = true; keyPressed = true; }; @@ -293,11 +294,11 @@ public void KeyDown_Handled_True_Stops_Processing () Assert.True (view.OnKeyDownCalled); Assert.False (view.OnInvokingKeyBindingsCalled); - Assert.False (view.OnKeyPressedContinued); + Assert.False (view.OnProcessKeyDownCalled); } [Fact] - public void KeyPress_Handled_Cancels () + public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing () { var view = new View (); var invokingKeyBindingsInvoked = false; @@ -338,13 +339,13 @@ public void KeyPress_Handled_Cancels () } [Fact] - public void KeyPressed_Handled_True_Stops_Processing () + public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing () { var keyDown = false; var invokingKeyBindings = false; - var keyPressed = false; + var processKeyDown = false; - var view = new OnKeyTestView (); + var view = new OnNewKeyTestView (); Assert.True (view.CanFocus); view.CancelVirtualMethods = false; @@ -360,7 +361,7 @@ public void KeyPressed_Handled_True_Stops_Processing () view.InvokingKeyBindings += (s, e) => { Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (keyPressed); + Assert.False (processKeyDown); Assert.False (view.OnInvokingKeyBindingsCalled); e.Handled = false; invokingKeyBindings = true; @@ -369,28 +370,28 @@ public void KeyPressed_Handled_True_Stops_Processing () view.ProcessKeyDown += (s, e) => { Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (keyPressed); - Assert.False (view.OnKeyPressedContinued); + Assert.False (processKeyDown); + Assert.True (view.OnProcessKeyDownCalled); e.Handled = true; - keyPressed = true; + processKeyDown = true; }; view.NewKeyDownEvent (Key.A); Assert.True (keyDown); Assert.True (invokingKeyBindings); - Assert.True (keyPressed); + Assert.True (processKeyDown); Assert.True (view.OnKeyDownCalled); Assert.True (view.OnInvokingKeyBindingsCalled); - Assert.False (view.OnKeyPressedContinued); + Assert.True (view.OnProcessKeyDownCalled); } [Fact] - public void KeyUp_Handled_True_Stops_Processing () + public void NewKeyUpEvent_KeyUp_Handled_True_Stops_Processing () { var keyUp = false; - var view = new OnKeyTestView (); + var view = new OnNewKeyTestView (); Assert.True (view.CanFocus); view.CancelVirtualMethods = false; @@ -398,7 +399,7 @@ public void KeyUp_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyUp); - Assert.False (view.OnKeyPressedContinued); + Assert.False (view.OnProcessKeyDownCalled); e.Handled = true; keyUp = true; }; @@ -409,14 +410,14 @@ public void KeyUp_Handled_True_Stops_Processing () Assert.True (view.OnKeyUpCalled); Assert.False (view.OnKeyDownCalled); Assert.False (view.OnInvokingKeyBindingsCalled); - Assert.False (view.OnKeyPressedContinued); + Assert.False (view.OnProcessKeyDownCalled); } [Theory] [InlineData (null, null)] [InlineData (true, true)] [InlineData (false, false)] - public void OnInvokingKeyBindings_Returns_Nullable_Properly (bool? toReturn, bool? expected) + public void RaiseInvokingKeyBindingsAndInvokeCommands_Returns_Nullable_Properly (bool? toReturn, bool? expected) { var view = new KeyBindingsTestView (); view.CommandReturns = toReturn; @@ -439,17 +440,17 @@ public KeyBindingsTestView () } /// A view that overrides the OnKey* methods so we can test that they are called. - public class OnKeyTestView : View + public class OnNewKeyTestView : View { - public OnKeyTestView () { CanFocus = true; } + public OnNewKeyTestView () { CanFocus = true; } public bool CancelVirtualMethods { set; private get; } public bool OnInvokingKeyBindingsCalled { get; set; } public bool OnKeyDownCalled { get; set; } - public bool OnKeyPressedContinued { get; set; } + public bool OnProcessKeyDownCalled { get; set; } public bool OnKeyUpCalled { get; set; } public override string Text { get; set; } - protected override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + protected override bool OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) { OnInvokingKeyBindingsCalled = true; @@ -473,12 +474,7 @@ public override bool OnKeyUp (Key keyEvent) protected override bool OnProcessKeyDown (Key keyEvent) { - if (base.OnProcessKeyDown (keyEvent)) - { - return true; - } - - OnKeyPressedContinued = true; + OnProcessKeyDownCalled = true; return CancelVirtualMethods; } diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs index 736e79890a..849d9b8f4a 100644 --- a/UnitTests/Views/MenuBarTests.cs +++ b/UnitTests/Views/MenuBarTests.cs @@ -1384,7 +1384,7 @@ params KeyCode [] keys foreach (Key key in keys) { - top.NewKeyDownEvent (new (key)); + top.NewKeyDownEvent (key); Application.MainLoop.RunIteration (); } diff --git a/docfx/docs/events.md b/docfx/docs/events.md new file mode 100644 index 0000000000..19e853f6a7 --- /dev/null +++ b/docfx/docs/events.md @@ -0,0 +1,126 @@ +# Terminal.Gui Event Deep Dive + +Terminal.Gui exposes and uses events in many places. This deep dive covers the patterns used, where they are used, and notes any exceptions. + +## Tenets for Terminal.Gui Events (Unless you know better ones...) + +Tenets higher in the list have precedence over tenets lower in the list. + +* **UI Interaction and Live Data Are Different Beasts** - TG distinguishes between events used for human interaction and events for live data. We don't believe in a one-size-fits-all eventing model. For UI interactions we use `EventHandler`. For data binding we think `INotifyPropertyChanged` is groovy. For some callbacks we use `Action`. + +## Lexicon and Taxonomy + +* *Action* +* *Event* +* *Command* +* *Invoke* +* *Raise* +* *Listen* +* *Handle/Handling/Handled* - Applies to scenarios where an event can either be handled by an event listener (or override) vs not handled. Events that originate from a user action like mouse moves and key presses are examples. +* *Cancel/Cancelling/Cancelled* - Applies to scenarios where something can be cancelled. Changing the `Orientation` of a `Slider` is cancelable. + +## Useful External Documentation + +* [.NET Naming Guidelines - Names of Events](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members?redirectedfrom=MSDN#names-of-events) +* [.NET Design for Extensibility - Events and Callbacks](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/events-and-callbacks) +* [C# Event Implementation Fundamentals, Best Practices and Conventions](https://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices) + +## Naming + +TG follows the *naming* advice provided in [.NET Naming Guidelines - Names of Events](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members?redirectedfrom=MSDN#names-of-events). + +## `EventHandler` style event best-practices + +* Implement a helper method for raising the event: `RaisexxxEvent`. + * If the event is cancelable, the return type should be either `bool` or `bool?`. + * Can be `private`, `internal`, or `public` depending on the situation. `internal` should only be used to enable unit tests. +* Raising an event involves FIRST calling the `protected virtual` method, THEN invoking the `EventHandler. + +## `Action` style callback best-practices + +- tbd + +## `INotifyPropertyChanged` style notification best practices + +- tbd + +## Common Patterns + +The primary pattern for events is the `event/EventHandler` idiom. We use the `Action` idiom sparingly. We support `INotifyPropertyChanged` in cases where data binding is relevant. + + + +## Cancellable Event Pattern + +A cancellable event is really two events and some activity that takes place between those events. The "pre-event" happens before the activity. The activity then takes place (or not). If the activity takes place, then the "post-event" is typically raised. So, to be precise, no event is being cancelled even though we say we have a cancellable event. Rather, the activity that takes place between the two events is what is cancelled — and likely prevented from starting at all. + +### **Before** - If any pre-conditions are met raise the "pre-event", typically named in the form of "xxxChanging". e.g. + + - A `protected virtual` method is called. This method is named `OnxxxChanging` and the base implementation simply does `return false`. + - If the `OnxxxChanging` method returns `true` it means a derived class canceled the event. Processing should stop. + - Otherwise, the `xxxChanging` event is invoked via `xxxChanging?.Invoke(args)`. If `args.Cancel/Handled == true` it means a subscriber has cancelled the event. Processing should stop. + + +### **During** - Do work. + +### **After** - Raise the "post-event", typically named in the form of "xxxChanged" + + - A `protected virtual` method is called. This method is named `OnxxxChanged` has a return type of `void`. + - The `xxxChanged` event is invoked via `xxxChanging?.Invoke(args)`. + +The `OrientationHelper` class supporting `IOrientation` and a `View` having an `Orientation` property illustrates the preferred TG pattern for cancelable events. + +```cs + /// + /// Gets or sets the orientation of the View. + /// + public Orientation Orientation + { + get => _orientation; + set + { + if (_orientation == value) + { + return; + } + + // Best practice is to invoke the virtual method first. + // This allows derived classes to handle the event and potentially cancel it. + if (_owner?.OnOrientationChanging (value, _orientation) ?? false) + { + return; + } + + // If the event is not canceled by the virtual method, raise the event to notify any external subscribers. + CancelEventArgs args = new (in _orientation, ref value); + OrientationChanging?.Invoke (_owner, args); + + if (args.Cancel) + { + return; + } + + // If the event is not canceled, update the value. + Orientation old = _orientation; + + if (_orientation != value) + { + _orientation = value; + + if (_owner is { }) + { + _owner.Orientation = value; + } + } + + // Best practice is to invoke the virtual method first. + _owner?.OnOrientationChanged (_orientation); + + // Even though Changed is not cancelable, it is still a good practice to raise the event after. + OrientationChanged?.Invoke (_owner, new (in _orientation)); + } + } +``` + + + From 84b271947a205f2ada39fc596e7784f11c90b79a Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 12:43:14 -0600 Subject: [PATCH 07/15] Fixed InvokingKeyBindings event semantics --- .../View/Orientation/OrientationHelper.cs | 6 +- Terminal.Gui/View/View.Keyboard.cs | 89 +++++++---------- UnitTests/View/Keyboard/KeyboardEventTests.cs | 6 +- docfx/docs/events.md | 98 +++++++++---------- 4 files changed, 92 insertions(+), 107 deletions(-) diff --git a/Terminal.Gui/View/Orientation/OrientationHelper.cs b/Terminal.Gui/View/Orientation/OrientationHelper.cs index 33c33c70f3..380dfbb53c 100644 --- a/Terminal.Gui/View/Orientation/OrientationHelper.cs +++ b/Terminal.Gui/View/Orientation/OrientationHelper.cs @@ -69,7 +69,7 @@ public Orientation Orientation return; } - // Best practice is to invoke the virtual method first. + // Best practice is to call the virtual method first. // This allows derived classes to handle the event and potentially cancel it. if (_owner?.OnOrientationChanging (value, _orientation) ?? false) { @@ -98,10 +98,8 @@ public Orientation Orientation } } - // Best practice is to invoke the virtual method first. + // Best practice is to call the virtual method first, then raise the event. _owner?.OnOrientationChanged (_orientation); - - // Even though Changed is not cancelable, it is still a good practice to raise the event after. OrientationChanged?.Invoke (_owner, new (in _orientation)); } } diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index cf7f9cdf1b..e3b2ab2ba0 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -219,7 +219,7 @@ public virtual Rune HotKeySpecifier private void SetHotKeyFromTitle () { - if (TitleTextFormatter == null || HotKeySpecifier == new Rune ('\xFFFF')) + if (HotKeySpecifier == new Rune ('\xFFFF')) { return; // throw new InvalidOperationException ("Can't set HotKey unless a TextFormatter has been created"); } @@ -264,7 +264,7 @@ private void SetHotKeyFromTitle () /// process the key press. /// /// - /// Callling this method for a key bound to the view via an Application-scoped keybinding will have no effect. + /// Calling this method for a key bound to the view via an Application-scoped keybinding will have no effect. /// Instead, /// use . /// @@ -294,8 +294,6 @@ public bool NewKeyDownEvent (Key key) // During (this is what can be cancelled) // TODO: NewKeyDownEvent returns bool. It should be bool? so state of RaiseInvokingKeyBindingsAndInvokeCommands can be reflected up stack - - if (RaiseInvokingKeyBindingsAndInvokeCommands (key) is true || key.Handled) { return true; @@ -310,28 +308,28 @@ public bool NewKeyDownEvent (Key key) return key.Handled; - bool RaiseKeyDown (Key key) + bool RaiseKeyDown (Key k) { // Before (fire the cancellable event) - if (OnKeyDown (key) || key.Handled) + if (OnKeyDown (k) || k.Handled) { return true; } // fire event - KeyDown?.Invoke (this, key); + KeyDown?.Invoke (this, k); - return key.Handled; + return k.Handled; } - bool RaiseProcessKeyDown (Key key) + bool RaiseProcessKeyDown (Key k) { - if (OnProcessKeyDown (key) || key.Handled) + if (OnProcessKeyDown (k) || k.Handled) { return true; } - ProcessKeyDown?.Invoke (this, key); + ProcessKeyDown?.Invoke (this, k); return false; } @@ -343,7 +341,7 @@ bool RaiseProcessKeyDown (Key key) /// to true to /// stop the key from being processed by other views. /// - /// Contains the details about the key that produced the event. + /// Contains the details about the key that produced the event. /// /// if the key press was not handled. if the keypress was handled /// and no other view should see it. @@ -355,7 +353,7 @@ bool RaiseProcessKeyDown (Key key) /// /// Fires the event. /// - protected virtual bool OnKeyDown (Key keyEvent) { return false; } + protected virtual bool OnKeyDown (Key key) { return false; } /// /// Raised when the user presses a key, allowing subscribers to pre-process the key down event. Raised @@ -385,12 +383,12 @@ bool RaiseProcessKeyDown (Key key) /// KeyUp events. /// /// - /// Contains the details about the key that produced the event. + /// Contains the details about the key that produced the event. /// /// if the key press was not handled. if the keypress was handled /// and no other view should see it. /// - protected virtual bool OnProcessKeyDown (Key keyEvent) { return keyEvent.Handled; } + protected virtual bool OnProcessKeyDown (Key key) { return key.Handled; } /// /// Raised when the user presses a key, allowing subscribers to do things during key down events. Set @@ -456,25 +454,25 @@ public bool NewKeyUpEvent (Key key) return false; - bool RaiseKeyUp (Key key) + bool RaiseKeyUp (Key k) { // Before (fire the cancellable event) - if (OnKeyUp (key) || key.Handled) + if (OnKeyUp (k) || k.Handled) { return true; } // fire event - KeyUp?.Invoke (this, key); + KeyUp?.Invoke (this, k); - return key.Handled; + return k.Handled; } } /// Method invoked when a key is released. This method is called from . - /// Contains the details about the key that produced the event. + /// Contains the details about the key that produced the event. /// - /// if the key stroke was not handled. if no other view should see + /// if the keys up event was not handled. if no other view should see /// it. /// /// @@ -486,7 +484,7 @@ bool RaiseKeyUp (Key key) /// /// See for an overview of Terminal.Gui keyboard APIs. /// - public virtual bool OnKeyUp (Key keyEvent) { return false; } + public virtual bool OnKeyUp (Key key) { return false; } /// /// Invoked when a key is released. Set to true to stop the key up event from being processed @@ -514,7 +512,6 @@ bool RaiseKeyUp (Key key) /// /// /// - /// /// /// if no command was invoked or there was no matching key binding; input processing should continue. /// if a command was invoked and was not handled (or cancelled); input processing should @@ -523,25 +520,18 @@ bool RaiseKeyUp (Key key) /// internal bool? RaiseInvokingKeyBindingsAndInvokeCommands (Key key) { - //// Before - //// fire event only if there's a hotkey binding for the key - //if (!KeyBindings.TryGet (key, KeyBindingScope.Focused | KeyBindingScope.HotKey, out KeyBinding kb)) - //{ - // return null; - //} - KeyBindingScope scope = KeyBindingScope.Focused | KeyBindingScope.HotKey; // During - // BUGBUG: The proper pattern is for the v-method (OnInvokingKeyBindings) to be called first, then the event - InvokingKeyBindings?.Invoke (this, key); - - if (key.Handled) + if (OnInvokingKeyBindings (key, scope)) { return true; } - if (OnInvokingKeyBindings (key, scope)) + // BUGBUG: The proper pattern is for the v-method (OnInvokingKeyBindings) to be called first, then the event + InvokingKeyBindings?.Invoke (this, key); + + if (key.Handled) { return true; } @@ -589,9 +579,9 @@ bool RaiseKeyUp (Key key) } - private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, KeyBindingScope scope, ref bool? handled) + private bool ProcessAdornmentKeyBindings (Adornment adornment, Key key, KeyBindingScope scope, ref bool? handled) { - bool? adornmentHandled = adornment.OnInvokingKeyBindings (keyEvent, scope); + bool? adornmentHandled = adornment.OnInvokingKeyBindings (key, scope); if (adornmentHandled is true) { @@ -605,7 +595,7 @@ private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, Key foreach (View subview in adornment.Subviews) { - bool? subViewHandled = subview.OnInvokingKeyBindings (keyEvent, scope); + bool? subViewHandled = subview.OnInvokingKeyBindings (key, scope); if (subViewHandled is { }) { @@ -621,7 +611,7 @@ private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, Key return false; } - private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref bool? handled, bool invoke = true) + private bool ProcessSubViewKeyBindings (Key key, KeyBindingScope scope, ref bool? handled, bool invoke = true) { // Now, process any key bindings in the subviews that are tagged to KeyBindingScope.HotKey. foreach (View subview in Subviews) @@ -631,7 +621,7 @@ private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref continue; } - if (subview.KeyBindings.TryGet (keyEvent, scope, out KeyBinding binding)) + if (subview.KeyBindings.TryGet (key, scope, out KeyBinding binding)) { if (binding.Scope == KeyBindingScope.Focused && !subview.HasFocus) { @@ -643,7 +633,7 @@ private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref return true; } - bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent); + bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (key); if (subViewHandled is { }) { @@ -656,7 +646,7 @@ private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref } } - bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, scope, ref handled, invoke); + bool recurse = subview.ProcessSubViewKeyBindings (key, scope, ref handled, invoke); if (recurse || (handled is { } && (bool)handled)) { @@ -676,7 +666,7 @@ private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref /// The key to test. /// Returns the view the key is bound to. /// - public bool IsHotKeyKeyBound (Key key, out View? boundView) + public bool IsHotKeyBound (Key key, out View? boundView) { // recurse through the subviews to find the views that has the key bound boundView = null; @@ -690,7 +680,7 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) return true; } - if (subview.IsHotKeyKeyBound (key, out boundView)) + if (subview.IsHotKeyBound (key, out boundView)) { return true; } @@ -707,14 +697,14 @@ public bool IsHotKeyKeyBound (Key key, out View? boundView) /// /// See for an overview of Terminal.Gui keyboard APIs. /// - /// Contains the details about the key that produced the event. + /// Contains the details about the key that produced the event. /// The scope. /// /// if the event was raised and was not handled (or cancelled); input processing should /// continue. /// if the event was raised and handled (or cancelled); input processing should stop. /// - protected virtual bool OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) + protected virtual bool OnInvokingKeyBindings (Key key, KeyBindingScope scope) { return false; } @@ -749,19 +739,16 @@ protected virtual bool OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scop #if DEBUG - // TODO: Determine if App scope bindings should be fired first or last (currently last). if (Application.KeyBindings.TryGet (key, KeyBindingScope.Focused | KeyBindingScope.HotKey, out KeyBinding b)) { - //var boundView = views [0]; - //var commandBinding = boundView.KeyBindings.Get (key); Debug.WriteLine ( - $"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command."); //{commandBinding.Commands [0]}: {boundView}."); + $"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command."); } // TODO: This is a "prototype" debug check. It may be too annoying vs. useful. // Scour the bindings up our View hierarchy // to ensure that the key is not already bound to a different set of commands. - if (SuperView?.IsHotKeyKeyBound (key, out View? previouslyBoundView) ?? false) + if (SuperView?.IsHotKeyBound (key, out View? previouslyBoundView) ?? false) { Debug.WriteLine ($"WARNING: InvokeKeyBindings ({key}) - A subview or peer has bound this Key and will not see it: {previouslyBoundView}."); } diff --git a/UnitTests/View/Keyboard/KeyboardEventTests.cs b/UnitTests/View/Keyboard/KeyboardEventTests.cs index ae597a5962..471016231c 100644 --- a/UnitTests/View/Keyboard/KeyboardEventTests.cs +++ b/UnitTests/View/Keyboard/KeyboardEventTests.cs @@ -225,7 +225,7 @@ public void NewKeyDownEvent_InvokingKeyBindings_Handled_True_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); - Assert.False (view.OnInvokingKeyBindingsCalled); + Assert.True (view.OnInvokingKeyBindingsCalled); e.Handled = true; invokingKeyBindings = true; }; @@ -245,7 +245,7 @@ public void NewKeyDownEvent_InvokingKeyBindings_Handled_True_Stops_Processing () Assert.False (keyPressed); Assert.True (view.OnKeyDownCalled); - Assert.False (view.OnInvokingKeyBindingsCalled); + Assert.True (view.OnInvokingKeyBindingsCalled); Assert.False (view.OnProcessKeyDownCalled); } @@ -362,7 +362,7 @@ public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing () { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (processKeyDown); - Assert.False (view.OnInvokingKeyBindingsCalled); + Assert.True (view.OnInvokingKeyBindingsCalled); e.Handled = false; invokingKeyBindings = true; }; diff --git a/docfx/docs/events.md b/docfx/docs/events.md index 19e853f6a7..56580a7a45 100644 --- a/docfx/docs/events.md +++ b/docfx/docs/events.md @@ -71,56 +71,56 @@ A cancellable event is really two events and some activity that takes place betw The `OrientationHelper` class supporting `IOrientation` and a `View` having an `Orientation` property illustrates the preferred TG pattern for cancelable events. ```cs - /// - /// Gets or sets the orientation of the View. - /// - public Orientation Orientation - { - get => _orientation; - set - { - if (_orientation == value) - { - return; - } - - // Best practice is to invoke the virtual method first. - // This allows derived classes to handle the event and potentially cancel it. - if (_owner?.OnOrientationChanging (value, _orientation) ?? false) - { - return; - } - - // If the event is not canceled by the virtual method, raise the event to notify any external subscribers. - CancelEventArgs args = new (in _orientation, ref value); - OrientationChanging?.Invoke (_owner, args); - - if (args.Cancel) - { - return; - } - - // If the event is not canceled, update the value. - Orientation old = _orientation; - - if (_orientation != value) - { - _orientation = value; - - if (_owner is { }) - { - _owner.Orientation = value; - } - } - - // Best practice is to invoke the virtual method first. - _owner?.OnOrientationChanged (_orientation); - - // Even though Changed is not cancelable, it is still a good practice to raise the event after. - OrientationChanged?.Invoke (_owner, new (in _orientation)); - } - } + /// + /// Gets or sets the orientation of the View. + /// + public Orientation Orientation + { + get => _orientation; + set + { + if (_orientation == value) + { + return; + } + + // Best practice is to call the virtual method first. + // This allows derived classes to handle the event and potentially cancel it. + if (_owner?.OnOrientationChanging (value, _orientation) ?? false) + { + return; + } + + // If the event is not canceled by the virtual method, raise the event to notify any external subscribers. + CancelEventArgs args = new (in _orientation, ref value); + OrientationChanging?.Invoke (_owner, args); + + if (args.Cancel) + { + return; + } + + // If the event is not canceled, update the value. + Orientation old = _orientation; + + if (_orientation != value) + { + _orientation = value; + + if (_owner is { }) + { + _owner.Orientation = value; + } + } + + // Best practice is to call the virtual method first, then raise the event. + _owner?.OnOrientationChanged (_orientation); + OrientationChanged?.Invoke (_owner, new (in _orientation)); + } + } ``` + ## `bool` or `bool?` + From c19cc7c332db278caeb4cf6c6a182d78da2f3d44 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 14:06:27 -0600 Subject: [PATCH 08/15] Code cleanup --- Terminal.Gui/View/View.Keyboard.cs | 106 +++++++++--------- Terminal.Gui/Views/DateField.cs | 2 +- Terminal.Gui/Views/HexView.cs | 2 +- Terminal.Gui/Views/Menu/Menu.cs | 2 +- Terminal.Gui/Views/TextField.cs | 21 +--- Terminal.Gui/Views/TextValidateField.cs | 2 +- Terminal.Gui/Views/TextView.cs | 2 +- Terminal.Gui/Views/TileView.cs | 2 +- Terminal.Gui/Views/TimeField.cs | 2 +- Terminal.Gui/Views/Wizard/Wizard.cs | 4 +- UnitTests/View/Keyboard/KeyboardEventTests.cs | 16 +-- UnitTests/View/ViewTests.cs | 2 +- 12 files changed, 71 insertions(+), 92 deletions(-) diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index e3b2ab2ba0..2bb4b3da6f 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -27,7 +27,7 @@ private void SetupKeyboard () #region HotKey Support - /// Invoked when the is changed. + /// Raised when the is changed. public event EventHandler? HotKeyChanged; private Key _hotKey = new (); @@ -254,13 +254,13 @@ private void SetHotKeyFromTitle () /// first. /// /// - /// If the focused sub view does not handle the key press, this method raises / + /// If a more focused subview does not handle the key press, this method raises / /// to allow the /// view to pre-process the key press. If / is not handled /// / will be raised to invoke any key /// bindings. /// Then, only if no key bindings are - /// handled, / will be raised allowing the view to + /// handled, / will be raised allowing the view to /// process the key press. /// /// @@ -301,7 +301,7 @@ public bool NewKeyDownEvent (Key key) // After // TODO: Is ProcessKeyDown really the right name? - if (RaiseProcessKeyDown (key) || key.Handled) + if (RaiseKeyDownNotHandled (key) || key.Handled) { return true; } @@ -322,14 +322,14 @@ bool RaiseKeyDown (Key k) return k.Handled; } - bool RaiseProcessKeyDown (Key k) + bool RaiseKeyDownNotHandled (Key k) { - if (OnProcessKeyDown (k) || k.Handled) + if (OnKeyDownNotHandled (k) || k.Handled) { return true; } - ProcessKeyDown?.Invoke (this, k); + KeyDownNotHandled?.Invoke (this, k); return false; } @@ -337,14 +337,14 @@ bool RaiseProcessKeyDown (Key k) /// /// Called when the user presses a key, allowing subscribers to pre-process the key down event. Called - /// before and are raised. Set + /// before and are raised. Set /// to true to - /// stop the key from being processed by other views. + /// stop the key from being processed further. /// - /// Contains the details about the key that produced the event. + /// The key that produced the event. /// - /// if the key press was not handled. if the keypress was handled - /// and no other view should see it. + /// if the key down event was not handled. if the event was handled + /// and processing should stop. /// /// /// @@ -356,9 +356,10 @@ bool RaiseProcessKeyDown (Key k) protected virtual bool OnKeyDown (Key key) { return false; } /// - /// Raised when the user presses a key, allowing subscribers to pre-process the key down event. Raised - /// before and . Set to true to - /// stop the key from being processed by other views. + /// Raised when the user presses a key, allowing subscribers to pre-process the key down event. Called + /// before and are raised. Set + /// to true to + /// stop the key from being processed further. /// /// /// @@ -370,8 +371,7 @@ bool RaiseProcessKeyDown (Key k) public event EventHandler? KeyDown; /// - /// Called when the user presses a key, allowing views do things during key down events. This is - /// called after the after are raised. + /// Called when the user has pressed key it wasn't handled by and was not bound to a key binding. /// /// /// @@ -388,12 +388,10 @@ bool RaiseProcessKeyDown (Key k) /// if the key press was not handled. if the keypress was handled /// and no other view should see it. /// - protected virtual bool OnProcessKeyDown (Key key) { return key.Handled; } + protected virtual bool OnKeyDownNotHandled (Key key) { return key.Handled; } /// - /// Raised when the user presses a key, allowing subscribers to do things during key down events. Set - /// to true to stop the key from being processed by other views. Invoked after - /// and . + /// Raised when the user has pressed key it wasn't handled by and was not bound to a key binding. /// /// /// @@ -401,12 +399,12 @@ bool RaiseProcessKeyDown (Key k) /// instead. /// /// - /// SubViews can use the of their super view override the default behavior of when + /// SubViews can use the of their super view override the default behavior of when /// key bindings are invoked. /// /// See for an overview of Terminal.Gui keyboard APIs. /// - public event EventHandler? ProcessKeyDown; + public event EventHandler? KeyDownNotHandled; #endregion KeyDown Event @@ -469,7 +467,7 @@ bool RaiseKeyUp (Key k) } } - /// Method invoked when a key is released. This method is called from . + /// Called when a key is released. This method is called from . /// Contains the details about the key that produced the event. /// /// if the keys up event was not handled. if no other view should see @@ -487,7 +485,7 @@ bool RaiseKeyUp (Key k) public virtual bool OnKeyUp (Key key) { return false; } /// - /// Invoked when a key is released. Set to true to stop the key up event from being processed + /// Raised when a key is released. Set to true to stop the key up event from being processed /// by other views. /// /// Not all terminals support key distinct down/up notifications, Applications should avoid depending on @@ -509,7 +507,7 @@ bool RaiseKeyUp (Key k) private Dictionary CommandImplementations { get; } = new (); /// - /// + /// INTERNAL API: Raises the event and invokes the commands bound to . /// /// /// @@ -576,12 +574,37 @@ bool RaiseKeyUp (Key k) } return handled; + } + /// + /// Called when a key is pressed that may be mapped to a key binding. Set to true to + /// stop the key from being processed by other views. + /// + /// + /// See for an overview of Terminal.Gui keyboard APIs. + /// + /// Contains the details about the key that produced the event. + /// The scope. + /// + /// if the event was raised and was not handled (or cancelled); input processing should + /// continue. + /// if the event was raised and handled (or cancelled); input processing should stop. + /// + protected virtual bool OnInvokingKeyBindings (Key key, KeyBindingScope scope) + { + return false; } + // TODO: This does not carry KeyBindingScope, but OnInvokingKeyBindings does + /// + /// Raised when a key is pressed that may be mapped to a key binding. Set to true to + /// stop the key from being processed by other views. + /// + public event EventHandler? InvokingKeyBindings; + private bool ProcessAdornmentKeyBindings (Adornment adornment, Key key, KeyBindingScope scope, ref bool? handled) { - bool? adornmentHandled = adornment.OnInvokingKeyBindings (key, scope); + bool? adornmentHandled = adornment.RaiseInvokingKeyBindingsAndInvokeCommands (key); if (adornmentHandled is true) { @@ -595,7 +618,7 @@ private bool ProcessAdornmentKeyBindings (Adornment adornment, Key key, KeyBindi foreach (View subview in adornment.Subviews) { - bool? subViewHandled = subview.OnInvokingKeyBindings (key, scope); + bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (key); if (subViewHandled is { }) { @@ -689,33 +712,6 @@ public bool IsHotKeyBound (Key key, out View? boundView) return false; } - - /// - /// Called when a key is pressed that may be mapped to a key binding. Set to true to - /// stop the key from being processed by other views. - /// - /// - /// See for an overview of Terminal.Gui keyboard APIs. - /// - /// Contains the details about the key that produced the event. - /// The scope. - /// - /// if the event was raised and was not handled (or cancelled); input processing should - /// continue. - /// if the event was raised and handled (or cancelled); input processing should stop. - /// - protected virtual bool OnInvokingKeyBindings (Key key, KeyBindingScope scope) - { - return false; - } - - // TODO: This does not carry KeyBindingScope, but OnInvokingKeyBindings does - /// - /// Raised when a key is pressed that may be mapped to a key binding. Set to true to - /// stop the key from being processed by other views. - /// - public event EventHandler? InvokingKeyBindings; - /// /// Invokes the Commands bound to . /// See for an overview of Terminal.Gui keyboard APIs. diff --git a/Terminal.Gui/Views/DateField.cs b/Terminal.Gui/Views/DateField.cs index 43060c0a57..49da4f100e 100644 --- a/Terminal.Gui/Views/DateField.cs +++ b/Terminal.Gui/Views/DateField.cs @@ -131,7 +131,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) public virtual void OnDateChanged (DateTimeEventArgs args) { DateChanged?.Invoke (this, args); } /// - protected override bool OnProcessKeyDown (Key a) + protected override bool OnKeyDownNotHandled (Key a) { // Ignore non-numeric characters. if (a >= Key.D0 && a <= Key.D9) diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index 8f7e3c1a06..a5a213b821 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/Terminal.Gui/Views/HexView.cs @@ -452,7 +452,7 @@ void SetAttribute (Attribute attribute) public virtual void OnPositionChanged () { PositionChanged?.Invoke (this, new HexViewEventArgs (Position, CursorPosition, BytesPerLine)); } /// - protected override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnKeyDownNotHandled (Key keyEvent) { if (!AllowEdits) { diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs index 9ee580d49d..b3429b190c 100644 --- a/Terminal.Gui/Views/Menu/Menu.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -306,7 +306,7 @@ private bool ExpandCollapse (MenuItem? menuItem) } /// - protected override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnKeyDownNotHandled (Key keyEvent) { // We didn't handle the key, pass it on to host return _host.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent) == true; diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index d1cfed3b1b..cb6d2e82ac 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -1037,25 +1037,8 @@ protected override void OnHasFocusChanged (bool newHasFocus, View previousFocuse // ClearAllSelection (); } - /// TODO: Flush out these docs - /// - /// Processes key presses for the . - /// - /// The control responds to the following keys: - /// - /// - /// Keys Function - /// - /// - /// , - /// Deletes the character before cursor. - /// - /// - /// - /// - /// - /// - protected override bool OnProcessKeyDown (Key a) + /// + protected override bool OnKeyDownNotHandled (Key a) { // Remember the cursor position because the new calculated cursor position is needed // to be set BEFORE the TextChanged event is triggered. diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs index 579b22f562..c6927b5e3c 100644 --- a/Terminal.Gui/Views/TextValidateField.cs +++ b/Terminal.Gui/Views/TextValidateField.cs @@ -597,7 +597,7 @@ public override void OnDrawContent (Rectangle viewport) } /// - protected override bool OnProcessKeyDown (Key a) + protected override bool OnKeyDownNotHandled (Key a) { if (_provider is null) { diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 4cc86bf2f9..97ccc47a9d 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -3703,7 +3703,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocus } /// - protected override bool OnProcessKeyDown (Key a) + protected override bool OnKeyDownNotHandled (Key a) { if (!CanFocus) { diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index 4adbb5b1b8..ec117c652b 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -286,7 +286,7 @@ public override void OnDrawContent (Rectangle viewport) //// BUGBUG: Why is this not handled by a key binding??? /// - protected override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnKeyDownNotHandled (Key keyEvent) { var focusMoved = false; diff --git a/Terminal.Gui/Views/TimeField.cs b/Terminal.Gui/Views/TimeField.cs index 305a3ce597..687a8cd719 100644 --- a/Terminal.Gui/Views/TimeField.cs +++ b/Terminal.Gui/Views/TimeField.cs @@ -177,7 +177,7 @@ protected internal override bool OnMouseEvent (MouseEvent ev) } /// - protected override bool OnProcessKeyDown (Key a) + protected override bool OnKeyDownNotHandled (Key a) { // Ignore non-numeric characters. if (a.KeyCode is >= (KeyCode)(int)KeyCode.D0 and <= (KeyCode)(int)KeyCode.D9) diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index 338328b02d..b9bc49bd58 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -381,12 +381,12 @@ public bool GoToStep (WizardStep newStep) /// /// is derived from and Dialog causes Esc to call /// , closing the Dialog. Wizard overrides - /// to instead fire the event when Wizard is being used as a + /// to instead fire the event when Wizard is being used as a /// non-modal (see ). /// /// /// - protected override bool OnProcessKeyDown (Key key) + protected override bool OnKeyDownNotHandled (Key key) { //// BUGBUG: Why is this not handled by a key binding??? if (!Modal) diff --git a/UnitTests/View/Keyboard/KeyboardEventTests.cs b/UnitTests/View/Keyboard/KeyboardEventTests.cs index 471016231c..6b484ec58e 100644 --- a/UnitTests/View/Keyboard/KeyboardEventTests.cs +++ b/UnitTests/View/Keyboard/KeyboardEventTests.cs @@ -43,7 +43,7 @@ public void AllViews_NewKeyDownEvent_All_EventsFire (Type viewType) var keyDownProcessed = false; - view.ProcessKeyDown += (s, a) => + view.KeyDownNotHandled += (s, a) => { a.Handled = true; keyDownProcessed = true; @@ -112,7 +112,7 @@ public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool s Assert.True (view.OnKeyDownCalled); keyDown = true; }; - view.ProcessKeyDown += (s, e) => { keyPressed = true; }; + view.KeyDownNotHandled += (s, e) => { keyPressed = true; }; view.KeyUp += (s, e) => { @@ -178,7 +178,7 @@ public void NewKeyDownEvent_InvokingKeyBindings_Handled_Cancels () Assert.Equal (KeyCode.N, e.KeyCode); }; - view.ProcessKeyDown += (s, e) => + view.KeyDownNotHandled += (s, e) => { processKeyPressInvoked = true; processKeyPressInvoked = true; @@ -230,7 +230,7 @@ public void NewKeyDownEvent_InvokingKeyBindings_Handled_True_Stops_Processing () invokingKeyBindings = true; }; - view.ProcessKeyDown += (s, e) => + view.KeyDownNotHandled += (s, e) => { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); @@ -278,7 +278,7 @@ public void NewKeyDownEvent_Handled_True_Stops_Processing () invokingKeyBindings = true; }; - view.ProcessKeyDown += (s, e) => + view.KeyDownNotHandled += (s, e) => { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (keyPressed); @@ -319,7 +319,7 @@ public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing () Assert.Equal (KeyCode.N, e.KeyCode); }; - view.ProcessKeyDown += (s, e) => + view.KeyDownNotHandled += (s, e) => { processKeyPressInvoked = true; Assert.False (e.Handled); @@ -367,7 +367,7 @@ public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing () invokingKeyBindings = true; }; - view.ProcessKeyDown += (s, e) => + view.KeyDownNotHandled += (s, e) => { Assert.Equal (KeyCode.A, e.KeyCode); Assert.False (processKeyDown); @@ -472,7 +472,7 @@ public override bool OnKeyUp (Key keyEvent) return CancelVirtualMethods; } - protected override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnKeyDownNotHandled (Key keyEvent) { OnProcessKeyDownCalled = true; diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index 54b9b31b55..1dc072e3a2 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -1146,7 +1146,7 @@ public override bool OnKeyUp (Key keyEvent) return true; } - protected override bool OnProcessKeyDown (Key keyEvent) + protected override bool OnKeyDownNotHandled (Key keyEvent) { IsKeyPress = true; From f536945df5a8e26c2345790720b978369ba27df5 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 14:19:48 -0600 Subject: [PATCH 09/15] Application.Keyboard Code cleanup --- .../Application/Application.Initialization.cs | 2 +- .../Application/Application.Keyboard.cs | 242 +++++------------- Terminal.Gui/Application/Application.Run.cs | 35 +++ Terminal.Gui/View/View.Keyboard.cs | 26 +- UnitTests/Application/KeyboardTests.cs | 58 ++--- UnitTests/Dialogs/MessageBoxTests.cs | 10 +- UnitTests/FileServices/FileDialogTests.cs | 6 +- UnitTests/UICatalog/ScenarioTests.cs | 2 +- UnitTests/View/HotKeyTests.cs | 6 +- UnitTests/View/Navigation/CanFocusTests.cs | 2 +- UnitTests/View/Navigation/NavigationTests.cs | 22 +- UnitTests/View/ViewKeyBindingTests.cs | 22 +- UnitTests/Views/ButtonTests.cs | 12 +- UnitTests/Views/ColorPickerTests.cs | 46 ++-- UnitTests/Views/ComboBoxTests.cs | 88 +++---- UnitTests/Views/ContextMenuTests.cs | 82 +++--- UnitTests/Views/DatePickerTests.cs | 2 +- UnitTests/Views/LabelTests.cs | 4 +- UnitTests/Views/MenuBarTests.cs | 4 +- UnitTests/Views/RadioGroupTests.cs | 66 ++--- UnitTests/Views/ShortcutTests.cs | 10 +- UnitTests/Views/StatusBarTests.cs | 2 +- UnitTests/Views/TabViewTests.cs | 30 +-- UnitTests/Views/TableViewTests.cs | 22 +- UnitTests/Views/TextFieldTests.cs | 6 +- UnitTests/Views/ToplevelTests.cs | 40 +-- UnitTests/Views/TreeTableSourceTests.cs | 2 +- 27 files changed, 385 insertions(+), 464 deletions(-) diff --git a/Terminal.Gui/Application/Application.Initialization.cs b/Terminal.Gui/Application/Application.Initialization.cs index 4fb11621d6..640c9b5f54 100644 --- a/Terminal.Gui/Application/Application.Initialization.cs +++ b/Terminal.Gui/Application/Application.Initialization.cs @@ -178,7 +178,7 @@ internal static void InternalInit ( } private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); } - private static void Driver_KeyDown (object? sender, Key e) { OnKeyDown (e); } + private static void Driver_KeyDown (object? sender, Key e) { RaiseKeyDownEvent (e); } private static void Driver_KeyUp (object? sender, Key e) { OnKeyUp (e); } private static void Driver_MouseEvent (object? sender, MouseEvent e) { OnMouseEvent (e); } diff --git a/Terminal.Gui/Application/Application.Keyboard.cs b/Terminal.Gui/Application/Application.Keyboard.cs index 01545deddb..a73e7cb868 100644 --- a/Terminal.Gui/Application/Application.Keyboard.cs +++ b/Terminal.Gui/Application/Application.Keyboard.cs @@ -3,91 +3,16 @@ namespace Terminal.Gui; public static partial class Application // Keyboard handling { - private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides - private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides - private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides - private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides - private static Key _quitKey = Key.Esc; // Resources/config.json overrrides - private static Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrrides - - static Application () { AddApplicationKeyBindings (); } - - /// Gets the key bindings for this view. - public static KeyBindings KeyBindings { get; internal set; } = new (); - - /// - /// Event fired when the user presses a key. Fired by . - /// - /// Set to to indicate the key was handled and to prevent - /// additional processing. - /// - /// - /// - /// All drivers support firing the event. Some drivers (Curses) do not support firing the - /// and events. - /// Fired after and before . - /// - public static event EventHandler? KeyDown; - - /// - /// Event fired when the user releases a key. Fired by . - /// - /// Set to to indicate the key was handled and to prevent - /// additional processing. - /// - /// - /// - /// All drivers support firing the event. Some drivers (Curses) do not support firing the - /// and events. - /// Fired after . - /// - public static event EventHandler? KeyUp; - - /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key. - [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] - public static Key NextTabGroupKey - { - get => _nextTabGroupKey; - set - { - if (_nextTabGroupKey != value) - { - ReplaceKey (_nextTabGroupKey, value); - _nextTabGroupKey = value; - } - } - } - - /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key. - [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] - public static Key NextTabKey - { - get => _nextTabKey; - set - { - if (_nextTabKey != value) - { - ReplaceKey (_nextTabKey, value); - _nextTabKey = value; - } - } - } - /// - /// Called by the when the user presses a key. Fires the event - /// then calls on all top level views. Called after and - /// before . + /// Called when the user presses a key (by the ). Raises the cancelable + /// event + /// then calls on all top level views. Called before . /// /// Can be used to simulate key press events. /// /// if the key was handled. - public static bool OnKeyDown (Key keyEvent) + public static bool RaiseKeyDownEvent (Key keyEvent) { - //if (!IsInitialized) - //{ - // return true; - //} - KeyDown?.Invoke (null, keyEvent); if (keyEvent.Handled) @@ -150,43 +75,50 @@ public static bool OnKeyDown (Key keyEvent) } return false; - } - /// - /// INTENRAL method to invoke one of the commands in - /// - /// - /// - /// - /// - /// - private static bool? InvokeCommand (Command command, Key keyEvent, KeyBinding appBinding) - { - if (!CommandImplementations!.ContainsKey (command)) + static bool? InvokeCommand (Command command, Key keyEvent, KeyBinding appBinding) { - throw new NotSupportedException ( - @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application." - ); - } + if (!CommandImplementations!.ContainsKey (command)) + { + throw new NotSupportedException ( + @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application." + ); + } - if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation)) - { - var context = new CommandContext (command, keyEvent, appBinding); // Create the context here + if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation)) + { + var context = new CommandContext (command, keyEvent, appBinding); // Create the context here - return implementation (context); - } + return implementation (context); + } - return false; + return false; + } } /// - /// Called by the when the user releases a key. Fires the event - /// then calls on all top level views. Called after . + /// Raised when the user presses a key. + /// + /// Set to to indicate the key was handled and to prevent + /// additional processing. + /// /// - /// Can be used to simulate key press events. + /// + /// All drivers support firing the event. Some drivers (Curses) do not support firing the + /// and events. + /// Fired after and before . + /// + public static event EventHandler? KeyDown; + + /// + /// Called when the user releases a key (by the ). Raises the cancelable + /// event + /// then calls on all top level views. Called after . + /// + /// Can be used to simulate key release events. /// /// if the key was handled. - public static bool OnKeyUp (Key a) + public static bool RaiseKeyUpEvent (Key a) { if (!IsInitialized) { @@ -216,65 +148,12 @@ public static bool OnKeyUp (Key a) return false; } - /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key. - [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] - public static Key PrevTabGroupKey - { - get => _prevTabGroupKey; - set - { - if (_prevTabGroupKey != value) - { - ReplaceKey (_prevTabGroupKey, value); - _prevTabGroupKey = value; - } - } - } - - /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key. - [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] - public static Key PrevTabKey - { - get => _prevTabKey; - set - { - if (_prevTabKey != value) - { - ReplaceKey (_prevTabKey, value); - _prevTabKey = value; - } - } - } + #region Application-scoped KeyBindings - /// Gets or sets the key to quit the application. - [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] - public static Key QuitKey - { - get => _quitKey; - set - { - if (_quitKey != value) - { - ReplaceKey (_quitKey, value); - _quitKey = value; - } - } - } + static Application () { AddApplicationKeyBindings (); } - /// Gets or sets the key to activate arranging views using the keyboard. - [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] - public static Key ArrangeKey - { - get => _arrangeKey; - set - { - if (_arrangeKey != value) - { - ReplaceKey (_arrangeKey, value); - _arrangeKey = value; - } - } - } + /// Gets the Application-scoped key bindings. + public static KeyBindings KeyBindings { get; internal set; } = new (); internal static void AddApplicationKeyBindings () { @@ -286,6 +165,7 @@ internal static void AddApplicationKeyBindings () static () => { RequestStop (); + return true; } ); @@ -348,7 +228,7 @@ internal static void AddApplicationKeyBindings () KeyBindings.Clear (); - // Resources/config.json overrrides + // Resources/config.json overrides NextTabKey = Key.Tab; PrevTabKey = Key.Tab.WithShift; NextTabGroupKey = Key.F6; @@ -397,6 +277,26 @@ internal static List GetViewKeyBindings () .ToList (); } + private static void ReplaceKey (Key oldKey, Key newKey) + { + if (KeyBindings.Bindings.Count == 0) + { + return; + } + + if (newKey == Key.Empty) + { + KeyBindings.Remove (oldKey); + } + else + { + KeyBindings.ReplaceKey (oldKey, newKey); + } + } + + + #endregion Application-scoped KeyBindings + /// /// /// Sets the function that will be invoked for a . @@ -420,20 +320,4 @@ internal static List GetViewKeyBindings () /// private static Dictionary? CommandImplementations { get; set; } - private static void ReplaceKey (Key oldKey, Key newKey) - { - if (KeyBindings.Bindings.Count == 0) - { - return; - } - - if (newKey == Key.Empty) - { - KeyBindings.Remove (oldKey); - } - else - { - KeyBindings.ReplaceKey (oldKey, newKey); - } - } } diff --git a/Terminal.Gui/Application/Application.Run.cs b/Terminal.Gui/Application/Application.Run.cs index 4a1d89541f..e3c78bdd7f 100644 --- a/Terminal.Gui/Application/Application.Run.cs +++ b/Terminal.Gui/Application/Application.Run.cs @@ -6,6 +6,41 @@ namespace Terminal.Gui; public static partial class Application // Run (Begin, Run, End, Stop) { + private static Key _quitKey = Key.Esc; // Resources/config.json overrides + + /// Gets or sets the key to quit the application. + [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] + public static Key QuitKey + { + get => _quitKey; + set + { + if (_quitKey != value) + { + ReplaceKey (_quitKey, value); + _quitKey = value; + } + } + } + + private static Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrides + + + /// Gets or sets the key to activate arranging views using the keyboard. + [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] + public static Key ArrangeKey + { + get => _arrangeKey; + set + { + if (_arrangeKey != value) + { + ReplaceKey (_arrangeKey, value); + _arrangeKey = value; + } + } + } + // When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`. // This variable is set in `End` in this case so that `Begin` correctly sets `Top`. private static Toplevel? _cachedRunStateToplevel; diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 2bb4b3da6f..992f29e2f7 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -1,6 +1,5 @@ #nullable enable using System.Diagnostics; -using Microsoft.CodeAnalysis.FlowAnalysis; namespace Terminal.Gui; @@ -266,7 +265,7 @@ private void SetHotKeyFromTitle () /// /// Calling this method for a key bound to the view via an Application-scoped keybinding will have no effect. /// Instead, - /// use . + /// use . /// /// See for an overview of Terminal.Gui keyboard APIs. /// @@ -337,7 +336,8 @@ bool RaiseKeyDownNotHandled (Key k) /// /// Called when the user presses a key, allowing subscribers to pre-process the key down event. Called - /// before and are raised. Set + /// before and are raised. Set + /// /// to true to /// stop the key from being processed further. /// @@ -357,7 +357,8 @@ bool RaiseKeyDownNotHandled (Key k) /// /// Raised when the user presses a key, allowing subscribers to pre-process the key down event. Called - /// before and are raised. Set + /// before and are raised. Set + /// /// to true to /// stop the key from being processed further. /// @@ -507,14 +508,17 @@ bool RaiseKeyUp (Key k) private Dictionary CommandImplementations { get; } = new (); /// - /// INTERNAL API: Raises the event and invokes the commands bound to . + /// INTERNAL API: Raises the event and invokes the commands bound to + /// . /// /// /// - /// if no command was invoked or there was no matching key binding; input processing should continue. + /// if no command was invoked or there was no matching key binding; input processing should + /// continue. /// if a command was invoked and was not handled (or cancelled); input processing should /// continue. - /// if was handled or a command was invoked and handled (or cancelled); input processing should stop. + /// if was handled or a command was invoked and handled (or + /// cancelled); input processing should stop. /// internal bool? RaiseInvokingKeyBindingsAndInvokeCommands (Key key) { @@ -590,10 +594,7 @@ bool RaiseKeyUp (Key k) /// continue. /// if the event was raised and handled (or cancelled); input processing should stop. /// - protected virtual bool OnInvokingKeyBindings (Key key, KeyBindingScope scope) - { - return false; - } + protected virtual bool OnInvokingKeyBindings (Key key, KeyBindingScope scope) { return false; } // TODO: This does not carry KeyBindingScope, but OnInvokingKeyBindings does /// @@ -722,7 +723,8 @@ public bool IsHotKeyBound (Key key, out View? boundView) /// if no command was invoked; input processing should continue. /// if at least one command was invoked and was not handled (or cancelled); input processing /// should continue. - /// if at least one command was invoked and handled (or cancelled); input processing should stop. + /// if at least one command was invoked and handled (or cancelled); input processing should + /// stop. /// protected bool? InvokeCommands (Key key, KeyBindingScope scope) { diff --git a/UnitTests/Application/KeyboardTests.cs b/UnitTests/Application/KeyboardTests.cs index 09b1bf7a3f..71b93ecc4d 100644 --- a/UnitTests/Application/KeyboardTests.cs +++ b/UnitTests/Application/KeyboardTests.cs @@ -64,14 +64,14 @@ public void EnsuresTopOnFront_CanFocus_False_By_Keyboard () Assert.True (win2.HasFocus); Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title); - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.True (win2.CanFocus); Assert.False (win.HasFocus); Assert.True (win2.CanFocus); Assert.True (win2.HasFocus); Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title); - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.False (win.CanFocus); Assert.False (win.HasFocus); Assert.True (win2.CanFocus); @@ -117,14 +117,14 @@ public void EnsuresTopOnFront_CanFocus_True_By_Keyboard () Assert.False (win2.HasFocus); Assert.Equal ("win", ((Window)top.Subviews [^1]).Title); - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.True (win.CanFocus); Assert.False (win.HasFocus); Assert.True (win2.CanFocus); Assert.True (win2.HasFocus); Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title); - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.True (win.CanFocus); Assert.True (win.HasFocus); Assert.True (win2.CanFocus); @@ -170,31 +170,31 @@ public void KeyBinding_OnKeyDown () top.Add (view); Application.Begin (top); - Application.OnKeyDown (Key.A); + Application.NewKeyDown (Key.A); Assert.False (invoked); Assert.True (view.ApplicationCommand); invoked = false; view.ApplicationCommand = false; Application.KeyBindings.Remove (KeyCode.A); - Application.OnKeyDown (Key.A); // old + Application.NewKeyDown (Key.A); // old Assert.False (invoked); Assert.False (view.ApplicationCommand); Application.KeyBindings.Add (Key.A.WithCtrl, view, Command.Save); - Application.OnKeyDown (Key.A); // old + Application.NewKeyDown (Key.A); // old Assert.False (invoked); Assert.False (view.ApplicationCommand); - Application.OnKeyDown (Key.A.WithCtrl); // new + Application.NewKeyDown (Key.A.WithCtrl); // new Assert.False (invoked); Assert.True (view.ApplicationCommand); invoked = false; - Application.OnKeyDown (Key.H); + Application.NewKeyDown (Key.H); Assert.True (invoked); invoked = false; Assert.False (view.HasFocus); - Application.OnKeyDown (Key.F); + Application.NewKeyDown (Key.F); Assert.False (invoked); Assert.True (view.ApplicationCommand); @@ -215,7 +215,7 @@ public void KeyBinding_OnKeyDown_Negative () top.Add (view); Application.Begin (top); - Application.OnKeyDown (Key.A.WithCtrl); + Application.NewKeyDown (Key.A.WithCtrl); Assert.False (invoked); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); @@ -223,7 +223,7 @@ public void KeyBinding_OnKeyDown_Negative () invoked = false; Assert.False (view.HasFocus); - Application.OnKeyDown (Key.Z); + Application.NewKeyDown (Key.Z); Assert.False (invoked); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); @@ -399,7 +399,7 @@ public void NextTabGroupKey_Moves_Focus_To_TabStop_In_Next_TabGroup () Assert.True (subView1.HasFocus); // Act - Application.OnKeyDown (Application.NextTabGroupKey); + Application.NewKeyDown (Application.NextTabGroupKey); // Assert Assert.True (view2.HasFocus); @@ -432,24 +432,24 @@ public void NextTabGroupKey_PrevTabGroupKey_Tests () Assert.True (v1.HasFocus); // Across TabGroups - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.True (v3.HasFocus); - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.True (v1.HasFocus); - Application.OnKeyDown (Key.F6.WithShift); + Application.NewKeyDown (Key.F6.WithShift); Assert.True (v3.HasFocus); - Application.OnKeyDown (Key.F6.WithShift); + Application.NewKeyDown (Key.F6.WithShift); Assert.True (v1.HasFocus); // Restore? - Application.OnKeyDown (Key.Tab); + Application.NewKeyDown (Key.Tab); Assert.True (v2.HasFocus); - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.True (v3.HasFocus); - Application.OnKeyDown (Key.F6); + Application.NewKeyDown (Key.F6); Assert.True (v1.HasFocus); Application.RequestStop (); @@ -485,7 +485,7 @@ public void NextTabKey_Moves_Focus_To_Next_TabStop () view1.SetFocus (); // Act - Application.OnKeyDown (Application.NextTabKey); + Application.NewKeyDown (Application.NextTabKey); // Assert Assert.True (view2.HasFocus); @@ -539,7 +539,7 @@ public void PrevTabGroupKey_Moves_Focus_To_TabStop_In_Prev_TabGroup () Assert.True (subView1.HasFocus); // Act - Application.OnKeyDown (Application.PrevTabGroupKey); + Application.NewKeyDown (Application.PrevTabGroupKey); // Assert Assert.True (view2.HasFocus); @@ -562,7 +562,7 @@ public void PrevTabKey_Moves_Focus_To_Prev_TabStop () view1.SetFocus (); // Act - Application.OnKeyDown (Application.NextTabKey); + Application.NewKeyDown (Application.NextTabKey); // Assert Assert.True (view2.HasFocus); @@ -605,21 +605,21 @@ public void QuitKey_Getter_Setter () Key prevKey = Application.QuitKey; - Application.OnKeyDown (Application.QuitKey); + Application.NewKeyDown (Application.QuitKey); Assert.True (isQuiting); isQuiting = false; - Application.OnKeyDown (Application.QuitKey); + Application.NewKeyDown (Application.QuitKey); Assert.True (isQuiting); isQuiting = false; Application.QuitKey = Key.C.WithCtrl; - Application.OnKeyDown (prevKey); // Should not quit + Application.NewKeyDown (prevKey); // Should not quit Assert.False (isQuiting); - Application.OnKeyDown (Key.Q.WithCtrl); // Should not quit + Application.NewKeyDown (Key.Q.WithCtrl); // Should not quit Assert.False (isQuiting); - Application.OnKeyDown (Application.QuitKey); + Application.NewKeyDown (Application.QuitKey); Assert.True (isQuiting); // Reset the QuitKey to avoid throws errors on another tests @@ -728,7 +728,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) if (Application.IsInitialized) { _output.WriteLine (" Pressing QuitKey"); - Application.OnKeyDown (Application.QuitKey); + Application.NewKeyDown (Application.QuitKey); } } } diff --git a/UnitTests/Dialogs/MessageBoxTests.cs b/UnitTests/Dialogs/MessageBoxTests.cs index 541ecd9e4c..caad58da55 100644 --- a/UnitTests/Dialogs/MessageBoxTests.cs +++ b/UnitTests/Dialogs/MessageBoxTests.cs @@ -33,14 +33,14 @@ public void KeyBindings_Enter_Causes_Focused_Button_Click_No_Accept () case 2: // Tab to btn2 - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); Button btn = Application.Navigation!.GetFocused () as Button; btn.Accepting += (sender, e) => { btnAcceptCount++; }; // Click - Application.OnKeyDown (Key.Enter); + Application.RaiseKeyDownEvent (Key.Enter); break; @@ -77,7 +77,7 @@ public void KeyBindings_Esc_Closes () break; case 2: - Application.OnKeyDown (Key.Esc); + Application.RaiseKeyDownEvent (Key.Esc); break; @@ -116,13 +116,13 @@ public void KeyBindings_Space_Causes_Focused_Button_Click_No_Accept () case 2: // Tab to btn2 - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); Button btn = Application.Navigation!.GetFocused () as Button; btn.Accepting += (sender, e) => { btnAcceptCount++; }; - Application.OnKeyDown (Key.Space); + Application.RaiseKeyDownEvent (Key.Space); break; diff --git a/UnitTests/FileServices/FileDialogTests.cs b/UnitTests/FileServices/FileDialogTests.cs index 28e81ef602..cb31b74628 100644 --- a/UnitTests/FileServices/FileDialogTests.cs +++ b/UnitTests/FileServices/FileDialogTests.cs @@ -140,7 +140,7 @@ public void DotDot_MovesToRoot_ThenPressBack () AssertIsTheStartingDirectory (dlg.Path); Assert.IsType (dlg.MostFocused); - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); var tv = GetTableView(dlg); tv.SetFocus (); @@ -152,7 +152,7 @@ public void DotDot_MovesToRoot_ThenPressBack () AssertIsTheStartingDirectory (dlg.Path); // Accept navigation up a directory - Application.OnKeyDown (Key.Enter); + Application.RaiseKeyDownEvent (Key.Enter); AssertIsTheRootDirectory (dlg.Path); @@ -162,7 +162,7 @@ public void DotDot_MovesToRoot_ThenPressBack () Assert.IsType (dlg.MostFocused); // Now press Backspace (in table view) - Application.OnKeyDown (Key.Backspace); + Application.RaiseKeyDownEvent (Key.Backspace); // Should move us back to the root AssertIsTheStartingDirectory (dlg.Path); diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index d7549b594c..4575ea5baa 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -132,7 +132,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) { // Press QuitKey //_output.WriteLine ($"Forcing Quit with {Application.QuitKey}"); - Application.OnKeyDown (Application.QuitKey); + Application.NewKeyDown (Application.QuitKey); } } } diff --git a/UnitTests/View/HotKeyTests.cs b/UnitTests/View/HotKeyTests.cs index c7032347c2..a3edaaf6c0 100644 --- a/UnitTests/View/HotKeyTests.cs +++ b/UnitTests/View/HotKeyTests.cs @@ -362,20 +362,20 @@ public void HotKey_Raises_HotKeyCommand () view.Selecting += (s, e) => selectRaised = true; Assert.Equal (KeyCode.T, view.HotKey); - Assert.True (Application.OnKeyDown (Key.T)); + Assert.True (Application.RaiseKeyDownEvent (Key.T)); Assert.True (hotKeyRaised); Assert.False (acceptRaised); Assert.False (selectRaised); hotKeyRaised = false; - Assert.True (Application.OnKeyDown (Key.T.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.T.WithAlt)); Assert.True (hotKeyRaised); Assert.False (acceptRaised); Assert.False (selectRaised); hotKeyRaised = false; view.HotKey = KeyCode.E; - Assert.True (Application.OnKeyDown (Key.E.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.E.WithAlt)); Assert.True (hotKeyRaised); Assert.False (acceptRaised); Assert.False (selectRaised); diff --git a/UnitTests/View/Navigation/CanFocusTests.cs b/UnitTests/View/Navigation/CanFocusTests.cs index 006a84dd4d..39fa34ad85 100644 --- a/UnitTests/View/Navigation/CanFocusTests.cs +++ b/UnitTests/View/Navigation/CanFocusTests.cs @@ -354,7 +354,7 @@ public void CanFocus_Set_True_Get_AdvanceFocus_Works () Assert.False (label.HasFocus); Assert.True (view.HasFocus); - Assert.True (Application.OnKeyDown (Key.Tab)); + Assert.True (Application.RaiseKeyDownEvent (Key.Tab)); Assert.True (label.HasFocus); Assert.False (view.HasFocus); diff --git a/UnitTests/View/Navigation/NavigationTests.cs b/UnitTests/View/Navigation/NavigationTests.cs index 12b49c1296..19b4757f9c 100644 --- a/UnitTests/View/Navigation/NavigationTests.cs +++ b/UnitTests/View/Navigation/NavigationTests.cs @@ -59,17 +59,17 @@ public void AllViews_AtLeastOneNavKey_Advances (Type viewType) case TabBehavior.TabStop: case TabBehavior.NoStop: case TabBehavior.TabGroup: - Application.OnKeyDown (key); + Application.RaiseKeyDownEvent (key); if (view.HasFocus) { // Try once more (HexView) - Application.OnKeyDown (key); + Application.RaiseKeyDownEvent (key); } break; default: - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); break; } @@ -178,18 +178,18 @@ public void AllViews_HasFocus_Changed_Event (Type viewType) case null: case TabBehavior.NoStop: case TabBehavior.TabStop: - if (Application.OnKeyDown (Key.Tab)) + if (Application.RaiseKeyDownEvent (Key.Tab)) { if (view.HasFocus) { // Try another nav key (e.g. for TextView that eats Tab) - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); } }; break; case TabBehavior.TabGroup: - Application.OnKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); break; default: @@ -211,18 +211,18 @@ public void AllViews_HasFocus_Changed_Event (Type viewType) break; case TabBehavior.TabStop: - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); break; case TabBehavior.TabGroup: - if (!Application.OnKeyDown (Key.F6)) + if (!Application.RaiseKeyDownEvent (Key.F6)) { view.SetFocus (); } break; case null: - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); break; default: @@ -308,12 +308,12 @@ public void AllViews_Visible_False_No_HasFocus_Events (Type viewType) Assert.Equal (0, hasFocusChangingCount); Assert.Equal (0, hasFocusChangedCount); - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); Assert.Equal (0, hasFocusChangingCount); Assert.Equal (0, hasFocusChangedCount); - Application.OnKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.Equal (0, hasFocusChangingCount); Assert.Equal (0, hasFocusChangedCount); diff --git a/UnitTests/View/ViewKeyBindingTests.cs b/UnitTests/View/ViewKeyBindingTests.cs index 2ac278a7b1..05471e4b51 100644 --- a/UnitTests/View/ViewKeyBindingTests.cs +++ b/UnitTests/View/ViewKeyBindingTests.cs @@ -18,17 +18,17 @@ public void Focus_KeyBinding () top.Add (view); Application.Begin (top); - Application.OnKeyDown (Key.A); + Application.RaiseKeyDownEvent (Key.A); Assert.False (invoked); Assert.True (view.ApplicationCommand); invoked = false; - Application.OnKeyDown (Key.H); + Application.RaiseKeyDownEvent (Key.H); Assert.True (invoked); invoked = false; Assert.False (view.HasFocus); - Application.OnKeyDown (Key.F); + Application.RaiseKeyDownEvent (Key.F); Assert.False (invoked); Assert.False (view.FocusedCommand); @@ -36,7 +36,7 @@ public void Focus_KeyBinding () view.CanFocus = true; view.SetFocus (); Assert.True (view.HasFocus); - Application.OnKeyDown (Key.F); + Application.RaiseKeyDownEvent (Key.F); Assert.True (invoked); Assert.True (view.ApplicationCommand); @@ -57,7 +57,7 @@ public void Focus_KeyBinding_Negative () top.Add (view); Application.Begin (top); - Application.OnKeyDown (Key.Z); + Application.RaiseKeyDownEvent (Key.Z); Assert.False (invoked); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); @@ -65,7 +65,7 @@ public void Focus_KeyBinding_Negative () invoked = false; Assert.False (view.HasFocus); - Application.OnKeyDown (Key.F); + Application.RaiseKeyDownEvent (Key.F); Assert.False (invoked); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); @@ -86,18 +86,18 @@ public void HotKey_KeyBinding () Application.Begin (top); invoked = false; - Application.OnKeyDown (Key.H); + Application.RaiseKeyDownEvent (Key.H); Assert.True (invoked); Assert.True (view.HotKeyCommand); view.HotKey = KeyCode.Z; invoked = false; view.HotKeyCommand = false; - Application.OnKeyDown (Key.H); // old hot key + Application.RaiseKeyDownEvent (Key.H); // old hot key Assert.False (invoked); Assert.False (view.HotKeyCommand); - Application.OnKeyDown (Key.Z); // new hot key + Application.RaiseKeyDownEvent (Key.Z); // new hot key Assert.True (invoked); Assert.True (view.HotKeyCommand); top.Dispose (); @@ -115,12 +115,12 @@ public void HotKey_KeyBinding_Negative () top.Add (view); Application.Begin (top); - Application.OnKeyDown (Key.Z); + Application.RaiseKeyDownEvent (Key.Z); Assert.False (invoked); Assert.False (view.HotKeyCommand); invoked = false; - Application.OnKeyDown (Key.F); + Application.RaiseKeyDownEvent (Key.F); Assert.False (view.HotKeyCommand); top.Dispose (); } diff --git a/UnitTests/Views/ButtonTests.cs b/UnitTests/Views/ButtonTests.cs index d7f092e808..e4b4f7efd7 100644 --- a/UnitTests/Views/ButtonTests.cs +++ b/UnitTests/Views/ButtonTests.cs @@ -376,7 +376,7 @@ public void KeyBindingExample () Assert.True (btn.HasFocus); // default keybinding is Space which results in Command.Accept (when focused) - Application.OnKeyDown (new ((KeyCode)' ')); + Application.RaiseKeyDownEvent (new ((KeyCode)' ')); Assert.Equal (1, pressed); // remove the default keybinding (Space) @@ -384,26 +384,26 @@ public void KeyBindingExample () btn.KeyBindings.Clear (Command.Accept); // After clearing the default keystroke the Space button no longer does anything for the Button - Application.OnKeyDown (new ((KeyCode)' ')); + Application.RaiseKeyDownEvent (new ((KeyCode)' ')); Assert.Equal (1, pressed); // Set a new binding of b for the click (Accept) event btn.KeyBindings.Add (Key.B, Command.HotKey); // b will now trigger the Accept command (when focused or not) // now pressing B should call the button click event - Application.OnKeyDown (Key.B); + Application.RaiseKeyDownEvent (Key.B); Assert.Equal (2, pressed); // now pressing Shift-B should NOT call the button click event - Application.OnKeyDown (Key.B.WithShift); + Application.RaiseKeyDownEvent (Key.B.WithShift); Assert.Equal (2, pressed); // now pressing Alt-B should NOT call the button click event - Application.OnKeyDown (Key.B.WithAlt); + Application.RaiseKeyDownEvent (Key.B.WithAlt); Assert.Equal (2, pressed); // now pressing Shift-Alt-B should NOT call the button click event - Application.OnKeyDown (Key.B.WithAlt.WithShift); + Application.RaiseKeyDownEvent (Key.B.WithAlt.WithShift); Assert.Equal (2, pressed); top.Dispose (); } diff --git a/UnitTests/Views/ColorPickerTests.cs b/UnitTests/Views/ColorPickerTests.cs index 0b5b050c33..57f4144c9d 100644 --- a/UnitTests/Views/ColorPickerTests.cs +++ b/UnitTests/Views/ColorPickerTests.cs @@ -64,14 +64,14 @@ public void ColorPicker_RGB_KeyboardNavigation () cp.Draw (); - Application.OnKeyDown (Key.CursorRight); + Application.RaiseKeyDownEvent (Key.CursorRight); cp.Draw (); Assert.Equal (3, r.TrianglePosition); Assert.Equal ("#0F0000", hex.Text); - Application.OnKeyDown (Key.CursorRight); + Application.RaiseKeyDownEvent (Key.CursorRight); cp.Draw (); @@ -81,7 +81,7 @@ public void ColorPicker_RGB_KeyboardNavigation () // Use cursor to move the triangle all the way to the right for (int i = 0; i < 1000; i++) { - Application.OnKeyDown (Key.CursorRight); + Application.RaiseKeyDownEvent (Key.CursorRight); } cp.Draw (); @@ -713,19 +713,19 @@ public void ColorPicker_TabCompleteColorName () name.Text = ""; Assert.Empty (name.Text); - Application.OnKeyDown (Key.A); - Application.OnKeyDown (Key.Q); + Application.RaiseKeyDownEvent (Key.A); + Application.RaiseKeyDownEvent (Key.Q); Assert.Equal ("aq", name.Text); // Auto complete the color name - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); Assert.Equal ("Aquamarine", name.Text); // Tab out of the text field - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); Assert.False (name.HasFocus); Assert.NotSame (name, cp.Focused); @@ -761,24 +761,24 @@ public void ColorPicker_EnterHexFor_ColorName () Assert.Empty (hex.Text); Assert.Empty (name.Text); - Application.OnKeyDown ('#'); + Application.RaiseKeyDownEvent ('#'); Assert.Empty (name.Text); //7FFFD4 Assert.Equal ("#", hex.Text); - Application.OnKeyDown ('7'); - Application.OnKeyDown ('F'); - Application.OnKeyDown ('F'); - Application.OnKeyDown ('F'); - Application.OnKeyDown ('D'); + Application.RaiseKeyDownEvent ('7'); + Application.RaiseKeyDownEvent ('F'); + Application.RaiseKeyDownEvent ('F'); + Application.RaiseKeyDownEvent ('F'); + Application.RaiseKeyDownEvent ('D'); Assert.Empty (name.Text); - Application.OnKeyDown ('4'); + Application.RaiseKeyDownEvent ('4'); Assert.True (hex.HasFocus); // Tab out of the hex field - should wrap to first focusable subview - Application.OnKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); Assert.False (hex.HasFocus); Assert.NotSame (hex, cp.Focused); @@ -819,24 +819,24 @@ public void ColorPicker_EnterHexFor_ColorName_AcceptVariation () Assert.Empty (hex.Text); Assert.Empty (name.Text); - Application.OnKeyDown ('#'); + Application.RaiseKeyDownEvent ('#'); Assert.Empty (name.Text); //7FFFD4 Assert.Equal ("#", hex.Text); - Application.OnKeyDown ('7'); - Application.OnKeyDown ('F'); - Application.OnKeyDown ('F'); - Application.OnKeyDown ('F'); - Application.OnKeyDown ('D'); + Application.RaiseKeyDownEvent ('7'); + Application.RaiseKeyDownEvent ('F'); + Application.RaiseKeyDownEvent ('F'); + Application.RaiseKeyDownEvent ('F'); + Application.RaiseKeyDownEvent ('D'); Assert.Empty (name.Text); - Application.OnKeyDown ('4'); + Application.RaiseKeyDownEvent ('4'); Assert.True (hex.HasFocus); // Should stay in the hex field (because accept not tab) - Application.OnKeyDown (Key.Enter); + Application.RaiseKeyDownEvent (Key.Enter); Assert.True (hex.HasFocus); Assert.Same (hex, cp.Focused); diff --git a/UnitTests/Views/ComboBoxTests.cs b/UnitTests/Views/ComboBoxTests.cs index 92cfb8c510..99a0576a04 100644 --- a/UnitTests/Views/ComboBoxTests.cs +++ b/UnitTests/Views/ComboBoxTests.cs @@ -105,13 +105,13 @@ public void Expanded_Collapsed_Events () Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.NotNull (cb.Source); Assert.True (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Null (cb.Source); Assert.False (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); @@ -155,7 +155,7 @@ public void HideDropdownListOnClick_False_OpenSelectedItem_With_Mouse_And_Key_Cu Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Equal ("Two", selected); Assert.True (cb.IsShow); Assert.Equal (1, cb.SelectedItem); @@ -166,7 +166,7 @@ public void HideDropdownListOnClick_False_OpenSelectedItem_With_Mouse_And_Key_Cu Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - Assert.True (Application.OnKeyDown (Key.Esc)); + Assert.True (Application.RaiseKeyDownEvent (Key.Esc)); Assert.Equal ("Two", selected); Assert.False (cb.IsShow); Assert.Equal (1, cb.SelectedItem); @@ -202,7 +202,7 @@ public void HideDropdownListOnClick_False_OpenSelectedItem_With_Mouse_And_Key_F4 Assert.Equal ("One", cb.Text); Assert.True (cb.Subviews [1].NewKeyDownEvent (Key.CursorDown)); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Equal ("Two", selected); Assert.False (cb.IsShow); Assert.Equal (1, cb.SelectedItem); @@ -251,7 +251,7 @@ public void Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Equal ("Two", selected); Assert.True (cb.IsShow); Assert.Equal (1, cb.SelectedItem); @@ -262,7 +262,7 @@ public void Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - Assert.True (Application.OnKeyDown (Key.Esc)); + Assert.True (Application.RaiseKeyDownEvent (Key.Esc)); Assert.Equal ("Two", selected); Assert.False (cb.IsShow); Assert.Equal (1, cb.SelectedItem); @@ -417,7 +417,7 @@ cb.Subviews [1] Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.True ( cb.Subviews [1] @@ -441,7 +441,7 @@ cb.Subviews [1] Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.True ( cb.Subviews [1] @@ -465,7 +465,7 @@ cb.Subviews [1] Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.True ( cb.Subviews [1] @@ -595,7 +595,7 @@ cb.Subviews [1].GetNormalColor () Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Equal ("Three", selected); Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); @@ -646,7 +646,7 @@ cb.Subviews [1].GetNormalColor () attributes ); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Equal ("Three", selected); Assert.False (cb.IsShow); Assert.Equal (2, cb.SelectedItem); @@ -756,7 +756,7 @@ public void HideDropdownListOnClick_True_OpenSelectedItem_With_Mouse_And_Key_Cur Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Equal ("Two", selected); Assert.True (cb.IsShow); Assert.Equal (1, cb.SelectedItem); @@ -767,7 +767,7 @@ public void HideDropdownListOnClick_True_OpenSelectedItem_With_Mouse_And_Key_Cur Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - Assert.True (Application.OnKeyDown (Key.Esc)); + Assert.True (Application.RaiseKeyDownEvent (Key.Esc)); Assert.Equal ("Two", selected); Assert.False (cb.IsShow); Assert.Equal (1, cb.SelectedItem); @@ -803,7 +803,7 @@ public void HideDropdownListOnClick_True_OpenSelectedItem_With_Mouse_And_Key_F4 Assert.Equal ("", cb.Text); Assert.True (cb.Subviews [1].NewKeyDownEvent (Key.CursorDown)); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.Equal ("", selected); Assert.False (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); @@ -834,32 +834,32 @@ public void KeyBindings_Command () cb.OpenSelectedItem += (s, _) => opened = true; - Assert.False (Application.OnKeyDown (Key.Enter)); + Assert.False (Application.RaiseKeyDownEvent (Key.Enter)); Assert.False (opened); cb.Text = "Tw"; - Assert.False (Application.OnKeyDown (Key.Enter)); + Assert.False (Application.RaiseKeyDownEvent (Key.Enter)); Assert.True (opened); Assert.Equal ("Tw", cb.Text); Assert.False (cb.IsShow); cb.SetSource (null); Assert.False (cb.IsShow); - Assert.False (Application.OnKeyDown (Key.Enter)); - Assert.True (Application.OnKeyDown (Key.F4)); // with no source also expand empty + Assert.False (Application.RaiseKeyDownEvent (Key.Enter)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); // with no source also expand empty Assert.True (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); cb.SetSource (source); cb.Text = ""; - Assert.True (Application.OnKeyDown (Key.F4)); // collapse + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); // collapse Assert.False (cb.IsShow); - Assert.True (Application.OnKeyDown (Key.F4)); // expand + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); // expand Assert.True (cb.IsShow); cb.Collapse (); Assert.False (cb.IsShow); Assert.True (cb.HasFocus); - Assert.True (Application.OnKeyDown (Key.CursorDown)); // losing focus + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); // losing focus Assert.False (cb.IsShow); Assert.False (cb.HasFocus); cb.SetFocus (); @@ -870,27 +870,27 @@ public void KeyBindings_Command () Assert.True (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.CursorDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); Assert.True (cb.IsShow); Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - Assert.True (Application.OnKeyDown (Key.CursorDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - Assert.True (Application.OnKeyDown (Key.CursorDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - Assert.True (Application.OnKeyDown (Key.CursorUp)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorUp)); Assert.True (cb.IsShow); Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - Assert.True (Application.OnKeyDown (Key.CursorUp)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorUp)); Assert.True (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.CursorUp)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorUp)); Assert.True (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); @@ -904,7 +904,7 @@ public void KeyBindings_Command () output ); - Assert.True (Application.OnKeyDown (Key.PageDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.PageDown)); Assert.True (cb.IsShow); Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); @@ -919,7 +919,7 @@ public void KeyBindings_Command () output ); - Assert.True (Application.OnKeyDown (Key.PageDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.PageDown)); Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); @@ -933,43 +933,43 @@ public void KeyBindings_Command () ", output ); - Assert.True (Application.OnKeyDown (Key.PageUp)); + Assert.True (Application.RaiseKeyDownEvent (Key.PageUp)); Assert.True (cb.IsShow); Assert.Equal (1, cb.SelectedItem); Assert.Equal ("Two", cb.Text); - Assert.True (Application.OnKeyDown (Key.PageUp)); + Assert.True (Application.RaiseKeyDownEvent (Key.PageUp)); Assert.True (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.False (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.End)); + Assert.True (Application.RaiseKeyDownEvent (Key.End)); Assert.False (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.Home)); + Assert.True (Application.RaiseKeyDownEvent (Key.Home)); Assert.False (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.True (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.End)); + Assert.True (Application.RaiseKeyDownEvent (Key.End)); Assert.True (cb.IsShow); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); - Assert.True (Application.OnKeyDown (Key.Home)); + Assert.True (Application.RaiseKeyDownEvent (Key.Home)); Assert.True (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.Esc)); + Assert.True (Application.RaiseKeyDownEvent (Key.Esc)); Assert.False (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("", cb.Text); - Assert.True (Application.OnKeyDown (Key.CursorDown)); // losing focus + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); // losing focus Assert.False (cb.HasFocus); Assert.False (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); @@ -980,7 +980,7 @@ public void KeyBindings_Command () Assert.False (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("One", cb.Text); - Assert.True (Application.OnKeyDown (Key.U.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.U.WithCtrl)); Assert.True (cb.HasFocus); Assert.True (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); @@ -1009,7 +1009,7 @@ public void Source_Equal_Null_Or_Count_Equal_Zero_Sets_SelectedItem_Equal_To_Min source.Add ("One"); Assert.Equal (1, cb.Source.Count); Assert.Equal (-1, cb.SelectedItem); - Assert.True (Application.OnKeyDown (Key.F4)); + Assert.True (Application.RaiseKeyDownEvent (Key.F4)); Assert.True (cb.IsShow); Assert.Equal (0, cb.SelectedItem); Assert.Equal ("One", cb.Text); @@ -1020,12 +1020,12 @@ public void Source_Equal_Null_Or_Count_Equal_Zero_Sets_SelectedItem_Equal_To_Min Assert.True (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("T", cb.Text); - Assert.True (Application.OnKeyDown (Key.Enter)); + Assert.True (Application.RaiseKeyDownEvent (Key.Enter)); Assert.False (cb.IsShow); Assert.Equal (2, cb.Source.Count); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("T", cb.Text); - Assert.True (Application.OnKeyDown (Key.Esc)); + Assert.True (Application.RaiseKeyDownEvent (Key.Esc)); Assert.False (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); // retains last accept selected item Assert.Equal ("", cb.Text); // clear text diff --git a/UnitTests/Views/ContextMenuTests.cs b/UnitTests/Views/ContextMenuTests.cs index ab9e1448e4..3a84d3b566 100644 --- a/UnitTests/Views/ContextMenuTests.cs +++ b/UnitTests/Views/ContextMenuTests.cs @@ -400,9 +400,9 @@ public void Key_Open_And_Close_The_ContextMenu () top.Add (tf); Application.Begin (top); - Assert.True (Application.OnKeyDown (ContextMenu.DefaultKey)); + Assert.True (Application.RaiseKeyDownEvent (ContextMenu.DefaultKey)); Assert.True (tf.ContextMenu.MenuBar!.IsMenuOpen); - Assert.True (Application.OnKeyDown (ContextMenu.DefaultKey)); + Assert.True (Application.RaiseKeyDownEvent (ContextMenu.DefaultKey)); // The last context menu bar opened is always preserved Assert.NotNull (tf.ContextMenu.MenuBar); @@ -1473,9 +1473,9 @@ public void KeyBindings_Removed_On_Close_ContextMenu () Application.Begin (top); Assert.Null (cm.MenuBar); - Assert.False (Application.OnKeyDown (Key.N.WithCtrl)); - Assert.False (Application.OnKeyDown (Key.R.WithCtrl)); - Assert.False (Application.OnKeyDown (Key.D.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.D.WithCtrl)); Assert.False (newFile); Assert.False (renameFile); Assert.False (deleteFile); @@ -1485,17 +1485,17 @@ public void KeyBindings_Removed_On_Close_ContextMenu () Assert.True (cm.MenuBar.KeyBindings.Bindings.ContainsKey (Key.R.WithCtrl)); Assert.True (cm.MenuBar.KeyBindings.Bindings.ContainsKey (Key.D.WithCtrl)); - Assert.True (Application.OnKeyDown (Key.N.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (newFile); Assert.False (cm.MenuBar!.IsMenuOpen); cm.Show (menuItems); - Assert.True (Application.OnKeyDown (Key.R.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.R.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (renameFile); Assert.False (cm.MenuBar.IsMenuOpen); cm.Show (menuItems); - Assert.True (Application.OnKeyDown (Key.D.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.D.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (deleteFile); Assert.False (cm.MenuBar.IsMenuOpen); @@ -1507,9 +1507,9 @@ public void KeyBindings_Removed_On_Close_ContextMenu () newFile = false; renameFile = false; deleteFile = false; - Assert.False (Application.OnKeyDown (Key.N.WithCtrl)); - Assert.False (Application.OnKeyDown (Key.R.WithCtrl)); - Assert.False (Application.OnKeyDown (Key.D.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.D.WithCtrl)); Assert.False (newFile); Assert.False (renameFile); Assert.False (deleteFile); @@ -1557,8 +1557,8 @@ public void KeyBindings_With_ContextMenu_And_MenuBar () Assert.False (menuBar.KeyBindings.Bindings.ContainsKey (Key.R.WithCtrl)); Assert.Null (cm.MenuBar); - Assert.True (Application.OnKeyDown (Key.N.WithCtrl)); - Assert.False (Application.OnKeyDown (Key.R.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (newFile); Assert.False (renameFile); @@ -1572,12 +1572,12 @@ public void KeyBindings_With_ContextMenu_And_MenuBar () Assert.True (cm.MenuBar.KeyBindings.Bindings.ContainsKey (Key.R.WithCtrl)); Assert.True (cm.MenuBar.IsMenuOpen); - Assert.True (Application.OnKeyDown (Key.N.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (newFile); Assert.False (cm.MenuBar!.IsMenuOpen); cm.Show (menuItems); - Assert.True (Application.OnKeyDown (Key.R.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.R.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (renameFile); Assert.False (cm.MenuBar.IsMenuOpen); @@ -1589,8 +1589,8 @@ public void KeyBindings_With_ContextMenu_And_MenuBar () newFile = false; renameFile = false; - Assert.True (Application.OnKeyDown (Key.N.WithCtrl)); - Assert.False (Application.OnKeyDown (Key.R.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (newFile); Assert.False (renameFile); @@ -1635,7 +1635,7 @@ public void KeyBindings_With_Same_Shortcut_ContextMenu_And_MenuBar () Assert.True (menuBar.KeyBindings.Bindings.ContainsKey (Key.N.WithCtrl)); Assert.Null (cm.MenuBar); - Assert.True (Application.OnKeyDown (Key.N.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (newMenuBar); Assert.False (newContextMenu); @@ -1647,7 +1647,7 @@ public void KeyBindings_With_Same_Shortcut_ContextMenu_And_MenuBar () Assert.True (cm.MenuBar!.KeyBindings.Bindings.ContainsKey (Key.N.WithCtrl)); Assert.True (cm.MenuBar.IsMenuOpen); - Assert.True (Application.OnKeyDown (Key.N.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.False (newMenuBar); @@ -1660,7 +1660,7 @@ public void KeyBindings_With_Same_Shortcut_ContextMenu_And_MenuBar () newMenuBar = false; newContextMenu = false; - Assert.True (Application.OnKeyDown (Key.N.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl)); Application.MainLoop!.RunIteration (); Assert.True (newMenuBar); Assert.False (newContextMenu); @@ -1693,9 +1693,9 @@ public void HotKeys_Removed_On_Close_ContextMenu () Application.Begin (top); Assert.Null (cm.MenuBar); - Assert.False (Application.OnKeyDown (Key.N.WithAlt)); - Assert.False (Application.OnKeyDown (Key.R.WithAlt)); - Assert.False (Application.OnKeyDown (Key.D.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.N.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.D.WithAlt)); Assert.False (newFile); Assert.False (renameFile); Assert.False (deleteFile); @@ -1717,17 +1717,17 @@ public void HotKeys_Removed_On_Close_ContextMenu () Assert.True (menus [0].KeyBindings.Bindings.ContainsKey (Key.D.WithAlt)); Assert.True (menus [0].KeyBindings.Bindings.ContainsKey (Key.D.NoShift)); - Assert.True (Application.OnKeyDown (Key.N.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt)); Assert.False (cm.MenuBar!.IsMenuOpen); Application.MainLoop!.RunIteration (); Assert.True (newFile); cm.Show (menuItems); - Assert.True (Application.OnKeyDown (Key.R.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.R.WithAlt)); Assert.False (cm.MenuBar.IsMenuOpen); Application.MainLoop!.RunIteration (); Assert.True (renameFile); cm.Show (menuItems); - Assert.True (Application.OnKeyDown (Key.D.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.D.WithAlt)); Assert.False (cm.MenuBar.IsMenuOpen); Application.MainLoop!.RunIteration (); Assert.True (deleteFile); @@ -1742,9 +1742,9 @@ public void HotKeys_Removed_On_Close_ContextMenu () newFile = false; renameFile = false; deleteFile = false; - Assert.False (Application.OnKeyDown (Key.N.WithAlt)); - Assert.False (Application.OnKeyDown (Key.R.WithAlt)); - Assert.False (Application.OnKeyDown (Key.D.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.N.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.D.WithAlt)); Assert.False (newFile); Assert.False (renameFile); Assert.False (deleteFile); @@ -1801,14 +1801,14 @@ public void HotKeys_With_ContextMenu_And_MenuBar () Assert.Empty (menus); Assert.Null (cm.MenuBar); - Assert.True (Application.OnKeyDown (Key.F.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt)); Assert.True (menuBar.IsMenuOpen); Assert.Equal (2, Application.Top!.Subviews.Count); menus = Application.Top!.Subviews.Where (v => v is Menu m && m.Host == menuBar).ToArray (); Assert.True (menus [0].KeyBindings.Bindings.ContainsKey (Key.N.WithAlt)); - Assert.True (Application.OnKeyDown (Key.N.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt)); Assert.False (menuBar.IsMenuOpen); - Assert.False (Application.OnKeyDown (Key.R.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt)); Application.MainLoop!.RunIteration (); Assert.True (newFile); Assert.False (renameFile); @@ -1840,9 +1840,9 @@ public void HotKeys_With_ContextMenu_And_MenuBar () Assert.True (menus [1].KeyBindings.Bindings.ContainsKey (Key.R.WithAlt)); Assert.True (menus [1].KeyBindings.Bindings.ContainsKey (Key.R.NoShift)); Assert.True (cm.MenuBar.IsMenuOpen); - Assert.True (Application.OnKeyDown (Key.F.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt)); Assert.False (cm.MenuBar.IsMenuOpen); - Assert.True (Application.OnKeyDown (Key.N.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt)); Application.MainLoop!.RunIteration (); Assert.True (newFile); @@ -1858,8 +1858,8 @@ public void HotKeys_With_ContextMenu_And_MenuBar () Assert.False (menus [1].KeyBindings.Bindings.ContainsKey (Key.E.NoShift)); Assert.True (menus [1].KeyBindings.Bindings.ContainsKey (Key.R.WithAlt)); Assert.True (menus [1].KeyBindings.Bindings.ContainsKey (Key.R.NoShift)); - Assert.True (Application.OnKeyDown (Key.E.NoShift)); - Assert.True (Application.OnKeyDown (Key.R.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.E.NoShift)); + Assert.True (Application.RaiseKeyDownEvent (Key.R.WithAlt)); Assert.False (cm.MenuBar.IsMenuOpen); Application.MainLoop!.RunIteration (); Assert.True (renameFile); @@ -1876,9 +1876,9 @@ public void HotKeys_With_ContextMenu_And_MenuBar () newFile = false; renameFile = false; - Assert.True (Application.OnKeyDown (Key.F.WithAlt)); - Assert.True (Application.OnKeyDown (Key.N.WithAlt)); - Assert.False (Application.OnKeyDown (Key.R.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt)); + Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt)); Application.MainLoop!.RunIteration (); Assert.True (newFile); Assert.False (renameFile); @@ -1923,14 +1923,14 @@ public void Opened_MenuBar_Is_Closed_When_Another_MenuBar_Is_Opening_Also_By_Hot top.Add (menuBar); Application.Begin (top); - Assert.True (Application.OnKeyDown (Key.F.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt)); Assert.True (menuBar.IsMenuOpen); cm.Show (menuItems); Assert.False (menuBar.IsMenuOpen); Assert.True (cm.MenuBar!.IsMenuOpen); - Assert.True (Application.OnKeyDown (Key.F.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt)); Assert.True (menuBar.IsMenuOpen); Assert.False (cm.MenuBar!.IsMenuOpen); diff --git a/UnitTests/Views/DatePickerTests.cs b/UnitTests/Views/DatePickerTests.cs index fd78ac432d..75761d94fa 100644 --- a/UnitTests/Views/DatePickerTests.cs +++ b/UnitTests/Views/DatePickerTests.cs @@ -82,7 +82,7 @@ public void DatePicker_ShouldNot_SetDateOutOfRange_UsingNextMonthButton () Assert.Equal (datePicker.Subviews.First (v => v.Id == "_nextMonthButton"), datePicker.Focused); // Change month to December - Assert.False (Application.OnKeyDown (Key.Enter)); + Assert.False (Application.RaiseKeyDownEvent (Key.Enter)); Assert.Equal (12, datePicker.Date.Month); // Next month button is disabled, so focus advanced to edit field diff --git a/UnitTests/Views/LabelTests.cs b/UnitTests/Views/LabelTests.cs index 2e3982fe75..2eb73d34fd 100644 --- a/UnitTests/Views/LabelTests.cs +++ b/UnitTests/Views/LabelTests.cs @@ -1341,7 +1341,7 @@ public void CanFocus_False_HotKey_SetsFocus_Next () Application.Top.SetFocus (); Assert.True (otherView.HasFocus); - Assert.True (Application.OnKeyDown (label.HotKey)); + Assert.True (Application.RaiseKeyDownEvent (label.HotKey)); Assert.False (otherView.HasFocus); Assert.False (label.HasFocus); Assert.True (nextView.HasFocus); @@ -1396,7 +1396,7 @@ public void CanFocus_True_HotKey_SetsFocus () Assert.True (view.HasFocus); // No focused view accepts Tab, and there's no other view to focus, so OnKeyDown returns false - Assert.True (Application.OnKeyDown (label.HotKey)); + Assert.True (Application.RaiseKeyDownEvent (label.HotKey)); Assert.True (label.HasFocus); Assert.False (view.HasFocus); diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs index 849d9b8f4a..9d2535e5c8 100644 --- a/UnitTests/Views/MenuBarTests.cs +++ b/UnitTests/Views/MenuBarTests.cs @@ -2666,7 +2666,7 @@ expectedMenu.Menus [i].Children.Length > 0 top.Draw (); TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - Assert.True (Application.OnKeyDown (menu.Key)); + Assert.True (Application.NewKeyDown (menu.Key)); Assert.False (menu.IsMenuOpen); Assert.True (tf.HasFocus); top.Draw (); @@ -2949,7 +2949,7 @@ public void ShortCut_Activates () top.Add (menu); Application.Begin (top); - Application.OnKeyDown (Key.S.WithCtrl); + Application.NewKeyDown (Key.S.WithCtrl); Application.MainLoop.RunIteration (); Assert.True (saveAction); diff --git a/UnitTests/Views/RadioGroupTests.cs b/UnitTests/Views/RadioGroupTests.cs index 48fc881190..a3e60941f9 100644 --- a/UnitTests/Views/RadioGroupTests.cs +++ b/UnitTests/Views/RadioGroupTests.cs @@ -57,7 +57,7 @@ public void Initialize_SelectedItem_With_Minus_One () rg.SetFocus (); Assert.Equal (-1, rg.SelectedItem); - Application.OnKeyDown (Key.Space); + Application.NewKeyDown (Key.Space); Assert.Equal (0, rg.SelectedItem); Application.Top.Dispose (); @@ -105,21 +105,21 @@ public void Commands_HasFocus () // 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.False (Application.NewKeyDown (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, selectingCount); Assert.Equal (0, acceptedCount); - Assert.True (Application.OnKeyDown (Key.CursorDown)); + Assert.True (Application.NewKeyDown (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); // Cursor changed, but selection didnt Assert.Equal (1, rg.Cursor); Assert.Equal (0, selectedItemChangedCount); Assert.Equal (0, selectingCount); Assert.Equal (0, acceptedCount); - Assert.False (Application.OnKeyDown (Key.CursorDown)); // Should not change selection (should focus next view if there was one, which there isn't) + Assert.False (Application.NewKeyDown (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); @@ -127,7 +127,7 @@ public void Commands_HasFocus () Assert.Equal (0, acceptedCount); // Test Select (Space) when Cursor != SelectedItem - Should select cursor - Assert.True (Application.OnKeyDown (Key.Space)); + Assert.True (Application.NewKeyDown (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); Assert.Equal (1, selectedItemChangedCount); @@ -135,34 +135,34 @@ public void Commands_HasFocus () Assert.Equal (0, acceptedCount); // Test Select (Space) when Cursor == SelectedItem - Should cycle - Assert.True (Application.OnKeyDown (Key.Space)); + Assert.True (Application.NewKeyDown (Key.Space)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); Assert.Equal (2, selectedItemChangedCount); Assert.Equal (2, selectingCount); Assert.Equal (0, acceptedCount); - Assert.True (Application.OnKeyDown (Key.Space)); + Assert.True (Application.NewKeyDown (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); - Assert.True (Application.OnKeyDown (Key.Space)); + Assert.True (Application.NewKeyDown (Key.Space)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); - Assert.True (Application.OnKeyDown (Key.Space)); + Assert.True (Application.NewKeyDown (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); - Assert.True (Application.OnKeyDown (Key.Home)); + Assert.True (Application.NewKeyDown (Key.Home)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (0, rg.Cursor); - Assert.True (Application.OnKeyDown (Key.Space)); + Assert.True (Application.NewKeyDown (Key.Space)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); - Assert.True (Application.OnKeyDown (Key.End)); + Assert.True (Application.NewKeyDown (Key.End)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); - Assert.True (Application.OnKeyDown (Key.Space)); + Assert.True (Application.NewKeyDown (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); Assert.Equal (7, selectedItemChangedCount); @@ -174,7 +174,7 @@ public void Commands_HasFocus () rg.HotKey = Key.L; Assert.Equal (Key.L, rg.HotKey); - Assert.True (Application.OnKeyDown (rg.HotKey)); + Assert.True (Application.NewKeyDown (rg.HotKey)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); Assert.Equal (8, selectedItemChangedCount); @@ -182,12 +182,12 @@ public void Commands_HasFocus () Assert.Equal (0, acceptedCount); // Make Selected != Cursor - Assert.True (Application.OnKeyDown (Key.CursorDown)); + Assert.True (Application.NewKeyDown (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); // Selected != Cursor - Raise HotKey event - Since we're focused, this should just advance - Assert.True (Application.OnKeyDown (rg.HotKey)); + Assert.True (Application.NewKeyDown (rg.HotKey)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); Assert.Equal (9, selectedItemChangedCount); @@ -239,7 +239,7 @@ public void HotKey_HasFocus_False () // Selected (0) == Cursor (0) - SetFocus rg.HotKey = Key.L; Assert.Equal (Key.L, rg.HotKey); - Assert.True (Application.OnKeyDown (rg.HotKey)); + Assert.True (Application.NewKeyDown (rg.HotKey)); Assert.True (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); @@ -248,14 +248,14 @@ public void HotKey_HasFocus_False () Assert.Equal (0, acceptCount); // Make Selected != Cursor - Assert.True (Application.OnKeyDown (Key.CursorDown)); + Assert.True (Application.NewKeyDown (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); otherView.SetFocus (); // Selected != Cursor - SetFocus - Assert.True (Application.OnKeyDown (rg.HotKey)); + Assert.True (Application.NewKeyDown (rg.HotKey)); Assert.True (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -263,7 +263,7 @@ public void HotKey_HasFocus_False () Assert.Equal (0, selectCount); Assert.Equal (0, acceptCount); - Assert.True (Application.OnKeyDown (rg.HotKey)); + Assert.True (Application.NewKeyDown (rg.HotKey)); Assert.True (rg.HasFocus); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -314,7 +314,7 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () // Test RadioTitem.HotKey - Should never SetFocus // Selected (0) == Cursor (0) - Assert.True (Application.OnKeyDown (Key.A)); + Assert.True (Application.NewKeyDown (Key.A)); Assert.False (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); @@ -325,14 +325,14 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () rg.SetFocus (); // Make Selected != Cursor - Assert.True (Application.OnKeyDown (Key.CursorDown)); + Assert.True (Application.NewKeyDown (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); otherView.SetFocus (); // Selected != Cursor - Assert.True (Application.OnKeyDown (Key.A)); + Assert.True (Application.NewKeyDown (Key.A)); Assert.False (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -341,7 +341,7 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () Assert.Equal (0, acceptCount); // Selected != Cursor - Should not set focus - Assert.True (Application.OnKeyDown (Key.B)); + Assert.True (Application.NewKeyDown (Key.B)); Assert.False (rg.HasFocus); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -349,7 +349,7 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () Assert.Equal (1, selectCount); Assert.Equal (0, acceptCount); - Assert.True (Application.OnKeyDown (Key.B)); + Assert.True (Application.NewKeyDown (Key.B)); Assert.False (rg.HasFocus); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -372,22 +372,22 @@ public void HotKeys_HasFocus_True_Selects () Assert.NotEmpty (rg.KeyBindings.GetCommands (KeyCode.L | KeyCode.ShiftMask)); Assert.NotEmpty (rg.KeyBindings.GetCommands (KeyCode.L | KeyCode.AltMask)); - Assert.True (Application.OnKeyDown (Key.T)); + Assert.True (Application.NewKeyDown (Key.T)); Assert.Equal (2, rg.SelectedItem); - Assert.True (Application.OnKeyDown (Key.L)); + Assert.True (Application.NewKeyDown (Key.L)); Assert.Equal (0, rg.SelectedItem); - Assert.True (Application.OnKeyDown (Key.J)); + Assert.True (Application.NewKeyDown (Key.J)); Assert.Equal (3, rg.SelectedItem); - Assert.True (Application.OnKeyDown (Key.R)); + Assert.True (Application.NewKeyDown (Key.R)); Assert.Equal (1, rg.SelectedItem); - Assert.True (Application.OnKeyDown (Key.T.WithAlt)); + Assert.True (Application.NewKeyDown (Key.T.WithAlt)); Assert.Equal (2, rg.SelectedItem); - Assert.True (Application.OnKeyDown (Key.L.WithAlt)); + Assert.True (Application.NewKeyDown (Key.L.WithAlt)); Assert.Equal (0, rg.SelectedItem); - Assert.True (Application.OnKeyDown (Key.J.WithAlt)); + Assert.True (Application.NewKeyDown (Key.J.WithAlt)); Assert.Equal (3, rg.SelectedItem); - Assert.True (Application.OnKeyDown (Key.R.WithAlt)); + Assert.True (Application.NewKeyDown (Key.R.WithAlt)); Assert.Equal (1, rg.SelectedItem); var superView = new View (); diff --git a/UnitTests/Views/ShortcutTests.cs b/UnitTests/Views/ShortcutTests.cs index dc3ab6e1c9..711ce243fe 100644 --- a/UnitTests/Views/ShortcutTests.cs +++ b/UnitTests/Views/ShortcutTests.cs @@ -667,7 +667,7 @@ public void KeyDown_Raises_Accepted_Selected (bool canFocus, KeyCode key, int ex var selected = 0; shortcut.Selecting += (s, e) => selected++; - Application.OnKeyDown (key); + Application.RaiseKeyDownEvent (key); Assert.Equal (expectedAccept, accepted); Assert.Equal (expectedSelect, selected); @@ -719,7 +719,7 @@ public void KeyDown_CheckBox_Raises_Accepted_Selected (bool canFocus, KeyCode ke var selected = 0; shortcut.Selecting += (s, e) => selected++; - Application.OnKeyDown (key); + Application.RaiseKeyDownEvent (key); Assert.Equal (expectedAccept, accepted); Assert.Equal (expectedSelect, selected); @@ -751,7 +751,7 @@ public void KeyDown_App_Scope_Invokes_Accept (KeyCode key, int expectedAccept) var accepted = 0; shortcut.Accepting += (s, e) => accepted++; - Application.OnKeyDown (key); + Application.RaiseKeyDownEvent (key); Assert.Equal (expectedAccept, accepted); @@ -792,7 +792,7 @@ public void KeyDown_Invokes_Action (bool canFocus, KeyCode key, int expectedActi var action = 0; shortcut.Action += () => action++; - Application.OnKeyDown (key); + Application.RaiseKeyDownEvent (key); Assert.Equal (expectedAction, action); @@ -831,7 +831,7 @@ public void KeyDown_App_Scope_Invokes_Action (bool canFocus, KeyCode key, int ex var action = 0; shortcut.Action += () => action++; - Application.OnKeyDown (key); + Application.RaiseKeyDownEvent (key); Assert.Equal (expectedAction, action); diff --git a/UnitTests/Views/StatusBarTests.cs b/UnitTests/Views/StatusBarTests.cs index 2622d807f3..f9c6fcd3b1 100644 --- a/UnitTests/Views/StatusBarTests.cs +++ b/UnitTests/Views/StatusBarTests.cs @@ -104,7 +104,7 @@ public void Run_Action_With_Key_And_Mouse () if (iteration == 0) { Assert.Equal ("", msg); - Application.OnKeyDown (Application.QuitKey); + Application.RaiseKeyDownEvent (Application.QuitKey); } else if (iteration == 1) { diff --git a/UnitTests/Views/TabViewTests.cs b/UnitTests/Views/TabViewTests.cs index 660434b660..dd1d3546d1 100644 --- a/UnitTests/Views/TabViewTests.cs +++ b/UnitTests/Views/TabViewTests.cs @@ -398,7 +398,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () Assert.Equal (tv.SelectedTab.View, top.Focused.MostFocused); // Press the cursor up key to focus the selected tab - Application.OnKeyDown (Key.CursorUp); + Application.RaiseKeyDownEvent (Key.CursorUp); Application.Refresh (); // Is the selected tab focused @@ -416,7 +416,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () }; // Press the cursor right key to select the next tab - Application.OnKeyDown (Key.CursorRight); + Application.RaiseKeyDownEvent (Key.CursorRight); Application.Refresh (); Assert.Equal (tab1, oldChanged); Assert.Equal (tab2, newChanged); @@ -425,7 +425,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () Assert.Equal (tv.MostFocused, top.Focused.MostFocused); // Press the cursor down key. Since the selected tab has no focusable views, the focus should move to the next view in the toplevel - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (btn, top.MostFocused); @@ -439,31 +439,31 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () tv.SelectedTab.View.Add (btnSubView); // Press cursor up. Should focus the subview in the selected tab. - Application.OnKeyDown (Key.CursorUp); + Application.RaiseKeyDownEvent (Key.CursorUp); Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (btnSubView, top.MostFocused); - Application.OnKeyDown (Key.CursorUp); + Application.RaiseKeyDownEvent (Key.CursorUp); Assert.Equal (tab2, top.MostFocused); // Press the cursor down key twice. - Application.OnKeyDown (Key.CursorDown); - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); Assert.Equal (btn, top.MostFocused); // Press the cursor down key again will focus next view in the toplevel, whic is the TabView - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (tv, top.Focused); Assert.Equal (tab1, tv.MostFocused); // Press the cursor down key to focus the selected tab view hosting again - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (btnSubView, top.MostFocused); // Press the cursor up key to focus the selected tab - Application.OnKeyDown (Key.CursorUp); + Application.RaiseKeyDownEvent (Key.CursorUp); Application.Refresh (); // Is the selected tab focused @@ -472,7 +472,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () Assert.Equal (tv.MostFocused, top.Focused.MostFocused); // Press the cursor left key to select the previous tab - Application.OnKeyDown (Key.CursorLeft); + Application.RaiseKeyDownEvent (Key.CursorLeft); Application.Refresh (); Assert.Equal (tab2, oldChanged); Assert.Equal (tab1, newChanged); @@ -481,7 +481,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () Assert.Equal (tv.MostFocused, top.Focused.MostFocused); // Press the end key to select the last tab - Application.OnKeyDown (Key.End); + Application.RaiseKeyDownEvent (Key.End); Application.Refresh (); Assert.Equal (tab1, oldChanged); Assert.Equal (tab2, newChanged); @@ -490,7 +490,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () Assert.Equal (tv.MostFocused, top.Focused.MostFocused); // Press the home key to select the first tab - Application.OnKeyDown (Key.Home); + Application.RaiseKeyDownEvent (Key.Home); Application.Refresh (); Assert.Equal (tab2, oldChanged); Assert.Equal (tab1, newChanged); @@ -499,7 +499,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () Assert.Equal (tv.MostFocused, top.Focused.MostFocused); // Press the page down key to select the next set of tabs - Application.OnKeyDown (Key.PageDown); + Application.RaiseKeyDownEvent (Key.PageDown); Application.Refresh (); Assert.Equal (tab1, oldChanged); Assert.Equal (tab2, newChanged); @@ -508,7 +508,7 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () Assert.Equal (tv.MostFocused, top.Focused.MostFocused); // Press the page up key to select the previous set of tabs - Application.OnKeyDown (Key.PageUp); + Application.RaiseKeyDownEvent (Key.PageUp); Application.Refresh (); Assert.Equal (tab2, oldChanged); Assert.Equal (tab1, newChanged); diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs index 6bb9829881..5c6a3c2042 100644 --- a/UnitTests/Views/TableViewTests.cs +++ b/UnitTests/Views/TableViewTests.cs @@ -3215,12 +3215,12 @@ public void CanTabOutOfTableViewUsingCursor_Left () tableView.SelectedColumn = 1; // Pressing left should move us to the first column without changing focus - Application.OnKeyDown (Key.CursorLeft); + Application.RaiseKeyDownEvent (Key.CursorLeft); Assert.Same (tableView, Application.Top!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the leftmost cell a further left press should move focus - Application.OnKeyDown (Key.CursorLeft); + Application.RaiseKeyDownEvent (Key.CursorLeft); Assert.NotSame (tableView, Application.Top.MostFocused); Assert.False (tableView.HasFocus); @@ -3240,12 +3240,12 @@ public void CanTabOutOfTableViewUsingCursor_Up () tableView.SelectedRow = 1; // First press should move us up - Application.OnKeyDown (Key.CursorUp); + Application.RaiseKeyDownEvent (Key.CursorUp); Assert.Same (tableView, Application.Top!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the top row a further press should move focus - Application.OnKeyDown (Key.CursorUp); + Application.RaiseKeyDownEvent (Key.CursorUp); Assert.NotSame (tableView, Application.Top.MostFocused); Assert.False (tableView.HasFocus); @@ -3264,12 +3264,12 @@ public void CanTabOutOfTableViewUsingCursor_Right () tableView.SelectedColumn = tableView.Table.Columns - 2; // First press should move us to the rightmost column without changing focus - Application.OnKeyDown (Key.CursorRight); + Application.RaiseKeyDownEvent (Key.CursorRight); Assert.Same (tableView, Application.Top!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the rightmost cell, a further right press should move focus - Application.OnKeyDown (Key.CursorRight); + Application.RaiseKeyDownEvent (Key.CursorRight); Assert.NotSame (tableView, Application.Top.MostFocused); Assert.False (tableView.HasFocus); @@ -3289,12 +3289,12 @@ public void CanTabOutOfTableViewUsingCursor_Down () tableView.SelectedRow = tableView.Table.Rows - 2; // First press should move us to the bottommost row without changing focus - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); Assert.Same (tableView, Application.Top!.MostFocused); Assert.True (tableView.HasFocus); // Because we are now on the bottommost cell, a further down press should move focus - Application.OnKeyDown (Key.CursorDown); + Application.RaiseKeyDownEvent (Key.CursorDown); Assert.NotSame (tableView, Application.Top.MostFocused); Assert.False (tableView.HasFocus); @@ -3315,7 +3315,7 @@ public void CanTabOutOfTableViewUsingCursor_Left_ClearsSelectionFirst () tableView.SelectedColumn = 1; // Pressing shift-left should give us a multi selection - Application.OnKeyDown (Key.CursorLeft.WithShift); + Application.RaiseKeyDownEvent (Key.CursorLeft.WithShift); Assert.Same (tableView, Application.Top!.MostFocused); Assert.True (tableView.HasFocus); Assert.Equal (2, tableView.GetAllSelectedCells ().Count ()); @@ -3323,7 +3323,7 @@ public void CanTabOutOfTableViewUsingCursor_Left_ClearsSelectionFirst () // Because we are now on the leftmost cell a further left press would normally move focus // However there is an ongoing selection so instead the operation clears the selection and // gets swallowed (not resulting in a focus change) - Application.OnKeyDown (Key.CursorLeft); + Application.RaiseKeyDownEvent (Key.CursorLeft); // Selection 'clears' just to the single cell and we remain focused Assert.Single (tableView.GetAllSelectedCells ()); @@ -3331,7 +3331,7 @@ public void CanTabOutOfTableViewUsingCursor_Left_ClearsSelectionFirst () Assert.True (tableView.HasFocus); // A further left will switch focus - Application.OnKeyDown (Key.CursorLeft); + Application.RaiseKeyDownEvent (Key.CursorLeft); Assert.NotSame (tableView, Application.Top.MostFocused); Assert.False (tableView.HasFocus); diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index d2f8ba4c5a..9fb6d3eac6 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -524,7 +524,7 @@ public void Space_Does_Not_Raise_Selected () Application.Top = new (); Application.Top.Add (tf); tf.SetFocus (); - Application.OnKeyDown (Key.Space); + Application.NewKeyDown (Key.Space); Application.Top.Dispose (); Application.ResetState (true); @@ -541,7 +541,7 @@ public void Enter_Does_Not_Raise_Selected () Application.Top = new (); Application.Top.Add (tf); tf.SetFocus (); - Application.OnKeyDown (Key.Enter); + Application.NewKeyDown (Key.Enter); Assert.Equal (0, selectingCount); @@ -560,7 +560,7 @@ public void Enter_Raises_Accepted () Application.Top = new (); Application.Top.Add (tf); tf.SetFocus (); - Application.OnKeyDown (Key.Enter); + Application.NewKeyDown (Key.Enter); Assert.Equal (1, acceptedCount); diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs index 12780d219d..f5f42b19cf 100644 --- a/UnitTests/Views/ToplevelTests.cs +++ b/UnitTests/Views/ToplevelTests.cs @@ -299,26 +299,26 @@ public void KeyBindings_Command () Assert.Equal (tf1W1, top.MostFocused); Assert.True (isRunning); - Assert.True (Application.OnKeyDown (Application.QuitKey)); + Assert.True (Application.RaiseKeyDownEvent (Application.QuitKey)); Assert.False (isRunning); - Assert.True (Application.OnKeyDown (Key.Z.WithCtrl)); + Assert.True (Application.RaiseKeyDownEvent (Key.Z.WithCtrl)); - Assert.True (Application.OnKeyDown (Key.F5)); // refresh + Assert.True (Application.RaiseKeyDownEvent (Key.F5)); // refresh - Assert.True (Application.OnKeyDown (Key.Tab)); + Assert.True (Application.RaiseKeyDownEvent (Key.Tab)); Assert.Equal (win1, top.Focused); Assert.Equal (tvW1, top.MostFocused); - Assert.True (Application.OnKeyDown (Key.Tab)); + Assert.True (Application.RaiseKeyDownEvent (Key.Tab)); Assert.Equal ($"\tFirst line Win1{Environment.NewLine}Second line Win1", tvW1.Text); - Assert.True (Application.OnKeyDown (Key.Tab.WithShift)); + Assert.True (Application.RaiseKeyDownEvent (Key.Tab.WithShift)); Assert.Equal ($"First line Win1{Environment.NewLine}Second line Win1", tvW1.Text); var prevMostFocusedSubview = top.MostFocused; - Assert.True (Application.OnKeyDown (Key.F6)); // move to next TabGroup (win2) + Assert.True (Application.RaiseKeyDownEvent (Key.F6)); // move to next TabGroup (win2) Assert.Equal (win2, top.Focused); - Assert.True (Application.OnKeyDown (Key.F6.WithShift)); // move to prev TabGroup (win1) + Assert.True (Application.RaiseKeyDownEvent (Key.F6.WithShift)); // move to prev TabGroup (win1) Assert.Equal (win1, top.Focused); Assert.Equal (tf2W1, top.MostFocused); // BUGBUG: Should be prevMostFocusedSubview - We need to cache the last focused view in the TabGroup somehow @@ -327,13 +327,13 @@ public void KeyBindings_Command () Assert.Equal (tvW1, top.MostFocused); tf2W1.SetFocus (); - Assert.True (Application.OnKeyDown (Key.Tab)); // tf2W1 is last subview in win1 - tabbing should take us to first subview of win1 + Assert.True (Application.RaiseKeyDownEvent (Key.Tab)); // tf2W1 is last subview in win1 - tabbing should take us to first subview of win1 Assert.Equal (win1, top.Focused); Assert.Equal (tf1W1, top.MostFocused); - Assert.True (Application.OnKeyDown (Key.CursorRight)); // move char to right in tf1W1. We're at last char so nav to next view + Assert.True (Application.RaiseKeyDownEvent (Key.CursorRight)); // move char to right in tf1W1. We're at last char so nav to next view Assert.Equal (win1, top.Focused); Assert.Equal (tvW1, top.MostFocused); - Assert.True (Application.OnKeyDown (Key.CursorDown)); // move down to next view (tvW1) + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); // move down to next view (tvW1) Assert.Equal (win1, top.Focused); Assert.Equal (tvW1, top.MostFocused); #if UNIX_KEY_BINDINGS @@ -341,34 +341,34 @@ public void KeyBindings_Command () Assert.Equal (win1, top.GetFocused ()); Assert.Equal (tf2W1, top.MostFocused); #endif - Assert.True (Application.OnKeyDown (Key.Tab.WithShift)); // Ignored. TextView eats shift-tab by default + Assert.True (Application.RaiseKeyDownEvent (Key.Tab.WithShift)); // Ignored. TextView eats shift-tab by default Assert.Equal (win1, top.Focused); Assert.Equal (tvW1, top.MostFocused); tvW1.AllowsTab = false; - Assert.True (Application.OnKeyDown (Key.Tab.WithShift)); + Assert.True (Application.RaiseKeyDownEvent (Key.Tab.WithShift)); Assert.Equal (win1, top.Focused); Assert.Equal (tf1W1, top.MostFocused); - Assert.True (Application.OnKeyDown (Key.CursorLeft)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorLeft)); Assert.Equal (win1, top.Focused); Assert.Equal (tf2W1, top.MostFocused); - Assert.True (Application.OnKeyDown (Key.CursorUp)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorUp)); Assert.Equal (win1, top.Focused); Assert.Equal (tvW1, top.MostFocused); // nav to win2 - Assert.True (Application.OnKeyDown (Key.F6)); + Assert.True (Application.RaiseKeyDownEvent (Key.F6)); Assert.Equal (win2, top.Focused); Assert.Equal (tf1W2, top.MostFocused); - Assert.True (Application.OnKeyDown (Key.F6.WithShift)); + Assert.True (Application.RaiseKeyDownEvent (Key.F6.WithShift)); Assert.Equal (win1, top.Focused); Assert.Equal (tf2W1, top.MostFocused); - Assert.True (Application.OnKeyDown (Application.NextTabGroupKey)); + Assert.True (Application.RaiseKeyDownEvent (Application.NextTabGroupKey)); Assert.Equal (win2, top.Focused); Assert.Equal (tf1W2, top.MostFocused); - Assert.True (Application.OnKeyDown (Application.PrevTabGroupKey)); + Assert.True (Application.RaiseKeyDownEvent (Application.PrevTabGroupKey)); Assert.Equal (win1, top.Focused); Assert.Equal (tf2W1, top.MostFocused); - Assert.True (Application.OnKeyDown (Key.CursorUp)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorUp)); Assert.Equal (win1, top.Focused); Assert.Equal (tvW1, top.MostFocused); diff --git a/UnitTests/Views/TreeTableSourceTests.cs b/UnitTests/Views/TreeTableSourceTests.cs index 04fcf420e3..3db7a27425 100644 --- a/UnitTests/Views/TreeTableSourceTests.cs +++ b/UnitTests/Views/TreeTableSourceTests.cs @@ -187,7 +187,7 @@ public void TestTreeTableSource_CombinedWithCheckboxes () Assert.Equal (0, tv.SelectedRow); Assert.Equal (1, tv.SelectedColumn); - Application.OnKeyDown (Key.CursorRight); + Application.RaiseKeyDownEvent (Key.CursorRight); tv.Draw (); From bc51f8868be4ac4dc24bc474b4bbe01b284503d3 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 14:56:46 -0600 Subject: [PATCH 10/15] KeyDown API usage cleanup --- .../Application/Application.Initialization.cs | 2 +- .../Application/Application.Navigation.cs | 79 +++++++++ Terminal.Gui/View/View.Keyboard.cs | 7 +- Terminal.Gui/Views/TextField.cs | 20 +-- Terminal.Gui/Views/TextView.cs | 162 +++++++++--------- Terminal.Gui/Views/TreeView/TreeView.cs | 5 +- UnitTests/Application/KeyboardTests.cs | 58 +++---- UnitTests/UICatalog/ScenarioTests.cs | 2 +- UnitTests/Views/MenuBarTests.cs | 4 +- UnitTests/Views/RadioGroupTests.cs | 66 +++---- UnitTests/Views/TextFieldTests.cs | 6 +- 11 files changed, 240 insertions(+), 171 deletions(-) diff --git a/Terminal.Gui/Application/Application.Initialization.cs b/Terminal.Gui/Application/Application.Initialization.cs index 640c9b5f54..c8310b7801 100644 --- a/Terminal.Gui/Application/Application.Initialization.cs +++ b/Terminal.Gui/Application/Application.Initialization.cs @@ -179,7 +179,7 @@ internal static void InternalInit ( private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); } private static void Driver_KeyDown (object? sender, Key e) { RaiseKeyDownEvent (e); } - private static void Driver_KeyUp (object? sender, Key e) { OnKeyUp (e); } + private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); } private static void Driver_MouseEvent (object? sender, MouseEvent e) { OnMouseEvent (e); } /// Gets of list of types that are available. diff --git a/Terminal.Gui/Application/Application.Navigation.cs b/Terminal.Gui/Application/Application.Navigation.cs index 440cd4b429..a4f9dcc545 100644 --- a/Terminal.Gui/Application/Application.Navigation.cs +++ b/Terminal.Gui/Application/Application.Navigation.cs @@ -7,4 +7,83 @@ public static partial class Application // Navigation stuff /// Gets the instance for the current . /// public static ApplicationNavigation? Navigation { get; internal set; } + + private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrides + private static Key _nextTabKey = Key.Tab; // Resources/config.json overrides + private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrides + private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrides + + /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key. + [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] + public static Key NextTabGroupKey + { + get => _nextTabGroupKey; + set + { + if (_nextTabGroupKey != value) + { + ReplaceKey (_nextTabGroupKey, value); + _nextTabGroupKey = value; + } + } + } + + /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key. + [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] + public static Key NextTabKey + { + get => _nextTabKey; + set + { + if (_nextTabKey != value) + { + ReplaceKey (_nextTabKey, value); + _nextTabKey = value; + } + } + } + + + /// + /// Raised when the user releases a key. + /// + /// Set to to indicate the key was handled and to prevent + /// additional processing. + /// + /// + /// + /// All drivers support firing the event. Some drivers (Curses) do not support firing the + /// and events. + /// Fired after . + /// + public static event EventHandler? KeyUp; + /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key. + [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] + public static Key PrevTabGroupKey + { + get => _prevTabGroupKey; + set + { + if (_prevTabGroupKey != value) + { + ReplaceKey (_prevTabGroupKey, value); + _prevTabGroupKey = value; + } + } + } + + /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key. + [SerializableConfigurationProperty (Scope = typeof (SettingsScope))] + public static Key PrevTabKey + { + get => _prevTabKey; + set + { + if (_prevTabKey != value) + { + ReplaceKey (_prevTabKey, value); + _prevTabKey = value; + } + } + } } diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 992f29e2f7..b78eb2804b 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -265,7 +265,7 @@ private void SetHotKeyFromTitle () /// /// Calling this method for a key bound to the view via an Application-scoped keybinding will have no effect. /// Instead, - /// use . + /// use . /// /// See for an overview of Terminal.Gui keyboard APIs. /// @@ -530,7 +530,6 @@ bool RaiseKeyUp (Key k) return true; } - // BUGBUG: The proper pattern is for the v-method (OnInvokingKeyBindings) to be called first, then the event InvokingKeyBindings?.Invoke (this, key); if (key.Handled) @@ -538,8 +537,6 @@ bool RaiseKeyUp (Key k) return true; } - bool? handled; - // After // * If no key binding was found, `InvokeKeyBindings` returns `null`. @@ -548,7 +545,7 @@ bool RaiseKeyUp (Key k) // `InvokeKeyBindings` returns `false`. Continue passing the event (return `false` from `OnInvokeKeyBindings`).. // * If key bindings were found, and any handled the key (at least one `Command` returned `true`), // `InvokeKeyBindings` returns `true`. Continue passing the event (return `false` from `OnInvokeKeyBindings`). - handled = InvokeCommands (key, scope); + bool? handled = InvokeCommands (key, scope); if (handled is { } && (bool)handled) { diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index cb6d2e82ac..94f512dedb 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -1014,27 +1014,27 @@ public override void OnDrawContent (Rectangle viewport) } /// - protected override bool OnInvokingKeyBindings (Key a, KeyBindingScope scope) + protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view) { - // Give autocomplete first opportunity to respond to key presses - if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (a)) + if (Application.MouseGrabView is { } && Application.MouseGrabView == this) { - return true; + Application.UngrabMouse (); } - return base.OnInvokingKeyBindings (a, scope); + //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar)) + // ClearAllSelection (); } /// - protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view) + protected override bool OnKeyDown (Key key) { - if (Application.MouseGrabView is { } && Application.MouseGrabView == this) + // Give autocomplete first opportunity to respond to key presses + if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (key)) { - Application.UngrabMouse (); + return true; } - //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar)) - // ClearAllSelection (); + return false; } /// diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 97ccc47a9d..54174c4957 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -1,10 +1,8 @@ #nullable enable // TextView.cs: multi-line text editing -using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Terminal.Gui.Resources; namespace Terminal.Gui; @@ -106,7 +104,7 @@ public void LoadListCells (List> cellsList, Attribute? attribute) public void LoadCells (List cells, Attribute? attribute) { - _lines = Cell.ToCells ((List)cells); + _lines = Cell.ToCells (cells); SetAttributes (attribute); OnLinesLoaded (); } @@ -180,7 +178,7 @@ public void ReplaceLine (int pos, List runes) { if (_lines.Count > 0 && pos < _lines.Count) { - _lines [pos] = [..runes]; + _lines [pos] = [.. runes]; } else if (_lines.Count == 0 || (_lines.Count > 0 && pos >= _lines.Count)) { @@ -188,8 +186,6 @@ public void ReplaceLine (int pos, List runes) } } - - public override string ToString () { var sb = new StringBuilder (); @@ -608,8 +604,7 @@ bool IsWideRune (Rune r, int tWidth, out int s, out int l) internal Size GetDisplaySize () { - Size size = Size.Empty; - + var size = Size.Empty; return size; } @@ -799,7 +794,7 @@ internal static int GetColFromX (List t, int start, int x, int tabWidth = string GetText (List x) { - string txt = Cell.ToString (x); + var txt = Cell.ToString (x); if (!matchCase) { @@ -872,7 +867,7 @@ Point start for (int i = start.Y; i < linesCount; i++) { List x = _lines [i]; - string txt = Cell.ToString (x); + var txt = Cell.ToString (x); if (!matchCase) { @@ -912,7 +907,7 @@ Point start for (int i = linesCount; i >= 0; i--) { List x = _lines [i]; - string txt = Cell.ToString (x); + var txt = Cell.ToString (x); if (!matchCase) { @@ -1075,7 +1070,7 @@ private bool MovePrev (ref int col, ref int row, out Rune rune) private string ReplaceText (List source, string textToReplace, string matchText, int col) { - string origTxt = Cell.ToString (source); + var origTxt = Cell.ToString (source); (_, int len) = DisplaySize (source, 0, col, false); (_, int len2) = DisplaySize (source, col, col + matchText.Length, false); (_, int len3) = DisplaySize (source, col + matchText.Length, origTxt.GetRuneCount (), false); @@ -1171,10 +1166,11 @@ public void Clear (List> cellsList) _historyTextItems.Clear (); _idxHistoryText = -1; _originalCellsList.Clear (); + // Save a copy of the original, not the reference foreach (List cells in cellsList) { - _originalCellsList.Add ([..cells]); + _originalCellsList.Add ([.. cells]); } OnChangeText (null); @@ -1678,15 +1674,15 @@ public TextModel WrapModel ( List line = Model.GetLine (i); List> wrappedLines = ToListRune ( - TextFormatter.Format ( - Cell.ToString (line), - width, - Alignment.Start, - true, - preserveTrailingSpaces, - tabWidth - ) - ); + TextFormatter.Format ( + Cell.ToString (line), + width, + Alignment.Start, + true, + preserveTrailingSpaces, + tabWidth + ) + ); var sumColWidth = 0; for (var j = 0; j < wrappedLines.Count; j++) @@ -1885,7 +1881,6 @@ public class TextView : View private WordWrapManager? _wrapManager; private bool _wrapNeeded; - /// /// Initializes a on the specified area, with dimensions controlled with the X, Y, Width /// and Height properties. @@ -1911,7 +1906,7 @@ public TextView () // Things this view knows how to do // Note - NewLine is only bound to Enter if Multiline is true - AddCommand (Command.NewLine, (ctx) => ProcessEnterKey (ctx)); + AddCommand (Command.NewLine, ctx => ProcessEnterKey (ctx)); AddCommand ( Command.PageDown, @@ -2376,7 +2371,7 @@ public TextView () KeyBindings.Add (Key.C.WithCtrl, Command.Copy); KeyBindings.Add (Key.W.WithCtrl, Command.Cut); // Move to Unix? - KeyBindings.Add (Key.X.WithCtrl, Command.Cut); + KeyBindings.Add (Key.X.WithCtrl, Command.Cut); KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.WordLeft); @@ -2422,10 +2417,7 @@ public TextView () KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.Context); } - private void TextView_Added1 (object? sender, SuperViewChangedEventArgs e) - { - throw new NotImplementedException (); - } + private void TextView_Added1 (object? sender, SuperViewChangedEventArgs e) { throw new NotImplementedException (); } // BUGBUG: AllowsReturn is mis-named. It should be EnterKeyAccepts. /// @@ -2435,11 +2427,13 @@ private void TextView_Added1 (object? sender, SuperViewChangedEventArgs e) /// /// /// Setting this property alters . - /// If is set to , then is also set to `true` and + /// If is set to , then is also set to + /// `true` and /// vice-versa. /// /// - /// If is set to , then gets set to . + /// If is set to , then gets set to + /// . /// /// public bool AllowsReturn @@ -2458,6 +2452,7 @@ public bool AllowsReturn if (!_allowsReturn && _multiline) { Multiline = false; + // BUGBUG: Setting properties should not have side-effects like this. Multiline and AllowsTab should be independent. AllowsTab = false; } @@ -2532,7 +2527,6 @@ public Point CursorPosition } } - /// /// Indicates whatever the text has history changes or not. if the text has history changes /// otherwise. @@ -2604,7 +2598,7 @@ public bool Multiline CurrentRow = 0; _savedHeight = Height; - Height = Dim.Auto (DimAutoStyle.Text, minimumContentDim: 1); + Height = Dim.Auto (DimAutoStyle.Text, 1); if (!IsInitialized) { @@ -2805,7 +2799,6 @@ public bool WordWrap } } - /// Allows clearing the items updating the original text. public void ClearHistoryChanges () { _historyText?.Clear (_model.GetAllLines ()); } @@ -2855,7 +2848,7 @@ internal void ApplyCellsAttribute (Attribute attribute) line [c] = cell; // Assign the modified copy back } - selectedCellsChanged.Add ([..GetLine (r)]); + selectedCellsChanged.Add ([.. GetLine (r)]); } GetSelectedRegion (); @@ -2906,10 +2899,10 @@ internal void ApplyCellsAttribute (Attribute attribute) public void PromptForColors () { if (!ColorPicker.Prompt ( - "Colors", - GetSelectedCellAttribute (), - out Attribute newAttribute - )) + "Colors", + GetSelectedCellAttribute (), + out Attribute newAttribute + )) { return; } @@ -3177,10 +3170,7 @@ public bool FindPreviousText ( public List GetLine (int line) { return _model.GetLine (line); } /// - public override Attribute GetNormalColor () - { - return GetFocusColor (); - } + public override Attribute GetNormalColor () { return GetFocusColor (); } /// /// Inserts the given text at the current cursor position exactly as if the user had just @@ -3563,7 +3553,6 @@ public virtual void OnContentsChanged () ProcessInheritsPreviousColorScheme (CurrentRow, CurrentColumn); ProcessAutocomplete (); - } /// @@ -3628,6 +3617,7 @@ public override void OnDrawContent (Rectangle viewport) else { AddRune (col, row, rune); + // Ensures that cols less than 0 to be 1 because it will be converted to a printable rune cols = Math.Max (cols, 1); } @@ -3664,42 +3654,29 @@ public override void OnDrawContent (Rectangle viewport) } /// - protected override bool OnInvokingKeyBindings (Key a, KeyBindingScope scope) + protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view) { - if (!a.IsValid) - { - return false; - } - - // Give autocomplete first opportunity to respond to key presses - if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (a)) + if (Application.MouseGrabView is { } && Application.MouseGrabView == this) { - return true; + Application.UngrabMouse (); } - - return base.OnInvokingKeyBindings (a, scope); } /// - public override bool OnKeyUp (Key key) + protected override bool OnKeyDown (Key key) { - if (key == Key.Space.WithCtrl) + if (!key.IsValid) { - return true; + return false; } - return false; - } - - /// - protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view) - { - if (Application.MouseGrabView is { } && Application.MouseGrabView == this) + // Give autocomplete first opportunity to respond to key presses + if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (key)) { - Application.UngrabMouse (); + return true; } - return; + return false; } /// @@ -3724,6 +3701,17 @@ protected override bool OnKeyDownNotHandled (Key a) return true; } + /// + public override bool OnKeyUp (Key key) + { + if (key == Key.Space.WithCtrl) + { + return true; + } + + return false; + } + /// Invoke the event with the unwrapped . public virtual void OnUnwrappedCursorPosition (int? cRow = null, int? cCol = null) { @@ -3760,7 +3748,7 @@ public void Paste () List> addedLine = [new (currentLine), runeList]; _historyText.Add ( - [..addedLine], + [.. addedLine], CursorPosition, HistoryText.LineStatus.Added ); @@ -3862,6 +3850,7 @@ public void Paste () if (posX > -1 && col >= posX && posX < Viewport.Width && _topRow <= CurrentRow && posY < Viewport.Height) { Move (col, CurrentRow - _topRow); + return new (col, CurrentRow - _topRow); } @@ -4481,12 +4470,12 @@ private bool DeleteTextForwards () } else { - _historyText.Add ([ [.. currentLine]], CursorPosition); + _historyText.Add ([[.. currentLine]], CursorPosition); currentLine.RemoveAt (CurrentColumn); _historyText.Add ( - [ [.. currentLine]], + [[.. currentLine]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -4656,6 +4645,7 @@ internal string GetRegion ( { cells = line.GetRange (startCol, endCol - startCol); cellsList.Add (cells); + return StringFromRunes (cells); } @@ -4668,6 +4658,7 @@ internal string GetRegion ( cellsList.AddRange ([]); cells = model == null ? _model.GetLine (row) : model.GetLine (row); cellsList.Add (cells); + res = res + Environment.NewLine + StringFromRunes (cells); @@ -4703,7 +4694,7 @@ private string GetSelectedRegion () OnUnwrappedCursorPosition (cRow, cCol); - return GetRegion (out _, sRow: startRow, sCol: startCol, cRow: cRow, cCol: cCol, model: model); + return GetRegion (out _, startRow, startCol, cRow, cCol, model); } private (int Row, int Col) GetUnwrappedPosition (int line, int col) @@ -4897,7 +4888,7 @@ private void InsertAllText (string text, bool fromClipboard = false) { _model.AddLine (CurrentRow + i, lines [i]); - addedLines.Add ([..lines [i]]); + addedLines.Add ([.. lines [i]]); } if (rest is { }) @@ -5071,7 +5062,7 @@ private void KillToEndOfLine () } _historyText.Add ( - [ [.. GetCurrentLine ()]], + [[.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5111,7 +5102,7 @@ private void KillToLeftStart () return; } - _historyText.Add ([ [.. currentLine]], CursorPosition); + _historyText.Add ([[.. currentLine]], CursorPosition); if (currentLine.Count == 0) { @@ -5178,7 +5169,7 @@ private void KillToLeftStart () } _historyText.Add ( - [ [.. GetCurrentLine ()]], + [[.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5202,14 +5193,14 @@ private void KillWordBackward () List currentLine = GetCurrentLine (); - _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); + _historyText.Add ([[.. GetCurrentLine ()]], CursorPosition); if (CurrentColumn == 0) { DeleteTextBackwards (); _historyText.ReplaceLast ( - [ [.. GetCurrentLine ()]], + [[.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5248,7 +5239,7 @@ [ [.. GetCurrentLine ()]], } _historyText.Add ( - [ [.. GetCurrentLine ()]], + [[.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5270,14 +5261,14 @@ private void KillWordForward () List currentLine = GetCurrentLine (); - _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition); + _historyText.Add ([[.. GetCurrentLine ()]], CursorPosition); if (currentLine.Count == 0 || CurrentColumn == currentLine.Count) { DeleteTextForwards (); _historyText.ReplaceLast ( - [ [.. GetCurrentLine ()]], + [[.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5307,7 +5298,7 @@ [ [.. GetCurrentLine ()]], } _historyText.Add ( - [ [.. GetCurrentLine ()]], + [[.. GetCurrentLine ()]], CursorPosition, HistoryText.LineStatus.Replaced ); @@ -5579,6 +5570,7 @@ private bool MoveUp () } DoNeededAction (); + return true; } @@ -5919,6 +5911,7 @@ private void ProcessMouseClick (MouseEvent ev, out List line) private bool ProcessMoveDown () { ResetContinuousFindTrack (); + if (_shiftSelecting && IsSelecting) { StopSelecting (); @@ -5961,8 +5954,10 @@ private bool ProcessMoveLeft () if (IsSelecting) { StopSelecting (); + return true; } + // do not respond (this lets the key press fall through to navigation system - which usually changes focus backward) return false; } @@ -6001,8 +5996,10 @@ private bool ProcessMoveRight () { // In which case clear StopSelecting (); + return true; } + return false; } @@ -6296,7 +6293,6 @@ private void ResetContinuousFindTrack () _continuousFind = false; } - private void ResetPosition () { _topRow = _leftColumn = CurrentRow = CurrentColumn = 0; @@ -6461,13 +6457,13 @@ private void TextView_Added (object sender, SuperViewChangedEventArgs e) } } - private void TextView_Initialized (object sender, EventArgs e) { if (Autocomplete.HostControl is null) { Autocomplete.HostControl = this; } + OnContentsChanged (); } diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 59fe8e04fe..36a249e8c8 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -1189,15 +1189,12 @@ protected override bool OnKeyDown (Key keyEvent) return false; } - // BUGBUG: this should move to OnInvokingKeyBindings // If not a keybinding, is the key a searchable key press? if (CollectionNavigatorBase.IsCompatibleKey (keyEvent) && AllowLetterBasedNavigation) { - IReadOnlyCollection> map; - // If there has been a call to InvalidateMap since the last time // we need a new one to reflect the new exposed tree state - map = BuildLineMap (); + IReadOnlyCollection> map = BuildLineMap (); // Find the current selected object within the tree int current = map.IndexOf (b => b.Model == SelectedObject); diff --git a/UnitTests/Application/KeyboardTests.cs b/UnitTests/Application/KeyboardTests.cs index 71b93ecc4d..2cbc322883 100644 --- a/UnitTests/Application/KeyboardTests.cs +++ b/UnitTests/Application/KeyboardTests.cs @@ -64,14 +64,14 @@ public void EnsuresTopOnFront_CanFocus_False_By_Keyboard () Assert.True (win2.HasFocus); Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title); - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.True (win2.CanFocus); Assert.False (win.HasFocus); Assert.True (win2.CanFocus); Assert.True (win2.HasFocus); Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title); - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.False (win.CanFocus); Assert.False (win.HasFocus); Assert.True (win2.CanFocus); @@ -117,14 +117,14 @@ public void EnsuresTopOnFront_CanFocus_True_By_Keyboard () Assert.False (win2.HasFocus); Assert.Equal ("win", ((Window)top.Subviews [^1]).Title); - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.True (win.CanFocus); Assert.False (win.HasFocus); Assert.True (win2.CanFocus); Assert.True (win2.HasFocus); Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title); - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.True (win.CanFocus); Assert.True (win.HasFocus); Assert.True (win2.CanFocus); @@ -170,31 +170,31 @@ public void KeyBinding_OnKeyDown () top.Add (view); Application.Begin (top); - Application.NewKeyDown (Key.A); + Application.RaiseKeyDownEvent (Key.A); Assert.False (invoked); Assert.True (view.ApplicationCommand); invoked = false; view.ApplicationCommand = false; Application.KeyBindings.Remove (KeyCode.A); - Application.NewKeyDown (Key.A); // old + Application.RaiseKeyDownEvent (Key.A); // old Assert.False (invoked); Assert.False (view.ApplicationCommand); Application.KeyBindings.Add (Key.A.WithCtrl, view, Command.Save); - Application.NewKeyDown (Key.A); // old + Application.RaiseKeyDownEvent (Key.A); // old Assert.False (invoked); Assert.False (view.ApplicationCommand); - Application.NewKeyDown (Key.A.WithCtrl); // new + Application.RaiseKeyDownEvent (Key.A.WithCtrl); // new Assert.False (invoked); Assert.True (view.ApplicationCommand); invoked = false; - Application.NewKeyDown (Key.H); + Application.RaiseKeyDownEvent (Key.H); Assert.True (invoked); invoked = false; Assert.False (view.HasFocus); - Application.NewKeyDown (Key.F); + Application.RaiseKeyDownEvent (Key.F); Assert.False (invoked); Assert.True (view.ApplicationCommand); @@ -215,7 +215,7 @@ public void KeyBinding_OnKeyDown_Negative () top.Add (view); Application.Begin (top); - Application.NewKeyDown (Key.A.WithCtrl); + Application.RaiseKeyDownEvent (Key.A.WithCtrl); Assert.False (invoked); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); @@ -223,7 +223,7 @@ public void KeyBinding_OnKeyDown_Negative () invoked = false; Assert.False (view.HasFocus); - Application.NewKeyDown (Key.Z); + Application.RaiseKeyDownEvent (Key.Z); Assert.False (invoked); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); @@ -399,7 +399,7 @@ public void NextTabGroupKey_Moves_Focus_To_TabStop_In_Next_TabGroup () Assert.True (subView1.HasFocus); // Act - Application.NewKeyDown (Application.NextTabGroupKey); + Application.RaiseKeyDownEvent (Application.NextTabGroupKey); // Assert Assert.True (view2.HasFocus); @@ -432,24 +432,24 @@ public void NextTabGroupKey_PrevTabGroupKey_Tests () Assert.True (v1.HasFocus); // Across TabGroups - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.True (v3.HasFocus); - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.True (v1.HasFocus); - Application.NewKeyDown (Key.F6.WithShift); + Application.RaiseKeyDownEvent (Key.F6.WithShift); Assert.True (v3.HasFocus); - Application.NewKeyDown (Key.F6.WithShift); + Application.RaiseKeyDownEvent (Key.F6.WithShift); Assert.True (v1.HasFocus); // Restore? - Application.NewKeyDown (Key.Tab); + Application.RaiseKeyDownEvent (Key.Tab); Assert.True (v2.HasFocus); - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.True (v3.HasFocus); - Application.NewKeyDown (Key.F6); + Application.RaiseKeyDownEvent (Key.F6); Assert.True (v1.HasFocus); Application.RequestStop (); @@ -485,7 +485,7 @@ public void NextTabKey_Moves_Focus_To_Next_TabStop () view1.SetFocus (); // Act - Application.NewKeyDown (Application.NextTabKey); + Application.RaiseKeyDownEvent (Application.NextTabKey); // Assert Assert.True (view2.HasFocus); @@ -539,7 +539,7 @@ public void PrevTabGroupKey_Moves_Focus_To_TabStop_In_Prev_TabGroup () Assert.True (subView1.HasFocus); // Act - Application.NewKeyDown (Application.PrevTabGroupKey); + Application.RaiseKeyDownEvent (Application.PrevTabGroupKey); // Assert Assert.True (view2.HasFocus); @@ -562,7 +562,7 @@ public void PrevTabKey_Moves_Focus_To_Prev_TabStop () view1.SetFocus (); // Act - Application.NewKeyDown (Application.NextTabKey); + Application.RaiseKeyDownEvent (Application.NextTabKey); // Assert Assert.True (view2.HasFocus); @@ -605,21 +605,21 @@ public void QuitKey_Getter_Setter () Key prevKey = Application.QuitKey; - Application.NewKeyDown (Application.QuitKey); + Application.RaiseKeyDownEvent (Application.QuitKey); Assert.True (isQuiting); isQuiting = false; - Application.NewKeyDown (Application.QuitKey); + Application.RaiseKeyDownEvent (Application.QuitKey); Assert.True (isQuiting); isQuiting = false; Application.QuitKey = Key.C.WithCtrl; - Application.NewKeyDown (prevKey); // Should not quit + Application.RaiseKeyDownEvent (prevKey); // Should not quit Assert.False (isQuiting); - Application.NewKeyDown (Key.Q.WithCtrl); // Should not quit + Application.RaiseKeyDownEvent (Key.Q.WithCtrl); // Should not quit Assert.False (isQuiting); - Application.NewKeyDown (Application.QuitKey); + Application.RaiseKeyDownEvent (Application.QuitKey); Assert.True (isQuiting); // Reset the QuitKey to avoid throws errors on another tests @@ -728,7 +728,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) if (Application.IsInitialized) { _output.WriteLine (" Pressing QuitKey"); - Application.NewKeyDown (Application.QuitKey); + Application.RaiseKeyDownEvent (Application.QuitKey); } } } diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index 4575ea5baa..87735fda6c 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -132,7 +132,7 @@ void OnApplicationOnIteration (object s, IterationEventArgs a) { // Press QuitKey //_output.WriteLine ($"Forcing Quit with {Application.QuitKey}"); - Application.NewKeyDown (Application.QuitKey); + Application.RaiseKeyDownEvent (Application.QuitKey); } } } diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs index 9d2535e5c8..938001fe14 100644 --- a/UnitTests/Views/MenuBarTests.cs +++ b/UnitTests/Views/MenuBarTests.cs @@ -2666,7 +2666,7 @@ expectedMenu.Menus [i].Children.Length > 0 top.Draw (); TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - Assert.True (Application.NewKeyDown (menu.Key)); + Assert.True (Application.RaiseKeyDownEvent (menu.Key)); Assert.False (menu.IsMenuOpen); Assert.True (tf.HasFocus); top.Draw (); @@ -2949,7 +2949,7 @@ public void ShortCut_Activates () top.Add (menu); Application.Begin (top); - Application.NewKeyDown (Key.S.WithCtrl); + Application.RaiseKeyDownEvent (Key.S.WithCtrl); Application.MainLoop.RunIteration (); Assert.True (saveAction); diff --git a/UnitTests/Views/RadioGroupTests.cs b/UnitTests/Views/RadioGroupTests.cs index a3e60941f9..ad0ccd630f 100644 --- a/UnitTests/Views/RadioGroupTests.cs +++ b/UnitTests/Views/RadioGroupTests.cs @@ -57,7 +57,7 @@ public void Initialize_SelectedItem_With_Minus_One () rg.SetFocus (); Assert.Equal (-1, rg.SelectedItem); - Application.NewKeyDown (Key.Space); + Application.RaiseKeyDownEvent (Key.Space); Assert.Equal (0, rg.SelectedItem); Application.Top.Dispose (); @@ -105,21 +105,21 @@ public void Commands_HasFocus () // With HasFocus // Test up/down without Select - Assert.False (Application.NewKeyDown (Key.CursorUp)); // Should not change (should focus prev view if there was one, which there isn't) + Assert.False (Application.RaiseKeyDownEvent (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, selectingCount); Assert.Equal (0, acceptedCount); - Assert.True (Application.NewKeyDown (Key.CursorDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); // Cursor changed, but selection didnt Assert.Equal (1, rg.Cursor); Assert.Equal (0, selectedItemChangedCount); Assert.Equal (0, selectingCount); Assert.Equal (0, acceptedCount); - Assert.False (Application.NewKeyDown (Key.CursorDown)); // Should not change selection (should focus next view if there was one, which there isn't) + Assert.False (Application.RaiseKeyDownEvent (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); @@ -127,7 +127,7 @@ public void Commands_HasFocus () Assert.Equal (0, acceptedCount); // Test Select (Space) when Cursor != SelectedItem - Should select cursor - Assert.True (Application.NewKeyDown (Key.Space)); + Assert.True (Application.RaiseKeyDownEvent (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); Assert.Equal (1, selectedItemChangedCount); @@ -135,34 +135,34 @@ public void Commands_HasFocus () Assert.Equal (0, acceptedCount); // Test Select (Space) when Cursor == SelectedItem - Should cycle - Assert.True (Application.NewKeyDown (Key.Space)); + Assert.True (Application.RaiseKeyDownEvent (Key.Space)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); Assert.Equal (2, selectedItemChangedCount); Assert.Equal (2, selectingCount); Assert.Equal (0, acceptedCount); - Assert.True (Application.NewKeyDown (Key.Space)); + Assert.True (Application.RaiseKeyDownEvent (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); - Assert.True (Application.NewKeyDown (Key.Space)); + Assert.True (Application.RaiseKeyDownEvent (Key.Space)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); - Assert.True (Application.NewKeyDown (Key.Space)); + Assert.True (Application.RaiseKeyDownEvent (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); - Assert.True (Application.NewKeyDown (Key.Home)); + Assert.True (Application.RaiseKeyDownEvent (Key.Home)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (0, rg.Cursor); - Assert.True (Application.NewKeyDown (Key.Space)); + Assert.True (Application.RaiseKeyDownEvent (Key.Space)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); - Assert.True (Application.NewKeyDown (Key.End)); + Assert.True (Application.RaiseKeyDownEvent (Key.End)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); - Assert.True (Application.NewKeyDown (Key.Space)); + Assert.True (Application.RaiseKeyDownEvent (Key.Space)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); Assert.Equal (7, selectedItemChangedCount); @@ -174,7 +174,7 @@ public void Commands_HasFocus () rg.HotKey = Key.L; Assert.Equal (Key.L, rg.HotKey); - Assert.True (Application.NewKeyDown (rg.HotKey)); + Assert.True (Application.RaiseKeyDownEvent (rg.HotKey)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); Assert.Equal (8, selectedItemChangedCount); @@ -182,12 +182,12 @@ public void Commands_HasFocus () Assert.Equal (0, acceptedCount); // Make Selected != Cursor - Assert.True (Application.NewKeyDown (Key.CursorDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); // Selected != Cursor - Raise HotKey event - Since we're focused, this should just advance - Assert.True (Application.NewKeyDown (rg.HotKey)); + Assert.True (Application.RaiseKeyDownEvent (rg.HotKey)); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); Assert.Equal (9, selectedItemChangedCount); @@ -239,7 +239,7 @@ public void HotKey_HasFocus_False () // Selected (0) == Cursor (0) - SetFocus rg.HotKey = Key.L; Assert.Equal (Key.L, rg.HotKey); - Assert.True (Application.NewKeyDown (rg.HotKey)); + Assert.True (Application.RaiseKeyDownEvent (rg.HotKey)); Assert.True (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); @@ -248,14 +248,14 @@ public void HotKey_HasFocus_False () Assert.Equal (0, acceptCount); // Make Selected != Cursor - Assert.True (Application.NewKeyDown (Key.CursorDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); otherView.SetFocus (); // Selected != Cursor - SetFocus - Assert.True (Application.NewKeyDown (rg.HotKey)); + Assert.True (Application.RaiseKeyDownEvent (rg.HotKey)); Assert.True (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -263,7 +263,7 @@ public void HotKey_HasFocus_False () Assert.Equal (0, selectCount); Assert.Equal (0, acceptCount); - Assert.True (Application.NewKeyDown (rg.HotKey)); + Assert.True (Application.RaiseKeyDownEvent (rg.HotKey)); Assert.True (rg.HasFocus); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -314,7 +314,7 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () // Test RadioTitem.HotKey - Should never SetFocus // Selected (0) == Cursor (0) - Assert.True (Application.NewKeyDown (Key.A)); + Assert.True (Application.RaiseKeyDownEvent (Key.A)); Assert.False (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (0, rg.Cursor); @@ -325,14 +325,14 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () rg.SetFocus (); // Make Selected != Cursor - Assert.True (Application.NewKeyDown (Key.CursorDown)); + Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown)); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); otherView.SetFocus (); // Selected != Cursor - Assert.True (Application.NewKeyDown (Key.A)); + Assert.True (Application.RaiseKeyDownEvent (Key.A)); Assert.False (rg.HasFocus); Assert.Equal (0, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -341,7 +341,7 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () Assert.Equal (0, acceptCount); // Selected != Cursor - Should not set focus - Assert.True (Application.NewKeyDown (Key.B)); + Assert.True (Application.RaiseKeyDownEvent (Key.B)); Assert.False (rg.HasFocus); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -349,7 +349,7 @@ public void HotKeys_HasFocus_False_Does_Not_SetFocus_Selects () Assert.Equal (1, selectCount); Assert.Equal (0, acceptCount); - Assert.True (Application.NewKeyDown (Key.B)); + Assert.True (Application.RaiseKeyDownEvent (Key.B)); Assert.False (rg.HasFocus); Assert.Equal (1, rg.SelectedItem); Assert.Equal (1, rg.Cursor); @@ -372,22 +372,22 @@ public void HotKeys_HasFocus_True_Selects () Assert.NotEmpty (rg.KeyBindings.GetCommands (KeyCode.L | KeyCode.ShiftMask)); Assert.NotEmpty (rg.KeyBindings.GetCommands (KeyCode.L | KeyCode.AltMask)); - Assert.True (Application.NewKeyDown (Key.T)); + Assert.True (Application.RaiseKeyDownEvent (Key.T)); Assert.Equal (2, rg.SelectedItem); - Assert.True (Application.NewKeyDown (Key.L)); + Assert.True (Application.RaiseKeyDownEvent (Key.L)); Assert.Equal (0, rg.SelectedItem); - Assert.True (Application.NewKeyDown (Key.J)); + Assert.True (Application.RaiseKeyDownEvent (Key.J)); Assert.Equal (3, rg.SelectedItem); - Assert.True (Application.NewKeyDown (Key.R)); + Assert.True (Application.RaiseKeyDownEvent (Key.R)); Assert.Equal (1, rg.SelectedItem); - Assert.True (Application.NewKeyDown (Key.T.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.T.WithAlt)); Assert.Equal (2, rg.SelectedItem); - Assert.True (Application.NewKeyDown (Key.L.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.L.WithAlt)); Assert.Equal (0, rg.SelectedItem); - Assert.True (Application.NewKeyDown (Key.J.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.J.WithAlt)); Assert.Equal (3, rg.SelectedItem); - Assert.True (Application.NewKeyDown (Key.R.WithAlt)); + Assert.True (Application.RaiseKeyDownEvent (Key.R.WithAlt)); Assert.Equal (1, rg.SelectedItem); var superView = new View (); diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 9fb6d3eac6..0de972262d 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -524,7 +524,7 @@ public void Space_Does_Not_Raise_Selected () Application.Top = new (); Application.Top.Add (tf); tf.SetFocus (); - Application.NewKeyDown (Key.Space); + Application.RaiseKeyDownEvent (Key.Space); Application.Top.Dispose (); Application.ResetState (true); @@ -541,7 +541,7 @@ public void Enter_Does_Not_Raise_Selected () Application.Top = new (); Application.Top.Add (tf); tf.SetFocus (); - Application.NewKeyDown (Key.Enter); + Application.RaiseKeyDownEvent (Key.Enter); Assert.Equal (0, selectingCount); @@ -560,7 +560,7 @@ public void Enter_Raises_Accepted () Application.Top = new (); Application.Top.Add (tf); tf.SetFocus (); - Application.NewKeyDown (Key.Enter); + Application.RaiseKeyDownEvent (Key.Enter); Assert.Equal (1, acceptedCount); From 3f3ceae9c7219206cd03fcc8d5cdbaaf1422bae2 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 16:35:10 -0600 Subject: [PATCH 11/15] API docs --- Terminal.Gui/View/View.Keyboard.cs | 79 ++----- Terminal.Gui/Views/Menu/Menu.cs | 2 +- UICatalog/Scenarios/Keys.cs | 166 +++++++++------ UICatalog/Scenarios/VkeyPacketSimulator.cs | 2 +- UnitTests/Application/KeyboardTests.cs | 32 +-- UnitTests/View/{ => Keyboard}/HotKeyTests.cs | 12 +- UnitTests/View/Keyboard/KeyboardEventTests.cs | 199 ++---------------- .../{ => Keyboard}/ViewKeyBindingTests.cs | 54 ++--- docfx/docs/keyboard.md | 38 ++-- 9 files changed, 211 insertions(+), 373 deletions(-) rename UnitTests/View/{ => Keyboard}/HotKeyTests.cs (97%) rename UnitTests/View/{ => Keyboard}/ViewKeyBindingTests.cs (76%) diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index b78eb2804b..10f1f97df8 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -255,9 +255,7 @@ private void SetHotKeyFromTitle () /// /// If a more focused subview does not handle the key press, this method raises / /// to allow the - /// view to pre-process the key press. If / is not handled - /// / will be raised to invoke any key - /// bindings. + /// view to pre-process the key press. If / is not handled any commands bound to the key will be invoked. /// Then, only if no key bindings are /// handled, / will be raised allowing the view to /// process the key press. @@ -292,8 +290,8 @@ public bool NewKeyDownEvent (Key key) // During (this is what can be cancelled) - // TODO: NewKeyDownEvent returns bool. It should be bool? so state of RaiseInvokingKeyBindingsAndInvokeCommands can be reflected up stack - if (RaiseInvokingKeyBindingsAndInvokeCommands (key) is true || key.Handled) + // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommands can be reflected up stack + if (InvokeCommandsBoundToKey (key) is true || key.Handled) { return true; } @@ -336,7 +334,7 @@ bool RaiseKeyDownNotHandled (Key k) /// /// Called when the user presses a key, allowing subscribers to pre-process the key down event. Called - /// before and are raised. Set + /// before key bindings are invoked and is raised. Set /// /// to true to /// stop the key from being processed further. @@ -357,7 +355,7 @@ bool RaiseKeyDownNotHandled (Key k) /// /// Raised when the user presses a key, allowing subscribers to pre-process the key down event. Called - /// before and are raised. Set + /// before key bindings are invoked and is raised. Set /// /// to true to /// stop the key from being processed further. @@ -508,8 +506,7 @@ bool RaiseKeyUp (Key k) private Dictionary CommandImplementations { get; } = new (); /// - /// INTERNAL API: Raises the event and invokes the commands bound to - /// . + /// INTERNAL API: Invokes any commands bound to on this view, adornments, and subviews. /// /// /// @@ -517,28 +514,13 @@ bool RaiseKeyUp (Key k) /// continue. /// if a command was invoked and was not handled (or cancelled); input processing should /// continue. - /// if was handled or a command was invoked and handled (or + /// if at least one command was invoked and handled (or /// cancelled); input processing should stop. /// - internal bool? RaiseInvokingKeyBindingsAndInvokeCommands (Key key) + internal bool? InvokeCommandsBoundToKey (Key key) { KeyBindingScope scope = KeyBindingScope.Focused | KeyBindingScope.HotKey; - // During - if (OnInvokingKeyBindings (key, scope)) - { - return true; - } - - InvokingKeyBindings?.Invoke (this, key); - - if (key.Handled) - { - return true; - } - - // After - // * If no key binding was found, `InvokeKeyBindings` returns `null`. // Continue passing the event (return `false` from `OnInvokeKeyBindings`). // * If key bindings were found, but none handled the key (all `Command`s returned `false`), @@ -547,29 +529,29 @@ bool RaiseKeyUp (Key k) // `InvokeKeyBindings` returns `true`. Continue passing the event (return `false` from `OnInvokeKeyBindings`). bool? handled = InvokeCommands (key, scope); - if (handled is { } && (bool)handled) + if (handled is true) { // Stop processing if any key binding handled the key. // DO NOT stop processing if there are no matching key bindings or none of the key bindings handled the key return handled; } - if (Margin is { } && ProcessAdornmentKeyBindings (Margin, key, scope, ref handled)) + if (Margin is { } && InvokeCommandsBoundToKeyOnAdornment (Margin, key, scope, ref handled)) { return true; } - if (Padding is { } && ProcessAdornmentKeyBindings (Padding, key, scope, ref handled)) + if (Padding is { } && InvokeCommandsBoundToKeyOnAdornment (Padding, key, scope, ref handled)) { return true; } - if (Border is { } && ProcessAdornmentKeyBindings (Border, key, scope, ref handled)) + if (Border is { } && InvokeCommandsBoundToKeyOnAdornment (Border, key, scope, ref handled)) { return true; } - if (ProcessSubViewKeyBindings (key, scope, ref handled)) + if (InvokeCommandsBoundToKeyOnSubviews (key, scope, ref handled)) { return true; } @@ -577,32 +559,9 @@ bool RaiseKeyUp (Key k) return handled; } - /// - /// Called when a key is pressed that may be mapped to a key binding. Set to true to - /// stop the key from being processed by other views. - /// - /// - /// See for an overview of Terminal.Gui keyboard APIs. - /// - /// Contains the details about the key that produced the event. - /// The scope. - /// - /// if the event was raised and was not handled (or cancelled); input processing should - /// continue. - /// if the event was raised and handled (or cancelled); input processing should stop. - /// - protected virtual bool OnInvokingKeyBindings (Key key, KeyBindingScope scope) { return false; } - - // TODO: This does not carry KeyBindingScope, but OnInvokingKeyBindings does - /// - /// Raised when a key is pressed that may be mapped to a key binding. Set to true to - /// stop the key from being processed by other views. - /// - public event EventHandler? InvokingKeyBindings; - - private bool ProcessAdornmentKeyBindings (Adornment adornment, Key key, KeyBindingScope scope, ref bool? handled) + private static bool InvokeCommandsBoundToKeyOnAdornment (Adornment adornment, Key key, KeyBindingScope scope, ref bool? handled) { - bool? adornmentHandled = adornment.RaiseInvokingKeyBindingsAndInvokeCommands (key); + bool? adornmentHandled = adornment.InvokeCommandsBoundToKey (key); if (adornmentHandled is true) { @@ -616,7 +575,7 @@ private bool ProcessAdornmentKeyBindings (Adornment adornment, Key key, KeyBindi foreach (View subview in adornment.Subviews) { - bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (key); + bool? subViewHandled = subview.InvokeCommandsBoundToKey (key); if (subViewHandled is { }) { @@ -632,7 +591,7 @@ private bool ProcessAdornmentKeyBindings (Adornment adornment, Key key, KeyBindi return false; } - private bool ProcessSubViewKeyBindings (Key key, KeyBindingScope scope, ref bool? handled, bool invoke = true) + private bool InvokeCommandsBoundToKeyOnSubviews (Key key, KeyBindingScope scope, ref bool? handled, bool invoke = true) { // Now, process any key bindings in the subviews that are tagged to KeyBindingScope.HotKey. foreach (View subview in Subviews) @@ -654,7 +613,7 @@ private bool ProcessSubViewKeyBindings (Key key, KeyBindingScope scope, ref bool return true; } - bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (key); + bool? subViewHandled = subview.InvokeCommandsBoundToKey (key); if (subViewHandled is { }) { @@ -667,7 +626,7 @@ private bool ProcessSubViewKeyBindings (Key key, KeyBindingScope scope, ref bool } } - bool recurse = subview.ProcessSubViewKeyBindings (key, scope, ref handled, invoke); + bool recurse = subview.InvokeCommandsBoundToKeyOnSubviews (key, scope, ref handled, invoke); if (recurse || (handled is { } && (bool)handled)) { diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs index b3429b190c..b961f68bc7 100644 --- a/Terminal.Gui/Views/Menu/Menu.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -309,7 +309,7 @@ private bool ExpandCollapse (MenuItem? menuItem) protected override bool OnKeyDownNotHandled (Key keyEvent) { // We didn't handle the key, pass it on to host - return _host.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent) == true; + return _host.InvokeCommandsBoundToKey (keyEvent) == true; } private void Current_TerminalResized (object? sender, SizeChangedEventArgs e) diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index a96f256bb2..7851d342d1 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -10,109 +10,136 @@ public class Keys : Scenario public override void Main () { Application.Init (); - ObservableCollection keyPressedList = []; - ObservableCollection invokingKeyBindingsList = new (); + ObservableCollection keyDownList = []; + ObservableCollection keyDownNotHandledList = new (); var win = new Window { Title = GetQuitKeyAndName () }; - var editLabel = new Label { X = 0, Y = 0, Text = "Type text here:" }; - win.Add (editLabel); - var edit = new TextField { X = Pos.Right (editLabel) + 1, Y = Pos.Top (editLabel), Width = Dim.Fill (2) }; + var label = new Label + { + X = 0, + Y = 0, + Text = "_Type text here:" + }; + win.Add (label); + + var edit = new TextField + { + X = Pos.Right (label) + 1, + Y = Pos.Top (label), + Width = Dim.Fill (2), + Height = 1, + }; win.Add (edit); - edit.KeyDown += (s, a) => { keyPressedList.Add (a.ToString ()); }; + label = new Label + { + X = 0, + Y = Pos.Bottom (label), + Text = "Last _Application.KeyDown:" + }; + win.Add (label); + var labelAppKeypress = new Label + { + X = Pos.Right (label) + 1, + Y = Pos.Top (label) + }; + win.Add (labelAppKeypress); - edit.InvokingKeyBindings += (s, a) => - { - if (edit.KeyBindings.TryGet (a, out KeyBinding binding)) - { - invokingKeyBindingsList.Add ($"{a}: {string.Join (",", binding.Commands)}"); - } - }; + Application.KeyDown += (s, e) => labelAppKeypress.Text = e.ToString (); - // Last KeyPress: ______ - var keyPressedLabel = new Label + label = new () { - X = Pos.Left (editLabel), Y = Pos.Top (editLabel) + 1, Text = "Last TextView.KeyPressed:" + X = 0, + Y = Pos.Bottom (label), + Text = "_Last TextField.KeyDown:" }; - win.Add (keyPressedLabel); - var labelTextViewKeypress = new Label { X = Pos.Right (keyPressedLabel) + 1, Y = Pos.Top (keyPressedLabel) }; - win.Add (labelTextViewKeypress); + win.Add (label); - edit.KeyDown += (s, e) => labelTextViewKeypress.Text = e.ToString (); - - keyPressedLabel = new Label + var lastTextFieldKeyDownLabel = new Label { - X = Pos.Left (keyPressedLabel), Y = Pos.Bottom (keyPressedLabel), Text = "Last Application.KeyDown:" + X = Pos.Right (label) + 1, + Y = Pos.Top (label), + Height = 1, }; - win.Add (keyPressedLabel); - var labelAppKeypress = new Label { X = Pos.Right (keyPressedLabel) + 1, Y = Pos.Top (keyPressedLabel) }; - win.Add (labelAppKeypress); + win.Add (lastTextFieldKeyDownLabel); - Application.KeyDown += (s, e) => labelAppKeypress.Text = e.ToString (); + edit.KeyDown += (s, e) => lastTextFieldKeyDownLabel.Text = e.ToString (); - // Key stroke log: - var keyLogLabel = new Label + // Application key event log: + label = new Label { - X = Pos.Left (editLabel), Y = Pos.Top (editLabel) + 4, Text = "Application Key Events:" + X = 0, + Y = Pos.Bottom (label) + 1, + Text = "Application Key Events:" }; - win.Add (keyLogLabel); + win.Add (label); int maxKeyString = Key.CursorRight.WithAlt.WithCtrl.WithShift.ToString ().Length; - var yOffset = 1; - ObservableCollection keyEventlist = new (); - var keyEventListView = new ListView + ObservableCollection keyEventList = new (); + + var appKeyEventListView = new ListView { X = 0, - Y = Pos.Top (keyLogLabel) + yOffset, - Width = "Key Down:".Length + maxKeyString, + Y = Pos.Bottom (label), + Width = "KeyDown:".Length + maxKeyString, Height = Dim.Fill (), - Source = new ListWrapper (keyEventlist) + Source = new ListWrapper (keyEventList) }; - keyEventListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; - win.Add (keyEventListView); - - // OnKeyPressed - var onKeyPressedLabel = new Label + appKeyEventListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; + win.Add (appKeyEventListView); + + // View key events... + edit.KeyDown += (s, a) => { keyDownList.Add (a.ToString ()); }; + + edit.KeyDownNotHandled += (s, a) => + { + if (edit.KeyBindings.TryGet (a, out KeyBinding binding)) + { + keyDownNotHandledList.Add ($"{a}: {string.Join (",", binding.Commands)}"); + } + }; + + // KeyDown + label = new Label { - X = Pos.Right (keyEventListView) + 1, Y = Pos.Top (editLabel) + 4, Text = "TextView KeyDown:" + X = Pos.Right (appKeyEventListView) + 1, + Y = Pos.Top (label), + Text = "TextView Key Down:" }; - win.Add (onKeyPressedLabel); + win.Add (label); - yOffset = 1; - - var onKeyPressedListView = new ListView + var onKeyDownListView = new ListView { - X = Pos.Left (onKeyPressedLabel), - Y = Pos.Top (onKeyPressedLabel) + yOffset, + X = Pos.Left (label), + Y = Pos.Bottom (label), Width = maxKeyString, Height = Dim.Fill (), - Source = new ListWrapper (keyPressedList) + Source = new ListWrapper (keyDownList) }; - onKeyPressedListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; - win.Add (onKeyPressedListView); + onKeyDownListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; + win.Add (onKeyDownListView); - // OnInvokeKeyBindings - var onInvokingKeyBindingsLabel = new Label + // KeyDownNotHandled + label = new Label { - X = Pos.Right (onKeyPressedListView) + 1, - Y = Pos.Top (editLabel) + 4, - Text = "TextView InvokingKeyBindings:" + X = Pos.Right (onKeyDownListView) + 1, + Y = Pos.Top (label), + Text = "TextView KeyDownNotHandled:" }; - win.Add (onInvokingKeyBindingsLabel); + win.Add (label); - var onInvokingKeyBindingsListView = new ListView + var onKeyDownNotHandledListView = new ListView { - X = Pos.Left (onInvokingKeyBindingsLabel), - Y = Pos.Top (onInvokingKeyBindingsLabel) + yOffset, - Width = Dim.Fill (1), + X = Pos.Left (label), + Y = Pos.Bottom (label), + Width = maxKeyString, Height = Dim.Fill (), - Source = new ListWrapper (invokingKeyBindingsList) + Source = new ListWrapper (keyDownNotHandledList) }; - onInvokingKeyBindingsListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; - win.Add (onInvokingKeyBindingsListView); + onKeyDownNotHandledListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; + win.Add (onKeyDownNotHandledListView); - //Application.KeyDown += (s, a) => KeyDownPressUp (a, "Down"); Application.KeyDown += (s, a) => KeyDownPressUp (a, "Down"); Application.KeyUp += (s, a) => KeyDownPressUp (a, "Up"); @@ -120,10 +147,9 @@ void KeyDownPressUp (Key args, string updown) { // BUGBUG: KeyEvent.ToString is badly broken var msg = $"Key{updown,-7}: {args}"; - keyEventlist.Add (msg); - keyEventListView.MoveDown (); - onKeyPressedListView.MoveDown (); - onInvokingKeyBindingsListView.MoveDown (); + keyEventList.Add (msg); + appKeyEventListView.MoveDown (); + onKeyDownNotHandledListView.MoveDown (); } Application.Run (win); diff --git a/UICatalog/Scenarios/VkeyPacketSimulator.cs b/UICatalog/Scenarios/VkeyPacketSimulator.cs index 059203ea7c..1659eaebc5 100644 --- a/UICatalog/Scenarios/VkeyPacketSimulator.cs +++ b/UICatalog/Scenarios/VkeyPacketSimulator.cs @@ -148,7 +148,7 @@ public override void Main () } }; - tvInput.InvokingKeyBindings += (s, e) => + tvInput.KeyDownNotHandled += (s, e) => { Key ev = e; diff --git a/UnitTests/Application/KeyboardTests.cs b/UnitTests/Application/KeyboardTests.cs index 2cbc322883..ba472d3aba 100644 --- a/UnitTests/Application/KeyboardTests.cs +++ b/UnitTests/Application/KeyboardTests.cs @@ -163,39 +163,39 @@ public void KeyBinding_Application_RemoveKeyBinding_Removes () public void KeyBinding_OnKeyDown () { var view = new ScopedKeyBindingView (); - var invoked = false; - view.InvokingKeyBindings += (s, e) => invoked = true; + var keyWasHandled = false; + view.KeyDownNotHandled += (s, e) => keyWasHandled = true; var top = new Toplevel (); top.Add (view); Application.Begin (top); Application.RaiseKeyDownEvent (Key.A); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.True (view.ApplicationCommand); - invoked = false; + keyWasHandled = false; view.ApplicationCommand = false; Application.KeyBindings.Remove (KeyCode.A); Application.RaiseKeyDownEvent (Key.A); // old - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.ApplicationCommand); Application.KeyBindings.Add (Key.A.WithCtrl, view, Command.Save); Application.RaiseKeyDownEvent (Key.A); // old - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.ApplicationCommand); Application.RaiseKeyDownEvent (Key.A.WithCtrl); // new - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.True (view.ApplicationCommand); - invoked = false; + keyWasHandled = false; Application.RaiseKeyDownEvent (Key.H); - Assert.True (invoked); + Assert.False (keyWasHandled); - invoked = false; + keyWasHandled = false; Assert.False (view.HasFocus); Application.RaiseKeyDownEvent (Key.F); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.True (view.ApplicationCommand); Assert.True (view.HotKeyCommand); @@ -208,23 +208,23 @@ public void KeyBinding_OnKeyDown () public void KeyBinding_OnKeyDown_Negative () { var view = new ScopedKeyBindingView (); - var invoked = false; - view.InvokingKeyBindings += (s, e) => invoked = true; + var keyWasHandled = false; + view.KeyDownNotHandled += (s, e) => keyWasHandled = true; var top = new Toplevel (); top.Add (view); Application.Begin (top); Application.RaiseKeyDownEvent (Key.A.WithCtrl); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); Assert.False (view.FocusedCommand); - invoked = false; + keyWasHandled = false; Assert.False (view.HasFocus); Application.RaiseKeyDownEvent (Key.Z); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); Assert.False (view.FocusedCommand); diff --git a/UnitTests/View/HotKeyTests.cs b/UnitTests/View/Keyboard/HotKeyTests.cs similarity index 97% rename from UnitTests/View/HotKeyTests.cs rename to UnitTests/View/Keyboard/HotKeyTests.cs index a3edaaf6c0..4c1e7812cd 100644 --- a/UnitTests/View/HotKeyTests.cs +++ b/UnitTests/View/Keyboard/HotKeyTests.cs @@ -95,7 +95,7 @@ public void NewKeyDownEvent_Ignores_Focus_KeyBindings_SuperView () { var view = new View (); view.KeyBindings.Add (Key.A, Command.HotKey); // implies KeyBindingScope.Focused - so this should not be invoked - view.InvokingKeyBindings += (s, e) => { Assert.Fail (); }; + view.KeyDownNotHandled += (s, e) => { Assert.Fail (); }; var superView = new View (); superView.Add (view); @@ -109,8 +109,11 @@ public void NewKeyDownEvent_Honors_HotKey_KeyBindings_SuperView () { var view = new View (); view.KeyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey); - bool invoked = false; - view.InvokingKeyBindings += (s, e) => { invoked = true; }; + bool hotKeyInvoked = false; + view.HandlingHotKey += (s, e) => { hotKeyInvoked = true; }; + + bool notHandled = false; + view.KeyDownNotHandled += (s, e) => { notHandled = true; }; var superView = new View (); superView.Add (view); @@ -118,7 +121,8 @@ public void NewKeyDownEvent_Honors_HotKey_KeyBindings_SuperView () var ke = Key.A; superView.NewKeyDownEvent (ke); - Assert.True (invoked); + Assert.False (notHandled); + Assert.True (hotKeyInvoked); } diff --git a/UnitTests/View/Keyboard/KeyboardEventTests.cs b/UnitTests/View/Keyboard/KeyboardEventTests.cs index 6b484ec58e..12cffb9a73 100644 --- a/UnitTests/View/Keyboard/KeyboardEventTests.cs +++ b/UnitTests/View/Keyboard/KeyboardEventTests.cs @@ -7,8 +7,8 @@ namespace Terminal.Gui.ViewTests; public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews { /// - /// This tests that when a new key down event is sent to the view will fire the 3 key-down related - /// events: KeyDown, InvokingKeyBindings, and ProcessKeyDown. Note that KeyUp is independent. + /// This tests that when a new key down event is sent to the view will fire the key-down related + /// events: KeyDown and KeyDownNotHandled. Note that KeyUp is independent. /// [Theory] [MemberData (nameof (AllViewTypes))] @@ -33,27 +33,18 @@ public void AllViews_NewKeyDownEvent_All_EventsFire (Type viewType) keyDown = true; }; - var invokingKeyBindings = false; - - view.InvokingKeyBindings += (s, a) => - { - a.Handled = false; // don't handle it so the other events are called - invokingKeyBindings = true; - }; - - var keyDownProcessed = false; + var keyDownNotHandled = false; view.KeyDownNotHandled += (s, a) => { a.Handled = true; - keyDownProcessed = true; + keyDownNotHandled = true; }; // Key.Empty is invalid, but it's used here to test that the event is fired Assert.True (view.NewKeyDownEvent (Key.Empty)); // this will be true because the ProcessKeyDown event handled it Assert.True (keyDown); - Assert.True (invokingKeyBindings); - Assert.True (keyDownProcessed); + Assert.True (keyDownNotHandled); view.Dispose (); } @@ -96,7 +87,7 @@ public void AllViews_NewKeyUpEvent_All_EventsFire (Type viewType) public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool shift, bool alt, bool control) { var keyDown = false; - var keyPressed = false; + var keyDownNotHandled = false; var keyUp = false; var view = new OnNewKeyTestView (); @@ -112,7 +103,7 @@ public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool s Assert.True (view.OnKeyDownCalled); keyDown = true; }; - view.KeyDownNotHandled += (s, e) => { keyPressed = true; }; + view.KeyDownNotHandled += (s, e) => { keyDownNotHandled = true; }; view.KeyUp += (s, e) => { @@ -125,11 +116,6 @@ public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool s keyUp = true; }; - //view.ProcessKeyDownEvent (new (Key.Null | (shift ? Key.ShiftMask : 0) | (alt ? Key.AltMask : 0) | (control ? Key.CtrlMask : 0))); - //Assert.True (keyDown); - //Assert.True (view.OnKeyDownWasCalled); - //Assert.True (view.OnProcessKeyDownWasCalled); - view.NewKeyDownEvent ( new ( KeyCode.Null @@ -138,7 +124,7 @@ public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool s | (control ? KeyCode.CtrlMask : 0) ) ); - Assert.True (keyPressed); + Assert.True (keyDownNotHandled); Assert.True (view.OnKeyDownCalled); Assert.True (view.OnProcessKeyDownCalled); @@ -154,107 +140,11 @@ public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool s Assert.True (view.OnKeyUpCalled); } - [Fact] - public void NewKeyDownEvent_InvokingKeyBindings_Handled_Cancels () - { - var view = new View (); - var keyPressInvoked = false; - var invokingKeyBindingsInvoked = false; - var processKeyPressInvoked = false; - var setHandledTo = false; - - view.KeyDown += (s, e) => - { - keyPressInvoked = true; - Assert.False (e.Handled); - Assert.Equal (KeyCode.N, e.KeyCode); - }; - - view.InvokingKeyBindings += (s, e) => - { - invokingKeyBindingsInvoked = true; - e.Handled = setHandledTo; - Assert.Equal (setHandledTo, e.Handled); - Assert.Equal (KeyCode.N, e.KeyCode); - }; - - view.KeyDownNotHandled += (s, e) => - { - processKeyPressInvoked = true; - processKeyPressInvoked = true; - Assert.False (e.Handled); - Assert.Equal (KeyCode.N, e.KeyCode); - }; - - view.NewKeyDownEvent (Key.N); - Assert.True (keyPressInvoked); - Assert.True (invokingKeyBindingsInvoked); - Assert.True (processKeyPressInvoked); - - keyPressInvoked = false; - invokingKeyBindingsInvoked = false; - processKeyPressInvoked = false; - setHandledTo = true; - view.NewKeyDownEvent (Key.N); - Assert.True (keyPressInvoked); - Assert.True (invokingKeyBindingsInvoked); - Assert.False (processKeyPressInvoked); - } - - [Fact] - public void NewKeyDownEvent_InvokingKeyBindings_Handled_True_Stops_Processing () - { - var keyDown = false; - var invokingKeyBindings = false; - var keyPressed = false; - - var view = new OnNewKeyTestView (); - Assert.True (view.CanFocus); - view.CancelVirtualMethods = false; - - view.KeyDown += (s, e) => - { - Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (keyDown); - Assert.True (view.OnKeyDownCalled); - e.Handled = false; - keyDown = true; - }; - - view.InvokingKeyBindings += (s, e) => - { - Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (keyPressed); - Assert.True (view.OnInvokingKeyBindingsCalled); - e.Handled = true; - invokingKeyBindings = true; - }; - - view.KeyDownNotHandled += (s, e) => - { - Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (keyPressed); - Assert.False (view.OnProcessKeyDownCalled); - e.Handled = true; - keyPressed = true; - }; - - view.NewKeyDownEvent (Key.A); - Assert.True (keyDown); - Assert.True (invokingKeyBindings); - Assert.False (keyPressed); - - Assert.True (view.OnKeyDownCalled); - Assert.True (view.OnInvokingKeyBindingsCalled); - Assert.False (view.OnProcessKeyDownCalled); - } - [Fact] public void NewKeyDownEvent_Handled_True_Stops_Processing () { var keyDown = false; - var invokingKeyBindings = false; - var keyPressed = false; + var keyDownNotHandled = false; var view = new OnNewKeyTestView (); Assert.True (view.CanFocus); @@ -269,31 +159,21 @@ public void NewKeyDownEvent_Handled_True_Stops_Processing () keyDown = true; }; - view.InvokingKeyBindings += (s, e) => - { - Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (keyPressed); - Assert.False (view.OnInvokingKeyBindingsCalled); - e.Handled = true; - invokingKeyBindings = true; - }; view.KeyDownNotHandled += (s, e) => { Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (keyPressed); + Assert.False (keyDownNotHandled); Assert.False (view.OnProcessKeyDownCalled); e.Handled = true; - keyPressed = true; + keyDownNotHandled = true; }; view.NewKeyDownEvent (Key.A); Assert.True (keyDown); - Assert.False (invokingKeyBindings); - Assert.False (keyPressed); + Assert.False (keyDownNotHandled); Assert.True (view.OnKeyDownCalled); - Assert.False (view.OnInvokingKeyBindingsCalled); Assert.False (view.OnProcessKeyDownCalled); } @@ -301,8 +181,7 @@ public void NewKeyDownEvent_Handled_True_Stops_Processing () public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing () { var view = new View (); - var invokingKeyBindingsInvoked = false; - var processKeyPressInvoked = false; + var keyDownNotHandled = false; var setHandledTo = false; view.KeyDown += (s, e) => @@ -312,38 +191,27 @@ public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing () Assert.Equal (KeyCode.N, e.KeyCode); }; - view.InvokingKeyBindings += (s, e) => - { - invokingKeyBindingsInvoked = true; - Assert.False (e.Handled); - Assert.Equal (KeyCode.N, e.KeyCode); - }; - view.KeyDownNotHandled += (s, e) => { - processKeyPressInvoked = true; + keyDownNotHandled = true; Assert.False (e.Handled); Assert.Equal (KeyCode.N, e.KeyCode); }; view.NewKeyDownEvent (Key.N); - Assert.True (invokingKeyBindingsInvoked); - Assert.True (processKeyPressInvoked); + Assert.True (keyDownNotHandled); - invokingKeyBindingsInvoked = false; - processKeyPressInvoked = false; + keyDownNotHandled = false; setHandledTo = true; view.NewKeyDownEvent (Key.N); - Assert.False (invokingKeyBindingsInvoked); - Assert.False (processKeyPressInvoked); + Assert.False (keyDownNotHandled); } [Fact] public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing () { var keyDown = false; - var invokingKeyBindings = false; - var processKeyDown = false; + var keyDownNotHandled = false; var view = new OnNewKeyTestView (); Assert.True (view.CanFocus); @@ -358,31 +226,20 @@ public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing () keyDown = true; }; - view.InvokingKeyBindings += (s, e) => - { - Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (processKeyDown); - Assert.True (view.OnInvokingKeyBindingsCalled); - e.Handled = false; - invokingKeyBindings = true; - }; - view.KeyDownNotHandled += (s, e) => { Assert.Equal (KeyCode.A, e.KeyCode); - Assert.False (processKeyDown); + Assert.False (keyDownNotHandled); Assert.True (view.OnProcessKeyDownCalled); e.Handled = true; - processKeyDown = true; + keyDownNotHandled = true; }; view.NewKeyDownEvent (Key.A); Assert.True (keyDown); - Assert.True (invokingKeyBindings); - Assert.True (processKeyDown); + Assert.True (keyDownNotHandled); Assert.True (view.OnKeyDownCalled); - Assert.True (view.OnInvokingKeyBindingsCalled); Assert.True (view.OnProcessKeyDownCalled); } @@ -409,7 +266,6 @@ public void NewKeyUpEvent_KeyUp_Handled_True_Stops_Processing () Assert.True (view.OnKeyUpCalled); Assert.False (view.OnKeyDownCalled); - Assert.False (view.OnInvokingKeyBindingsCalled); Assert.False (view.OnProcessKeyDownCalled); } @@ -417,12 +273,12 @@ public void NewKeyUpEvent_KeyUp_Handled_True_Stops_Processing () [InlineData (null, null)] [InlineData (true, true)] [InlineData (false, false)] - public void RaiseInvokingKeyBindingsAndInvokeCommands_Returns_Nullable_Properly (bool? toReturn, bool? expected) + public void InvokeCommandsBoundToKey_Returns_Nullable_Properly (bool? toReturn, bool? expected) { var view = new KeyBindingsTestView (); view.CommandReturns = toReturn; - bool? result = view.RaiseInvokingKeyBindingsAndInvokeCommands (Key.A); + bool? result = view.InvokeCommandsBoundToKey (Key.A); Assert.Equal (expected, result); } @@ -444,20 +300,11 @@ public class OnNewKeyTestView : View { public OnNewKeyTestView () { CanFocus = true; } public bool CancelVirtualMethods { set; private get; } - public bool OnInvokingKeyBindingsCalled { get; set; } public bool OnKeyDownCalled { get; set; } public bool OnProcessKeyDownCalled { get; set; } public bool OnKeyUpCalled { get; set; } public override string Text { get; set; } - protected override bool OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope) - { - - OnInvokingKeyBindingsCalled = true; - - return CancelVirtualMethods; - } - protected override bool OnKeyDown (Key keyEvent) { OnKeyDownCalled = true; diff --git a/UnitTests/View/ViewKeyBindingTests.cs b/UnitTests/View/Keyboard/ViewKeyBindingTests.cs similarity index 76% rename from UnitTests/View/ViewKeyBindingTests.cs rename to UnitTests/View/Keyboard/ViewKeyBindingTests.cs index 05471e4b51..b1483c4dad 100644 --- a/UnitTests/View/ViewKeyBindingTests.cs +++ b/UnitTests/View/Keyboard/ViewKeyBindingTests.cs @@ -11,37 +11,38 @@ public class ViewKeyBindingTests (ITestOutputHelper output) public void Focus_KeyBinding () { var view = new ScopedKeyBindingView (); - var invoked = false; - view.InvokingKeyBindings += (s, e) => invoked = true; + var keyWasHandled = false; + view.KeyDownNotHandled += (s, e) => keyWasHandled = true; var top = new Toplevel (); top.Add (view); Application.Begin (top); Application.RaiseKeyDownEvent (Key.A); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.True (view.ApplicationCommand); - invoked = false; + keyWasHandled = false; Application.RaiseKeyDownEvent (Key.H); - Assert.True (invoked); + Assert.True (view.HotKeyCommand); + Assert.False (keyWasHandled); - invoked = false; + keyWasHandled = false; Assert.False (view.HasFocus); Application.RaiseKeyDownEvent (Key.F); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.FocusedCommand); - invoked = false; + keyWasHandled = false; view.CanFocus = true; view.SetFocus (); Assert.True (view.HasFocus); Application.RaiseKeyDownEvent (Key.F); - Assert.True (invoked); + Assert.True (view.FocusedCommand); + Assert.False (keyWasHandled); // Command was invoked, but wasn't handled Assert.True (view.ApplicationCommand); Assert.True (view.HotKeyCommand); - Assert.True (view.FocusedCommand); top.Dispose (); } @@ -50,23 +51,23 @@ public void Focus_KeyBinding () public void Focus_KeyBinding_Negative () { var view = new ScopedKeyBindingView (); - var invoked = false; - view.InvokingKeyBindings += (s, e) => invoked = true; + var keyWasHandled = false; + view.KeyDownNotHandled += (s, e) => keyWasHandled = true; var top = new Toplevel (); top.Add (view); Application.Begin (top); Application.RaiseKeyDownEvent (Key.Z); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); Assert.False (view.FocusedCommand); - invoked = false; + keyWasHandled = false; Assert.False (view.HasFocus); Application.RaiseKeyDownEvent (Key.F); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.ApplicationCommand); Assert.False (view.HotKeyCommand); Assert.False (view.FocusedCommand); @@ -78,28 +79,29 @@ public void Focus_KeyBinding_Negative () public void HotKey_KeyBinding () { var view = new ScopedKeyBindingView (); - var invoked = false; - view.InvokingKeyBindings += (s, e) => invoked = true; + var keyWasHandled = false; + view.KeyDownNotHandled += (s, e) => keyWasHandled = true; var top = new Toplevel (); top.Add (view); Application.Begin (top); - invoked = false; + keyWasHandled = false; Application.RaiseKeyDownEvent (Key.H); - Assert.True (invoked); Assert.True (view.HotKeyCommand); + Assert.False (keyWasHandled); view.HotKey = KeyCode.Z; - invoked = false; + keyWasHandled = false; view.HotKeyCommand = false; Application.RaiseKeyDownEvent (Key.H); // old hot key - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.HotKeyCommand); Application.RaiseKeyDownEvent (Key.Z); // new hot key - Assert.True (invoked); Assert.True (view.HotKeyCommand); + Assert.False (keyWasHandled); + top.Dispose (); } @@ -108,18 +110,18 @@ public void HotKey_KeyBinding () public void HotKey_KeyBinding_Negative () { var view = new ScopedKeyBindingView (); - var invoked = false; - view.InvokingKeyBindings += (s, e) => invoked = true; + var keyWasHandled = false; + view.KeyDownNotHandled += (s, e) => keyWasHandled = true; var top = new Toplevel (); top.Add (view); Application.Begin (top); Application.RaiseKeyDownEvent (Key.Z); - Assert.False (invoked); + Assert.False (keyWasHandled); Assert.False (view.HotKeyCommand); - invoked = false; + keyWasHandled = false; Application.RaiseKeyDownEvent (Key.F); Assert.False (view.HotKeyCommand); top.Dispose (); diff --git a/docfx/docs/keyboard.md b/docfx/docs/keyboard.md index 8b29a1a6d0..ddace3282c 100644 --- a/docfx/docs/keyboard.md +++ b/docfx/docs/keyboard.md @@ -59,22 +59,25 @@ The Command can be invoked even if the `View` that defines them is not focused o ### **Handling Keyboard Events** -Keyboard events are retrieved from [Console Drivers](drivers.md) and passed on -to the [Application](~/api/Terminal.Gui.Application.yml) class by the [Main Loop](mainloop.md). +Keyboard events are retrieved from [Console Drivers](drivers.md) each iteration of the [Application](~/api/Terminal.Gui.Application.yml) [Main Loop](mainloop.md). The console driver raises the @Terminal.Gui.ConsoleDriver.KeyDown and @Terminal.Gui.ConsoleDriver.KeyUp events which invoke @Terminal.Gui.Application.RaiseKeyDown(Terminal.Gui.Key) and @Terminal.Gui.Application.RaiseKeyUp(Terminal.Gui.Key) respectively. -[Application](~/api/Terminal.Gui.Application.yml) then determines the current [Toplevel](~/api/Terminal.Gui.Toplevel.yml) view -(either the default created by calling @Terminal.Gui.Application.Init(Terminal.Gui.ConsoleDriver,System.String), or the one set by calling `Application.Run`). The mouse event, using [Viewport-relative coordinates](xref:Terminal.Gui.View.Viewport) is then passed to the @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) method of the current [Toplevel](~/api/Terminal.Gui.Toplevel.yml) view. + NOTE: Not all drivers/platforms support sensing distinct KeyUp events. These drivers will simulate KeyUp events by raising @Terminal.Gui.ConsoleDriver.KeyUp after @Terminal.Gui.ConsoleDriver.KeyDown. -If the view is enabled, the @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) method will do the following: +@Terminal.Gui.Application.RaiseKeyDown(Terminal.Gui.Key) raises @Terminal.Gui.Application.KeyDown and then calls @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) on all toplevel Views. If no View handles the key event, any Application-scoped key bindings will be invoked. -1) If the view has a subview that has focus, 'ProcessKeyDown' on the focused view will be called. If the focused view handles the key press, processing stops. -2) If there is no focused sub-view, or the focused sub-view does not handle the key press, @Terminal.Gui.View.OnKeyDown(Terminal.Gui.Key) will be called. If the view handles the key press, processing stops. -3) If the view does not handle the key press, @Terminal.Gui.TextField.OnInvokingKeyBindings(Terminal.Gui.Key,Terminal.Gui.KeyBindingScope) will be called. This method calls @Terminal.Gui.View.InvokeKeyBindings(Terminal.Gui.Key,Terminal.Gui.KeyBindingScope) to invoke any keys bound to commands. If the key is bound and any of it's command handlers return true, processing stops. -4) If the key is not bound, or the bound command handlers do not return true, @Terminal.Gui.View.OnProcessKeyDown(Terminal.Gui.Key) is called. If the view handles the key press, processing stops. +@Terminal.Gui.Application.RaiseKeyDown(Terminal.Gui.Key) raises @Terminal.Gui.Application.KeyDown and then calls @Terminal.Gui.View.NewKeyUpEvent(Terminal.Gui.Key) on all toplevel Views. + +If a view is enabled, the @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) method will do the following: + +1) If the view has a subview that has focus, 'NewKeyDown' on the focused view will be called. This is recursive. If the most-focused view handles the key press, processing stops. +2) If there is no most-focused sub-view, or a most-focused sub-view does not handle the key press, @Terminal.Gui.View.OnKeyDown(Terminal.Gui.Key) will be called. If the view handles the key press, processing stops. +3) If @Terminal.Gui.View.OnKeyDown(Terminal.Gui.Key) does not handle the event. @Terminal.Gui.View.KeyDown will be raised. +4) If the view does not handle the key down event, any bindings for the key will be invoked (see @Terminal.Gui.View.KeyBindings). If the key is bound and any of it's command handlers return true, processing stops. +5) If the key is not bound, or the bound command handlers do not return true, @Terminal.Gui.View.OnKeyDownNotHandled(Terminal.Gui.Key) is called. ## **Application Key Handling** -To define application key handling logic for an entire application in cases where the methods listed above are not suitable, use the `Application.OnKeyDown` event. +To define application key handling logic for an entire application in cases where the methods listed above are not suitable, use the @Terminal.Gui.Application.KeyDown event. ## **Key Down/Up Events** @@ -90,17 +93,14 @@ To define application key handling logic for an entire application in cases wher - `NewKeyDownEvent` is called on the most-focused SubView (if any) that has focus. If that call returns true, the method returns. - Calls `OnKeyDown`. - **During** - - Assuming `OnKeyDown` call returns false (indicating the key wasn't handled) - - `OnInvokingKeyBindings` is called to invoke any bound commands. - - `OnInvokingKeyBindings` fires the `InvokingKeyBindings` event + - Assuming `OnKeyDown` call returns false (indicating the key wasn't handled) any commands bound to the key will be invoked. - **After** - - Assuming `OnInvokingKeyBindings` returns false (indicating the key wasn't handled) - - `OnProcessKeyDown` is called to process the key. - - `OnProcessKeyDown` fires the `ProcessKeyDown` event + - Assuming no keybinding was found or all invoked commands were not handled: + - `OnKeyDownNotHandled` is called to process the key. + - `KeyDownNotHandled` is raised. -- Subclasses of `View` can (rarely) override `OnKeyDown` to see keys before they are processed by `OnInvokingKeyBindings` and `OnProcessKeyDown -- Subclasses of `View` can (rarely) override `OnInvokingKeyBindings` to see keys before they are processed by `OnProcessKeyDown` -- Subclasses of `View` can (often) override `OnProcessKeyDown` to do normal key processing. +- Subclasses of `View` can (rarely) override `OnKeyDown` (or subscribe to `KeyDown`) to see keys before they are processed +- Subclasses of `View` can (often) override `OnKeyDownNotHandled` to do key processing for keys that were not previously handled. `TextField` and `TextView` are examples. ## ConsoleDriver From 89d8b74c3d9e0e134e5fd5ac8aa2ee1729906861 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 16:48:28 -0600 Subject: [PATCH 12/15] Key API simplification --- Terminal.Gui/View/View.Keyboard.cs | 4 +--- Terminal.Gui/View/View.Layout.cs | 1 - Terminal.Gui/Views/ListView.cs | 2 +- UICatalog/Scenarios/Keys.cs | 5 +---- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 10f1f97df8..1ebc77df0d 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -73,7 +73,7 @@ private void SetupKeyboard () /// If the hot key is changed, the event is fired. /// Set to to disable the hot key. /// - public virtual Key HotKey + public Key HotKey { get => _hotKey; set @@ -684,8 +684,6 @@ public bool IsHotKeyBound (Key key, out View? boundView) /// protected bool? InvokeCommands (Key key, KeyBindingScope scope) { - bool? toReturn = null; - if (!KeyBindings.TryGet (key, scope, out KeyBinding binding)) { return null; diff --git a/Terminal.Gui/View/View.Layout.cs b/Terminal.Gui/View/View.Layout.cs index 2d8fdfedad..318b5c993a 100644 --- a/Terminal.Gui/View/View.Layout.cs +++ b/Terminal.Gui/View/View.Layout.cs @@ -28,7 +28,6 @@ public partial class View // Layout APIs /// The target y location. /// The new x location that will ensure will be fully visible. /// The new y location that will ensure will be fully visible. - /// The new top most statusBar /// /// Either (if does not have a Super View) or /// 's SuperView. This can be used to ensure LayoutSubviews is called on the correct View. diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index f7e65f843b..0a768584a0 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -806,7 +806,7 @@ public bool OnOpenSelectedItem () protected override bool OnKeyDown (Key a) { // Enable user to find & select an item by typing text - if (CollectionNavigatorBase.IsCompatibleKey (a) && (!AllowsMarking && a == Key.Space)) + if (CollectionNavigatorBase.IsCompatibleKey (a) || (!AllowsMarking && a == Key.Space)) { int? newItem = KeystrokeNavigator?.GetNextMatchingItem (SelectedItem, (char)a); diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index 7851d342d1..e758af366c 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -94,10 +94,7 @@ public override void Main () edit.KeyDownNotHandled += (s, a) => { - if (edit.KeyBindings.TryGet (a, out KeyBinding binding)) - { - keyDownNotHandledList.Add ($"{a}: {string.Join (",", binding.Commands)}"); - } + keyDownNotHandledList.Add ($"{a}"); }; // KeyDown From b766e4efdde100a9fae3eb11c114cf6e54c3966d Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 14 Oct 2024 17:30:30 -0600 Subject: [PATCH 13/15] Fixed listView issue. Added ability to mark/unmark all to ListView --- Terminal.Gui/Views/ListView.cs | 57 ++++++++++++++++++++++++++++++-- UnitTests/Views/ListViewTests.cs | 16 ++++----- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 0a768584a0..1fa76e5404 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -147,7 +147,7 @@ public ListView () if (OnOpenSelectedItem ()) { - return true; + return true; } return false; @@ -189,6 +189,7 @@ public ListView () return !SetFocus (); }); + AddCommand (Command.SelectAll, (ctx) => MarkAll((bool)ctx.KeyBinding?.Context!)); // Default keybindings for all ListViews KeyBindings.Add (Key.CursorUp, Command.Up); @@ -205,6 +206,13 @@ public ListView () KeyBindings.Add (Key.Home, Command.Start); KeyBindings.Add (Key.End, Command.End); + + // Key.Space is already bound to Command.Select; this gives us select then move down + KeyBindings.Add (Key.Space.WithShift, [Command.Select, Command.Down]); + + // Use the form of Add that lets us pass context to the handler + KeyBindings.Add (Key.A.WithCtrl, new KeyBinding ([Command.SelectAll], KeyBindingScope.Focused, true)); + KeyBindings.Add (Key.U.WithCtrl, new KeyBinding ([Command.SelectAll], KeyBindingScope.Focused, false)); } /// Gets or sets whether this allows items to be marked. @@ -370,6 +378,31 @@ public int TopItem } } + /// + /// If and are both , + /// marks all items. + /// + /// marks all items; otherwise unmarks all items. + /// if marking was successful. + public bool MarkAll (bool mark) + { + if (!_allowsMarking) + { + return false; + } + + if (AllowsMultipleSelection) + { + for (var i = 0; i < Source.Count; i++) + { + Source.SetMark (i, mark); + } + return true; + } + + return false; + } + /// /// If and are both , /// unmarks all marked items other than . @@ -805,8 +838,28 @@ public bool OnOpenSelectedItem () /// protected override bool OnKeyDown (Key a) { + // If marking is enabled and the user presses the space key don't let CollectionNavigator + // at it + if (AllowsMarking) + { + var keys = KeyBindings.GetKeysFromCommands (Command.Select); + + if (keys.Contains (a)) + { + return false; + } + + keys = KeyBindings.GetKeysFromCommands ([Command.Select, Command.Down]); + + if (keys.Contains (a)) + { + return false; + } + + } + // Enable user to find & select an item by typing text - if (CollectionNavigatorBase.IsCompatibleKey (a) || (!AllowsMarking && a == Key.Space)) + if (CollectionNavigatorBase.IsCompatibleKey (a)) { int? newItem = KeystrokeNavigator?.GetNextMatchingItem (SelectedItem, (char)a); diff --git a/UnitTests/Views/ListViewTests.cs b/UnitTests/Views/ListViewTests.cs index 7714c82040..3da369db1b 100644 --- a/UnitTests/Views/ListViewTests.cs +++ b/UnitTests/Views/ListViewTests.cs @@ -522,7 +522,7 @@ public void ListViewProcessKeyReturnValue_WithMultipleCommands () } [Fact] - public void ListViewSelectThenDown () + public void AllowsMarking_True_SpaceWithShift_SelectsThenDown () { var lv = new ListView { Source = new ListWrapper (["One", "Two", "Three"]) }; lv.AllowsMarking = true; @@ -537,12 +537,8 @@ public void ListViewSelectThenDown () Assert.False (lv.Source.IsMarked (1)); Assert.False (lv.Source.IsMarked (2)); - lv.KeyBindings.Add (Key.Space.WithShift, Command.Select, Command.Down); - - Key key = Key.Space.WithShift; - // view should indicate that it has accepted and consumed the event - Assert.True (lv.NewKeyDownEvent (key)); + Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift)); // first item should now be selected Assert.Equal (0, lv.SelectedItem); @@ -553,7 +549,7 @@ public void ListViewSelectThenDown () Assert.False (lv.Source.IsMarked (2)); // Press key combo again - Assert.True (lv.NewKeyDownEvent (key)); + Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift)); // second item should now be selected Assert.Equal (1, lv.SelectedItem); @@ -564,21 +560,21 @@ public void ListViewSelectThenDown () Assert.False (lv.Source.IsMarked (2)); // Press key combo again - Assert.True (lv.NewKeyDownEvent (key)); + Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift)); Assert.Equal (2, lv.SelectedItem); Assert.True (lv.Source.IsMarked (0)); Assert.True (lv.Source.IsMarked (1)); Assert.False (lv.Source.IsMarked (2)); // Press key combo again - Assert.True (lv.NewKeyDownEvent (key)); + Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift)); Assert.Equal (2, lv.SelectedItem); // cannot move down any further Assert.True (lv.Source.IsMarked (0)); Assert.True (lv.Source.IsMarked (1)); Assert.True (lv.Source.IsMarked (2)); // but can toggle marked // Press key combo again - Assert.True (lv.NewKeyDownEvent (key)); + Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift)); Assert.Equal (2, lv.SelectedItem); // cannot move down any further Assert.True (lv.Source.IsMarked (0)); Assert.True (lv.Source.IsMarked (1)); From f503a5c06fa041a105f80f0c693d11a23244ad35 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 15 Oct 2024 09:51:02 -0600 Subject: [PATCH 14/15] keyEvent -> key --- .../Application/Application.Keyboard.cs | 38 +++++++++---------- Terminal.Gui/View/View.Keyboard.cs | 2 +- Terminal.Gui/Views/FileDialog.cs | 6 +-- Terminal.Gui/Views/TableView/TableView.cs | 4 +- Terminal.Gui/Views/TileView.cs | 4 +- Terminal.Gui/Views/TreeView/TreeView.cs | 6 +-- UICatalog/Scenarios/Keys.cs | 17 ++++----- UICatalog/Scenarios/Snake.cs | 10 ++--- 8 files changed, 43 insertions(+), 44 deletions(-) diff --git a/Terminal.Gui/Application/Application.Keyboard.cs b/Terminal.Gui/Application/Application.Keyboard.cs index a73e7cb868..c9fa96c631 100644 --- a/Terminal.Gui/Application/Application.Keyboard.cs +++ b/Terminal.Gui/Application/Application.Keyboard.cs @@ -5,17 +5,17 @@ public static partial class Application // Keyboard handling { /// /// Called when the user presses a key (by the ). Raises the cancelable - /// event - /// then calls on all top level views. Called before . + /// event, then calls on all top level views, and finally + /// if the key was not handled, invokes any Application-scoped . /// /// Can be used to simulate key press events. - /// + /// /// if the key was handled. - public static bool RaiseKeyDownEvent (Key keyEvent) + public static bool RaiseKeyDownEvent (Key key) { - KeyDown?.Invoke (null, keyEvent); + KeyDown?.Invoke (null, key); - if (keyEvent.Handled) + if (key.Handled) { return true; } @@ -24,7 +24,7 @@ public static bool RaiseKeyDownEvent (Key keyEvent) { foreach (Toplevel topLevel in TopLevels.ToList ()) { - if (topLevel.NewKeyDownEvent (keyEvent)) + if (topLevel.NewKeyDownEvent (key)) { return true; } @@ -37,7 +37,7 @@ public static bool RaiseKeyDownEvent (Key keyEvent) } else { - if (Top.NewKeyDownEvent (keyEvent)) + if (Top.NewKeyDownEvent (key)) { return true; } @@ -45,7 +45,7 @@ public static bool RaiseKeyDownEvent (Key keyEvent) // Invoke any Application-scoped KeyBindings. // The first view that handles the key will stop the loop. - foreach (KeyValuePair binding in KeyBindings.Bindings.Where (b => b.Key == keyEvent.KeyCode)) + foreach (KeyValuePair binding in KeyBindings.Bindings.Where (b => b.Key == key.KeyCode)) { if (binding.Value.BoundView is { }) { @@ -58,7 +58,7 @@ public static bool RaiseKeyDownEvent (Key keyEvent) } else { - if (!KeyBindings.TryGet (keyEvent, KeyBindingScope.Application, out KeyBinding appBinding)) + if (!KeyBindings.TryGet (key, KeyBindingScope.Application, out KeyBinding appBinding)) { continue; } @@ -67,7 +67,7 @@ public static bool RaiseKeyDownEvent (Key keyEvent) foreach (Command command in appBinding.Commands) { - toReturn = InvokeCommand (command, keyEvent, appBinding); + toReturn = InvokeCommand (command, key, appBinding); } return toReturn ?? true; @@ -76,18 +76,18 @@ public static bool RaiseKeyDownEvent (Key keyEvent) return false; - static bool? InvokeCommand (Command command, Key keyEvent, KeyBinding appBinding) + static bool? InvokeCommand (Command command, Key key, KeyBinding appBinding) { if (!CommandImplementations!.ContainsKey (command)) { throw new NotSupportedException ( - @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application." + @$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by Application." ); } if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation)) { - var context = new CommandContext (command, keyEvent, appBinding); // Create the context here + var context = new CommandContext (command, key, appBinding); // Create the context here return implementation (context); } @@ -116,25 +116,25 @@ public static bool RaiseKeyDownEvent (Key keyEvent) /// then calls on all top level views. Called after . /// /// Can be used to simulate key release events. - /// + /// /// if the key was handled. - public static bool RaiseKeyUpEvent (Key a) + public static bool RaiseKeyUpEvent (Key key) { if (!IsInitialized) { return true; } - KeyUp?.Invoke (null, a); + KeyUp?.Invoke (null, key); - if (a.Handled) + if (key.Handled) { return true; } foreach (Toplevel topLevel in TopLevels.ToList ()) { - if (topLevel.NewKeyUpEvent (a)) + if (topLevel.NewKeyUpEvent (key)) { return true; } diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 1ebc77df0d..698d4e7b16 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -426,7 +426,7 @@ bool RaiseKeyDownNotHandled (Key k) /// /// If the focused sub view does not handle the key press, this method raises / /// to allow the - /// view to pre-process the key press. If /. + /// view to pre-process the key press. /// /// See for an overview of Terminal.Gui keyboard APIs. /// diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs index 78ed5b584a..3bc7bb3863 100644 --- a/Terminal.Gui/Views/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialog.cs @@ -670,11 +670,11 @@ private void Accept (bool allowMulti) FinishAccept (); } - private void AcceptIf (Key keyEvent, KeyCode isKey) + private void AcceptIf (Key key, KeyCode isKey) { - if (!keyEvent.Handled && keyEvent.KeyCode == isKey) + if (!key.Handled && key.KeyCode == isKey) { - keyEvent.Handled = true; + key.Handled = true; // User hit Enter in text box so probably wants the // contents of the text box as their selection not diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index 5da0b11e3e..5617c7c210 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -1563,7 +1563,7 @@ private TableSelection CreateTableSelection (int pt1X, int pt1Y, int pt2X, int p /// private TableSelection CreateTableSelection (int x, int y) { return CreateTableSelection (x, y, x, y); } - private bool CycleToNextTableEntryBeginningWith (Key keyEvent) + private bool CycleToNextTableEntryBeginningWith (Key key) { int row = SelectedRow; @@ -1573,7 +1573,7 @@ private bool CycleToNextTableEntryBeginningWith (Key keyEvent) return false; } - int match = CollectionNavigator.GetNextMatchingItem (row, (char)keyEvent); + int match = CollectionNavigator.GetNextMatchingItem (row, (char)key); if (match != -1) { diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index ec117c652b..5400ca0467 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -286,11 +286,11 @@ public override void OnDrawContent (Rectangle viewport) //// BUGBUG: Why is this not handled by a key binding??? /// - protected override bool OnKeyDownNotHandled (Key keyEvent) + protected override bool OnKeyDownNotHandled (Key key) { var focusMoved = false; - if (keyEvent.KeyCode == ToggleResizable) + if (key.KeyCode == ToggleResizable) { foreach (TileViewLineView l in _splitterLines) { diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 36a249e8c8..a1a110cbe3 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -1182,7 +1182,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View cu } /// - protected override bool OnKeyDown (Key keyEvent) + protected override bool OnKeyDown (Key key) { if (!Enabled) { @@ -1190,7 +1190,7 @@ protected override bool OnKeyDown (Key keyEvent) } // If not a keybinding, is the key a searchable key press? - if (CollectionNavigatorBase.IsCompatibleKey (keyEvent) && AllowLetterBasedNavigation) + if (CollectionNavigatorBase.IsCompatibleKey (key) && AllowLetterBasedNavigation) { // If there has been a call to InvalidateMap since the last time // we need a new one to reflect the new exposed tree state @@ -1198,7 +1198,7 @@ protected override bool OnKeyDown (Key keyEvent) // Find the current selected object within the tree int current = map.IndexOf (b => b.Model == SelectedObject); - int? newIndex = KeystrokeNavigator?.GetNextMatchingItem (current, (char)keyEvent); + int? newIndex = KeystrokeNavigator?.GetNextMatchingItem (current, (char)key); if (newIndex is int && newIndex != -1) { diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index e758af366c..f2796a344d 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -76,18 +76,18 @@ public override void Main () win.Add (label); int maxKeyString = Key.CursorRight.WithAlt.WithCtrl.WithShift.ToString ().Length; - ObservableCollection keyEventList = new (); + ObservableCollection keyList = new (); - var appKeyEventListView = new ListView + var appKeyListView = new ListView { X = 0, Y = Pos.Bottom (label), Width = "KeyDown:".Length + maxKeyString, Height = Dim.Fill (), - Source = new ListWrapper (keyEventList) + Source = new ListWrapper (keyList) }; - appKeyEventListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; - win.Add (appKeyEventListView); + appKeyListView.ColorScheme = Colors.ColorSchemes ["TopLevel"]; + win.Add (appKeyListView); // View key events... edit.KeyDown += (s, a) => { keyDownList.Add (a.ToString ()); }; @@ -100,7 +100,7 @@ public override void Main () // KeyDown label = new Label { - X = Pos.Right (appKeyEventListView) + 1, + X = Pos.Right (appKeyListView) + 1, Y = Pos.Top (label), Text = "TextView Key Down:" }; @@ -142,10 +142,9 @@ public override void Main () void KeyDownPressUp (Key args, string updown) { - // BUGBUG: KeyEvent.ToString is badly broken var msg = $"Key{updown,-7}: {args}"; - keyEventList.Add (msg); - appKeyEventListView.MoveDown (); + keyList.Add (msg); + appKeyListView.MoveDown (); onKeyDownNotHandledListView.MoveDown (); } diff --git a/UICatalog/Scenarios/Snake.cs b/UICatalog/Scenarios/Snake.cs index 998969d8da..906bc931dc 100644 --- a/UICatalog/Scenarios/Snake.cs +++ b/UICatalog/Scenarios/Snake.cs @@ -357,30 +357,30 @@ public override void OnDrawContent (Rectangle viewport) } // BUGBUG: Should (can) this use key bindings instead. - protected override bool OnKeyDown (Key keyEvent) + protected override bool OnKeyDown (Key key) { - if (keyEvent.KeyCode == KeyCode.CursorUp) + if (key.KeyCode == KeyCode.CursorUp) { State.PlannedDirection = Direction.Up; return true; } - if (keyEvent.KeyCode == KeyCode.CursorDown) + if (key.KeyCode == KeyCode.CursorDown) { State.PlannedDirection = Direction.Down; return true; } - if (keyEvent.KeyCode == KeyCode.CursorLeft) + if (key.KeyCode == KeyCode.CursorLeft) { State.PlannedDirection = Direction.Left; return true; } - if (keyEvent.KeyCode == KeyCode.CursorRight) + if (key.KeyCode == KeyCode.CursorRight) { State.PlannedDirection = Direction.Right; From c50f47be1e985d619b3b38369e2cf5726891d901 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 15 Oct 2024 09:58:25 -0600 Subject: [PATCH 15/15] Code cleanup --- Terminal.Gui/Input/ShortcutHelper.cs | 173 --------------------------- Terminal.Gui/View/View.Keyboard.cs | 1 - Terminal.Gui/Views/ListView.cs | 1 - 3 files changed, 175 deletions(-) delete mode 100644 Terminal.Gui/Input/ShortcutHelper.cs diff --git a/Terminal.Gui/Input/ShortcutHelper.cs b/Terminal.Gui/Input/ShortcutHelper.cs deleted file mode 100644 index d5e776a538..0000000000 --- a/Terminal.Gui/Input/ShortcutHelper.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Diagnostics; - -namespace Terminal.Gui; - -// TODO: Nuke when #2975 is completed -/// Represents a helper to manipulate shortcut keys used on views. -public class ShortcutHelper -{ - // TODO: Update this to use Key, not KeyCode - private KeyCode shortcut; - - /// This is the global setting that can be used as a global shortcut to invoke the action on the view. - public virtual KeyCode Shortcut - { - get => shortcut; - set - { - if (shortcut != value && (PostShortcutValidation (value) || value is KeyCode.Null)) - { - shortcut = value; - } - } - } - - /// The keystroke combination used in the as string. - public virtual string ShortcutTag => Key.ToString (shortcut, Key.Separator); - - /// Lookup for a on range of keys. - /// The source key. - /// First key in range. - /// Last key in range. - public static bool CheckKeysFlagRange (KeyCode key, KeyCode first, KeyCode last) - { - for (var i = (uint)first; i < (uint)last; i++) - { - if ((key | (KeyCode)i) == key) - { - return true; - } - } - - return false; - } - - /// Allows to retrieve a from a - /// The key as string. - /// The delimiter string. - public static KeyCode GetShortcutFromTag (string tag, Rune delimiter = default) - { - string sCut = tag; - - if (string.IsNullOrEmpty (sCut)) - { - return default (KeyCode); - } - - var key = KeyCode.Null; - - //var hasCtrl = false; - if (delimiter == default (Rune)) - { - delimiter = Key.Separator; - } - - string [] keys = sCut.Split (delimiter.ToString ()); - - for (var i = 0; i < keys.Length; i++) - { - string k = keys [i]; - - if (k == "Ctrl") - { - //hasCtrl = true; - key |= KeyCode.CtrlMask; - } - else if (k == "Shift") - { - key |= KeyCode.ShiftMask; - } - else if (k == "Alt") - { - key |= KeyCode.AltMask; - } - else if (k.StartsWith ("F") && k.Length > 1) - { - int.TryParse (k.Substring (1), out int n); - - for (var j = (uint)KeyCode.F1; j <= (uint)KeyCode.F12; j++) - { - int.TryParse (((KeyCode)j).ToString ().Substring (1), out int f); - - if (f == n) - { - key |= (KeyCode)j; - } - } - } - else - { - key |= (KeyCode)Enum.Parse (typeof (KeyCode), k); - } - } - - return key; - } - - /// Used at key up validation. - /// The key to validate. - /// true if is valid.falseotherwise. - public static bool PostShortcutValidation (KeyCode key) - { - GetKeyToString (key, out KeyCode knm); - - if (CheckKeysFlagRange (key, KeyCode.F1, KeyCode.F12) || ((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) != 0 && knm != KeyCode.Null)) - { - return true; - } - - return false; - } - - /// Used at key down or key press validation. - /// The key to validate. - /// true if is valid.falseotherwise. - public static bool PreShortcutValidation (KeyCode key) - { - if ((key & (KeyCode.CtrlMask | KeyCode.ShiftMask | KeyCode.AltMask)) == 0 - && !CheckKeysFlagRange (key, KeyCode.F1, KeyCode.F12)) - { - return false; - } - - return true; - } - - /// Return key as string. - /// The key to extract. - /// Correspond to the non modifier key. - private static string GetKeyToString (KeyCode key, out KeyCode knm) - { - if (key == KeyCode.Null) - { - knm = KeyCode.Null; - - return ""; - } - - knm = key; - KeyCode mK = key & (KeyCode.AltMask | KeyCode.CtrlMask | KeyCode.ShiftMask); - knm &= ~mK; - - for (var i = (uint)KeyCode.F1; i < (uint)KeyCode.F12; i++) - { - if (knm == (KeyCode)i) - { - mK |= (KeyCode)i; - } - } - - knm &= ~mK; - uint.TryParse (knm.ToString (), out uint c); - string s = mK == KeyCode.Null ? "" : mK.ToString (); - - if (s != "" && (knm != KeyCode.Null || c > 0)) - { - s += ","; - } - - s += c == 0 ? knm == KeyCode.Null ? "" : knm.ToString () : ((char)c).ToString (); - - return s; - } -} diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 698d4e7b16..87c5a1ce64 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -297,7 +297,6 @@ public bool NewKeyDownEvent (Key key) } // After - // TODO: Is ProcessKeyDown really the right name? if (RaiseKeyDownNotHandled (key) || key.Handled) { return true; diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 1fa76e5404..bae3400ced 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -817,7 +817,6 @@ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View cu } } - // TODO: This should be cancelable /// Invokes the event if it is defined. /// if the event was fired. public bool OnOpenSelectedItem ()