mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-24 18:23:01 +08:00
246 lines
7.6 KiB
C#
246 lines
7.6 KiB
C#
using AtomUI.Controls.Utils;
|
|
using AtomUI.Data;
|
|
using AtomUI.Theme;
|
|
using AtomUI.Theme.Utils;
|
|
using Avalonia;
|
|
using Avalonia.Animation;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Input;
|
|
using Avalonia.Media;
|
|
using Avalonia.VisualTree;
|
|
|
|
namespace AtomUI.Controls;
|
|
|
|
public class Segmented : SelectingItemsControl,
|
|
IMotionAwareControl,
|
|
IControlSharedTokenResourcesHost
|
|
{
|
|
#region 公共属性定义
|
|
|
|
public static readonly StyledProperty<SizeType> SizeTypeProperty =
|
|
SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner<Segmented>();
|
|
|
|
public static readonly StyledProperty<bool> IsExpandingProperty =
|
|
AvaloniaProperty.Register<Segmented, bool>(nameof(IsExpanding));
|
|
|
|
public static readonly StyledProperty<bool> IsMotionEnabledProperty
|
|
= MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<Segmented>();
|
|
|
|
public SizeType SizeType
|
|
{
|
|
get => GetValue(SizeTypeProperty);
|
|
set => SetValue(SizeTypeProperty, value);
|
|
}
|
|
|
|
public bool IsExpanding
|
|
{
|
|
get => GetValue(IsExpandingProperty);
|
|
set => SetValue(IsExpandingProperty, value);
|
|
}
|
|
|
|
public bool IsMotionEnabled
|
|
{
|
|
get => GetValue(IsMotionEnabledProperty);
|
|
set => SetValue(IsMotionEnabledProperty, value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 内部属性定义
|
|
|
|
public static readonly StyledProperty<CornerRadius> SelectedThumbCornerRadiusProperty =
|
|
AvaloniaProperty.Register<Segmented, CornerRadius>(nameof(SelectedThumbCornerRadius));
|
|
|
|
internal static readonly StyledProperty<IBrush?> SelectedThumbBgProperty =
|
|
AvaloniaProperty.Register<Segmented, IBrush?>(
|
|
nameof(SelectedThumbBg));
|
|
|
|
internal static readonly StyledProperty<BoxShadows> SelectedThumbBoxShadowsProperty =
|
|
AvaloniaProperty.Register<Segmented, BoxShadows>(
|
|
nameof(SelectedThumbBoxShadows));
|
|
|
|
internal CornerRadius SelectedThumbCornerRadius
|
|
{
|
|
get => GetValue(SelectedThumbCornerRadiusProperty);
|
|
set => SetValue(SelectedThumbCornerRadiusProperty, value);
|
|
}
|
|
|
|
internal IBrush? SelectedThumbBg
|
|
{
|
|
get => GetValue(SelectedThumbBgProperty);
|
|
set => SetValue(SelectedThumbBgProperty, value);
|
|
}
|
|
|
|
internal BoxShadows SelectedThumbBoxShadows
|
|
{
|
|
get => GetValue(SelectedThumbBoxShadowsProperty);
|
|
set => SetValue(SelectedThumbBoxShadowsProperty, value);
|
|
}
|
|
|
|
// 内部动画属性
|
|
internal static readonly StyledProperty<Size> SelectedThumbSizeProperty =
|
|
AvaloniaProperty.Register<Segmented, Size>(nameof(SelectedThumbSize));
|
|
|
|
internal Size SelectedThumbSize
|
|
{
|
|
get => GetValue(SelectedThumbSizeProperty);
|
|
set => SetValue(SelectedThumbSizeProperty, value);
|
|
}
|
|
|
|
internal static readonly StyledProperty<Point> SelectedThumbPosProperty =
|
|
AvaloniaProperty.Register<Segmented, Point>(nameof(SelectedThumbPos));
|
|
|
|
internal Point SelectedThumbPos
|
|
{
|
|
get => GetValue(SelectedThumbPosProperty);
|
|
set => SetValue(SelectedThumbPosProperty, value);
|
|
}
|
|
|
|
Control IMotionAwareControl.PropertyBindTarget => this;
|
|
Control IControlSharedTokenResourcesHost.HostControl => this;
|
|
string IControlSharedTokenResourcesHost.TokenId => SegmentedToken.ID;
|
|
|
|
#endregion
|
|
|
|
static Segmented()
|
|
{
|
|
AffectsMeasure<Segmented>(IsExpandingProperty, SizeTypeProperty);
|
|
AffectsRender<Segmented>(
|
|
SelectedThumbCornerRadiusProperty,
|
|
SelectedThumbBgProperty,
|
|
SelectedThumbBoxShadowsProperty,
|
|
SelectedThumbSizeProperty,
|
|
SelectedThumbPosProperty);
|
|
}
|
|
|
|
public Segmented()
|
|
{
|
|
this.RegisterResources();
|
|
SelectionChanged += HandleSelectionChanged;
|
|
SelectionMode = SelectionMode.Single;
|
|
}
|
|
|
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
|
{
|
|
base.OnApplyTemplate(e);
|
|
var hasDefaultSelected = false;
|
|
foreach (var item in Items)
|
|
{
|
|
if (item is not null)
|
|
{
|
|
var container = ContainerFromItem(item);
|
|
if (container is not null)
|
|
{
|
|
if (GetIsSelected(container))
|
|
{
|
|
hasDefaultSelected = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!hasDefaultSelected)
|
|
{
|
|
SelectedIndex = 0;
|
|
}
|
|
|
|
SetupSelectedThumbRect();
|
|
}
|
|
|
|
private void HandleSelectionChanged(object? sender, SelectionChangedEventArgs args)
|
|
{
|
|
if (this.IsAttachedToVisualTree())
|
|
{
|
|
SetupSelectedThumbRect();
|
|
}
|
|
}
|
|
|
|
protected override void OnSizeChanged(SizeChangedEventArgs e)
|
|
{
|
|
base.OnSizeChanged(e);
|
|
SetupSelectedThumbRect();
|
|
}
|
|
|
|
private void SetupSelectedThumbRect()
|
|
{
|
|
if (SelectedItem is not null)
|
|
{
|
|
var segmentedItem = ContainerFromItem(SelectedItem);
|
|
if (segmentedItem is not null)
|
|
{
|
|
var offset = segmentedItem.TranslatePoint(new Point(0, 0), this) ?? default;
|
|
var offsetX = offset.X;
|
|
var targetPos = new Point(offsetX, offset.Y);
|
|
SelectedThumbPos = targetPos;
|
|
SelectedThumbSize = segmentedItem.DesiredSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
|
|
{
|
|
return new SegmentedItem();
|
|
}
|
|
|
|
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
|
|
{
|
|
return NeedsContainer<SegmentedItem>(item, out recycleKey);
|
|
}
|
|
|
|
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
|
{
|
|
base.PrepareContainerForItemOverride(container, item, index);
|
|
if (container is SegmentedItem segmentedItem)
|
|
{
|
|
BindUtils.RelayBind(this, SizeTypeProperty, segmentedItem, SegmentedItem.SizeTypeProperty);
|
|
BindUtils.RelayBind(this, IsMotionEnabledProperty, segmentedItem, SegmentedItem.IsMotionEnabledProperty);
|
|
}
|
|
}
|
|
|
|
private void ConfigureTransitions()
|
|
{
|
|
if (IsMotionEnabled)
|
|
{
|
|
Transitions = new Transitions
|
|
{
|
|
TransitionUtils.CreateTransition<PointTransition>(SelectedThumbPosProperty),
|
|
TransitionUtils.CreateTransition<SizeTransition>(SelectedThumbSizeProperty)
|
|
};
|
|
}
|
|
else
|
|
{
|
|
Transitions = null;
|
|
}
|
|
}
|
|
|
|
internal bool UpdateSelectionFromPointerEvent(Control source, PointerEventArgs e)
|
|
{
|
|
return UpdateSelectionFromEventSource(
|
|
source,
|
|
true,
|
|
false,
|
|
false,
|
|
e.GetCurrentPoint(source).Properties.IsRightButtonPressed);
|
|
}
|
|
|
|
public sealed override void Render(DrawingContext context)
|
|
{
|
|
context.DrawRectangle(Background, null, new RoundedRect(new Rect(DesiredSize.Deflate(Margin)), CornerRadius));
|
|
context.DrawRectangle(SelectedThumbBg, null,
|
|
new RoundedRect(new Rect(SelectedThumbPos, SelectedThumbSize), SelectedThumbCornerRadius),
|
|
SelectedThumbBoxShadows);
|
|
}
|
|
|
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
|
{
|
|
base.OnPropertyChanged(change);
|
|
if (this.IsAttachedToVisualTree())
|
|
{
|
|
if (change.Property == IsMotionEnabledProperty)
|
|
{
|
|
ConfigureTransitions();
|
|
}
|
|
}
|
|
}
|
|
} |