From 52b15f29ad238c0584fc0dd16592aa16e189f24c Mon Sep 17 00:00:00 2001 From: Bartosz Sosnowski Date: Wed, 16 Oct 2019 10:21:44 +0200 Subject: [PATCH] Add darkmode support for the settings window (#494) --- src/common/windows_colors.cpp | 4 + src/common/windows_colors.h | 2 +- src/runner/general_settings.cpp | 20 +++- src/runner/settings_window.cpp | 9 ++ src/settings-web/index-dark.html | 26 +++++ src/settings-web/package-lock.json | 105 +++++++++++++++++- src/settings-web/package.json | 6 +- src/settings-web/src/components/App.tsx | 28 ++--- .../components/ChoiceGroupSettingsControl.tsx | 7 +- .../src/components/GeneralSettings.tsx | 84 +++++++++++++- src/settings-web/src/css/layout.css | 1 - src/settings-web/src/index.tsx | 30 ++++- .../src/svg/animated_gif_recorder.svg | 8 +- src/settings-web/src/svg/batch_renamer.svg | 2 +- src/settings-web/src/svg/fancy_zones.svg | 2 +- .../src/svg/maximize_new_desktop.svg | 2 +- src/settings-web/src/svg/shortcut_guide.svg | 4 +- src/settings-web/src/svg/terminate_tool.svg | 2 +- src/settings/main.cpp | 38 ++++--- src/settings/settings-html/dist/bundle.js | 6 +- src/settings/settings-html/index-dark.html | 14 +++ src/settings/settings-html/index.html | 2 +- 22 files changed, 343 insertions(+), 59 deletions(-) create mode 100644 src/settings-web/index-dark.html create mode 100644 src/settings/settings-html/index-dark.html diff --git a/src/common/windows_colors.cpp b/src/common/windows_colors.cpp index 490469f677..f067aaeb9a 100644 --- a/src/common/windows_colors.cpp +++ b/src/common/windows_colors.cpp @@ -50,6 +50,10 @@ WindowsColors::Color WindowsColors::get_accent_color() { WindowsColors::Color WindowsColors::get_background_color() { winrt::Windows::UI::ViewManagement::UISettings uiSettings; return uiSettings.GetColorValue(winrt::Windows::UI::ViewManagement::UIColorType::Background); +} + +bool WindowsColors::is_dark_mode() { + return rgb_color(get_background_color()) == 0; } bool WindowsColors::update() { diff --git a/src/common/windows_colors.h b/src/common/windows_colors.h index 2fd4e834b9..ba87382bd5 100644 --- a/src/common/windows_colors.h +++ b/src/common/windows_colors.h @@ -16,7 +16,7 @@ struct WindowsColors { static Color get_accent_dark_1_color(); static Color get_accent_color(); static Color get_background_color(); - + static bool is_dark_mode(); // Update colors - returns true if the values where changed bool update(); diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index d01e818b1b..73b14d3967 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -3,11 +3,23 @@ #include "auto_start_helper.h" #include #include "powertoy_module.h" +#include using namespace web; +static std::wstring settings_theme; + web::json::value load_general_settings() { - return PTSettingsHelper::load_general_settings(); + auto loaded = PTSettingsHelper::load_general_settings(); + if (loaded.has_string_field(L"theme")) { + settings_theme = loaded.as_object()[L"theme"].as_string(); + if (settings_theme != L"dark" && settings_theme != L"light") { + settings_theme = L"system"; + } + } else { + settings_theme = L"system"; + } + return loaded; } web::json::value get_general_settings() { @@ -20,6 +32,9 @@ web::json::value get_general_settings() { enabled.as_object()[name] = json::value::boolean(powertoy.is_enabled()); } result.as_object()[L"enabled"] = enabled; + + result.as_object()[L"theme"] = json::value::string(settings_theme); + result.as_object()[L"system_theme"] = json::value::string(WindowsColors::is_dark_mode() ? L"dark" : L"light"); return result; } @@ -52,6 +67,9 @@ void apply_general_settings(const json::value& general_configs) { } } } + if (general_configs.has_string_field(L"theme")) { + settings_theme = general_configs.at(L"theme").as_string(); + } json::value save_settings = get_general_settings(); PTSettingsHelper::save_general_settings(save_settings); } diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index c26813db76..104501937b 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -9,6 +9,7 @@ #include #include "tray_icon.h" #include "general_settings.h" +#include "common/windows_colors.h" #define BUFSIZE 1024 @@ -116,6 +117,12 @@ void run_settings_window() { PathRemoveFileSpec(executable_path); wcscat_s(executable_path, L"\\PowerToysSettings.exe"); WCHAR executable_args[MAX_PATH * 3]; + + std::wstring settings_theme_setting = get_general_settings().at(L"theme").as_string(); + std::wstring settings_theme; + if (settings_theme_setting == L"dark" || (settings_theme_setting == L"system" && WindowsColors::is_dark_mode())) { + settings_theme = L" dark"; // Include arg separating space + } // Generate unique names for the pipes, if getting a UUID is possible std::wstring powertoys_pipe_name(L"\\\\.\\pipe\\powertoys_runner_"); std::wstring settings_pipe_name(L"\\\\.\\pipe\\powertoys_settings_"); @@ -136,6 +143,7 @@ void run_settings_window() { // powertoys_pipe - PowerToys pipe server. // settings_pipe - Settings pipe server. // powertoys_pid - PowerToys process pid. + // settings_theme - pass "dark" to start the settings window in dark mode wcscpy_s(executable_args, L"\""); wcscat_s(executable_args, executable_path); wcscat_s(executable_args, L"\""); @@ -145,6 +153,7 @@ void run_settings_window() { wcscat_s(executable_args, settings_pipe_name.c_str()); wcscat_s(executable_args, L" "); wcscat_s(executable_args, std::to_wstring(powertoys_pid).c_str()); + wcscat_s(executable_args, settings_theme.c_str()); // Run the Settings process with non-elevated privileges diff --git a/src/settings-web/index-dark.html b/src/settings-web/index-dark.html new file mode 100644 index 0000000000..b3aab7880c --- /dev/null +++ b/src/settings-web/index-dark.html @@ -0,0 +1,26 @@ + + + + + PowerToys Settings + + +
+ + + diff --git a/src/settings-web/package-lock.json b/src/settings-web/package-lock.json index f6117502f3..589dca5229 100644 --- a/src/settings-web/package-lock.json +++ b/src/settings-web/package-lock.json @@ -1450,6 +1450,105 @@ "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", "dev": true }, + "@uifabric/azure-themes": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/@uifabric/azure-themes/-/azure-themes-7.0.8.tgz", + "integrity": "sha512-vgZn+D/oYpwtkhqPXKCYe7tTqJukjsMhoiOXhhnpHO9ad+CS/KPsY9fL4juEJ0qELTkdTvzqQeXgF4Bl+eqPrg==", + "requires": { + "@uifabric/merge-styles": "^7.5.1", + "@uifabric/set-version": "^7.0.2", + "office-ui-fabric-react": "^7.30.0", + "tslib": "^1.7.1" + }, + "dependencies": { + "@uifabric/foundation": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.4.0.tgz", + "integrity": "sha512-3XOlCPit+8JfgRcT5xlI5REE8UXpSGKRSX1vRkKwLDQx7acp1d8eJA4S51b3Addhks9QoH/rEPYJg7ExbscI4Q==", + "requires": { + "@uifabric/set-version": "^7.0.2", + "@uifabric/styling": "^7.6.2", + "@uifabric/utilities": "^7.0.9", + "tslib": "^1.7.1" + } + }, + "@uifabric/icons": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.2.0.tgz", + "integrity": "sha512-CLhAhoEevbwJVaVigBfRBU0WWOjQzICEbov/fYYMBbAbjgVOpuAbSquvJHkZtlq79O1p0iCoxMeXQVVbtcnPZw==", + "requires": { + "@uifabric/set-version": "^7.0.2", + "@uifabric/styling": "^7.6.2", + "tslib": "^1.7.1" + } + }, + "@uifabric/merge-styles": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.5.1.tgz", + "integrity": "sha512-uhgTryb8YCbnd8Q/o6H644QMqUX/nhGAK7IGgv8G1lLwUY0zMuBBFTSXa6+oGMgsLZxNQupdhkyJ5GzgDhg5Pw==", + "requires": { + "@uifabric/set-version": "^7.0.2", + "tslib": "^1.7.1" + } + }, + "@uifabric/set-version": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.2.tgz", + "integrity": "sha512-3mQp7gqPOqphwX74j+N/lJEFeivKPv8ryY9QFXUxVPnrXNwpIkDW9Wk6CPqArzgGvQngRRKYD/PcyP5iuHN52A==", + "requires": { + "tslib": "^1.7.1" + } + }, + "@uifabric/styling": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.6.2.tgz", + "integrity": "sha512-2l4N8dpHk4kHj+EKRuLrguLJcgwrXWsn3W5cbP34C9q6xrN8ShmZE3ce8TBzT1kLPtjkrUpiJXtV5qqfc/uNXg==", + "requires": { + "@microsoft/load-themed-styles": "^1.7.13", + "@uifabric/merge-styles": "^7.5.1", + "@uifabric/set-version": "^7.0.2", + "@uifabric/utilities": "^7.0.9", + "tslib": "^1.7.1" + } + }, + "@uifabric/utilities": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.0.9.tgz", + "integrity": "sha512-Q5lzCJ/KiDchhVysbI4xXiYjRiw3EQymvCXTKnmkXUo5hWRnsVPEhlWHZAXynu4gS303ZlV73bxtcQHWl80Z5Q==", + "requires": { + "@uifabric/merge-styles": "^7.5.1", + "@uifabric/set-version": "^7.0.2", + "prop-types": "^15.5.10", + "tslib": "^1.7.1" + } + }, + "office-ui-fabric-react": { + "version": "7.31.2", + "resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.31.2.tgz", + "integrity": "sha512-MLZ5fdh3k4u0WvTout6aO4SRIBLEaiTj1HrLRlqO9wxEBZJ9VVgAoHOmltCY/B+SG2E2p5lo//iMp6gEu4/tsQ==", + "requires": { + "@microsoft/load-themed-styles": "^1.7.13", + "@uifabric/example-data": "^7.0.1", + "@uifabric/foundation": "^7.4.0", + "@uifabric/icons": "^7.2.0", + "@uifabric/merge-styles": "^7.5.1", + "@uifabric/set-version": "^7.0.2", + "@uifabric/styling": "^7.6.2", + "@uifabric/utilities": "^7.0.9", + "prop-types": "^15.5.10", + "tslib": "^1.7.1" + } + } + } + }, + "@uifabric/example-data": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@uifabric/example-data/-/example-data-7.0.1.tgz", + "integrity": "sha512-HWw2lAbF5Fn1NhmxxEpzgWrprEsQ7+Ut71zpAHdCJoVCVeesHl03gY/obBIz8ogrufW0RIlkrNSredy6uRzYDQ==", + "requires": { + "tslib": "^1.7.1" + } + }, "@uifabric/foundation": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.0.2.tgz", @@ -5524,9 +5623,9 @@ "dev": true }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.2.tgz", + "integrity": "sha512-cIv17+GhL8pHHnRJzGu2wwcthL5sb8uDKBHvZ2Dtu5s1YNt0ljbzKbamnc+gr69y7bzwQiBdr5+hOpRd5pnOdg==", "dev": true, "requires": { "neo-async": "^2.6.0", diff --git a/src/settings-web/package.json b/src/settings-web/package.json index c6311d517c..448ffd61c2 100644 --- a/src/settings-web/package.json +++ b/src/settings-web/package.json @@ -14,15 +14,19 @@ "scripts": { "just": "just-scripts", "clean": "rimraf build lib lib-commonjs && just-scripts clean", - "build": "rimraf build && just-scripts build --min --production && cp index.html build && react-snap && cp -r build/* ../settings/settings-html", + "build": "rimraf build && just-scripts build --min --production && cp *.html build && react-snap && cp -r build/* ../settings/settings-html", "test": "just-scripts test", "test:update": "just-scripts jest -u", "test:start": "just-scripts start-test", "start": "just-scripts start", "stack:upgrade": "just-scripts upgrade-stack" }, + "reactSnap": { + "include": ["/index.html", "/index-dark.html"] + }, "dependencies": { "@svgr/webpack": "^4.3.2", + "@uifabric/azure-themes": "^7.0.8", "office-ui-fabric-react": "^7.4.3", "react": "~16.8.0", "react-dom": "~16.8.0" diff --git a/src/settings-web/src/components/App.tsx b/src/settings-web/src/components/App.tsx index b53c6571e3..6cb22d12fd 100644 --- a/src/settings-web/src/components/App.tsx +++ b/src/settings-web/src/components/App.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {Stack, Text, Nav, DefaultButton, PrimaryButton, ScrollablePane, INavLink, Spinner, SpinnerSize, Dialog, DialogType, DialogFooter} from 'office-ui-fabric-react'; +import {Stack, Text, Nav, DefaultButton, PrimaryButton, ScrollablePane, INavLink, Spinner, SpinnerSize, Dialog, DialogType, DialogFooter, getTheme} from 'office-ui-fabric-react'; import {GeneralSettings} from './GeneralSettings'; import {ModuleSettings} from './ModuleSettings'; import '../css/layout.css'; @@ -123,10 +123,10 @@ export class App extends React.Component { }); } } - + const theme = getTheme() return ( -
-
+
+
-
+
{this.choiceref=element;}} - onChange={()=>{ + onChange={(ev:any, option:any)=>{ + this.selected = option.key; this.parent_on_change(); }} /> diff --git a/src/settings-web/src/components/GeneralSettings.tsx b/src/settings-web/src/components/GeneralSettings.tsx index 492e16b325..acd507b215 100644 --- a/src/settings-web/src/components/GeneralSettings.tsx +++ b/src/settings-web/src/components/GeneralSettings.tsx @@ -1,11 +1,13 @@ import React from 'react'; -import { Stack, Text, DefaultButton, Label, Link} from 'office-ui-fabric-react'; -import {BoolToggleSettingsControl} from './BoolToggleSettingsControl' +import { Stack, Text, PrimaryButton, Label, Link, loadTheme } from 'office-ui-fabric-react'; +import { BoolToggleSettingsControl } from './BoolToggleSettingsControl' +import { ChoiceGroupSettingsControl } from './ChoiceGroupSettingsControl' import { Separator } from 'office-ui-fabric-react/lib/Separator'; export class GeneralSettings extends React.Component { references: any = {}; startup_reference: any; + theme_reference: any; parent_on_change: Function; constructor(props: any) { super(props); @@ -36,6 +38,7 @@ export class GeneralSettings extends React.Component { let result : any = {}; result[this.state.settings_key]= { startup: this.startup_reference.get_value().value, + theme: this.theme_reference.get_value().value, enabled: enabled }; return result; @@ -116,19 +119,88 @@ export class GeneralSettings extends React.Component { setting={{display_name: 'Start at login', value: this.state.settings.general.startup}} on_change={this.parent_on_change} ref={(input) => {this.startup_reference=input;}} - /> + /> + { + const dark_mode = this.theme_reference.get_value().value === 'dark' || + (this.theme_reference.get_value().value === 'system' && this.state.settings.general.system_theme === 'dark'); + if (dark_mode) { + loadTheme({ + palette: { + themePrimary: '#0088e4', + themeLighterAlt: '#000509', + themeLighter: '#001624', + themeLight: '#002944', + themeTertiary: '#005288', + themeSecondary: '#0078c8', + themeDarkAlt: '#1793e6', + themeDark: '#38a3ea', + themeDarker: '#69baef', + neutralLighterAlt: '#0b0b0b', + neutralLighter: '#151515', + neutralLight: '#252525', + neutralQuaternaryAlt: '#2f2f2f', + neutralQuaternary: '#373737', + neutralTertiaryAlt: '#595959', + neutralTertiary: '#eaeaea', + neutralSecondary: '#eeeeee', + neutralPrimaryAlt: '#f1f1f1', + neutralPrimary: '#e0e0e0', + neutralDark: '#f8f8f8', + black: '#fbfbfb', + white: '#000000', + } + }); + } else { + loadTheme({ + palette: { + themePrimary: '#0078d4', + themeLighterAlt: '#f3f9fd', + themeLighter: '#d0e7f8', + themeLight: '#a9d3f2', + themeTertiary: '#5ca9e5', + themeSecondary: '#1a86d9', + themeDarkAlt: '#006cbe', + themeDark: '#005ba1', + themeDarker: '#004377', + neutralLighterAlt: '#f8f8f8', + neutralLighter: '#f4f4f4', + neutralLight: '#eaeaea', + neutralQuaternaryAlt: '#dadada', + neutralQuaternary: '#d0d0d0', + neutralTertiaryAlt: '#c8c8c8', + neutralTertiary: '#bab8b7', + neutralSecondary: '#a3a2a0', + neutralPrimaryAlt: '#8d8b8a', + neutralPrimary: '#323130', + neutralDark: '#605e5d', + black: '#494847', + white: '#ffffff', + } + }); + } + this.parent_on_change(); + }} + ref={(input) => {this.theme_reference=input;}} + /> - Check for updates + >Check for updates {/* An empty span to always give 30px padding in Edge. */} diff --git a/src/settings-web/src/css/layout.css b/src/settings-web/src/css/layout.css index 635cbb4b1e..3ccd4912f7 100644 --- a/src/settings-web/src/css/layout.css +++ b/src/settings-web/src/css/layout.css @@ -11,7 +11,6 @@ flex-basis: 228px; display: flex; flex-direction: column; - background-color: #f3f2f1; } .body .sidebar.collapsed { diff --git a/src/settings-web/src/index.tsx b/src/settings-web/src/index.tsx index 924afd2c70..4f899cdb03 100644 --- a/src/settings-web/src/index.tsx +++ b/src/settings-web/src/index.tsx @@ -1,8 +1,36 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { App } from './components/App'; -import { mergeStyles } from 'office-ui-fabric-react'; +import { loadTheme, mergeStyles } from 'office-ui-fabric-react'; +if ((window as any).start_with_dark_theme) { + loadTheme({ + palette: { + themePrimary: '#0088e4', + themeLighterAlt: '#000509', + themeLighter: '#001624', + themeLight: '#002944', + themeTertiary: '#005288', + themeSecondary: '#0078c8', + themeDarkAlt: '#1793e6', + themeDark: '#38a3ea', + themeDarker: '#69baef', + neutralLighterAlt: '#0b0b0b', + neutralLighter: '#151515', + neutralLight: '#252525', + neutralQuaternaryAlt: '#2f2f2f', + neutralQuaternary: '#373737', + neutralTertiaryAlt: '#595959', + neutralTertiary: '#eaeaea', + neutralSecondary: '#eeeeee', + neutralPrimaryAlt: '#f1f1f1', + neutralPrimary: '#e0e0e0', + neutralDark: '#f8f8f8', + black: '#fbfbfb', + white: '#000000', + } + }); +} // Inject some global styles mergeStyles({ selectors: { diff --git a/src/settings-web/src/svg/animated_gif_recorder.svg b/src/settings-web/src/svg/animated_gif_recorder.svg index 2f556659f2..1e38974863 100644 --- a/src/settings-web/src/svg/animated_gif_recorder.svg +++ b/src/settings-web/src/svg/animated_gif_recorder.svg @@ -3,14 +3,14 @@ Group 6 Created with Sketch. - + - - - + + + diff --git a/src/settings-web/src/svg/batch_renamer.svg b/src/settings-web/src/svg/batch_renamer.svg index ead0b2ee83..c30f184de1 100644 --- a/src/settings-web/src/svg/batch_renamer.svg +++ b/src/settings-web/src/svg/batch_renamer.svg @@ -4,7 +4,7 @@ Fancy zones Created with Sketch. - + diff --git a/src/settings-web/src/svg/fancy_zones.svg b/src/settings-web/src/svg/fancy_zones.svg index 64af67c861..ee9b2ae39b 100644 --- a/src/settings-web/src/svg/fancy_zones.svg +++ b/src/settings-web/src/svg/fancy_zones.svg @@ -4,7 +4,7 @@ Fancy zones Created with Sketch. - + diff --git a/src/settings-web/src/svg/maximize_new_desktop.svg b/src/settings-web/src/svg/maximize_new_desktop.svg index 0275f2e19a..428b35d84f 100644 --- a/src/settings-web/src/svg/maximize_new_desktop.svg +++ b/src/settings-web/src/svg/maximize_new_desktop.svg @@ -4,7 +4,7 @@ maximize Created with Sketch. - + diff --git a/src/settings-web/src/svg/shortcut_guide.svg b/src/settings-web/src/svg/shortcut_guide.svg index 8e546405f2..e5ca28ce17 100644 --- a/src/settings-web/src/svg/shortcut_guide.svg +++ b/src/settings-web/src/svg/shortcut_guide.svg @@ -3,8 +3,8 @@ Shortcut guide Created with Sketch. - - + + diff --git a/src/settings-web/src/svg/terminate_tool.svg b/src/settings-web/src/svg/terminate_tool.svg index 14cf33184b..7af9e22268 100644 --- a/src/settings-web/src/svg/terminate_tool.svg +++ b/src/settings-web/src/svg/terminate_tool.svg @@ -4,7 +4,7 @@ Terminate tool Created with Sketch. - + diff --git a/src/settings/main.cpp b/src/settings/main.cpp index 1e3199a393..488603beca 100644 --- a/src/settings/main.cpp +++ b/src/settings/main.cpp @@ -53,6 +53,9 @@ TwoWayPipeMessageIPC* g_message_pipe = nullptr; // Set to true if waiting for webview confirmation before closing the Window. bool g_waiting_for_close_confirmation = false; +// Is the setting window to be started in dark mode +bool g_start_in_dark_mode = false; + #ifdef _DEBUG void NavigateToLocalhostReactServer() { // Useful for connecting to instance running in react development server. @@ -203,7 +206,7 @@ void receive_message_from_webview(const std::wstring& msg) { } } -void initialize_webview() { +void initialize_webview(int nShowCmd) { try { g_webview_process = WebViewControlProcess(); auto asyncwebview = g_webview_process.CreateWebViewControlAsync((int64_t)g_main_wnd, client_rect_to_bounds_rect(g_main_wnd)); @@ -222,29 +225,29 @@ void initialize_webview() { // Open the requested link in the default browser registered in the Shell int res = static_cast(reinterpret_cast(ShellExecute(nullptr, L"open", args.Uri().AbsoluteUri().c_str(), nullptr, nullptr, SW_SHOWNORMAL))); WINRT_VERIFY(res > 32); - }); + }); - g_webview.DOMContentLoaded([=](IWebViewControl sender_loaded, WebViewControlDOMContentLoadedEventArgs const& args_loaded) { - // runs when the content has been loaded. - }); + g_webview.ContentLoading([=](IWebViewControl sender, WebViewControlContentLoadingEventArgs const& args) { + ShowWindow(g_main_wnd, nShowCmd); + }); g_webview.ScriptNotify([=](IWebViewControl sender_script_notify, WebViewControlScriptNotifyEventArgs const& args_script_notify) { // content called window.external.notify() std::wstring message_sent = args_script_notify.Value().c_str(); receive_message_from_webview(message_sent); - }); + }); g_webview.AcceleratorKeyPressed([&](IWebViewControl sender, WebViewControlAcceleratorKeyPressedEventArgs const& args) { if (args.VirtualKey() == winrt::Windows::System::VirtualKey::F4) { // WebView swallows key-events. Detect Alt-F4 one and close the window manually. const auto _ = g_webview.InvokeScriptAsync(hstring(L"exit_settings_app"), {}); } - }); + }); resize_web_view(); #if defined(_DEBUG) && _DEBUG_WITH_LOCALHOST // Navigates to localhost:8080 NavigateToLocalhostReactServer(); #else - // Navigates to settings-html/index.html. - NavigateToUri(L"index.html"); + // Navigates to settings-html/index.html or index-dark.html + NavigateToUri(g_start_in_dark_mode ? L"index-dark.html" : L"index.html"); #endif } else if (status == AsyncStatus::Error) { MessageBox(NULL, L"Failed to create the WebView control.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK); @@ -254,7 +257,7 @@ void initialize_webview() { } else if (status == AsyncStatus::Canceled) { // Ignore. } - }); + }); } catch (hresult_error const& e) { WCHAR message[1024] = L""; StringCchPrintf(message, ARRAYSIZE(message), L"failed: %ls", e.message().c_str()); @@ -343,7 +346,7 @@ void register_classes(HINSTANCE hInstance) { wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(APPICON)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = g_start_in_dark_mode ? CreateSolidBrush(0) : (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = nullptr; wcex.lpszClassName = L"PTSettingsClass"; wcex.hIconSm = nullptr; @@ -395,12 +398,15 @@ void quit_when_parent_terminates(std::wstring parent_pid) { std::thread(wait_on_parent_process_thread, pid).detach(); } -void initialize_message_pipe() { +// Parse arguments: initialize two-way IPC message pipe and if settings window is to be started +// in dark mode. +void parse_args() { // Expected calling arguments: // [0] - This executable's path. // [1] - PowerToys pipe server. // [2] - Settings pipe server. // [3] - PowerToys process pid. + // [4] - optional "dark" parameter if the settings window is to be started in dark mode LPWSTR *argument_list; int n_args; @@ -415,6 +421,9 @@ void initialize_message_pipe() { exit(1); #endif } + if (n_args > 4) { + g_start_in_dark_mode = wcscmp(argument_list[4], L"dark") == 0; + } LocalFree(argument_list); } @@ -429,15 +438,14 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _ } g_hinst = hInstance; - initialize_message_pipe(); + parse_args(); register_classes(hInstance); g_main_wnd = create_main_window(hInstance); if (g_main_wnd == nullptr) { MessageBox(NULL, L"Failed to create main window.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK); exit(1); } - initialize_webview(); - ShowWindow(g_main_wnd, nShowCmd); + initialize_webview(nShowCmd); // Main message loop. MSG msg; diff --git a/src/settings/settings-html/dist/bundle.js b/src/settings/settings-html/dist/bundle.js index 1a01a4f4e6..7db86448ae 100644 --- a/src/settings/settings-html/dist/bundle.js +++ b/src/settings/settings-html/dist/bundle.js @@ -11,7 +11,7 @@ object-assign * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var o=n(15),r="function"==typeof Symbol&&Symbol.for,i=r?Symbol.for("react.element"):60103,a=r?Symbol.for("react.portal"):60106,s=r?Symbol.for("react.fragment"):60107,l=r?Symbol.for("react.strict_mode"):60108,u=r?Symbol.for("react.profiler"):60114,c=r?Symbol.for("react.provider"):60109,d=r?Symbol.for("react.context"):60110,p=r?Symbol.for("react.concurrent_mode"):60111,f=r?Symbol.for("react.forward_ref"):60112,h=r?Symbol.for("react.suspense"):60113,m=r?Symbol.for("react.memo"):60115,g=r?Symbol.for("react.lazy"):60116,v="function"==typeof Symbol&&Symbol.iterator;function y(e){for(var t=arguments.length-1,n="https://reactjs.org/docs/error-decoder.html?invariant="+e,o=0;oN.length&&N.push(e)}function F(e,t,n){return null==e?0:function e(t,n,o,r){var s=typeof t;"undefined"!==s&&"boolean"!==s||(t=null);var l=!1;if(null===t)l=!0;else switch(s){case"string":case"number":l=!0;break;case"object":switch(t.$$typeof){case i:case a:l=!0}}if(l)return o(r,t,""===n?"."+O(t,0):n),1;if(l=0,n=""===n?".":n+":",Array.isArray(t))for(var u=0;uN.length&&N.push(e)}function F(e,t,n){return null==e?0:function e(t,n,o,r){var s=typeof t;"undefined"!==s&&"boolean"!==s||(t=null);var l=!1;if(null===t)l=!0;else switch(s){case"string":case"number":l=!0;break;case"object":switch(t.$$typeof){case i:case a:l=!0}}if(l)return o(r,t,""===n?"."+O(t,0):n),1;if(l=0,n=""===n?".":n+":",Array.isArray(t))for(var u=0;uthis.eventPool.length&&this.eventPool.push(e)}function de(e){e.eventPool=[],e.getPooled=ue,e.release=ce}r(le.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=ae)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=ae)},persist:function(){this.isPersistent=ae},isPersistent:se,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=se,this._dispatchInstances=this._dispatchListeners=null}}),le.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},le.extend=function(e){function t(){}function n(){return o.apply(this,arguments)}var o=this;t.prototype=o.prototype;var i=new t;return r(i,n.prototype),n.prototype=i,n.prototype.constructor=n,n.Interface=r({},o.Interface,e),n.extend=o.extend,de(n),n},de(le);var pe=le.extend({data:null}),fe=le.extend({data:null}),he=[9,13,27,32],me=V&&"CompositionEvent"in window,ge=null;V&&"documentMode"in document&&(ge=document.documentMode);var ve=V&&"TextEvent"in window&&!ge,ye=V&&(!me||ge&&8=ge),be=String.fromCharCode(32),_e={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},xe=!1;function ke(e,t){switch(e){case"keyup":return-1!==he.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function Ce(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var we=!1;var Ee={eventTypes:_e,extractEvents:function(e,t,n,o){var r=void 0,i=void 0;if(me)e:{switch(e){case"compositionstart":r=_e.compositionStart;break e;case"compositionend":r=_e.compositionEnd;break e;case"compositionupdate":r=_e.compositionUpdate;break e}r=void 0}else we?ke(e,n)&&(r=_e.compositionEnd):"keydown"===e&&229===n.keyCode&&(r=_e.compositionStart);return r?(ye&&"ko"!==n.locale&&(we||r!==_e.compositionStart?r===_e.compositionEnd&&we&&(i=ie()):(oe="value"in(ne=o)?ne.value:ne.textContent,we=!0)),r=pe.getPooled(r,t,n,o),i?r.data=i:null!==(i=Ce(n))&&(r.data=i),j(r),i=r):i=null,(e=ve?function(e,t){switch(e){case"compositionend":return Ce(t);case"keypress":return 32!==t.which?null:(xe=!0,be);case"textInput":return(e=t.data)===be&&xe?null:e;default:return null}}(e,n):function(e,t){if(we)return"compositionend"===e||!me&&ke(e,t)?(e=ie(),re=oe=ne=null,we=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1