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 75d1b2eff..467b202c8 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 @@ -45,6 +45,7 @@ namespace AtomUI.Theme tokenTypes.Add(typeof(AtomUI.Controls.ScrollBarToken)); tokenTypes.Add(typeof(AtomUI.Controls.SegmentedToken)); tokenTypes.Add(typeof(AtomUI.Controls.SeparatorToken)); + tokenTypes.Add(typeof(AtomUI.Controls.SkeletonToken)); tokenTypes.Add(typeof(AtomUI.Controls.SliderToken)); tokenTypes.Add(typeof(AtomUI.Controls.TabControlToken)); tokenTypes.Add(typeof(AtomUI.Controls.TagToken)); 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 b15e8430c..4cb12bc5a 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 @@ -659,6 +659,18 @@ namespace AtomUI.Theme.Styling public static readonly TokenResourceKey VerticalMarginInline = new TokenResourceKey("Separator.VerticalMarginInline"); } + public static class SkeletonTokenKey + { + public static readonly TokenResourceKey BlockRadius = new TokenResourceKey("Skeleton.BlockRadius"); + public static readonly TokenResourceKey GradientFromColor = new TokenResourceKey("Skeleton.GradientFromColor"); + public static readonly TokenResourceKey GradientToColor = new TokenResourceKey("Skeleton.GradientToColor"); + public static readonly TokenResourceKey LoadingBackground = new TokenResourceKey("Skeleton.LoadingBackground"); + public static readonly TokenResourceKey LoadingMotionDuration = new TokenResourceKey("Skeleton.LoadingMotionDuration"); + public static readonly TokenResourceKey ParagraphLineHeight = new TokenResourceKey("Skeleton.ParagraphLineHeight"); + public static readonly TokenResourceKey ParagraphMarginTop = new TokenResourceKey("Skeleton.ParagraphMarginTop"); + public static readonly TokenResourceKey TitleHeight = new TokenResourceKey("Skeleton.TitleHeight"); + } + public static class SliderTokenKey { public static readonly TokenResourceKey MarginPartWithMark = new TokenResourceKey("Slider.MarginPartWithMark"); diff --git a/src/AtomUI.Controls/Skeleton/AbstractSkeleton.cs b/src/AtomUI.Controls/Skeleton/AbstractSkeleton.cs new file mode 100644 index 000000000..cc4826997 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/AbstractSkeleton.cs @@ -0,0 +1,29 @@ +using Avalonia; +using Avalonia.Controls.Primitives; + +namespace AtomUI.Controls; + +public abstract class AbstractSkeleton : TemplatedControl, + ISizeTypeAware +{ + #region 公共属性定义 + + public static readonly StyledProperty IsActiveProperty = + AvaloniaProperty.Register(nameof(IsActive)); + + public static readonly StyledProperty SizeTypeProperty = + SizeTypeAwareControlProperty.SizeTypeProperty.AddOwner(); + + public bool IsActive + { + get => GetValue(IsActiveProperty); + set => SetValue(IsActiveProperty, value); + } + + public SizeType SizeType + { + get => GetValue(SizeTypeProperty); + set => SetValue(SizeTypeProperty, value); + } + #endregion +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/Skeleton.cs b/src/AtomUI.Controls/Skeleton/Skeleton.cs new file mode 100644 index 000000000..85d13941b --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/Skeleton.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace AtomUI.Controls; + +public class Skeleton : ContentControl +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonAvatar.cs b/src/AtomUI.Controls/Skeleton/SkeletonAvatar.cs new file mode 100644 index 000000000..64d6e2651 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonAvatar.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +public class SkeletonAvatar : AbstractSkeleton +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonButton.cs b/src/AtomUI.Controls/Skeleton/SkeletonButton.cs new file mode 100644 index 000000000..3c84c7cfe --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonButton.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +public class SkeletonButton : AbstractSkeleton +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonInput.cs b/src/AtomUI.Controls/Skeleton/SkeletonInput.cs new file mode 100644 index 000000000..7c7fde5ca --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonInput.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +public class SkeletonInput : AbstractSkeleton +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonLine.cs b/src/AtomUI.Controls/Skeleton/SkeletonLine.cs new file mode 100644 index 000000000..a983a8a3f --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonLine.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +internal class SkeletonLine : AbstractSkeleton +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonParagraph.cs b/src/AtomUI.Controls/Skeleton/SkeletonParagraph.cs new file mode 100644 index 000000000..12d319d42 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonParagraph.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +public class SkeletonParagraph : AbstractSkeleton +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonTitle.cs b/src/AtomUI.Controls/Skeleton/SkeletonTitle.cs new file mode 100644 index 000000000..eaf4cdd66 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonTitle.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls; + +public class SkeletonTitle +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonToken.cs b/src/AtomUI.Controls/Skeleton/SkeletonToken.cs new file mode 100644 index 000000000..44122ddc7 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonToken.cs @@ -0,0 +1,86 @@ +using AtomUI.Theme.TokenSystem; +using Avalonia; +using Avalonia.Media; + +namespace AtomUI.Controls; + +[ControlDesignToken] +internal class SkeletonToken : AbstractControlDesignToken + +{ + public const string ID = "Skeleton"; + + /// + /// 渐变色起点颜色 + /// Start color of gradient + /// + public Color GradientFromColor { get; set; } + + /// + /// 渐变色终点颜色 + /// End color of gradient + /// + public Color GradientToColor { get; set; } + + /// + /// 标题骨架屏高度 + /// Height of title skeleton + /// + public double TitleHeight { get; set; } + + /// + /// 骨架屏圆角 + /// Border radius of skeleton + /// + public CornerRadius BlockRadius { get; set; } + + /// + /// 段落骨架屏上间距 + /// Margin top of paragraph skeleton + /// + public Thickness ParagraphMarginTop { get; set; } + + /// + /// 段落骨架屏单行高度 + /// Line height of paragraph skeleton + /// + public double ParagraphLineHeight { get; set; } + + /// + /// 默认加载的时长 + /// + public TimeSpan LoadingMotionDuration { get; set; } + + /// + /// 默认加载的动画背景笔刷 + /// + public IBrush? LoadingBackground { get; set; } + + public SkeletonToken() + : base(ID) + { + } + + protected internal override void CalculateFromAlias() + { + base.CalculateFromAlias(); + GradientFromColor = SharedToken.ColorFillContent; + GradientToColor = SharedToken.ColorFill; + TitleHeight = SharedToken.ControlHeight / 2; + BlockRadius = SharedToken.BorderRadiusSM; + ParagraphMarginTop = new Thickness(0, SharedToken.UniformlyMarginLG + SharedToken.UniformlyMarginXXS, 0, 0); + ParagraphLineHeight = SharedToken.ControlHeight / 2; + LoadingMotionDuration = TimeSpan.FromSeconds(1.4); + LoadingBackground = new LinearGradientBrush() + { + StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative), + EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative), + GradientStops = + { + new GradientStop { Offset = 0.25, Color = GradientFromColor }, + new GradientStop { Offset = 0.37, Color = GradientToColor }, + new GradientStop { Offset = 0.63, Color = GradientFromColor } + } + }; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/SkeletonWidth.cs b/src/AtomUI.Controls/Skeleton/SkeletonWidth.cs new file mode 100644 index 000000000..64d64e6c1 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/SkeletonWidth.cs @@ -0,0 +1,126 @@ +using System.Globalization; +using System.Text.RegularExpressions; +using AtomUI.Utils; + +namespace AtomUI.Controls; + +public enum SkeletonUnitType +{ + Percentage = 0, + Pixel = 1, +} + +public struct SkeletonWidth : IEquatable +{ + private readonly SkeletonUnitType _type; + private readonly double _value; + + public SkeletonWidth(double value) + : this(value, SkeletonUnitType.Pixel) + { + } + + public SkeletonWidth(double value, SkeletonUnitType type) + { + if (value < 0 || double.IsNaN(value) || double.IsInfinity(value)) + { + throw new ArgumentException("Invalid value", nameof(value)); + } + + if (type < SkeletonUnitType.Percentage || type > SkeletonUnitType.Pixel) + { + throw new ArgumentException("Invalid value", nameof(type)); + } + + _type = type; + _value = value; + } + + public SkeletonUnitType SkeletonUnitType => _type; + + public bool IsAbsolute => _type == SkeletonUnitType.Pixel; + + public bool IsPercentage => _type == SkeletonUnitType.Percentage; + + public bool IsPixel => _type == SkeletonUnitType.Pixel; + + public double Value => _value; + + public static bool operator ==(SkeletonWidth a, SkeletonWidth b) + { + return (MathUtils.AreClose(a._value, b._value) && a._type == b._type); + } + + public static bool operator !=(SkeletonWidth gl1, SkeletonWidth gl2) + { + return !(gl1 == gl2); + } + + public override bool Equals(object? o) + { + if (o == null) + { + return false; + } + + if (!(o is SkeletonWidth)) + { + return false; + } + + return this == (SkeletonWidth)o; + } + + public bool Equals(SkeletonWidth gridLength) + { + return this == gridLength; + } + + public override int GetHashCode() + { + return _value.GetHashCode() ^ _type.GetHashCode(); + } + + public override string ToString() + { + var vstr = _value.ToString(CultureInfo.InvariantCulture); + if (IsPercentage) + { + return $"{vstr}%"; + } + return $"{vstr}px"; + } + + public static SkeletonWidth Parse(string s) + { + s = s.ToUpperInvariant(); + var match = Regex.Match(s.Trim(), @"^([+-]?\d*\.?\d+)\s*([a-zA-Z%]+)?$", RegexOptions.IgnoreCase); + if (!match.Success) + { + throw new FormatException($"Invalid width format: '{s}'"); + } + + SkeletonUnitType unit = SkeletonUnitType.Pixel; + if (match.Groups[2].Value == "%") + { + unit = SkeletonUnitType.Percentage; + } + + var value = double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); + return new SkeletonWidth(value, unit); + } + + public static IEnumerable ParseWidths(string s) + { + var result = new List(); + using (var tokenizer = new SpanStringTokenizer(s, CultureInfo.InvariantCulture)) + { + while (tokenizer.TryReadString(out var item)) + { + result.Add(Parse(item)); + } + } + + return result; + } +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/Themes/SkeletonTheme.axaml b/src/AtomUI.Controls/Skeleton/Themes/SkeletonTheme.axaml new file mode 100644 index 000000000..02212c223 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/Themes/SkeletonTheme.axaml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/Themes/SkeletonThemeConstants.cs b/src/AtomUI.Controls/Skeleton/Themes/SkeletonThemeConstants.cs new file mode 100644 index 000000000..93859c5b1 --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/Themes/SkeletonThemeConstants.cs @@ -0,0 +1,6 @@ +namespace AtomUI.Controls.Themes; + +internal static class SkeletonThemeConstants +{ + +} \ No newline at end of file diff --git a/src/AtomUI.Controls/Skeleton/Themes/SkeletonThemes.axaml b/src/AtomUI.Controls/Skeleton/Themes/SkeletonThemes.axaml new file mode 100644 index 000000000..22b93c22d --- /dev/null +++ b/src/AtomUI.Controls/Skeleton/Themes/SkeletonThemes.axaml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file