Add PaginationNavItem

This commit is contained in:
CHINBOY
2025-05-22 17:54:37 +08:00
parent 8794df7f0a
commit 0a4fec78da
17 changed files with 940 additions and 34 deletions
+1
View File
@@ -15,6 +15,7 @@
<InternalsVisibleTo Include="AtomUI"/>
<InternalsVisibleTo Include="AtomUI.Controls"/>
<InternalsVisibleTo Include="AtomUI.Theme"/>
<InternalsVisibleTo Include="AtomUI.IconPkg"/>
</ItemGroup>
<ItemGroup>
+1 -1
View File
@@ -172,7 +172,7 @@ internal class AlertTheme : BaseControlTheme
return descriptionLabel;
}
protected override void BuildStyles()
protected override void BuildStyles()
{
BuildAlertTypeStyle();
BuildMessageLabelStyle();
@@ -4,6 +4,7 @@
<TargetFrameworks>$(AtomUIDevelopTargetFramework);$(AtomUIProductionTargetFramework)</TargetFrameworks>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>GeneratedFiles</CompilerGeneratedFilesOutputPath>
<RootNamespace>AtomUI.Controls</RootNamespace>
</PropertyGroup>
<ItemGroup>
@@ -74,6 +74,9 @@
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.NumericUpDownTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.OptionButtonGroupTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.OptionButtonTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.PaginationNavItemTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.PaginationNavTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.PaginationTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.PickerClearUpButtonTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.PopupConfirmContainerTheme());
ThemeManager.Current.RegisterControlTheme(new AtomUI.Controls.PrimaryButtonTheme());
@@ -32,6 +32,7 @@
ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.NotificationToken));
ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.NumericUpDownToken));
ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.OptionButtonToken));
ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.PaginationToken));
ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.PopupConfirmToken));
ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.ProgressBarToken));
ThemeManager.Current.RegisterControlTokenType(typeof(AtomUI.Controls.RadioButtonToken));
@@ -504,6 +504,24 @@ namespace AtomUI.Theme.Styling
public static readonly TokenResourceKey PaddingSM = new TokenResourceKey("OptionButton.PaddingSM");
}
public static class PaginationTokenKey
{
public static readonly TokenResourceKey InputOutlineOffset = new TokenResourceKey("Pagination.InputOutlineOffset");
public static readonly TokenResourceKey ItemActiveBg = new TokenResourceKey("Pagination.ItemActiveBg");
public static readonly TokenResourceKey ItemActiveBgDisabled = new TokenResourceKey("Pagination.ItemActiveBgDisabled");
public static readonly TokenResourceKey ItemActiveColorDisabled = new TokenResourceKey("Pagination.ItemActiveColorDisabled");
public static readonly TokenResourceKey ItemBg = new TokenResourceKey("Pagination.ItemBg");
public static readonly TokenResourceKey ItemInputBg = new TokenResourceKey("Pagination.ItemInputBg");
public static readonly TokenResourceKey ItemLinkBg = new TokenResourceKey("Pagination.ItemLinkBg");
public static readonly TokenResourceKey ItemSize = new TokenResourceKey("Pagination.ItemSize");
public static readonly TokenResourceKey ItemSizeSM = new TokenResourceKey("Pagination.ItemSizeSM");
public static readonly TokenResourceKey MiniOptionsSizeChangerTop = new TokenResourceKey("Pagination.MiniOptionsSizeChangerTop");
public static readonly TokenResourceKey PaginationItemPaddingInline = new TokenResourceKey("Pagination.PaginationItemPaddingInline");
public static readonly TokenResourceKey PaginationMiniOptionsMarginInline = new TokenResourceKey("Pagination.PaginationMiniOptionsMarginInline");
public static readonly TokenResourceKey PaginationMiniQuickJumperInputWidth = new TokenResourceKey("Pagination.PaginationMiniQuickJumperInputWidth");
public static readonly TokenResourceKey PaginationSlashMarginInline = new TokenResourceKey("Pagination.PaginationSlashMarginInline");
}
public static class PopupConfirmTokenKey
{
public static readonly TokenResourceKey ButtonContainerMargin = new TokenResourceKey("PopupConfirm.ButtonContainerMargin");
@@ -0,0 +1,204 @@
using System.Diagnostics;
using AtomUI.IconPkg.AntDesign;
using AtomUI.Theme;
using AtomUI.Theme.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;
namespace AtomUI.Controls;
public enum PaginationAlign
{
Start,
Center,
End
}
public class Pagination : TemplatedControl,
ISizeTypeAware,
IMotionAwareControl,
IControlSharedTokenResourcesHost
{
public const int DefaultPageSize = 10;
public const int DefaultCurrentPage = 1;
#region 公共属性定义
public static readonly StyledProperty<PaginationAlign> AlignProperty =
AvaloniaProperty.Register<Button, PaginationAlign>(nameof(PaginationAlign));
public static readonly StyledProperty<SizeType> SizeTypeProperty =
SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner<Pagination>();
public static readonly DirectProperty<Pagination, int> CurrentPageProperty =
AvaloniaProperty.RegisterDirect<Pagination, int>(nameof(CurrentPage),
o => o.CurrentPage,
(o, v) => o.CurrentPage = v,
unsetValue: DefaultCurrentPage);
public static readonly DirectProperty<Pagination, int> PageSizeProperty =
AvaloniaProperty.RegisterDirect<Pagination, int>(nameof(PageSize),
o => o.PageSize,
(o, v) => o.PageSize = v,
unsetValue: DefaultPageSize);
public static readonly DirectProperty<Pagination, long> TotalProperty =
AvaloniaProperty.RegisterDirect<Pagination, long>(nameof(Total),
o => o.Total,
(o, v) => o.Total = v);
public static readonly DirectProperty<Pagination, bool> HideOnSinglePageProperty =
AvaloniaProperty.RegisterDirect<Pagination, bool>(nameof(HideOnSinglePage),
o => o.HideOnSinglePage,
(o, v) => o.HideOnSinglePage = v);
public static readonly DirectProperty<Pagination, bool> ShowSizeChangerProperty =
AvaloniaProperty.RegisterDirect<Pagination, bool>(nameof(ShowSizeChanger),
o => o.ShowSizeChanger,
(o, v) => o.ShowSizeChanger = v);
public static readonly DirectProperty<Pagination, bool> ShowTotalInfoProperty =
AvaloniaProperty.RegisterDirect<Pagination, bool>(nameof(ShowTotalInfo),
o => o.ShowTotalInfo,
(o, v) => o.ShowTotalInfo = v);
public static readonly StyledProperty<IDataTemplate?> TotalInfoTemplateProperty =
AvaloniaProperty.Register<Button, IDataTemplate?>(nameof(TotalInfoTemplate));
public static readonly StyledProperty<bool> IsMotionEnabledProperty
= WaveSpiritAwareControlProperty.IsMotionEnabledProperty.AddOwner<CheckBox>();
public SizeType SizeType
{
get => GetValue(SizeTypeProperty);
set => SetValue(SizeTypeProperty, value);
}
public PaginationAlign Align
{
get => GetValue(AlignProperty);
set => SetValue(AlignProperty, value);
}
private int _currentPage = DefaultCurrentPage;
public int CurrentPage
{
get => _currentPage;
set => SetAndRaise(CurrentPageProperty, ref _currentPage, value);
}
private int _pageSize = DefaultPageSize;
public int PageSize
{
get => _pageSize;
set => SetAndRaise(PageSizeProperty, ref _pageSize, value);
}
private long _total;
public long Total
{
get => _total;
set => SetAndRaise(TotalProperty, ref _total, value);
}
private bool _hideOnSinglePage;
public bool HideOnSinglePage
{
get => _hideOnSinglePage;
set => SetAndRaise(HideOnSinglePageProperty, ref _hideOnSinglePage, value);
}
private bool _showSizeChanger;
public bool ShowSizeChanger
{
get => _hideOnSinglePage;
set => SetAndRaise(ShowSizeChangerProperty, ref _showSizeChanger, value);
}
private bool _showTotalInfo;
public bool ShowTotalInfo
{
get => _showTotalInfo;
set => SetAndRaise(ShowTotalInfoProperty, ref _showTotalInfo, value);
}
public IDataTemplate? TotalInfoTemplate
{
get => GetValue(TotalInfoTemplateProperty);
set => SetValue(TotalInfoTemplateProperty, value);
}
public bool IsMotionEnabled
{
get => GetValue(IsMotionEnabledProperty);
set => SetValue(IsMotionEnabledProperty, value);
}
#endregion
#region 内部属性定义
Control IMotionAwareControl.PropertyBindTarget => this;
Control IControlSharedTokenResourcesHost.HostControl => this;
string IControlSharedTokenResourcesHost.TokenId => PaginationToken.ID;
#endregion
private PaginationNav? _paginationNav;
private PaginationNavItem? _previousPageItem;
private PaginationNavItem? _nextPageItem;
private PaginationNavItem? _leftEllipsesItem;
private PaginationNavItem? _rightEllipsesItem;
static Pagination()
{
AffectsMeasure<Button>(SizeTypeProperty);
}
public Pagination()
{
this.RegisterResources();
this.BindMotionProperties();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_paginationNav = e.NameScope.Find<PaginationNav>(PaginationTheme.NavPart);
Debug.Assert(_paginationNav is not null);
var count = _paginationNav.ItemCount;
_paginationNav.ContainerPrepared += (sender, args) =>
{
if (args.Container is PaginationNavItem navItem)
{
if (0 == args.Index)
{
navItem.PaginationItemType = PaginationItemType.Previous;
_previousPageItem = navItem;
_previousPageItem.Content = AntDesignIconPackage.LeftOutlined();
}
else if (2 == args.Index)
{
navItem.PaginationItemType = PaginationItemType.Ellipses;
_leftEllipsesItem = navItem;
_leftEllipsesItem.Content = AntDesignIconPackage.EllipsisOutlined();
}
else if (count - 1 == args.Index)
{
navItem.PaginationItemType = PaginationItemType.Next;
_nextPageItem = navItem;
_nextPageItem.Content = AntDesignIconPackage.RightOutlined();
}
else if (count - 3 == args.Index)
{
navItem.PaginationItemType = PaginationItemType.Ellipses;
_rightEllipsesItem = navItem;
_rightEllipsesItem.Content = AntDesignIconPackage.EllipsisOutlined();
}
else
{
navItem.PaginationItemType = PaginationItemType.PageIndicator;
}
}
};
}
}
@@ -0,0 +1,91 @@
using System.Reactive.Disposables;
using AtomUI.Data;
using AtomUI.Theme;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.LogicalTree;
namespace AtomUI.Controls;
internal class PaginationNav : SelectingItemsControl,
ISizeTypeAware,
ITokenResourceConsumer
{
#region 公共属性
public static readonly StyledProperty<SizeType> SizeTypeProperty =
SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner<PaginationNav>();
public SizeType SizeType
{
get => GetValue(SizeTypeProperty);
set => SetValue(SizeTypeProperty, value);
}
public static readonly StyledProperty<bool> IsMotionEnabledProperty
= MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<PaginationNav>();
public bool IsMotionEnabled
{
get => GetValue(IsMotionEnabledProperty);
set => SetValue(IsMotionEnabledProperty, value);
}
#endregion
#region 内部属性
CompositeDisposable? ITokenResourceConsumer.TokenBindingsDisposable => _tokenBindingsDisposable;
#endregion
private CompositeDisposable? _tokenBindingsDisposable;
static PaginationNav()
{
AffectsMeasure<PaginationNav>(SizeTypeProperty);
}
public PaginationNav()
{
for (var i = 0; i < 11; i++)
{
Items.Add(new PaginationNavItem());
}
SelectionMode = SelectionMode.Single;
}
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
return new PaginationNavItem();
}
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
return NeedsContainer<PaginationNavItem>(item, out recycleKey);
}
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
{
base.PrepareContainerForItemOverride(container, item, index);
if (container is PaginationNavItem navItem)
{
BindUtils.RelayBind(this, SizeTypeProperty, navItem, PaginationNavItem.SizeTypeProperty);
BindUtils.RelayBind(this, IsMotionEnabledProperty, navItem, PaginationNavItem.IsMotionEnabledProperty);
}
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
_tokenBindingsDisposable = new CompositeDisposable();
base.OnAttachedToLogicalTree(e);
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
this.DisposeTokenBindings();
}
}
@@ -0,0 +1,197 @@
using System.Reactive.Disposables;
using AtomUI.Controls.Utils;
using AtomUI.IconPkg;
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
using Avalonia.LogicalTree;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace AtomUI.Controls;
internal enum PaginationItemType
{
Previous,
PageIndicator,
Next,
Ellipses
}
internal class PaginationNavItem : ContentControl,
ISelectable,
ICustomHitTest,
ITokenResourceConsumer
{
public static readonly StyledProperty<bool> IsSelectedProperty =
SelectingItemsControl.IsSelectedProperty.AddOwner<PaginationNavItem>();
public static readonly StyledProperty<PaginationItemType> PaginationItemTypeProperty
= AvaloniaProperty.Register<PaginationNavItem, PaginationItemType>(nameof(PaginationItemType));
public bool IsSelected
{
get => GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
public PaginationItemType PaginationItemType
{
get => GetValue(PaginationItemTypeProperty);
set => SetValue(PaginationItemTypeProperty, value);
}
public static readonly StyledProperty<SizeType> SizeTypeProperty =
SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner<PaginationNavItem>();
public static readonly StyledProperty<bool> IsMotionEnabledProperty
= MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner<PaginationNavItem>();
public SizeType SizeType
{
get => GetValue(SizeTypeProperty);
set => SetValue(SizeTypeProperty, value);
}
public bool IsMotionEnabled
{
get => GetValue(IsMotionEnabledProperty);
set => SetValue(IsMotionEnabledProperty, value);
}
CompositeDisposable? ITokenResourceConsumer.TokenBindingsDisposable => _tokenBindingsDisposable;
private CompositeDisposable? _tokenBindingsDisposable;
static PaginationNavItem()
{
SelectableMixin.Attach<PaginationNavItem>(IsSelectedProperty);
PressedMixin.Attach<PaginationNavItem>();
FocusableProperty.OverrideDefaultValue<PaginationNavItem>(true);
AffectsRender<PaginationNavItem>(BackgroundProperty);
}
public PaginationNavItem()
{
_tokenBindingsDisposable = new CompositeDisposable();
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
this.DisposeTokenBindings();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
SetupTransitions();
if (Content is Icon icon)
{
SetupIconFillColors(icon);
SetupIconSizeType(icon);
SetupIconStatus(icon);
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (this.IsAttachedToVisualTree())
{
if (change.Property == IsMotionEnabledProperty)
{
SetupTransitions();
}
if (change.Property == ContentProperty)
{
if (change.NewValue is Icon newIcon)
{
SetupIconFillColors(newIcon);
SetupIconSizeType(newIcon);
SetupIconStatus(newIcon);
}
}
else if (change.Property == IsEnabledProperty)
{
if (Content is Icon icon)
{
SetupIconStatus(icon);
}
}
else if (change.Property == SizeTypeProperty)
{
if (Content is Icon icon)
{
SetupIconSizeType(icon);
}
}
}
}
private void SetupIconFillColors(Icon icon)
{
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, Icon.NormalFilledBrushProperty, SharedTokenKey.ColorText));
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, Icon.DisabledFilledBrushProperty, SharedTokenKey.ColorTextDisabled));
}
private void SetupIconStatus(Icon icon)
{
if (IsEnabled)
{
icon.IconMode = IconMode.Normal;
}
else
{
icon.IconMode = IconMode.Disabled;
}
}
private void SetupIconSizeType(Icon icon)
{
if (SizeType == SizeType.Large)
{
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, WidthProperty, SharedTokenKey.IconSizeLG));
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, HeightProperty, SharedTokenKey.IconSizeLG));
}
else if (SizeType == SizeType.Middle)
{
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, WidthProperty, SharedTokenKey.IconSize));
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, HeightProperty, SharedTokenKey.IconSize));
}
else
{
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, WidthProperty, SharedTokenKey.IconSizeSM));
this.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(icon, HeightProperty, SharedTokenKey.IconSizeSM));
}
}
private void SetupTransitions()
{
if (IsMotionEnabled)
{
Transitions ??= new Transitions
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(BackgroundProperty)
};
}
else
{
Transitions = null;
}
}
public bool HitTest(Point point)
{
return true;
}
}
@@ -0,0 +1,165 @@
using AtomUI.Controls.Utils;
using AtomUI.IconPkg;
using AtomUI.IconPkg.AntDesign;
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Styling;
namespace AtomUI.Controls;
[ControlThemeProvider]
internal class PaginationNavItemTheme : BaseControlTheme
{
public const string RootLayoutPart = "PART_RootLayoutPart";
public const string MainFramePart = "PART_MainFrame";
public const string ContentPart = "PART_Content";
public PaginationNavItemTheme()
: base(typeof(PaginationNavItem))
{
}
protected override IControlTemplate BuildControlTemplate()
{
return new FuncControlTemplate<PaginationNavItem>((paginationNavItem, scope) =>
{
var rootLayout = new Panel
{
Name = RootLayoutPart
};
var mainFrame = new Border
{
Name = MainFramePart
};
rootLayout.Children.Add(mainFrame);
CreateTemplateParentBinding(mainFrame, Border.BackgroundProperty, TemplatedControl.BackgroundProperty);
CreateTemplateParentBinding(mainFrame, Border.CornerRadiusProperty, TemplatedControl.CornerRadiusProperty);
CreateTemplateParentBinding(mainFrame, Decorator.PaddingProperty, TemplatedControl.PaddingProperty);
var contentPresenter = new ContentPresenter
{
Name = ContentPart,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalContentAlignment = VerticalAlignment.Center,
};
contentPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentProperty,
ContentControl.ContentProperty,
BindingMode.Default,
new FuncValueConverter<object?, object?>(content =>
{
if (content is string text)
{
return new TextBlock()
{
Text = text,
VerticalAlignment = VerticalAlignment.Center,
};
}
return content;
}));
rootLayout.Children.Add(contentPresenter);
return rootLayout;
});
}
protected override void BuildStyles()
{
var commonStyle = new Style(selector => selector.Nesting());
commonStyle.Add(TemplatedControl.CornerRadiusProperty, SharedTokenKey.BorderRadius);
commonStyle.Add(TemplatedControl.FontSizeProperty, SharedTokenKey.FontSize);
commonStyle.Add(TemplatedControl.PaddingProperty, PaginationTokenKey.PaginationItemPaddingInline);
var enabledStyle =
new Style(selector => selector.Nesting().PropertyEquals(InputElement.IsEnabledProperty, true));
enabledStyle.Add(InputElement.CursorProperty, new SetterValueFactory<Cursor>(() => new Cursor(StandardCursorType.Hand)));
BuildPageIndicatorEnabledStyle(enabledStyle);
BuildPreviousAndNextItemEnabledStyle(enabledStyle);
commonStyle.Add(enabledStyle);
Add(commonStyle);
BuildSizeTypeStyle();
BuildDisabledStyle();
}
private void BuildPageIndicatorEnabledStyle(Style enabledStyle)
{
var indicatorStyle = new Style(selector =>
selector.Nesting().PropertyEquals(PaginationNavItem.PaginationItemTypeProperty,
PaginationItemType.PageIndicator));
var selectedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Selected));
indicatorStyle.Add(selectedStyle);
var notSelectedStyle =
new Style(selector => selector.Nesting().Not(x => x.Nesting().Class(StdPseudoClass.Selected)));
notSelectedStyle.Add(TemplatedControl.BackgroundProperty, SharedTokenKey.ColorTransparent);
notSelectedStyle.Add(TemplatedControl.ForegroundProperty, SharedTokenKey.ColorText);
{
var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TemplatedControl.BackgroundProperty, SharedTokenKey.ColorBgTextHover);
notSelectedStyle.Add(hoverStyle);
}
{
var pressedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Pressed));
pressedStyle.Add(TemplatedControl.BackgroundProperty, SharedTokenKey.ColorBgTextActive);
notSelectedStyle.Add(pressedStyle);
}
indicatorStyle.Add(notSelectedStyle);
enabledStyle.Add(indicatorStyle);
}
private void BuildPreviousAndNextItemEnabledStyle(Style enabledStyle)
{
var previousAndNextStyle = new Style(selector =>
Selectors.Or(selector.Nesting().PropertyEquals(PaginationNavItem.PaginationItemTypeProperty,
PaginationItemType.Previous),
selector.Nesting().PropertyEquals(PaginationNavItem.PaginationItemTypeProperty,
PaginationItemType.Next)));
previousAndNextStyle.Add(TemplatedControl.BackgroundProperty, SharedTokenKey.ColorTransparent);
previousAndNextStyle.Add(TemplatedControl.ForegroundProperty, SharedTokenKey.ColorText);
var hoverStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.PointerOver));
hoverStyle.Add(TemplatedControl.BackgroundProperty, SharedTokenKey.ColorBgTextHover);
previousAndNextStyle.Add(hoverStyle);
var pressedStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Pressed));
pressedStyle.Add(TemplatedControl.BackgroundProperty, SharedTokenKey.ColorBgTextActive);
previousAndNextStyle.Add(pressedStyle);
enabledStyle.Add(previousAndNextStyle);
}
private void BuildSizeTypeStyle()
{
var largeSizeStyle =
new Style(selector => selector.Nesting().PropertyEquals(PaginationNavItem.SizeTypeProperty, SizeType.Large));
largeSizeStyle.Add(Layoutable.HeightProperty, PaginationTokenKey.ItemSize);
largeSizeStyle.Add(Layoutable.MinWidthProperty, PaginationTokenKey.ItemSize);
Add(largeSizeStyle);
var middleSizeStyle =
new Style(selector => selector.Nesting().PropertyEquals(PaginationNavItem.SizeTypeProperty, SizeType.Middle));
middleSizeStyle.Add(Layoutable.HeightProperty, PaginationTokenKey.ItemSize);
middleSizeStyle.Add(Layoutable.MinWidthProperty, PaginationTokenKey.ItemSize);
Add(middleSizeStyle);
var smallSizeStyle =
new Style(selector => selector.Nesting().PropertyEquals(PaginationNavItem.SizeTypeProperty, SizeType.Small));
smallSizeStyle.Add(Layoutable.HeightProperty, PaginationTokenKey.ItemSizeSM);
smallSizeStyle.Add(Layoutable.MinWidthProperty, PaginationTokenKey.ItemSizeSM);
Add(smallSizeStyle);
}
private void BuildDisabledStyle()
{
var disabledStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Disabled));
disabledStyle.Add(TemplatedControl.ForegroundProperty, SharedTokenKey.ColorTextDisabled);
Add(disabledStyle);
}
}
@@ -0,0 +1,54 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
namespace AtomUI.Controls;
[ControlThemeProvider]
internal class PaginationNavTheme : BaseControlTheme
{
public const string FramePart = "PART_Frame";
public const string ItemsPresenterPart = "PART_ItemsPresenter";
public PaginationNavTheme()
: base(typeof(PaginationNav))
{
}
protected override IControlTemplate BuildControlTemplate()
{
return new FuncControlTemplate<PaginationNav>((paginationNav, scope) =>
{
var frame = new Border
{
Name = FramePart,
ClipToBounds = true
};
var itemsPresenter = new ItemsPresenter
{
Name = ItemsPresenterPart,
ItemsPanel = new FuncTemplate<Panel?>(() =>
{
var panel = new StackPanel()
{
Orientation = Orientation.Horizontal
};
paginationNav.AddTokenBindingDisposable(TokenResourceBinder.CreateTokenBinding(panel, StackPanel.SpacingProperty, SharedTokenKey.MarginXS));
return panel;
})
};
itemsPresenter.RegisterInNameScope(scope);
frame.Child = itemsPresenter;
CreateTemplateParentBinding(frame, Border.CornerRadiusProperty,
TemplatedControl.CornerRadiusProperty);
CreateTemplateParentBinding(frame, Decorator.PaddingProperty, TemplatedControl.PaddingProperty);
return frame;
});
}
}
@@ -0,0 +1,45 @@
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
namespace AtomUI.Controls;
[ControlThemeProvider]
internal class PaginationTheme : BaseControlTheme
{
public const string RootLayoutPart = "PART_RootLayout";
public const string NavPart = "PART_Nav";
public PaginationTheme()
: base(typeof(Pagination))
{
}
protected override IControlTemplate BuildControlTemplate()
{
var controlTemplate = new FuncControlTemplate<Pagination>((pagination, scope) =>
{
var rootLayout = new StackPanel()
{
Name = RootLayoutPart,
Orientation = Orientation.Horizontal
};
var nav = new PaginationNav()
{
Name = NavPart
};
nav.RegisterInNameScope(scope);
CreateTemplateParentBinding(nav, PaginationNav.IsMotionEnabledProperty, Pagination.IsMotionEnabledProperty);
CreateTemplateParentBinding(nav, PaginationNav.SizeTypeProperty, Pagination.SizeTypeProperty);
rootLayout.Children.Add(nav);
return rootLayout;
});
return controlTemplate;
}
protected override void BuildStyles()
{
}
}
@@ -0,0 +1,120 @@
using AtomUI.Theme.TokenSystem;
using Avalonia;
using Avalonia.Media;
namespace AtomUI.Controls;
[ControlDesignToken]
internal class PaginationToken : AbstractControlDesignToken
{
public const string ID = "Pagination";
/// <summary>
/// 页码选项背景色
/// Background color of Pagination item
/// </summary>
public Color ItemBg { get; set; }
/// <summary>
/// 页码尺寸
/// Size of Pagination item
/// </summary>
public double ItemSize { get; set; }
/// <summary>
/// 页码激活态背景色
/// Background color of active Pagination item
/// </summary>
public Color ItemActiveBg { get; set; }
/// <summary>
/// 小号页码尺寸
/// Size of small Pagination item
/// </summary>
public double ItemSizeSM { get; set; }
/// <summary>
/// 页码链接背景色
/// Background color of Pagination item link
/// </summary>
public Color ItemLinkBg { get; set; }
/// <summary>
/// 页码激活态禁用状态背景色
/// Background color of disabled active Pagination item
/// </summary>
public Color ItemActiveBgDisabled { get; set; }
/// <summary>
/// 页码激活态禁用状态文字颜色
/// Text color of disabled active Pagination item
/// </summary>
public Color ItemActiveColorDisabled { get; set; }
/// <summary>
/// 输入框背景色
/// Background color of input
/// </summary>
public Color ItemInputBg { get; set; }
/// <summary>
/// 每页展示数量选择器 top
/// Top of Pagination size changer
/// </summary>
public double MiniOptionsSizeChangerTop { get; set; }
/// <summary>
/// 输入框轮廓偏移量
/// Outline offset of input
/// </summary>
public Thickness InputOutlineOffset { get; set; }
/// <summary>
/// 迷你选项横向外边距
/// Horizontal margin of mini options
/// </summary>
public Thickness PaginationMiniOptionsMarginInline { get; set; }
/// <summary>
/// 迷你快速跳转输入框宽度
/// Width of mini quick jumper input
/// </summary>
public double PaginationMiniQuickJumperInputWidth { get; set; }
/// <summary>
/// 页码横向内边距
/// Horizontal padding of Pagination item
/// </summary>
public Thickness PaginationItemPaddingInline { get; set; }
/// <summary>
/// 斜杠横向外边距
/// Horizontal margin of slash
/// </summary>
public Thickness PaginationSlashMarginInline { get; set; }
public PaginationToken()
: base(ID)
{
}
internal override void CalculateFromAlias()
{
base.CalculateFromAlias();
ItemBg = SharedToken.ColorBgContainer;
ItemSize = SharedToken.ControlHeight;
ItemSizeSM = SharedToken.ControlHeightSM;
ItemActiveBg = SharedToken.ColorBgContainer;
ItemLinkBg = SharedToken.ColorBgContainer;
ItemActiveColorDisabled = SharedToken.ColorTextDisabled;
ItemActiveBgDisabled = SharedToken.ControlItemBgActiveDisabled;
ItemInputBg = SharedToken.ColorBgContainer;
MiniOptionsSizeChangerTop = 0;
InputOutlineOffset = new Thickness(0);
PaginationMiniOptionsMarginInline = new Thickness(SharedToken.MarginXXS / 2);
PaginationMiniQuickJumperInputWidth = SharedToken.ControlHeightLG * 1.1;
PaginationItemPaddingInline = new Thickness(SharedToken.MarginXXS * 1.5);
PaginationSlashMarginInline = new Thickness(SharedToken.MarginXXS, 0);
}
}
@@ -0,0 +1,6 @@
namespace AtomUI.Controls;
public class SimplePagination
{
}
+4 -2
View File
@@ -1,4 +1,5 @@
using AtomUI.Controls.Utils;
using System.Reactive.Disposables;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Theme;
using AtomUI.Theme.Styling;
@@ -10,6 +11,7 @@ using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.VisualTree;
@@ -104,7 +106,7 @@ public class Segmented : SelectingItemsControl,
Control IMotionAwareControl.PropertyBindTarget => this;
Control IControlSharedTokenResourcesHost.HostControl => this;
string IControlSharedTokenResourcesHost.TokenId => SegmentedToken.ID;
#endregion
static Segmented()
@@ -29,14 +29,14 @@ internal class SegmentedTheme : BaseControlTheme
Name = FramePart,
ClipToBounds = true
};
// TODO 需要观察是否会有内存泄漏
var itemsPresenter = new ItemsPresenter
{
Name = ItemsPresenterPart,
ItemsPanel = new FuncTemplate<Panel?>(() =>
{
var panel = new SegmentedStackPanel();
BindUtils.RelayBind(segmented, Segmented.IsExpandingProperty, panel, SegmentedStackPanel.IsExpandingProperty);
BindUtils.RelayBind(segmented, Segmented.IsExpandingProperty, panel,
SegmentedStackPanel.IsExpandingProperty);
return panel;
})
};
+27 -29
View File
@@ -1,6 +1,6 @@
using System.Globalization;
using AtomUI.Utils;
using Avalonia.Media;
using Avalonia.Utilities;
namespace AtomUI.IconPkg;
@@ -60,44 +60,42 @@ public record struct ColorInfo
public static ColorInfo Parse(string expr)
{
const string exceptionMessage = "Invalid ColorInfo.";
using (var tokenizer = new StringTokenizer(expr, CultureInfo.InvariantCulture, exceptionMessage))
using var tokenizer = new SpanStringTokenizer(expr, CultureInfo.InvariantCulture);
try
{
try
var colorInfo = new ColorInfo();
if (tokenizer.TryReadString(out var color1Str))
{
var colorInfo = new ColorInfo();
if (tokenizer.TryReadString(out var color1Str))
colorInfo.NormalColor = Color.Parse(color1Str);
if (tokenizer.TryReadString(out var color2Str))
{
colorInfo.NormalColor = Color.Parse(color1Str);
if (tokenizer.TryReadString(out var color2Str))
colorInfo.ActiveColor = Color.Parse(color2Str);
if (tokenizer.TryReadString(out var color3Str))
{
colorInfo.ActiveColor = Color.Parse(color2Str);
if (tokenizer.TryReadString(out var color3Str))
colorInfo.SelectedColor = Color.Parse(color3Str);
if (tokenizer.TryReadString(out var color4Str))
{
colorInfo.SelectedColor = Color.Parse(color3Str);
if (tokenizer.TryReadString(out var color4Str))
{
colorInfo.DisabledColor = Color.Parse(color4Str);
}
colorInfo.DisabledColor = Color.Parse(color4Str);
}
}
}
else
{
// 至少要一个
throw new FormatException(exceptionMessage);
}
return colorInfo;
}
catch (Exception e)
else
{
if (e is not FormatException)
{
throw new FormatException(exceptionMessage, e);
}
throw;
// 至少要一个
throw new FormatException(exceptionMessage);
}
return colorInfo;
}
catch (Exception e)
{
if (e is not FormatException)
{
throw new FormatException(exceptionMessage, e);
}
throw;
}
}
}
@@ -110,7 +108,7 @@ public record struct TwoToneColorInfo
public static TwoToneColorInfo Parse(string expr)
{
const string exceptionMessage = "Invalid TwoToneColorInfo.";
using (var tokenizer = new StringTokenizer(expr, CultureInfo.InvariantCulture, exceptionMessage))
using (var tokenizer = new SpanStringTokenizer(expr, CultureInfo.InvariantCulture))
{
if (tokenizer.TryReadString(out var color1Str))
{