diff --git a/CHANGELOG.md b/CHANGELOG.md
index 02d508f7..7654da54 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,15 +4,19 @@
> - Breaking Changes:
> - Made the setter of NodifyEditor.IsPanning private
+> - Made SelectionHelper internal
> - Renamed StartCutting to BeginCutting in NodifyEditor
> - Renamed PushItems to UpdatePushedArea and StartPushingItems to BeginPushingItems in NodifyEditor
> - Renamed UnselectAllConnection to UnselectAllConnections in NodifyEditor
+> - Removed DragStarted, DragDelta and DragCompleted routed events from ItemContainer
> - Features:
> - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor
> - Added UpdateCuttingLine to NodifyEditor
> - Added BeginSelecting, UpdateSelection, EndSelecting, CancelSelecting and AllowSelectionCancellation to NodifyEditor
> - Added IsDragging, BeginDragging, UpdateDragging, EndDragging and CancelDragging to NodifyEditor
+> - Added Select, BeginDragging, UpdateDragging, EndDragging and CancelDragging to ItemContainer
> - Bugfixes:
+> - Fixed ItemContainer being selected by releasing the mouse button on it without having the mouse captured
#### **Version 6.6.0**
diff --git a/Examples/Nodify.Calculator/EditorView.xaml.cs b/Examples/Nodify.Calculator/EditorView.xaml.cs
index a0004a4c..87a0f6e3 100644
--- a/Examples/Nodify.Calculator/EditorView.xaml.cs
+++ b/Examples/Nodify.Calculator/EditorView.xaml.cs
@@ -11,7 +11,6 @@ public EditorView()
InitializeComponent();
EventManager.RegisterClassHandler(typeof(NodifyEditor), MouseLeftButtonDownEvent, new MouseButtonEventHandler(CloseOperationsMenu));
- EventManager.RegisterClassHandler(typeof(ItemContainer), ItemContainer.DragStartedEvent, new RoutedEventHandler(CloseOperationsMenu));
EventManager.RegisterClassHandler(typeof(NodifyEditor), MouseRightButtonUpEvent, new MouseButtonEventHandler(OpenOperationsMenu));
}
diff --git a/Nodify/Connections/ConnectionContainer.cs b/Nodify/Connections/ConnectionContainer.cs
index 38479941..671c2acb 100644
--- a/Nodify/Connections/ConnectionContainer.cs
+++ b/Nodify/Connections/ConnectionContainer.cs
@@ -102,28 +102,36 @@ protected override void OnMouseUp(MouseButtonEventArgs e)
EditorGestures.ConnectionGestures gestures = EditorGestures.Mappings.Connection;
if (gestures.Selection.Select.Matches(e.Source, e))
{
- if (gestures.Selection.Append.Matches(e.Source, e))
+ var selectionType = gestures.Selection.GetSelectionType(e);
+ bool allowContextMenu = e.ChangedButton == MouseButton.Right && IsSelected;
+ if (!(selectionType == SelectionType.Replace && allowContextMenu))
{
- IsSelected = true;
+ Select(selectionType);
}
- else if (gestures.Selection.Invert.Matches(e.Source, e))
- {
- IsSelected = !IsSelected;
- }
- else if (gestures.Selection.Remove.Matches(e.Source, e))
- {
- IsSelected = false;
- }
- else
- {
- // Allow context menu on selection
- if (!(e.ChangedButton == MouseButton.Right && e.RightButton == MouseButtonState.Released) || !IsSelected)
- {
- Selector.UnselectAll();
- }
+ }
+ }
+ ///
+ /// Modifies the selection state of the current item based on the specified selection type.
+ ///
+ /// The type of selection to perform.
+ private void Select(SelectionType type)
+ {
+ switch (type)
+ {
+ case SelectionType.Append:
IsSelected = true;
- }
+ break;
+ case SelectionType.Remove:
+ IsSelected = false;
+ break;
+ case SelectionType.Invert:
+ IsSelected = !IsSelected;
+ break;
+ case SelectionType.Replace:
+ Selector.UnselectAll();
+ IsSelected = true;
+ break;
}
}
}
diff --git a/Nodify/EditorStates/ContainerDefaultState.cs b/Nodify/EditorStates/ContainerDefaultState.cs
index d69469e0..02aac831 100644
--- a/Nodify/EditorStates/ContainerDefaultState.cs
+++ b/Nodify/EditorStates/ContainerDefaultState.cs
@@ -1,12 +1,14 @@
-using System.Windows.Input;
+using System.Windows;
+using System.Windows.Input;
namespace Nodify
{
/// The default state of the .
public class ContainerDefaultState : ContainerState
{
- private bool _canBeDragging;
- private bool _canceled;
+ private Point _initialPosition;
+ private SelectionType? _selectionType;
+ private bool _isDragging;
/// Creates a new instance of the .
/// The owner of the state.
@@ -17,80 +19,68 @@ public ContainerDefaultState(ItemContainer container) : base(container)
///
public override void ReEnter(ContainerState from)
{
- if (from is ContainerDraggingState drag)
- {
- Container.IsSelected = true;
- _canceled = drag.Canceled;
- }
-
- _canBeDragging = false;
+ _isDragging = false;
+ _selectionType = null;
+ _initialPosition = Editor.MouseLocation;
}
///
public override void HandleMouseDown(MouseButtonEventArgs e)
{
- _canceled = false;
-
EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
if (gestures.Drag.Matches(e.Source, e))
{
- _canBeDragging = Container.IsDraggable;
+ _isDragging = Container.IsDraggable;
+ }
- // Clear the selection if dragging an item that is not part of the selection will not add it to the selection
- if (_canBeDragging && !Container.IsSelected && !gestures.Selection.Append.Matches(e.Source, e) && !gestures.Selection.Invert.Matches(e.Source, e))
- {
- Editor.UnselectAll();
- }
+ if (gestures.Selection.Select.Matches(e.Source, e))
+ {
+ _selectionType = gestures.Selection.GetSelectionType(e);
}
+
+ _initialPosition = Editor.MouseLocation;
}
///
- public override void HandleMouseUp(MouseButtonEventArgs e)
+ public override void HandleMouseMove(MouseEventArgs e)
{
- EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
- if (!_canceled && gestures.Selection.Select.Matches(e.Source, e))
+ double dragThreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;
+ double dragDistance = (Editor.MouseLocation - _initialPosition).LengthSquared;
+
+ if (_isDragging && (dragDistance > dragThreshold))
{
- if (gestures.Selection.Append.Matches(e.Source, e))
- {
- Container.IsSelected = true;
- }
- else if (gestures.Selection.Invert.Matches(e.Source, e))
- {
- Container.IsSelected = !Container.IsSelected;
- }
- else if (gestures.Selection.Remove.Matches(e.Source, e))
- {
- Container.IsSelected = false;
- }
- else
+ if (!Container.IsSelected)
{
- // Allow context menu on selection
- if (!(e.ChangedButton == MouseButton.Right && e.RightButton == MouseButtonState.Released) || !Container.IsSelected)
- {
- Editor.UnselectAll();
- }
-
- Container.IsSelected = true;
+ var selectionType = GetSelectionTypeForDragging(_selectionType);
+ Container.Select(selectionType);
}
- _canBeDragging = false;
+ PushState(new ContainerDraggingState(Container));
}
+ }
- if(!_canceled && gestures.Drag.Matches(e.Source, e))
+ ///
+ public override void HandleMouseUp(MouseButtonEventArgs e)
+ {
+ if (_selectionType.HasValue)
{
- _canBeDragging = false;
+ bool allowContextMenu = e.ChangedButton == MouseButton.Right && Container.IsSelected;
+ if (!(_selectionType == SelectionType.Replace && allowContextMenu))
+ {
+ Container.Select(_selectionType.Value);
+ }
}
- _canceled = false;
+ _isDragging = false;
+ _selectionType = null;
}
- ///
- public override void HandleMouseMove(MouseEventArgs e)
+ private static SelectionType GetSelectionTypeForDragging(SelectionType? selectionType)
{
- if (_canBeDragging)
- {
- PushState(new ContainerDraggingState(Container));
- }
+ // Always select the container when dragging
+ return selectionType == SelectionType.Remove
+ ? SelectionType.Replace
+ : selectionType.GetValueOrDefault(SelectionType.Replace);
}
}
}
diff --git a/Nodify/EditorStates/ContainerDraggingState.cs b/Nodify/EditorStates/ContainerDraggingState.cs
index 2ce2343f..6c0ca312 100644
--- a/Nodify/EditorStates/ContainerDraggingState.cs
+++ b/Nodify/EditorStates/ContainerDraggingState.cs
@@ -1,5 +1,4 @@
using System.Windows;
-using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace Nodify
@@ -9,8 +8,8 @@ public class ContainerDraggingState : ContainerState
{
private Point _initialMousePosition;
private Point _previousMousePosition;
- private Point _currentMousePosition;
- public bool Canceled { get; set; } = ItemContainer.AllowDraggingCancellation; // Because of LostMouseCapture that calls Exit
+
+ private bool Canceled { get; set; } = ItemContainer.AllowDraggingCancellation;
/// Constructs an instance of the state.
/// The owner of the state.
@@ -21,64 +20,60 @@ public ContainerDraggingState(ItemContainer container) : base(container)
///
public override void Enter(ContainerState? from)
{
- _initialMousePosition = Mouse.GetPosition(Editor.ItemsHost);
-
- Container.IsSelected = true;
- Container.IsPreviewingLocation = true;
- Container.RaiseEvent(new DragStartedEventArgs(_initialMousePosition.X, _initialMousePosition.Y)
- {
- RoutedEvent = ItemContainer.DragStartedEvent
- });
+ Canceled = false;
+ _initialMousePosition = Editor.MouseLocation;
_previousMousePosition = _initialMousePosition;
+
+ Container.BeginDragging();
}
///
public override void Exit()
{
- Container.IsPreviewingLocation = false;
- var delta = _currentMousePosition - _initialMousePosition;
- Container.RaiseEvent(new DragCompletedEventArgs(delta.X, delta.Y, Canceled)
+ // TODO: This is not canceled on LostMouseCapture (add OnLostMouseCapture/OnCancel callback?)
+ if (Canceled)
{
- RoutedEvent = ItemContainer.DragCompletedEvent
- });
+ Container.CancelDragging();
+ }
+ else
+ {
+ Container.EndDragging();
+ }
}
///
public override void HandleMouseMove(MouseEventArgs e)
{
- _currentMousePosition = e.GetPosition(Editor.ItemsHost);
- var delta = _currentMousePosition - _previousMousePosition;
- Container.RaiseEvent(new DragDeltaEventArgs(delta.X, delta.Y)
- {
- RoutedEvent = ItemContainer.DragDeltaEvent
- });
-
- _previousMousePosition = _currentMousePosition;
+ Container.UpdateDragging(Editor.MouseLocation - _previousMousePosition);
+ _previousMousePosition = Editor.MouseLocation;
}
///
public override void HandleMouseUp(MouseButtonEventArgs e)
{
EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
-
- bool canCancel = gestures.CancelAction.Matches(e.Source, e) && ItemContainer.AllowDraggingCancellation;
- bool canComplete = gestures.Drag.Matches(e.Source, e);
- if (canCancel || canComplete)
+ if (gestures.Drag.Matches(e.Source, e))
{
- // Prevent canceling if drag and cancel are bound to the same mouse action
- Canceled = !canComplete && canCancel;
-
- // Handle right click if dragging or canceled and moved the mouse more than threshold so context menus don't open
+ // Suppress the context menu if the mouse moved beyond the defined drag threshold
if (e.ChangedButton == MouseButton.Right)
{
- double contextMenuTreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;
- if ((_currentMousePosition - _initialMousePosition).LengthSquared > contextMenuTreshold)
+ double dragThreshold = NodifyEditor.HandleRightClickAfterPanningThreshold * NodifyEditor.HandleRightClickAfterPanningThreshold;
+ double dragDistance = (Editor.MouseLocation - _initialMousePosition).LengthSquared;
+
+ if (dragDistance > dragThreshold)
{
e.Handled = true;
}
}
+ PopState();
+ }
+ else if (ItemContainer.AllowDraggingCancellation && gestures.CancelAction.Matches(e.Source, e))
+ {
+ Canceled = true;
+ e.Handled = true;
+
PopState();
}
}
@@ -86,9 +81,10 @@ public override void HandleMouseUp(MouseButtonEventArgs e)
///
public override void HandleKeyUp(KeyEventArgs e)
{
- Canceled = EditorGestures.Mappings.ItemContainer.CancelAction.Matches(e.Source, e) && ItemContainer.AllowDraggingCancellation;
- if (Canceled)
+ EditorGestures.ItemContainerGestures gestures = EditorGestures.Mappings.ItemContainer;
+ if (ItemContainer.AllowDraggingCancellation && gestures.CancelAction.Matches(e.Source, e))
{
+ Canceled = true;
PopState();
}
}
diff --git a/Nodify/EditorStates/EditorDefaultState.cs b/Nodify/EditorStates/EditorDefaultState.cs
index eba8eed1..373a37eb 100644
--- a/Nodify/EditorStates/EditorDefaultState.cs
+++ b/Nodify/EditorStates/EditorDefaultState.cs
@@ -33,7 +33,7 @@ public override void HandleMouseDown(MouseButtonEventArgs e)
EditorGestures.NodifyEditorGestures gestures = EditorGestures.Mappings.Editor;
if (Editor.CanSelectMultipleItems && gestures.Selection.Select.Matches(e.Source, e))
{
- SelectionType selectionType = SelectionHelper.GetSelectionType(e);
+ SelectionType selectionType = gestures.Selection.GetSelectionType(e);
PushState(new EditorSelectingState(Editor, selectionType));
}
else if (!Editor.DisablePanning && gestures.Pan.Matches(e.Source, e))
diff --git a/Nodify/Helpers/SelectionHelper.cs b/Nodify/Helpers/SelectionHelper.cs
index 699a90fd..b6293f4e 100644
--- a/Nodify/Helpers/SelectionHelper.cs
+++ b/Nodify/Helpers/SelectionHelper.cs
@@ -167,10 +167,12 @@ private void PreviewInvertSelection(Rect area, bool fit = false)
}
}
}
+ }
- internal static SelectionType GetSelectionType(MouseButtonEventArgs e)
+ internal static class SelectionGesturesExtensions
+ {
+ public static SelectionType GetSelectionType(this EditorGestures.SelectionGestures gestures, MouseButtonEventArgs e)
{
- EditorGestures.SelectionGestures gestures = EditorGestures.Mappings.Editor.Selection;
if (gestures.Append.Matches(e.Source, e))
{
return SelectionType.Append;
diff --git a/Nodify/ItemContainer.cs b/Nodify/ItemContainer.cs
index 7b855afb..3d902db8 100644
--- a/Nodify/ItemContainer.cs
+++ b/Nodify/ItemContainer.cs
@@ -150,9 +150,6 @@ private static void OnLocationChanged(DependencyObject d, DependencyPropertyChan
#region Routed Events
- public static readonly RoutedEvent DragStartedEvent = EventManager.RegisterRoutedEvent(nameof(DragStarted), RoutingStrategy.Bubble, typeof(DragStartedEventHandler), typeof(ItemContainer));
- public static readonly RoutedEvent DragCompletedEvent = EventManager.RegisterRoutedEvent(nameof(DragCompleted), RoutingStrategy.Bubble, typeof(DragCompletedEventHandler), typeof(ItemContainer));
- public static readonly RoutedEvent DragDeltaEvent = EventManager.RegisterRoutedEvent(nameof(DragDelta), RoutingStrategy.Bubble, typeof(DragDeltaEventHandler), typeof(ItemContainer));
public static readonly RoutedEvent SelectedEvent = Selector.SelectedEvent.AddOwner(typeof(ItemContainer));
public static readonly RoutedEvent UnselectedEvent = Selector.UnselectedEvent.AddOwner(typeof(ItemContainer));
public static readonly RoutedEvent LocationChangedEvent = EventManager.RegisterRoutedEvent(nameof(LocationChanged), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ItemContainer));
@@ -166,33 +163,6 @@ public event RoutedEventHandler LocationChanged
remove => RemoveHandler(LocationChangedEvent, value);
}
- ///
- /// Occurs when this is the instigator of a drag operation.
- ///
- public event DragStartedEventHandler DragStarted
- {
- add => AddHandler(DragStartedEvent, value);
- remove => RemoveHandler(DragStartedEvent, value);
- }
-
- ///
- /// Occurs when this is being dragged.
- ///
- public event DragDeltaEventHandler DragDelta
- {
- add => AddHandler(DragDeltaEvent, value);
- remove => RemoveHandler(DragDeltaEvent, value);
- }
-
- ///
- /// Occurs when this completed the drag operation.
- ///
- public event DragCompletedEventHandler DragCompleted
- {
- add => AddHandler(DragCompletedEvent, value);
- remove => RemoveHandler(DragCompletedEvent, value);
- }
-
///
/// Occurs when this is selected.
///
@@ -335,6 +305,54 @@ public virtual bool IsSelectableInArea(Rect area, bool isContained)
return isContained ? area.Contains(bounds) : area.IntersectsWith(bounds);
}
+ ///
+ /// Replaces the selection if the container is not selected.
+ public void BeginDragging()
+ {
+ if (!IsSelected)
+ {
+ Select(SelectionType.Replace);
+ }
+
+ Editor.BeginDragging();
+ }
+
+ ///
+ public void UpdateDragging(Vector amount)
+ => Editor.UpdateDragging(amount);
+
+ ///
+ public void CancelDragging()
+ => Editor.CancelDragging();
+
+ ///
+ public void EndDragging()
+ => Editor.EndDragging();
+
+ ///
+ /// Modifies the selection state of the current item based on the specified selection type.
+ ///
+ /// The type of selection to perform.
+ public void Select(SelectionType type)
+ {
+ switch (type)
+ {
+ case SelectionType.Append:
+ IsSelected = true;
+ break;
+ case SelectionType.Remove:
+ IsSelected = false;
+ break;
+ case SelectionType.Invert:
+ IsSelected = !IsSelected;
+ break;
+ case SelectionType.Replace:
+ Editor.UnselectAll();
+ IsSelected = true;
+ break;
+ }
+ }
+
#region State Handling
private readonly Stack _states = new Stack();
@@ -399,7 +417,7 @@ protected override void OnMouseDown(MouseButtonEventArgs e)
///
protected override void OnMouseUp(MouseButtonEventArgs e)
{
- if (IsSelectableLocation(e.GetPosition(this)) || IsMouseCaptured)
+ if (IsMouseCaptured)
{
State.HandleMouseUp(e);
}
@@ -412,16 +430,12 @@ protected override void OnMouseUp(MouseButtonEventArgs e)
}
///
- protected override void OnMouseMove(MouseEventArgs e)
- {
- State.HandleMouseMove(e);
- }
+ protected override void OnMouseMove(MouseEventArgs e)
+ => State.HandleMouseMove(e);
///
- protected override void OnMouseWheel(MouseWheelEventArgs e)
- {
- State.HandleMouseWheel(e);
- }
+ protected override void OnMouseWheel(MouseWheelEventArgs e)
+ => State.HandleMouseWheel(e);
///
protected override void OnLostMouseCapture(MouseEventArgs e)
diff --git a/Nodify/NodifyEditor.Dragging.cs b/Nodify/NodifyEditor.Dragging.cs
index 38703a76..290048c2 100644
--- a/Nodify/NodifyEditor.Dragging.cs
+++ b/Nodify/NodifyEditor.Dragging.cs
@@ -1,7 +1,6 @@
using System.Windows.Input;
using System.Windows;
using System.Collections.Generic;
-using System.Windows.Controls.Primitives;
using System.Diagnostics;
namespace Nodify
@@ -74,13 +73,6 @@ public bool IsDragging
private IDraggingStrategy? _draggingStrategy;
- private void RegisterDragEvents()
- {
- AddHandler(ItemContainer.DragStartedEvent, new DragStartedEventHandler(OnItemsDragStarted));
- AddHandler(ItemContainer.DragCompletedEvent, new DragCompletedEventHandler(OnItemsDragCompleted));
- AddHandler(ItemContainer.DragDeltaEvent, new DragDeltaEventHandler(OnItemsDragDelta));
- }
-
///
/// Initiates the dragging operation using the currently selected s.
///
@@ -163,28 +155,5 @@ private IDraggingStrategy CreateDraggingStrategy(IEnumerable cont
return new DraggingSimple(containers, GridCellSize);
}
-
- private void OnItemsDragStarted(object sender, DragStartedEventArgs e)
- {
- BeginDragging();
- e.Handled = true;
- }
-
- private void OnItemsDragDelta(object sender, DragDeltaEventArgs e)
- {
- UpdateDragging(new Vector(e.HorizontalChange, e.VerticalChange));
- }
-
- private void OnItemsDragCompleted(object sender, DragCompletedEventArgs e)
- {
- if (e.Canceled)
- {
- CancelDragging();
- }
- else
- {
- EndDragging();
- }
- }
}
}
diff --git a/Nodify/NodifyEditor.cs b/Nodify/NodifyEditor.cs
index 4ace8a65..433ef79d 100644
--- a/Nodify/NodifyEditor.cs
+++ b/Nodify/NodifyEditor.cs
@@ -529,8 +529,6 @@ public NodifyEditor()
AddHandler(BaseConnection.DisconnectEvent, new ConnectionEventHandler(OnRemoveConnection));
- RegisterDragEvents();
-
var transform = new TransformGroup();
transform.Children.Add(ScaleTransform);
transform.Children.Add(TranslateTransform);