From fe3522014517b22c233db59916169f64a7d31aa7 Mon Sep 17 00:00:00 2001 From: dengr1065 Date: Mon, 20 Jun 2022 16:35:37 +0300 Subject: [PATCH 1/2] Implement system theme preference --- electron/index.js | 20 +++++++++++++- src/js/game/theme.js | 37 +++++++++++++++++++++++++- src/js/game/themes/dark.json | 2 ++ src/js/game/themes/light.json | 2 ++ src/js/mods/mod_interface.js | 2 ++ src/js/profile/application_settings.js | 17 +++++------- src/js/states/main_menu.js | 4 +++ src/js/states/preload.js | 2 ++ translations/base-en.yaml | 1 + 9 files changed, 75 insertions(+), 12 deletions(-) diff --git a/electron/index.js b/electron/index.js index 31b4bca2..d641fc56 100644 --- a/electron/index.js +++ b/electron/index.js @@ -1,6 +1,16 @@ /* eslint-disable quotes,no-undef */ -const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron"); +const { + app, + BrowserWindow, + Menu, + MenuItem, + ipcMain, + shell, + dialog, + session, + nativeTheme, +} = require("electron"); const path = require("path"); const url = require("url"); const fs = require("fs"); @@ -97,6 +107,10 @@ function createWindow() { win.webContents.session.clearCache(); win.webContents.session.clearStorageData(); + nativeTheme.on("updated", () => { + win.webContents.send("system-theme-updated"); + }); + ////// SECURITY // Disable permission requests @@ -381,6 +395,10 @@ ipcMain.handle("get-mods", async () => { return mods; }); +ipcMain.handle("get-system-theme", async () => { + return nativeTheme.shouldUseDarkColors ? "dark" : "light"; +}); + steam.init(isDev); // Only allow achievements and puzzle DLC if no mods are loaded diff --git a/src/js/game/theme.js b/src/js/game/theme.js index 251f4433..c4e15e5a 100644 --- a/src/js/game/theme.js +++ b/src/js/game/theme.js @@ -4,7 +4,42 @@ export const THEMES = { }; export let THEME = THEMES.light; +let currentThemePreference = "light"; + +THEMES.system = THEMES.light; +ipcRenderer.on("system-theme-updated", detectSystemTheme); + +export function detectSystemTheme() { + return ipcRenderer + .invoke("get-system-theme") + .then(theme => (THEMES.system = THEMES[theme])) + .catch(() => (THEMES.system = THEMES.light)) + .then(() => { + // Re-apply the theme, this only affects system + applyGameTheme(); + }); +} export function applyGameTheme(id) { - THEME = THEMES[id]; + if (id === undefined) { + id = currentThemePreference; + } + + const isSystem = id === "system"; + const themeId = isSystem ? THEMES.system.id : id; + + if (!isSystem && id === undefined) { + // Re-applying light/dark themes is not needed + return; + } + + if (document.body.id != "state_InGameState") { + // Only set the theme if not playing, otherwise this causes bugs + // Main menu re-applies the theme anyway + THEME = THEMES[themeId]; + document.documentElement.setAttribute("data-theme", themeId); + } + + // Keep the theme to re-apply system theme + currentThemePreference = id; } diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index 8650d56f..00bcc2dc 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -1,4 +1,6 @@ { + "id": "dark", + "map": { "background": "#3e3f47", "gridRegular": "rgba(255, 255, 255, 0.02)", diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index cd7677e2..26b65cdd 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -1,4 +1,6 @@ { + "id": "light", + "map": { "background": "#eceef2", diff --git a/src/js/mods/mod_interface.js b/src/js/mods/mod_interface.js index 8130dc5a..69eaee92 100644 --- a/src/js/mods/mod_interface.js +++ b/src/js/mods/mod_interface.js @@ -399,6 +399,8 @@ export class ModInterface { */ registerGameTheme({ id, name, theme }) { THEMES[id] = theme; + theme.id = id; + this.registerTranslations("en", { settings: { labels: { diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 281b532c..f048a3f9 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -6,7 +6,7 @@ import { ReadWriteProxy } from "../core/read_write_proxy"; import { BoolSetting, EnumSetting, RangeSetting, BaseSetting } from "./setting_types"; import { createLogger } from "../core/logging"; import { ExplainedResult } from "../core/explained_result"; -import { THEMES, applyGameTheme } from "../game/theme"; +import { THEMES, applyGameTheme, detectSystemTheme } from "../game/theme"; import { T } from "../translations"; import { LANGUAGES } from "../languages"; @@ -209,14 +209,7 @@ function initializeSettings() { textGetter: theme => T.settings.labels.theme.themes[theme], category: enumCategories.userInterface, restartRequired: false, - changeCb: - /** - * @param {Application} app - */ - (app, id) => { - applyGameTheme(id); - document.documentElement.setAttribute("data-theme", id); - }, + changeCb: (app, id) => applyGameTheme(id), enabledCb: /** * @param {Application} app */ app => app.restrictionMgr.getHasExtendedSettings(), @@ -298,7 +291,7 @@ class SettingsStorage { this.soundVolume = 1.0; this.musicVolume = 1.0; - this.theme = "light"; + this.theme = "system"; this.refreshRate = "60"; this.scrollWheelSensitivity = "regular"; this.movementSpeed = "regular"; @@ -344,6 +337,10 @@ export class ApplicationSettings extends ReadWriteProxy { initialize() { // Read and directly write latest data back return this.readAsync() + .then(() => { + // Make sure it's ready + return detectSystemTheme(); + }) .then(() => { // Apply default setting callbacks const settings = this.getAllSettings(); diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index 1e7c8c07..599796b3 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -16,6 +16,7 @@ import { waitNextFrame, } from "../core/utils"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { detectSystemTheme } from "../game/theme"; import { MODS } from "../mods/modloader"; import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper"; import { PlatformWrapperImplElectron } from "../platform/electron/wrapper"; @@ -348,6 +349,9 @@ export class MainMenuState extends GameState { ); } + // Apply the system theme if returning from InGameState + detectSystemTheme(); + if (G_IS_DEV && globalConfig.debug.testPuzzleMode) { this.onPuzzleModeButtonClicked(true); return; diff --git a/src/js/states/preload.js b/src/js/states/preload.js index 3706555f..706640ec 100644 --- a/src/js/states/preload.js +++ b/src/js/states/preload.js @@ -6,6 +6,7 @@ import { createLogger } from "../core/logging"; import { getLogoSprite } from "../core/utils"; import { getRandomHint } from "../game/hints"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { detectSystemTheme } from "../game/theme"; import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper"; import { autoDetectLanguageId, T, updateApplicationLanguage } from "../translations"; @@ -108,6 +109,7 @@ export class PreloadState extends GameState { .then(() => this.fetchDiscounts()) .then(() => this.setStatus("Initializing settings", 20)) + .then(() => detectSystemTheme()) .then(() => { return this.app.settings.initialize(); }) diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 27f00080..2bc1fe62 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -1267,6 +1267,7 @@ settings: themes: dark: Dark light: Light + system: System refreshRate: title: Tick Rate From d8a8bf7a5818648611893eba83d010535b699bec Mon Sep 17 00:00:00 2001 From: dengr1065 Date: Mon, 20 Jun 2022 17:22:47 +0300 Subject: [PATCH 2/2] Check for standalone before using the system theme --- src/js/game/theme.js | 10 ++++++++-- src/js/profile/application_settings.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/js/game/theme.js b/src/js/game/theme.js index c4e15e5a..2a451b9f 100644 --- a/src/js/game/theme.js +++ b/src/js/game/theme.js @@ -6,10 +6,16 @@ export const THEMES = { export let THEME = THEMES.light; let currentThemePreference = "light"; -THEMES.system = THEMES.light; -ipcRenderer.on("system-theme-updated", detectSystemTheme); +if (G_IS_STANDALONE) { + THEMES.system = THEMES.light; + ipcRenderer.on("system-theme-updated", detectSystemTheme); +} export function detectSystemTheme() { + if (!G_IS_STANDALONE) { + return; + } + return ipcRenderer .invoke("get-system-theme") .then(theme => (THEMES.system = THEMES[theme])) diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index f048a3f9..6aeb4e4c 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -291,7 +291,7 @@ class SettingsStorage { this.soundVolume = 1.0; this.musicVolume = 1.0; - this.theme = "system"; + this.theme = G_IS_STANDALONE ? "system" : "light"; this.refreshRate = "60"; this.scrollWheelSensitivity = "regular"; this.movementSpeed = "regular";