Skip to content

Commit 7f2269c

Browse files
Remaining implementation of the NativeTheme API: shouldUseHighContrastColors, shouldUseInvertedColorScheme, get/set themeSource and updated event
1 parent 05de407 commit 7f2269c

File tree

4 files changed

+228
-6
lines changed

4 files changed

+228
-6
lines changed

ElectronNET.API/NativeTheme.cs

Lines changed: 178 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Threading.Tasks;
1+
using System;
2+
using System.Threading.Tasks;
23

34
namespace ElectronNET.API
45
{
@@ -32,11 +33,114 @@ internal static NativeTheme Instance
3233
}
3334

3435
/// <summary>
35-
/// A `Boolean` for if the OS / Chromium currently has a dark mode enabled or is
36-
/// being instructed to show a dark-style UI.If you want to modify this value you
37-
/// should use `themeSource` below.
36+
/// Checks if the new ThemeSource is valid.
37+
/// </summary>
38+
/// <param name="themeSource">The new ThemeSource to check.</param>
39+
/// <returns>True, if is a valid ThemeSource.</returns>
40+
internal bool IsValidThemeSource(string themeSource)
41+
{
42+
var result =
43+
string.Equals(themeSource, "dark", StringComparison.OrdinalIgnoreCase) ||
44+
string.Equals(themeSource, "light", StringComparison.OrdinalIgnoreCase) ||
45+
string.Equals(themeSource, "system", StringComparison.OrdinalIgnoreCase);
46+
47+
48+
return result;
49+
}
50+
51+
/// <summary>
52+
/// Setting this property to 'system' will remove the override and everything will be reset to the OS default. By default 'ThemeSource' is 'system'.
53+
/// <para/>
54+
/// Settings this property to 'dark' will have the following effects:
55+
/// <list type="bullet">
56+
/// <item>
57+
/// <description><see cref="ShouldUseDarkColorsAsync"/> will be <see langword="true"/> when accessed</description>
58+
/// </item>
59+
/// <item>
60+
/// <description>Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the dark UI.</description>
61+
/// </item>
62+
/// <item>
63+
/// <description>Any UI the OS renders on macOS including menus, window frames, etc. will use the dark UI.</description>
64+
/// </item>
65+
/// <item>
66+
/// <description>The 'prefers-color-scheme' CSS query will match 'dark' mode.</description>
67+
/// </item>
68+
/// <item>
69+
/// <description>The 'updated' event will be emitted</description>
70+
/// </item>
71+
/// </list>
72+
/// <para/>
73+
/// Settings this property to 'light' will have the following effects:
74+
/// <list type="bullet">
75+
/// <item>
76+
/// <description><see cref="ShouldUseDarkColorsAsync"/> will be <see langword="false"/> false when accessed</description>
77+
/// </item>
78+
/// <item>
79+
/// <description>Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the light UI.</description>
80+
/// </item>
81+
/// <item>
82+
/// <description>Any UI the OS renders on macOS including menus, window frames, etc. will use the light UI.</description>
83+
/// </item>
84+
/// <item>
85+
/// <description>The 'prefers-color-scheme' CSS query will match 'light' mode.</description>
86+
/// </item>
87+
/// <item>
88+
/// <description>The 'updated' event will be emitted</description>
89+
/// </item>
90+
/// </list>
91+
/// The usage of this property should align with a classic "dark mode" state machine in your application where the user has three options.
92+
/// <para/>
93+
/// <list type="bullet">
94+
/// <item>
95+
/// <description>Follow OS: SetThemeSource("system");</description>
96+
/// </item>
97+
/// <item>
98+
/// <description>Dark Mode: SetThemeSource("dark");</description>
99+
/// </item>
100+
/// <item>
101+
/// <description>Light Mode: SetThemeSource("light");</description>
102+
/// </item>
103+
/// </list>
104+
/// Your application should then always use <see cref="ShouldUseDarkColorsAsync"/> to determine what CSS to apply.
105+
/// </summary>
106+
/// <param name="themeSource">The new ThemeSource.</param>
107+
public void SetThemeSource(string themeSource)
108+
{
109+
// Check for supported themeSource, otherwise it sets the default
110+
if (!IsValidThemeSource(themeSource))
111+
{
112+
themeSource = "system";
113+
}
114+
115+
BridgeConnector.Socket.Emit("nativeTheme-themeSource", themeSource.ToLower());
116+
}
117+
118+
/// <summary>
119+
/// A <see cref="string"/> property that can be 'system', 'light' or 'dark'. It is used to override (<seealso cref="SetThemeSource"/>) and
120+
/// supercede the value that Chromium has chosen to use internally.
38121
/// </summary>
39122
/// <returns></returns>
123+
public Task<string> GetThemeSourceAsync()
124+
{
125+
var taskCompletionSource = new TaskCompletionSource<string>();
126+
127+
BridgeConnector.Socket.On("nativeTheme-themeSource-getCompleted", (themeSource) =>
128+
{
129+
BridgeConnector.Socket.Off("nativeTheme-themeSource-getCompleted");
130+
131+
taskCompletionSource.SetResult((string)themeSource);
132+
});
133+
134+
BridgeConnector.Socket.Emit("nativeTheme-themeSource-get");
135+
136+
return taskCompletionSource.Task;
137+
}
138+
139+
/// <summary>
140+
/// A <see cref="bool"/> for if the OS / Chromium currently has a dark mode enabled or is
141+
/// being instructed to show a dark-style UI.If you want to modify this value you
142+
/// should use 'themeSource' below.
143+
/// </summary>
40144
public Task<bool> ShouldUseDarkColorsAsync()
41145
{
42146
var taskCompletionSource = new TaskCompletionSource<bool>();
@@ -51,5 +155,75 @@ public Task<bool> ShouldUseDarkColorsAsync()
51155

52156
return taskCompletionSource.Task;
53157
}
158+
159+
/// <summary>
160+
/// A <see cref="bool"/> for if the OS / Chromium currently has high-contrast mode enabled or is
161+
/// being instructed to show a high-contrast UI.
162+
/// </summary>
163+
public Task<bool> ShouldUseHighContrastColorsAsync()
164+
{
165+
var taskCompletionSource = new TaskCompletionSource<bool>();
166+
167+
BridgeConnector.Socket.On("nativeTheme-shouldUseHighContrastColors-completed", (shouldUseHighContrastColors) => {
168+
BridgeConnector.Socket.Off("nativeTheme-shouldUseHighContrastColors-completed");
169+
170+
taskCompletionSource.SetResult((bool)shouldUseHighContrastColors);
171+
});
172+
173+
BridgeConnector.Socket.Emit("nativeTheme-shouldUseHighContrastColors");
174+
175+
return taskCompletionSource.Task;
176+
}
177+
178+
/// <summary>
179+
/// A <see cref="bool"/> for if the OS / Chromium currently has an inverted color scheme or is
180+
/// being instructed to use an inverted color scheme.
181+
/// </summary>
182+
public Task<bool> ShouldUseInvertedColorSchemeAsync()
183+
{
184+
var taskCompletionSource = new TaskCompletionSource<bool>();
185+
186+
BridgeConnector.Socket.On("nativeTheme-shouldUseInvertedColorScheme-completed", (shouldUseInvertedColorScheme) => {
187+
BridgeConnector.Socket.Off("nativeTheme-shouldUseInvertedColorScheme-completed");
188+
189+
taskCompletionSource.SetResult((bool)shouldUseInvertedColorScheme);
190+
});
191+
192+
BridgeConnector.Socket.Emit("nativeTheme-shouldUseInvertedColorScheme");
193+
194+
return taskCompletionSource.Task;
195+
}
196+
197+
/// <summary>
198+
/// Emitted when something in the underlying NativeTheme has changed. This normally means that either the value of <see cref="ShouldUseDarkColorsAsync"/>,
199+
/// <see cref="ShouldUseHighContrastColorsAsync"/> or <see cref="ShouldUseInvertedColorSchemeAsync"/> has changed. You will have to check them to determine which one has changed.
200+
/// </summary>
201+
public event Action Updated
202+
{
203+
add
204+
{
205+
if (_updated == null)
206+
{
207+
BridgeConnector.Socket.On("nativeTheme-updated" + GetHashCode(), () =>
208+
{
209+
_updated();
210+
});
211+
212+
BridgeConnector.Socket.Emit("register-nativeTheme-updated-event", GetHashCode());
213+
}
214+
_updated += value;
215+
}
216+
remove
217+
{
218+
_updated -= value;
219+
220+
if (_updated == null)
221+
{
222+
BridgeConnector.Socket.Off("nativeTheme-updated" + GetHashCode());
223+
}
224+
}
225+
}
226+
227+
private event Action _updated;
54228
}
55229
}

ElectronNET.Host/api/nativeTheme.js

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ElectronNET.Host/api/nativeTheme.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ElectronNET.Host/api/nativeTheme.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,32 @@ export = (socket: SocketIO.Socket) => {
99

1010
electronSocket.emit('nativeTheme-shouldUseDarkColors-completed', shouldUseDarkColors);
1111
});
12-
};
12+
13+
socket.on('nativeTheme-shouldUseHighContrastColors', () => {
14+
const shouldUseHighContrastColors = nativeTheme.shouldUseHighContrastColors;
15+
16+
electronSocket.emit('nativeTheme-shouldUseHighContrastColors-completed', shouldUseHighContrastColors);
17+
});
18+
19+
socket.on('nativeTheme-shouldUseInvertedColorScheme', () => {
20+
const shouldUseInvertedColorScheme = nativeTheme.shouldUseInvertedColorScheme;
21+
22+
electronSocket.emit('nativeTheme-shouldUseInvertedColorScheme-completed', shouldUseInvertedColorScheme);
23+
});
24+
25+
socket.on('nativeTheme-themeSource-get', () => {
26+
const themeSource = nativeTheme.themeSource;
27+
28+
electronSocket.emit('nativeTheme-themeSource-getCompleted', themeSource);
29+
});
30+
31+
socket.on('nativeTheme-themeSource', (themeSource) => {
32+
nativeTheme.themeSource = themeSource;
33+
});
34+
35+
socket.on('register-nativeTheme-updated-event', (id) => {
36+
nativeTheme.on('updated', () => {
37+
electronSocket.emit('nativeTheme-updated' + id);
38+
});
39+
});
40+
};

0 commit comments

Comments
 (0)