Skip to content

Fixes wrong accent colors #1492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 89 additions & 19 deletions src/Wpf.Ui/Appearance/ApplicationAccentColorManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using System.IO;
using System.Runtime.InteropServices;
using Wpf.Ui.Interop;
using Wpf.Ui.Win32;
using static Wpf.Ui.Appearance.UISettingsRCW;

namespace Wpf.Ui.Appearance;

Expand All @@ -28,6 +32,23 @@ namespace Wpf.Ui.Appearance;
/// </example>
public static class ApplicationAccentColorManager
{
private static readonly IUISettings3? _uisettings;
private static readonly bool _isSupported;

static ApplicationAccentColorManager()
{
try
{
_uisettings = GetWinRTInstance() as IUISettings3;
_isSupported = _uisettings != null;
}
catch (COMException)
{
// We don't want to throw any exceptions here.
// If we can't get the instance, we will use the fallback accent color.
}
}

/// <summary>
/// The maximum value of the background HSV brightness after which the text on the accent will be turned dark.
/// </summary>
Expand Down Expand Up @@ -149,18 +170,37 @@ public static void Apply(

if (applicationTheme == ApplicationTheme.Dark)
{
primaryAccent = systemAccent.Update(15f, -12f);
secondaryAccent = systemAccent.Update(30f, -24f);
tertiaryAccent = systemAccent.Update(45f, -36f);
primaryAccent = GetColor(UIColorType.AccentLight1, 17, -30f);
secondaryAccent = GetColor(UIColorType.AccentLight2, 17, -45f);
tertiaryAccent = GetColor(UIColorType.AccentLight3, 17, -65f);
}
else
{
primaryAccent = systemAccent.UpdateBrightness(-5f);
secondaryAccent = systemAccent.UpdateBrightness(-10f);
tertiaryAccent = systemAccent.UpdateBrightness(-15f);
primaryAccent = GetColor(UIColorType.AccentDark1, -10);
secondaryAccent = GetColor(UIColorType.AccentDark2, -25);
tertiaryAccent = GetColor(UIColorType.AccentDark3, -40);
}

UpdateColorResources(systemAccent, primaryAccent, secondaryAccent, tertiaryAccent);
UpdateColorResources(applicationTheme, systemAccent, primaryAccent, secondaryAccent, tertiaryAccent);

Color GetColor(UIColorType colorType, float brightnessFactor, float saturationFactor = 0.0f)
{
if (_isSupported)
{
try
{
var uiColor = _uisettings!.GetColorValue(colorType);
return Color.FromArgb(uiColor.A, uiColor.R, uiColor.G, uiColor.B);
}
catch (COMException)
{
// We don't want to throw any exceptions here.
// If we can't get the instance, we will use the default accent color.
}
}

return systemAccent.Update(brightnessFactor, saturationFactor);
}
}

/// <summary>
Expand All @@ -177,7 +217,7 @@ public static void Apply(
Color tertiaryAccent
)
{
UpdateColorResources(systemAccent, primaryAccent, secondaryAccent, tertiaryAccent);
UpdateColorResources(ApplicationThemeManager.GetAppTheme(), systemAccent, primaryAccent, secondaryAccent, tertiaryAccent);
}

/// <summary>
Expand All @@ -200,12 +240,13 @@ public static Color GetColorizationColor()
/// <summary>
/// Updates application resources.
/// </summary>
private static void UpdateColorResources(
private static void UpdateColorResources
(
ApplicationTheme applicationTheme,
Color systemAccent,
Color primaryAccent,
Color secondaryAccent,
Color tertiaryAccent
)
Color tertiaryAccent)
{
System.Diagnostics.Debug.WriteLine("INFO | SystemAccentColor: " + systemAccent, "Wpf.Ui.Accent");
System.Diagnostics.Debug.WriteLine(
Expand Down Expand Up @@ -295,16 +336,45 @@ Color tertiaryAccent
UiApplication.Current.Resources["SystemAccentColorSecondary"] = secondaryAccent;
UiApplication.Current.Resources["SystemAccentColorTertiary"] = tertiaryAccent;

UiApplication.Current.Resources["SystemAccentBrush"] = secondaryAccent.ToBrush();
UiApplication.Current.Resources["SystemAccentBrush"] = systemAccent.ToBrush();
UiApplication.Current.Resources["SystemFillColorAttentionBrush"] = secondaryAccent.ToBrush();
UiApplication.Current.Resources["AccentTextFillColorPrimaryBrush"] = tertiaryAccent.ToBrush();

UiApplication.Current.Resources["AccentTextFillColorPrimaryBrush"] = secondaryAccent.ToBrush();
UiApplication.Current.Resources["AccentTextFillColorSecondaryBrush"] = tertiaryAccent.ToBrush();
UiApplication.Current.Resources["AccentTextFillColorTertiaryBrush"] = secondaryAccent.ToBrush();
UiApplication.Current.Resources["AccentFillColorSelectedTextBackgroundBrush"] =
systemAccent.ToBrush();
UiApplication.Current.Resources["AccentFillColorDefaultBrush"] = secondaryAccent.ToBrush();
UiApplication.Current.Resources["AccentTextFillColorTertiaryBrush"] = primaryAccent.ToBrush();

UiApplication.Current.Resources["AccentFillColorSelectedTextBackgroundBrush"] = systemAccent.ToBrush();
Comment on lines +339 to +346
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


var themeAccent = applicationTheme == ApplicationTheme.Dark ? secondaryAccent : primaryAccent;
UiApplication.Current.Resources["AccentFillColorDefault"] = themeAccent;
UiApplication.Current.Resources["AccentFillColorDefaultBrush"] = themeAccent.ToBrush();
UiApplication.Current.Resources["AccentFillColorSecondary"] = Color.FromArgb(229, themeAccent.R, themeAccent.G, themeAccent.B); // 229 = 0.9 * 255
UiApplication.Current.Resources["AccentFillColorSecondaryBrush"] = themeAccent.ToBrush(0.9);
UiApplication.Current.Resources["AccentFillColorTertiary"] = Color.FromArgb(204, themeAccent.R, themeAccent.G, themeAccent.B); // 204 = 0.8 * 255
UiApplication.Current.Resources["AccentFillColorTertiaryBrush"] = themeAccent.ToBrush(0.8);
}

/// <summary>
/// Gets the WinRT instance of UISettings.
/// </summary>
private static object? GetWinRTInstance()
{
if (!Utilities.IsOSWindows10OrNewer)
{
return null;
}

object? winRtInstance;

try
{
winRtInstance = GetUISettingsInstance();
}
catch (Exception e) when (e is TypeLoadException or FileNotFoundException)
{
winRtInstance = null;
}

UiApplication.Current.Resources["AccentFillColorSecondaryBrush"] = secondaryAccent.ToBrush(0.9);
UiApplication.Current.Resources["AccentFillColorTertiaryBrush"] = secondaryAccent.ToBrush(0.8);
return winRtInstance;
}
}
89 changes: 89 additions & 0 deletions src/Wpf.Ui/Appearance/UISettingsRCW.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using System.Runtime.InteropServices;

namespace Wpf.Ui.Appearance;

/// <summary>
/// Contains internal RCWs for invoking the UISettings
/// </summary>
internal static class UISettingsRCW
{
public enum UIColorType
{
Background = 0,
Foreground = 1,
AccentDark3 = 2,
AccentDark2 = 3,
AccentDark1 = 4,
Accent = 5,
AccentLight1 = 6,
AccentLight2 = 7,
AccentLight3 = 8,
Complement = 9
}

public static object GetUISettingsInstance()
{
const string typeName = "Windows.UI.ViewManagement.UISettings";

int hr = NativeMethods.WindowsCreateString(typeName, typeName.Length, out IntPtr hstring);
Marshal.ThrowExceptionForHR(hr);

try
{
hr = NativeMethods.RoActivateInstance(hstring, out object instance);
Marshal.ThrowExceptionForHR(hr);
return instance;
}
finally
{
hr = NativeMethods.WindowsDeleteString(hstring);
Marshal.ThrowExceptionForHR(hr);
}
}

/// <summary>
/// Contains internal RCWs for invoking the InputPane (tiptsf touch keyboard)
/// </summary>
internal static class NativeMethods
{
[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern int WindowsCreateString(
[MarshalAs(UnmanagedType.LPWStr)] string sourceString,
int length,
out IntPtr hstring);

[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern int WindowsDeleteString(IntPtr hstring);

[DllImport("api-ms-win-core-winrt-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern int RoActivateInstance(IntPtr runtimeClassId, [MarshalAs(UnmanagedType.Interface)] out object instance);
}

[Guid("03021BE4-5254-4781-8194-5168F7D06D7B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
internal interface IUISettings3
{
void GetIids(out uint iidCount, out IntPtr iids);

void GetRuntimeClassName(out string className);

void GetTrustLevel(out TrustLevel TrustLevel);

UIColor GetColorValue(UIColorType desiredColor);
}

internal enum TrustLevel
{
BaseTrust,
PartialTrust,
FullTrust
}

internal readonly record struct UIColor(byte A, byte R, byte G, byte B);
}
16 changes: 8 additions & 8 deletions src/Wpf.Ui/Resources/Accent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<!-- Colors depending on the theme should be changed by the Manager -->
<Color x:Key="SystemAccentColor">#3379d9</Color>
<Color x:Key="SystemAccentColor">#0078d4</Color>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is based on Windows' Blue default accent color.


<!-- While the name remains Light to stay with the official nomenclature, it's made dark in Dark Theme -->

<!-- SystemAccentColorDark1 | SystemAccentColorLight1 -->
<Color x:Key="SystemAccentColorPrimary">#559ce4</Color>
<Color x:Key="SystemAccentColorPrimary">#0067c0</Color>
<!-- SystemAccentColorDark2 | SystemAccentColorLight2 -->
<Color x:Key="SystemAccentColorSecondary">#80b9ee</Color>
<Color x:Key="SystemAccentColorSecondary">#003e92</Color>
<!-- SystemAccentColorDark3 | SystemAccentColorLight3 -->
<Color x:Key="SystemAccentColorTertiary">#add8ff</Color>
<Color x:Key="SystemAccentColorTertiary">#001a68</Color>

<SolidColorBrush x:Key="SystemAccentColorBrush" Color="{StaticResource SystemAccentColor}" />

Expand All @@ -31,17 +31,17 @@

<SolidColorBrush x:Key="AccentFillColorSelectedTextBackgroundBrush" Color="{StaticResource SystemAccentColor}" />

<SolidColorBrush x:Key="AccentFillColorDefaultBrush" Color="{StaticResource SystemAccentColorPrimary}" />
<SolidColorBrush x:Key="AccentFillColorDefaultBrush" Color="{StaticResource SystemAccentColorSecondary}" />
<SolidColorBrush
x:Key="AccentFillColorSecondaryBrush"
Opacity="0.9"
Color="{StaticResource SystemAccentColorPrimary}" />
Color="{StaticResource SystemAccentColorSecondary}" />
<SolidColorBrush
x:Key="AccentFillColorTertiaryBrush"
Opacity="0.8"
Color="{StaticResource SystemAccentColorPrimary}" />
<SolidColorBrush x:Key="AccentFillColorDisabledBrush" Color="{StaticResource AccentFillColorDisabled}" />
Color="{StaticResource SystemAccentColorSecondary}" />

<SolidColorBrush x:Key="AccentFillColorDisabledBrush" Color="{StaticResource AccentFillColorDisabled}" />

<SolidColorBrush x:Key="SystemFillColorAttentionBrush" Color="{StaticResource SystemAccentColor}" />
</ResourceDictionary>