Skip to content

Commit 23eb14c

Browse files
authored
Merge pull request #3795 from tig/v2_3793-CollectionNavigator-Use-OnKeyDown
Fixes #3793. Simplifies and makes correct `KeyDown/Up` API, Fixes: `CollectionNavigator` should be called on `KeyDown`
2 parents cc0d965 + c50f47b commit 23eb14c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1519
-1737
lines changed

Terminal.Gui/Application/Application.Initialization.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ internal static void InternalInit (
178178
}
179179

180180
private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); }
181-
private static void Driver_KeyDown (object? sender, Key e) { OnKeyDown (e); }
182-
private static void Driver_KeyUp (object? sender, Key e) { OnKeyUp (e); }
181+
private static void Driver_KeyDown (object? sender, Key e) { RaiseKeyDownEvent (e); }
182+
private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); }
183183
private static void Driver_MouseEvent (object? sender, MouseEvent e) { OnMouseEvent (e); }
184184

185185
/// <summary>Gets of list of <see cref="ConsoleDriver"/> types that are available.</summary>

Terminal.Gui/Application/Application.Keyboard.cs

Lines changed: 75 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -3,94 +3,19 @@ namespace Terminal.Gui;
33

44
public static partial class Application // Keyboard handling
55
{
6-
private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides
7-
private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides
8-
private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
9-
private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
10-
private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
11-
private static Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrrides
12-
13-
static Application () { AddApplicationKeyBindings (); }
14-
15-
/// <summary>Gets the key bindings for this view.</summary>
16-
public static KeyBindings KeyBindings { get; internal set; } = new ();
17-
18-
/// <summary>
19-
/// Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
20-
/// <para>
21-
/// Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
22-
/// additional processing.
23-
/// </para>
24-
/// </summary>
25-
/// <remarks>
26-
/// All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
27-
/// <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
28-
/// <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
29-
/// </remarks>
30-
public static event EventHandler<Key>? KeyDown;
31-
326
/// <summary>
33-
/// Event fired when the user releases a key. Fired by <see cref="OnKeyUp"/>.
34-
/// <para>
35-
/// Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
36-
/// additional processing.
37-
/// </para>
38-
/// </summary>
39-
/// <remarks>
40-
/// All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
41-
/// <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
42-
/// <para>Fired after <see cref="KeyDown"/>.</para>
43-
/// </remarks>
44-
public static event EventHandler<Key>? KeyUp;
45-
46-
/// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
47-
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
48-
public static Key NextTabGroupKey
49-
{
50-
get => _nextTabGroupKey;
51-
set
52-
{
53-
if (_nextTabGroupKey != value)
54-
{
55-
ReplaceKey (_nextTabGroupKey, value);
56-
_nextTabGroupKey = value;
57-
}
58-
}
59-
}
60-
61-
/// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
62-
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
63-
public static Key NextTabKey
64-
{
65-
get => _nextTabKey;
66-
set
67-
{
68-
if (_nextTabKey != value)
69-
{
70-
ReplaceKey (_nextTabKey, value);
71-
_nextTabKey = value;
72-
}
73-
}
74-
}
75-
76-
/// <summary>
77-
/// Called by the <see cref="ConsoleDriver"/> when the user presses a key. Fires the <see cref="KeyDown"/> event
78-
/// then calls <see cref="View.NewKeyDownEvent"/> on all top level views. Called after <see cref="OnKeyDown"/> and
79-
/// before <see cref="OnKeyUp"/>.
7+
/// Called when the user presses a key (by the <see cref="ConsoleDriver"/>). Raises the cancelable
8+
/// <see cref="KeyDown"/> event, then calls <see cref="View.NewKeyDownEvent"/> on all top level views, and finally
9+
/// if the key was not handled, invokes any Application-scoped <see cref="KeyBindings"/>.
8010
/// </summary>
8111
/// <remarks>Can be used to simulate key press events.</remarks>
82-
/// <param name="keyEvent"></param>
12+
/// <param name="key"></param>
8313
/// <returns><see langword="true"/> if the key was handled.</returns>
84-
public static bool OnKeyDown (Key keyEvent)
14+
public static bool RaiseKeyDownEvent (Key key)
8515
{
86-
//if (!IsInitialized)
87-
//{
88-
// return true;
89-
//}
16+
KeyDown?.Invoke (null, key);
9017

91-
KeyDown?.Invoke (null, keyEvent);
92-
93-
if (keyEvent.Handled)
18+
if (key.Handled)
9419
{
9520
return true;
9621
}
@@ -99,7 +24,7 @@ public static bool OnKeyDown (Key keyEvent)
9924
{
10025
foreach (Toplevel topLevel in TopLevels.ToList ())
10126
{
102-
if (topLevel.NewKeyDownEvent (keyEvent))
27+
if (topLevel.NewKeyDownEvent (key))
10328
{
10429
return true;
10530
}
@@ -112,15 +37,15 @@ public static bool OnKeyDown (Key keyEvent)
11237
}
11338
else
11439
{
115-
if (Top.NewKeyDownEvent (keyEvent))
40+
if (Top.NewKeyDownEvent (key))
11641
{
11742
return true;
11843
}
11944
}
12045

12146
// Invoke any Application-scoped KeyBindings.
12247
// The first view that handles the key will stop the loop.
123-
foreach (KeyValuePair<Key, KeyBinding> binding in KeyBindings.Bindings.Where (b => b.Key == keyEvent.KeyCode))
48+
foreach (KeyValuePair<Key, KeyBinding> binding in KeyBindings.Bindings.Where (b => b.Key == key.KeyCode))
12449
{
12550
if (binding.Value.BoundView is { })
12651
{
@@ -133,7 +58,7 @@ public static bool OnKeyDown (Key keyEvent)
13358
}
13459
else
13560
{
136-
if (!KeyBindings.TryGet (keyEvent, KeyBindingScope.Application, out KeyBinding appBinding))
61+
if (!KeyBindings.TryGet (key, KeyBindingScope.Application, out KeyBinding appBinding))
13762
{
13863
continue;
13964
}
@@ -142,67 +67,74 @@ public static bool OnKeyDown (Key keyEvent)
14267

14368
foreach (Command command in appBinding.Commands)
14469
{
145-
toReturn = InvokeCommand (command, keyEvent, appBinding);
70+
toReturn = InvokeCommand (command, key, appBinding);
14671
}
14772

14873
return toReturn ?? true;
14974
}
15075
}
15176

15277
return false;
153-
}
15478

155-
/// <summary>
156-
/// INTENRAL method to invoke one of the commands in <see cref="CommandImplementations"/>
157-
/// </summary>
158-
/// <param name="command"></param>
159-
/// <param name="keyEvent"></param>
160-
/// <param name="appBinding"></param>
161-
/// <returns></returns>
162-
/// <exception cref="NotSupportedException"></exception>
163-
private static bool? InvokeCommand (Command command, Key keyEvent, KeyBinding appBinding)
164-
{
165-
if (!CommandImplementations!.ContainsKey (command))
79+
static bool? InvokeCommand (Command command, Key key, KeyBinding appBinding)
16680
{
167-
throw new NotSupportedException (
168-
@$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application."
169-
);
170-
}
81+
if (!CommandImplementations!.ContainsKey (command))
82+
{
83+
throw new NotSupportedException (
84+
@$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by Application."
85+
);
86+
}
17187

172-
if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation))
173-
{
174-
var context = new CommandContext (command, keyEvent, appBinding); // Create the context here
88+
if (CommandImplementations.TryGetValue (command, out View.CommandImplementation? implementation))
89+
{
90+
var context = new CommandContext (command, key, appBinding); // Create the context here
17591

176-
return implementation (context);
177-
}
92+
return implementation (context);
93+
}
17894

179-
return false;
95+
return false;
96+
}
18097
}
18198

18299
/// <summary>
183-
/// Called by the <see cref="ConsoleDriver"/> when the user releases a key. Fires the <see cref="KeyUp"/> event
184-
/// then calls <see cref="View.NewKeyUpEvent"/> on all top level views. Called after <see cref="OnKeyDown"/>.
100+
/// Raised when the user presses a key.
101+
/// <para>
102+
/// Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
103+
/// additional processing.
104+
/// </para>
185105
/// </summary>
186-
/// <remarks>Can be used to simulate key press events.</remarks>
187-
/// <param name="a"></param>
106+
/// <remarks>
107+
/// All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
108+
/// <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
109+
/// <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
110+
/// </remarks>
111+
public static event EventHandler<Key>? KeyDown;
112+
113+
/// <summary>
114+
/// Called when the user releases a key (by the <see cref="ConsoleDriver"/>). Raises the cancelable <see cref="KeyUp"/>
115+
/// event
116+
/// then calls <see cref="View.NewKeyUpEvent"/> on all top level views. Called after <see cref="RaiseKeyDownEvent"/>.
117+
/// </summary>
118+
/// <remarks>Can be used to simulate key release events.</remarks>
119+
/// <param name="key"></param>
188120
/// <returns><see langword="true"/> if the key was handled.</returns>
189-
public static bool OnKeyUp (Key a)
121+
public static bool RaiseKeyUpEvent (Key key)
190122
{
191123
if (!IsInitialized)
192124
{
193125
return true;
194126
}
195127

196-
KeyUp?.Invoke (null, a);
128+
KeyUp?.Invoke (null, key);
197129

198-
if (a.Handled)
130+
if (key.Handled)
199131
{
200132
return true;
201133
}
202134

203135
foreach (Toplevel topLevel in TopLevels.ToList ())
204136
{
205-
if (topLevel.NewKeyUpEvent (a))
137+
if (topLevel.NewKeyUpEvent (key))
206138
{
207139
return true;
208140
}
@@ -216,65 +148,12 @@ public static bool OnKeyUp (Key a)
216148
return false;
217149
}
218150

219-
/// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
220-
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
221-
public static Key PrevTabGroupKey
222-
{
223-
get => _prevTabGroupKey;
224-
set
225-
{
226-
if (_prevTabGroupKey != value)
227-
{
228-
ReplaceKey (_prevTabGroupKey, value);
229-
_prevTabGroupKey = value;
230-
}
231-
}
232-
}
233-
234-
/// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
235-
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
236-
public static Key PrevTabKey
237-
{
238-
get => _prevTabKey;
239-
set
240-
{
241-
if (_prevTabKey != value)
242-
{
243-
ReplaceKey (_prevTabKey, value);
244-
_prevTabKey = value;
245-
}
246-
}
247-
}
151+
#region Application-scoped KeyBindings
248152

249-
/// <summary>Gets or sets the key to quit the application.</summary>
250-
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
251-
public static Key QuitKey
252-
{
253-
get => _quitKey;
254-
set
255-
{
256-
if (_quitKey != value)
257-
{
258-
ReplaceKey (_quitKey, value);
259-
_quitKey = value;
260-
}
261-
}
262-
}
153+
static Application () { AddApplicationKeyBindings (); }
263154

264-
/// <summary>Gets or sets the key to activate arranging views using the keyboard.</summary>
265-
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
266-
public static Key ArrangeKey
267-
{
268-
get => _arrangeKey;
269-
set
270-
{
271-
if (_arrangeKey != value)
272-
{
273-
ReplaceKey (_arrangeKey, value);
274-
_arrangeKey = value;
275-
}
276-
}
277-
}
155+
/// <summary>Gets the Application-scoped key bindings.</summary>
156+
public static KeyBindings KeyBindings { get; internal set; } = new ();
278157

279158
internal static void AddApplicationKeyBindings ()
280159
{
@@ -286,6 +165,7 @@ internal static void AddApplicationKeyBindings ()
286165
static () =>
287166
{
288167
RequestStop ();
168+
289169
return true;
290170
}
291171
);
@@ -348,7 +228,7 @@ internal static void AddApplicationKeyBindings ()
348228

349229
KeyBindings.Clear ();
350230

351-
// Resources/config.json overrrides
231+
// Resources/config.json overrides
352232
NextTabKey = Key.Tab;
353233
PrevTabKey = Key.Tab.WithShift;
354234
NextTabGroupKey = Key.F6;
@@ -397,6 +277,26 @@ internal static List<KeyBinding> GetViewKeyBindings ()
397277
.ToList ();
398278
}
399279

280+
private static void ReplaceKey (Key oldKey, Key newKey)
281+
{
282+
if (KeyBindings.Bindings.Count == 0)
283+
{
284+
return;
285+
}
286+
287+
if (newKey == Key.Empty)
288+
{
289+
KeyBindings.Remove (oldKey);
290+
}
291+
else
292+
{
293+
KeyBindings.ReplaceKey (oldKey, newKey);
294+
}
295+
}
296+
297+
298+
#endregion Application-scoped KeyBindings
299+
400300
/// <summary>
401301
/// <para>
402302
/// Sets the function that will be invoked for a <see cref="Command"/>.
@@ -420,20 +320,4 @@ internal static List<KeyBinding> GetViewKeyBindings ()
420320
/// </summary>
421321
private static Dictionary<Command, View.CommandImplementation>? CommandImplementations { get; set; }
422322

423-
private static void ReplaceKey (Key oldKey, Key newKey)
424-
{
425-
if (KeyBindings.Bindings.Count == 0)
426-
{
427-
return;
428-
}
429-
430-
if (newKey == Key.Empty)
431-
{
432-
KeyBindings.Remove (oldKey);
433-
}
434-
else
435-
{
436-
KeyBindings.ReplaceKey (oldKey, newKey);
437-
}
438-
}
439323
}

0 commit comments

Comments
 (0)