272 lines
9.9 KiB
C#
272 lines
9.9 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Globalization;
|
||
using System.Runtime.CompilerServices;
|
||
using System.Windows;
|
||
using System.Windows.Data;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using FontAwesome.Sharp;
|
||
using Color = System.Windows.Media.Color;
|
||
|
||
namespace Pf2e.WPF.EntityViewer.EntityViewStructure;
|
||
|
||
/// <summary>
|
||
/// Строка структуры, представляющая поле сущности.
|
||
/// </summary>
|
||
internal class StructureRow : INotifyPropertyChanged
|
||
{
|
||
/// <summary>
|
||
/// Отображаемая строка поля сущности.
|
||
/// </summary>
|
||
public StructureField Field { get; }
|
||
|
||
/// <summary>
|
||
/// Список типов лестницы.
|
||
/// </summary>
|
||
public List<LadderType> Ladder { get; set; } = [];
|
||
|
||
/// <summary>
|
||
/// Флаг наличия кнопки.
|
||
/// </summary>
|
||
public bool HasButton =>
|
||
Field.FieldType == StructureFieldType.Complex && ((StructureFieldComplex)Field).Collapsable;
|
||
|
||
/// <summary>
|
||
/// Флаг, указывающий, отображается ли строка.
|
||
/// </summary>
|
||
public bool Visible => Field.Visible;
|
||
|
||
/// <summary>
|
||
/// Флаг, указывающий, отображается ли строка.
|
||
/// </summary>
|
||
public Visibility ItemVisibility => Visible ? Visibility.Visible : Visibility.Collapsed;
|
||
|
||
/// <summary>
|
||
/// Флаг, указывающий, отображается ли строка.
|
||
/// </summary>
|
||
public Visibility ButtonVisibility => HasButton ? Visibility.Visible : Visibility.Collapsed;
|
||
|
||
/// <summary>
|
||
/// Иконка кнопки.
|
||
/// </summary>
|
||
public string ButtonIcon =>
|
||
HasButton && !((StructureFieldComplex)Field).Collapsed
|
||
? IconChar.MinusSquare.ToChar()
|
||
: IconChar.PlusSquare.ToChar();
|
||
|
||
/// <summary>
|
||
/// Глубина отображения поля.
|
||
/// </summary>
|
||
public int Depth => Field.Depth;
|
||
|
||
/// <summary>
|
||
/// Строковое представление названия поля.
|
||
/// </summary>
|
||
public string Name => Field.Name;
|
||
|
||
/// <summary>
|
||
/// Строковое представление типа поля.
|
||
/// </summary>
|
||
public string Type
|
||
{
|
||
get
|
||
{
|
||
string result = Field.GetTypeString();
|
||
if (result != "")
|
||
{
|
||
result = $@"({result})";
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Строковое представление значения поля.
|
||
/// </summary>
|
||
public string Value => Field.GetValueString().Replace("\r", "").Replace("\n", "");
|
||
|
||
/// <summary>
|
||
/// Цвет значения
|
||
/// </summary>
|
||
public Color? AccentColor =>
|
||
Field switch
|
||
{
|
||
StructureFieldPrimitive primitive => System.Type.GetTypeCode(primitive.Type) switch
|
||
{
|
||
TypeCode.Boolean => Colors.DarkGoldenrod,
|
||
TypeCode.Int32 => Colors.Blue,
|
||
TypeCode.Double => Colors.Aqua,
|
||
TypeCode.String => Colors.Green,
|
||
_ => null,
|
||
},
|
||
StructureFieldEnum => Colors.Red,
|
||
StructureFieldNull => Colors.Transparent,
|
||
_ => null,
|
||
};
|
||
|
||
/// <summary>
|
||
/// Команда переключения.
|
||
/// </summary>
|
||
public ICommand CollapseCommand { get; }
|
||
|
||
/// <summary>
|
||
/// Кониструктор класса <see cref="StructureRow"/>.
|
||
/// </summary>
|
||
/// <param name="field">Поле.</param>
|
||
public StructureRow(StructureField field)
|
||
{
|
||
Field = field;
|
||
CollapseCommand = new CollapseCommand(this);
|
||
|
||
Field.PropertyChanged += Field_PropertyChanged;
|
||
}
|
||
|
||
private void Field_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||
{
|
||
OnPropertyChanged(nameof(Field));
|
||
OnPropertyChanged(nameof(Visible));
|
||
OnPropertyChanged(nameof(ItemVisibility));
|
||
OnPropertyChanged(nameof(ButtonVisibility));
|
||
OnPropertyChanged(nameof(ButtonIcon));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Событие изменения поля.
|
||
/// </summary>
|
||
public event Action<StructureRow, StructureField>? OnChange;
|
||
|
||
/// <summary>
|
||
/// Изменение поля.
|
||
/// </summary>
|
||
public event PropertyChangedEventHandler? PropertyChanged;
|
||
|
||
/// <summary>
|
||
/// При изменении поля.
|
||
/// </summary>
|
||
/// <param name="propertyName">Название поля.</param>
|
||
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||
{
|
||
PropertyChanged?.Invoke(this, new(propertyName));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Команда для сворачивания/разворачивания поля структуры.
|
||
/// </summary>
|
||
/// <param name="structureRow">Строка структуры, содержащая поле.</param>
|
||
internal class CollapseCommand(StructureRow structureRow) : ICommand
|
||
{
|
||
/// <summary>
|
||
/// Строка структуры, содержащая поле, для которого выполняется команда.
|
||
/// </summary>
|
||
private StructureRow StructureRow { get; } = structureRow;
|
||
|
||
/// <summary>
|
||
/// Событие, вызываемое при изменении возможности выполнения команды.
|
||
/// </summary>
|
||
public event EventHandler? CanExecuteChanged;
|
||
|
||
/// <summary>
|
||
/// Проверяет, может ли команда быть выполнена с указанным параметром.
|
||
/// </summary>
|
||
/// <param name="parameter">Параметр, передаваемый команде.</param>
|
||
/// <returns><c>true</c>, если команда может быть выполнена; в противном случае <c>false</c>.</returns>
|
||
public bool CanExecute(object? parameter)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Выполняет команду с указанным параметром.
|
||
/// </summary>
|
||
/// <param name="parameter">Параметр, передаваемый команде.</param>
|
||
public void Execute(object? parameter)
|
||
{
|
||
StructureFieldComplex field = (StructureFieldComplex)StructureRow.Field;
|
||
field.Collapsed = !field.Collapsed;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Преобразователь, который адаптирует цвет в зависимости от типа целевого свойства.
|
||
/// </summary>
|
||
public class ColorAdapterConverter : IMultiValueConverter
|
||
{
|
||
/// <summary>
|
||
/// Смешивает два компнента цвета с заданным коэффициентом смешивания.
|
||
/// </summary>
|
||
/// <param name="a">Первый компонент цвета.</param>
|
||
/// <param name="b">Второй компонент цвета.</param>
|
||
/// <param name="rate">Коэффициент смешивания, где 0.0 означает полный компонент <paramref name="a"/>, а 1.0 означает полный компонент <paramref name="b"/>.</param>
|
||
/// <returns>Смешанный компонент цвета.</returns>
|
||
private byte Mix(byte a, byte b, double rate) =>
|
||
System.Convert.ToByte(Math.Round(a * rate + b * (1 - rate)));
|
||
|
||
/// <summary>
|
||
/// Преобразует массив значений в целевой тип, усредняя цвета.
|
||
/// </summary>
|
||
/// <param name="value">Массив значений, содержащий цвета.</param>
|
||
/// <param name="targetType">Целевой тип, в который нужно преобразовать значения.</param>
|
||
/// <param name="parameter">Параметр преобразования, не используется.</param>
|
||
/// <param name="culture">Культура, используемая для преобразования, не используется.</param>
|
||
/// <returns>Результат преобразования в целевой тип, который может быть <c>null</c>, если преобразование невозможно.</returns>
|
||
public object? Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
|
||
{
|
||
Color textColor = SystemColors.WindowTextColor;
|
||
double a = textColor.A;
|
||
double r = textColor.R;
|
||
double g = textColor.G;
|
||
double b = textColor.B;
|
||
int n = 1 + value.Length;
|
||
|
||
foreach (object item in value)
|
||
{
|
||
if (item is not Color color)
|
||
{
|
||
n--;
|
||
continue;
|
||
}
|
||
|
||
a += color.A;
|
||
r += color.R;
|
||
g += color.G;
|
||
b += color.B;
|
||
}
|
||
|
||
Color resultColor = Color.FromArgb(
|
||
System.Convert.ToByte(Math.Round(a / n)),
|
||
System.Convert.ToByte(Math.Round(r / n)),
|
||
System.Convert.ToByte(Math.Round(g / n)),
|
||
System.Convert.ToByte(Math.Round(b / n))
|
||
);
|
||
|
||
Dictionary<Type, Func<object>> @switch = new()
|
||
{
|
||
{ typeof(Brush), () => new SolidColorBrush(resultColor) },
|
||
};
|
||
|
||
return @switch.TryGetValue(targetType, out Func<object>? action) ? action() : null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Преобразует значение обратно в массив типов, но не реализован.
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
/// <param name="targetType"></param>
|
||
/// <param name="parameter"></param>
|
||
/// <param name="culture"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
public object[] ConvertBack(
|
||
object value,
|
||
Type[] targetType,
|
||
object parameter,
|
||
CultureInfo culture
|
||
)
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
}
|