Respect the system high contrast setting when using system theme (#7043)
* Respect the system high contrast setting when using system theme * Restore correct behaviour of getEffectiveThemepull/21833/head
parent
76254977d6
commit
68b64564c0
|
@ -2,6 +2,7 @@
|
||||||
$accent: #268075;
|
$accent: #268075;
|
||||||
$alert: #D62C25;
|
$alert: #D62C25;
|
||||||
$links: #0A6ECA;
|
$links: #0A6ECA;
|
||||||
|
$primary-content: #17191C;
|
||||||
$secondary-content: #5E6266;
|
$secondary-content: #5E6266;
|
||||||
$tertiary-content: $secondary-content;
|
$tertiary-content: $secondary-content;
|
||||||
$quaternary-content: $secondary-content;
|
$quaternary-content: $secondary-content;
|
||||||
|
@ -106,3 +107,11 @@ $roomtopic-color: $secondary-content;
|
||||||
.mx_FontScalingPanel_fontSlider {
|
.mx_FontScalingPanel_fontSlider {
|
||||||
background-color: $roomlist-button-bg-color !important;
|
background-color: $roomlist-button-bg-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ThemeChoicePanel > .mx_ThemeSelectors > .mx_RadioButton input[type="radio"]:disabled + div {
|
||||||
|
border-color: $primary-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ThemeChoicePanel > .mx_ThemeSelectors > .mx_RadioButton.mx_RadioButton_disabled {
|
||||||
|
color: $primary-content;
|
||||||
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import FeedbackDialog from "../views/dialogs/FeedbackDialog";
|
||||||
import Modal from "../../Modal";
|
import Modal from "../../Modal";
|
||||||
import LogoutDialog from "../views/dialogs/LogoutDialog";
|
import LogoutDialog from "../views/dialogs/LogoutDialog";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import { getCustomTheme } from "../../theme";
|
import { findHighContrastTheme, getCustomTheme, isHighContrastTheme } from "../../theme";
|
||||||
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
|
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
|
||||||
import SdkConfig from "../../SdkConfig";
|
import SdkConfig from "../../SdkConfig";
|
||||||
import { getHomePageUrl } from "../../utils/pages";
|
import { getHomePageUrl } from "../../utils/pages";
|
||||||
|
@ -69,6 +69,7 @@ type PartialDOMRect = Pick<DOMRect, "width" | "left" | "top" | "height">;
|
||||||
interface IState {
|
interface IState {
|
||||||
contextMenuPosition: PartialDOMRect;
|
contextMenuPosition: PartialDOMRect;
|
||||||
isDarkTheme: boolean;
|
isDarkTheme: boolean;
|
||||||
|
isHighContrast: boolean;
|
||||||
selectedSpace?: Room;
|
selectedSpace?: Room;
|
||||||
pendingRoomJoin: Set<string>;
|
pendingRoomJoin: Set<string>;
|
||||||
}
|
}
|
||||||
|
@ -87,6 +88,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
contextMenuPosition: null,
|
contextMenuPosition: null,
|
||||||
isDarkTheme: this.isUserOnDarkTheme(),
|
isDarkTheme: this.isUserOnDarkTheme(),
|
||||||
|
isHighContrast: this.isUserOnHighContrastTheme(),
|
||||||
pendingRoomJoin: new Set<string>(),
|
pendingRoomJoin: new Set<string>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,6 +144,18 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isUserOnHighContrastTheme(): boolean {
|
||||||
|
if (SettingsStore.getValue("use_system_theme")) {
|
||||||
|
return window.matchMedia("(prefers-contrast: more)").matches;
|
||||||
|
} else {
|
||||||
|
const theme = SettingsStore.getValue("theme");
|
||||||
|
if (theme.startsWith("custom-")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isHighContrastTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private onProfileUpdate = async () => {
|
private onProfileUpdate = async () => {
|
||||||
// the store triggered an update, so force a layout update. We don't
|
// the store triggered an update, so force a layout update. We don't
|
||||||
// have any state to store here for that to magically happen.
|
// have any state to store here for that to magically happen.
|
||||||
|
@ -153,7 +167,11 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onThemeChanged = () => {
|
private onThemeChanged = () => {
|
||||||
this.setState({ isDarkTheme: this.isUserOnDarkTheme() });
|
this.setState(
|
||||||
|
{
|
||||||
|
isDarkTheme: this.isUserOnDarkTheme(),
|
||||||
|
isHighContrast: this.isUserOnHighContrastTheme(),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = (ev: ActionPayload) => {
|
private onAction = (ev: ActionPayload) => {
|
||||||
|
@ -221,7 +239,13 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
// Disable system theme matching if the user hits this button
|
// Disable system theme matching if the user hits this button
|
||||||
SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
|
SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
|
||||||
|
|
||||||
const newTheme = this.state.isDarkTheme ? "light" : "dark";
|
let newTheme = this.state.isDarkTheme ? "light" : "dark";
|
||||||
|
if (this.state.isHighContrast) {
|
||||||
|
const hcTheme = findHighContrastTheme(newTheme);
|
||||||
|
if (hcTheme) {
|
||||||
|
newTheme = hcTheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
|
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import SettingsStore from '../SettingsStore';
|
||||||
import dis from '../../dispatcher/dispatcher';
|
import dis from '../../dispatcher/dispatcher';
|
||||||
import { Action } from '../../dispatcher/actions';
|
import { Action } from '../../dispatcher/actions';
|
||||||
import ThemeController from "../controllers/ThemeController";
|
import ThemeController from "../controllers/ThemeController";
|
||||||
import { setTheme } from "../../theme";
|
import { findHighContrastTheme, setTheme } from "../../theme";
|
||||||
import { ActionPayload } from '../../dispatcher/payloads';
|
import { ActionPayload } from '../../dispatcher/payloads';
|
||||||
import { SettingLevel } from "../SettingLevel";
|
import { SettingLevel } from "../SettingLevel";
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ export default class ThemeWatcher {
|
||||||
|
|
||||||
private preferDark: MediaQueryList;
|
private preferDark: MediaQueryList;
|
||||||
private preferLight: MediaQueryList;
|
private preferLight: MediaQueryList;
|
||||||
|
private preferHighContrast: MediaQueryList;
|
||||||
|
|
||||||
private currentTheme: string;
|
private currentTheme: string;
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ export default class ThemeWatcher {
|
||||||
// we can get the tristate of dark/light/unsupported
|
// we can get the tristate of dark/light/unsupported
|
||||||
this.preferDark = (<any>global).matchMedia("(prefers-color-scheme: dark)");
|
this.preferDark = (<any>global).matchMedia("(prefers-color-scheme: dark)");
|
||||||
this.preferLight = (<any>global).matchMedia("(prefers-color-scheme: light)");
|
this.preferLight = (<any>global).matchMedia("(prefers-color-scheme: light)");
|
||||||
|
this.preferHighContrast = (<any>global).matchMedia("(prefers-contrast: more)");
|
||||||
|
|
||||||
this.currentTheme = this.getEffectiveTheme();
|
this.currentTheme = this.getEffectiveTheme();
|
||||||
}
|
}
|
||||||
|
@ -54,6 +56,7 @@ export default class ThemeWatcher {
|
||||||
if (this.preferDark.addEventListener) {
|
if (this.preferDark.addEventListener) {
|
||||||
this.preferDark.addEventListener('change', this.onChange);
|
this.preferDark.addEventListener('change', this.onChange);
|
||||||
this.preferLight.addEventListener('change', this.onChange);
|
this.preferLight.addEventListener('change', this.onChange);
|
||||||
|
this.preferHighContrast.addEventListener('change', this.onChange);
|
||||||
}
|
}
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
@ -62,6 +65,7 @@ export default class ThemeWatcher {
|
||||||
if (this.preferDark.addEventListener) {
|
if (this.preferDark.addEventListener) {
|
||||||
this.preferDark.removeEventListener('change', this.onChange);
|
this.preferDark.removeEventListener('change', this.onChange);
|
||||||
this.preferLight.removeEventListener('change', this.onChange);
|
this.preferLight.removeEventListener('change', this.onChange);
|
||||||
|
this.preferHighContrast.removeEventListener('change', this.onChange);
|
||||||
}
|
}
|
||||||
SettingsStore.unwatchSetting(this.systemThemeWatchRef);
|
SettingsStore.unwatchSetting(this.systemThemeWatchRef);
|
||||||
SettingsStore.unwatchSetting(this.themeWatchRef);
|
SettingsStore.unwatchSetting(this.themeWatchRef);
|
||||||
|
@ -108,8 +112,10 @@ export default class ThemeWatcher {
|
||||||
SettingLevel.DEVICE, "use_system_theme", null, false, true);
|
SettingLevel.DEVICE, "use_system_theme", null, false, true);
|
||||||
if (systemThemeExplicit) {
|
if (systemThemeExplicit) {
|
||||||
logger.log("returning explicit system theme");
|
logger.log("returning explicit system theme");
|
||||||
if (this.preferDark.matches) return 'dark';
|
const theme = this.themeBasedOnSystem();
|
||||||
if (this.preferLight.matches) return 'light';
|
if (theme) {
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has specifically enabled the theme (without the system matching option being
|
// If the user has specifically enabled the theme (without the system matching option being
|
||||||
|
@ -125,13 +131,31 @@ export default class ThemeWatcher {
|
||||||
// If the user hasn't really made a preference in either direction, assume the defaults of the
|
// If the user hasn't really made a preference in either direction, assume the defaults of the
|
||||||
// settings and use those.
|
// settings and use those.
|
||||||
if (SettingsStore.getValue('use_system_theme')) {
|
if (SettingsStore.getValue('use_system_theme')) {
|
||||||
if (this.preferDark.matches) return 'dark';
|
const theme = this.themeBasedOnSystem();
|
||||||
if (this.preferLight.matches) return 'light';
|
if (theme) {
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logger.log("returning theme value");
|
logger.log("returning theme value");
|
||||||
return SettingsStore.getValue('theme');
|
return SettingsStore.getValue('theme');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private themeBasedOnSystem() {
|
||||||
|
let newTheme: string;
|
||||||
|
if (this.preferDark.matches) {
|
||||||
|
newTheme = 'dark';
|
||||||
|
} else if (this.preferLight.matches) {
|
||||||
|
newTheme = 'light';
|
||||||
|
}
|
||||||
|
if (this.preferHighContrast.matches) {
|
||||||
|
const hcTheme = findHighContrastTheme(newTheme);
|
||||||
|
if (hcTheme) {
|
||||||
|
newTheme = hcTheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newTheme;
|
||||||
|
}
|
||||||
|
|
||||||
public isSystemThemeSupported() {
|
public isSystemThemeSupported() {
|
||||||
return this.preferDark.matches || this.preferLight.matches;
|
return this.preferDark.matches || this.preferLight.matches;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue