From 1549f5ebba5f7596c3a76974005d98a0fe50f4af Mon Sep 17 00:00:00 2001 From: Richard Race <1273965+richrace@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:22:08 +0100 Subject: [PATCH] UI improvements (#11) * Update how emojis are handled * Use template icon to support light/dark theme for macOS * Removed npm-emoji as it didn't support low battery * Handle light/dark theme on Windows --- ...adphones.png => headphonesTemplate@1x.png} | Bin assets/headphonesTemplate@2x.png | Bin 0 -> 709 bytes assets/icon_white.ico | Bin 0 -> 5629 bytes package-lock.json | 15 ++++++++- package.json | 4 ++- src/headphone_view.ts | 12 ++++--- src/main.ts | 10 ++---- src/tray.ts | 26 +++++++++++++-- src/windows/handle_themes.ts | 31 ++++++++++++++++++ src/windows/icon_picker.ts | 14 ++++++++ 10 files changed, 94 insertions(+), 18 deletions(-) rename assets/{headphones.png => headphonesTemplate@1x.png} (100%) create mode 100644 assets/headphonesTemplate@2x.png create mode 100644 assets/icon_white.ico create mode 100644 src/windows/handle_themes.ts create mode 100644 src/windows/icon_picker.ts diff --git a/assets/headphones.png b/assets/headphonesTemplate@1x.png similarity index 100% rename from assets/headphones.png rename to assets/headphonesTemplate@1x.png diff --git a/assets/headphonesTemplate@2x.png b/assets/headphonesTemplate@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5621a527b190585113010820b2c3bed064c16952 GIT binary patch literal 709 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 TlU#nO+yeSKM%oRUnAraT2mn6z literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index 95dd0e2..3323166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "license": "MIT", "dependencies": { "arctis-usb-finder": "^0.0.19", - "electron-squirrel-startup": "^1.0.0" + "electron-squirrel-startup": "^1.0.0", + "winreg": "^1.2.4" }, "devDependencies": { "@electron-forge/cli": "^6.1.1", @@ -21,6 +22,7 @@ "@electron-forge/maker-zip": "^6.1.1", "@electron-forge/publisher-github": "^6.1.1", "@types/node-hid": "^1.3.1", + "@types/winreg": "^1.2.32", "@typescript-eslint/eslint-plugin": "^5.59.6", "@typescript-eslint/parser": "^5.59.6", "electron": "^26.1.0", @@ -1189,6 +1191,12 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/winreg": { + "version": "1.2.32", + "resolved": "https://registry.npmjs.org/@types/winreg/-/winreg-1.2.32.tgz", + "integrity": "sha512-+TgocDdajdQVGVDw0XxaOH2rn2YTb8zHqDKdcqj9bHiE0NZaU1KL9StdApF74bNvMx+itYabv9NT6E/iB94N/A==", + "dev": true + }, "node_modules/@types/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", @@ -7067,6 +7075,11 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/winreg": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.4.tgz", + "integrity": "sha512-IHpzORub7kYlb8A43Iig3reOvlcBJGX9gZ0WycHhghHtA65X0LYnMRuJs+aH1abVnMJztQkvQNlltnbPi5aGIA==" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index c225fd0..c0245a7 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@electron-forge/maker-zip": "^6.1.1", "@electron-forge/publisher-github": "^6.1.1", "@types/node-hid": "^1.3.1", + "@types/winreg": "^1.2.32", "@typescript-eslint/eslint-plugin": "^5.59.6", "@typescript-eslint/parser": "^5.59.6", "electron": "^26.1.0", @@ -37,6 +38,7 @@ }, "dependencies": { "arctis-usb-finder": "^0.0.19", - "electron-squirrel-startup": "^1.0.0" + "electron-squirrel-startup": "^1.0.0", + "winreg": "^1.2.4" } } diff --git a/src/headphone_view.ts b/src/headphone_view.ts index d341fe8..e8cf5f9 100644 --- a/src/headphone_view.ts +++ b/src/headphone_view.ts @@ -17,18 +17,20 @@ function exportView(mainTray: any, headphone: SimpleHeadphone) { mainTray.setToolTip(`${text}`); mainTray.setTitle(` ${percentage}%`); - if (headphone.isCharging) { - text += ' 🔋 '; + if (percentage === 100) { + text += ' \uD83D\uDD0B '; + } else if (headphone.isCharging) { + text += ' \uD83D\uDD0B '; } if (headphone.isDischarging) { - text += ' đŸĒĢ '; + text += ' \uD83E\uDEAB '; } if (headphone.isMuted) { - text += ' 🔇 '; + text += ' \uD83D\uDD07 '; } else { - text += ' 🔊 '; + text += ' \uD83D\uDD0A '; } return new MenuItem({ label: text }); diff --git a/src/main.ts b/src/main.ts index 81129aa..ecbff27 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,9 @@ import { app, BrowserWindow } from 'electron'; import createTray from './tray'; -import path = require('path'); - if (require('electron-squirrel-startup')) app.quit(); -let mainWindow: any; +let mainWindow: BrowserWindow; const createWindow = () => { mainWindow = new BrowserWindow({ @@ -30,13 +28,9 @@ const createWindow = () => { // Don't show the app in the doc app.dock?.hide(); -function handleQuit() { - app.quit(); -} - app.whenReady().then(() => { createWindow(); - createTray(path); + createTray(); }); // Quit the app when the window is closed diff --git a/src/tray.ts b/src/tray.ts index 6a49687..21ac08e 100644 --- a/src/tray.ts +++ b/src/tray.ts @@ -1,13 +1,18 @@ +import path = require('path'); + import { Tray, Menu, MenuItem } from 'electron'; import SimpleHeadphone from 'arctis-usb-finder/dist/interfaces/simple_headphone'; +import Host from 'arctis-usb-finder/dist/utils/host'; import exportView from './headphone_view'; import debugMenu from './menu_items/debug'; import helpMenuItem from './menu_items/help'; import quitMenuItem from './menu_items/quit'; import HeadphoneManager from './headphone_manager'; +import HandleThemes from './windows/handle_themes'; +import iconPicker from './windows/icon_picker'; -let mainTray: any; +let mainTray: Tray; const headphoneManager = new HeadphoneManager(); const buildTrayMenu = (force: boolean = false, debug: boolean = false) => { @@ -31,9 +36,24 @@ const buildTrayMenu = (force: boolean = false, debug: boolean = false) => { mainTray.setContextMenu(contextMenu); }; -const createTray = (path: any) => { +const icon = (): string => { const assetsDirectory = path.join(__dirname, '../assets'); - mainTray = new Tray(path.join(assetsDirectory, 'headphones.png')); + + if (Host.isMac()) { + return path.join(assetsDirectory, 'headphonesTemplate.png'); + } else { + return path.join(assetsDirectory, 'headphonesTemplate@2x.png'); + } +}; + +const createTray = async () => { + if (Host.isWin()) { + const handleThemes = new HandleThemes(); + mainTray = new Tray(iconPicker(await handleThemes.isUsedSystemLightTheme())); + handleThemes.tray = mainTray; + } else { + mainTray = new Tray(icon()); + } const contextMenu = Menu.buildFromTemplate([ { label: 'No Headphones USB devices plugged in', type: 'normal' }, diff --git a/src/windows/handle_themes.ts b/src/windows/handle_themes.ts new file mode 100644 index 0000000..08b64bd --- /dev/null +++ b/src/windows/handle_themes.ts @@ -0,0 +1,31 @@ +import { Tray, nativeTheme } from 'electron'; +import Registry = require('winreg'); +import iconPicker from './icon_picker'; + +export default class HandleThemes { + tray: Tray; + + constructor() { + nativeTheme.on('updated', () => this.handleThemeChange()); + } + + async isUsedSystemLightTheme(): Promise { + const reg = new Registry({ + hive: Registry.HKCU, + key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize', + }); + return new Promise((resolve) => + reg.get('SystemUsesLightTheme', (err: any, item: { value: string }) => { + if (!err) return resolve(item.value === '0x1'); + resolve(false); + }) + ); + } + + async handleThemeChange() { + const isLightTheme = await this.isUsedSystemLightTheme(); + console.log('in updated', isLightTheme); + + this.tray.setImage(iconPicker(isLightTheme)); + } +} diff --git a/src/windows/icon_picker.ts b/src/windows/icon_picker.ts new file mode 100644 index 0000000..c913538 --- /dev/null +++ b/src/windows/icon_picker.ts @@ -0,0 +1,14 @@ +import path = require('path'); + +const iconPicker = (lightTheme: boolean) => { + const assetsDirectory = path.join(__dirname, '../../assets'); + + console.log('icon', lightTheme); + if (lightTheme) { + return path.join(assetsDirectory, 'icon.ico'); + } else { + return path.join(assetsDirectory, 'icon_white.ico'); + } +}; + +export default iconPicker;