From 7f2269c4bd5e8e2b784c721cb4b5ce795c9382c8 Mon Sep 17 00:00:00 2001 From: Konstantin Gross Date: Sun, 17 May 2020 02:54:01 +0200 Subject: [PATCH 1/3] Remaining implementation of the NativeTheme API: shouldUseHighContrastColors, shouldUseInvertedColorScheme, get/set themeSource and updated event --- ElectronNET.API/NativeTheme.cs | 182 +++++++++++++++++++++++- ElectronNET.Host/api/nativeTheme.js | 20 +++ ElectronNET.Host/api/nativeTheme.js.map | 2 +- ElectronNET.Host/api/nativeTheme.ts | 30 +++- 4 files changed, 228 insertions(+), 6 deletions(-) diff --git a/ElectronNET.API/NativeTheme.cs b/ElectronNET.API/NativeTheme.cs index 3b1721ad..c52374c4 100644 --- a/ElectronNET.API/NativeTheme.cs +++ b/ElectronNET.API/NativeTheme.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace ElectronNET.API { @@ -32,11 +33,114 @@ internal static NativeTheme Instance } /// - /// A `Boolean` for if the OS / Chromium currently has a dark mode enabled or is - /// being instructed to show a dark-style UI.If you want to modify this value you - /// should use `themeSource` below. + /// Checks if the new ThemeSource is valid. + /// + /// The new ThemeSource to check. + /// True, if is a valid ThemeSource. + internal bool IsValidThemeSource(string themeSource) + { + var result = + string.Equals(themeSource, "dark", StringComparison.OrdinalIgnoreCase) || + string.Equals(themeSource, "light", StringComparison.OrdinalIgnoreCase) || + string.Equals(themeSource, "system", StringComparison.OrdinalIgnoreCase); + + + return result; + } + + /// + /// Setting this property to 'system' will remove the override and everything will be reset to the OS default. By default 'ThemeSource' is 'system'. + /// + /// Settings this property to 'dark' will have the following effects: + /// + /// + /// will be when accessed + /// + /// + /// Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the dark UI. + /// + /// + /// Any UI the OS renders on macOS including menus, window frames, etc. will use the dark UI. + /// + /// + /// The 'prefers-color-scheme' CSS query will match 'dark' mode. + /// + /// + /// The 'updated' event will be emitted + /// + /// + /// + /// Settings this property to 'light' will have the following effects: + /// + /// + /// will be false when accessed + /// + /// + /// Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the light UI. + /// + /// + /// Any UI the OS renders on macOS including menus, window frames, etc. will use the light UI. + /// + /// + /// The 'prefers-color-scheme' CSS query will match 'light' mode. + /// + /// + /// The 'updated' event will be emitted + /// + /// + /// The usage of this property should align with a classic "dark mode" state machine in your application where the user has three options. + /// + /// + /// + /// Follow OS: SetThemeSource("system"); + /// + /// + /// Dark Mode: SetThemeSource("dark"); + /// + /// + /// Light Mode: SetThemeSource("light"); + /// + /// + /// Your application should then always use to determine what CSS to apply. + /// + /// The new ThemeSource. + public void SetThemeSource(string themeSource) + { + // Check for supported themeSource, otherwise it sets the default + if (!IsValidThemeSource(themeSource)) + { + themeSource = "system"; + } + + BridgeConnector.Socket.Emit("nativeTheme-themeSource", themeSource.ToLower()); + } + + /// + /// A property that can be 'system', 'light' or 'dark'. It is used to override () and + /// supercede the value that Chromium has chosen to use internally. /// /// + public Task GetThemeSourceAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("nativeTheme-themeSource-getCompleted", (themeSource) => + { + BridgeConnector.Socket.Off("nativeTheme-themeSource-getCompleted"); + + taskCompletionSource.SetResult((string)themeSource); + }); + + BridgeConnector.Socket.Emit("nativeTheme-themeSource-get"); + + return taskCompletionSource.Task; + } + + /// + /// A for if the OS / Chromium currently has a dark mode enabled or is + /// being instructed to show a dark-style UI.If you want to modify this value you + /// should use 'themeSource' below. + /// public Task ShouldUseDarkColorsAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -51,5 +155,75 @@ public Task ShouldUseDarkColorsAsync() return taskCompletionSource.Task; } + + /// + /// A for if the OS / Chromium currently has high-contrast mode enabled or is + /// being instructed to show a high-contrast UI. + /// + public Task ShouldUseHighContrastColorsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("nativeTheme-shouldUseHighContrastColors-completed", (shouldUseHighContrastColors) => { + BridgeConnector.Socket.Off("nativeTheme-shouldUseHighContrastColors-completed"); + + taskCompletionSource.SetResult((bool)shouldUseHighContrastColors); + }); + + BridgeConnector.Socket.Emit("nativeTheme-shouldUseHighContrastColors"); + + return taskCompletionSource.Task; + } + + /// + /// A for if the OS / Chromium currently has an inverted color scheme or is + /// being instructed to use an inverted color scheme. + /// + public Task ShouldUseInvertedColorSchemeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("nativeTheme-shouldUseInvertedColorScheme-completed", (shouldUseInvertedColorScheme) => { + BridgeConnector.Socket.Off("nativeTheme-shouldUseInvertedColorScheme-completed"); + + taskCompletionSource.SetResult((bool)shouldUseInvertedColorScheme); + }); + + BridgeConnector.Socket.Emit("nativeTheme-shouldUseInvertedColorScheme"); + + return taskCompletionSource.Task; + } + + /// + /// Emitted when something in the underlying NativeTheme has changed. This normally means that either the value of , + /// or has changed. You will have to check them to determine which one has changed. + /// + public event Action Updated + { + add + { + if (_updated == null) + { + BridgeConnector.Socket.On("nativeTheme-updated" + GetHashCode(), () => + { + _updated(); + }); + + BridgeConnector.Socket.Emit("register-nativeTheme-updated-event", GetHashCode()); + } + _updated += value; + } + remove + { + _updated -= value; + + if (_updated == null) + { + BridgeConnector.Socket.Off("nativeTheme-updated" + GetHashCode()); + } + } + } + + private event Action _updated; } } diff --git a/ElectronNET.Host/api/nativeTheme.js b/ElectronNET.Host/api/nativeTheme.js index e18bde21..b971f6bb 100644 --- a/ElectronNET.Host/api/nativeTheme.js +++ b/ElectronNET.Host/api/nativeTheme.js @@ -7,5 +7,25 @@ module.exports = (socket) => { const shouldUseDarkColors = electron_1.nativeTheme.shouldUseDarkColors; electronSocket.emit('nativeTheme-shouldUseDarkColors-completed', shouldUseDarkColors); }); + socket.on('nativeTheme-shouldUseHighContrastColors', () => { + const shouldUseHighContrastColors = electron_1.nativeTheme.shouldUseHighContrastColors; + electronSocket.emit('nativeTheme-shouldUseHighContrastColors-completed', shouldUseHighContrastColors); + }); + socket.on('nativeTheme-shouldUseInvertedColorScheme', () => { + const shouldUseInvertedColorScheme = electron_1.nativeTheme.shouldUseInvertedColorScheme; + electronSocket.emit('nativeTheme-shouldUseInvertedColorScheme-completed', shouldUseInvertedColorScheme); + }); + socket.on('nativeTheme-themeSource-get', () => { + const themeSource = electron_1.nativeTheme.themeSource; + electronSocket.emit('nativeTheme-themeSource-getCompleted', themeSource); + }); + socket.on('nativeTheme-themeSource', (themeSource) => { + electron_1.nativeTheme.themeSource = themeSource; + }); + socket.on('register-nativeTheme-updated-event', (id) => { + electron_1.nativeTheme.on('updated', () => { + electronSocket.emit('nativeTheme-updated' + id); + }); + }); }; //# sourceMappingURL=nativeTheme.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/nativeTheme.js.map b/ElectronNET.Host/api/nativeTheme.js.map index 4da7e71d..2e22ca06 100644 --- a/ElectronNET.Host/api/nativeTheme.js.map +++ b/ElectronNET.Host/api/nativeTheme.js.map @@ -1 +1 @@ -{"version":3,"file":"nativeTheme.js","sourceRoot":"","sources":["nativeTheme.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,IAAI,cAAc,CAAC;AAEnB,iBAAS,CAAC,MAAuB,EAAE,EAAE;IACjC,cAAc,GAAG,MAAM,CAAC;IAExB,MAAM,CAAC,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC9C,MAAM,mBAAmB,GAAG,sBAAW,CAAC,mBAAmB,CAAC;QAE5D,cAAc,CAAC,IAAI,CAAC,2CAA2C,EAAE,mBAAmB,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACP,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"nativeTheme.js","sourceRoot":"","sources":["nativeTheme.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,IAAI,cAAc,CAAC;AAEnB,iBAAS,CAAC,MAAuB,EAAE,EAAE;IACjC,cAAc,GAAG,MAAM,CAAC;IAExB,MAAM,CAAC,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC9C,MAAM,mBAAmB,GAAG,sBAAW,CAAC,mBAAmB,CAAC;QAE5D,cAAc,CAAC,IAAI,CAAC,2CAA2C,EAAE,mBAAmB,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACtD,MAAM,2BAA2B,GAAG,sBAAW,CAAC,2BAA2B,CAAC;QAE5E,cAAc,CAAC,IAAI,CAAC,mDAAmD,EAAE,2BAA2B,CAAC,CAAC;IAC1G,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACvD,MAAM,4BAA4B,GAAG,sBAAW,CAAC,4BAA4B,CAAC;QAE9E,cAAc,CAAC,IAAI,CAAC,oDAAoD,EAAE,4BAA4B,CAAC,CAAC;IAC5G,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QAC1C,MAAM,WAAW,GAAG,sBAAW,CAAC,WAAW,CAAC;QAE5C,cAAc,CAAC,IAAI,CAAC,sCAAsC,EAAE,WAAW,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,WAAW,EAAE,EAAE;QACjD,sBAAW,CAAC,WAAW,GAAG,WAAW,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,oCAAoC,EAAE,CAAC,EAAE,EAAE,EAAE;QACnD,sBAAW,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YAC3B,cAAc,CAAC,IAAI,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC"} \ No newline at end of file diff --git a/ElectronNET.Host/api/nativeTheme.ts b/ElectronNET.Host/api/nativeTheme.ts index 9781ffaf..7ff0cb45 100644 --- a/ElectronNET.Host/api/nativeTheme.ts +++ b/ElectronNET.Host/api/nativeTheme.ts @@ -9,4 +9,32 @@ export = (socket: SocketIO.Socket) => { electronSocket.emit('nativeTheme-shouldUseDarkColors-completed', shouldUseDarkColors); }); -}; + + socket.on('nativeTheme-shouldUseHighContrastColors', () => { + const shouldUseHighContrastColors = nativeTheme.shouldUseHighContrastColors; + + electronSocket.emit('nativeTheme-shouldUseHighContrastColors-completed', shouldUseHighContrastColors); + }); + + socket.on('nativeTheme-shouldUseInvertedColorScheme', () => { + const shouldUseInvertedColorScheme = nativeTheme.shouldUseInvertedColorScheme; + + electronSocket.emit('nativeTheme-shouldUseInvertedColorScheme-completed', shouldUseInvertedColorScheme); + }); + + socket.on('nativeTheme-themeSource-get', () => { + const themeSource = nativeTheme.themeSource; + + electronSocket.emit('nativeTheme-themeSource-getCompleted', themeSource); + }); + + socket.on('nativeTheme-themeSource', (themeSource) => { + nativeTheme.themeSource = themeSource; + }); + + socket.on('register-nativeTheme-updated-event', (id) => { + nativeTheme.on('updated', () => { + electronSocket.emit('nativeTheme-updated' + id); + }); + }); +}; \ No newline at end of file From e77f48b2c5ef619b6c3fd54796d2d5b72f355cdf Mon Sep 17 00:00:00 2001 From: Konstantin Gross <6459825+konstantingross@users.noreply.github.com> Date: Mon, 18 May 2020 21:01:47 +0200 Subject: [PATCH 2/3] Fix formatting and summary --- ElectronNET.API/NativeTheme.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ElectronNET.API/NativeTheme.cs b/ElectronNET.API/NativeTheme.cs index c52374c4..a3485cc5 100644 --- a/ElectronNET.API/NativeTheme.cs +++ b/ElectronNET.API/NativeTheme.cs @@ -44,7 +44,6 @@ internal bool IsValidThemeSource(string themeSource) string.Equals(themeSource, "light", StringComparison.OrdinalIgnoreCase) || string.Equals(themeSource, "system", StringComparison.OrdinalIgnoreCase); - return result; } @@ -119,7 +118,6 @@ public void SetThemeSource(string themeSource) /// A property that can be 'system', 'light' or 'dark'. It is used to override () and /// supercede the value that Chromium has chosen to use internally. /// - /// public Task GetThemeSourceAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -138,8 +136,8 @@ public Task GetThemeSourceAsync() /// /// A for if the OS / Chromium currently has a dark mode enabled or is - /// being instructed to show a dark-style UI.If you want to modify this value you - /// should use 'themeSource' below. + /// being instructed to show a dark-style UI. If you want to modify this value you + /// should use . /// public Task ShouldUseDarkColorsAsync() { @@ -226,4 +224,4 @@ public event Action Updated private event Action _updated; } -} +} \ No newline at end of file From b7960eb77291245b2e9d8e6e67bc52a25753c30c Mon Sep 17 00:00:00 2001 From: Konstantin Gross <6459825+konstantingross@users.noreply.github.com> Date: Mon, 18 May 2020 23:57:10 +0200 Subject: [PATCH 3/3] PR fixes --- ElectronNET.API/Entities/ThemeSourceMode.cs | 28 +++++++++++ ElectronNET.API/NativeTheme.cs | 51 ++++++++------------- 2 files changed, 46 insertions(+), 33 deletions(-) create mode 100644 ElectronNET.API/Entities/ThemeSourceMode.cs diff --git a/ElectronNET.API/Entities/ThemeSourceMode.cs b/ElectronNET.API/Entities/ThemeSourceMode.cs new file mode 100644 index 00000000..53fa23ed --- /dev/null +++ b/ElectronNET.API/Entities/ThemeSourceMode.cs @@ -0,0 +1,28 @@ +using System.ComponentModel; + +namespace ElectronNET.API.Entities +{ + /// + /// Defines the ThemeSourceMode enumeration. + /// + public enum ThemeSourceMode + { + /// + /// Operating system default. + /// + [Description("system")] + System, + + /// + /// Light theme. + /// + [Description("light")] + Light, + + /// + /// Dark theme. + /// + [Description("dark")] + Dark + } +} \ No newline at end of file diff --git a/ElectronNET.API/NativeTheme.cs b/ElectronNET.API/NativeTheme.cs index a3485cc5..81770e9c 100644 --- a/ElectronNET.API/NativeTheme.cs +++ b/ElectronNET.API/NativeTheme.cs @@ -1,5 +1,7 @@ using System; using System.Threading.Tasks; +using ElectronNET.API.Entities; +using ElectronNET.API.Extensions; namespace ElectronNET.API { @@ -33,24 +35,9 @@ internal static NativeTheme Instance } /// - /// Checks if the new ThemeSource is valid. - /// - /// The new ThemeSource to check. - /// True, if is a valid ThemeSource. - internal bool IsValidThemeSource(string themeSource) - { - var result = - string.Equals(themeSource, "dark", StringComparison.OrdinalIgnoreCase) || - string.Equals(themeSource, "light", StringComparison.OrdinalIgnoreCase) || - string.Equals(themeSource, "system", StringComparison.OrdinalIgnoreCase); - - return result; - } - - /// - /// Setting this property to 'system' will remove the override and everything will be reset to the OS default. By default 'ThemeSource' is 'system'. + /// Setting this property to will remove the override and everything will be reset to the OS default. By default 'ThemeSource' is . /// - /// Settings this property to 'dark' will have the following effects: + /// Settings this property to will have the following effects: /// /// /// will be when accessed @@ -69,10 +56,10 @@ internal bool IsValidThemeSource(string themeSource) /// /// /// - /// Settings this property to 'light' will have the following effects: + /// Settings this property to will have the following effects: /// /// - /// will be false when accessed + /// will be when accessed /// /// /// Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the light UI. @@ -91,42 +78,40 @@ internal bool IsValidThemeSource(string themeSource) /// /// /// - /// Follow OS: SetThemeSource("system"); + /// Follow OS: SetThemeSource(ThemeSourceMode.System); /// /// - /// Dark Mode: SetThemeSource("dark"); + /// Dark Mode: SetThemeSource(ThemeSourceMode.Dark); /// /// - /// Light Mode: SetThemeSource("light"); + /// Light Mode: SetThemeSource(ThemeSourceMode.Light); /// /// /// Your application should then always use to determine what CSS to apply. /// /// The new ThemeSource. - public void SetThemeSource(string themeSource) + public void SetThemeSource(ThemeSourceMode themeSourceMode) { - // Check for supported themeSource, otherwise it sets the default - if (!IsValidThemeSource(themeSource)) - { - themeSource = "system"; - } + var themeSource = themeSourceMode.GetDescription(); - BridgeConnector.Socket.Emit("nativeTheme-themeSource", themeSource.ToLower()); + BridgeConnector.Socket.Emit("nativeTheme-themeSource", themeSource); } /// - /// A property that can be 'system', 'light' or 'dark'. It is used to override () and + /// A property that can be , or . It is used to override () and /// supercede the value that Chromium has chosen to use internally. /// - public Task GetThemeSourceAsync() + public Task GetThemeSourceAsync() { - var taskCompletionSource = new TaskCompletionSource(); + var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("nativeTheme-themeSource-getCompleted", (themeSource) => { BridgeConnector.Socket.Off("nativeTheme-themeSource-getCompleted"); - taskCompletionSource.SetResult((string)themeSource); + var themeSourceValue = (ThemeSourceMode)Enum.Parse(typeof(ThemeSourceMode), (string)themeSource, true); + + taskCompletionSource.SetResult(themeSourceValue); }); BridgeConnector.Socket.Emit("nativeTheme-themeSource-get");