Files
AtomUI/src/AtomUI.Desktop.Controls.ColorPicker/GradientColorPicker.cs
T

348 lines
13 KiB
C#

using System.Reactive.Disposables;
using AtomUI.Desktop.Controls.Themes;
using AtomUI.Data;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.VisualTree;
namespace AtomUI.Desktop.Controls;
using AvaloniaTextBlock = Avalonia.Controls.TextBlock;
public class GradientColorPicker : AbstractColorPicker
{
#region 公共属性定义
public static readonly StyledProperty<LinearGradientBrush?> DefaultValueProperty =
GradientColorPickerView.DefaultValueProperty.AddOwner<GradientColorPicker>();
public static readonly StyledProperty<LinearGradientBrush?> ValueProperty =
GradientColorPickerView.ValueProperty.AddOwner<GradientColorPicker>();
public static readonly AttachedProperty<Action<LinearGradientBrush?, ColorFormat, Avalonia.Controls.Controls>?> ColorTextFormatterProperty =
AvaloniaProperty.RegisterAttached<GradientColorPicker, Control, Action<LinearGradientBrush?, ColorFormat, Avalonia.Controls.Controls>?>("ColorTextFormatter");
public LinearGradientBrush? DefaultValue
{
get => GetValue(DefaultValueProperty);
set => SetValue(DefaultValueProperty, value);
}
public LinearGradientBrush? Value
{
get => GetValue(ValueProperty);
private set => SetValue(ValueProperty, value);
}
public static Action<LinearGradientBrush?, ColorFormat, Avalonia.Controls.Controls>? GetColorTextFormatter(GradientColorPicker colorPicker)
{
return colorPicker.GetValue(ColorTextFormatterProperty);
}
public static void SetPresetColor(GradientColorPicker colorPicker, Func<LinearGradientBrush?, ColorFormat, Avalonia.Controls.Controls> formatter)
{
colorPicker.SetValue(ColorTextFormatterProperty, formatter);
}
#endregion
#region 公共事件定义
/// <summary>
/// Keep distributing as long as there are changes
/// </summary>
public event EventHandler<GradientColorChangedEventArgs>? GradientValueChanged;
/// <summary>
/// Dispatched once when Flyout is closed
/// </summary>
public event EventHandler<GradientColorSelectedEventArgs>? ValueSelected;
#endregion
#region 内部事件定义
internal static readonly StyledProperty<IBrush?> InactiveStopTextColorProperty =
AvaloniaProperty.Register<GradientColorPicker, IBrush?>(nameof(InactiveStopTextColor));
internal IBrush? InactiveStopTextColor
{
get => GetValue(InactiveStopTextColorProperty);
set => SetValue(InactiveStopTextColorProperty, value);
}
#endregion
#region 内部属性定义
internal static readonly DirectProperty<GradientColorPicker, int?> ActivatedStopIndexProperty =
AvaloniaProperty.RegisterDirect<GradientColorPicker, int?>(
nameof(ActivatedStopIndex),
o => o.ActivatedStopIndex,
(o, v) => o.ActivatedStopIndex = v);
private int? _activatedStopIndex;
internal int? ActivatedStopIndex
{
get => _activatedStopIndex;
set => SetAndRaise(ActivatedStopIndexProperty, ref _activatedStopIndex, value);
}
#endregion
private GradientColorPickerView? _presenter;
private CompositeDisposable? _flyoutBindingDisposables;
private WrapPanel? _textPanel;
private IDisposable? _activeStopIndexChangedDisposable;
private int? _latestActivatedStopIndex;
private IDisposable? _emptyTextBindingDisposable;
private ColorBlock? _colorIndicator;
static GradientColorPicker()
{
AffectsMeasure<GradientColorPicker>(ColorTextFormatterProperty);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ValueProperty)
{
NotifyValueChanged(new GradientColorChangedEventArgs(change.GetOldValue<LinearGradientBrush?>(),
change.GetNewValue<LinearGradientBrush?>()));
}
if (change.Property == ColorTextFormatterProperty)
{
GenerateValueText();
}
if (this.IsAttachedToVisualTree())
{
if (change.Property == DefaultValueProperty)
{
Value ??= DefaultValue;
}
else if (change.Property == ValueProperty)
{
if (Value != null)
{
Value.StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative);
Value.EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative);
}
GenerateValueText();
GenerateColorBlockBackground();
}
if (change.Property == ActivatedStopIndexProperty)
{
HandleGradientActiveStopChanged(ActivatedStopIndex);
}
}
}
protected override void GenerateValueText()
{
if (_textPanel == null || !IsShowText)
{
return;
}
var textRunCount = _textPanel.Children.Count;
if (Value == null || Value.GradientStops.Count == 0)
{
_textPanel.Children.Clear();
}
else
{
var stops = Value.GradientStops;
var stopsCount = stops.Count;
if (stopsCount < textRunCount)
{
// 删除多于的
var delta = textRunCount - stopsCount;
for (var i = 0; i < delta; i++)
{
_textPanel.Children.Remove(_textPanel.Children.Last());
}
}
else if (stopsCount > textRunCount)
{
var delta = stopsCount - textRunCount;
for (var i = 0; i < delta; i++)
{
var textBlock = new AvaloniaTextBlock();
_textPanel.Children.Add(textBlock);
}
}
}
textRunCount = _textPanel.Children.Count;
if (Value == null || Value.GradientStops.Count == 0)
{
var emptyTextBlock = new AvaloniaTextBlock();
_emptyTextBindingDisposable?.Dispose();
_emptyTextBindingDisposable = BindUtils.RelayBind(this, EmptyColorTextProperty, emptyTextBlock, AvaloniaTextBlock.TextProperty);
_textPanel.Children.Add(emptyTextBlock);
}
else
{
var customFormatter = GetColorTextFormatter(this);
if (customFormatter != null)
{
customFormatter(Value, Format, _textPanel.Children);
}
else
{
for (var i = 0; i < textRunCount; i++)
{
var stop = Value.GradientStops[i];
var textBlock = _textPanel.Children[i] as AvaloniaTextBlock;
var colorText = FormatColor(stop.Color, Format);
var percent = $"{stop.Offset * 100:0}%";
if (textBlock != null)
{
if (i < textRunCount - 1)
{
textBlock.Text = $"{colorText} {percent}, ";
}
else
{
textBlock.Text = $"{colorText} {percent}";
}
}
}
}
}
}
protected override void GenerateColorBlockBackground()
{
if (_colorIndicator != null)
{
_colorIndicator.SetCurrentValue(ColorBlock.IsEmptyColorModeProperty, false);
}
if (Value == null)
{
SetCurrentValue(ColorBlockBackgroundProperty, new SolidColorBrush(Colors.Transparent));
}
else
{
SetCurrentValue(ColorBlockBackgroundProperty, Value);
}
}
protected override Flyout CreatePickerFlyout()
{
var flyout = new GradientColorPickerFlyout();
flyout.IsDetectMouseClickEnabled = false;
_flyoutBindingDisposables?.Dispose();
_flyoutBindingDisposables = new CompositeDisposable(6);
_flyoutBindingDisposables.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, flyout, GradientColorPickerFlyout.IsMotionEnabledProperty));
_flyoutBindingDisposables.Add(BindUtils.RelayBind(this, IsClearEnabledProperty, flyout, GradientColorPickerFlyout.IsClearEnabledProperty));
_flyoutBindingDisposables.Add(BindUtils.RelayBind(this, FormatProperty, flyout, GradientColorPickerFlyout.FormatProperty));
_flyoutBindingDisposables.Add(BindUtils.RelayBind(this, IsAlphaEnabledProperty, flyout, GradientColorPickerFlyout.IsAlphaEnabledProperty));
_flyoutBindingDisposables.Add(BindUtils.RelayBind(this, IsFormatEnabledProperty, flyout, GradientColorPickerFlyout.IsFormatEnabledProperty));
_flyoutBindingDisposables.Add(BindUtils.RelayBind(this, IsPaletteGroupEnabledProperty, flyout, GradientColorPickerFlyout.IsPaletteGroupEnabledProperty));
_flyoutBindingDisposables.Add(BindUtils.RelayBind(this, PaletteGroupProperty, flyout, GradientColorPickerFlyout.PaletteGroupProperty));
return flyout;
}
protected override void NotifyFlyoutPresenterCreated(Control control)
{
if (control is FlyoutPresenter flyoutPresenter && flyoutPresenter.Content is GradientColorPickerView presenter)
{
_presenter = presenter;
}
}
private void HandleColorPickerViewValueChanged(object? sender, GradientColorChangedEventArgs args)
{
if (IsFlyoutOpen)
{
SetCurrentValue(ValueProperty, args.NewColor);
}
}
protected override void NotifyFlyoutOpened()
{
if (_presenter != null)
{
_presenter.GradientValueChanged += HandleColorPickerViewValueChanged;
_presenter.ColorValueCleared += HandleColorCleared;
var effectiveColor = Value ?? DefaultValue;
_activeStopIndexChangedDisposable = BindUtils.RelayBind(_presenter, GradientColorPickerView.ActivatedStopIndexProperty, this, ActivatedStopIndexProperty);
if (effectiveColor != null)
{
_presenter.SetCurrentValue(GradientColorPickerView.ValueProperty, effectiveColor);
_presenter.SetCurrentValue(GradientColorPickerView.ActivatedStopIndexProperty, _latestActivatedStopIndex ?? 0);
}
}
}
protected override void NotifyFlyoutClosed()
{
if (_presenter != null)
{
if (Value != null)
{
ValueSelected?.Invoke(this, new GradientColorSelectedEventArgs(Value));
}
_latestActivatedStopIndex = ActivatedStopIndex;
_presenter.GradientValueChanged -= HandleColorPickerViewValueChanged;
_presenter.ColorValueCleared -= HandleColorCleared;
_activeStopIndexChangedDisposable?.Dispose();
_activeStopIndexChangedDisposable = null;
SetCurrentValue(ActivatedStopIndexProperty, null);
}
}
private void HandleGradientActiveStopChanged(int? index)
{
if (_textPanel != null)
{
for (var i = 0; i < _textPanel.Children.Count; i++)
{
var textBlock = _textPanel.Children[i] as AvaloniaTextBlock;
if (textBlock != null)
{
if (i == index)
{
textBlock.Foreground = InactiveStopTextColor;
}
else
{
textBlock.Foreground = Foreground;
}
}
}
}
}
internal void NotifyValueChanged(GradientColorChangedEventArgs e)
{
GradientValueChanged?.Invoke(this, e);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
Value ??= DefaultValue;
_textPanel = e.NameScope.Find<WrapPanel>(ColorPickerThemeConstants.ColorTextPanelPart);
_colorIndicator = e.NameScope.Find<ColorBlock>(ColorPickerThemeConstants.ColorIndicatorPart);
GenerateValueText();
GenerateColorBlockBackground();
}
private void HandleColorCleared(object? sender, EventArgs args)
{
if (_colorIndicator != null)
{
_colorIndicator.SetCurrentValue(ColorBlock.IsEmptyColorModeProperty, true);
_textPanel?.Children.Clear();
_emptyTextBindingDisposable?.Dispose();
var emptyTextBlock = new AvaloniaTextBlock();
_emptyTextBindingDisposable = BindUtils.RelayBind(this, EmptyColorTextProperty, emptyTextBlock, AvaloniaTextBlock.TextProperty);
_textPanel?.Children.Add(emptyTextBlock);
}
}
}