diff --git a/src/AtomUI.Controls/AtomUIOSSControlThemesProvider.axaml b/src/AtomUI.Controls/AtomUIOSSControlThemesProvider.axaml index c185c532c..bcc56ca4f 100644 --- a/src/AtomUI.Controls/AtomUIOSSControlThemesProvider.axaml +++ b/src/AtomUI.Controls/AtomUIOSSControlThemesProvider.axaml @@ -8,6 +8,7 @@ + diff --git a/src/AtomUI.Controls/Avatar/Avatar.cs b/src/AtomUI.Controls/Avatar/Avatar.cs new file mode 100644 index 000000000..128d70610 --- /dev/null +++ b/src/AtomUI.Controls/Avatar/Avatar.cs @@ -0,0 +1,311 @@ +using System.Diagnostics; +using AtomUI.Controls.Themes; +using AtomUI.IconPkg; +using AtomUI.Media; +using AtomUI.Theme; +using AtomUI.Theme.Utils; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Data; +using Avalonia.Media; +using Avalonia.Media.Transformation; +using Avalonia.Metadata; + +namespace AtomUI.Controls; + +public enum AvatarSizeType +{ + Large, + Middle, + Small, + Custom +} + +public enum AvatarShape +{ + Circle, + Square +} + +internal enum AvatarContentType +{ + Icon, + BitmapImage, + SvgImage, + Text +} + +public class Avatar : TemplatedControl, IControlSharedTokenResourcesHost, IMotionAwareControl +{ + #region 公共属性定义 + + public static readonly StyledProperty GapProperty = + AvaloniaProperty.Register(nameof(Gap), defaultValue: 4.0); + + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon)); + + public static readonly StyledProperty BitmapSrcProperty = + AvaloniaProperty.Register(nameof(BitmapSrc)); + + public static readonly StyledProperty SrcProperty = + AvaloniaProperty.Register(nameof(Src)); + + public static readonly StyledProperty TextProperty = + AvaloniaProperty.Register(nameof (Text)); + + public static readonly StyledProperty SizeTypeProperty = + AvaloniaProperty.Register(nameof(SizeType), + AvatarSizeType.Middle); + + public static readonly StyledProperty SizeProperty = + AvaloniaProperty.Register(nameof(Size), Double.NaN); + + public static readonly StyledProperty ShapeProperty = + AvaloniaProperty.Register(nameof(Shape), AvatarShape.Circle); + + public static readonly StyledProperty IsMotionEnabledProperty = + MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner(); + + public double Gap + { + get => GetValue(GapProperty); + set => SetValue(GapProperty, value); + } + + public Icon? Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public IImage? BitmapSrc + { + get => GetValue(BitmapSrcProperty); + set => SetValue(BitmapSrcProperty, value); + } + + public string? Src + { + get => GetValue(SrcProperty); + set => SetValue(SrcProperty, value); + } + + [Content] + public string? Text + { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public AvatarSizeType SizeType + { + get => GetValue(SizeTypeProperty); + set => SetValue(SizeTypeProperty, value); + } + + public double Size + { + get => GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + + public AvatarShape Shape + { + get => GetValue(ShapeProperty); + set => SetValue(ShapeProperty, value); + } + + public bool IsMotionEnabled + { + get => GetValue(IsMotionEnabledProperty); + set => SetValue(IsMotionEnabledProperty, value); + } + + #endregion + + #region 内部属性定义 + internal static readonly StyledProperty EffectiveIconSizeProperty = + AvaloniaProperty.Register(nameof(EffectiveIconSize)); + + internal static readonly StyledProperty TextRenderTransformProperty = + AvaloniaProperty.Register(nameof(TextRenderTransform)); + + internal static readonly DirectProperty ContentTypeProperty = + AvaloniaProperty.RegisterDirect( + nameof(ContentType), + o => o.ContentType, + (o, v) => o.ContentType = v); + + internal double EffectiveIconSize + { + get => GetValue(EffectiveIconSizeProperty); + set => SetValue(EffectiveIconSizeProperty, value); + } + + public ITransform? TextRenderTransform + { + get => GetValue(TextRenderTransformProperty); + set => SetValue(TextRenderTransformProperty, value); + } + + private AvatarContentType _contentType = AvatarContentType.Icon; + + internal AvatarContentType ContentType + { + get => _contentType; + set => SetAndRaise(ContentTypeProperty, ref _contentType, value); + } + + Control IMotionAwareControl.PropertyBindTarget => this; + Control IControlSharedTokenResourcesHost.HostControl => this; + string IControlSharedTokenResourcesHost.TokenId => ButtonToken.ID; + + #endregion + + private AvatarSizeType? _originSizeType; + private TextBlock? _textPresenter; + + static Avatar() + { + AffectsMeasure(SizeTypeProperty, TextProperty); + AffectsRender(ShapeProperty, IconProperty, SrcProperty, GapProperty); + } + + public Avatar() + { + this.RegisterResources(); + this.BindMotionProperties(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == SizeProperty) + { + if (!double.IsNaN(Size)) + { + _originSizeType = SizeType; + SizeType = AvatarSizeType.Custom; + } + else + { + if (_originSizeType.HasValue) + { + SizeType = _originSizeType.Value; + } + } + } + else if (change.Property == SizeTypeProperty) + { + ConfigureIconSize(); + } + else if (change.Property == SrcProperty || + change.Property == IconProperty || + change.Property == TextProperty) + { + ConfigureContentType(); + } + if (change.Property == ContentTypeProperty || + change.Property == GapProperty) + { + ConfigureTextRenderTransform(); + } + else if (change.Property == ShapeProperty) + { + ConfigureShape(); + } + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + _textPresenter = e.NameScope.Find(AvatarThemeConstants.TextPresenterPart); + if (_textPresenter != null) + { + _textPresenter.SizeChanged += (sender, args) => + { + ConfigureTextRenderTransform(); + }; + } + ConfigureShape(); + ConfigureIconSize(); + ConfigureContentType(); + } + + private void ConfigureIconSize() + { + if (SizeType == AvatarSizeType.Custom) + { + Debug.Assert(!double.IsNaN(Size)); + // 不影响模板设置 + SetValue(WidthProperty, Size, BindingPriority.Template); + SetValue(HeightProperty, Size, BindingPriority.Template); + SetValue(EffectiveIconSizeProperty, + (Icon != null || Src != null) ? Size / 2 : 18, BindingPriority.Template); + } + } + + private void ConfigureContentType() + { + if (Src != null) + { + ContentType = AvatarContentType.SvgImage; + } + else if (BitmapSrc != null) + { + ContentType = AvatarContentType.BitmapImage; + } + else if (Text != null) + { + ContentType = AvatarContentType.Text; + } + else + { + ContentType = AvatarContentType.Icon; + } + } + + private void ConfigureTextRenderTransform() + { + if (ContentType != AvatarContentType.Text) + { + TextRenderTransform = null; + } + else + { + if (_textPresenter != null && Gap * 2 < Width) + { + double scale = 1; + double offsetX = 0; + var textWidth = TextUtils.CalculateTextSize(Text ?? string.Empty, FontSize, FontFamily).Width; + if (Gap * 2 < Width) + { + scale = (Width - Gap * 2) / textWidth; + scale = Math.Min(scale, 1.0); + offsetX = (scale * (textWidth - Width)) / 2; + } + var builder = new TransformOperations.Builder(2); + builder.AppendScale(scale, scale); + if (scale < 1.0) + { + builder.AppendTranslate(-offsetX, 0); + } + TextRenderTransform = builder.Build(); + } + else + { + TextRenderTransform = null; + } + } + } + + private void ConfigureShape() + { + if (Shape == AvatarShape.Circle) + { + SetValue(CornerRadiusProperty, new CornerRadius(Width / 2), BindingPriority.Template); + } + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Avatar/AvatarGroup.cs b/src/AtomUI.Controls/Avatar/AvatarGroup.cs new file mode 100644 index 000000000..6a8a5d3d8 --- /dev/null +++ b/src/AtomUI.Controls/Avatar/AvatarGroup.cs @@ -0,0 +1,409 @@ +using System.Collections.Specialized; +using System.Reactive.Disposables; +using AtomUI.Data; +using AtomUI.Theme; +using AtomUI.Theme.Data; +using AtomUI.Theme.Styling; +using AtomUI.Theme.Utils; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Layout; +using Avalonia.Media; +using Avalonia.Metadata; + +namespace AtomUI.Controls; + +using ControlList = Avalonia.Controls.Controls; + +public class AvatarGroup : Control, + IMotionAwareControl, + IControlSharedTokenResourcesHost, + IResourceBindingManager +{ + #region 公共属性定义 + + public static readonly StyledProperty FoldInfoAvatarBackgroundProperty = + AvaloniaProperty.Register(nameof (FoldInfoAvatarBackground)); + + public static readonly StyledProperty FoldInfoAvatarForegroundProperty = + AvaloniaProperty.Register(nameof (FoldInfoAvatarForeground)); + + public static readonly StyledProperty FoldAvatarFlyoutTriggerTypeProperty = + AvaloniaProperty.Register(nameof(FoldAvatarFlyoutTriggerType), FlyoutTriggerType.Hover); + + public static readonly StyledProperty BorderThicknessProperty = + Border.BorderThicknessProperty.AddOwner(); + + public static readonly StyledProperty MaxDisplayCountProperty = + AvaloniaProperty.Register(nameof(MaxDisplayCount)); + + public static readonly StyledProperty SizeTypeProperty = + Avatar.SizeTypeProperty.AddOwner(); + + public static readonly StyledProperty SizeProperty = + Avatar.SizeProperty.AddOwner(); + + public static readonly StyledProperty ShapeProperty = + Avatar.ShapeProperty.AddOwner(); + + public static readonly StyledProperty IsMotionEnabledProperty = + MotionAwareControlProperty.IsMotionEnabledProperty.AddOwner(); + + public IBrush? FoldInfoAvatarBackground + { + get => GetValue(FoldInfoAvatarBackgroundProperty); + set => SetValue(FoldInfoAvatarBackgroundProperty, value); + } + + public IBrush? FoldInfoAvatarForeground + { + get => GetValue(FoldInfoAvatarForegroundProperty); + set => SetValue(FoldInfoAvatarForegroundProperty, value); + } + + public FlyoutTriggerType FoldAvatarFlyoutTriggerType + { + get => GetValue(FoldAvatarFlyoutTriggerTypeProperty); + set => SetValue(FoldAvatarFlyoutTriggerTypeProperty, value); + } + + public Thickness BorderThickness + { + get => GetValue(BorderThicknessProperty); + set => SetValue(BorderThicknessProperty, value); + } + + public int? MaxDisplayCount + { + get => GetValue(MaxDisplayCountProperty); + set => SetValue(MaxDisplayCountProperty, value); + } + + public AvatarSizeType SizeType + { + get => GetValue(SizeTypeProperty); + set => SetValue(SizeTypeProperty, value); + } + + public double Size + { + get => GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + + public AvatarShape Shape + { + get => GetValue(ShapeProperty); + set => SetValue(ShapeProperty, value); + } + + public bool IsMotionEnabled + { + get => GetValue(IsMotionEnabledProperty); + set => SetValue(IsMotionEnabledProperty, value); + } + + [Content] public ControlList Children { get; } = new(); + + #endregion + + #region 内部属性定义 + + internal static readonly DirectProperty GroupSpaceProperty = + AvaloniaProperty.RegisterDirect( + nameof(GroupSpace), + o => o.GroupSpace, + (o, v) => o.GroupSpace = v); + + internal static readonly DirectProperty GroupOverlappingProperty = + AvaloniaProperty.RegisterDirect( + nameof(GroupOverlapping), + o => o.GroupOverlapping, + (o, v) => o.GroupOverlapping = v); + + private double _groupSpace; + + internal double GroupSpace + { + get => _groupSpace; + set => SetAndRaise(GroupSpaceProperty, ref _groupSpace, value); + } + + private double _groupOverlapping; + + internal double GroupOverlapping + { + get => _groupOverlapping; + set => SetAndRaise(GroupOverlappingProperty, ref _groupOverlapping, value); + } + + Control IMotionAwareControl.PropertyBindTarget => this; + Control IControlSharedTokenResourcesHost.HostControl => this; + string IControlSharedTokenResourcesHost.TokenId => ButtonToken.ID; + + CompositeDisposable? IResourceBindingManager.ResourceBindingsDisposable + { + get => _resourceBindingsDisposable; + set => _resourceBindingsDisposable = value; + } + #endregion + + private CompositeDisposable? _resourceBindingsDisposable; + private Avatar? _foldCountAvatar; + private FlyoutHost? _foldCountFlyout; + private StackPanel? _foldCountStackPanel; + private Dictionary _disposables = new(); + + static AvatarGroup() + { + AffectsMeasure(GroupOverlappingProperty); + } + + public AvatarGroup() + { + Children.CollectionChanged += ChildrenChanged; + this.RegisterResources(); + this.BindMotionProperties(); + } + + protected virtual void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + var tobeAdded = e.NewItems!.OfType().ToList(); + foreach (var item in tobeAdded) + { + if (item is Avatar avatar) + { + ConfigureAvatar(avatar); + } + } + if (!MaxDisplayCount.HasValue || LogicalChildren.Count < MaxDisplayCount.Value) + { + var startingIndex = e.NewStartingIndex; + foreach (var item in tobeAdded) + { + LogicalChildren.Insert(startingIndex, item); + VisualChildren.Insert(startingIndex, item); + startingIndex++; + } + } + break; + + case NotifyCollectionChangedAction.Move: + if (!MaxDisplayCount.HasValue) + { + LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex); + VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex); + } + else + { + if (e.OldStartingIndex < MaxDisplayCount.Value && + e.OldStartingIndex + e.OldItems!.Count <= MaxDisplayCount.Value && + e.NewStartingIndex <= MaxDisplayCount.Value) + { + LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex); + VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex); + } + } + break; + + case NotifyCollectionChangedAction.Remove: + var items = e.OldItems!.OfType().ToList(); + LogicalChildren.RemoveAll(items); + VisualChildren.RemoveAll(items); + foreach (var child in items) + { + if (_disposables.TryGetValue(child, out var disposable)) + { + disposable.Dispose(); + } + _disposables.Remove(child); + } + break; + + case NotifyCollectionChangedAction.Replace: + if (!MaxDisplayCount.HasValue) + { + for (var i = 0; i < e.OldItems!.Count; ++i) + { + var index = i + e.OldStartingIndex; + var child = (Control)e.NewItems![i]!; + LogicalChildren[index] = child; + VisualChildren[index] = child; + } + } + else + { + for (var i = 0; i < e.OldItems!.Count; ++i) + { + var index = i + e.OldStartingIndex; + if (index < MaxDisplayCount.Value) + { + var child = (Control)e.NewItems![i]!; + LogicalChildren[index] = child; + VisualChildren[index] = child; + } + } + } + + break; + + case NotifyCollectionChangedAction.Reset: + throw new NotSupportedException(); + } + + var zindex = 1; + foreach (var child in Children) + { + child.ZIndex = zindex++; + } + + ConfigureFoldInfo(); + InvalidateMeasureOnChildrenChanged(); + } + + private void ConfigureAvatar(Avatar avatar) + { + var disposable = new CompositeDisposable(); + disposable.Add(BindUtils.RelayBind(this, BorderThicknessProperty, avatar, BorderThicknessProperty)); + disposable.Add(BindUtils.RelayBind(this, IsMotionEnabledProperty, avatar, IsMotionEnabledProperty)); + disposable.Add(BindUtils.RelayBind(this, ShapeProperty, avatar, ShapeProperty)); + disposable.Add(BindUtils.RelayBind(this, SizeProperty, avatar, SizeProperty)); + disposable.Add(BindUtils.RelayBind(this, SizeTypeProperty, avatar, SizeTypeProperty)); + _disposables.Add(avatar, disposable); + } + + private Avatar GetFoldCountAvatar() + { + _foldCountAvatar ??= new Avatar(); + BindUtils.RelayBind(this, BorderThicknessProperty, _foldCountAvatar, BorderThicknessProperty); + BindUtils.RelayBind(this, ShapeProperty, _foldCountAvatar, ShapeProperty); + BindUtils.RelayBind(this, SizeProperty, _foldCountAvatar, SizeProperty); + BindUtils.RelayBind(this, SizeTypeProperty, _foldCountAvatar, SizeTypeProperty); + BindUtils.RelayBind(this, FoldInfoAvatarForegroundProperty, _foldCountAvatar, Avatar.ForegroundProperty); + BindUtils.RelayBind(this, FoldInfoAvatarBackgroundProperty, _foldCountAvatar, Avatar.BackgroundProperty); + return _foldCountAvatar; + } + + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + _resourceBindingsDisposable = new CompositeDisposable(); + this.AddResourceBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, BorderThicknessProperty, + SharedTokenKey.BorderThickness, + BindingPriority.Template, + new RenderScaleAwareThicknessConfigure(this))); + this.AddResourceBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, GroupSpaceProperty, + AvatarTokenKey.GroupSpace)); + this.AddResourceBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, GroupOverlappingProperty, + AvatarTokenKey.GroupOverlapping)); + this.AddResourceBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, FoldInfoAvatarForegroundProperty, + AvatarTokenKey.AvatarColor)); + this.AddResourceBindingDisposable(TokenResourceBinder.CreateTokenBinding(this, FoldInfoAvatarBackgroundProperty, + AvatarTokenKey.AvatarBg)); + var foldCountAvatar = GetFoldCountAvatar(); + if (_foldCountFlyout == null) + { + _foldCountFlyout = new FlyoutHost(); + _foldCountFlyout.ZIndex = Int32.MaxValue; + _foldCountFlyout.AnchorTarget = foldCountAvatar; + _foldCountStackPanel = new StackPanel(); + _foldCountStackPanel.Orientation = Orientation.Horizontal; + BindUtils.RelayBind(this, GroupSpaceProperty, _foldCountStackPanel, StackPanel.SpacingProperty); + BindUtils.RelayBind(this, FoldAvatarFlyoutTriggerTypeProperty, _foldCountFlyout, FlyoutHost.TriggerProperty); + _foldCountFlyout.Flyout = new Flyout() + { + Content = _foldCountStackPanel + }; + } + + ConfigureFoldInfo(); + } + + private void ConfigureFoldInfo() + { + var foldCountAvatar = GetFoldCountAvatar(); + if (_foldCountFlyout != null) + { + if (MaxDisplayCount.HasValue && Children.Count > MaxDisplayCount.Value) + { + foldCountAvatar.Text = $"+{Children.Count - MaxDisplayCount.Value}"; + LogicalChildren.Add(_foldCountFlyout); + VisualChildren.Add(_foldCountFlyout); + for (var i = MaxDisplayCount.Value; i < Children.Count; ++i) + { + _foldCountStackPanel?.Children.Add(Children[i]); + } + } + else + { + LogicalChildren.Remove(_foldCountFlyout); + VisualChildren.Remove(_foldCountFlyout); + _foldCountStackPanel?.Children.Clear(); + } + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + this.DisposeTokenBindings(); + } + + private protected virtual void InvalidateMeasureOnChildrenChanged() + { + InvalidateMeasure(); + } + + protected override Size MeasureOverride(Size availableSize) + { + var size = base.MeasureOverride(availableSize); + // 理论上是统一的,我们的孩子都一样大 + var count = LogicalChildren.Count; + var totalWidth = count * size.Width - (count - 1) * GroupOverlapping; + return size.WithWidth(totalWidth); + } + + protected override Size ArrangeOverride(Size finalSize) + { + var offetX = 0.0; + for (var i = 0; i < LogicalChildren.Count; i++) + { + var child = LogicalChildren[i]; + if (child is Control avatar) + { + var childSize = avatar.DesiredSize; + avatar.Arrange(new Rect(offetX, 0, childSize.Width, childSize.Height)); + offetX += childSize.Width - GroupOverlapping; + } + } + return finalSize; + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == FoldAvatarFlyoutTriggerTypeProperty) + { + ConfigureFoldAvatarCursor(); + } + } + + private void ConfigureFoldAvatarCursor() + { + var foldCountAvatar = GetFoldCountAvatar(); + if (FoldAvatarFlyoutTriggerType == FlyoutTriggerType.Click) + { + + foldCountAvatar.Cursor = new Cursor(StandardCursorType.Hand); + } + else + { + foldCountAvatar.Cursor = new Cursor(StandardCursorType.Arrow); + } + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Avatar/AvatarPseudoClass.cs b/src/AtomUI.Controls/Avatar/AvatarPseudoClass.cs new file mode 100644 index 000000000..b875d2e80 --- /dev/null +++ b/src/AtomUI.Controls/Avatar/AvatarPseudoClass.cs @@ -0,0 +1,9 @@ +namespace AtomUI.Controls; + +public static class AvatarPseudoClass +{ + public const string Large = ":large"; + public const string Middle = ":middle"; + public const string Small = ":small"; + public const string Custom = ":custom-size"; +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Avatar/AvatarToken.cs b/src/AtomUI.Controls/Avatar/AvatarToken.cs new file mode 100644 index 000000000..8487b18a0 --- /dev/null +++ b/src/AtomUI.Controls/Avatar/AvatarToken.cs @@ -0,0 +1,81 @@ +using AtomUI.Theme.TokenSystem; +using Avalonia.Media; + +namespace AtomUI.Controls; + +[ControlDesignToken] +internal class AvatarToken : AbstractControlDesignToken +{ + public const string ID = "Avatar"; + + /// + /// 头像尺寸 + /// + public double ContainerSize { get; set; } + + /// + /// 大号头像尺寸 + /// + public double ContainerSizeLG { get; set; } + + /// + /// 小号头像尺寸 + /// + public double ContainerSizeSM { get; set; } + + /// + /// 头像文字大小 + /// + public double TextFontSize { get; set; } + + /// + /// 大号头像文字大小 + /// + public double TextFontSizeLG { get; set; } + + /// + /// 小号头像文字大小 + /// + public double TextFontSizeSM { get; set; } + + /// + /// 头像组间距 + /// + public double GroupSpace { get; set; } + + /// + /// 头像组重叠宽度 + /// + public double GroupOverlapping { get; set; } + + /// + /// 头像组边框颜色 + /// + public Color GroupBorderColor { get; set; } + + // 内部使用别名 + public Color AvatarBg { get; set; } + public Color AvatarColor { get; set; } + + public AvatarToken() + : base(ID) + { + } + + protected internal override void CalculateFromAlias() + { + base.CalculateFromAlias(); + ContainerSize = SharedToken.ControlHeight; + ContainerSizeLG = SharedToken.ControlHeightLG; + ContainerSizeSM = SharedToken.ControlHeightSM; + TextFontSize = Math.Round((SharedToken.FontSizeLG + SharedToken.FontSizeXL) / 2); + TextFontSizeLG = SharedToken.FontSizeHeading3; + TextFontSizeSM = SharedToken.FontSize; + GroupSpace = SharedToken.UniformlyMarginXXS; + GroupOverlapping = SharedToken.UniformlyMarginXS; + GroupBorderColor = SharedToken.ColorBorderBg; + + AvatarBg = SharedToken.ColorTextPlaceholder; + AvatarColor = SharedToken.ColorTextLightSolid; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Avatar/Themes/AvatarTheme.axaml b/src/AtomUI.Controls/Avatar/Themes/AvatarTheme.axaml new file mode 100644 index 000000000..675287a2c --- /dev/null +++ b/src/AtomUI.Controls/Avatar/Themes/AvatarTheme.axaml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AtomUI.Controls/Avatar/Themes/AvatarThemeConstants.cs b/src/AtomUI.Controls/Avatar/Themes/AvatarThemeConstants.cs new file mode 100644 index 000000000..0ba19f09e --- /dev/null +++ b/src/AtomUI.Controls/Avatar/Themes/AvatarThemeConstants.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls.Themes; + +internal static class AvatarThemeConstants +{ + public const string TextPresenterPart = "PART_TextPresenter"; +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Avatar/Themes/AvatarThemes.axaml b/src/AtomUI.Controls/Avatar/Themes/AvatarThemes.axaml new file mode 100644 index 000000000..beb68fe75 --- /dev/null +++ b/src/AtomUI.Controls/Avatar/Themes/AvatarThemes.axaml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenTypePool.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenTypePool.g.cs index bf0b4fe3e..417b13fe7 100644 --- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenTypePool.g.cs +++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/ControlTokenTypePool.g.cs @@ -12,6 +12,7 @@ namespace AtomUI.Theme tokenTypes.Add(typeof(AtomUI.Controls.AdornerLayerToken)); tokenTypes.Add(typeof(AtomUI.Controls.AlertToken)); tokenTypes.Add(typeof(AtomUI.Controls.ArrowDecoratedBoxToken)); + tokenTypes.Add(typeof(AtomUI.Controls.AvatarToken)); tokenTypes.Add(typeof(AtomUI.Controls.BadgeToken)); tokenTypes.Add(typeof(AtomUI.Controls.ButtonSpinnerToken)); tokenTypes.Add(typeof(AtomUI.Controls.ButtonToken)); diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs index ced059d4d..4269f171f 100644 --- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs +++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs @@ -47,6 +47,21 @@ namespace AtomUI.Theme.Styling public static readonly TokenResourceKey Padding = new TokenResourceKey("ArrowDecoratedBox.Padding"); } + public static class AvatarTokenKey + { + public static readonly TokenResourceKey AvatarBg = new TokenResourceKey("Avatar.AvatarBg"); + public static readonly TokenResourceKey AvatarColor = new TokenResourceKey("Avatar.AvatarColor"); + public static readonly TokenResourceKey ContainerSize = new TokenResourceKey("Avatar.ContainerSize"); + public static readonly TokenResourceKey ContainerSizeLG = new TokenResourceKey("Avatar.ContainerSizeLG"); + public static readonly TokenResourceKey ContainerSizeSM = new TokenResourceKey("Avatar.ContainerSizeSM"); + public static readonly TokenResourceKey GroupBorderColor = new TokenResourceKey("Avatar.GroupBorderColor"); + public static readonly TokenResourceKey GroupOverlapping = new TokenResourceKey("Avatar.GroupOverlapping"); + public static readonly TokenResourceKey GroupSpace = new TokenResourceKey("Avatar.GroupSpace"); + public static readonly TokenResourceKey TextFontSize = new TokenResourceKey("Avatar.TextFontSize"); + public static readonly TokenResourceKey TextFontSizeLG = new TokenResourceKey("Avatar.TextFontSizeLG"); + public static readonly TokenResourceKey TextFontSizeSM = new TokenResourceKey("Avatar.TextFontSizeSM"); + } + public static class BadgeTokenKey { public static readonly TokenResourceKey BadgeColor = new TokenResourceKey("Badge.BadgeColor");