Skip to content

Commit 23344ba

Browse files
committed
Incorporated tznind's stuff
1 parent d15b302 commit 23344ba

File tree

9 files changed

+191
-182
lines changed

9 files changed

+191
-182
lines changed

Terminal.Gui/Input/Bindings.cs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#nullable enable
2+
using System;
3+
4+
namespace Terminal.Gui;
5+
6+
/// <summary>
7+
/// Abstract base class for <see cref="KeyBindings"/> and <see cref="MouseBindings"/>.
8+
/// </summary>
9+
/// <typeparam name="TEvent">The type of the event (e.g. <see cref="Key"/> or <see cref="MouseEventArgs"/>).</typeparam>
10+
/// <typeparam name="TBinding">The binding type (e.g. <see cref="KeyBinding"/>).</typeparam>
11+
public abstract class Bindings<TEvent, TBinding> where TBinding : IInputBinding, new() where TEvent : notnull
12+
{
13+
/// <summary>
14+
/// The bindings.
15+
/// </summary>
16+
protected readonly Dictionary<TEvent, TBinding> _bindings;
17+
18+
private readonly Func<Command [], TEvent, TBinding> _constructBinding;
19+
20+
/// <summary>
21+
/// Initializes a new instance.
22+
/// </summary>
23+
/// <param name="constructBinding"></param>
24+
/// <param name="equalityComparer"></param>
25+
protected Bindings (Func<Command [], TEvent, TBinding> constructBinding, IEqualityComparer<TEvent> equalityComparer)
26+
{
27+
_constructBinding = constructBinding;
28+
_bindings = new (equalityComparer);
29+
}
30+
31+
/// <summary>Adds a <see cref="TEvent"/> bound to <see cref="TBinding"/> to the collection.</summary>
32+
/// <param name="eventArgs"></param>
33+
/// <param name="binding"></param>
34+
public void Add (TEvent eventArgs, TBinding binding)
35+
{
36+
if (TryGet (eventArgs, out TBinding _))
37+
{
38+
throw new InvalidOperationException (@$"A binding for {eventArgs} exists ({binding}).");
39+
}
40+
41+
// IMPORTANT: Add a COPY of the mouseEventArgs. This is needed because ConfigurationManager.Apply uses DeepMemberWiseCopy
42+
// IMPORTANT: update the memory referenced by the key, and Dictionary uses caching for performance, and thus
43+
// IMPORTANT: Apply will update the Dictionary with the new mouseEventArgs, but the old mouseEventArgs will still be in the dictionary.
44+
// IMPORTANT: See the ConfigurationManager.Illustrate_DeepMemberWiseCopy_Breaks_Dictionary test for details.
45+
_bindings.Add (eventArgs, binding);
46+
}
47+
48+
49+
/// <summary>Gets the commands bound with the specified <see cref="TEvent"/>.</summary>
50+
/// <remarks></remarks>
51+
/// <param name="eventArgs">The args to check.</param>
52+
/// <param name="binding">
53+
/// When this method returns, contains the commands bound with the specified mouse flags, if the mouse flags are
54+
/// found; otherwise, null. This parameter is passed uninitialized.
55+
/// </param>
56+
/// <returns><see langword="true"/> if the mouse flags are bound; otherwise <see langword="false"/>.</returns>
57+
public bool TryGet (TEvent eventArgs, out TBinding? binding)
58+
{
59+
return _bindings.TryGetValue (eventArgs, out binding);
60+
}
61+
62+
63+
/// <summary>
64+
/// <para>Adds a new mouse flag combination that will trigger the commands in <paramref name="commands"/>.</para>
65+
/// <para>
66+
/// If the key is already bound to a different array of <see cref="Command"/>s it will be rebound
67+
/// <paramref name="commands"/>.
68+
/// </para>
69+
/// </summary>
70+
/// <remarks>
71+
/// Commands are only ever applied to the current <see cref="View"/> (i.e. this feature cannot be used to switch
72+
/// focus to another view and perform multiple commands there).
73+
/// </remarks>
74+
/// <param name="eventArgs">The mouse flags to check.</param>
75+
/// <param name="commands">
76+
/// The command to invoked on the <see cref="View"/> when <paramref name="eventArgs"/> is received. When
77+
/// multiple commands are provided,they will be applied in sequence. The bound <paramref name="eventArgs"/> event
78+
/// will be
79+
/// consumed if any took effect.
80+
/// </param>
81+
public void Add (TEvent eventArgs, params Command [] commands)
82+
{
83+
if (commands.Length == 0)
84+
{
85+
throw new ArgumentException (@"At least one command must be specified", nameof (commands));
86+
}
87+
88+
if (TryGet (eventArgs, out var binding))
89+
{
90+
throw new InvalidOperationException (@$"A binding for {eventArgs} exists ({binding}).");
91+
}
92+
93+
Add (eventArgs, _constructBinding(commands,eventArgs));
94+
}
95+
96+
/// <summary>
97+
/// Gets the bindings.
98+
/// </summary>
99+
/// <returns></returns>
100+
public IEnumerable<KeyValuePair<TEvent, TBinding>> GetBindings ()
101+
{
102+
return _bindings;
103+
}
104+
105+
/// <summary>Removes all <see cref="TEvent"/> objects from the collection.</summary>
106+
public void Clear () { _bindings.Clear (); }
107+
108+
/// <summary>
109+
/// Removes all bindings that trigger the given command set. Views can have multiple different events bound to
110+
/// the same command sets and this method will clear all of them.
111+
/// </summary>
112+
/// <param name="command"></param>
113+
public void Clear (params Command [] command)
114+
{
115+
KeyValuePair<TEvent, TBinding> [] kvps = _bindings
116+
.Where (kvp => kvp.Value.Commands.SequenceEqual (command))
117+
.ToArray ();
118+
119+
foreach (KeyValuePair<TEvent, TBinding> kvp in kvps)
120+
{
121+
Remove (kvp.Key);
122+
}
123+
}
124+
125+
/// <summary>Gets the <see cref="TBinding"/> for the specified <see cref="TEvent"/>.</summary>
126+
/// <param name="eventArgs"></param>
127+
/// <returns></returns>
128+
public TBinding? Get (TEvent eventArgs)
129+
{
130+
if (TryGet (eventArgs, out var binding))
131+
{
132+
return binding;
133+
}
134+
135+
throw new InvalidOperationException ($"{eventArgs} is not bound.");
136+
}
137+
138+
139+
/// <summary>Removes a <see cref="MouseBinding"/> from the collection.</summary>
140+
/// <param name="mouseEventArgs"></param>
141+
public void Remove (TEvent mouseEventArgs)
142+
{
143+
if (!TryGet (mouseEventArgs, out var _))
144+
{
145+
return;
146+
}
147+
148+
_bindings.Remove (mouseEventArgs);
149+
}
150+
}

Terminal.Gui/Input/CommandContext.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ namespace Terminal.Gui;
77
/// </summary>
88
/// <seealso cref="View.Invoke(Command)"/>.
99
#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
10-
public record struct CommandContext<TBindingType> : ICommandContext
10+
public record struct CommandContext<TBinding> : ICommandContext
1111
{
1212
/// <summary>
1313
/// Initializes a new instance with the specified <see cref="Command"/>,
1414
/// </summary>
1515
/// <param name="command"></param>
1616
/// <param name="binding"></param>
17-
public CommandContext (Command command, TBindingType? binding)
17+
public CommandContext (Command command, TBinding? binding)
1818
{
1919
Command = command;
2020
Binding = binding;
@@ -26,5 +26,5 @@ public CommandContext (Command command, TBindingType? binding)
2626
/// <summary>
2727
/// The keyboard or mouse minding that was used to invoke the <see cref="Command"/>, if any.
2828
/// </summary>
29-
public TBindingType? Binding { get; set; }
29+
public TBinding? Binding { get; set; }
3030
}

Terminal.Gui/Input/Keyboard/Key.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.ComponentModel;
12
using System.Diagnostics.CodeAnalysis;
23
using System.Globalization;
34

@@ -69,6 +70,7 @@ namespace Terminal.Gui;
6970
/// </list>
7071
/// </para>
7172
/// </remarks>
73+
[DefaultValue(KeyCode.Null)]
7274
public class Key : EventArgs, IEquatable<Key>
7375
{
7476
/// <summary>Constructs a new <see cref="Key"/></summary>

Terminal.Gui/Input/Keyboard/KeyBindings.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ namespace Terminal.Gui;
77
/// <seealso cref="Application.KeyBindings"/>
88
/// <seealso cref="View.KeyBindings"/>
99
/// <seealso cref="Command"/>
10-
public class KeyBindings : Bindings<Key,KeyBinding>
10+
public class KeyBindings : Bindings<Key, KeyBinding>
1111
{
1212
/// <summary>Initializes a new instance bound to <paramref name="target"/>.</summary>
13-
public KeyBindings (View? target) :base(
14-
(commands,key)=> new KeyBinding (commands),
15-
new KeyEqualityComparer ()) { Target = target; }
16-
17-
13+
public KeyBindings (View? target) : base (
14+
(commands, key) => new (commands),
15+
new KeyEqualityComparer ())
16+
{
17+
Target = target;
18+
}
1819

1920
/// <summary>
2021
/// <para>
@@ -29,11 +30,14 @@ public KeyBindings (View? target) :base(
2930
/// <remarks>
3031
/// </remarks>
3132
/// <param name="key">The key to check.</param>
32-
/// <param name="target">The View the commands will be invoked on. If <see langword="null"/>, the key will be bound to <see cref="Application"/>.</param>
33+
/// <param name="target">
34+
/// The View the commands will be invoked on. If <see langword="null"/>, the key will be bound to
35+
/// <see cref="Application"/>.
36+
/// </param>
3337
/// <param name="commands">
3438
/// The command to invoked on the <see paramref="target"/> when <paramref name="key"/> is pressed. When
3539
/// multiple commands are provided,they will be applied in sequence. The bound <paramref name="key"/> strike will be
36-
/// consumed if any took effect.
40+
/// consumed if any took effect.
3741
/// </param>
3842
public void Add (Key key, View? target, params Command [] commands)
3943
{
@@ -45,10 +49,7 @@ public void Add (Key key, View? target, params Command [] commands)
4549
/// Gets the bindings.
4650
/// </summary>
4751
/// <returns></returns>
48-
public IEnumerable<KeyValuePair<Key, KeyBinding>> GetBindings ()
49-
{
50-
return _bindings;
51-
}
52+
public IEnumerable<KeyValuePair<Key, KeyBinding>> GetBindings () { return _bindings; }
5253

5354
/// <summary>
5455
/// Gets the keys that are bound.
@@ -179,10 +180,10 @@ public void ReplaceKey (Key oldKey, Key newKey)
179180
if (newKey == Key.Empty)
180181
{
181182
Remove (oldKey);
183+
182184
return;
183185
}
184186

185-
186187
if (TryGet (oldKey, out KeyBinding binding))
187188
{
188189
Remove (oldKey);

0 commit comments

Comments
 (0)