diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b1f19a4..9c18bc0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,10 @@
> - Breaking Changes:
> - Made the setter of NodifyEditor.IsPanning private
+> - Renamed StartCutting to BeginCutting in NodifyEditor
> - Features:
> - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor
+> - Added UpdateCuttingLine to NodifyEditor
> - Bugfixes:
#### **Version 6.6.0**
diff --git a/Nodify/Connections/CuttingLine.cs b/Nodify/Connections/CuttingLine.cs
index 9053fb16..754335da 100644
--- a/Nodify/Connections/CuttingLine.cs
+++ b/Nodify/Connections/CuttingLine.cs
@@ -21,7 +21,7 @@ public static void SetIsOverElement(UIElement elem, bool value)
=> elem.SetValue(IsOverElementProperty, value);
///
- /// Gets or sets whether cancelling a cutting operation is allowed.
+ /// Gets or sets whether cancelling a cutting operation is allowed (see ).
///
public static bool AllowCuttingCancellation { get; set; } = true;
diff --git a/Nodify/EditorStates/EditorCuttingState.cs b/Nodify/EditorStates/EditorCuttingState.cs
index 0b37d536..2bef1545 100644
--- a/Nodify/EditorStates/EditorCuttingState.cs
+++ b/Nodify/EditorStates/EditorCuttingState.cs
@@ -1,15 +1,9 @@
-using System.Collections.Generic;
-using System.Windows;
-using System.Windows.Input;
-using System.Windows.Media;
+using System.Windows.Input;
namespace Nodify
{
public class EditorCuttingState : EditorState
{
- private readonly LineGeometry _lineGeometry = new LineGeometry();
- private List? _previousConnections;
-
public bool Canceled { get; set; } = CuttingLine.AllowCuttingCancellation;
public EditorCuttingState(NodifyEditor editor) : base(editor)
@@ -20,17 +14,11 @@ public override void Enter(EditorState? from)
{
Canceled = false;
- var startLocation = Editor.MouseLocation;
- Editor.StartCutting(startLocation);
-
- _lineGeometry.StartPoint = startLocation;
- _lineGeometry.EndPoint = startLocation;
+ Editor.BeginCutting(Editor.MouseLocation);
}
public override void Exit()
{
- ResetConnectionStyle();
-
// TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)
if (Canceled)
{
@@ -38,7 +26,7 @@ public override void Exit()
}
else
{
- Editor.EndCutting(Editor.MouseLocation);
+ Editor.EndCutting();
}
}
@@ -60,32 +48,7 @@ public override void HandleMouseUp(MouseButtonEventArgs e)
public override void HandleMouseMove(MouseEventArgs e)
{
- Editor.CuttingLineEnd = Editor.MouseLocation;
-
- if (NodifyEditor.EnableCuttingLinePreview)
- {
- ResetConnectionStyle();
-
- _lineGeometry.EndPoint = Editor.MouseLocation;
- var connections = Editor.ConnectionsHost.GetIntersectingElements(_lineGeometry, NodifyEditor.CuttingConnectionTypes);
- foreach (var connection in connections)
- {
- CuttingLine.SetIsOverElement(connection, true);
- }
-
- _previousConnections = connections;
- }
- }
-
- private void ResetConnectionStyle()
- {
- if (_previousConnections != null)
- {
- foreach (var connection in _previousConnections)
- {
- CuttingLine.SetIsOverElement(connection, false);
- }
- }
+ Editor.UpdateCuttingLine(Editor.MouseLocation);
}
public override void HandleKeyUp(KeyEventArgs e)
diff --git a/Nodify/EditorStates/EditorPanningState.cs b/Nodify/EditorStates/EditorPanningState.cs
index d85aef9d..2c006a72 100644
--- a/Nodify/EditorStates/EditorPanningState.cs
+++ b/Nodify/EditorStates/EditorPanningState.cs
@@ -21,6 +21,7 @@ public EditorPanningState(NodifyEditor editor) : base(editor)
///
public override void Exit()
{
+ // TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)
if (Canceled)
{
Editor.CancelPanning();
diff --git a/Nodify/NodifyEditor.Cutting.cs b/Nodify/NodifyEditor.Cutting.cs
new file mode 100644
index 00000000..ee84d632
--- /dev/null
+++ b/Nodify/NodifyEditor.Cutting.cs
@@ -0,0 +1,244 @@
+using System.Collections.Generic;
+using System;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Diagnostics;
+
+namespace Nodify
+{
+ [StyleTypedProperty(Property = nameof(CuttingLineStyle), StyleTargetType = typeof(CuttingLine))]
+ public partial class NodifyEditor
+ {
+ protected static readonly DependencyPropertyKey CuttingLineStartPropertyKey = DependencyProperty.RegisterReadOnly(nameof(CuttingLineStart), typeof(Point), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.Point));
+ public static readonly DependencyProperty CuttingLineStartProperty = CuttingLineStartPropertyKey.DependencyProperty;
+
+ protected static readonly DependencyPropertyKey CuttingLineEndPropertyKey = DependencyProperty.RegisterReadOnly(nameof(CuttingLineEnd), typeof(Point), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.Point));
+ public static readonly DependencyProperty CuttingLineEndProperty = CuttingLineEndPropertyKey.DependencyProperty;
+
+ protected static readonly DependencyPropertyKey IsCuttingPropertyKey = DependencyProperty.RegisterReadOnly(nameof(IsCutting), typeof(bool), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.False, OnIsCuttingChanged));
+ public static readonly DependencyProperty IsCuttingProperty = IsCuttingPropertyKey.DependencyProperty;
+
+ public static readonly DependencyProperty CuttingLineStyleProperty = DependencyProperty.Register(nameof(CuttingLineStyle), typeof(Style), typeof(NodifyEditor));
+
+ public static readonly DependencyProperty CuttingStartedCommandProperty = DependencyProperty.Register(nameof(CuttingStartedCommand), typeof(ICommand), typeof(NodifyEditor));
+ public static readonly DependencyProperty CuttingCompletedCommandProperty = DependencyProperty.Register(nameof(CuttingCompletedCommand), typeof(ICommand), typeof(NodifyEditor));
+
+ private static void OnIsCuttingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var editor = (NodifyEditor)d;
+ if ((bool)e.NewValue == true)
+ editor.OnCuttingStarted();
+ else
+ editor.OnCuttingCompleted();
+ }
+
+ private void OnCuttingCompleted()
+ {
+ if (CuttingCompletedCommand?.CanExecute(DataContext) ?? false)
+ CuttingCompletedCommand.Execute(DataContext);
+ }
+
+ private void OnCuttingStarted()
+ {
+ if (CuttingStartedCommand?.CanExecute(DataContext) ?? false)
+ CuttingStartedCommand.Execute(DataContext);
+ }
+
+ ///
+ /// Gets or sets the style to use for the cutting line.
+ ///
+ public Style CuttingLineStyle
+ {
+ get => (Style)GetValue(CuttingLineStyleProperty);
+ set => SetValue(CuttingLineStyleProperty, value);
+ }
+
+ ///
+ /// Gets the start point of the while is true.
+ ///
+ public Point CuttingLineStart
+ {
+ get => (Point)GetValue(CuttingLineStartProperty);
+ private set => SetValue(CuttingLineStartPropertyKey, value);
+ }
+
+ ///
+ /// Gets the end point of the while is true.
+ ///
+ public Point CuttingLineEnd
+ {
+ get => (Point)GetValue(CuttingLineEndProperty);
+ private set => SetValue(CuttingLineEndPropertyKey, value);
+ }
+
+ ///
+ /// Gets a value that indicates whether a cutting operation is in progress.
+ ///
+ public bool IsCutting
+ {
+ get => (bool)GetValue(IsCuttingProperty);
+ private set => SetValue(IsCuttingPropertyKey, value);
+ }
+
+ /// Invoked when a cutting operation is started.
+ public ICommand? CuttingStartedCommand
+ {
+ get => (ICommand?)GetValue(CuttingStartedCommandProperty);
+ set => SetValue(CuttingStartedCommandProperty, value);
+ }
+
+ /// Invoked when a cutting operation is completed.
+ public ICommand? CuttingCompletedCommand
+ {
+ get => (ICommand?)GetValue(CuttingCompletedCommandProperty);
+ set => SetValue(CuttingCompletedCommandProperty, value);
+ }
+
+ ///
+ /// Gets or sets whether the cutting line should apply the preview style to the interesected elements.
+ ///
+ ///
+ /// This may hurt performance because intersection must be calculated on mouse move.
+ ///
+ public static bool EnableCuttingLinePreview { get; set; } = false;
+
+ ///
+ /// The list of supported connection types for cutting. Type must be derived from .
+ ///
+ public static readonly HashSet CuttingConnectionTypes = new HashSet();
+
+ private List? _cuttingLinePreviousConnections;
+ private readonly LineGeometry _cuttingLineGeometry = new LineGeometry();
+
+ ///
+ /// Starts the cutting operation at the specified location. Call to complete the operation or to abort it.
+ ///
+ /// This method has no effect if a cutting operation is already in progress.
+ /// The starting location for cutting items, in graph space coordinates.
+ public void BeginCutting(Point location)
+ {
+ if (IsCutting)
+ {
+ return;
+ }
+
+ CuttingLineStart = location;
+ CuttingLineEnd = location;
+ IsCutting = true;
+
+ _cuttingLineGeometry.StartPoint = location;
+ _cuttingLineGeometry.EndPoint = location;
+ }
+
+ ///
+ /// Updates the current cutting line position and the style for the intersecting elements if is true.
+ ///
+ /// The amount to adjust the cutting line's endpoint.
+ public void UpdateCuttingLine(Vector amount)
+ {
+ CuttingLineEnd += amount;
+
+ UpdateCuttingLine(CuttingLineEnd);
+ }
+
+ ///
+ /// Updates the current cutting line position and the style for the intersecting elements if is true.
+ ///
+ /// The location of the cutting line's endpoint.
+ public void UpdateCuttingLine(Point location)
+ {
+ Debug.Assert(IsCutting);
+ CuttingLineEnd = location;
+
+ if (EnableCuttingLinePreview)
+ {
+ _cuttingLineGeometry.EndPoint = CuttingLineEnd;
+
+ ResetConnectionStyle();
+ ApplyConnectionStyle();
+ }
+ }
+
+ ///
+ /// Cancels the current cutting operation without applying any changes.
+ ///
+ /// This method has no effect if there's no cutting operation in progress.
+ public void CancelCutting()
+ {
+ if (!CuttingLine.AllowCuttingCancellation || !IsCutting)
+ {
+ return;
+ }
+
+ ResetConnectionStyle();
+ IsCutting = false;
+ }
+
+ ///
+ /// Completes the cutting operation and applies the changes.
+ ///
+ /// This method has no effect if there's no cutting operation in progress.
+ public void EndCutting()
+ {
+ if (!IsCutting)
+ {
+ return;
+ }
+
+ ResetConnectionStyle();
+
+ var lineGeometry = new LineGeometry(CuttingLineStart, CuttingLineEnd);
+ var connections = ConnectionsHost.GetIntersectingElements(lineGeometry, CuttingConnectionTypes);
+
+ if (RemoveConnectionCommand != null)
+ {
+ foreach (var connection in connections)
+ {
+ OnRemoveConnection(connection.DataContext);
+ }
+ }
+ else
+ {
+ RemoveSupportedConnections(connections);
+ }
+
+ IsCutting = false;
+ }
+
+ private static void RemoveSupportedConnections(List connections)
+ {
+ foreach (var connection in connections)
+ {
+ if (connection is BaseConnection bc)
+ {
+ bc.OnDisconnect();
+ }
+ }
+ }
+
+ private void ApplyConnectionStyle()
+ {
+ var connections = ConnectionsHost.GetIntersectingElements(_cuttingLineGeometry, CuttingConnectionTypes);
+ foreach (var connection in connections)
+ {
+ CuttingLine.SetIsOverElement(connection, true);
+ }
+
+ _cuttingLinePreviousConnections = connections;
+ }
+
+ private void ResetConnectionStyle()
+ {
+ if (_cuttingLinePreviousConnections != null)
+ {
+ foreach (var connection in _cuttingLinePreviousConnections)
+ {
+ CuttingLine.SetIsOverElement(connection, false);
+ }
+
+ _cuttingLinePreviousConnections = null;
+ }
+ }
+ }
+}
diff --git a/Nodify/NodifyEditor.cs b/Nodify/NodifyEditor.cs
index d90182a9..1f1a7b87 100644
--- a/Nodify/NodifyEditor.cs
+++ b/Nodify/NodifyEditor.cs
@@ -21,7 +21,6 @@ namespace Nodify
[StyleTypedProperty(Property = nameof(ItemContainerStyle), StyleTargetType = typeof(ItemContainer))]
[StyleTypedProperty(Property = nameof(DecoratorContainerStyle), StyleTargetType = typeof(DecoratorContainer))]
[StyleTypedProperty(Property = nameof(SelectionRectangleStyle), StyleTargetType = typeof(Rectangle))]
- [StyleTypedProperty(Property = nameof(CuttingLineStyle), StyleTargetType = typeof(CuttingLine))]
[ContentProperty(nameof(Decorators))]
[DefaultProperty(nameof(Decorators))]
public partial class NodifyEditor : MultiSelector
@@ -252,7 +251,6 @@ private void ApplyRenderingOptimizations()
public static readonly DependencyProperty DecoratorTemplateProperty = DependencyProperty.Register(nameof(DecoratorTemplate), typeof(DataTemplate), typeof(NodifyEditor));
public static readonly DependencyProperty PendingConnectionTemplateProperty = DependencyProperty.Register(nameof(PendingConnectionTemplate), typeof(DataTemplate), typeof(NodifyEditor));
public static readonly DependencyProperty SelectionRectangleStyleProperty = DependencyProperty.Register(nameof(SelectionRectangleStyle), typeof(Style), typeof(NodifyEditor));
- public static readonly DependencyProperty CuttingLineStyleProperty = DependencyProperty.Register(nameof(CuttingLineStyle), typeof(Style), typeof(NodifyEditor));
public static readonly DependencyProperty DecoratorContainerStyleProperty = DependencyProperty.Register(nameof(DecoratorContainerStyle), typeof(Style), typeof(NodifyEditor));
///
@@ -319,15 +317,6 @@ public Style SelectionRectangleStyle
set => SetValue(SelectionRectangleStyleProperty, value);
}
- ///
- /// Gets or sets the style to use for the cutting line.
- ///
- public Style CuttingLineStyle
- {
- get => (Style)GetValue(CuttingLineStyleProperty);
- set => SetValue(CuttingLineStyleProperty, value);
- }
-
///
/// Gets or sets the style to use for the .
///
@@ -347,15 +336,6 @@ public Style DecoratorContainerStyle
protected static readonly DependencyPropertyKey IsSelectingPropertyKey = DependencyProperty.RegisterReadOnly(nameof(IsSelecting), typeof(bool), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.False, OnIsSelectingChanged));
public static readonly DependencyProperty IsSelectingProperty = IsSelectingPropertyKey.DependencyProperty;
- protected static readonly DependencyPropertyKey CuttingLineStartPropertyKey = DependencyProperty.RegisterReadOnly(nameof(CuttingLineStart), typeof(Point), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.Point));
- public static readonly DependencyProperty CuttingLineStartProperty = CuttingLineStartPropertyKey.DependencyProperty;
-
- protected static readonly DependencyPropertyKey CuttingLineEndPropertyKey = DependencyProperty.RegisterReadOnly(nameof(CuttingLineEnd), typeof(Point), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.Point));
- public static readonly DependencyProperty CuttingLineEndProperty = CuttingLineEndPropertyKey.DependencyProperty;
-
- protected static readonly DependencyPropertyKey IsCuttingPropertyKey = DependencyProperty.RegisterReadOnly(nameof(IsCutting), typeof(bool), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.False, OnIsCuttingChanged));
- public static readonly DependencyProperty IsCuttingProperty = IsCuttingPropertyKey.DependencyProperty;
-
protected static readonly DependencyPropertyKey MouseLocationPropertyKey = DependencyProperty.RegisterReadOnly(nameof(MouseLocation), typeof(Point), typeof(NodifyEditor), new FrameworkPropertyMetadata(BoxValue.Point));
public static readonly DependencyProperty MouseLocationProperty = MouseLocationPropertyKey.DependencyProperty;
@@ -380,27 +360,6 @@ private void OnItemsSelectStarted()
ItemsSelectStartedCommand.Execute(DataContext);
}
- private static void OnIsCuttingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var editor = (NodifyEditor)d;
- if ((bool)e.NewValue == true)
- editor.OnCuttingStarted();
- else
- editor.OnCuttingCompleted();
- }
-
- private void OnCuttingCompleted()
- {
- if (CuttingCompletedCommand?.CanExecute(DataContext) ?? false)
- CuttingCompletedCommand.Execute(DataContext);
- }
-
- private void OnCuttingStarted()
- {
- if (CuttingStartedCommand?.CanExecute(DataContext) ?? false)
- CuttingStartedCommand.Execute(DataContext);
- }
-
///
/// Gets the currently selected area while is true.
///
@@ -419,33 +378,6 @@ public bool IsSelecting
internal set => SetValue(IsSelectingPropertyKey, value);
}
- ///
- /// Gets the start point of the while is true.
- ///
- public Point CuttingLineStart
- {
- get => (Point)GetValue(CuttingLineStartProperty);
- private set => SetValue(CuttingLineStartPropertyKey, value);
- }
-
- ///
- /// Gets the end point of the while is true.
- ///
- public Point CuttingLineEnd
- {
- get => (Point)GetValue(CuttingLineEndProperty);
- protected internal set => SetValue(CuttingLineEndPropertyKey, value);
- }
-
- ///
- /// Gets a value that indicates whether a cutting operation is in progress.
- ///
- public bool IsCutting
- {
- get => (bool)GetValue(IsCuttingProperty);
- private set => SetValue(IsCuttingPropertyKey, value);
- }
-
///
/// Gets the current mouse location in graph space coordinates (relative to the ).
///
@@ -603,8 +535,6 @@ private bool CanSelectMultipleItemsBase
public static readonly DependencyProperty ItemsDragCompletedCommandProperty = DependencyProperty.Register(nameof(ItemsDragCompletedCommand), typeof(ICommand), typeof(NodifyEditor));
public static readonly DependencyProperty ItemsSelectStartedCommandProperty = DependencyProperty.Register(nameof(ItemsSelectStartedCommand), typeof(ICommand), typeof(NodifyEditor));
public static readonly DependencyProperty ItemsSelectCompletedCommandProperty = DependencyProperty.Register(nameof(ItemsSelectCompletedCommand), typeof(ICommand), typeof(NodifyEditor));
- public static readonly DependencyProperty CuttingStartedCommandProperty = DependencyProperty.Register(nameof(CuttingStartedCommand), typeof(ICommand), typeof(NodifyEditor));
- public static readonly DependencyProperty CuttingCompletedCommandProperty = DependencyProperty.Register(nameof(CuttingCompletedCommand), typeof(ICommand), typeof(NodifyEditor));
///
/// Invoked when the is completed.
@@ -682,20 +612,6 @@ public ICommand? ItemsSelectCompletedCommand
set => SetValue(ItemsSelectCompletedCommandProperty, value);
}
- /// Invoked when a cutting operation is started.
- public ICommand? CuttingStartedCommand
- {
- get => (ICommand?)GetValue(CuttingStartedCommandProperty);
- set => SetValue(CuttingStartedCommandProperty, value);
- }
-
- /// Invoked when a cutting operation is completed.
- public ICommand? CuttingCompletedCommand
- {
- get => (ICommand?)GetValue(CuttingCompletedCommandProperty);
- set => SetValue(CuttingCompletedCommandProperty, value);
- }
-
#endregion
#region Fields
@@ -731,19 +647,6 @@ public ICommand? CuttingCompletedCommand
///
public static bool EnableDraggingContainersOptimizations { get; set; } = true;
- ///
- /// Gets or sets whether the cutting line should apply the preview style to the interesected elements.
- ///
- ///
- /// This may hurt performance because intersection must be calculated on mouse move.
- ///
- public static bool EnableCuttingLinePreview { get; set; } = false;
-
- ///
- /// The list of supported connection types for cutting. Type must be derived from .
- ///
- public static readonly HashSet CuttingConnectionTypes = new HashSet();
-
///
/// Tells if the is doing operations on multiple items at once.
///
@@ -1437,62 +1340,6 @@ internal IDraggingStrategy CreateDraggingStrategy(IEnumerable con
#endregion
- #region Cutting
-
- ///
- /// Starts the cutting operation at the specified location. Call to finish cutting.
- ///
- protected internal void StartCutting(Point location)
- {
- CuttingLineStart = location;
- CuttingLineEnd = location;
- IsCutting = true;
- }
-
- ///
- /// Cancels the cutting operation.
- ///
- protected internal void CancelCutting()
- {
- if (IsCutting)
- {
- IsCutting = false;
- }
- }
-
- ///
- /// Ends the cutting operation at the specified location.
- ///
- protected internal void EndCutting(Point location)
- {
- CuttingLineEnd = location;
-
- var lineGeometry = new LineGeometry(CuttingLineStart, CuttingLineEnd);
- var connections = ConnectionsHost.GetIntersectingElements(lineGeometry, CuttingConnectionTypes);
-
- if (RemoveConnectionCommand != null)
- {
- foreach (var connection in connections)
- {
- OnRemoveConnection(connection.DataContext);
- }
- }
- else
- {
- foreach (var connection in connections)
- {
- if (connection is BaseConnection bc)
- {
- bc.OnDisconnect();
- }
- }
- }
-
- IsCutting = false;
- }
-
- #endregion
-
///
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{