6.6 KiB
Web project for the Settings UI
Introduction
This project generates the web UI shown in the PowerToys Settings.
It's a ReactJS
project created using UI Fabric.
Build Commands
Here are the commands to build and test this project:
To start the development server
npm install
npm run start
Building and integrating into PowerToys settings project
npm run build
Updating the icons
Icons inside src/icons/
were generated from the Office UI Fabric Icons subset generation tool.
In case the subset needs to be changed, additional steps are needed to include the icon font in the built dist/bundle.js
:
- Copy the inline font data taken from
src/icons/css/fabric-icons-inline.css
and place it in thefontFace
src
value insrc/icons/src/fabric-icons.ts
.
A list of the current icons in the subset can be seen in the icons
object in src/icons/src/fabric-icons.ts
.
SVG icons, including the icons for each PowerToy listed in the Settings, are contained in src/svg/
. To add additional SVG icons add them to src/svg/
and register them in src/setup_icons.tsx
.
Code Structure
The project structure is based on the UI Fabric
scaffold obtained by initializing it with npm init uifabric
.
index.html
The HTML entry-point of the project.
Loads the ReactJS
distribution script.
Defines JavaScript functions to receive and send messages to the PowerToys Settings window.
src/index.tsx
Main ReactJS
entrypoint, initializing the ReactDOM
.
src/setup_icons.tsx
Defines the setup_powertoys_icons
function that registers the icons to be used in the components.
src/components/
Contains the ReactJS
components, including the Settings controls for each type of setting.
src/components/App.tsx
Defines the main App component, containing the UI layout, navigation menu, dialogs and main load/save logic.
src/components/GeneralSettings.tsx
Defines the PowerToys General Settings component, including logic to construct the object sent to PowerToys to change the General settings.
src/components/ModuleSettings.tsx
Defines the component that generates the settings screen for a PowerToy depending on its settings definition.
src/components/BaseSettingsControl.tsx
Defines the base class for a Settings control.
src/css/layout.css
General layout styles.
src/icons/
Icons generated from the Office UI Fabric Icons subset generation tool.
src/svg/
SVG icon assets.
Creating a new settings control
The BaseSettingsControl
class can be extended to create a new Settings control type.
export class BaseSettingsControl extends React.Component <any, any> {
parent_on_change: Function;
constructor(props:any) {
super(props);
this.parent_on_change=props.on_change;
}
public get_value():any {
return null;
}
}
A settings control overrides the get_value
function to return the value to be used for the Setting the control is representing.
It will use the parent_on_change
property to signal that the user made some changes to the settings.
Here's the StringTextSettingsControl
component to serve as an example:
export class StringTextSettingsControl extends BaseSettingsControl {
textref:any = null; // Keeps a reference to the corresponding TextField in the DOM.
constructor(props:any) {
super(props);
this.textref = null;
this.state={
property_values: props.setting
}
}
componentWillReceiveProps(props: any) {
// Fully controlled component.
// Reacting to a property change so that the control is redrawn properly.
this.setState({ property_values: props.setting })
}
public get_value() : any {
// Returns the TextField value.
return {value: this.textref.value};
}
public render(): JSX.Element {
// Renders a UI Fabric TextField.
return (
<TextField
onChange = {
(_event,_new_value) => {
// Updates the state with the new value introduced in the TextField.
this.setState( (prev_state:any) => ({
property_values: {
...(prev_state.property_values),
value: _new_value
}
})
);
// Signal the parent that the user changed a value.
this.parent_on_change();
}
}
value={this.state.property_values.value}
label={this.state.property_values.display_name}
componentRef= {(input) => {this.textref=input;}}
/>
);
}
}
Each settings property has a editor_type
field that's used to differentiate between the Settings control types:
'test string_text': {
display_name: 'This is what a string_text looks like',
editor_type: 'string_text',
value: 'A sample string value'
}
A new Settings control component can be added to src/components/
.
To render the new Settings control, its editor_type
and component instance need to be added to the ModuleSettings
component render():
import React from 'react';
import {StringTextSettingsControl} from './StringTextSettingsControl';
...
export class ModuleSettings extends React.Component <any, any> {
references: any;
parent_on_change: Function;
...
public render(): JSX.Element {
let power_toys_properties = this.state.powertoy.properties;
return (
<Stack tokens={{childrenGap:20}}>
...
{
Object.keys(power_toys_properties).
map( (key) => {
switch(power_toys_properties[key].editor_type) {
...
case 'string_text':
return <StringTextSettingsControl
setting = {power_toys_properties[key]}
key={key}
on_change={this.parent_on_change}
ref={(input) => {this.references[key]=input;}}
/>;
...