Merge pull request #4761 from matrix-org/joriks/appearance-advanced

Custom font selection
This commit is contained in:
Jorik Schellekens 2020-06-23 15:38:50 +01:00 committed by GitHub
commit 1e39279a77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 243 additions and 9 deletions

View file

@ -16,11 +16,14 @@ limitations under the License.
.mx_AppearanceUserSettingsTab_fontSlider, .mx_AppearanceUserSettingsTab_fontSlider,
.mx_AppearanceUserSettingsTab_fontSlider_preview, .mx_AppearanceUserSettingsTab_fontSlider_preview,
.mx_AppearanceUserSettingsTab_Layout, .mx_AppearanceUserSettingsTab_Layout {
.mx_AppearanceUserSettingsTab .mx_Field {
@mixin mx_Settings_fullWidthField; @mixin mx_Settings_fullWidthField;
} }
.mx_AppearanceUserSettingsTab .mx_Field {
width: 256px;
}
.mx_AppearanceUserSettingsTab_fontScaling { .mx_AppearanceUserSettingsTab_fontScaling {
color: $primary-fg-color; color: $primary-fg-color;
} }
@ -204,3 +207,14 @@ limitations under the License.
background-color: rgba($accent-color, 0.08); background-color: rgba($accent-color, 0.08);
} }
} }
.mx_AppearanceUserSettingsTab_Advanced {
.mx_AppearanceUserSettingsTab_AdvancedToggle {
color: $accent-color;
margin-bottom: 16px;
}
.mx_AppearanceUserSettingsTab_systemFont {
margin-left: calc($font-16px + 10px);
}
}

View file

@ -54,6 +54,8 @@ interface IProps {
// If specified, contents will appear as a tooltip on the element and // If specified, contents will appear as a tooltip on the element and
// validation feedback tooltips will be suppressed. // validation feedback tooltips will be suppressed.
tooltipContent?: React.ReactNode; tooltipContent?: React.ReactNode;
// If specified the tooltip will be shown regardless of feedback
forceTooltipVisible?: boolean;
// If specified alongside tooltipContent, the class name to apply to the // If specified alongside tooltipContent, the class name to apply to the
// tooltip itself. // tooltip itself.
tooltipClassName?: string; tooltipClassName?: string;
@ -242,10 +244,9 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
const Tooltip = sdk.getComponent("elements.Tooltip"); const Tooltip = sdk.getComponent("elements.Tooltip");
let fieldTooltip; let fieldTooltip;
if (tooltipContent || this.state.feedback) { if (tooltipContent || this.state.feedback) {
const addlClassName = tooltipClassName ? tooltipClassName : '';
fieldTooltip = <Tooltip fieldTooltip = <Tooltip
tooltipClassName={`mx_Field_tooltip ${addlClassName}`} tooltipClassName={classNames("mx_Field_tooltip", tooltipClassName)}
visible={this.state.feedbackVisible} visible={(this.state.focused && this.props.forceTooltipVisible) || this.state.feedbackVisible}
label={tooltipContent || this.state.feedback} label={tooltipContent || this.state.feedback}
/>; />;
} }

View file

@ -21,7 +21,6 @@ import {_t} from "../../../../../languageHandler";
import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore"; import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore";
import { enumerateThemes } from "../../../../../theme"; import { enumerateThemes } from "../../../../../theme";
import ThemeWatcher from "../../../../../settings/watchers/ThemeWatcher"; import ThemeWatcher from "../../../../../settings/watchers/ThemeWatcher";
import Field from "../../../elements/Field";
import Slider from "../../../elements/Slider"; import Slider from "../../../elements/Slider";
import AccessibleButton from "../../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import dis from "../../../../../dispatcher/dispatcher"; import dis from "../../../../../dispatcher/dispatcher";
@ -32,6 +31,7 @@ import { IValidationResult, IFieldState } from '../../../elements/Validation';
import StyledRadioButton from '../../../elements/StyledRadioButton'; import StyledRadioButton from '../../../elements/StyledRadioButton';
import StyledCheckbox from '../../../elements/StyledCheckbox'; import StyledCheckbox from '../../../elements/StyledCheckbox';
import SettingsFlag from '../../../elements/SettingsFlag'; import SettingsFlag from '../../../elements/SettingsFlag';
import Field from '../../../elements/Field';
import EventTilePreview from '../../../elements/EventTilePreview'; import EventTilePreview from '../../../elements/EventTilePreview';
interface IProps { interface IProps {
@ -55,6 +55,9 @@ interface IState extends IThemeState {
customThemeUrl: string; customThemeUrl: string;
customThemeMessage: CustomThemeMessage; customThemeMessage: CustomThemeMessage;
useCustomFontSize: boolean; useCustomFontSize: boolean;
useSystemFont: boolean;
systemFont: string;
showAdvanced: boolean;
useIRCLayout: boolean; useIRCLayout: boolean;
} }
@ -73,6 +76,9 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
customThemeUrl: "", customThemeUrl: "",
customThemeMessage: {isError: false, text: ""}, customThemeMessage: {isError: false, text: ""},
useCustomFontSize: SettingsStore.getValue("useCustomFontSize"), useCustomFontSize: SettingsStore.getValue("useCustomFontSize"),
useSystemFont: SettingsStore.getValue("useSystemFont"),
systemFont: SettingsStore.getValue("systemFont"),
showAdvanced: false,
useIRCLayout: SettingsStore.getValue("useIRCLayout"), useIRCLayout: SettingsStore.getValue("useIRCLayout"),
}; };
} }
@ -374,6 +380,47 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
</div>; </div>;
}; };
private renderAdvancedSection() {
const toggle = <div
className="mx_AppearanceUserSettingsTab_AdvancedToggle"
onClick={() => this.setState({showAdvanced: !this.state.showAdvanced})}
>
{this.state.showAdvanced ? "Hide advanced" : "Show advanced"}
</div>;
let advanced: React.ReactNode;
if (this.state.showAdvanced) {
advanced = <div>
<SettingsFlag
name="useSystemFont"
level={SettingLevel.DEVICE}
useCheckbox={true}
onChange={(checked) => this.setState({useSystemFont: checked})}
/>
<Field
className="mx_AppearanceUserSettingsTab_systemFont"
label={SettingsStore.getDisplayName("systemFont")}
onChange={(value) => {
this.setState({
systemFont: value.target.value,
});
SettingsStore.setValue("systemFont", null, SettingLevel.DEVICE, value.target.value);
}}
tooltipContent="Set the name of a font installed on your system & Riot will attempt to use it."
forceTooltipVisible={true}
disabled={!this.state.useSystemFont}
value={this.state.systemFont}
/>
</div>;
}
return <div className="mx_SettingsTab_section mx_AppearanceUserSettingsTab_Advanced">
{toggle}
{advanced}
</div>;
}
render() { render() {
return ( return (
<div className="mx_SettingsTab mx_AppearanceUserSettingsTab"> <div className="mx_SettingsTab mx_AppearanceUserSettingsTab">
@ -384,6 +431,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
{this.renderThemeSection()} {this.renderThemeSection()}
{SettingsStore.isFeatureEnabled("feature_font_scaling") ? this.renderFontSection() : null} {SettingsStore.isFeatureEnabled("feature_font_scaling") ? this.renderFontSection() : null}
{SettingsStore.isFeatureEnabled("feature_irc_ui") ? this.renderLayoutSection() : null} {SettingsStore.isFeatureEnabled("feature_irc_ui") ? this.renderLayoutSection() : null}
{this.renderAdvancedSection()}
</div> </div>
); );
} }

View file

@ -69,4 +69,14 @@ export enum Action {
* Opens the user menu (previously known as the top left menu). No additional payload information required. * Opens the user menu (previously known as the top left menu). No additional payload information required.
*/ */
ToggleUserMenu = "toggle_user_menu", ToggleUserMenu = "toggle_user_menu",
/**
* Sets the apps root font size. Should be used with UpdateFontSizePayload
*/
UpdateFontSize = "update_font_size",
/**
* Sets a system font. Should be used with UpdateSystemFontPayload
*/
UpdateSystemFont = "update_system_font",
} }

View file

@ -0,0 +1,27 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
export interface UpdateFontSizePayload extends ActionPayload {
action: Action.UpdateFontSize;
/**
* The font size to set the root to
*/
size: number;
}

View file

@ -0,0 +1,32 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
export interface UpdateSystemFontPayload extends ActionPayload {
action: Action.UpdateSystemFont;
/**
* Specify whether to use a system font or the stylesheet font
*/
useSystemFont: boolean;
/**
* The system font to use
*/
font: string;
}

View file

@ -460,6 +460,8 @@
"Mirror local video feed": "Mirror local video feed", "Mirror local video feed": "Mirror local video feed",
"Enable Community Filter Panel": "Enable Community Filter Panel", "Enable Community Filter Panel": "Enable Community Filter Panel",
"Match system theme": "Match system theme", "Match system theme": "Match system theme",
"Use a system font": "Use a system font",
"System font name": "System font name",
"Allow Peer-to-Peer for 1:1 calls": "Allow Peer-to-Peer for 1:1 calls", "Allow Peer-to-Peer for 1:1 calls": "Allow Peer-to-Peer for 1:1 calls",
"Send analytics data": "Send analytics data", "Send analytics data": "Send analytics data",
"Never send encrypted messages to unverified sessions from this session": "Never send encrypted messages to unverified sessions from this session", "Never send encrypted messages to unverified sessions from this session": "Never send encrypted messages to unverified sessions from this session",

View file

@ -30,6 +30,8 @@ import PushToMatrixClientController from './controllers/PushToMatrixClientContro
import ReloadOnChangeController from "./controllers/ReloadOnChangeController"; import ReloadOnChangeController from "./controllers/ReloadOnChangeController";
import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases"; import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases";
import FontSizeController from './controllers/FontSizeController'; import FontSizeController from './controllers/FontSizeController';
import SystemFontController from './controllers/SystemFontController';
import UseSystemFontController from './controllers/UseSystemFontController';
// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config'];
@ -319,6 +321,18 @@ export const SETTINGS = {
default: true, default: true,
displayName: _td("Match system theme"), displayName: _td("Match system theme"),
}, },
"useSystemFont": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
displayName: _td("Use a system font"),
controller: new UseSystemFontController(),
},
"systemFont": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: "",
displayName: _td("System font name"),
controller: new SystemFontController(),
},
"webRtcAllowPeerToPeer": { "webRtcAllowPeerToPeer": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
displayName: _td('Allow Peer-to-Peer for 1:1 calls'), displayName: _td('Allow Peer-to-Peer for 1:1 calls'),

View file

@ -16,6 +16,8 @@ limitations under the License.
import SettingController from "./SettingController"; import SettingController from "./SettingController";
import dis from "../../dispatcher/dispatcher"; import dis from "../../dispatcher/dispatcher";
import { UpdateFontSizePayload } from "../../dispatcher/payloads/UpdateFontSizePayload";
import { Action } from "../../dispatcher/actions";
export default class FontSizeController extends SettingController { export default class FontSizeController extends SettingController {
constructor() { constructor() {
@ -24,8 +26,8 @@ export default class FontSizeController extends SettingController {
onChange(level, roomId, newValue) { onChange(level, roomId, newValue) {
// Dispatch font size change so that everything open responds to the change. // Dispatch font size change so that everything open responds to the change.
dis.dispatch({ dis.dispatch<UpdateFontSizePayload>({
action: "update-font-size", action: Action.UpdateFontSize,
size: newValue, size: newValue,
}); });
} }

View file

@ -0,0 +1,36 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import SettingController from "./SettingController";
import SettingsStore from "../SettingsStore";
import dis from "../../dispatcher/dispatcher";
import { UpdateSystemFontPayload } from "../../dispatcher/payloads/UpdateSystemFontPayload";
import { Action } from "../../dispatcher/actions";
export default class SystemFontController extends SettingController {
constructor() {
super();
}
onChange(level, roomId, newValue) {
// Dispatch font size change so that everything open responds to the change.
dis.dispatch<UpdateSystemFontPayload>({
action: Action.UpdateSystemFont,
useSystemFont: SettingsStore.getValue("useSystemFont"),
font: newValue,
});
}
}

View file

@ -0,0 +1,36 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import SettingController from "./SettingController";
import SettingsStore from "../SettingsStore";
import dis from "../../dispatcher/dispatcher";
import { UpdateSystemFontPayload } from "../../dispatcher/payloads/UpdateSystemFontPayload";
import { Action } from "../../dispatcher/actions";
export default class UseSystemFontController extends SettingController {
constructor() {
super();
}
onChange(level, roomId, newValue) {
// Dispatch font size change so that everything open responds to the change.
dis.dispatch<UpdateSystemFontPayload>({
action: Action.UpdateSystemFont,
useSystemFont: newValue,
font: SettingsStore.getValue("systemFont"),
});
}
}

View file

@ -18,6 +18,8 @@ import dis from '../../dispatcher/dispatcher';
import SettingsStore, {SettingLevel} from '../SettingsStore'; import SettingsStore, {SettingLevel} from '../SettingsStore';
import IWatcher from "./Watcher"; import IWatcher from "./Watcher";
import { toPx } from '../../utils/units'; import { toPx } from '../../utils/units';
import { Action } from '../../dispatcher/actions';
import { UpdateSystemFontPayload } from '../../dispatcher/payloads/UpdateSystemFontPayload';
export class FontWatcher implements IWatcher { export class FontWatcher implements IWatcher {
public static readonly MIN_SIZE = 8; public static readonly MIN_SIZE = 8;
@ -33,6 +35,10 @@ export class FontWatcher implements IWatcher {
public start() { public start() {
this.setRootFontSize(SettingsStore.getValue("baseFontSize")); this.setRootFontSize(SettingsStore.getValue("baseFontSize"));
this.setSystemFont({
useSystemFont: SettingsStore.getValue("useSystemFont"),
font: SettingsStore.getValue("systemFont"),
});
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
} }
@ -41,8 +47,10 @@ export class FontWatcher implements IWatcher {
} }
private onAction = (payload) => { private onAction = (payload) => {
if (payload.action === 'update-font-size') { if (payload.action === Action.UpdateFontSize) {
this.setRootFontSize(payload.size); this.setRootFontSize(payload.size);
} else if (payload.action === Action.UpdateSystemFont) {
this.setSystemFont(payload);
} }
}; };
@ -54,4 +62,8 @@ export class FontWatcher implements IWatcher {
} }
(<HTMLElement>document.querySelector(":root")).style.fontSize = toPx(fontSize); (<HTMLElement>document.querySelector(":root")).style.fontSize = toPx(fontSize);
}; };
private setSystemFont = ({useSystemFont, font}) => {
document.body.style.fontFamily = useSystemFont ? font : "";
};
} }