diff --git a/AuroraDesk.Core/Entities/ConnectionPoint.cs b/AuroraDesk.Core/Entities/ConnectionPoint.cs index afaf5ed..11c03b4 100644 --- a/AuroraDesk.Core/Entities/ConnectionPoint.cs +++ b/AuroraDesk.Core/Entities/ConnectionPoint.cs @@ -17,7 +17,7 @@ public class ConnectionPoint : ReactiveObject private bool _isConnected; private double _diameter = 10; private string _color = "#3498DB"; - private ConnectorPlacementMode _placement = ConnectorPlacementMode.Inside; + private ConnectorPlacementMode _placement = ConnectorPlacementMode.Outside; /// /// 连接点唯一标识符 diff --git a/AuroraDesk.Core/Entities/Node.cs b/AuroraDesk.Core/Entities/Node.cs index 7e8203f..8f32301 100644 --- a/AuroraDesk.Core/Entities/Node.cs +++ b/AuroraDesk.Core/Entities/Node.cs @@ -28,8 +28,8 @@ public class Node : ReactiveObject private double _rightConnectorSize = 10; private string _leftConnectorColor = "#3498DB"; private string _rightConnectorColor = "#FF6B6B"; - private ConnectorPlacementMode _leftConnectorPlacement = ConnectorPlacementMode.Inside; - private ConnectorPlacementMode _rightConnectorPlacement = ConnectorPlacementMode.Inside; + private ConnectorPlacementMode _leftConnectorPlacement = ConnectorPlacementMode.Outside; + private ConnectorPlacementMode _rightConnectorPlacement = ConnectorPlacementMode.Outside; /// /// 构造函数 diff --git a/AuroraDesk.Core/Entities/NodeTemplate.cs b/AuroraDesk.Core/Entities/NodeTemplate.cs index 6e5d18c..df6f1b1 100644 --- a/AuroraDesk.Core/Entities/NodeTemplate.cs +++ b/AuroraDesk.Core/Entities/NodeTemplate.cs @@ -18,8 +18,8 @@ public class NodeTemplate : ReactiveObject private double _height = 80; private double _leftConnectorSize = 10; private double _rightConnectorSize = 10; - private ConnectorPlacementMode _leftConnectorPlacement = ConnectorPlacementMode.Inside; - private ConnectorPlacementMode _rightConnectorPlacement = ConnectorPlacementMode.Inside; + private ConnectorPlacementMode _leftConnectorPlacement = ConnectorPlacementMode.Outside; + private ConnectorPlacementMode _rightConnectorPlacement = ConnectorPlacementMode.Outside; private string _leftConnectorColor = "#3498DB"; private string _rightConnectorColor = "#FF6B6B"; diff --git a/AuroraDesk.Presentation/AuroraDesk.Presentation.csproj b/AuroraDesk.Presentation/AuroraDesk.Presentation.csproj index 559d7eb..3cbe029 100644 --- a/AuroraDesk.Presentation/AuroraDesk.Presentation.csproj +++ b/AuroraDesk.Presentation/AuroraDesk.Presentation.csproj @@ -8,6 +8,7 @@ + diff --git a/AuroraDesk.Presentation/Controls/ConnectorColumnPanel.cs b/AuroraDesk.Presentation/Controls/ConnectorColumnPanel.cs index 7dbf64d..f0e0b4e 100644 --- a/AuroraDesk.Presentation/Controls/ConnectorColumnPanel.cs +++ b/AuroraDesk.Presentation/Controls/ConnectorColumnPanel.cs @@ -9,6 +9,15 @@ namespace AuroraDesk.Presentation.Controls; /// public class ConnectorColumnPanel : Panel { + public static readonly StyledProperty SideProperty = + AvaloniaProperty.Register(nameof(Side), ConnectorColumnSide.Center); + + public ConnectorColumnSide Side + { + get => GetValue(SideProperty); + set => SetValue(SideProperty, value); + } + protected override Size MeasureOverride(Size availableSize) { var maxWidth = 0d; @@ -18,8 +27,13 @@ public class ConnectorColumnPanel : Panel { child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var desired = child.DesiredSize; - maxWidth = Math.Max(maxWidth, desired.Width); - totalHeight += desired.Height; + var margin = (child as Control)?.Margin ?? new Thickness(0); + + var horizontalMargin = Math.Abs(margin.Left) + Math.Abs(margin.Right); + var verticalMargin = Math.Max(0, margin.Top) + Math.Max(0, margin.Bottom); + + maxWidth = Math.Max(maxWidth, desired.Width + horizontalMargin); + totalHeight += desired.Height + verticalMargin; } var height = double.IsInfinity(availableSize.Height) ? totalHeight : availableSize.Height; @@ -37,7 +51,8 @@ public class ConnectorColumnPanel : Panel var totalHeight = 0d; foreach (var child in Children) { - totalHeight += child.DesiredSize.Height; + var margin = (child as Control)?.Margin ?? new Thickness(0); + totalHeight += child.DesiredSize.Height + Math.Max(0, margin.Top) + Math.Max(0, margin.Bottom); } var availableHeight = finalSize.Height; @@ -49,13 +64,29 @@ public class ConnectorColumnPanel : Panel foreach (var child in Children) { var childHeight = child.DesiredSize.Height; - var childWidth = Math.Min(child.DesiredSize.Width, finalSize.Width); - var x = (finalSize.Width - childWidth) / 2; - child.Arrange(new Rect(x, currentY, childWidth, childHeight)); - currentY += childHeight + spacing; + var childWidth = child.DesiredSize.Width; + var margin = (child as Control)?.Margin ?? new Thickness(0); + + double x = Side switch + { + ConnectorColumnSide.Left => margin.Left, + ConnectorColumnSide.Right => finalSize.Width - childWidth - margin.Right, + _ => (finalSize.Width - childWidth) / 2 + (margin.Left - margin.Right) / 2 + }; + + var y = currentY + Math.Max(0, margin.Top); + child.Arrange(new Rect(x, y, childWidth, childHeight)); + currentY += Math.Max(0, margin.Top) + childHeight + Math.Max(0, margin.Bottom) + spacing; } return finalSize; } } +public enum ConnectorColumnSide +{ + Center, + Left, + Right +} + diff --git a/AuroraDesk.Presentation/Converters/NodeCanvasConverters.cs b/AuroraDesk.Presentation/Converters/NodeCanvasConverters.cs index 3d57ebd..0abba63 100644 --- a/AuroraDesk.Presentation/Converters/NodeCanvasConverters.cs +++ b/AuroraDesk.Presentation/Converters/NodeCanvasConverters.cs @@ -21,6 +21,7 @@ public static class NodeCanvasConverters public static readonly IValueConverter IsNullConverter = new IsNullConverter(); public static readonly IValueConverter DoubleToStringConverter = new DoubleToStringConverter(); public static readonly IValueConverter ColorHexToBrushConverter = new ColorHexToBrushConverter(); + public static readonly IValueConverter ColorHexToColorConverter = new ColorHexToColorConverter(); public static readonly IValueConverter ConnectorAttachmentModeToTextConverter = new ConnectorAttachmentModeToTextConverter(); public static readonly IValueConverter ConnectionPointsToInputsConverter = new ConnectionPointsToInputsConverter(); public static readonly IValueConverter ConnectionPointsToOutputsConverter = new ConnectionPointsToOutputsConverter(); @@ -318,6 +319,39 @@ public class ColorHexToBrushConverter : IValueConverter } } +/// +/// 将 Hex 字符串与 Avalonia.Media.Color 互转 +/// +public class ColorHexToColorConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is string hex && !string.IsNullOrWhiteSpace(hex)) + { + try + { + return Color.Parse(hex); + } + catch + { + // ignore parse error + } + } + + return Colors.Transparent; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is Color color) + { + return $"#{color.R:X2}{color.G:X2}{color.B:X2}"; + } + + return AvaloniaProperty.UnsetValue; + } +} + /// /// 将零基索引转换为显示文本(圆 1、圆 2 ...) /// diff --git a/AuroraDesk.Presentation/ViewModels/Pages/NodeCanvasPageViewModel.cs b/AuroraDesk.Presentation/ViewModels/Pages/NodeCanvasPageViewModel.cs index 2595cfe..a10c283 100644 --- a/AuroraDesk.Presentation/ViewModels/Pages/NodeCanvasPageViewModel.cs +++ b/AuroraDesk.Presentation/ViewModels/Pages/NodeCanvasPageViewModel.cs @@ -315,8 +315,8 @@ public class NodeCanvasPageViewModel : RoutableViewModel RightConnectorSize = DefaultConnectorSize, LeftConnectorColor = "#2D9CDB", RightConnectorColor = "#EB5757", - LeftConnectorPlacement = ConnectorPlacementMode.Inside, - RightConnectorPlacement = ConnectorPlacementMode.Inside + LeftConnectorPlacement = ConnectorPlacementMode.Outside, + RightConnectorPlacement = ConnectorPlacementMode.Outside }); NodeTemplates.Add(new NodeTemplate @@ -333,8 +333,8 @@ public class NodeCanvasPageViewModel : RoutableViewModel RightConnectorSize = DefaultConnectorSize, LeftConnectorColor = "#2D9CDB", RightConnectorColor = "#EB5757", - LeftConnectorPlacement = ConnectorPlacementMode.Inside, - RightConnectorPlacement = ConnectorPlacementMode.Inside + LeftConnectorPlacement = ConnectorPlacementMode.Outside, + RightConnectorPlacement = ConnectorPlacementMode.Outside }); } @@ -359,8 +359,8 @@ public class NodeCanvasPageViewModel : RoutableViewModel RightConnectorSize = DefaultConnectorSize, LeftConnectorColor = "#2D9CDB", RightConnectorColor = "#EB5757", - LeftConnectorPlacement = ConnectorPlacementMode.Inside, - RightConnectorPlacement = ConnectorPlacementMode.Inside + LeftConnectorPlacement = ConnectorPlacementMode.Outside, + RightConnectorPlacement = ConnectorPlacementMode.Outside }; // 默认提供输入和输出连接点 diff --git a/AuroraDesk.Presentation/Views/Pages/NodeCanvasPageView.axaml b/AuroraDesk.Presentation/Views/Pages/NodeCanvasPageView.axaml index e80ac5f..cc0265e 100644 --- a/AuroraDesk.Presentation/Views/Pages/NodeCanvasPageView.axaml +++ b/AuroraDesk.Presentation/Views/Pages/NodeCanvasPageView.axaml @@ -5,9 +5,10 @@ xmlns:vm="using:AuroraDesk.Presentation.ViewModels.Pages" xmlns:reactive="using:ReactiveUI.Avalonia" xmlns:entities="using:AuroraDesk.Core.Entities" - xmlns:converters="using:AuroraDesk.Presentation.Converters" - xmlns:heroicons="clr-namespace:HeroIconsAvalonia.Controls;assembly=HeroIconsAvalonia" + xmlns:converters="using:AuroraDesk.Presentation.Converters" + xmlns:heroicons="clr-namespace:HeroIconsAvalonia.Controls;assembly=HeroIconsAvalonia" xmlns:controls="clr-namespace:AuroraDesk.Presentation.Controls" + xmlns:cp="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls.ColorPicker" mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800" x:Class="AuroraDesk.Presentation.Views.Pages.NodeCanvasPageView" x:DataType="vm:NodeCanvasPageViewModel" @@ -21,6 +22,7 @@ + @@ -338,6 +340,7 @@ BorderBrush="{Binding IsSelected, Converter={StaticResource BooleanToBorderBrushConverter}}" BorderThickness="{Binding IsSelected, Converter={StaticResource BooleanToBorderThicknessConverter}}" BoxShadow="0 2 8 0 #00000015" + ClipToBounds="False" Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"> - + @@ -399,7 +403,8 @@ VerticalAlignment="Center"> - + @@ -477,6 +482,15 @@ +