From e80a514abdff4e1cbeb36fc78f25cc4ca8e53ecc Mon Sep 17 00:00:00 2001 From: chinboy Date: Thu, 16 Oct 2025 10:05:21 +0800 Subject: [PATCH] Refactor ColorUtils --- src/AtomUI.Base/Media/ColorUtils.cs | 46 ++++++++++++++++--- src/AtomUI.Controls/Buttons/ButtonToken.cs | 44 +++++++++++++++++- .../TokenResourceConst.g.cs | 4 ++ .../TokenResourceConst.g.cs | 3 ++ .../Styling/DarkThemeVariantCalculator.cs | 40 ++++++++-------- .../DesignToken.ColorNeutralMap.cs | 19 ++++++++ .../TokenDefinitions/DesignToken.cs | 8 ++-- 7 files changed, 134 insertions(+), 30 deletions(-) diff --git a/src/AtomUI.Base/Media/ColorUtils.cs b/src/AtomUI.Base/Media/ColorUtils.cs index 732e0491e..d312b7654 100644 --- a/src/AtomUI.Base/Media/ColorUtils.cs +++ b/src/AtomUI.Base/Media/ColorUtils.cs @@ -98,17 +98,50 @@ public static class ColorUtils return FromRgbF(na, nr, ng, nb); } - public static bool IsStableColor(int color) + private static bool IsStableColor(int color) { return color >= 0 && color <= 255; } - - public static bool IsStableColor(double color) + + private static bool IsStableColor(double color) { return MathUtils.GreaterThanOrClose(color, 0.0d) && MathUtils.LessThanOrClose(color, 255.0d); } + + public static double GetRelativeLuminance(Color color) + { + // The equation for relative luminance is given by + // + // L = 0.2126 * Rg + 0.7152 * Gg + 0.0722 * Bg + // + // where Xg = { X/3294 if X <= 10, (R/269 + 0.0513)^2.4 otherwise } + // + // If L is closer to 1, then the color is closer to white; if it is closer to 0, + // then the color is closer to black. This is based on the fact that the human + // eye perceives green to be much brighter than red, which in turn is perceived to be + // brighter than blue. - public static Color AlphaColor(in Color frontColor, in Color backgroundColor) + double rg = color.R <= 10 ? color.R / 3294.0 : Math.Pow(color.R / 269.0 + 0.0513, 2.4); + double gg = color.G <= 10 ? color.G / 3294.0 : Math.Pow(color.G / 269.0 + 0.0513, 2.4); + double bg = color.B <= 10 ? color.B / 3294.0 : Math.Pow(color.B / 269.0 + 0.0513, 2.4); + + return (0.2126 * rg + 0.7152 * gg + 0.0722 * bg); + } + + public static bool IsBright(Color color) + { + var lum = GetRelativeLuminance(color); + return lum <= 0.5; + } + + public static bool IsBright(Color color, Color bgColor) + { + var resultColor = OnBackground(color, bgColor); + var lum = GetRelativeLuminance(resultColor); + return lum <= 0.5; + } + + public static Color CalculateAlphaColor(in Color frontColor, in Color backgroundColor) { var fR = frontColor.R; var fG = frontColor.G; @@ -221,9 +254,8 @@ public static class ColorUtils /// --------------------- /// public Color DefaultGhostBorderColor { get; set; } + + // 主要填充按钮的浅色背景颜色 + // Background color of primary filled button + + /// + /// 默认实心按钮的文本色 + /// Default text color for solid buttons. + /// + public Color SolidTextColor { get; set; } + + /// + /// 默认文本按钮的文本色 + /// Default text color for text buttons on hover + /// + public Color TextTextColor { get; set; } + + /// + /// 默认文本按钮悬浮态文本颜色 + /// Default text color for text buttons on hover + /// + public Color TextTextHoverColor { get; set; } + + /// + /// 默认文本按钮激活态文字颜色 + /// Default text color for text buttons on active + /// + public Color TextTextActiveColor { get; set; } /// /// 按钮内间距 @@ -315,7 +343,7 @@ internal class ButtonToken : AbstractControlDesignToken LinkHoverBg = Colors.Transparent; TextHoverBg = SharedToken.ColorBgTextHover; DefaultColor = SharedToken.ColorText; - DefaultBg = SharedToken.ColorTransparent; + DefaultBg = SharedToken.ColorBgContainer; DefaultBorderColor = SharedToken.ColorBorder; DefaultBorderColorDisabled = SharedToken.ColorBorder; DefaultHoverBg = SharedToken.ColorBgContainer; @@ -325,6 +353,20 @@ internal class ButtonToken : AbstractControlDesignToken DefaultActiveColor = SharedToken.ColorPrimaryActive; DefaultActiveBorderColor = SharedToken.ColorPrimaryActive; + var isBright = ColorUtils.IsBright(SharedToken.ColorBgSolid, Colors.White); + if (isBright) + { + SolidTextColor = Colors.Black; + } + else + { + SolidTextColor = Colors.White; + } + + TextTextColor = SharedToken.ColorText; + TextTextHoverColor = SharedToken.ColorText; + TextTextActiveColor = SharedToken.ColorText; + var controlHeightSM = SharedToken.ControlHeightSM; var controlHeight = SharedToken.ControlHeight; var controlHeightLG = SharedToken.ControlHeightLG; 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 4977ab2af..ffd40faa6 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 @@ -149,7 +149,11 @@ namespace AtomUI.Controls.DesignTokens public static readonly TokenResourceKey PaddingSM = new TokenResourceKey("Button.PaddingSM"); public static readonly TokenResourceKey PrimaryColor = new TokenResourceKey("Button.PrimaryColor"); public static readonly TokenResourceKey PrimaryShadow = new TokenResourceKey("Button.PrimaryShadow"); + public static readonly TokenResourceKey SolidTextColor = new TokenResourceKey("Button.SolidTextColor"); public static readonly TokenResourceKey TextHoverBg = new TokenResourceKey("Button.TextHoverBg"); + public static readonly TokenResourceKey TextTextActiveColor = new TokenResourceKey("Button.TextTextActiveColor"); + public static readonly TokenResourceKey TextTextColor = new TokenResourceKey("Button.TextTextColor"); + public static readonly TokenResourceKey TextTextHoverColor = new TokenResourceKey("Button.TextTextHoverColor"); } public static class ButtonSpinnerTokenKey diff --git a/src/AtomUI.Theme/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs b/src/AtomUI.Theme/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs index 747706649..31308df36 100644 --- a/src/AtomUI.Theme/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs +++ b/src/AtomUI.Theme/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs @@ -20,6 +20,9 @@ namespace AtomUI.Theme.Styling public static readonly TokenResourceKey ColorBgElevated = new TokenResourceKey("ColorBgElevated"); public static readonly TokenResourceKey ColorBgLayout = new TokenResourceKey("ColorBgLayout"); public static readonly TokenResourceKey ColorBgMask = new TokenResourceKey("ColorBgMask"); + public static readonly TokenResourceKey ColorBgSolid = new TokenResourceKey("ColorBgSolid"); + public static readonly TokenResourceKey ColorBgSolidActive = new TokenResourceKey("ColorBgSolidActive"); + public static readonly TokenResourceKey ColorBgSolidHover = new TokenResourceKey("ColorBgSolidHover"); public static readonly TokenResourceKey ColorBgSpotlight = new TokenResourceKey("ColorBgSpotlight"); public static readonly TokenResourceKey ColorBgTextActive = new TokenResourceKey("ColorBgTextActive"); public static readonly TokenResourceKey ColorBgTextHover = new TokenResourceKey("ColorBgTextHover"); diff --git a/src/AtomUI.Theme/Styling/DarkThemeVariantCalculator.cs b/src/AtomUI.Theme/Styling/DarkThemeVariantCalculator.cs index 030ff83ff..99ae9ab5d 100644 --- a/src/AtomUI.Theme/Styling/DarkThemeVariantCalculator.cs +++ b/src/AtomUI.Theme/Styling/DarkThemeVariantCalculator.cs @@ -66,37 +66,41 @@ public class DarkThemeVariantCalculator : AbstractThemeVariantCalculator } protected override void CalculateNeutralColorPalettes(Color? bgBaseColor, Color? textBaseColor, - DesignToken designToken) + DesignToken designToken) { var colorBgBase = bgBaseColor ?? _colorBgBase; var colorTextBase = textBaseColor ?? _colorTextBase; - designToken.ColorText = AlphaColor(colorTextBase, 0.85); - designToken.ColorTextSecondary = AlphaColor(colorTextBase, 0.65); - designToken.ColorTextTertiary = AlphaColor(colorTextBase, 0.45); - designToken.ColorTextQuaternary = AlphaColor(colorTextBase, 0.25); + designToken.ColorText = CalculateAlphaColor(colorTextBase, 0.85); + designToken.ColorTextSecondary = CalculateAlphaColor(colorTextBase, 0.65); + designToken.ColorTextTertiary = CalculateAlphaColor(colorTextBase, 0.45); + designToken.ColorTextQuaternary = CalculateAlphaColor(colorTextBase, 0.25); - designToken.ColorFill = AlphaColor(colorTextBase, 0.18); - designToken.ColorFillSecondary = AlphaColor(colorTextBase, 0.12); - designToken.ColorFillTertiary = AlphaColor(colorTextBase, 0.08); - designToken.ColorFillQuaternary = AlphaColor(colorTextBase, 0.04); + designToken.ColorFill = CalculateAlphaColor(colorTextBase, 0.18); + designToken.ColorFillSecondary = CalculateAlphaColor(colorTextBase, 0.12); + designToken.ColorFillTertiary = CalculateAlphaColor(colorTextBase, 0.08); + designToken.ColorFillQuaternary = CalculateAlphaColor(colorTextBase, 0.04); - designToken.ColorBgElevated = SolidColor(colorBgBase, 12); - designToken.ColorBgContainer = SolidColor(colorBgBase, 8); - designToken.ColorBgLayout = SolidColor(colorBgBase, 0); - designToken.ColorBgSpotlight = SolidColor(colorBgBase, 26); - designToken.ColorBgBlur = AlphaColor(colorTextBase, 0.04); + designToken.ColorBgSolid = CalculateAlphaColor(colorTextBase, 1); + designToken.ColorBgSolidHover = CalculateAlphaColor(colorTextBase, 0.75); + designToken.ColorBgSolidActive = CalculateAlphaColor(colorTextBase, 0.95); - designToken.ColorBorder = SolidColor(colorBgBase, 26); - designToken.ColorBorderSecondary = SolidColor(colorBgBase, 19); + designToken.ColorBgElevated = CalculateSolidColor(colorBgBase, 12); + designToken.ColorBgContainer = CalculateSolidColor(colorBgBase, 8); + designToken.ColorBgLayout = CalculateSolidColor(colorBgBase, 0); + designToken.ColorBgSpotlight = CalculateSolidColor(colorBgBase, 26); + designToken.ColorBgBlur = CalculateAlphaColor(colorTextBase, 0.04); + + designToken.ColorBorder = CalculateSolidColor(colorBgBase, 26); + designToken.ColorBorderSecondary = CalculateSolidColor(colorBgBase, 19); } - private Color AlphaColor(in Color baseColor, double alpha) + private Color CalculateAlphaColor(in Color baseColor, double alpha) { return Color.FromArgb((byte)(alpha * 255), baseColor.R, baseColor.G, baseColor.B); } - private Color SolidColor(Color baseColor, int brightness) + private Color CalculateSolidColor(Color baseColor, int brightness) { return baseColor.Lighten(brightness); } diff --git a/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.ColorNeutralMap.cs b/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.ColorNeutralMap.cs index cf94bb972..7bbc14d0d 100644 --- a/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.ColorNeutralMap.cs +++ b/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.ColorNeutralMap.cs @@ -99,4 +99,23 @@ public partial class DesignToken /// 控制毛玻璃容器的背景色,通常为透明色。 /// public Color ColorBgBlur { get; set; } + + // ---------- Solid ---------- // + /// + /// 实心的背景颜色,目前只用在默认实心按钮背景色上。 + /// Solid background color active state, currently only used in the active effect of the default solid button. + /// + public Color ColorBgSolid { get; set; } + + /// + /// 实心的背景颜色激活态,目前只用在默认实心按钮的 active 效果。 + /// Solid background color active state, currently only used in the active effect of the default solid button. + /// + public Color ColorBgSolidActive { get; set; } + + /// + /// 实心的背景颜色悬浮态,目前只用在默认实心按钮的 hover 效果。 + /// Solid background color hover state, currently only used in the hover effect of the default solid button. + /// + public Color ColorBgSolidHover { get; set; } } \ No newline at end of file diff --git a/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.cs b/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.cs index e15009827..1dab2acb3 100644 --- a/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.cs +++ b/src/AtomUI.Theme/TokenSystem/TokenDefinitions/DesignToken.cs @@ -77,7 +77,7 @@ public partial class DesignToken : AbstractDesignToken // ============== Split ============== // ColorBorderBg = ColorBgContainer; - ColorSplit = ColorUtils.AlphaColor(ColorBorderSecondary, ColorBgContainer); + ColorSplit = ColorUtils.CalculateAlphaColor(ColorBorderSecondary, ColorBgContainer); // ============== Text ============== // ColorTextPlaceholder = ColorTextQuaternary; @@ -93,8 +93,8 @@ public partial class DesignToken : AbstractDesignToken ColorIcon = ColorTextTertiary; ColorIconHover = ColorText; - ColorErrorOutline = ColorUtils.AlphaColor(ColorErrorBg, ColorBgContainer); - ColorWarningOutline = ColorUtils.AlphaColor(ColorWarningBg, ColorBgContainer); + ColorErrorOutline = ColorUtils.CalculateAlphaColor(ColorErrorBg, ColorBgContainer); + ColorWarningOutline = ColorUtils.CalculateAlphaColor(ColorWarningBg, ColorBgContainer); // Font FontSizeIcon = FontSizeSM; @@ -122,7 +122,7 @@ public partial class DesignToken : AbstractDesignToken ControlItemBgActive = ColorPrimaryBg; ControlItemBgActiveHover = ColorPrimaryBgHover; ControlItemBgActiveDisabled = ColorFill; - ColorControlOutline = ColorUtils.AlphaColor(ColorPrimaryBg, ColorBgContainer); + ColorControlOutline = ColorUtils.CalculateAlphaColor(ColorPrimaryBg, ColorBgContainer); FontWeightStrong = FontWeight.SemiBold; OpacityLoading = 0.65;