From 5882fc2032fa175bc2b1ba0c338cf6b3ab5b0f7f Mon Sep 17 00:00:00 2001 From: MakesYT <42534870+MakesYT@users.noreply.github.com> Date: Mon, 6 May 2024 20:03:21 +0800 Subject: [PATCH] Fix OnPointerPressed event handler --- NodifyM.Avalonia/Controls/BaseNode.axaml.cs | 134 ++++++++++--------- NodifyM.Avalonia/Controls/Connector.axaml.cs | 110 +++++++++------ NodifyM.Avalonia/NodifyM.Avalonia.csproj | 2 +- README.md | 8 ++ 4 files changed, 144 insertions(+), 110 deletions(-) diff --git a/NodifyM.Avalonia/Controls/BaseNode.axaml.cs b/NodifyM.Avalonia/Controls/BaseNode.axaml.cs index 140defd..562d75f 100644 --- a/NodifyM.Avalonia/Controls/BaseNode.axaml.cs +++ b/NodifyM.Avalonia/Controls/BaseNode.axaml.cs @@ -7,7 +7,6 @@ using Avalonia.VisualTree; using NodifyM.Avalonia.Events; using NodifyM.Avalonia.Helpers; -using NodifyM.Avalonia.ViewModelBase; namespace NodifyM.Avalonia.Controls; @@ -15,31 +14,90 @@ public class BaseNode : ContentControl { public static readonly AvaloniaProperty LocationProperty = AvaloniaProperty.Register(nameof(Location)); - public static readonly RoutedEvent LocationChangedEvent = RoutedEvent.Register(nameof(LocationChanged), RoutingStrategies.Bubble, typeof(BaseNode)); + + public static readonly RoutedEvent LocationChangedEvent = + RoutedEvent.Register(nameof(LocationChanged), RoutingStrategies.Bubble, + typeof(BaseNode)); + public static readonly AvaloniaProperty IsSelectedProperty = AvaloniaProperty.Register(nameof(IsSelected)); + public bool IsSelected { get => (bool)GetValue(IsSelectedProperty); set => SetValue(IsSelectedProperty, value); } + public event NodeLocationEventHandler LocationChanged { add => AddHandler(LocationChangedEvent, value); remove => RemoveHandler(LocationChangedEvent, value); } + public Point Location { get => (Point)GetValue(LocationProperty); set => SetValue(LocationProperty, value); } - public BaseNode() + + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + base.OnPointerReleased(e); + if (!isDragging) return; + // 停止拖动 + isDragging = false; + e.Handled = true; + // 停止计时器 + _editor.ClearAlignmentLine(); + + // var currentPoint = e.GetCurrentPoint(this); + // Debug.WriteLine($"停止拖动坐标X:{OffsetX} Y:{OffsetY}"); + RaiseEvent(new NodeLocationEventArgs(Location, this, LocationChangedEvent, true)); + } + + protected override void OnPointerPressed(PointerPressedEventArgs e) { - PointerPressed += OnPointerPressed; - PointerMoved += OnPointerMoved; - PointerReleased += OnPointerReleased; - + base.OnPointerPressed(e); + _editor.SelectItem(this); + if (!e.GetCurrentPoint(this) + .Properties.IsLeftButtonPressed) return; + e.GetCurrentPoint(this) + .Pointer.Capture(this); + // 启动拖动 + isDragging = true; + // 记录当前坐标 + var relativeTo = ((Visual)this.GetLogicalParent()).GetVisualParent(); + lastMousePosition = e.GetPosition((Visual)relativeTo); + // Debug.WriteLine($"记录当前坐标X:{lastMousePosition.X} Y:{lastMousePosition.Y}"); + _startOffsetX = Location.X; + _startOffsetY = Location.Y; + e.Handled = true; + } + + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + if (!e.GetCurrentPoint(((Visual)this.GetLogicalParent()).GetVisualParent()) + .Properties + .IsLeftButtonPressed) return; + + // 如果没有启动拖动,则不执行 + if (!isDragging) return; + + var currentMousePosition = e.GetPosition(((Visual)this.GetLogicalParent()).GetVisualParent()); + var offset = currentMousePosition - lastMousePosition; + + if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + { + _editor.ClearAlignmentLine(); + Location = new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY); + } + else + Location = _editor.TryAlignNode(this, + new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY)); + + RaiseEvent(new NodeLocationEventArgs(Location, this, LocationChangedEvent)); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -55,12 +113,13 @@ private void NodifyAutoPanningEvent(object sender, NodifyAutoPanningEventArgs e) { return; } - RaiseEvent(new NodeLocationEventArgs(Location,this,LocationChangedEvent)); + RaiseEvent(new NodeLocationEventArgs(Location, this, LocationChangedEvent)); } private NodifyEditor _editor; + /// /// 记录上一次鼠标位置 /// @@ -75,68 +134,11 @@ private void NodifyAutoPanningEvent(object sender, NodifyAutoPanningEventArgs e) private double _startOffsetX; private double _startOffsetY; - private void OnPointerPressed(object sender, PointerPressedEventArgs e) - { - e.GetCurrentPoint(this).Pointer.Capture(this); - - - _editor.SelectItem(this); - if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return; - // 启动拖动 - isDragging = true; - // 记录当前坐标 - var relativeTo = ((Visual)this.GetLogicalParent()).GetVisualParent(); - lastMousePosition = e.GetPosition((Visual)relativeTo); - - // Debug.WriteLine($"记录当前坐标X:{lastMousePosition.X} Y:{lastMousePosition.Y}"); - _startOffsetX = Location.X; - _startOffsetY = Location.Y; - e.Handled = true; - } - - private void OnPointerReleased(object sender, PointerReleasedEventArgs e) - { - - - if (!isDragging) return; - // 停止拖动 - isDragging = false; - e.Handled = true; - // 停止计时器 - _editor.ClearAlignmentLine(); - - // var currentPoint = e.GetCurrentPoint(this); - // Debug.WriteLine($"停止拖动坐标X:{OffsetX} Y:{OffsetY}"); - RaiseEvent(new NodeLocationEventArgs(Location,this,LocationChangedEvent,true)); - } protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { base.OnPointerCaptureLost(e); - RaiseEvent(new NodeLocationEventArgs(Location,this,LocationChangedEvent,true)); + RaiseEvent(new NodeLocationEventArgs(Location, this, LocationChangedEvent, true)); _editor.ClearAlignmentLine(); } - - private void OnPointerMoved(object sender, PointerEventArgs e) - { - if (!e.GetCurrentPoint(((Visual)this.GetLogicalParent()).GetVisualParent()).Properties - .IsLeftButtonPressed) return; - - // 如果没有启动拖动,则不执行 - if (!isDragging) return; - - var currentMousePosition = e.GetPosition(((Visual)this.GetLogicalParent()).GetVisualParent()); - var offset = currentMousePosition - lastMousePosition; - - if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) - { - _editor.ClearAlignmentLine(); - Location = new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY); - } - else - Location = _editor.TryAlignNode(this, - new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY)); - - RaiseEvent(new NodeLocationEventArgs(Location,this,LocationChangedEvent)); - } } \ No newline at end of file diff --git a/NodifyM.Avalonia/Controls/Connector.axaml.cs b/NodifyM.Avalonia/Controls/Connector.axaml.cs index abdd98a..8ae8566 100644 --- a/NodifyM.Avalonia/Controls/Connector.axaml.cs +++ b/NodifyM.Avalonia/Controls/Connector.axaml.cs @@ -1,16 +1,12 @@ -using System.Diagnostics; -using System.Windows.Input; +using System.Windows.Input; using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.VisualTree; using NodifyM.Avalonia.Events; using NodifyM.Avalonia.Helpers; -using NodifyM.Avalonia.ViewModelBase; namespace NodifyM.Avalonia.Controls; @@ -20,11 +16,21 @@ public class Connector : ContentControl #region Routed Events - public static readonly RoutedEvent PendingConnectionStartedEvent = RoutedEvent.Register(nameof(PendingConnectionStarted),RoutingStrategies.Bubble); - public static readonly RoutedEvent PendingConnectionCompletedEvent = RoutedEvent.Register(nameof(PendingConnectionCompleted),RoutingStrategies.Bubble); - public static readonly RoutedEvent PendingConnectionDragEvent = RoutedEvent.Register(nameof(PendingConnectionDrag),RoutingStrategies.Bubble); - public static readonly RoutedEvent DisconnectEvent = RoutedEvent.Register(nameof(Disconnect),RoutingStrategies.Bubble); - + public static readonly RoutedEvent PendingConnectionStartedEvent = + RoutedEvent.Register(nameof(PendingConnectionStarted), + RoutingStrategies.Bubble); + + public static readonly RoutedEvent PendingConnectionCompletedEvent = + RoutedEvent.Register(nameof(PendingConnectionCompleted), + RoutingStrategies.Bubble); + + public static readonly RoutedEvent PendingConnectionDragEvent = + RoutedEvent.Register(nameof(PendingConnectionDrag), + RoutingStrategies.Bubble); + + public static readonly RoutedEvent DisconnectEvent = + RoutedEvent.Register(nameof(Disconnect), RoutingStrategies.Bubble); + /// Triggered by the gesture. public event PendingConnectionEventHandler PendingConnectionStarted @@ -60,12 +66,18 @@ public event ConnectorEventHandler Disconnect #region Dependency Properties - - public static readonly StyledProperty AnchorProperty = AvaloniaProperty.Register(nameof(Anchor), BoxValue.Point); - public static readonly StyledProperty IsConnectedProperty = AvaloniaProperty.Register(nameof(Anchor), BoxValue.False); - public static readonly AvaloniaProperty DisconnectCommandProperty = AvaloniaProperty.Register(nameof(DisconnectCommand)); - public static readonly AvaloniaProperty IsPendingConnectionProperty = AvaloniaProperty.Register(nameof(IsPendingConnection), BoxValue.False); - + public static readonly StyledProperty AnchorProperty = + AvaloniaProperty.Register(nameof(Anchor), BoxValue.Point); + + public static readonly StyledProperty IsConnectedProperty = + AvaloniaProperty.Register(nameof(Anchor), BoxValue.False); + + public static readonly AvaloniaProperty DisconnectCommandProperty = + AvaloniaProperty.Register(nameof(DisconnectCommand)); + + public static readonly AvaloniaProperty IsPendingConnectionProperty = + AvaloniaProperty.Register(nameof(IsPendingConnection), BoxValue.False); + /// /// Gets the location where s can be attached to. @@ -106,6 +118,7 @@ public ICommand? DisconnectCommand } #endregion + protected internal NodifyEditor? Editor { get; private set; } protected Control? Thumb { get; private set; } protected BaseNode? Container { get; private set; } @@ -120,17 +133,21 @@ public ICommand? DisconnectCommand /// Gets the that owns this . /// private Point _lastUpdatedContainerPosition; + private Point _thumbCenter; + static Connector() { //DefaultStyleKeyProperty.OverrideMetadata(typeof(Connector), new FrameworkPropertyMetadata(typeof(Connector))); FocusableProperty.OverrideMetadata(typeof(Connector), new StyledPropertyMetadata(BoxValue.True)); } + private void OnConnectorLoaded() => TrySetAnchorUpdateEvents(true); private void OnConnectorUnloaded() => TrySetAnchorUpdateEvents(false); + private void TrySetAnchorUpdateEvents(bool value) { if (Container != null && Editor != null) @@ -142,17 +159,20 @@ private void TrySetAnchorUpdateEvents(bool value) Container.SizeChanged += OnContainerSizeChanged; } // If events are already hooked and we are asked to unsubscribe - else if ( !value) + else if (!value) { Container.LocationChanged -= OnLocationChanged; Container.SizeChanged -= OnContainerSizeChanged; } } } + private void OnContainerSizeChanged(object sender, SizeChangedEventArgs e) => UpdateAnchor(Container!.Location); + private void OnLocationChanged(object sender, RoutedEventArgs e) => UpdateAnchor(Container!.Location); + public void UpdateAnchor() { if (Container != null) @@ -160,39 +180,40 @@ public void UpdateAnchor() UpdateAnchor(Container.Location); } } + /// /// Updates the and applies optimizations if needed based on flag /// /// - protected void UpdateAnchor(Point location) { _lastUpdatedContainerPosition = location; - if ( Container != null) + if (Container != null) { Size containerMargin = (Size)Container.Bounds.Size - (Size)Container.DesiredSize; - Point relativeLocation = Thumb.TranslatePoint(new Point((Thumb.Bounds.Width - containerMargin.Width)/2, - (Thumb.Bounds.Height -containerMargin.Height)/2), Container)!.Value; + Point relativeLocation = Thumb.TranslatePoint(new Point((Thumb.Bounds.Width - containerMargin.Width) / 2, + (Thumb.Bounds.Height - containerMargin.Height) / 2), Container)!.Value; Anchor = new Point(location.X + relativeLocation.X, location.Y + relativeLocation.Y); } } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); Container = this.GetParentOfType(); - - Editor = this.GetParentOfType(); + + Editor = this.GetParentOfType(); Thumb = this.GetChildOfType("PART_Connector"); - } - + protected override void OnPointerPressed(PointerPressedEventArgs e) { - e.GetCurrentPoint(this).Pointer.Capture(this); base.OnPointerPressed(e); + e.GetCurrentPoint(this) + .Pointer.Capture(this); e.Handled = true; var currentPoint = e.GetCurrentPoint(this); - if (currentPoint.Properties.IsLeftButtonPressed&& e.KeyModifiers.HasFlag(KeyModifiers.Alt)) + if (currentPoint.Properties.IsLeftButtonPressed && e.KeyModifiers.HasFlag(KeyModifiers.Alt)) { OnDisconnect(); } @@ -205,21 +226,20 @@ protected override void OnPointerPressed(PointerPressedEventArgs e) else { Editor.SelectItem(this.GetParentOfType()); - + UpdateAnchor(); OnConnectorDragStarted(); } } } - protected override void OnPointerMoved(PointerEventArgs e) { base.OnPointerMoved(e); if (IsPendingConnection) { - Vector offset = e.GetPosition(Thumb)-_thumbCenter; + Vector offset = e.GetPosition(Thumb) - _thumbCenter; OnConnectorDrag(offset); } } @@ -227,25 +247,27 @@ protected override void OnPointerMoved(PointerEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e) { base.OnPointerReleased(e); - Vector offset = e.GetPosition(Thumb)-_thumbCenter; + Vector offset = e.GetPosition(Thumb) - _thumbCenter; var point = new Point(Anchor.X + offset.X, Anchor.Y + offset.Y); var currentPoint = e.GetCurrentPoint(this); e.Handled = EnableStickyConnections && IsPendingConnection; - if (!EnableStickyConnections&&e.InitialPressMouseButton.HasFlag( MouseButton.Left)) + if (!EnableStickyConnections && e.InitialPressMouseButton.HasFlag(MouseButton.Left)) { OnConnectorDragCompleted(point); e.Handled = true; } - else if (AllowPendingConnectionCancellation && IsPendingConnection&&(currentPoint.Properties.IsRightButtonPressed)) + else if (AllowPendingConnectionCancellation && IsPendingConnection && + (currentPoint.Properties.IsRightButtonPressed)) { // Cancel pending connection - OnConnectorDragCompleted(point,true); - // ReleaseMouseCapture(); + OnConnectorDragCompleted(point, true); + // ReleaseMouseCapture(); // Don't show context menu e.Handled = true; } } + protected override void OnKeyUp(KeyEventArgs e) { if (AllowPendingConnectionCancellation && e.Key.HasFlag(global::Avalonia.Input.Key.Escape)) @@ -254,6 +276,7 @@ protected override void OnKeyUp(KeyEventArgs e) OnConnectorDragCompleted(cancel: true); } } + protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { base.OnPointerCaptureLost(e); @@ -262,7 +285,6 @@ protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) protected virtual void OnDisconnect() { - if (IsConnected && !IsPendingConnection) { object? connector = DataContext; @@ -282,6 +304,7 @@ protected virtual void OnDisconnect() } } } + protected virtual void OnConnectorDrag(Vector offset) { var args = new PendingConnectionEventArgs(DataContext) @@ -302,6 +325,7 @@ protected virtual void OnConnectorDragStarted() { _thumbCenter = new Point(Thumb.Bounds.Width / 2, Thumb.Bounds.Height / 2); } + var args = new PendingConnectionEventArgs(DataContext) { RoutedEvent = PendingConnectionStartedEvent, @@ -311,15 +335,16 @@ protected virtual void OnConnectorDragStarted() RaiseEvent(args); IsPendingConnection = !args.Canceled; - - } - protected virtual void OnConnectorDragCompleted(Point? point=null,bool cancel = false) + protected virtual void OnConnectorDragCompleted(Point? point = null, bool cancel = false) { if (IsPendingConnection) { - Control? elem = (Editor != null&&point.HasValue) ? PendingConnection.GetPotentialConnector(Editor, PendingConnection.GetAllowOnlyConnectorsAttached(Editor),point.Value) : null; + Control? elem = (Editor != null && point.HasValue) + ? PendingConnection.GetPotentialConnector(Editor, + PendingConnection.GetAllowOnlyConnectorsAttached(Editor), point.Value) + : null; var args = new PendingConnectionEventArgs(DataContext) { @@ -340,12 +365,11 @@ protected override void OnLoaded(RoutedEventArgs e) base.OnLoaded(e); UpdateAnchor(); OnConnectorLoaded(); - } protected override void OnUnloaded(RoutedEventArgs e) { base.OnUnloaded(e); - OnConnectorUnloaded(); + OnConnectorUnloaded(); } } \ No newline at end of file diff --git a/NodifyM.Avalonia/NodifyM.Avalonia.csproj b/NodifyM.Avalonia/NodifyM.Avalonia.csproj index 44ce1c7..247dbea 100644 --- a/NodifyM.Avalonia/NodifyM.Avalonia.csproj +++ b/NodifyM.Avalonia/NodifyM.Avalonia.csproj @@ -8,7 +8,7 @@ true https://github.com/MakesYT/NodifyM.Avalonia 10 - 1.0.14 + 1.0.15 A collection of controls for node based editors designed for MVVM. diff --git a/README.md b/README.md index e31be8d..a03ff3d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,14 @@ This project is a refactoring of [Nodify](https://github.com/miroiu/nodify) on t #### You can git clone the project and run `NodifyM.Avalonia.Example` ## Changelog + +### 1.0.15 + +- Fix OnPointerPressed event handler + +### 1.0.14 + +- Remove unnecessary packages ### 1.0.13 - Fixed SelectedNode Property - Added the ability to select and drag the Node corresponding to the Connector