From 24b9f8c2e4f5607a8aa4ab7646c5e992100a4b4b Mon Sep 17 00:00:00 2001 From: miroiu Date: Tue, 26 Nov 2024 20:06:37 +0200 Subject: [PATCH 1/2] Improve cutting methods in NodifyEditor --- CHANGELOG.md | 2 + Nodify/Connections/CuttingLine.cs | 2 +- Nodify/EditorStates/EditorCuttingState.cs | 45 +--- Nodify/EditorStates/EditorPanningState.cs | 1 + Nodify/NodifyEditor.Cutting.cs | 244 ++++++++++++++++++++++ Nodify/NodifyEditor.cs | 153 -------------- 6 files changed, 252 insertions(+), 195 deletions(-) create mode 100644 Nodify/NodifyEditor.Cutting.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b1f19a4..2630dc75 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 UpdateCutting 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) { From 4a6aa303cf5881d56114c1fae19ac4f5e7f34f73 Mon Sep 17 00:00:00 2001 From: miroiu Date: Tue, 26 Nov 2024 20:08:10 +0200 Subject: [PATCH 2/2] Fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2630dc75..9c18bc0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ > - Renamed StartCutting to BeginCutting in NodifyEditor > - Features: > - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor -> - Added UpdateCutting to NodifyEditor +> - Added UpdateCuttingLine to NodifyEditor > - Bugfixes: #### **Version 6.6.0**