Refactor AnimationUtils

This commit is contained in:
polarboy 2024-10-08 22:21:49 +08:00
parent 9d04394913
commit 95905bc890
88 changed files with 873 additions and 222 deletions

View File

@ -0,0 +1,26 @@
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
namespace AtomUI.Utils;
public class AnimationUtils
{
protected AnimationUtils() {}
public static ITransition CreateTransition<T>(AvaloniaProperty targetProperty,
TimeSpan? duration = null,
Easing? easing = null)
where T : TransitionBase, new()
{
easing ??= new LinearEasing();
duration ??= TimeSpan.FromMilliseconds(300);
var transition = new T
{
Property = targetProperty,
Easing = easing,
Duration = duration.Value
};
return transition;
}
}

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;

View File

@ -1,7 +1,7 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;

View File

@ -1,7 +1,7 @@
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,7 +1,7 @@
using AtomUI.Data;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;

View File

@ -1,7 +1,7 @@
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Palette;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,7 +1,7 @@
using AtomUI.Controls.Badge;
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,7 +1,7 @@
using AtomUI.Controls.Badge;
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,5 +1,5 @@
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;

View File

@ -5,8 +5,6 @@ using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.TokenSystem;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,8 +1,8 @@
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.TokenSystem;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;

View File

@ -1,7 +1,7 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;

View File

@ -1,9 +1,8 @@
using System.Globalization;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Data;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia.Animation;
using Avalonia.Controls.Primitives;

View File

@ -2,8 +2,6 @@
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;

View File

@ -1,7 +1,7 @@
using AtomUI.Controls.Utils;
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation.Easings;
using Avalonia.Automation.Peers;

View File

@ -1,8 +1,7 @@
using AtomUI.Controls.Primitives;
using AtomUI.MotionScene;
using AtomUI.MotionScene;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,7 +1,7 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;

View File

@ -1,9 +1,8 @@
using System.Globalization;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,7 +1,7 @@
using AtomUI.Data;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,6 +1,6 @@
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,8 +1,7 @@
using AtomUI.Controls.Primitives;
using AtomUI.MotionScene;
using AtomUI.MotionScene;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,8 +1,8 @@
using System.ComponentModel;
using System.Reactive.Disposables;
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives.PopupPositioning;

View File

@ -1,6 +1,6 @@
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives.PopupPositioning;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;

View File

@ -1,6 +1,6 @@
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
@ -74,7 +74,7 @@ internal class PickerClearUpButtonTheme : BaseControlTheme
PickerClearUpButton.IsInClearModeProperty);
layout.Children.Add(clearButton);
}
protected override void BuildInstanceStyles(Control control)
{
var iconStyle = new Style(selector => selector.Name(InfoIconContentPart).Child().OfType<PathIcon>());

View File

@ -1,4 +1,4 @@
using AtomUI.Theme.Utils;
using AtomUI.Controls.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,5 +1,5 @@
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;

View File

@ -1,5 +1,5 @@
using AtomUI.Media;
using AtomUI.Theme.Utils;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using Avalonia;
using Avalonia.Animation;
using Avalonia.LogicalTree;

View File

@ -1,3 +1,4 @@
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;

View File

@ -1,5 +1,5 @@
using AtomUI.Data;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using Avalonia;
using Avalonia.Controls;
using Avalonia.LogicalTree;

View File

@ -1,7 +1,7 @@
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,7 +1,7 @@
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;

View File

@ -1,7 +1,7 @@
using AtomUI.Icon;
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation.Easings;
using Avalonia.Controls;

View File

@ -1,11 +1,8 @@
using AtomUI.Controls.Primitives;
using AtomUI.MotionScene;
using AtomUI.MotionScene;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;

View File

@ -1,8 +1,8 @@
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,11 +1,9 @@
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Layout;
using Avalonia.Styling;
@ -15,12 +13,12 @@ namespace AtomUI.Controls;
internal class HorizontalNavMenuTheme : BaseNavMenuTheme
{
public const string ID = "HorizontalNavMenu";
public HorizontalNavMenuTheme()
: base(typeof(NavMenu))
{
}
public override string ThemeResourceKey()
{
return ID;

View File

@ -1,7 +1,6 @@
using AtomUI.Controls.Primitives;
using AtomUI.MotionScene;
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
@ -23,19 +22,19 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
public const string MenuIndicatorIconLayoutPart = "PART_MenuIndicatorIconLayout";
public const string ChildItemsPresenterPart = "PART_ChildItemsPresenter";
public const string ChildItemsLayoutTransformPart = "PART_ChildItemsLayoutTransform";
public InlineNavMenuItemTheme() : base(typeof(NavMenuItem))
{
}
public override string ThemeResourceKey()
{
return ID;
}
protected override Control BuildMenuIndicatorIcon(INameScope scope)
{
var indicatorIcon = base.BuildMenuIndicatorIcon(scope);
var indicatorIcon = base.BuildMenuIndicatorIcon(scope);
var menuIndicatorIconPresenter = new Border()
{
Name = MenuIndicatorIconLayoutPart,
@ -55,14 +54,14 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
{
Orientation = Orientation.Vertical
};
var headerContent = base.BuildMenuItemContent(navMenuItem, scope);
var childItemsLayoutTransform = new MotionActorControl()
{
Name = ChildItemsLayoutTransformPart,
Name = ChildItemsLayoutTransformPart,
};
TokenResourceBinder.CreateTokenBinding(childItemsLayoutTransform, MotionActorControl.MarginProperty,
TokenResourceBinder.CreateTokenBinding(childItemsLayoutTransform, MotionActorControl.MarginProperty,
NavMenuTokenResourceKey.VerticalItemsPanelSpacing, BindingPriority.Template,
(v) =>
{
@ -74,7 +73,7 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
return new Thickness();
});
childItemsLayoutTransform.RegisterInNameScope(scope);
var itemsPresenter = new ItemsPresenter
{
Name = ChildItemsPresenterPart,
@ -84,7 +83,7 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
CreateTemplateParentBinding(itemsPresenter, ItemsPresenter.ItemsPanelProperty, NavMenuItem.ItemsPanelProperty);
childItemsLayoutTransform.Child = itemsPresenter;
rootLayout.Children.Add(headerContent);
rootLayout.Children.Add(childItemsLayoutTransform);
return rootLayout;
@ -109,8 +108,9 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
{
base.BuildStyles();
BuildMenuIndicatorStyle();
var itemsPanelStyle = new Style(selector => selector.Nesting().Template().Name(ChildItemsPresenterPart).Child().OfType<StackPanel>());
var itemsPanelStyle = new Style(selector =>
selector.Nesting().Template().Name(ChildItemsPresenterPart).Child().OfType<StackPanel>());
itemsPanelStyle.Add(StackPanel.SpacingProperty, NavMenuTokenResourceKey.VerticalItemsPanelSpacing);
Add(itemsPanelStyle);
}
@ -118,8 +118,9 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
private void BuildMenuIndicatorStyle()
{
{
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
var transformOptions = new TransformOperations.Builder(1);
var menuIndicatorStyle =
new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
var transformOptions = new TransformOperations.Builder(1);
transformOptions.AppendRotate(MathUtils.Deg2Rad(90));
menuIndicatorStyle.Add(Border.RenderTransformProperty, transformOptions.Build());
menuIndicatorStyle.Add(Visual.IsVisibleProperty, true);
@ -127,8 +128,9 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
}
var openSubMenuStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Open));
{
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
var transformOptions = new TransformOperations.Builder(1);
var menuIndicatorStyle =
new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
var transformOptions = new TransformOperations.Builder(1);
transformOptions.AppendRotate(MathUtils.Deg2Rad(-90));
menuIndicatorStyle.Add(Border.RenderTransformProperty, transformOptions.Build());
openSubMenuStyle.Add(menuIndicatorStyle);
@ -136,7 +138,8 @@ internal class InlineNavMenuItemTheme : BaseNavMenuItemTheme
Add(openSubMenuStyle);
var emptySubMenuStyle = new Style(selector => selector.Nesting().Class(StdPseudoClass.Empty));
{
var menuIndicatorStyle = new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
var menuIndicatorStyle =
new Style(selector => selector.Nesting().Template().Name(MenuIndicatorIconLayoutPart));
menuIndicatorStyle.Add(Visual.IsVisibleProperty, false);
emptySubMenuStyle.Add(menuIndicatorStyle);
}

View File

@ -1,12 +1,12 @@
using System.Windows.Input;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Input;
using AtomUI.Media;
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;

View File

@ -1,5 +1,5 @@
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
@ -14,23 +14,23 @@ internal class NavMenuItemTheme : BaseNavMenuItemTheme
{
public const string ItemsPresenterPart = "PART_ItemsPresenter";
public const string PopupFramePart = "PART_PopupFrame";
public NavMenuItemTheme()
: base(typeof(NavMenuItem))
{
}
protected NavMenuItemTheme(Type targetType) : base(targetType)
{
}
protected override void BuildExtraItem(Panel layout, INameScope scope)
{
var popup = CreateMenuPopup();
popup.RegisterInNameScope(scope);
layout.Children.Add(popup);
}
protected Popup CreateMenuPopup()
{
var popup = new Popup
@ -45,7 +45,7 @@ internal class NavMenuItemTheme : BaseNavMenuItemTheme
{
Name = PopupFramePart
};
TokenResourceBinder.CreateTokenBinding(popup, Popup.MarginToAnchorProperty,
NavMenuTokenResourceKey.TopLevelItemPopupMarginToAnchor);
TokenResourceBinder.CreateTokenBinding(border, Border.CornerRadiusProperty,
@ -80,11 +80,12 @@ internal class NavMenuItemTheme : BaseNavMenuItemTheme
return popup;
}
protected override void BuildStyles()
{
base.BuildStyles();
var itemsPanelStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart).Child().OfType<StackPanel>());
var itemsPanelStyle = new Style(selector =>
selector.Nesting().Template().Name(ItemsPresenterPart).Child().OfType<StackPanel>());
itemsPanelStyle.Add(StackPanel.SpacingProperty, NavMenuTokenResourceKey.VerticalItemsPanelSpacing);
Add(itemsPanelStyle);
@ -93,8 +94,9 @@ internal class NavMenuItemTheme : BaseNavMenuItemTheme
popupFrameStyle.Add(Border.BackgroundProperty, GlobalTokenResourceKey.ColorBgContainer);
Add(popupFrameStyle);
}
var darkCommonStyle = new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.IsDarkStyleProperty, true));
var darkCommonStyle =
new Style(selector => selector.Nesting().PropertyEquals(NavMenuItem.IsDarkStyleProperty, true));
{
var popupFrameStyle = new Style(selector => selector.Nesting().Template().Name(PopupFramePart));
popupFrameStyle.Add(Border.BackgroundProperty, NavMenuTokenResourceKey.DarkItemBg);

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;

View File

@ -1,8 +1,8 @@
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,6 +1,6 @@
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;

View File

@ -1,12 +1,8 @@
using AtomUI.Controls.Primitives;
using AtomUI.Controls.Utils;
using AtomUI.MotionScene;
using AtomUI.MotionScene;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;

View File

@ -1,4 +1,5 @@
using AtomUI.Theme.Styling;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;

View File

@ -1,6 +1,6 @@
using AtomUI.Icon;
using AtomUI.Controls.Utils;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Theme.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,8 +1,8 @@
using System.Reactive.Disposables;
using AtomUI.Data;
using AtomUI.MotionScene;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;

View File

@ -1,5 +1,5 @@
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Templates;

View File

@ -1,5 +1,5 @@
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
@ -10,6 +10,7 @@ using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Media;
using AnimationUtils = AtomUI.Controls.Utils.AnimationUtils;
namespace AtomUI.Controls;

View File

@ -2,8 +2,6 @@
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,6 +1,6 @@
using AtomUI.Data;
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,8 +1,7 @@
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,4 +1,5 @@
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Automation.Peers;

View File

@ -1,7 +1,7 @@
using System.Globalization;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,7 +1,7 @@
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,7 +1,7 @@
using AtomUI.Media;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;

View File

@ -1,7 +1,7 @@
using AtomUI.Icon;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;

View File

@ -1,6 +1,6 @@
using System.Globalization;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Converters;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using AtomUI.Controls.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;

View File

@ -1,9 +1,8 @@
using AtomUI.Controls.Utils;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,7 +1,7 @@
using AtomUI.Icon;
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;

View File

@ -1,6 +1,6 @@
using AtomUI.Media;
using AtomUI.Controls.Utils;
using AtomUI.Media;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using AtomUI.Controls.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;

View File

@ -1,9 +1,8 @@
using AtomUI.Controls.Utils;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,6 +1,6 @@
using AtomUI.Theme;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;

View File

@ -1,7 +1,7 @@
using AtomUI.Reflection;
using AtomUI.Theme.Data;
using AtomUI.Theme.Palette;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;

View File

@ -1,5 +1,5 @@
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;

View File

@ -2,9 +2,8 @@
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls.Primitives;

View File

@ -1,5 +1,6 @@
using AtomUI.Controls.Utils;
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;

View File

@ -3,7 +3,6 @@ using AtomUI.Media;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Theme.Utils;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;

View File

@ -0,0 +1,32 @@
using AtomUI.Theme.Styling;
using AtomUI.Theme.TokenSystem;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
namespace AtomUI.Controls.Utils;
public sealed class AnimationUtils : AtomUI.Utils.AnimationUtils
{
public static ITransition CreateTransition<T>(AvaloniaProperty targetProperty,
TokenResourceKey? durationResourceKey = null,
Easing? easing = null)
where T : TransitionBase, new()
{
easing ??= new LinearEasing();
durationResourceKey ??= GlobalTokenResourceKey.MotionDurationMid;
var transition = new T
{
Property = targetProperty,
Easing = easing
};
var application = Application.Current;
if (application is not null)
{
transition.Bind(TransitionBase.DurationProperty, application.GetResourceObservable(durationResourceKey));
}
return transition;
}
}

View File

@ -7,4 +7,8 @@
<PackageReference Include="Avalonia"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AtomUI.Base\AtomUI.Base.csproj" />
</ItemGroup>
</Project>

View File

@ -3,7 +3,7 @@ using Avalonia.Media;
namespace AtomUI.Icon;
public class IconImage : DrawingImage, IImage
internal class IconImage : DrawingImage, IImage
{
public static readonly StyledProperty<IconInfo> DataProperty = AvaloniaProperty.Register<
IconImage,

667
src/AtomUI.Icon/PathIcon.cs Normal file
View File

@ -0,0 +1,667 @@
using AtomUI.Media;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Reactive;
using Avalonia.Rendering;
using Avalonia.Styling;
namespace AtomUI.Icon;
public sealed class PathIcon : Control, ICustomHitTest
{
public static readonly StyledProperty<string> KindProperty = AvaloniaProperty.Register<PathIcon, string>(
nameof(Kind), string.Empty);
public static readonly StyledProperty<IconAnimation> LoadingAnimationProperty =
AvaloniaProperty.Register<PathIcon, IconAnimation>(
nameof(LoadingAnimation));
public static readonly StyledProperty<string?> PackageProviderProperty =
AvaloniaProperty.Register<PathIcon, string?>(
nameof(PackageProvider));
// Fill 和 Outline 支持的颜色
public static readonly StyledProperty<IBrush?> NormalFilledBrushProperty =
AvaloniaProperty.Register<PathIcon, IBrush?>(
nameof(NormalFilledBrush));
public static readonly StyledProperty<IBrush?> ActiveFilledBrushProperty =
AvaloniaProperty.Register<PathIcon, IBrush?>(
nameof(ActiveFilledBrush));
public static readonly StyledProperty<IBrush?> SelectedFilledBrushProperty =
AvaloniaProperty.Register<PathIcon, IBrush?>(
nameof(SelectedFilledBrush));
public static readonly StyledProperty<IBrush?> DisabledFilledBrushProperty =
AvaloniaProperty.Register<PathIcon, IBrush?>(
nameof(DisabledFilledBrush));
// TwoTone 类型的颜色
public static readonly StyledProperty<IBrush?> PrimaryFilledBrushProperty =
AvaloniaProperty.Register<PathIcon, IBrush?>(
nameof(PrimaryFilledBrush));
public static readonly StyledProperty<IBrush?> SecondaryFilledBrushProperty =
AvaloniaProperty.Register<PathIcon, IBrush?>(
nameof(SecondaryFilledBrush));
public static readonly StyledProperty<TimeSpan> LoadingAnimationDurationProperty =
AvaloniaProperty.Register<PathIcon, TimeSpan>(
nameof(LoadingAnimationDuration), TimeSpan.FromSeconds(1));
public static readonly StyledProperty<TimeSpan> FillAnimationDurationProperty =
AvaloniaProperty.Register<PathIcon, TimeSpan>(
nameof(FillAnimationDuration), TimeSpan.FromSeconds(300));
public static readonly StyledProperty<IconMode> IconModeProperty =
AvaloniaProperty.Register<PathIcon, IconMode>(nameof(IconMode));
public string Kind
{
get => GetValue(KindProperty);
set => SetValue(KindProperty, value);
}
public string? PackageProvider
{
get => GetValue(PackageProviderProperty);
set => SetValue(PackageProviderProperty, value);
}
public IBrush? NormalFilledBrush
{
get => GetValue(NormalFilledBrushProperty);
set => SetValue(NormalFilledBrushProperty, value);
}
public IBrush? ActiveFilledBrush
{
get => GetValue(ActiveFilledBrushProperty);
set => SetValue(ActiveFilledBrushProperty, value);
}
public IBrush? SelectedFilledBrush
{
get => GetValue(SelectedFilledBrushProperty);
set => SetValue(SelectedFilledBrushProperty, value);
}
public IBrush? DisabledFilledBrush
{
get => GetValue(DisabledFilledBrushProperty);
set => SetValue(DisabledFilledBrushProperty, value);
}
public IBrush? PrimaryFilledBrush
{
get => GetValue(PrimaryFilledBrushProperty);
set => SetValue(PrimaryFilledBrushProperty, value);
}
public IBrush? SecondaryFilledBrush
{
get => GetValue(SecondaryFilledBrushProperty);
set => SetValue(SecondaryFilledBrushProperty, value);
}
public TimeSpan LoadingAnimationDuration
{
get => GetValue(LoadingAnimationDurationProperty);
set => SetValue(LoadingAnimationDurationProperty, value);
}
public TimeSpan FillAnimationDuration
{
get => GetValue(FillAnimationDurationProperty);
set => SetValue(FillAnimationDurationProperty, value);
}
/// <summary>
/// PathIcon 的模式,只对 Outlined 和 Filled 类型有效
/// </summary>
public IconMode IconMode
{
get => GetValue(IconModeProperty);
set => SetValue(IconModeProperty, value);
}
private static readonly StyledProperty<IBrush?> FilledBrushProperty
= AvaloniaProperty.Register<ToggleSwitch, IBrush?>(
nameof(IBrush));
/// <summary>
/// 当是非 TwoTone icon 的时候,填充色是支持渐变的
/// </summary>
private IBrush? FilledBrush
{
get => GetValue(FilledBrushProperty);
set => SetValue(FilledBrushProperty, value);
}
public IconThemeType ThemeType => _iconInfo?.ThemeType ?? IconThemeType.Filled;
public IconAnimation LoadingAnimation
{
get => GetValue(LoadingAnimationProperty);
set => SetValue(LoadingAnimationProperty, value);
}
#region
internal static readonly StyledProperty<double> AngleAnimationRotateProperty =
AvaloniaProperty.Register<PathIcon, double>(
nameof(AngleAnimationRotate));
internal double AngleAnimationRotate
{
get => GetValue(AngleAnimationRotateProperty);
set => SetValue(AngleAnimationRotateProperty, value);
}
#endregion
private Animation? _animation;
private CancellationTokenSource? _animationCancellationTokenSource;
private WeakReference<IIconPackageProvider>? _iconPackageRef;
private readonly List<Matrix> _transforms;
private readonly List<Geometry> _sourceGeometriesData;
private IconInfo? _iconInfo;
private Rect _viewBox;
static PathIcon()
{
AffectsGeometry(KindProperty, PackageProviderProperty);
AffectsMeasure<PathIcon>(HeightProperty, WidthProperty);
AffectsRender<PathIcon>(IconModeProperty,
FilledBrushProperty,
PrimaryFilledBrushProperty,
SecondaryFilledBrushProperty);
HorizontalAlignmentProperty.OverrideDefaultValue<PathIcon>(HorizontalAlignment.Left);
VerticalAlignmentProperty.OverrideDefaultValue<PathIcon>(VerticalAlignment.Center);
}
public PathIcon()
{
var rotateTransform = new RotateTransform();
RenderTransform = rotateTransform;
_sourceGeometriesData = new List<Geometry>();
_transforms = new List<Matrix>();
}
private void SetupTransitions()
{
Transitions ??= new Transitions()
{
AnimationUtils.CreateTransition<SolidColorBrushTransition>(FilledBrushProperty, FillAnimationDuration)
};
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == KindProperty)
{
// TODO 这里不存在记录日志吗?暂时构造一个默认的
if (VisualRoot is not null)
{
BuildSourceRenderData();
}
}
else if (change.Property == IsEnabledProperty)
{
// TODO 这个地方需要优化一点,是否需要保存老的,当状态为 Enabled 的时候进行还原
if (!IsEnabled)
{
IconMode = IconMode.Disabled;
}
}
else if (change.Property == NormalFilledBrushProperty ||
change.Property == ActiveFilledBrushProperty ||
change.Property == SelectedFilledBrushProperty ||
change.Property == DisabledFilledBrushProperty ||
change.Property == PrimaryFilledBrushProperty ||
change.Property == SecondaryFilledBrushProperty ||
change.Property == IconModeProperty)
{
SetupFilledBrush();
}
else if (change.Property == AngleAnimationRotateProperty)
{
SetCurrentValue(RenderTransformProperty, new RotateTransform(AngleAnimationRotate));
}
if (VisualRoot is not null)
{
if (change.Property == LoadingAnimationProperty)
{
SetupRotateAnimation();
}
}
}
private void SetupRotateAnimation()
{
if (_animation is not null)
{
_animationCancellationTokenSource?.Cancel();
_animation = null;
_animationCancellationTokenSource = null;
}
if (LoadingAnimation == IconAnimation.Spin || LoadingAnimation == IconAnimation.Pulse)
{
_animation = new Animation
{
Duration = LoadingAnimationDuration,
IterationCount = new IterationCount(ulong.MaxValue),
Children =
{
new KeyFrame
{
Cue = new Cue(0d),
Setters = { new Setter(AngleAnimationRotateProperty, 0d) }
},
new KeyFrame
{
Cue = new Cue(1d),
Setters = { new Setter(AngleAnimationRotateProperty, 360d) }
}
}
};
if (LoadingAnimation == IconAnimation.Pulse)
{
_animation.Easing = new PulseEasing();
}
if (VisualRoot is not null)
{
_animationCancellationTokenSource = new CancellationTokenSource();
_animation.RunAsync(this, _animationCancellationTokenSource.Token);
}
}
}
private void SetupFilledBrush()
{
var colorInfo = _iconInfo?.ColorInfo;
if (IconMode == IconMode.Normal)
{
if (NormalFilledBrush is not null)
{
FilledBrush = NormalFilledBrush;
}
else if (colorInfo.HasValue)
{
FilledBrush = new SolidColorBrush(colorInfo.Value.NormalColor);
}
}
else if (IconMode == IconMode.Active)
{
if (ActiveFilledBrush is not null)
{
FilledBrush = ActiveFilledBrush;
}
else if (NormalFilledBrush is not null)
{
FilledBrush = NormalFilledBrush;
}
else if (colorInfo.HasValue)
{
FilledBrush = new SolidColorBrush(colorInfo.Value.ActiveColor);
}
}
else if (IconMode == IconMode.Selected)
{
if (SelectedFilledBrush is not null)
{
FilledBrush = SelectedFilledBrush;
}
else if (NormalFilledBrush is not null)
{
FilledBrush = NormalFilledBrush;
}
else if (colorInfo.HasValue)
{
FilledBrush = new SolidColorBrush(colorInfo.Value.SelectedColor);
}
}
else
{
if (DisabledFilledBrush is not null)
{
FilledBrush = DisabledFilledBrush;
}
else if (NormalFilledBrush is not null)
{
FilledBrush = NormalFilledBrush;
}
else if (colorInfo.HasValue)
{
FilledBrush = new SolidColorBrush(colorInfo.Value.DisabledColor);
}
}
}
/// <summary>
/// Invalidates the geometry of this shape.
/// </summary>
private void InvalidateGeometry()
{
if (_animation is not null)
{
_animationCancellationTokenSource?.Cancel();
}
_sourceGeometriesData.Clear();
_transforms.Clear();
_iconInfo = null;
InvalidateMeasure();
}
/// <summary>
/// Marks a property as affecting the shape's geometry.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateGeometry" /> to be called on the element.
/// </remarks>
private static void AffectsGeometry(params AvaloniaProperty[] properties)
{
foreach (var property in properties)
{
property.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs>(e =>
{
if (e.Sender is PathIcon icon)
{
AffectsGeometryInvalidate(icon, e);
}
}));
}
}
private static void AffectsGeometryInvalidate(PathIcon control, AvaloniaPropertyChangedEventArgs e)
{
// If the geometry is invalidated when Bounds changes, only invalidate when the Size
// portion changes.
if (e.Property == BoundsProperty)
{
var oldBounds = (Rect)e.OldValue!;
var newBounds = (Rect)e.NewValue!;
if (oldBounds.Size == newBounds.Size)
{
return;
}
}
control.InvalidateGeometry();
}
private void BuildSourceRenderData()
{
if (_sourceGeometriesData.Count > 0)
{
return;
}
var manager = IconManager.Current;
PackageProvider ??= manager.DefaultPackage;
var iconPackage = manager.GetIconProvider(PackageProvider);
// 这里报错还是?
if (iconPackage is not null)
{
_iconPackageRef = new WeakReference<IIconPackageProvider>(iconPackage);
}
if (_iconPackageRef != null && _iconPackageRef.TryGetTarget(out var iconPackageProvider))
{
// TODO 这里可能需要优化,针对 IconInfo 的拷贝问题
_iconInfo = iconPackageProvider.GetIcon(Kind) ?? new IconInfo();
foreach (var geometryData in _iconInfo.Data)
{
_sourceGeometriesData.Add(Geometry.Parse(geometryData.PathData));
}
_viewBox = _iconInfo!.ViewBox;
// 先求最大的 bounds
// 裁剪边距算法,暂时先注释掉
Geometry? combined = null;
foreach (var geometry in _sourceGeometriesData)
{
if (combined is null)
{
combined = geometry;
}
else
{
combined = new CombinedGeometry(combined, geometry);
}
}
var combinedBounds = combined!.Bounds;
var marginHorizontal = Math.Min(_iconInfo.ViewBox.Right - combinedBounds.Right, combinedBounds.X);
var marginVertical = Math.Min(_iconInfo.ViewBox.Bottom - combinedBounds.Bottom, combinedBounds.Y);
var margin = Math.Min(marginHorizontal, marginVertical);
var scaleX = 1 - margin / _viewBox.Width;
var scaleY = 1 - margin / _viewBox.Height;
if (margin > 0)
{
for (var i = 0; i < _sourceGeometriesData.Count; i++)
{
var geometry = _sourceGeometriesData[i];
var cloned = geometry.Clone();
var offsetX = -margin / 2;
var offsetY = -margin / 2;
var matrix = Matrix.CreateTranslation(offsetX, offsetY);
matrix = matrix * Matrix.CreateScale(scaleX, scaleY);
cloned.Transform = new MatrixTransform(matrix);
_sourceGeometriesData[i] = cloned;
}
_viewBox = combined!.Bounds;
}
}
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
SetupTransitions();
SetupRotateAnimation();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (_sourceGeometriesData.Count == 0)
{
BuildSourceRenderData();
SetupFilledBrush();
}
if (_animation is not null && _animationCancellationTokenSource is null)
{
_animationCancellationTokenSource = new CancellationTokenSource();
_animation.RunAsync(this, _animationCancellationTokenSource.Token);
}
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_animationCancellationTokenSource?.Cancel();
_animationCancellationTokenSource = null;
}
protected override Size MeasureOverride(Size availableSize)
{
if (_sourceGeometriesData.Count == 0)
{
return default;
}
Size targetSize = default;
for (var i = 0; i < _sourceGeometriesData.Count; i++)
{
var sourceGeometry = _sourceGeometriesData[i];
var currentSize = CalculateSizeAndTransform(availableSize, sourceGeometry.Bounds).size;
targetSize = new Size(Math.Max(targetSize.Width, currentSize.Width),
Math.Min(targetSize.Height, currentSize.Height));
}
return targetSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
_transforms.Clear();
// This should probably use GetRenderBounds(strokeThickness) but then the calculations
// will multiply the stroke thickness as well, which isn't correct.
for (var i = 0; i < _sourceGeometriesData.Count; i++)
{
var sourceGeometry = _sourceGeometriesData[i];
var (_, transform) = CalculateSizeAndTransform(finalSize, sourceGeometry.Bounds);
_transforms.Insert(i, transform);
}
return finalSize;
}
public override void Render(DrawingContext context)
{
if (IsVisible &&
_sourceGeometriesData.Count > 0 &&
Bounds.Width > 0 &&
Bounds.Width > 0)
{
for (var i = 0; i < _sourceGeometriesData.Count; i++)
{
var renderedGeometry = _sourceGeometriesData[i];
var geometryData = _iconInfo!.Data[i];
IBrush? fillBrush = null;
if (_iconInfo.ThemeType == IconThemeType.TwoTone)
{
var colorInfo = _iconInfo.TwoToneColorInfo;
if (colorInfo.HasValue)
{
if (geometryData.IsPrimary)
{
if (PrimaryFilledBrush is not null)
{
fillBrush = PrimaryFilledBrush;
}
else
{
fillBrush = new SolidColorBrush(colorInfo.Value.PrimaryColor);
}
}
else
{
if (SecondaryFilledBrush is not null)
{
fillBrush = SecondaryFilledBrush;
}
else
{
fillBrush = new SolidColorBrush(colorInfo.Value.SecondaryColor);
}
}
}
}
else
{
fillBrush = FilledBrush;
}
using var state = context.PushTransform(_transforms[i]);
context.DrawGeometry(fillBrush, null, renderedGeometry);
}
}
}
private (Size size, Matrix transform) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds)
{
var shapeSize = new Size(shapeBounds.Width, shapeBounds.Height);
var desiredX = availableSize.Width;
var desiredY = availableSize.Height;
var sx = 0.0;
var sy = 0.0;
var viewBoxWidth = _viewBox.Width;
var viewBoxHeight = _viewBox.Height;
// 计算大小的比例因子
var shapeWidthScale = shapeBounds.Width / viewBoxWidth;
var shapeHeightScale = shapeBounds.Height / viewBoxHeight;
// 计算位移的比例因子
var offsetXScale = Math.Floor(availableSize.Width / viewBoxWidth);
var offsetYScale = Math.Floor(availableSize.Height / viewBoxHeight);
var offsetX = shapeBounds.X;
var offsetY = shapeBounds.Y;
shapeSize = shapeBounds.Size;
if (double.IsInfinity(availableSize.Width))
{
desiredX = shapeSize.Width;
}
else
{
desiredX = availableSize.Width * shapeWidthScale;
offsetX *= offsetXScale;
}
if (double.IsInfinity(availableSize.Height))
{
desiredY = shapeSize.Height;
}
else
{
desiredY = availableSize.Height * shapeHeightScale;
offsetY *= offsetYScale;
}
var translate = Matrix.CreateTranslation(-offsetX, -offsetY);
if (shapeBounds.Width > 0)
{
sx = desiredX / shapeSize.Width;
}
if (shapeBounds.Height > 0)
{
sy = desiredY / shapeSize.Height;
}
if (double.IsInfinity(availableSize.Width))
{
sx = sy;
}
if (double.IsInfinity(availableSize.Height))
{
sy = sx;
}
sx = sy = Math.Min(sx, sy);
translate = translate * Matrix.CreateScale(sx, sy);
var size = new Size(shapeSize.Width * sx, shapeSize.Height * sy);
return (size, translate);
}
public bool HitTest(Point point)
{
var targetRect = new Rect(0, 0, DesiredSize.Width, DesiredSize.Height);
return targetRect.Contains(point);
}
}

View File

@ -5,7 +5,7 @@ using Avalonia.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Styling;
namespace AtomUI.Utils;
namespace AtomUI.Theme.Data;
public static class TokenResourceBinder
{

View File

@ -1,64 +0,0 @@
using AtomUI.Theme.Styling;
using AtomUI.Theme.TokenSystem;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Styling;
namespace AtomUI.Theme.Utils;
public static class AnimationUtils
{
public static Animation RunAnimation<ValueType>(AvaloniaProperty targetProperty,
ValueType startValue,
ValueType endValue,
TimeSpan duration,
Easing? easing = null)
{
if (easing is null)
{
easing = new LinearEasing();
}
var animation = new Animation
{
Duration = duration,
Easing = easing,
FillMode = FillMode.Backward,
Children =
{
new KeyFrame
{
Setters = { new Setter(targetProperty, startValue) }, KeyTime = TimeSpan.FromMilliseconds(0)
},
new KeyFrame
{
Setters = { new Setter(targetProperty, endValue) }, KeyTime = duration
}
}
};
return animation;
}
public static ITransition CreateTransition<T>(AvaloniaProperty targetProperty,
TokenResourceKey? durationResourceKey = null,
Easing? easing = null)
where T : TransitionBase, new()
{
easing ??= new LinearEasing();
durationResourceKey ??= GlobalTokenResourceKey.MotionDurationMid;
var transition = new T
{
Property = targetProperty,
Easing = easing
};
var application = Application.Current;
if (application is not null)
{
transition.Bind(TransitionBase.DurationProperty, application.GetResourceObservable(durationResourceKey));
}
return transition;
}
}