-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Expand file tree
/
Copy pathSwatchesProvider.cs
More file actions
105 lines (92 loc) · 4.16 KB
/
SwatchesProvider.cs
File metadata and controls
105 lines (92 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Resources;
using System.Text.RegularExpressions;
using System.Windows.Media;
namespace MaterialDesignColors;
/// <summary>
/// Provides access to all colour swatches. For information regarding Material Design colours see https://www.google.com/design/spec/style/color.html
/// </summary>
public class SwatchesProvider
{
private static readonly object _syncLock = new();
/// <summary>
/// Generates an instance reading swatches from the provided assembly, allowing
/// colours outside of the standard material palette to be loaded provided the are stored in the expected XAML format.
/// </summary>
/// <param name="assembly"></param>
public SwatchesProvider(Assembly assembly)
{
string resourcesName = assembly.GetName().Name + ".g";
var manager = new ResourceManager(resourcesName, assembly);
var resourceSet = manager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
var dictionaryEntries = resourceSet?.OfType<DictionaryEntry>().ToList();
string? assemblyName = assembly.GetName().Name;
var regex = new Regex(@"^themes\/materialdesigncolor\.(?<name>[a-z]+)\.(?<type>primary|secondary)\.baml$");
Swatches =
dictionaryEntries?
.Select(x => new { key = x.Key.ToString(), match = regex.Match(x.Key.ToString() ?? "") })
.Where(x => x.match.Success && x.match.Groups["name"].Value != "black")
.GroupBy(x => x.match.Groups["name"].Value)
.Select(x =>
CreateSwatch
(
x.Key,
Read(assemblyName, x.SingleOrDefault(y => y.match.Groups["type"].Value == "primary")?.key),
Read(assemblyName, x.SingleOrDefault(y => y.match.Groups["type"].Value == "secondary")?.key)
))
.ToList() ?? [];
}
/// <summary>
/// Creates a new swatch provider based on standard Material Design colors.
/// </summary>
public SwatchesProvider() : this(Assembly.GetExecutingAssembly())
{ }
public IEnumerable<Swatch> Swatches { get; }
private static Swatch CreateSwatch(string name, ResourceDictionary? primaryDictionary, ResourceDictionary? secondaryDictionary)
{
return new Swatch(name, GetHues(primaryDictionary), GetHues(secondaryDictionary));
static List<Hue> GetHues(ResourceDictionary? resourceDictionary)
{
var hues = new List<Hue>();
if (resourceDictionary != null)
{
foreach (var entry in resourceDictionary.OfType<DictionaryEntry>()
.OrderBy(de => de.Key)
.Where(de => !(de.Key.ToString() ?? "").EndsWith("Foreground", StringComparison.Ordinal)))
{
hues.Add(GetHue(resourceDictionary, entry));
}
}
return hues;
}
static Hue GetHue(ResourceDictionary dictionary, DictionaryEntry entry)
{
if (entry.Value is not Color colour)
{
throw new InvalidOperationException($"Entry {entry.Key} was not of type {nameof(Color)}");
}
string foregroundKey = entry.Key?.ToString() + "Foreground";
if (dictionary.OfType<DictionaryEntry>()
.Single(de => string.Equals(de.Key.ToString(), foregroundKey, StringComparison.Ordinal))
.Value is not Color foregroundColour)
{
throw new InvalidOperationException($"Entry {foregroundKey} was not of type {nameof(Color)}");
}
return new Hue(entry.Key?.ToString() ?? "", colour, foregroundColour);
}
}
private static ResourceDictionary? Read(string? assemblyName, string? path)
{
if (assemblyName is null || path is null)
return null;
lock (_syncLock)
{
//NB: Application.LoadComponent is not thread safe
return (ResourceDictionary)Application.LoadComponent(new Uri(
$"/{assemblyName};component/{path.Replace(".baml", ".xaml")}",
UriKind.RelativeOrAbsolute));
}
}
}