From 9727f0a9d8af6de773b9c0dccf6491a71cab7402 Mon Sep 17 00:00:00 2001 From: David Kallesen Date: Sun, 17 Dec 2023 11:24:50 +0100 Subject: [PATCH 1/2] feat: Improve GetColorFromHex & GetBrushFromHex --- src/Atc.Wpf/Helpers/ColorHelper.cs | 27 +++++++++++------- src/Atc.Wpf/Helpers/SolidColorBrushHelper.cs | 28 ++++++++++++------- .../Atc.Wpf.Tests/Helpers/ColorHelperTests.cs | 2 ++ .../Helpers/SolidColorBrushHelperTests.cs | 2 ++ 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/Atc.Wpf/Helpers/ColorHelper.cs b/src/Atc.Wpf/Helpers/ColorHelper.cs index 297c657..c6ddb7f 100644 --- a/src/Atc.Wpf/Helpers/ColorHelper.cs +++ b/src/Atc.Wpf/Helpers/ColorHelper.cs @@ -30,16 +30,21 @@ public static void InitializeWithSupportedLanguages() ArgumentException.ThrowIfNullOrEmpty(value); ArgumentNullException.ThrowIfNull(culture); - EnsureColorNamesForCulture(culture); - - var colorKey = GetColorKeyFromCulture(culture); - - if (value.StartsWith('#') && - ColorConverter.ConvertFromString(value) is Color color) + if (value.StartsWith('#')) { - return ColorNames[colorKey] - .FirstOrDefault(x => string.Equals(x.Key.ToString(GlobalizationConstants.EnglishCultureInfo), color.ToString(GlobalizationConstants.EnglishCultureInfo), StringComparison.OrdinalIgnoreCase)) - .Key; + try + { + if (ColorConverter.ConvertFromString(value) is Color color) + { + return color; + } + + return null; + } + catch + { + return null; + } } if (!value.Contains(' ', StringComparison.Ordinal)) @@ -52,7 +57,9 @@ public static void InitializeWithSupportedLanguages() } } - return ColorNames[colorKey] + EnsureColorNamesForCulture(culture); + + return ColorNames[GetColorKeyFromCulture(culture)] .FirstOrDefault(x => string.Equals(x.Value, value, StringComparison.OrdinalIgnoreCase)) .Key; } diff --git a/src/Atc.Wpf/Helpers/SolidColorBrushHelper.cs b/src/Atc.Wpf/Helpers/SolidColorBrushHelper.cs index eda40c5..9d7b64d 100644 --- a/src/Atc.Wpf/Helpers/SolidColorBrushHelper.cs +++ b/src/Atc.Wpf/Helpers/SolidColorBrushHelper.cs @@ -4,6 +4,7 @@ namespace Atc.Wpf.Helpers; /// /// A Helper class for the SolidColorBrush. /// +[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "OK.")] public static class SolidColorBrushHelper { private static readonly ConcurrentDictionary BaseBrushes = new(StringComparer.Ordinal); @@ -29,16 +30,21 @@ public static void InitializeWithSupportedLanguages() ArgumentException.ThrowIfNullOrEmpty(value); ArgumentNullException.ThrowIfNull(culture); - EnsureBrushNamesForCulture(culture); - - var brushKey = GetBrushKeyFromCulture(culture); - - if (value.StartsWith('#') && - ColorConverter.ConvertFromString(value) is Color color) + if (value.StartsWith('#')) { - return BrushNames[brushKey] - .FirstOrDefault(x => string.Equals(x.Key.Color.ToString(GlobalizationConstants.EnglishCultureInfo), color.ToString(GlobalizationConstants.EnglishCultureInfo), StringComparison.OrdinalIgnoreCase)) - .Key; + try + { + if (ColorConverter.ConvertFromString(value) is Color color) + { + return new SolidColorBrush(color); + } + + return null; + } + catch + { + return null; + } } if (!value.Contains(' ', StringComparison.Ordinal)) @@ -51,7 +57,9 @@ public static void InitializeWithSupportedLanguages() } } - return BrushNames[brushKey] + EnsureBrushNamesForCulture(culture); + + return BrushNames[GetBrushKeyFromCulture(culture)] .FirstOrDefault(x => string.Equals(x.Value, value, StringComparison.OrdinalIgnoreCase)) .Key; } diff --git a/test/Atc.Wpf.Tests/Helpers/ColorHelperTests.cs b/test/Atc.Wpf.Tests/Helpers/ColorHelperTests.cs index 49d1deb..55e1c30 100644 --- a/test/Atc.Wpf.Tests/Helpers/ColorHelperTests.cs +++ b/test/Atc.Wpf.Tests/Helpers/ColorHelperTests.cs @@ -34,6 +34,8 @@ public void GetBrushFromString(string input, byte r, byte g, byte b, int lcid) } [Theory] + [InlineData("#FF333333", 0x33, 0x33, 0x33)] + [InlineData("#FF666666", 0x66, 0x66, 0x66)] [InlineData("#FF00CED1", 0, 206, 209)] [InlineData("#00CED1", 0, 206, 209)] [InlineData("#FF2F4F4F", 47, 79, 79)] diff --git a/test/Atc.Wpf.Tests/Helpers/SolidColorBrushHelperTests.cs b/test/Atc.Wpf.Tests/Helpers/SolidColorBrushHelperTests.cs index bcf66c2..8826103 100644 --- a/test/Atc.Wpf.Tests/Helpers/SolidColorBrushHelperTests.cs +++ b/test/Atc.Wpf.Tests/Helpers/SolidColorBrushHelperTests.cs @@ -34,6 +34,8 @@ public void GetBrushFromString(string input, byte r, byte g, byte b, int lcid) } [Theory] + [InlineData("#FF333333", 0x33, 0x33, 0x33)] + [InlineData("#FF666666", 0x66, 0x66, 0x66)] [InlineData("#FF00CED1", 0, 206, 209)] [InlineData("#00CED1", 0, 206, 209)] [InlineData("#FF2F4F4F", 47, 79, 79)] From 404c89178b6410c952a44ba2c1d9c191409815fe Mon Sep 17 00:00:00 2001 From: David Kallesen Date: Sun, 17 Dec 2023 11:25:23 +0100 Subject: [PATCH 2/2] feat: Add ThemeManagerHelper --- .../Enums/AtcAppsBrushKeyType.cs | 39 +++++++++ .../Enums/AtcAppsColorKeyType.cs | 39 +++++++++ src/Atc.Wpf.Theming/GlobalUsings.cs | 1 + .../Helpers/ThemeManagerHelper.cs | 80 +++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 src/Atc.Wpf.Theming/Enums/AtcAppsBrushKeyType.cs create mode 100644 src/Atc.Wpf.Theming/Enums/AtcAppsColorKeyType.cs create mode 100644 src/Atc.Wpf.Theming/Helpers/ThemeManagerHelper.cs diff --git a/src/Atc.Wpf.Theming/Enums/AtcAppsBrushKeyType.cs b/src/Atc.Wpf.Theming/Enums/AtcAppsBrushKeyType.cs new file mode 100644 index 0000000..62b78f2 --- /dev/null +++ b/src/Atc.Wpf.Theming/Enums/AtcAppsBrushKeyType.cs @@ -0,0 +1,39 @@ +// ReSharper disable CheckNamespace +namespace Atc.Wpf.Theming; + +public enum AtcAppsBrushKeyType +{ + Highlight, + AccentBase, + Accent, + Accent2, + Accent3, + Accent4, + ThemeBackground, + ThemeBackground1, + ThemeBackground2, + ThemeBackground3, + ThemeBackground4, + ThemeBackground5, + ThemeBackground6, + ThemeBackground7, + ThemeForeground7, + ThemeForeground6, + ThemeForeground5, + ThemeForeground4, + ThemeForeground3, + ThemeForeground2, + ThemeForeground1, + ThemeForeground, + IdealForeground, + Gray1, + Gray2, + Gray3, + Gray4, + Gray5, + Gray6, + Gray7, + Gray8, + Gray9, + Gray10, +} \ No newline at end of file diff --git a/src/Atc.Wpf.Theming/Enums/AtcAppsColorKeyType.cs b/src/Atc.Wpf.Theming/Enums/AtcAppsColorKeyType.cs new file mode 100644 index 0000000..a5fd470 --- /dev/null +++ b/src/Atc.Wpf.Theming/Enums/AtcAppsColorKeyType.cs @@ -0,0 +1,39 @@ +// ReSharper disable CheckNamespace +namespace Atc.Wpf.Theming; + +public enum AtcAppsColorKeyType +{ + Highlight, + AccentBase, + Accent, + Accent2, + Accent3, + Accent4, + ThemeBackground, + ThemeBackground1, + ThemeBackground2, + ThemeBackground3, + ThemeBackground4, + ThemeBackground5, + ThemeBackground6, + ThemeBackground7, + ThemeForeground7, + ThemeForeground6, + ThemeForeground5, + ThemeForeground4, + ThemeForeground3, + ThemeForeground2, + ThemeForeground1, + ThemeForeground, + IdealForeground, + Gray1, + Gray2, + Gray3, + Gray4, + Gray5, + Gray6, + Gray7, + Gray8, + Gray9, + Gray10, +} \ No newline at end of file diff --git a/src/Atc.Wpf.Theming/GlobalUsings.cs b/src/Atc.Wpf.Theming/GlobalUsings.cs index 2c3101e..55b2bed 100644 --- a/src/Atc.Wpf.Theming/GlobalUsings.cs +++ b/src/Atc.Wpf.Theming/GlobalUsings.cs @@ -1,4 +1,5 @@ global using System.Collections; +global using System.Collections.Concurrent; global using System.Collections.ObjectModel; global using System.Collections.Specialized; global using System.ComponentModel; diff --git a/src/Atc.Wpf.Theming/Helpers/ThemeManagerHelper.cs b/src/Atc.Wpf.Theming/Helpers/ThemeManagerHelper.cs new file mode 100644 index 0000000..7368d91 --- /dev/null +++ b/src/Atc.Wpf.Theming/Helpers/ThemeManagerHelper.cs @@ -0,0 +1,80 @@ +namespace Atc.Wpf.Theming.Helpers; + +public static class ThemeManagerHelper +{ + private static readonly ConcurrentDictionary CacheColors = new(StringComparer.Ordinal); + private static readonly ConcurrentDictionary CacheBrushes = new(StringComparer.Ordinal); + + public static Color GetColorByResourceKey( + AtcAppsColorKeyType colorKey) + => GetColorByResourceKey(colorKey.ToString()); + + public static Color GetColorByResourceKey( + string resourceKey) + { + ArgumentException.ThrowIfNullOrEmpty(resourceKey); + + if (!resourceKey.StartsWith("AtcApps.Colors.", StringComparison.OrdinalIgnoreCase)) + { + resourceKey = $"AtcApps.Colors.{resourceKey}"; + } + + var currentTheme = ThemeManager.Current.DetectTheme(Application.Current)!; + var cacheKey = $"{currentTheme.BaseColorScheme}_{resourceKey}"; + + if (CacheColors.TryGetValue(cacheKey, out var cacheColor)) + { + return cacheColor; + } + + var color = (Color)currentTheme.Resources[resourceKey]!; + CacheColors.TryAdd(cacheKey, color); + return color; + } + + public static SolidColorBrush GetBrushByResourceKey( + AtcAppsBrushKeyType brushKey) + => GetBrushByResourceKey(brushKey.ToString()); + + public static SolidColorBrush GetBrushByResourceKey( + string resourceKey) + { + ArgumentException.ThrowIfNullOrEmpty(resourceKey); + + if (!resourceKey.StartsWith("AtcApps.Brushes.", StringComparison.OrdinalIgnoreCase)) + { + resourceKey = $"AtcApps.Brushes.{resourceKey}"; + } + + var currentTheme = ThemeManager.Current.DetectTheme(Application.Current)!; + var cacheKey = $"{currentTheme.BaseColorScheme}_{resourceKey}"; + + if (CacheBrushes.TryGetValue(cacheKey, out var cacheBrush)) + { + return cacheBrush; + } + + var brush = (SolidColorBrush)currentTheme.Resources[resourceKey]!; + if (brush.CanFreeze) + { + brush.Freeze(); + } + + CacheBrushes.TryAdd(cacheKey, brush); + return brush; + } + + public static Color GetPrimaryAccentColor() + { + var currentTheme = ThemeManager.Current.DetectTheme(Application.Current)!; + return currentTheme.PrimaryAccentColor; + } + + public static SolidColorBrush GetPrimaryAccentBrush() + { + var color = GetPrimaryAccentColor(); + var brush = new SolidColorBrush(color); + brush.Freeze(); + return brush; + } +} \ No newline at end of file