Skip to content

Commit 8952558

Browse files
JesseColJesse CollinsMikeHillberg
authored
API Spec for Microsoft.UI.ThemeSettings class (#3640)
Adds the API spec for the Microsoft.UI.ThemeSettings class. API reviewed on 5/25/23. Co-authored-by: Jesse Collins <Jesse.Collins@microsoft.com> Co-authored-by: Mike Hillberg <18429489+MikeHillberg@users.noreply.github.com>
1 parent 607be88 commit 8952558

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

specs/themes/HighContrastThemes.gif

528 KB
Loading

specs/themes/ThemeSettings.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
ThemeSettings
2+
===
3+
4+
# Background
5+
_This section is only for internal use, it should not be part of the public documentation._
6+
7+
In UWP, apps can use the
8+
[AccessibilitySettings](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.accessibilitysettings?view=winrt-22621)
9+
API to query the system's high-contrast setting, and detect when it changes. But in a Win32/non-UWP context, the
10+
**AccessibilitySettings.HighContrastChanged** event never fires. This is because the event relies on the **CoreWindow**
11+
object to handle the `WM_THEMECHANGED` message and fire an internal event to the **AccessibilitySettings** object -- but
12+
in a non-UWP, Win32/WinAppSDK context, no **CoreWindow** exists.
13+
14+
The API described in this doc, **ThemeSettings**, is a WinAppSDK WinRT API that provides the functionality of
15+
**AccessibilitySettings**, but for a Win32 environment. In the future we'd like to add other kinds of theme-related
16+
information to this API, so we decided to use a name that's not specific to accessibility or high-contrast.
17+
18+
The **ThemeSettings.HighContrast** and **ThemeSettings.HighContrastScheme** properties shown below are basically identical to the
19+
existing UWP [AccessibilitySettings.HighContrast](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.accessibilitysettings.highcontrast?view=winrt-22621)
20+
and [AccessibilitySettings.HighContrastScheme](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.accessibilitysettings.highcontrastscheme?view=winrt-22621)
21+
properties. They're all implemented by calling the Win32 [SystemParametersInfo](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow)
22+
function with the `SPI_GETHIGHCONTRAST` flag.
23+
24+
Note that **HighContrastScheme** will, like **AccessibilitySettings.HighContrastScheme**, continue to return a valid
25+
string value even when **HighContrast** is false. The Win10 Settings UI had two controls, a toggle for high-contrast
26+
enablement and a combo box to select the HC theme, the UWP **AccessibilitySettings** API was probably designed to match
27+
that. Rather than change the model, we stick with what the UWP API already exposed, since we know it works for app
28+
developers and can be implemented very simply on Windows.
29+
30+
This GIF shows how a user can change the HighContrast theme in the Settings app today, and a System Xaml app that displays the current **HighContrastScheme**:
31+
32+
![Choosing high contast themes in the settings app](HighContrastThemes.gif)
33+
34+
### Why does this API require a WindowId?
35+
This API requires a **WindowId** object because the implementation listens for a `WM_THEMECHANGED` message. (This is
36+
the same message the **CoreWindow** listens for that drives the UWP **AccessibilitySettings.HighContrastChanged** event).
37+
An alternative option is for the API to create and manage its own top-level window, but we decided to use an existing
38+
window so we could have better control over the order in which these kinds of WinAppSDK events are fired.
39+
40+
### Future directions
41+
The expectation is to add more theme APIs here that we should have full APIs for:
42+
43+
* We need an API for light/dark mode, which currently you have to read from the registry.
44+
* System colors currently exposed by GetSysColor (Win32).
45+
* Accent colors currently exposed by UISettings.GetColorValue.
46+
47+
# API Pages
48+
49+
## ThemeSettings class
50+
The **ThemeSettings** API allows Win32 WinRT apps to detect that the system's High Contrast setting
51+
has changed.
52+
53+
In this sample code, an app uses a **ThemeSettings** object to listen for high contrast changes
54+
on the system:
55+
56+
```c++
57+
void MyApp::ListenForHighContrastChange(XamlRoot& xamlRoot)
58+
{
59+
const auto myWindowId = xamlRoot.ContentEnvironment().AppWindowId();
60+
m_themeSettings = ThemeSettings::CreateForWindowId(myWindowId);
61+
62+
m_themeSettings.Changed([xamlRoot](const ThemeSettings& ts, const auto&) {
63+
if (ts.HighContrast())
64+
{
65+
::OutputDebugString(L"High contrast is ON, scheme is: ");
66+
::OutputDebugString(ts.HighContrastScheme().c_str());
67+
}
68+
else
69+
{
70+
::OutputDebugString(L"High contrast is OFF.\n");
71+
}
72+
});
73+
}
74+
```
75+
76+
When the app releases all its references to a **ThemeSettings** object, the object will be destroyed and the **Changed** event will
77+
no longer fire.
78+
79+
### Methods
80+
|Name|Description|
81+
|-|-|
82+
|static CreateForWindowId(WindowId)|Creates a ThemeSettings object that will provide theme information for the given WindowId.|
83+
84+
### Events
85+
|Name|Description|
86+
|-|-|
87+
|Changed|Fired when one of the object's properties has changed.|
88+
89+
### Properties
90+
| Name | Description |
91+
|-|-|
92+
| Boolean HighContrast | True if High Contrast mode is enabled on the system. |
93+
| String HighContrastScheme | If HighContrast is true, a string from the system representing the High Contrast scheme, or style. |
94+
95+
## ThemeSettings.CreateForWindowId static method
96+
97+
``` c#
98+
static CreateForWindowId(WindowId windowId)
99+
```
100+
101+
Creates a **ThemeSettings** object that will provide theme information for the given **WindowId**.
102+
103+
The given **WindowId** must represent a window that:
104+
* is a top-level window,
105+
* AND is on the same process and thread as the caller.
106+
107+
For example, you can get a **WindowId** from the **ContentEnvironment.AppWindowId** property. You can get a **ContentEnvironment**
108+
object from a **XamlRoot** object.
109+
110+
## ThemeSettings.Changed event
111+
112+
```c#
113+
event TypedEventHandler<ThemeSettings, Object> Changed
114+
```
115+
116+
Fired when one of the object's properties has changed.
117+
118+
The **Changed** event will only fire while the **ThemeSettings** object is alive, so apps must ensure
119+
they hold a reference to the object as long as they want to receive the event.
120+
121+
The **Changed** event will only fire as long as the window represented by **WindowId** is alive (has not yet received `WM_NCDESTROY`).
122+
123+
# API Details
124+
125+
_Spec note: this is a new namespace, **Microsoft.UI.System**.
126+
The predecessor for this type is `AccessibilitySettings`,
127+
which is in the `Windows.UI.ViewManagement` namespace,
128+
which is all UWP types we don't plan to carry into Desktop.
129+
This is a UI type that doesn't fit into any of the children of the `UI` namespace.
130+
We anticipate more types here, but don't have any current plans._
131+
132+
133+
``` c# (really midl3)
134+
namespace Microsoft.UI.System
135+
{
136+
runtimeclass ThemeSettings
137+
{
138+
static ThemeSettings CreateForWindowId(WindowId windowId);
139+
140+
event TypedEventHandler<ThemeSettings, Object> Changed;
141+
142+
Boolean HighContrast { get; }
143+
String HighContrastScheme { get; }
144+
}
145+
}
146+
```
147+
148+
# Appendix
149+
_This section is only for internal use, it should not be part of the public documentation._
150+
151+
## Future Directions
152+
In the future, when we support cross-process and/or cross-thread islands, a WindowId won't be sufficient anymore. We'll need
153+
a static method that allows the app to create a ThemeSettings object for a given island.
154+
155+
## Similar APIs
156+
Note in .net there is a
157+
[SystemEvents](https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.systemevents?view=dotnet-plat-ext-7.0) type
158+
that provides related APIs. This is implemented by creating a top-level window to receive `WM_THEMECHANGED` and other
159+
messages. That implementation is
160+
[here](https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System/compmod/microsoft/win32/SystemEvents.cs).

0 commit comments

Comments
 (0)