Skip to content

Commit 8a8630b

Browse files
authored
Add dragging methods to ItemContainer (#160)
* Add dragging methods to ItemContainer * Removed container drag events and simplified dragging logic * Start dragging after a distance threshold
1 parent e4c5a50 commit 8a8630b

File tree

10 files changed

+161
-181
lines changed

10 files changed

+161
-181
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44

55
> - Breaking Changes:
66
> - Made the setter of NodifyEditor.IsPanning private
7+
> - Made SelectionHelper internal
78
> - Renamed StartCutting to BeginCutting in NodifyEditor
89
> - Renamed PushItems to UpdatePushedArea and StartPushingItems to BeginPushingItems in NodifyEditor
910
> - Renamed UnselectAllConnection to UnselectAllConnections in NodifyEditor
11+
> - Removed DragStarted, DragDelta and DragCompleted routed events from ItemContainer
1012
> - Features:
1113
> - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor
1214
> - Added UpdateCuttingLine to NodifyEditor
1315
> - Added BeginSelecting, UpdateSelection, EndSelecting, CancelSelecting and AllowSelectionCancellation to NodifyEditor
1416
> - Added IsDragging, BeginDragging, UpdateDragging, EndDragging and CancelDragging to NodifyEditor
17+
> - Added Select, BeginDragging, UpdateDragging, EndDragging and CancelDragging to ItemContainer
1518
> - Bugfixes:
19+
> - Fixed ItemContainer being selected by releasing the mouse button on it without having the mouse captured
1620
1721
#### **Version 6.6.0**
1822

Examples/Nodify.Calculator/EditorView.xaml.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ public EditorView()
1111
InitializeComponent();
1212

1313
EventManager.RegisterClassHandler(typeof(NodifyEditor), MouseLeftButtonDownEvent, new MouseButtonEventHandler(CloseOperationsMenu));
14-
EventManager.RegisterClassHandler(typeof(ItemContainer), ItemContainer.DragStartedEvent, new RoutedEventHandler(CloseOperationsMenu));
1514
EventManager.RegisterClassHandler(typeof(NodifyEditor), MouseRightButtonUpEvent, new MouseButtonEventHandler(OpenOperationsMenu));
1615
}
1716

Nodify/Connections/ConnectionContainer.cs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -102,28 +102,36 @@ protected override void OnMouseUp(MouseButtonEventArgs e)
102102
EditorGestures.ConnectionGestures gestures = EditorGestures.Mappings.Connection;
103103
if (gestures.Selection.Select.Matches(e.Source, e))
104104
{
105-
if (gestures.Selection.Append.Matches(e.Source, e))
105+
var selectionType = gestures.Selection.GetSelectionType(e);
106+
bool allowContextMenu = e.ChangedButton == MouseButton.Right && IsSelected;
107+
if (!(selectionType == SelectionType.Replace && allowContextMenu))
106108
{
107-
IsSelected = true;
109+
Select(selectionType);
108110
}
109-
else if (gestures.Selection.Invert.Matches(e.Source, e))
110-
{
111-
IsSelected = !IsSelected;
112-
}
113-
else if (gestures.Selection.Remove.Matches(e.Source, e))
114-
{
115-
IsSelected = false;
116-
}
117-
else
118-
{
119-
// Allow context menu on selection
120-
if (!(e.ChangedButton == MouseButton.Right && e.RightButton == MouseButtonState.Released) || !IsSelected)
121-
{
122-
Selector.UnselectAll();
123-
}
111+
}
112+
}
124113

114+
/// <summary>
115+
/// Modifies the selection state of the current item based on the specified selection type.
116+
/// </summary>
117+
/// <param name="type">The type of selection to perform.</param>
118+
private void Select(SelectionType type)
119+
{
120+
switch (type)
121+
{
122+
case SelectionType.Append:
125123
IsSelected = true;
126-
}
124+
break;
125+
case SelectionType.Remove:
126+
IsSelected = false;
127+
break;
128+
case SelectionType.Invert:
129+
IsSelected = !IsSelected;
130+
break;
131+
case SelectionType.Replace:
132+
Selector.UnselectAll();
133+
IsSelected = true;
134+
break;
127135
}
128136
}
129137
}
Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
using System.Windows.Input;
1+
using System.Windows;
2+
using System.Windows.Input;
23

34
namespace Nodify
45
{
56
/// <summary>The default state of the <see cref="ItemContainer"/>.</summary>
67
public class ContainerDefaultState : ContainerState
78
{
8-
private bool _canBeDragging;
9-
private bool _canceled;
9+
private Point _initialPosition;
10+
private SelectionType? _selectionType;
11+
private bool _isDragging;
1012

1113
/// <summary>Creates a new instance of the <see cref="ContainerDefaultState"/>.</summary>
1214
/// <param name="container">The owner of the state.</param>
@@ -17,80 +19,68 @@ public ContainerDefaultState(ItemContainer container) : base(container)
1719
/// <inheritdoc />
1820
public override void ReEnter(ContainerState from)
1921
{
20-
if (from is ContainerDraggingState drag)
21-
{
22-
Container.IsSelected = true;
23-
_canceled = drag.Canceled;
24-
}
25-
26-
_canBeDragging = false;
22+
_isDragging = false;
23+
_selectionType = null;
24+
_initialPosition = Editor.MouseLocation;
2725
}
2826

2927
/// <inheritdoc />
3028
public override void HandleMouseDown(MouseButtonEventArgs e)
3129
{
32-
_canceled = false;
33-
3430
EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
3531
if (gestures.Drag.Matches(e.Source, e))
3632
{
37-
_canBeDragging = Container.IsDraggable;
33+
_isDragging = Container.IsDraggable;
34+
}
3835

39-
// Clear the selection if dragging an item that is not part of the selection will not add it to the selection
40-
if (_canBeDragging && !Container.IsSelected && !gestures.Selection.Append.Matches(e.Source, e) && !gestures.Selection.Invert.Matches(e.Source, e))
41-
{
42-
Editor.UnselectAll();
43-
}
36+
if (gestures.Selection.Select.Matches(e.Source, e))
37+
{
38+
_selectionType = gestures.Selection.GetSelectionType(e);
4439
}
40+
41+
_initialPosition = Editor.MouseLocation;
4542
}
4643

4744
/// <inheritdoc />
48-
public override void HandleMouseUp(MouseButtonEventArgs e)
45+
public override void HandleMouseMove(MouseEventArgs e)
4946
{
50-
EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
51-
if (!_canceled && gestures.Selection.Select.Matches(e.Source, e))
47+
double dragThreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;
48+
double dragDistance = (Editor.MouseLocation - _initialPosition).LengthSquared;
49+
50+
if (_isDragging && (dragDistance > dragThreshold))
5251
{
53-
if (gestures.Selection.Append.Matches(e.Source, e))
54-
{
55-
Container.IsSelected = true;
56-
}
57-
else if (gestures.Selection.Invert.Matches(e.Source, e))
58-
{
59-
Container.IsSelected = !Container.IsSelected;
60-
}
61-
else if (gestures.Selection.Remove.Matches(e.Source, e))
62-
{
63-
Container.IsSelected = false;
64-
}
65-
else
52+
if (!Container.IsSelected)
6653
{
67-
// Allow context menu on selection
68-
if (!(e.ChangedButton == MouseButton.Right && e.RightButton == MouseButtonState.Released) || !Container.IsSelected)
69-
{
70-
Editor.UnselectAll();
71-
}
72-
73-
Container.IsSelected = true;
54+
var selectionType = GetSelectionTypeForDragging(_selectionType);
55+
Container.Select(selectionType);
7456
}
7557

76-
_canBeDragging = false;
58+
PushState(new ContainerDraggingState(Container));
7759
}
60+
}
7861

79-
if(!_canceled && gestures.Drag.Matches(e.Source, e))
62+
/// <inheritdoc />
63+
public override void HandleMouseUp(MouseButtonEventArgs e)
64+
{
65+
if (_selectionType.HasValue)
8066
{
81-
_canBeDragging = false;
67+
bool allowContextMenu = e.ChangedButton == MouseButton.Right && Container.IsSelected;
68+
if (!(_selectionType == SelectionType.Replace && allowContextMenu))
69+
{
70+
Container.Select(_selectionType.Value);
71+
}
8272
}
8373

84-
_canceled = false;
74+
_isDragging = false;
75+
_selectionType = null;
8576
}
8677

87-
/// <inheritdoc />
88-
public override void HandleMouseMove(MouseEventArgs e)
78+
private static SelectionType GetSelectionTypeForDragging(SelectionType? selectionType)
8979
{
90-
if (_canBeDragging)
91-
{
92-
PushState(new ContainerDraggingState(Container));
93-
}
80+
// Always select the container when dragging
81+
return selectionType == SelectionType.Remove
82+
? SelectionType.Replace
83+
: selectionType.GetValueOrDefault(SelectionType.Replace);
9484
}
9585
}
9686
}

Nodify/EditorStates/ContainerDraggingState.cs

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Windows;
2-
using System.Windows.Controls.Primitives;
32
using System.Windows.Input;
43

54
namespace Nodify
@@ -9,8 +8,8 @@ public class ContainerDraggingState : ContainerState
98
{
109
private Point _initialMousePosition;
1110
private Point _previousMousePosition;
12-
private Point _currentMousePosition;
13-
public bool Canceled { get; set; } = ItemContainer.AllowDraggingCancellation; // Because of LostMouseCapture that calls Exit
11+
12+
private bool Canceled { get; set; } = ItemContainer.AllowDraggingCancellation;
1413

1514
/// <summary>Constructs an instance of the <see cref="ContainerDraggingState"/> state.</summary>
1615
/// <param name="container">The owner of the state.</param>
@@ -21,74 +20,71 @@ public ContainerDraggingState(ItemContainer container) : base(container)
2120
/// <inheritdoc />
2221
public override void Enter(ContainerState? from)
2322
{
24-
_initialMousePosition = Mouse.GetPosition(Editor.ItemsHost);
25-
26-
Container.IsSelected = true;
27-
Container.IsPreviewingLocation = true;
28-
Container.RaiseEvent(new DragStartedEventArgs(_initialMousePosition.X, _initialMousePosition.Y)
29-
{
30-
RoutedEvent = ItemContainer.DragStartedEvent
31-
});
23+
Canceled = false;
3224

25+
_initialMousePosition = Editor.MouseLocation;
3326
_previousMousePosition = _initialMousePosition;
27+
28+
Container.BeginDragging();
3429
}
3530

3631
/// <inheritdoc />
3732
public override void Exit()
3833
{
39-
Container.IsPreviewingLocation = false;
40-
var delta = _currentMousePosition - _initialMousePosition;
41-
Container.RaiseEvent(new DragCompletedEventArgs(delta.X, delta.Y, Canceled)
34+
// TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)
35+
if (Canceled)
4236
{
43-
RoutedEvent = ItemContainer.DragCompletedEvent
44-
});
37+
Container.CancelDragging();
38+
}
39+
else
40+
{
41+
Container.EndDragging();
42+
}
4543
}
4644

4745
/// <inheritdoc />
4846
public override void HandleMouseMove(MouseEventArgs e)
4947
{
50-
_currentMousePosition = e.GetPosition(Editor.ItemsHost);
51-
var delta = _currentMousePosition - _previousMousePosition;
52-
Container.RaiseEvent(new DragDeltaEventArgs(delta.X, delta.Y)
53-
{
54-
RoutedEvent = ItemContainer.DragDeltaEvent
55-
});
56-
57-
_previousMousePosition = _currentMousePosition;
48+
Container.UpdateDragging(Editor.MouseLocation - _previousMousePosition);
49+
_previousMousePosition = Editor.MouseLocation;
5850
}
5951

6052
/// <inheritdoc />
6153
public override void HandleMouseUp(MouseButtonEventArgs e)
6254
{
6355
EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
64-
65-
bool canCancel = gestures.CancelAction.Matches(e.Source, e) && ItemContainer.AllowDraggingCancellation;
66-
bool canComplete = gestures.Drag.Matches(e.Source, e);
67-
if (canCancel || canComplete)
56+
if (gestures.Drag.Matches(e.Source, e))
6857
{
69-
// Prevent canceling if drag and cancel are bound to the same mouse action
70-
Canceled = !canComplete && canCancel;
71-
72-
// Handle right click if dragging or canceled and moved the mouse more than threshold so context menus don't open
58+
// Suppress the context menu if the mouse moved beyond the defined drag threshold
7359
if (e.ChangedButton == MouseButton.Right)
7460
{
75-
double contextMenuTreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;
76-
if ((_currentMousePosition - _initialMousePosition).LengthSquared > contextMenuTreshold)
61+
double dragThreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;
62+
double dragDistance = (Editor.MouseLocation - _initialMousePosition).LengthSquared;
63+
64+
if (dragDistance > dragThreshold)
7765
{
7866
e.Handled = true;
7967
}
8068
}
8169

70+
PopState();
71+
}
72+
else if (ItemContainer.AllowDraggingCancellation && gestures.CancelAction.Matches(e.Source, e))
73+
{
74+
Canceled = true;
75+
e.Handled = true;
76+
8277
PopState();
8378
}
8479
}
8580

8681
/// <inheritdoc />
8782
public override void HandleKeyUp(KeyEventArgs e)
8883
{
89-
Canceled = EditorGestures.Mappings.ItemContainer.CancelAction.Matches(e.Source, e) && ItemContainer.AllowDraggingCancellation;
90-
if (Canceled)
84+
EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
85+
if (ItemContainer.AllowDraggingCancellation && gestures.CancelAction.Matches(e.Source, e))
9186
{
87+
Canceled = true;
9288
PopState();
9389
}
9490
}

Nodify/EditorStates/EditorDefaultState.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public override void HandleMouseDown(MouseButtonEventArgs e)
3333
EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;
3434
if (Editor.CanSelectMultipleItems && gestures.Selection.Select.Matches(e.Source, e))
3535
{
36-
SelectionType selectionType = SelectionHelper.GetSelectionType(e);
36+
SelectionType selectionType = gestures.Selection.GetSelectionType(e);
3737
PushState(new EditorSelectingState(Editor, selectionType));
3838
}
3939
else if (!Editor.DisablePanning && gestures.Pan.Matches(e.Source, e))

Nodify/Helpers/SelectionHelper.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,12 @@ private void PreviewInvertSelection(Rect area, bool fit = false)
167167
}
168168
}
169169
}
170+
}
170171

171-
internal static SelectionType GetSelectionType(MouseButtonEventArgs e)
172+
internal static class SelectionGesturesExtensions
173+
{
174+
public static SelectionType GetSelectionType(this EditorGestures.SelectionGestures gestures, MouseButtonEventArgs e)
172175
{
173-
EditorGestures.SelectionGestures gestures = EditorGestures.Mappings.Editor.Selection;
174176
if (gestures.Append.Matches(e.Source, e))
175177
{
176178
return SelectionType.Append;

0 commit comments

Comments
 (0)