Browse Source

Implement SystemAccentColors backed by the actual system values

pull/11097/head
Max Katz 3 years ago
parent
commit
a09c182e89
  1. 2
      samples/ControlCatalog/App.xaml
  2. 48
      samples/ControlCatalog/MainView.xaml.cs
  3. 12
      src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml
  4. 163
      src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs
  5. 2
      src/Avalonia.Themes.Fluent/FluentTheme.xaml
  6. 12
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs

2
samples/ControlCatalog/App.xaml

@ -26,8 +26,6 @@
<Color x:Key="CatalogBaseHighColor">#FFFFFFFF</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Color x:Key="SystemAccentColor">#FF0078D7</Color>
<Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
<!-- Styles attached dynamically depending on current theme (simple or fluent) -->
<StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />

48
samples/ControlCatalog/MainView.xaml.cs

@ -19,13 +19,9 @@ namespace ControlCatalog
{
public class MainView : UserControl
{
private readonly IPlatformSettings _platformSettings;
public MainView()
{
AvaloniaXamlLoader.Load(this);
_platformSettings = AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>();
PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
var sideBar = this.Get<TabControl>("Sidebar");
@ -141,50 +137,6 @@ namespace ControlCatalog
ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
};
}
_platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
_platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
}
private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
{
Application.Current!.Resources["SystemAccentColor"] = e.AccentColor1;
Application.Current.Resources["SystemAccentColorDark1"] = ChangeColorLuminosity(e.AccentColor1, -0.3);
Application.Current.Resources["SystemAccentColorDark2"] = ChangeColorLuminosity(e.AccentColor1, -0.5);
Application.Current.Resources["SystemAccentColorDark3"] = ChangeColorLuminosity(e.AccentColor1, -0.7);
Application.Current.Resources["SystemAccentColorLight1"] = ChangeColorLuminosity(e.AccentColor1, 0.3);
Application.Current.Resources["SystemAccentColorLight2"] = ChangeColorLuminosity(e.AccentColor1, 0.5);
Application.Current.Resources["SystemAccentColorLight3"] = ChangeColorLuminosity(e.AccentColor1, 0.7);
static Color ChangeColorLuminosity(Color color, double luminosityFactor)
{
var red = (double)color.R;
var green = (double)color.G;
var blue = (double)color.B;
if (luminosityFactor < 0)
{
luminosityFactor = 1 + luminosityFactor;
red *= luminosityFactor;
green *= luminosityFactor;
blue *= luminosityFactor;
}
else if (luminosityFactor >= 0)
{
red = (255 - red) * luminosityFactor + red;
green = (255 - green) * luminosityFactor + green;
blue = (255 - blue) * luminosityFactor + blue;
}
return new Color(color.A, (byte)red, (byte)green, (byte)blue);
}
}
}
}

12
src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml

@ -1,12 +0,0 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Accent Colours -->
<!-- TODO pull accents from system... algorithm to generate shades -->
<Color x:Key="SystemAccentColor">#FF0078D7</Color>
<Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
<Color x:Key="SystemAccentColorDark2">#FF004275</Color>
<Color x:Key="SystemAccentColorDark3">#FF002642</Color>
<Color x:Key="SystemAccentColorLight1">#FF429CE3</Color>
<Color x:Key="SystemAccentColorLight2">#FF76B9ED</Color>
<Color x:Key="SystemAccentColorLight3">#FFA6D8FF</Color>
</ResourceDictionary>

163
src/Avalonia.Themes.Fluent/Accents/SystemAccentColors.cs

@ -0,0 +1,163 @@
using System;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
namespace Avalonia.Themes.Fluent.Accents;
internal class SystemAccentColors : IResourceProvider
{
public const string AccentKey = "SystemAccentColor";
public const string AccentDark1Key = "SystemAccentColorDark1";
public const string AccentDark2Key = "SystemAccentColorDark2";
public const string AccentDark3Key = "SystemAccentColorDark3";
public const string AccentLight1Key = "SystemAccentColorLight1";
public const string AccentLight2Key = "SystemAccentColorLight2";
public const string AccentLight3Key = "SystemAccentColorLight3";
private static readonly Color s_defaultSystemAccentColor = Color.FromRgb(0, 120, 215);
private readonly IPlatformSettings? _platformSettings;
private bool _invalidateColors = true;
private Color _systemAccentColor;
private Color _systemAccentColorDark1, _systemAccentColorDark2, _systemAccentColorDark3;
private Color _systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3;
public SystemAccentColors()
{
_platformSettings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
}
public bool HasResources => true;
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
if (key is string strKey)
{
if (strKey.Equals(AccentKey, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColor;
return true;
}
if (strKey.Equals(AccentDark1Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorDark1;
return true;
}
if (strKey.Equals(AccentDark2Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorDark2;
return true;
}
if (strKey.Equals(AccentDark3Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorDark3;
return true;
}
if (strKey.Equals(AccentLight1Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorLight1;
return true;
}
if (strKey.Equals(AccentLight2Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorLight2;
return true;
}
if (strKey.Equals(AccentLight3Key, StringComparison.InvariantCulture))
{
EnsureColors();
value = _systemAccentColorLight3;
return true;
}
}
value = null;
return false;
}
public IResourceHost? Owner { get; private set; }
public event EventHandler? OwnerChanged;
public void AddOwner(IResourceHost owner)
{
if (Owner != owner)
{
Owner = owner;
OwnerChanged?.Invoke(this, EventArgs.Empty);
if (_platformSettings is not null)
{
_platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
}
}
}
public void RemoveOwner(IResourceHost owner)
{
if (Owner == owner)
{
Owner = null;
OwnerChanged?.Invoke(this, EventArgs.Empty);
if (_platformSettings is not null)
{
_platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
}
}
}
private void EnsureColors()
{
if (_invalidateColors)
{
_invalidateColors = false;
_systemAccentColor = _platformSettings?.GetColorValues().AccentColor1 ?? s_defaultSystemAccentColor;
(_systemAccentColorDark1,_systemAccentColorDark2, _systemAccentColorDark3,
_systemAccentColorLight1, _systemAccentColorLight2, _systemAccentColorLight3) = CalculateAccentShades(_systemAccentColor);
}
}
public static (Color d1, Color d2, Color d3, Color l1, Color l2, Color l3) CalculateAccentShades(Color accentColor)
{
// dark1step = (hslAccent.L - SystemAccentColorDark1.L) * 255
const double dark1step = 28.5 / 255d;
const double dark2step = 49 / 255d;
const double dark3step = 74.5 / 255d;
// light1step = (SystemAccentColorLight1.L - hslAccent.L) * 255
const double light1step = 39 / 255d;
const double light2step = 70 / 255d;
const double light3step = 103 / 255d;
var hslAccent = accentColor.ToHsl();
return (
// Darker shades
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark1step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark2step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L - dark3step).ToRgb(),
// Lighter shades
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light1step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light2step).ToRgb(),
new HslColor(hslAccent.A, hslAccent.H, hslAccent.S, hslAccent.L + light3step).ToRgb()
);
}
private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
{
_invalidateColors = true;
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
}

2
src/Avalonia.Themes.Fluent/FluentTheme.xaml

@ -4,9 +4,9 @@
<Styles.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source="/Accents/AccentColors.xaml" />
<MergeResourceInclude Source="/Accents/Base.xaml" />
<MergeResourceInclude Source="/Accents/FluentControlResources.xaml" />
<ResourceInclude Source="/Accents/BaseColorsPalette.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- These are not part of MergedDictionaries so we can add or remove them easier -->

12
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs

@ -24,7 +24,6 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude;
var mergeSourceNodes = new List<XamlPropertyAssignmentNode>();
var hasAnyNonMergedResource = false;
foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray())
{
void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode)
@ -47,17 +46,6 @@ internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
valueNode);
}
}
else
{
hasAnyNonMergedResource = true;
}
if (hasAnyNonMergedResource && mergeSourceNodes.Any())
{
throw new XamlDocumentParseException(context.CurrentDocument,
"Mix of MergeResourceInclude and other dictionaries inside of the ResourceDictionary.MergedDictionaries is not allowed",
valueNode);
}
}
}

Loading…
Cancel
Save