Skip to content

Commit da921f9

Browse files
authored
Improve cutting methods in NodifyEditor (#154)
* Improve cutting methods in NodifyEditor
1 parent 4521531 commit da921f9

File tree

6 files changed

+252
-195
lines changed

6 files changed

+252
-195
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
> - Breaking Changes:
66
> - Made the setter of NodifyEditor.IsPanning private
7+
> - Renamed StartCutting to BeginCutting in NodifyEditor
78
> - Features:
89
> - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor
10+
> - Added UpdateCuttingLine to NodifyEditor
911
> - Bugfixes:
1012
1113
#### **Version 6.6.0**

Nodify/Connections/CuttingLine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public static void SetIsOverElement(UIElement elem, bool value)
2121
=> elem.SetValue(IsOverElementProperty, value);
2222

2323
/// <summary>
24-
/// Gets or sets whether cancelling a cutting operation is allowed.
24+
/// Gets or sets whether cancelling a cutting operation is allowed (see <see cref="EditorGestures.NodifyEditorGestures.CancelAction"/>).
2525
/// </summary>
2626
public static bool AllowCuttingCancellation { get; set; } = true;
2727

Nodify/EditorStates/EditorCuttingState.cs

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
using System.Collections.Generic;
2-
using System.Windows;
3-
using System.Windows.Input;
4-
using System.Windows.Media;
1+
using System.Windows.Input;
52

63
namespace Nodify
74
{
85
public class EditorCuttingState : EditorState
96
{
10-
private readonly LineGeometry _lineGeometry = new LineGeometry();
11-
private List<FrameworkElement>? _previousConnections;
12-
137
public bool Canceled { get; set; } = CuttingLine.AllowCuttingCancellation;
148

159
public EditorCuttingState(NodifyEditor editor) : base(editor)
@@ -20,25 +14,19 @@ public override void Enter(EditorState? from)
2014
{
2115
Canceled = false;
2216

23-
var startLocation = Editor.MouseLocation;
24-
Editor.StartCutting(startLocation);
25-
26-
_lineGeometry.StartPoint = startLocation;
27-
_lineGeometry.EndPoint = startLocation;
17+
Editor.BeginCutting(Editor.MouseLocation);
2818
}
2919

3020
public override void Exit()
3121
{
32-
ResetConnectionStyle();
33-
3422
// TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)
3523
if (Canceled)
3624
{
3725
Editor.CancelCutting();
3826
}
3927
else
4028
{
41-
Editor.EndCutting(Editor.MouseLocation);
29+
Editor.EndCutting();
4230
}
4331
}
4432

@@ -60,32 +48,7 @@ public override void HandleMouseUp(MouseButtonEventArgs e)
6048

6149
public override void HandleMouseMove(MouseEventArgs e)
6250
{
63-
Editor.CuttingLineEnd = Editor.MouseLocation;
64-
65-
if (NodifyEditor.EnableCuttingLinePreview)
66-
{
67-
ResetConnectionStyle();
68-
69-
_lineGeometry.EndPoint = Editor.MouseLocation;
70-
var connections = Editor.ConnectionsHost.GetIntersectingElements(_lineGeometry, NodifyEditor.CuttingConnectionTypes);
71-
foreach (var connection in connections)
72-
{
73-
CuttingLine.SetIsOverElement(connection, true);
74-
}
75-
76-
_previousConnections = connections;
77-
}
78-
}
79-
80-
private void ResetConnectionStyle()
81-
{
82-
if (_previousConnections != null)
83-
{
84-
foreach (var connection in _previousConnections)
85-
{
86-
CuttingLine.SetIsOverElement(connection, false);
87-
}
88-
}
51+
Editor.UpdateCuttingLine(Editor.MouseLocation);
8952
}
9053

9154
public override void HandleKeyUp(KeyEventArgs e)

Nodify/EditorStates/EditorPanningState.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public EditorPanningState(NodifyEditor editor) : base(editor)
2121
/// <inheritdoc />
2222
public override void Exit()
2323
{
24+
// TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)
2425
if (Canceled)
2526
{
2627
Editor.CancelPanning();

Nodify/NodifyEditor.Cutting.cs

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
using System.Collections.Generic;
2+
using System;
3+
using System.Windows;
4+
using System.Windows.Input;
5+
using System.Windows.Media;
6+
using System.Diagnostics;
7+
8+
namespace Nodify
9+
{
10+
[StyleTypedProperty(Property = nameof(CuttingLineStyle), StyleTargetType = typeof(CuttingLine))]
11+
public partial class NodifyEditor
12+
{
13+
protected static readonly DependencyPropertyKey CuttingLineStartPropertyKey = DependencyProperty.RegisterReadOnly(nameof(CuttingLineStart), typeof(Point), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.Point));
14+
public static readonly DependencyProperty CuttingLineStartProperty = CuttingLineStartPropertyKey.DependencyProperty;
15+
16+
protected static readonly DependencyPropertyKey CuttingLineEndPropertyKey = DependencyProperty.RegisterReadOnly(nameof(CuttingLineEnd), typeof(Point), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.Point));
17+
public static readonly DependencyProperty CuttingLineEndProperty = CuttingLineEndPropertyKey.DependencyProperty;
18+
19+
protected static readonly DependencyPropertyKey IsCuttingPropertyKey = DependencyProperty.RegisterReadOnly(nameof(IsCutting), typeof(bool), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.False, OnIsCuttingChanged));
20+
public static readonly DependencyProperty IsCuttingProperty = IsCuttingPropertyKey.DependencyProperty;
21+
22+
public static readonly DependencyProperty CuttingLineStyleProperty = DependencyProperty.Register(nameof(CuttingLineStyle), typeof(Style), typeof(NodifyEditor));
23+
24+
public static readonly DependencyProperty CuttingStartedCommandProperty = DependencyProperty.Register(nameof(CuttingStartedCommand), typeof(ICommand), typeof(NodifyEditor));
25+
public static readonly DependencyProperty CuttingCompletedCommandProperty = DependencyProperty.Register(nameof(CuttingCompletedCommand), typeof(ICommand), typeof(NodifyEditor));
26+
27+
private static void OnIsCuttingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
28+
{
29+
var editor = (NodifyEditor)d;
30+
if ((bool)e.NewValue == true)
31+
editor.OnCuttingStarted();
32+
else
33+
editor.OnCuttingCompleted();
34+
}
35+
36+
private void OnCuttingCompleted()
37+
{
38+
if (CuttingCompletedCommand?.CanExecute(DataContext) ?? false)
39+
CuttingCompletedCommand.Execute(DataContext);
40+
}
41+
42+
private void OnCuttingStarted()
43+
{
44+
if (CuttingStartedCommand?.CanExecute(DataContext) ?? false)
45+
CuttingStartedCommand.Execute(DataContext);
46+
}
47+
48+
/// <summary>
49+
/// Gets or sets the style to use for the cutting line.
50+
/// </summary>
51+
public Style CuttingLineStyle
52+
{
53+
get => (Style)GetValue(CuttingLineStyleProperty);
54+
set => SetValue(CuttingLineStyleProperty, value);
55+
}
56+
57+
/// <summary>
58+
/// Gets the start point of the <see cref="CuttingLine"/> while <see cref="IsCutting"/> is true.
59+
/// </summary>
60+
public Point CuttingLineStart
61+
{
62+
get => (Point)GetValue(CuttingLineStartProperty);
63+
private set => SetValue(CuttingLineStartPropertyKey, value);
64+
}
65+
66+
/// <summary>
67+
/// Gets the end point of the <see cref="CuttingLine"/> while <see cref="IsCutting"/> is true.
68+
/// </summary>
69+
public Point CuttingLineEnd
70+
{
71+
get => (Point)GetValue(CuttingLineEndProperty);
72+
private set => SetValue(CuttingLineEndPropertyKey, value);
73+
}
74+
75+
/// <summary>
76+
/// Gets a value that indicates whether a cutting operation is in progress.
77+
/// </summary>
78+
public bool IsCutting
79+
{
80+
get => (bool)GetValue(IsCuttingProperty);
81+
private set => SetValue(IsCuttingPropertyKey, value);
82+
}
83+
84+
/// <summary>Invoked when a cutting operation is started.</summary>
85+
public ICommand? CuttingStartedCommand
86+
{
87+
get => (ICommand?)GetValue(CuttingStartedCommandProperty);
88+
set => SetValue(CuttingStartedCommandProperty, value);
89+
}
90+
91+
/// <summary>Invoked when a cutting operation is completed.</summary>
92+
public ICommand? CuttingCompletedCommand
93+
{
94+
get => (ICommand?)GetValue(CuttingCompletedCommandProperty);
95+
set => SetValue(CuttingCompletedCommandProperty, value);
96+
}
97+
98+
/// <summary>
99+
/// Gets or sets whether the cutting line should apply the preview style to the interesected elements.
100+
/// </summary>
101+
/// <remarks>
102+
/// This may hurt performance because intersection must be calculated on mouse move.
103+
/// </remarks>
104+
public static bool EnableCuttingLinePreview { get; set; } = false;
105+
106+
/// <summary>
107+
/// The list of supported connection types for cutting. Type must be derived from <see cref="FrameworkElement" />.
108+
/// </summary>
109+
public static readonly HashSet<Type> CuttingConnectionTypes = new HashSet<Type>();
110+
111+
private List<FrameworkElement>? _cuttingLinePreviousConnections;
112+
private readonly LineGeometry _cuttingLineGeometry = new LineGeometry();
113+
114+
/// <summary>
115+
/// Starts the cutting operation at the specified location. Call <see cref="EndCutting"/> to complete the operation or <see cref="CancelCutting"/> to abort it.
116+
/// </summary>
117+
/// <remarks>This method has no effect if a cutting operation is already in progress.</remarks>
118+
/// <param name="location">The starting location for cutting items, in graph space coordinates.</param>
119+
public void BeginCutting(Point location)
120+
{
121+
if (IsCutting)
122+
{
123+
return;
124+
}
125+
126+
CuttingLineStart = location;
127+
CuttingLineEnd = location;
128+
IsCutting = true;
129+
130+
_cuttingLineGeometry.StartPoint = location;
131+
_cuttingLineGeometry.EndPoint = location;
132+
}
133+
134+
/// <summary>
135+
/// Updates the current cutting line position and the style for the intersecting elements if <see cref="EnableCuttingLinePreview"/> is true.
136+
/// </summary>
137+
/// <param name="amount">The amount to adjust the cutting line's endpoint.</param>
138+
public void UpdateCuttingLine(Vector amount)
139+
{
140+
CuttingLineEnd += amount;
141+
142+
UpdateCuttingLine(CuttingLineEnd);
143+
}
144+
145+
/// <summary>
146+
/// Updates the current cutting line position and the style for the intersecting elements if <see cref="EnableCuttingLinePreview"/> is true.
147+
/// </summary>
148+
/// <param name="location">The location of the cutting line's endpoint.</param>
149+
public void UpdateCuttingLine(Point location)
150+
{
151+
Debug.Assert(IsCutting);
152+
CuttingLineEnd = location;
153+
154+
if (EnableCuttingLinePreview)
155+
{
156+
_cuttingLineGeometry.EndPoint = CuttingLineEnd;
157+
158+
ResetConnectionStyle();
159+
ApplyConnectionStyle();
160+
}
161+
}
162+
163+
/// <summary>
164+
/// Cancels the current cutting operation without applying any changes.
165+
/// </summary>
166+
/// <remarks>This method has no effect if there's no cutting operation in progress.</remarks>
167+
public void CancelCutting()
168+
{
169+
if (!CuttingLine.AllowCuttingCancellation || !IsCutting)
170+
{
171+
return;
172+
}
173+
174+
ResetConnectionStyle();
175+
IsCutting = false;
176+
}
177+
178+
/// <summary>
179+
/// Completes the cutting operation and applies the changes.
180+
/// </summary>
181+
/// <remarks>This method has no effect if there's no cutting operation in progress.</remarks>
182+
public void EndCutting()
183+
{
184+
if (!IsCutting)
185+
{
186+
return;
187+
}
188+
189+
ResetConnectionStyle();
190+
191+
var lineGeometry = new LineGeometry(CuttingLineStart, CuttingLineEnd);
192+
var connections = ConnectionsHost.GetIntersectingElements(lineGeometry, CuttingConnectionTypes);
193+
194+
if (RemoveConnectionCommand != null)
195+
{
196+
foreach (var connection in connections)
197+
{
198+
OnRemoveConnection(connection.DataContext);
199+
}
200+
}
201+
else
202+
{
203+
RemoveSupportedConnections(connections);
204+
}
205+
206+
IsCutting = false;
207+
}
208+
209+
private static void RemoveSupportedConnections(List<FrameworkElement> connections)
210+
{
211+
foreach (var connection in connections)
212+
{
213+
if (connection is BaseConnection bc)
214+
{
215+
bc.OnDisconnect();
216+
}
217+
}
218+
}
219+
220+
private void ApplyConnectionStyle()
221+
{
222+
var connections = ConnectionsHost.GetIntersectingElements(_cuttingLineGeometry, CuttingConnectionTypes);
223+
foreach (var connection in connections)
224+
{
225+
CuttingLine.SetIsOverElement(connection, true);
226+
}
227+
228+
_cuttingLinePreviousConnections = connections;
229+
}
230+
231+
private void ResetConnectionStyle()
232+
{
233+
if (_cuttingLinePreviousConnections != null)
234+
{
235+
foreach (var connection in _cuttingLinePreviousConnections)
236+
{
237+
CuttingLine.SetIsOverElement(connection, false);
238+
}
239+
240+
_cuttingLinePreviousConnections = null;
241+
}
242+
}
243+
}
244+
}

0 commit comments

Comments
 (0)