From 13c14e7360d032168b9a04f4fdd3089f800808bb Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 6 Jan 2023 22:40:23 -0500 Subject: [PATCH] Add ColorContrastPreference to the PlatformColorValues --- .../src/OSX/PlatformSettings.mm | 14 ++-- .../Platform/AndroidPlatformSettings.cs | 37 ++++++++--- .../Platform/DefaultPlatformSettings.cs | 5 +- .../Platform/PlatformColorValues.cs | 64 +++++++++++++------ .../DBusPlatformSettings.cs | 9 ++- src/Avalonia.Native/NativePlatformSettings.cs | 22 ++++++- src/Avalonia.Native/avn.idl | 4 +- .../BrowserPlatformSettings.cs | 13 ++-- .../Avalonia.Browser/Interop/DomHelper.cs | 6 +- .../webapp/modules/avalonia/dom.ts | 18 ++++-- src/Windows/Avalonia.Win32/Win32Platform.cs | 11 ++++ .../Avalonia.Win32/Win32PlatformSettings.cs | 45 ++++++++++--- .../WinRT/NativeWinRTMethods.cs | 26 +++++++- src/Windows/Avalonia.Win32/WinRT/winrt.idl | 7 ++ .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 8 --- src/iOS/Avalonia.iOS/PlatformSettings.cs | 11 +++- 16 files changed, 228 insertions(+), 72 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PlatformSettings.mm b/native/Avalonia.Native/src/OSX/PlatformSettings.mm index e709cd5135..fe88214c8d 100644 --- a/native/Avalonia.Native/src/OSX/PlatformSettings.mm +++ b/native/Avalonia.Native/src/OSX/PlatformSettings.mm @@ -17,15 +17,17 @@ public: if (@available(macOS 10.14, *)) { if (NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAqua - || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameVibrantLight - || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastAqua - || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastVibrantLight) { + || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameVibrantLight) { return AvnPlatformThemeVariant::Light; } else if (NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameDarkAqua - || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameVibrantDark - || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastDarkAqua - || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastVibrantDark) { + || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameVibrantDark) { return AvnPlatformThemeVariant::Dark; + } else if (NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastAqua + || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastVibrantLight) { + return AvnPlatformThemeVariant::HighContrastLight; + } else if (|| NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastDarkAqua + || NSApplication.sharedApplication.effectiveAppearance.name == NSAppearanceNameAccessibilityHighContrastVibrantDark) { + return AvnPlatformThemeVariant::HighContrastDark; } } return AvnPlatformThemeVariant::Light; diff --git a/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs b/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs index f560dea3ce..0872603e7b 100644 --- a/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs +++ b/src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs @@ -3,6 +3,8 @@ using Android; using Android.Content; using Android.Content.Res; using Android.Graphics; +using Android.Provider; +using Android.Views.Accessibility; using AndroidX.Core.Content.Resources; using Avalonia.Media; using Avalonia.Platform; @@ -46,11 +48,14 @@ internal class AndroidPlatformSettings : DefaultPlatformSettings var accent2 = context.Resources.GetColor(17170507, context.Theme); // Resource.Color.SystemAccent2500 var accent3 = context.Resources.GetColor(17170520, context.Theme); // Resource.Color.SystemAccent3500 - _latestValues = new PlatformColorValues( - systemTheme, - new Color(accent1.A, accent1.R, accent1.G, accent1.B), - new Color(accent2.A, accent2.R, accent2.G, accent2.B), - new Color(accent3.A, accent3.R, accent3.G, accent3.B)); + _latestValues = new PlatformColorValues + { + ThemeVariant = systemTheme, + ContrastPreference = IsHighContrast(context), + AccentColor1 = new Color(accent1.A, accent1.R, accent1.G, accent1.B), + AccentColor2 = new Color(accent2.A, accent2.R, accent2.G, accent2.B), + AccentColor3 = new Color(accent3.A, accent3.R, accent3.G, accent3.B), + }; } else if (OperatingSystem.IsAndroidVersionAtLeast(23)) { @@ -58,9 +63,12 @@ internal class AndroidPlatformSettings : DefaultPlatformSettings var array = context.Theme.ObtainStyledAttributes(new[] { 16843829 }); // Resource.Attribute.ColorAccent var accent = array.GetColor(0, 0); - _latestValues = new PlatformColorValues( - systemTheme, - new Color(accent.A, accent.R, accent.G, accent.B)); + _latestValues = new PlatformColorValues + { + ThemeVariant = systemTheme, + ContrastPreference = IsHighContrast(context), + AccentColor1 = new Color(accent.A, accent.R, accent.G, accent.B) + }; array.Recycle(); } else @@ -70,4 +78,17 @@ internal class AndroidPlatformSettings : DefaultPlatformSettings OnColorValuesChanged(_latestValues); } + + private static ColorContrastPreference IsHighContrast(Context context) + { + try + { + return Settings.Secure.GetInt(context.ContentResolver, "high_text_contrast_enabled", 0) == 1 + ? ColorContrastPreference.High : ColorContrastPreference.NoPreference; + } + catch + { + return ColorContrastPreference.NoPreference; + } + } } diff --git a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs index 98f2367819..b5e7298b7e 100644 --- a/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/DefaultPlatformSettings.cs @@ -31,7 +31,10 @@ namespace Avalonia.Platform public virtual PlatformColorValues GetColorValues() { - return new PlatformColorValues(PlatformThemeVariant.Light); + return new PlatformColorValues + { + ThemeVariant = PlatformThemeVariant.Light + }; } public event EventHandler? ColorValuesChanged; diff --git a/src/Avalonia.Base/Platform/PlatformColorValues.cs b/src/Avalonia.Base/Platform/PlatformColorValues.cs index 3071cc217f..d14b58e939 100644 --- a/src/Avalonia.Base/Platform/PlatformColorValues.cs +++ b/src/Avalonia.Base/Platform/PlatformColorValues.cs @@ -11,32 +11,58 @@ public enum PlatformThemeVariant Dark } +/// +/// System high contrast preference. +/// +public enum ColorContrastPreference +{ + NoPreference, + High +} + /// /// Information about current system color values, including information about dark mode and accent colors. /// -/// System theme variant or mode. -/// Primary system accent color. -/// Secondary system accent color. On some platforms can return the same value as AccentColor1. -/// Tertiary system accent color. On some platforms can return the same value as AccentColor1. -public record struct PlatformColorValues( - PlatformThemeVariant ThemeVariant, - Color AccentColor1, - Color AccentColor2, - Color AccentColor3) +public record PlatformColorValues { - public PlatformColorValues( - PlatformThemeVariant ThemeVariant, - Color AccentColor1) - : this(ThemeVariant, AccentColor1, AccentColor1, AccentColor1) + private static Color DefaultAccent => new(255, 0, 120, 215); + private Color _accentColor2, _accentColor3; + + /// + /// System theme variant or mode. + /// + public PlatformThemeVariant ThemeVariant { get; init; } + + /// + /// System high contrast preference. + /// + public ColorContrastPreference ContrastPreference { get; init; } + + /// + /// Primary system accent color. + /// + public Color AccentColor1 { get; init; } + + /// + /// Secondary system accent color. On some platforms can return the same value as . + /// + public Color AccentColor2 { - + get => _accentColor2 != default ? _accentColor2 : AccentColor1; + init => _accentColor2 = value; } - - public PlatformColorValues(PlatformThemeVariant ThemeVariant) - : this(ThemeVariant, DefaultAccent) + + /// + /// Tertiary system accent color. On some platforms can return the same value as . + /// + public Color AccentColor3 { + get => _accentColor3 != default ? _accentColor3 : AccentColor1; + init => _accentColor3 = value; + } + public PlatformColorValues() + { + AccentColor1 = DefaultAccent; } - - private static Color DefaultAccent => new(255, 0, 120, 215); } diff --git a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs index 82b4a72e74..039fc7c088 100644 --- a/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs +++ b/src/Avalonia.FreeDesktop/DBusPlatformSettings.cs @@ -41,7 +41,7 @@ internal class DBusPlatformSettings : DefaultPlatformSettings { var value = await colorSchemeTask; _lastColorValues = GetColorValuesFromSetting(value); - OnColorValuesChanged(_lastColorValues.Value); + OnColorValuesChanged(_lastColorValues); } catch (Exception ex) { @@ -62,13 +62,16 @@ internal class DBusPlatformSettings : DefaultPlatformSettings 2: Prefer light appearance */ _lastColorValues = GetColorValuesFromSetting(tuple.value); - OnColorValuesChanged(_lastColorValues.Value); + OnColorValuesChanged(_lastColorValues); } } private static PlatformColorValues GetColorValuesFromSetting(object value) { var isDark = value?.ToString() == "1"; - return new PlatformColorValues(isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light); + return new PlatformColorValues + { + ThemeVariant = isDark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light + }; } } diff --git a/src/Avalonia.Native/NativePlatformSettings.cs b/src/Avalonia.Native/NativePlatformSettings.cs index cea748bbae..53d3da0378 100644 --- a/src/Avalonia.Native/NativePlatformSettings.cs +++ b/src/Avalonia.Native/NativePlatformSettings.cs @@ -18,16 +18,32 @@ internal class NativePlatformSettings : DefaultPlatformSettings public override PlatformColorValues GetColorValues() { - var theme = (PlatformThemeVariant)_platformSettings.PlatformTheme; + var (theme, contrast) = _platformSettings.PlatformTheme switch + { + AvnPlatformThemeVariant.Dark => (PlatformThemeVariant.Dark, ColorContrastPreference.NoPreference), + AvnPlatformThemeVariant.Light => (PlatformThemeVariant.Light, ColorContrastPreference.NoPreference), + AvnPlatformThemeVariant.HighContrastDark => (PlatformThemeVariant.Dark, ColorContrastPreference.High), + AvnPlatformThemeVariant.HighContrastLight => (PlatformThemeVariant.Dark, ColorContrastPreference.High), + _ => throw new ArgumentOutOfRangeException() + }; var color = _platformSettings.AccentColor; if (color > 0) { - _lastColorValues = new PlatformColorValues(theme, Color.FromUInt32(color)); + _lastColorValues = new PlatformColorValues + { + ThemeVariant = theme, + ContrastPreference = contrast, + AccentColor1 = Color.FromUInt32(color) + }; } else { - _lastColorValues = new PlatformColorValues(theme); + _lastColorValues = new PlatformColorValues + { + ThemeVariant = theme, + ContrastPreference = contrast + }; } return _lastColorValues; diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 29f839c826..a062fdc61d 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -476,7 +476,9 @@ enum AvnWindowTransparencyMode enum AvnPlatformThemeVariant { Light, - Dark + Dark, + HighContrastLight, + HighContrastDark, } [uuid(809c652e-7396-11d2-9771-00a0c9b4d50c)] diff --git a/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs b/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs index 8a5a089752..6084c5c7de 100644 --- a/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs +++ b/src/Browser/Avalonia.Browser/BrowserPlatformSettings.cs @@ -6,21 +6,26 @@ namespace Avalonia.Browser; internal class BrowserPlatformSettings : DefaultPlatformSettings { private bool _isDarkMode; + private bool _isHighContrast; public BrowserPlatformSettings() { - _isDarkMode = DomHelper.ObserveDarkMode(m => + var obj = DomHelper.ObserveDarkMode((isDarkMode, isHighContrast) => { - _isDarkMode = m; + _isDarkMode = isDarkMode; + _isHighContrast = isHighContrast; OnColorValuesChanged(GetColorValues()); }); + _isDarkMode = obj.GetPropertyAsBoolean("isDarkMode"); + _isHighContrast = obj.GetPropertyAsBoolean("isHighContrast"); } - + public override PlatformColorValues GetColorValues() { return base.GetColorValues() with { - ThemeVariant = _isDarkMode ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light + ThemeVariant = _isDarkMode ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light, + ContrastPreference = _isHighContrast ? ColorContrastPreference.High : ColorContrastPreference.NoPreference }; } } diff --git a/src/Browser/Avalonia.Browser/Interop/DomHelper.cs b/src/Browser/Avalonia.Browser/Interop/DomHelper.cs index 090f03c4f1..9ee96768ec 100644 --- a/src/Browser/Avalonia.Browser/Interop/DomHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/DomHelper.cs @@ -27,7 +27,7 @@ internal static partial class DomHelper Action onDpiChanged); [JSImport("AvaloniaDOM.observeDarkMode", AvaloniaModule.MainModuleName)] - public static partial bool ObserveDarkMode( - [JSMarshalAs>] - Action onDpiChanged); + public static partial JSObject ObserveDarkMode( + [JSMarshalAs>] + Action onDpiChanged); } diff --git a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts index 9760fccc3b..b99f8e7907 100644 --- a/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts +++ b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts @@ -3,17 +3,25 @@ export class AvaloniaDOM { element.classList.add(className); } - static observeDarkMode(observer: (isDarkMode: boolean) => boolean): boolean { + static observeDarkMode(observer: (isDarkMode: boolean, isHighContrast: boolean) => boolean) { if (globalThis.matchMedia === undefined) { return false; } - const media = globalThis.matchMedia("(prefers-color-scheme: dark)"); - media.addEventListener("change", (args: MediaQueryListEvent) => { - observer(args.matches); + const colorShemeMedia = globalThis.matchMedia("(prefers-color-scheme: dark)"); + const prefersContrastMedia = globalThis.matchMedia("(prefers-contrast: more)"); + + colorShemeMedia.addEventListener("change", (args: MediaQueryListEvent) => { + observer(args.matches, prefersContrastMedia.matches); + }); + prefersContrastMedia.addEventListener("change", (args: MediaQueryListEvent) => { + observer(colorShemeMedia.matches, args.matches); }); - return media.matches; + return { + isDarkMode: colorShemeMedia.matches, + isHighContrast: prefersContrastMedia.matches + }; } static createAvaloniaHost(host: HTMLElement) { diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index c0a0ce4da0..26c9fc7abe 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -287,6 +287,17 @@ namespace Avalonia.Win32 } } } + + if (msg == (uint)WindowsMessage.WM_SETTINGCHANGE + && PlatformSettings is Win32PlatformSettings win32PlatformSettings) + { + var changedSetting = Marshal.PtrToStringAuto(lParam); + if (changedSetting == "ImmersiveColorSet" // dark/light mode + || changedSetting == "WindowsThemeElement") // high contrast mode + { + win32PlatformSettings.OnColorValuesChanged(); + } + } TrayIconImpl.ProcWnd(hWnd, msg, wParam, lParam); diff --git a/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs b/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs index 73ac5bf5a9..a021a3c48c 100644 --- a/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs +++ b/src/Windows/Avalonia.Win32/Win32PlatformSettings.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using Avalonia.Input; using Avalonia.Platform; using Avalonia.Win32.Interop; @@ -38,18 +39,44 @@ internal class Win32PlatformSettings : DefaultPlatformSettings return base.GetColorValues(); } - var settings = NativeWinRTMethods.CreateInstance("Windows.UI.ViewManagement.UISettings"); - var accent = settings.GetColorValue(UIColorType.Accent).ToAvalonia(); - var background = settings.GetColorValue(UIColorType.Background).ToAvalonia(); + var uiSettings = NativeWinRTMethods.CreateInstance("Windows.UI.ViewManagement.UISettings"); + var accent = uiSettings.GetColorValue(UIColorType.Accent).ToAvalonia(); - return _lastColorValues = new PlatformColorValues( - background.R + background.G + background.B < (255 * 3 - background.R - background.G - background.B) - ? PlatformThemeVariant.Dark - : PlatformThemeVariant.Light, - accent, accent, accent); + var accessibilitySettings = NativeWinRTMethods.CreateInstance("Windows.UI.ViewManagement.AccessibilitySettings"); + if (accessibilitySettings.HighContrast == 1) + { + // Windows 11 has 4 different high contrast schemes: + // - Aquatic - High Contrast Black + // - Desert - High Contrast White + // - Dusk - High Contrast #1 + // - Night sky - High Contrast #2 + // Only "Desert" one can be considered a "light" preference. + using var highContrastScheme = new HStringInterop(accessibilitySettings.HighContrastScheme); + return _lastColorValues = new PlatformColorValues + { + ThemeVariant = highContrastScheme.Value?.Contains("White", StringComparison.OrdinalIgnoreCase) == true ? + PlatformThemeVariant.Light : + PlatformThemeVariant.Dark, + ContrastPreference = ColorContrastPreference.High, + // Windows provides more than one accent color for the HighContrast themes, but with no API for that (at least not in the WinRT) + AccentColor1 = accent + }; + } + else + { + var background = uiSettings.GetColorValue(UIColorType.Background).ToAvalonia(); + return _lastColorValues = new PlatformColorValues + { + ThemeVariant = background.R + background.G + background.B < (255 * 3 - background.R - background.G - background.B) ? + PlatformThemeVariant.Dark : + PlatformThemeVariant.Light, + ContrastPreference = ColorContrastPreference.NoPreference, + AccentColor1 = accent + }; + } } - internal void OnColorValuesChanged(IntPtr handle) + internal void OnColorValuesChanged() { var oldColorValues = _lastColorValues; var colorValues = GetColorValues(); diff --git a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs index 1794b022fe..01b45f3df2 100644 --- a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs +++ b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs @@ -22,6 +22,9 @@ namespace Avalonia.Win32.WinRT internal static IntPtr WindowsCreateString(string sourceString) => WindowsCreateString(sourceString, sourceString.Length); + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + internal static extern unsafe char* WindowsGetStringRawBuffer(IntPtr hstring, uint* length); + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall, PreserveSig = false)] internal static extern unsafe void WindowsDeleteString(IntPtr hString); @@ -120,17 +123,38 @@ namespace Avalonia.Win32.WinRT class HStringInterop : IDisposable { private IntPtr _s; + private bool _owns; public HStringInterop(string s) { _s = s == null ? IntPtr.Zero : NativeWinRTMethods.WindowsCreateString(s); + _owns = true; + } + + public HStringInterop(IntPtr str, bool owns = false) + { + _s = str; + _owns = owns; } public IntPtr Handle => _s; + public unsafe string Value + { + get + { + if (_s == IntPtr.Zero) + return null; + + uint length; + var buffer = NativeWinRTMethods.WindowsGetStringRawBuffer(_s, &length); + return new string(buffer, 0, (int) length); + } + } + public void Dispose() { - if (_s != IntPtr.Zero) + if (_s != IntPtr.Zero && _owns) { NativeWinRTMethods.WindowsDeleteString(_s); _s = IntPtr.Zero; diff --git a/src/Windows/Avalonia.Win32/WinRT/winrt.idl b/src/Windows/Avalonia.Win32/WinRT/winrt.idl index 8dee275d50..beb88352dd 100644 --- a/src/Windows/Avalonia.Win32/WinRT/winrt.idl +++ b/src/Windows/Avalonia.Win32/WinRT/winrt.idl @@ -864,3 +864,10 @@ interface IUISettings3 : IInspectable { HRESULT GetColorValue([in] UIColorType desiredColor, [out][retval] Color* value); } + +[uuid(FE0E8147-C4C0-4562-B962-1327B52AD5B9)] +interface IAccessibilitySettings : IInspectable +{ + [propget] HRESULT HighContrast([out] [retval] boolean* value); + [propget] HRESULT HighContrastScheme([out] [retval] HSTRING* value); +} diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index af634fd760..8c362b0c29 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -732,14 +732,6 @@ namespace Avalonia.Win32 var node = AutomationNode.GetOrCreate(peer); return UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, wParam, lParam, node); } - break; - case WindowsMessage.WM_SETTINGCHANGE: - if (Marshal.PtrToStringAuto(lParam) == "ImmersiveColorSet" - && Win32Platform.PlatformSettings is Win32PlatformSettings win32PlatformSettings) - { - win32PlatformSettings.OnColorValuesChanged(_hwnd); - } - break; } diff --git a/src/iOS/Avalonia.iOS/PlatformSettings.cs b/src/iOS/Avalonia.iOS/PlatformSettings.cs index 736e94ffc6..b8557f31bd 100644 --- a/src/iOS/Avalonia.iOS/PlatformSettings.cs +++ b/src/iOS/Avalonia.iOS/PlatformSettings.cs @@ -17,8 +17,17 @@ internal class PlatformSettings : DefaultPlatformSettings var themeVariant = UITraitCollection.CurrentTraitCollection.UserInterfaceStyle == UIUserInterfaceStyle.Dark ? PlatformThemeVariant.Dark : PlatformThemeVariant.Light; + + + var contrastPreference = UITraitCollection.CurrentTraitCollection.AccessibilityContrast == UIAccessibilityContrast.High ? + ColorContrastPreference.High : + ColorContrastPreference.NoPreference; - return _lastColorValues = new PlatformColorValues(themeVariant); + return _lastColorValues = new PlatformColorValues + { + ThemeVariant = themeVariant, + ContrastPreference = contrastPreference + }; } public void TraitCollectionDidChange()