diff --git a/NodifyM.Avalonia.Example/MainWindow.axaml b/NodifyM.Avalonia.Example/MainWindow.axaml index a2cce0b..e25253a 100644 --- a/NodifyM.Avalonia.Example/MainWindow.axaml +++ b/NodifyM.Avalonia.Example/MainWindow.axaml @@ -112,7 +112,6 @@ diff --git a/NodifyM.Avalonia/Controls/BaseNode.axaml.cs b/NodifyM.Avalonia/Controls/BaseNode.axaml.cs index 18a0784..939f9b3 100644 --- a/NodifyM.Avalonia/Controls/BaseNode.axaml.cs +++ b/NodifyM.Avalonia/Controls/BaseNode.axaml.cs @@ -13,8 +13,15 @@ namespace NodifyM.Avalonia.Controls; 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(Node)); + AvaloniaProperty.Register(nameof(Location)); + 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 EventHandler LocationChanged { add => AddHandler(LocationChangedEvent, value); @@ -63,9 +70,14 @@ private void OnPointerPressed(object sender, PointerPressedEventArgs e) foreach (var visual in parent) { visual.ZIndex = 0; - + var first = visual.GetVisualChildren().First(); + if (first is BaseNode baseNode) + { + baseNode.IsSelected = false; + } } visualParent.ZIndex = 1; + this.IsSelected = true; if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return; // 启动拖动 isDragging = true; @@ -88,7 +100,7 @@ private void OnPointerReleased(object sender, PointerReleasedEventArgs e) isDragging = false; e.Handled = true; // 停止计时器 - + _editor.ClearAlignmentLine(); // var currentPoint = e.GetCurrentPoint(this); // Debug.WriteLine($"停止拖动坐标X:{OffsetX} Y:{OffsetY}"); @@ -105,7 +117,14 @@ private void OnPointerMoved(object sender, PointerEventArgs e) var currentMousePosition = e.GetPosition(((Visual)this.GetLogicalParent()).GetVisualParent()); var offset = currentMousePosition - lastMousePosition; - ((BaseNodeViewModel)DataContext).Location = e.KeyModifiers.HasFlag(KeyModifiers.Shift) ? new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY) : _editor.TryAlignNode(this,new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY)); + if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + { + _editor.ClearAlignmentLine(); + ((BaseNodeViewModel)DataContext).Location = new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY); + } + else + ((BaseNodeViewModel)DataContext).Location = _editor.TryAlignNode(this, + new Point((offset.X + _startOffsetX), offset.Y + _startOffsetY)); RaiseEvent(new RoutedEventArgs(LocationChangedEvent,this)); } diff --git a/NodifyM.Avalonia/Controls/KnotNode.axaml b/NodifyM.Avalonia/Controls/KnotNode.axaml index 076b024..a22b753 100644 --- a/NodifyM.Avalonia/Controls/KnotNode.axaml +++ b/NodifyM.Avalonia/Controls/KnotNode.axaml @@ -35,7 +35,7 @@ BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" CornerRadius="3"> - diff --git a/NodifyM.Avalonia/Controls/Node.axaml b/NodifyM.Avalonia/Controls/Node.axaml index da10b8a..26625d6 100644 --- a/NodifyM.Avalonia/Controls/Node.axaml +++ b/NodifyM.Avalonia/Controls/Node.axaml @@ -11,6 +11,7 @@ diff --git a/NodifyM.Avalonia/Controls/Node.axaml.cs b/NodifyM.Avalonia/Controls/Node.axaml.cs index 64a9bb6..b25e08c 100644 --- a/NodifyM.Avalonia/Controls/Node.axaml.cs +++ b/NodifyM.Avalonia/Controls/Node.axaml.cs @@ -54,13 +54,7 @@ public class Node : BaseNode public static readonly AvaloniaProperty OutputProperty = AvaloniaProperty.Register(nameof(Output)); - public static readonly AvaloniaProperty IsSelectedProperty = - AvaloniaProperty.Register(nameof(IsSelected)); - public bool IsSelected - { - get => (bool)GetValue(IsSelectedProperty); - set => SetValue(IsSelectedProperty, value); - } + public Brush ContentBrush { diff --git a/NodifyM.Avalonia/Controls/NodifyEditor.axaml b/NodifyM.Avalonia/Controls/NodifyEditor.axaml index cf22391..8e9f127 100644 --- a/NodifyM.Avalonia/Controls/NodifyEditor.axaml +++ b/NodifyM.Avalonia/Controls/NodifyEditor.axaml @@ -30,8 +30,8 @@ - - + + @@ -61,7 +61,7 @@ diff --git a/NodifyM.Avalonia/Controls/NodifyEditor.axaml.cs b/NodifyM.Avalonia/Controls/NodifyEditor.axaml.cs index 025c8bd..4c17d20 100644 --- a/NodifyM.Avalonia/Controls/NodifyEditor.axaml.cs +++ b/NodifyM.Avalonia/Controls/NodifyEditor.axaml.cs @@ -15,6 +15,7 @@ using Avalonia.VisualTree; using NodifyM.Avalonia.Events; using NodifyM.Avalonia.Helpers; +using NodifyM.Avalonia.ViewModelBase; namespace NodifyM.Avalonia.Controls; @@ -355,7 +356,7 @@ private void OnRemoveConnection(object sender, ConnectionEventArgs e) public static readonly AvaloniaProperty AllowAlignProperty = AvaloniaProperty.Register(nameof(AllowAlign),BoxValue.True); public static readonly StyledProperty AlignmentLineTemplateProperty = AvaloniaProperty.Register(nameof(AlignmentLineTemplate)); - public static readonly StyledProperty> AlignmentLineProperty = AvaloniaProperty.Register>(nameof(AlignmentLine)); + public static readonly StyledProperty> AlignmentLineProperty = AvaloniaProperty.Register>(nameof(AlignmentLine),new AvaloniaList()); public AvaloniaList AlignmentLine { get => GetValue(AlignmentLineProperty); @@ -377,6 +378,10 @@ public bool AllowAlign get => (bool)GetValue(AllowAlignProperty); set => SetValue(AllowAlignProperty, value); } + public void ClearAlignmentLine() + { + AlignmentLine.Clear(); + } public Point TryAlignNode(BaseNode control,Point point) { AlignmentLine.Clear(); @@ -386,7 +391,8 @@ public Point TryAlignNode(BaseNode control,Point point) double y = (int)point.Y; double nowIntervalX = AlignmentRange; double nowIntervalY = AlignmentRange; - + var movingNodeWidth = control.Bounds.Width; + var movingNodeHeight = control.Bounds.Height; if (ItemsPanelRoot?.Children == null) return point; foreach (var child in ItemsPanelRoot?.Children) { @@ -395,76 +401,141 @@ public Point TryAlignNode(BaseNode control,Point point) { continue; } - - // 合并两个区域的代码 - var regionX = node.Location.X; - var regionY = node.Location.Y; - var controlWidth = control.Bounds.Width; - var controlHeight = control.Bounds.Height; - - // 计算左上角区域的边界 - var intervalX = Math.Abs(regionX - x); - if (intervalX < nowIntervalX) + + var nodeLocationX = node.Location.X; + var nodeLocationY = node.Location.Y; + var nodeWidth = node.Bounds.Width; + var nodeHeight = node.Bounds.Height; + + + //上->上 + var intervalY = Math.Abs(nodeLocationY - y); + if (intervalY <= nowIntervalY) { - x = regionX; - - nowIntervalX = intervalX; + y = nodeLocationY; + nowIntervalY = intervalY; + AlignmentLine.Add(x <= nodeLocationX + ? new AlignmentLineViewModel(new Point(nodeLocationX + nodeWidth, y ), new Point(control.Location.X, y )) + : new AlignmentLineViewModel(new Point(nodeLocationX, y ), + new Point(control.Location.X + movingNodeWidth, y ))); } - - var intervalX2 = Math.Abs(regionX + node.Bounds.Width - x); - if (intervalX2 < nowIntervalX) + //上->下 + var intervalY3 = Math.Abs(nodeLocationY - movingNodeHeight - y); + if (intervalY3 <= nowIntervalY) { - x = regionX + node.Bounds.Width; - nowIntervalX = intervalX2; + y = nodeLocationY - movingNodeHeight; + nowIntervalY = intervalY3; + AlignmentLine.Add(x <= nodeLocationX + ? new AlignmentLineViewModel(new Point(nodeLocationX + nodeWidth, nodeLocationY ), new Point(control.Location.X, nodeLocationY )) + : new AlignmentLineViewModel(new Point(nodeLocationX, nodeLocationY ), + new Point(control.Location.X + movingNodeWidth, nodeLocationY ))); } - - var intervalY = Math.Abs(regionY - y); - if (intervalY < nowIntervalY) + //下->下 + var intervalY4 = Math.Abs(nodeLocationY - movingNodeHeight + nodeHeight - y); + if (intervalY4 <= nowIntervalY) { - y = regionY; - nowIntervalY = intervalY; + y = nodeLocationY - movingNodeHeight + nodeHeight; + nowIntervalY = intervalY4; + AlignmentLine.Add(x <= nodeLocationX + ? new AlignmentLineViewModel(new Point(nodeLocationX + nodeWidth, y+movingNodeHeight ), new Point(control.Location.X, y+movingNodeHeight )) + : new AlignmentLineViewModel(new Point(nodeLocationX, y+movingNodeHeight ), + new Point(control.Location.X + movingNodeWidth, y+movingNodeHeight ))); } - - var intervalY2 = Math.Abs(regionY + node.Bounds.Height - y); - if (intervalY2 < nowIntervalY) + //下->上 + var intervalY2 = Math.Abs(nodeLocationY + nodeHeight - y); + if (intervalY2 <= nowIntervalY) { - y = regionY + node.Bounds.Height; + + y = nodeLocationY + nodeHeight; nowIntervalY = intervalY2; + AlignmentLine.Add(x <= nodeLocationX + ? new AlignmentLineViewModel(new Point(nodeLocationX + nodeWidth, y ), new Point(control.Location.X, y )) + : new AlignmentLineViewModel(new Point(nodeLocationX, y ), + new Point(control.Location.X + movingNodeWidth, y ))); } - - // 计算右下角区域的边界 - var intervalX3 = Math.Abs(regionX - controlWidth - x); - if (intervalX3 < nowIntervalX) + //左->右 + var intervalX3 = Math.Abs(nodeLocationX - movingNodeWidth - x); + if (intervalX3 <= nowIntervalX) { - x = regionX - controlWidth; + x = nodeLocationX - movingNodeWidth; nowIntervalX = intervalX3; + AlignmentLine.Add(y <= nodeLocationY + ? new AlignmentLineViewModel(new Point(x+movingNodeWidth, control.Location.Y), + new Point(x+movingNodeWidth, nodeLocationY+nodeHeight)) + : new AlignmentLineViewModel(new Point(x+movingNodeWidth, control.Location.Y+movingNodeHeight), + new Point(x+movingNodeWidth, nodeLocationY))); } - - var intervalX4 = Math.Abs(regionX - controlWidth + node.Bounds.Width - x); - if (intervalX4 < nowIntervalX) + + //左->左 + var intervalX = Math.Abs(nodeLocationX - x); + if (intervalX <= nowIntervalX) { - x = regionX - controlWidth + node.Bounds.Width; + x = nodeLocationX; + nowIntervalX = intervalX; + AlignmentLine.Add(y <= nodeLocationY + ? new AlignmentLineViewModel(new Point(x, control.Location.Y), + new Point(x, nodeLocationY+nodeHeight)) + : new AlignmentLineViewModel(new Point(x, control.Location.Y+movingNodeHeight), + new Point(x, nodeLocationY))); + } + //右->右 + var intervalX4 = Math.Abs(nodeLocationX - movingNodeWidth + nodeWidth - x); + if (intervalX4 <= nowIntervalX) + { + x = nodeLocationX - movingNodeWidth + nodeWidth; nowIntervalX = intervalX4; + AlignmentLine.Add(y <= nodeLocationY + ? new AlignmentLineViewModel(new Point(x+movingNodeWidth, control.Location.Y), + new Point(x+movingNodeWidth, nodeLocationY+nodeHeight)) + : new AlignmentLineViewModel(new Point(x+movingNodeWidth, control.Location.Y+movingNodeHeight), + new Point(x+movingNodeWidth, nodeLocationY))); } - var intervalY3 = Math.Abs(regionY - controlHeight - y); - if (intervalY3 < nowIntervalY) + //右->左 + var intervalX2 = Math.Abs(nodeLocationX + nodeWidth - x); + if (intervalX2 <= nowIntervalX) { - y = regionY - controlHeight; - nowIntervalY = intervalY3; + x = nodeLocationX + nodeWidth; + nowIntervalX = intervalX2; + AlignmentLine.Add(y <= nodeLocationY + ? new AlignmentLineViewModel(new Point(x, control.Location.Y), + new Point(x, nodeLocationY+nodeHeight)) + : new AlignmentLineViewModel(new Point(x, control.Location.Y+movingNodeHeight), + new Point(x, nodeLocationY))); } + + + } - var intervalY4 = Math.Abs(regionY - controlHeight + node.Bounds.Height - y); - if (intervalY4 < nowIntervalY) + for (var index = AlignmentLine.Count - 1; index >= 0; index--) + { + var o = AlignmentLine[index]; + if (o is AlignmentLineViewModel alignmentLineViewModel) { - y = regionY - controlHeight + node.Bounds.Height; - nowIntervalY = intervalY4; + if (alignmentLineViewModel.Start.X.Equals(alignmentLineViewModel.End.X)) + { + //竖向 + if (!alignmentLineViewModel.Start.X.Equals(x)&&!alignmentLineViewModel.Start.X.Equals(x+movingNodeWidth)) + { + AlignmentLine.RemoveAt(index); + } + } + + if (alignmentLineViewModel.Start.Y.Equals(alignmentLineViewModel.End.Y)) + { + //横向 + if (!alignmentLineViewModel.Start.Y.Equals(y)&&!alignmentLineViewModel.Start.Y.Equals(y+movingNodeHeight)) + { + AlignmentLine.RemoveAt(index); + } + } } } - + return new Point(x, y); } + #endregion } \ No newline at end of file diff --git a/NodifyM.Avalonia/ResourceDictionaries/Dark.axaml b/NodifyM.Avalonia/ResourceDictionaries/Dark.axaml index 339be5f..9d75aaf 100644 --- a/NodifyM.Avalonia/ResourceDictionaries/Dark.axaml +++ b/NodifyM.Avalonia/ResourceDictionaries/Dark.axaml @@ -20,7 +20,7 @@ White #1E1E1E #1E1E1E - Transparent + #2D2D30 #171717 diff --git a/NodifyM.Avalonia/ResourceDictionaries/Light.axaml b/NodifyM.Avalonia/ResourceDictionaries/Light.axaml index b6c0ad0..4f156cf 100644 --- a/NodifyM.Avalonia/ResourceDictionaries/Light.axaml +++ b/NodifyM.Avalonia/ResourceDictionaries/Light.axaml @@ -19,7 +19,7 @@ White #5C6A98 #5C6A98 - Transparent + #CBCCDF #5C6A98 diff --git a/NodifyM.Avalonia/ResourceDictionaries/Nodify.axaml b/NodifyM.Avalonia/ResourceDictionaries/Nodify.axaml index 738ad42..ea204d2 100644 --- a/NodifyM.Avalonia/ResourceDictionaries/Nodify.axaml +++ b/NodifyM.Avalonia/ResourceDictionaries/Nodify.axaml @@ -12,7 +12,7 @@ -