|
|
|
|
using Avalonia.Data.Converters;
|
|
|
|
|
using Avalonia.Media;
|
|
|
|
|
using Avalonia;
|
|
|
|
|
using AuroraDesk.Core.Entities;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
namespace AuroraDesk.Presentation.Converters;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 节点画布相关转换器
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static class NodeCanvasConverters
|
|
|
|
|
{
|
|
|
|
|
public static readonly IValueConverter BooleanToBorderBrushConverter = new BooleanToBorderBrushConverter();
|
|
|
|
|
public static readonly IValueConverter BooleanToBorderThicknessConverter = new BooleanToBorderThicknessConverter();
|
|
|
|
|
public static readonly IValueConverter NodeToSelectionTextConverter = new NodeToSelectionTextConverter();
|
|
|
|
|
public static readonly IValueConverter IsNotNullConverter = new IsNotNullConverter();
|
|
|
|
|
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();
|
|
|
|
|
public static readonly IValueConverter ConnectorPlacementToTextConverter = new ConnectorPlacementToTextConverter();
|
|
|
|
|
public static readonly IMultiValueConverter ConnectorPlacementMarginConverter = new ConnectorPlacementMarginConverter();
|
|
|
|
|
public static readonly IValueConverter IndexToDisplayTextConverter = new IndexToDisplayTextConverter();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 布尔值到边框颜色转换器(选中时显示蓝色边框)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class BooleanToBorderBrushConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is bool isSelected && isSelected)
|
|
|
|
|
{
|
|
|
|
|
// 使用 StaticResource 的颜色,如果无法获取则使用默认蓝色
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var color = Color.Parse("#3498DB"); // PrimaryBlue
|
|
|
|
|
return new SolidColorBrush(color);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
return new SolidColorBrush(Color.FromRgb(52, 152, 219)); // #3498DB
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return new SolidColorBrush(Colors.Transparent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 布尔值到边框厚度转换器(选中时显示边框)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class BooleanToBorderThicknessConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is bool isSelected && isSelected)
|
|
|
|
|
{
|
|
|
|
|
return new Thickness(2);
|
|
|
|
|
}
|
|
|
|
|
return new Thickness(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 过滤输入连接点转换器
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class NodeToSelectionTextConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is Node node)
|
|
|
|
|
{
|
|
|
|
|
return $"已选中1个对象 (节点)";
|
|
|
|
|
}
|
|
|
|
|
return "未选中对象";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 非空转换器(用于控制可见性)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class IsNotNullConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
return value != null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 空值转换器(用于控制可见性)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class IsNullConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
return value == null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将附件圆模式枚举转换为中文描述
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ConnectorAttachmentModeToTextConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is ConnectorAttachmentMode mode)
|
|
|
|
|
{
|
|
|
|
|
return mode switch
|
|
|
|
|
{
|
|
|
|
|
ConnectorAttachmentMode.None => "不显示",
|
|
|
|
|
ConnectorAttachmentMode.LeftOnly => "仅左侧",
|
|
|
|
|
ConnectorAttachmentMode.RightOnly => "仅右侧",
|
|
|
|
|
ConnectorAttachmentMode.Both => "左右两侧",
|
|
|
|
|
_ => mode.ToString()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 过滤节点输入连接点集合
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ConnectionPointsToInputsConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is IEnumerable<ConnectionPoint> points)
|
|
|
|
|
{
|
|
|
|
|
return points
|
|
|
|
|
.Where(p => p.Type == ConnectionPointType.Input)
|
|
|
|
|
.OrderBy(p => p.Index)
|
|
|
|
|
.ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Array.Empty<ConnectionPoint>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 过滤节点输出连接点集合
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ConnectionPointsToOutputsConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is IEnumerable<ConnectionPoint> points)
|
|
|
|
|
{
|
|
|
|
|
return points
|
|
|
|
|
.Where(p => p.Type == ConnectionPointType.Output)
|
|
|
|
|
.OrderBy(p => p.Index)
|
|
|
|
|
.ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Array.Empty<ConnectionPoint>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将附件圆定位模式转换为显示文本
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ConnectorPlacementToTextConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is ConnectorPlacementMode placement)
|
|
|
|
|
{
|
|
|
|
|
return placement switch
|
|
|
|
|
{
|
|
|
|
|
ConnectorPlacementMode.Inside => "内侧",
|
|
|
|
|
ConnectorPlacementMode.Outside => "外侧",
|
|
|
|
|
_ => placement.ToString()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据附件圆定位和尺寸计算 ItemsControl 外边距
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ConnectorPlacementMarginConverter : IMultiValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (values.Count < 2)
|
|
|
|
|
{
|
|
|
|
|
return new Thickness(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (values[0] is ConnectorPlacementMode placement && values[1] is double size)
|
|
|
|
|
{
|
|
|
|
|
var side = parameter?.ToString() ?? string.Empty;
|
|
|
|
|
var offset = placement == ConnectorPlacementMode.Outside ? size / 2 : 0;
|
|
|
|
|
|
|
|
|
|
return side switch
|
|
|
|
|
{
|
|
|
|
|
"Left" => new Thickness(-offset, 0, 0, 0),
|
|
|
|
|
"Right" => new Thickness(0, 0, -offset, 0),
|
|
|
|
|
_ => new Thickness(0)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Thickness(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Double 与字符串互转转换器
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class DoubleToStringConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is double d)
|
|
|
|
|
{
|
|
|
|
|
return d.ToString("0.##", culture);
|
|
|
|
|
}
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is string s && double.TryParse(s, NumberStyles.Float, culture, out var result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return AvaloniaProperty.UnsetValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将 Hex 字符串转换为 SolidColorBrush
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ColorHexToBrushConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is string hex && !string.IsNullOrWhiteSpace(hex))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var color = Color.Parse(hex);
|
|
|
|
|
return new SolidColorBrush(color);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// ignore parse error
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return new SolidColorBrush(Colors.Transparent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将 Hex 字符串与 Avalonia.Media.Color 互转
|
|
|
|
|
/// </summary>
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将零基索引转换为显示文本(圆 1、圆 2 ...)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class IndexToDisplayTextConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is int index)
|
|
|
|
|
{
|
|
|
|
|
return $"圆 {index + 1}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|