Skip to content

Commit

Permalink
Added Node **auto panning** when close to edge
Browse files Browse the repository at this point in the history
  • Loading branch information
MakesYT committed Feb 1, 2024
1 parent 97b3205 commit f643fee
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 8 deletions.
20 changes: 17 additions & 3 deletions NodifyM.Avalonia/Controls/BaseNode.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
using NodifyM.Avalonia.Events;
using NodifyM.Avalonia.Helpers;
using NodifyM.Avalonia.ViewModelBase;

Expand All @@ -14,15 +15,15 @@ public class BaseNode : ContentControl
{
public static readonly AvaloniaProperty<Point> LocationProperty =
AvaloniaProperty.Register<BaseNode, Point>(nameof(Location));
public static readonly RoutedEvent LocationChangedEvent = RoutedEvent.Register<RoutedEventArgs>(nameof(LocationChanged), RoutingStrategies.Bubble, typeof(BaseNode));
public static readonly RoutedEvent LocationChangedEvent = RoutedEvent.Register<NodeLocationEventArgs>(nameof(LocationChanged), RoutingStrategies.Bubble, typeof(BaseNode));
public static readonly AvaloniaProperty<bool> IsSelectedProperty =
AvaloniaProperty.Register<BaseNode, bool>(nameof(IsSelected));
public bool IsSelected
{
get => (bool)GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
public event EventHandler<RoutedEventArgs> LocationChanged
public event NodeLocationEventHandler LocationChanged
{
add => AddHandler(LocationChangedEvent, value);
remove => RemoveHandler(LocationChangedEvent, value);
Expand All @@ -45,8 +46,20 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_editor = this.GetParentOfType<NodifyEditor>();
_editor.NodifyAutoPanning += NodifyAutoPanningEvent;
}

private void NodifyAutoPanningEvent(object sender, NodifyAutoPanningEventArgs e)
{
if (e.Node != this)
{
return;
}
RaiseEvent(new NodeLocationEventArgs(((BaseNodeViewModel)DataContext).Location,this,LocationChangedEvent));

}


private NodifyEditor _editor;
/// <summary>
/// 记录上一次鼠标位置
Expand Down Expand Up @@ -104,6 +117,7 @@ private void OnPointerReleased(object sender, PointerReleasedEventArgs e)

// var currentPoint = e.GetCurrentPoint(this);
// Debug.WriteLine($"停止拖动坐标X:{OffsetX} Y:{OffsetY}");
RaiseEvent(new NodeLocationEventArgs(((BaseNodeViewModel)DataContext).Location,this,LocationChangedEvent,true));
}

private void OnPointerMoved(object sender, PointerEventArgs e)
Expand All @@ -126,6 +140,6 @@ private void OnPointerMoved(object sender, PointerEventArgs e)
((BaseNodeViewModel)DataContext).Location = _editor.TryAlignNode(this,
new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY));

RaiseEvent(new RoutedEventArgs(LocationChangedEvent,this));
RaiseEvent(new NodeLocationEventArgs(((BaseNodeViewModel)DataContext).Location,this,LocationChangedEvent));
}
}
106 changes: 104 additions & 2 deletions NodifyM.Avalonia/Controls/NodifyEditor.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media;
Expand Down Expand Up @@ -175,11 +176,13 @@ public NodifyEditor()
AddHandler(Connector.DisconnectEvent, new ConnectorEventHandler(OnConnectorDisconnected));
AddHandler(Connector.PendingConnectionStartedEvent, new PendingConnectionEventHandler(OnConnectionStarted));
AddHandler(Connector.PendingConnectionCompletedEvent, new PendingConnectionEventHandler(OnConnectionCompleted));

AddHandler(BaseNode.LocationChangedEvent,new NodeLocationEventHandler( OnNodeLocationChanged));
AddHandler(BaseConnection.DisconnectEvent, new ConnectionEventHandler(OnRemoveConnection));

}



protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
Expand All @@ -193,6 +196,8 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
ScaleTransform = scaleTransform;
renderTransform.Children.Add(scaleTransform);
RenderTransform = renderTransform;
AutoPanningTimer=new DispatcherTimer(TimeSpan.FromMilliseconds(10), DispatcherPriority.Normal,HandleAutoPanning);
AutoPanningTimer.Stop();
}


Expand Down Expand Up @@ -240,7 +245,7 @@ private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
isDragging = false;
e.Handled = true;
// 停止计时器

AutoPanningTimer.Stop();
// var currentPoint = e.GetCurrentPoint(this);
// Debug.WriteLine($"停止拖动坐标X:{OffsetX} Y:{OffsetY}");
}
Expand Down Expand Up @@ -538,4 +543,101 @@ public Point TryAlignNode(BaseNode control,Point point)


#endregion

#region Auto Panning

public static readonly AvaloniaProperty<bool> AllowAutoPanningProperty = AvaloniaProperty.Register<NodifyEditor, bool>(nameof(AllowAutoPanning),BoxValue.True);
public static readonly AvaloniaProperty<int> AutoPanningSpeedProperty = AvaloniaProperty.Register<NodifyEditor, int>(nameof(AutoPanningSpeed),10);
public static readonly AvaloniaProperty<double> AutoPanningXEdgeDistanceProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(AutoPanningXEdgeDistance),0.02);
public static readonly AvaloniaProperty<double> AutoPanningYEdgeDistanceProperty = AvaloniaProperty.Register<NodifyEditor, double>(nameof(AutoPanningYEdgeDistance),0.04);
public double AutoPanningXEdgeDistance
{
get => (double)GetValue(AutoPanningXEdgeDistanceProperty);
set => SetValue(AutoPanningXEdgeDistanceProperty, value);
}
public double AutoPanningYEdgeDistance
{
get => (double)GetValue(AutoPanningYEdgeDistanceProperty);
set => SetValue(AutoPanningYEdgeDistanceProperty, value);
}

public int AutoPanningSpeed
{
get => (int)GetValue(AutoPanningSpeedProperty);
set => SetValue(AutoPanningSpeedProperty, value);
}
public bool AllowAutoPanning
{
get => (bool)GetValue(AllowAutoPanningProperty);
set => SetValue(AllowAutoPanningProperty, value);
}


public static readonly RoutedEvent NodifyAutoPanningEvent = RoutedEvent.Register<NodifyAutoPanningEventArgs>(nameof(NodifyAutoPanning), RoutingStrategies.Bubble, typeof(NodifyEditor));
public event NodifyAutoPanningEventHandler NodifyAutoPanning
{
add => AddHandler(NodifyAutoPanningEvent, value);
remove => RemoveHandler(NodifyAutoPanningEvent, value);
}
DispatcherTimer AutoPanningTimer;
NodeLocationEventArgs locationChangedEventArgs;
BaseNode baseNode;
private void OnNodeLocationChanged(object? sender, NodeLocationEventArgs e)
{
if (!AllowAutoPanning)
{
return;
}
baseNode= e.Sender;
locationChangedEventArgs = e;
if (!AutoPanningTimer.IsEnabled)
{
AutoPanningTimer.Start();
}

if (e.Stop)
{
AutoPanningTimer.Stop();
}

}

private void HandleAutoPanning(object? sender, EventArgs eventArgs)
{
var offset = 10/Zoom;
if (OffsetX+locationChangedEventArgs.Location.X<Bounds.Width*AutoPanningXEdgeDistance)
{
OffsetX += offset;
ViewTranslateTransform.X = OffsetX;
((BaseNodeViewModel)baseNode.DataContext).Location += new Point(-offset,0);
locationChangedEventArgs.Location+=new Point(-offset,0);
RaiseEvent(new NodifyAutoPanningEventArgs(NodifyAutoPanningEvent, baseNode));
}else if (OffsetX+baseNode.Bounds.Width+locationChangedEventArgs.Location.X>Bounds.Width*(1-AutoPanningXEdgeDistance))
{
OffsetX -= offset;
ViewTranslateTransform.X = OffsetX;
((BaseNodeViewModel)baseNode.DataContext).Location += new Point(offset,0);
locationChangedEventArgs.Location+=new Point(offset,0);
RaiseEvent(new NodifyAutoPanningEventArgs(NodifyAutoPanningEvent, baseNode));
}else if (OffsetY+locationChangedEventArgs.Location.Y<Bounds.Height*AutoPanningYEdgeDistance)
{
OffsetY += offset;
ViewTranslateTransform.Y = OffsetY;
((BaseNodeViewModel)baseNode.DataContext).Location += new Point(0,-offset);
locationChangedEventArgs.Location+=new Point(0,-offset);
RaiseEvent(new NodifyAutoPanningEventArgs(NodifyAutoPanningEvent, baseNode));
}else if (OffsetY+baseNode.Bounds.Height+locationChangedEventArgs.Location.Y>Bounds.Height* (1-AutoPanningYEdgeDistance))
{
OffsetY -= offset;
ViewTranslateTransform.Y = OffsetY;
((BaseNodeViewModel)baseNode.DataContext).Location += new Point(0,offset);
locationChangedEventArgs.Location += new Point(0, offset);
RaiseEvent(new NodifyAutoPanningEventArgs(NodifyAutoPanningEvent, baseNode));
}
else
{
AutoPanningTimer.Stop();
}
}
#endregion
}
18 changes: 18 additions & 0 deletions NodifyM.Avalonia/Events/NodeLocationEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Avalonia;
using Avalonia.Interactivity;
using NodifyM.Avalonia.Controls;

namespace NodifyM.Avalonia.Events;
public delegate void NodeLocationEventHandler(object sender, NodeLocationEventArgs e);
public class NodeLocationEventArgs : RoutedEventArgs{
public Point Location { get; set; }
public BaseNode Sender{ get; set; }
public bool Stop{ get; set; }
public NodeLocationEventArgs(Point location, BaseNode sender,RoutedEvent routedEvent,bool stop = false)
{
Location = location;
this.Sender = sender;
this.RoutedEvent = routedEvent;
this.Stop = stop;
}
}
15 changes: 15 additions & 0 deletions NodifyM.Avalonia/Events/NodifyAutoPanningEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Avalonia;
using Avalonia.Interactivity;
using NodifyM.Avalonia.Controls;

namespace NodifyM.Avalonia.Events;
public delegate void NodifyAutoPanningEventHandler(object sender, NodifyAutoPanningEventArgs e);
public class NodifyAutoPanningEventArgs : RoutedEventArgs{
public BaseNode Node { get; set; }

public NodifyAutoPanningEventArgs(RoutedEvent? routedEvent, BaseNode node) : base(routedEvent)
{
Node = node;

}
}
2 changes: 1 addition & 1 deletion NodifyM.Avalonia/NodifyM.Avalonia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageProjectUrl>https://github.com/MakesYT/NodifyM.Avalonia</PackageProjectUrl>
<LangVersion>10</LangVersion>
<Version>1.0.6</Version>
<Version>1.0.7</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ This project is a refactoring of [Nodify](https://github.com/miroiu/nodify) on t
- Designed from the start to work with **MVVM**
- Built-in dark and light **themes**
- **Selecting****zooming****panning**
- Select, move, **_auto align_** connect nodes
- Select, move, **_auto align_**, **auto panning** when close to edge and connect nodes
### What are the differences compared to Nodify
- **Supports**
- auto align Node
- display text on Connection
- **Nonsupport**
- Select multiple nodes
- **Will be supported in the future**
- **auto panning** when close to edge
- none
## Usage
### NodifyEditor
- `Press` and `Hold` -> Move the all show items
Expand All @@ -46,6 +46,8 @@ This project is still in its early stages and lacks many events compared to **No
#### You can git clone the project and run `NodifyM.Avalonia.Example`

## Changelog
### 1.0.7
- Added Node **auto panning** when close to edge
### 1.0.6
- Fixed Node IsSelected property
- Fixed Node BorderBrush Style
Expand Down

0 comments on commit f643fee

Please sign in to comment.