Skip to content

Commit 138de2a

Browse files
authored
Add a custom MouseGesture with support for key combinations (#167)
1 parent 6ae4459 commit 138de2a

File tree

3 files changed

+109
-1
lines changed

3 files changed

+109
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
> - Renamed PushItems to UpdatePushedArea and StartPushingItems to BeginPushingItems in NodifyEditor
1111
> - Renamed UnselectAllConnection to UnselectAllConnections in NodifyEditor
1212
> - Removed DragStarted, DragDelta and DragCompleted routed events from ItemContainer
13+
> - Replaced the System.Windows.Input.MouseGesture with Nodify.MouseGesture
1314
> - Features:
1415
> - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor
1516
> - Added UpdateCuttingLine to NodifyEditor
@@ -21,6 +22,7 @@
2122
> - Added PreserveSelectionOnRightClick configuration field to ItemContainer
2223
> - Added State, GetInitialState, PushState, PopState and PopAllStates to Connector
2324
> - Added BeginConnecting, UpdatePendingConnection, EndConnecting, CancelConnecting and RemoveConnections methods to Connector
25+
> - Added a custom MouseGesture with support for key combinations
2426
> - Bugfixes:
2527
> - Fixed an issue where the ItemContainer was selected by releasing the mouse button on it, even when the mouse was not captured
2628
> - Fixed an issue where the Home button caused the editor to fail to display items when contained within a ScrollViewer

Examples/Nodify.Calculator/EditorView.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@
220220
PendingConnectionTemplate="{StaticResource PendingConnectionTemplate}"
221221
ConnectionTemplate="{StaticResource ConnectionTemplate}"
222222
ItemContainerStyle="{StaticResource ItemContainerStyle}"
223+
HasCustomContextMenu="True"
223224
Background="Transparent"
224225
GridCellSize="15"
225226
AllowDrop="True"

Nodify/Helpers/MultiGesture.cs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Windows.Input;
1+
using System;
2+
using System.Linq;
3+
using System.Windows.Input;
24

35
namespace Nodify
46
{
@@ -101,10 +103,113 @@ public override bool Matches(object targetElement, InputEventArgs inputEventArgs
101103
public static implicit operator InputGestureRef(MouseGesture gesture)
102104
=> new InputGestureRef { Value = gesture };
103105

106+
public static implicit operator InputGestureRef(System.Windows.Input.MouseGesture gesture)
107+
=> new InputGestureRef { Value = gesture };
108+
104109
public static implicit operator InputGestureRef(KeyGesture gesture)
105110
=> new InputGestureRef { Value = gesture };
106111

107112
public static implicit operator InputGestureRef(MultiGesture gesture)
108113
=> new InputGestureRef { Value = gesture };
109114
}
115+
116+
/// <summary>
117+
/// Represents a mouse gesture that optionally includes a specific key press as part of the gesture.
118+
/// </summary>
119+
public sealed class MouseGesture : System.Windows.Input.MouseGesture
120+
{
121+
/// <summary>
122+
/// Gets or sets the key that must be pressed to match this gesture.
123+
/// </summary>
124+
public Key Key { get; set; }
125+
126+
/// <summary>
127+
/// Initializes a new instance of the <see cref="MouseGesture"/> class with the specified mouse action, modifier keys, and a specific key.
128+
/// </summary>
129+
/// <param name="action">The action associated with this gesture.</param>
130+
/// <param name="modifiers">The modifiers associated with this gesture.</param>
131+
/// <param name="key">The key required to match the gesture.</param>
132+
public MouseGesture(MouseAction action, ModifierKeys modifiers, Key key) : base(action, modifiers)
133+
{
134+
Key = key;
135+
}
136+
137+
/// <summary>
138+
/// Initializes a new instance of the <see cref="MouseGesture"/> class with the specified mouse action and key.
139+
/// </summary>
140+
/// <param name="action">The action associated with this gesture.</param>
141+
/// <param name="key">The key required to match the gesture.</param>
142+
public MouseGesture(MouseAction action, Key key) : base(action)
143+
{
144+
Key = key;
145+
}
146+
147+
/// <inheritdoc />
148+
public MouseGesture(MouseAction action, ModifierKeys modifiers) : base(action, modifiers)
149+
{
150+
}
151+
152+
/// <inheritdoc />
153+
public MouseGesture(MouseAction action) : base(action)
154+
{
155+
}
156+
157+
/// <inheritdoc />
158+
public MouseGesture()
159+
{
160+
}
161+
162+
/// <inheritdoc />
163+
public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
164+
{
165+
if (inputEventArgs is MouseButtonEventArgs || inputEventArgs is MouseWheelEventArgs)
166+
{
167+
return base.Matches(targetElement, inputEventArgs) && MatchesKeyboard();
168+
}
169+
170+
return false;
171+
}
172+
173+
/// <summary>
174+
/// Checks whether the required key is pressed or no keys are pressed when <see cref="Key"/> is <see cref="Key.None"/>.
175+
/// </summary>
176+
private bool MatchesKeyboard()
177+
{
178+
if (Key is Key.None)
179+
{
180+
return !IsAnyKeyPressed();
181+
}
182+
183+
return Keyboard.GetKeyStates(Key).HasFlag(KeyStates.Down);
184+
}
185+
186+
private static readonly Key[] _allKeys = GetValidKeys();
187+
188+
private static Key[] GetValidKeys()
189+
{
190+
var excludedKeys = new[]
191+
{
192+
Key.LeftCtrl, Key.RightCtrl,
193+
Key.LeftShift, Key.RightShift,
194+
Key.LeftAlt, Key.RightAlt,
195+
Key.LWin, Key.RWin,
196+
Key.None
197+
};
198+
199+
#if NET5_0_OR_GREATER
200+
return Enum.GetValues<Key>()
201+
#else
202+
return Enum.GetValues(typeof(Key))
203+
.Cast<Key>()
204+
#endif
205+
.Where(key => !excludedKeys.Contains(key))
206+
.ToArray();
207+
}
208+
209+
/// <summary>
210+
/// Determines whether any key (excluding modifiers) is currently pressed.
211+
/// </summary>
212+
private static bool IsAnyKeyPressed()
213+
=> _allKeys.Any(Keyboard.IsKeyDown);
214+
}
110215
}

0 commit comments

Comments
 (0)