Add darkmode support for the settings window (#494)
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -3,11 +3,23 @@
|
||||
#include "auto_start_helper.h"
|
||||
#include <common/settings_helpers.h>
|
||||
#include "powertoy_module.h"
|
||||
#include <common/windows_colors.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <common/two_way_pipe_message_ipc.h>
|
||||
#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
|
||||
|
||||
|
||||
26
src/settings-web/index-dark.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
window.output_from_webview = function(arg) {
|
||||
if (typeof (window.external) !== 'undefined' && ('notify' in window.external)) {
|
||||
window.external.notify(arg);
|
||||
}
|
||||
}
|
||||
function receive_from_settings_app(arg) {
|
||||
window.react_app_component.receive_config_msg(JSON.parse(arg));
|
||||
return '';
|
||||
}
|
||||
function exit_settings_app() {
|
||||
window.react_app_component.receive_exit_request();
|
||||
return '';
|
||||
}
|
||||
window.start_with_dark_theme = true;
|
||||
</script>
|
||||
<title>PowerToys Settings</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="dist/bundle.js" charset="UTF-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
105
src/settings-web/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 <any, any> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const theme = getTheme()
|
||||
return (
|
||||
<div className='body'>
|
||||
<div className='sidebar'>
|
||||
<div className='body' style={{stroke: theme.palette.black}}>
|
||||
<div className='sidebar' style={{backgroundColor: theme.palette.neutralLighter, color: theme.palette.black}}>
|
||||
<Nav
|
||||
selectedKey= {this.state.selected_menu}
|
||||
onLinkClick = {
|
||||
@@ -146,27 +146,27 @@ export class App extends React.Component <any, any> {
|
||||
styles = {{
|
||||
navItems: { margin : '0'},
|
||||
compositeLink: {
|
||||
backgroundColor : '#f3f2f1',
|
||||
color: '#323130',
|
||||
backgroundColor : theme.palette.neutralLighter,
|
||||
color: theme.palette.neutralPrimary,
|
||||
selectors: {
|
||||
'&.is-selected button' : {
|
||||
backgroundColor: '#e1dfdd',
|
||||
color: '#201F1E',
|
||||
backgroundColor: theme.palette.neutralLight,
|
||||
color: theme.palette.neutralPrimaryAlt,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
'&:hover button.ms-Nav-link' : {
|
||||
backgroundColor: '#e1dfdd',
|
||||
color: '#323130'
|
||||
backgroundColor: theme.palette.neutralLight,
|
||||
color: theme.palette.neutralPrimary
|
||||
},
|
||||
'i.ms-Button-icon' : {
|
||||
color: '#201F1E',
|
||||
color: theme.palette.neutralPrimary,
|
||||
fontWeight: 'normal'
|
||||
},
|
||||
'&:hover i.ms-Button-icon' : {
|
||||
color: '#201F1E',
|
||||
color: theme.palette.neutralPrimary,
|
||||
},
|
||||
'&:active i.ms-Button-icon' : {
|
||||
color: '#201F1E',
|
||||
color: theme.palette.neutralPrimary,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -181,7 +181,7 @@ export class App extends React.Component <any, any> {
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className='editorzone'>
|
||||
<div className='editorzone' style={{backgroundColor: theme.palette.white, color: theme.palette.black}}>
|
||||
<div className='editorhead'>
|
||||
<div className='editortitle'>
|
||||
<Text
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ChoiceGroup } from 'office-ui-fabric-react';
|
||||
|
||||
export class ChoiceGroupSettingsControl extends BaseSettingsControl {
|
||||
choiceref:any = null; // Keeps a reference to the corresponding item in the DOM.
|
||||
selected:any;
|
||||
|
||||
constructor(props:any) {
|
||||
super(props);
|
||||
@@ -17,10 +18,11 @@ export class ChoiceGroupSettingsControl extends BaseSettingsControl {
|
||||
// Fully controlled component.
|
||||
// Reacting to a property change so that the control is redrawn properly.
|
||||
this.setState({ property_values: props.setting })
|
||||
this.selected = props.setting.value;
|
||||
}
|
||||
|
||||
public get_value() : any {
|
||||
return {'value': this.choiceref.checkedOption.key};
|
||||
return {'value': this.selected};
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
@@ -31,7 +33,8 @@ export class ChoiceGroupSettingsControl extends BaseSettingsControl {
|
||||
options={this.state.property_values.options}
|
||||
label={this.state.property_values.display_name}
|
||||
componentRef={(element) => {this.choiceref=element;}}
|
||||
onChange={()=>{
|
||||
onChange={(ev:any, option:any)=>{
|
||||
this.selected = option.key;
|
||||
this.parent_on_change();
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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 <any, any> {
|
||||
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 <any, any> {
|
||||
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 <any, any> {
|
||||
setting={{display_name: 'Start at login', value: this.state.settings.general.startup}}
|
||||
on_change={this.parent_on_change}
|
||||
ref={(input) => {this.startup_reference=input;}}
|
||||
/>
|
||||
/>
|
||||
<ChoiceGroupSettingsControl
|
||||
setting={{display_name: 'Chose Settings color',
|
||||
value: this.state.settings.general.theme,
|
||||
options: [
|
||||
{ key: 'system', text: 'System default app mode'},
|
||||
{ key: 'light', text: 'Light' },
|
||||
{ key: 'dark', text: 'Dark' }
|
||||
]}}
|
||||
on_change={() => {
|
||||
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;}}
|
||||
/>
|
||||
<Stack>
|
||||
<Label>Version 0.11.0</Label>
|
||||
<DefaultButton
|
||||
<PrimaryButton
|
||||
styles={{
|
||||
root: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
alignSelf: "start"
|
||||
}
|
||||
}}
|
||||
href='https://github.com/microsoft/PowerToys/releases'
|
||||
target='_blank'
|
||||
>Check for updates</DefaultButton>
|
||||
>Check for updates</PrimaryButton>
|
||||
</Stack>
|
||||
{/* An empty span to always give 30px padding in Edge. */}
|
||||
<span/>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
flex-basis: 228px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f3f2f1;
|
||||
}
|
||||
|
||||
.body .sidebar.collapsed {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
|
||||
<title>Group 6</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Icons" stroke-width="1" fill-rule="evenodd">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -237.000000)">
|
||||
<g id="Group-4">
|
||||
<g id="Navigation-/-Left-Nav-/-Default-/-Level-2-w-Icon-/-Rest-Copy-5" transform="translate(0.000000, 224.000000)">
|
||||
<g id="Group-6" transform="translate(17.000000, 13.000000)">
|
||||
<rect id="Rectangle" stroke="#000000" x="0.5" y="0.5" width="14" height="14"></rect>
|
||||
<circle id="Oval" fill="#000000" cx="7.5" cy="7.5" r="2.5"></circle>
|
||||
<circle id="Oval" stroke="#000000" cx="7.5" cy="7.5" r="4"></circle>
|
||||
<rect id="Rectangle" fill="none" x="0.5" y="0.5" width="14" height="14"></rect>
|
||||
<circle id="Oval" cx="7.5" cy="7.5" r="2.5"></circle>
|
||||
<circle id="Oval" fill="none" cx="7.5" cy="7.5" r="4"></circle>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -4,7 +4,7 @@
|
||||
<title>Fancy zones</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -164.000000)" stroke="#000000">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -164.000000)">
|
||||
<g id="Group-4">
|
||||
<g id="Navigation-/-Left-Nav-/-Default-/-Level-2-w-Icon-/-Rest-Copy-3" transform="translate(0.000000, 152.000000)">
|
||||
<g id="Fancy-zones" transform="translate(17.000000, 12.000000)">
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -4,7 +4,7 @@
|
||||
<title>Fancy zones</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -128.000000)" stroke="#000000">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -128.000000)">
|
||||
<g id="Group-4">
|
||||
<g id="Navigation-/-Left-Nav-/-Default-/-Level-2-w-Icon-/-Rest-Copy-2" transform="translate(0.000000, 116.000000)">
|
||||
<g id="Fancy-zones" transform="translate(17.000000, 12.000000)">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -4,7 +4,7 @@
|
||||
<title>maximize</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -56.000000)" stroke="#000000">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -56.000000)">
|
||||
<g id="Group-4">
|
||||
<g id="Navigation-/-Left-Nav-/-Default-/-Level-2-w-Icon-/-Rest" transform="translate(0.000000, 44.000000)">
|
||||
<g id="maximize" transform="translate(17.000000, 12.000000)">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -3,8 +3,8 @@
|
||||
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
|
||||
<title>Shortcut guide</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -92.000000)" stroke="#000000">
|
||||
<g id="Icons" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -92.000000)">
|
||||
<g id="Group-4">
|
||||
<g id="Navigation-/-Left-Nav-/-Default-/-Level-2-w-Icon-/-Rest-Copy" transform="translate(0.000000, 80.000000)">
|
||||
<g id="Shortcut-guide" transform="translate(17.000000, 12.000000)">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -4,7 +4,7 @@
|
||||
<title>Terminate tool</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -200.000000)" stroke="#000000">
|
||||
<g id="X-Large-Copy-5" transform="translate(-17.000000, -200.000000)">
|
||||
<g id="Group-4">
|
||||
<g id="Navigation-/-Left-Nav-/-Default-/-Level-2-w-Icon-/-Rest-Copy-4" transform="translate(0.000000, 188.000000)">
|
||||
<g id="Terminate-tool" transform="translate(17.000000, 12.000000)">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -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<int>(reinterpret_cast<uintptr_t>(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;
|
||||
|
||||