mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-24 02:09:37 +08:00
348 lines
13 KiB
C#
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);
|
|
}
|
|
}
|
|
} |