From 8ff64d285ae4c01a53c7abac662d5474f08e4007 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:58:33 -0400 Subject: [PATCH] [MM-59841] Migrate to `titleBarOverlay` for Windows (#3111) --- src/assets/titlebar/chrome-close.svg | 3 - src/assets/titlebar/chrome-maximize.svg | 3 - src/assets/titlebar/chrome-minimize.svg | 3 - src/assets/titlebar/chrome-restore.svg | 3 - src/common/communication.ts | 5 +- src/common/config/index.test.js | 4 -- src/common/config/index.ts | 10 +-- src/main/app/initialize.ts | 12 ---- src/main/app/windows.ts | 14 ---- src/main/preload/internalAPI.js | 10 +-- src/main/windows/mainWindow.ts | 35 +++------- src/renderer/components/MainPage.tsx | 90 +++++-------------------- src/renderer/css/index.css | 28 +------- src/renderer/index.tsx | 1 - src/types/config.ts | 1 - src/types/window.ts | 5 +- 16 files changed, 33 insertions(+), 194 deletions(-) delete mode 100644 src/assets/titlebar/chrome-close.svg delete mode 100644 src/assets/titlebar/chrome-maximize.svg delete mode 100644 src/assets/titlebar/chrome-minimize.svg delete mode 100644 src/assets/titlebar/chrome-restore.svg diff --git a/src/assets/titlebar/chrome-close.svg b/src/assets/titlebar/chrome-close.svg deleted file mode 100644 index cb148f5a..00000000 --- a/src/assets/titlebar/chrome-close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/titlebar/chrome-maximize.svg b/src/assets/titlebar/chrome-maximize.svg deleted file mode 100644 index 0d991756..00000000 --- a/src/assets/titlebar/chrome-maximize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/titlebar/chrome-minimize.svg b/src/assets/titlebar/chrome-minimize.svg deleted file mode 100644 index 62f72c70..00000000 --- a/src/assets/titlebar/chrome-minimize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/titlebar/chrome-restore.svg b/src/assets/titlebar/chrome-restore.svg deleted file mode 100644 index 7dc8f55b..00000000 --- a/src/assets/titlebar/chrome-restore.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/common/communication.ts b/src/common/communication.ts index 841d7b92..d1cc0a8b 100644 --- a/src/common/communication.ts +++ b/src/common/communication.ts @@ -45,10 +45,7 @@ export const MODAL_RESULT = 'modal-result'; export const MODAL_OPEN = 'modal-open'; export const MODAL_CLOSE = 'modal-close'; export const NOTIFY_MENTION = 'notify_mention'; -export const WINDOW_CLOSE = 'window_close'; -export const WINDOW_MINIMIZE = 'window_minimize'; -export const WINDOW_MAXIMIZE = 'window_maximize'; -export const WINDOW_RESTORE = 'window_restore'; +export const EXIT_FULLSCREEN = 'exit-fullscreen'; export const GET_FULL_SCREEN_STATUS = 'get-full-screen-status'; export const UPDATE_TARGET_URL = 'update_target_url'; diff --git a/src/common/config/index.test.js b/src/common/config/index.test.js index 7f106682..8caaaf50 100644 --- a/src/common/config/index.test.js +++ b/src/common/config/index.test.js @@ -319,7 +319,6 @@ describe('common/config', () => { const config = new Config(); config.reload = jest.fn(); config.init(configPath, appName, appPath); - config.useNativeWindow = false; config.defaultConfigData = {defaultSetting: 'default', otherDefaultSetting: 'default'}; config.localConfigData = {otherDefaultSetting: 'local', localSetting: 'local', otherLocalSetting: 'local'}; config.buildConfigData = {otherLocalSetting: 'build', buildSetting: 'build', otherBuildSetting: 'build'}; @@ -329,7 +328,6 @@ describe('common/config', () => { config.combinedData.darkMode = false; expect(config.combinedData).toStrictEqual({ appName: 'app-name', - useNativeWindow: false, darkMode: false, otherBuildSetting: 'registry', registrySetting: 'registry', @@ -350,7 +348,6 @@ describe('common/config', () => { config.buildConfigData = {enableServerManagement: true}; config.registryConfigData = {}; config.predefinedServers.push(server, server); - config.useNativeWindow = false; config.localConfigData = {teams: [ server, { @@ -370,7 +367,6 @@ describe('common/config', () => { config.combinedData.darkMode = false; expect(config.combinedData).toStrictEqual({ appName: 'app-name', - useNativeWindow: false, darkMode: false, enableServerManagement: true, }); diff --git a/src/common/config/index.ts b/src/common/config/index.ts index 5baa4f0c..07ee14eb 100644 --- a/src/common/config/index.ts +++ b/src/common/config/index.ts @@ -2,13 +2,12 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import fs from 'fs'; -import os from 'os'; import path from 'path'; import {EventEmitter} from 'events'; import {Logger} from 'common/log'; -import Utils, {copy} from 'common/utils/util'; +import {copy} from 'common/utils/util'; import * as Validator from 'common/Validator'; import {getDefaultViewsForConfigServer} from 'common/views/View'; @@ -36,7 +35,6 @@ export class Config extends EventEmitter { private registryConfig: RegistryConfig; private _predefinedServers: ConfigServer[]; - private useNativeWindow: boolean; private combinedData?: CombinedConfig; private localConfigData?: ConfigType; @@ -52,11 +50,6 @@ export class Config extends EventEmitter { if (buildConfig.defaultServers) { this._predefinedServers.push(...buildConfig.defaultServers.map((server, index) => getDefaultViewsForConfigServer({...server, order: index}))); } - try { - this.useNativeWindow = os.platform() === 'win32' && !Utils.isVersionGreaterThanOrEqualTo(os.release(), '6.2'); - } catch { - this.useNativeWindow = false; - } } init = (configFilePath: string, appName: string, appPath: string) => { @@ -370,7 +363,6 @@ export class Config extends EventEmitter { this.localConfigData, this.buildConfigData, this.registryConfigData, - {useNativeWindow: this.useNativeWindow}, ); // We don't want to include the servers in the combined config, they should only be accesible via the ServerManager diff --git a/src/main/app/initialize.ts b/src/main/app/initialize.ts index 387df0d8..637bbb48 100644 --- a/src/main/app/initialize.ts +++ b/src/main/app/initialize.ts @@ -25,10 +25,6 @@ import { UPDATE_PATHS, SERVERS_URL_MODIFIED, GET_DARK_MODE, - WINDOW_CLOSE, - WINDOW_MAXIMIZE, - WINDOW_MINIMIZE, - WINDOW_RESTORE, DOUBLE_CLICK_ON_WINDOW, TOGGLE_SECURE_INPUT, GET_APP_INFO, @@ -97,12 +93,8 @@ import { flushCookiesStore, } from './utils'; import { - handleClose, handleDoubleClick, handleGetDarkMode, - handleMaximize, - handleMinimize, - handleRestore, } from './windows'; import {protocols} from '../../../electron-builder.json'; @@ -283,10 +275,6 @@ function initializeInterCommunicationEventListeners() { ipcMain.on(UPDATE_CONFIGURATION, updateConfiguration); ipcMain.handle(GET_DARK_MODE, handleGetDarkMode); - ipcMain.on(WINDOW_CLOSE, handleClose); - ipcMain.on(WINDOW_MAXIMIZE, handleMaximize); - ipcMain.on(WINDOW_MINIMIZE, handleMinimize); - ipcMain.on(WINDOW_RESTORE, handleRestore); ipcMain.on(DOUBLE_CLICK_ON_WINDOW, handleDoubleClick); ipcMain.on(TOGGLE_SECURE_INPUT, handleToggleSecureInput); diff --git a/src/main/app/windows.ts b/src/main/app/windows.ts index 246f4e6d..57cfdd69 100644 --- a/src/main/app/windows.ts +++ b/src/main/app/windows.ts @@ -13,20 +13,6 @@ export const handleGetDarkMode = () => { return Config.darkMode; }; -export const handleClose = (event: IpcMainEvent) => BrowserWindow.fromWebContents(event.sender)?.close(); -export const handleMaximize = (event: IpcMainEvent) => BrowserWindow.fromWebContents(event.sender)?.maximize(); -export const handleMinimize = (event: IpcMainEvent) => BrowserWindow.fromWebContents(event.sender)?.minimize(); -export const handleRestore = (event: IpcMainEvent) => { - const window = BrowserWindow.fromWebContents(event.sender); - if (!window) { - return; - } - window.restore(); - if (window.isFullScreen()) { - window.setFullScreen(false); - } -}; - export const handleDoubleClick = (event: IpcMainEvent, windowType?: string) => { log.debug('handleDoubleClick', windowType); diff --git a/src/main/preload/internalAPI.js b/src/main/preload/internalAPI.js index 72cb2d7d..161f3b59 100644 --- a/src/main/preload/internalAPI.js +++ b/src/main/preload/internalAPI.js @@ -14,10 +14,7 @@ import { OPEN_SERVERS_DROPDOWN, SWITCH_TAB, CLOSE_VIEW, - WINDOW_CLOSE, - WINDOW_MINIMIZE, - WINDOW_MAXIMIZE, - WINDOW_RESTORE, + EXIT_FULLSCREEN, DOUBLE_CLICK_ON_WINDOW, FOCUS_BROWSERVIEW, RELOAD_CURRENT_VIEW, @@ -117,10 +114,7 @@ contextBridge.exposeInMainWorld('desktop', { openServersDropdown: () => ipcRenderer.send(OPEN_SERVERS_DROPDOWN), switchTab: (viewId) => ipcRenderer.send(SWITCH_TAB, viewId), closeView: (viewId) => ipcRenderer.send(CLOSE_VIEW, viewId), - closeWindow: () => ipcRenderer.send(WINDOW_CLOSE), - minimizeWindow: () => ipcRenderer.send(WINDOW_MINIMIZE), - maximizeWindow: () => ipcRenderer.send(WINDOW_MAXIMIZE), - restoreWindow: () => ipcRenderer.send(WINDOW_RESTORE), + exitFullScreen: () => ipcRenderer.send(EXIT_FULLSCREEN), doubleClickOnWindow: (windowName) => ipcRenderer.send(DOUBLE_CLICK_ON_WINDOW, windowName), focusCurrentView: () => ipcRenderer.send(FOCUS_BROWSERVIEW), reloadCurrentView: () => ipcRenderer.send(RELOAD_CURRENT_VIEW), diff --git a/src/main/windows/mainWindow.ts b/src/main/windows/mainWindow.ts index b88ca94e..074036ef 100644 --- a/src/main/windows/mainWindow.ts +++ b/src/main/windows/mainWindow.ts @@ -18,13 +18,13 @@ import { SERVERS_UPDATE, UPDATE_APPSTATE_FOR_VIEW_ID, UPDATE_MENTIONS, - MAXIMIZE_CHANGE, MAIN_WINDOW_CREATED, MAIN_WINDOW_RESIZED, MAIN_WINDOW_FOCUSED, VIEW_FINISHED_RESIZING, TOGGLE_SECURE_INPUT, EMIT_CONFIGURATION, + EXIT_FULLSCREEN, } from 'common/communication'; import Config from 'common/config'; import {Logger} from 'common/log'; @@ -50,7 +50,6 @@ export class MainWindow extends EventEmitter { private ready: boolean; private isResizing: boolean; private lastEmittedBounds?: Electron.Rectangle; - private isMaximized: boolean; constructor() { super(); @@ -58,11 +57,11 @@ export class MainWindow extends EventEmitter { // Create the browser window. this.ready = false; this.isResizing = false; - this.isMaximized = false; ipcMain.handle(GET_FULL_SCREEN_STATUS, () => this.win?.isFullScreen()); ipcMain.on(VIEW_FINISHED_RESIZING, this.handleViewFinishedResizing); ipcMain.on(EMIT_CONFIGURATION, this.handleUpdateTitleBarOverlay); + ipcMain.on(EXIT_FULLSCREEN, this.handleExitFullScreen); ServerManager.on(SERVERS_UPDATE, this.handleUpdateConfig); @@ -83,7 +82,7 @@ export class MainWindow extends EventEmitter { frame: !this.isFramelessWindow(), fullscreen: this.shouldStartFullScreen(), titleBarStyle: 'hidden' as const, - titleBarOverlay: process.platform === 'linux' ? this.getTitleBarOverlay() : false, + titleBarOverlay: this.getTitleBarOverlay(), trafficLightPosition: {x: 12, y: 12}, backgroundColor: '#fff', // prevents blurry text: https://electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do webPreferences: { @@ -129,9 +128,6 @@ export class MainWindow extends EventEmitter { this.win.on('focus', this.onFocus); this.win.on('blur', this.onBlur); this.win.on('unresponsive', this.onUnresponsive); - this.win.on('maximize', this.onMaximize); - this.win.on('unmaximize', this.onUnmaximize); - this.win.on('restore', this.onRestore); this.win.on('enter-full-screen', this.onEnterFullScreen); this.win.on('leave-full-screen', this.onLeaveFullScreen); this.win.on('will-resize', this.onWillResize); @@ -252,6 +248,7 @@ export class MainWindow extends EventEmitter { private getTitleBarOverlay = () => { return { color: Config.darkMode ? '#2e2e2e' : '#efefef', + symbolColor: Config.darkMode ? '#c1c1c1' : '#474747', height: TAB_BAR_HEIGHT, }; }; @@ -465,24 +462,6 @@ export class MainWindow extends EventEmitter { }, 10); }; - private onMaximize = () => { - this.isMaximized = true; - this.win?.webContents.send(MAXIMIZE_CHANGE, true); - this.emitBounds(); - }; - - private onUnmaximize = () => { - this.isMaximized = false; - this.win?.webContents.send(MAXIMIZE_CHANGE, false); - this.emitBounds(); - }; - - private onRestore = () => { - if (this.isMaximized && !this.win?.isMaximized()) { - this.win?.maximize(); - } - }; - private onEnterFullScreen = () => { this.win?.webContents.send('enter-full-screen'); this.emitBounds(); @@ -544,6 +523,12 @@ export class MainWindow extends EventEmitter { this.isResizing = false; }; + private handleExitFullScreen = () => { + if (this.win?.isFullScreen()) { + this.win.setFullScreen(false); + } + }; + /** * Server Manager update handler */ diff --git a/src/renderer/components/MainPage.tsx b/src/renderer/components/MainPage.tsx index 2bafaa70..536b9727 100644 --- a/src/renderer/components/MainPage.tsx +++ b/src/renderer/components/MainPage.tsx @@ -18,10 +18,6 @@ import ExtraBar from './ExtraBar'; import ServerDropdownButton from './ServerDropdownButton'; import TabBar from './TabBar'; -import closeButton from '../../assets/titlebar/chrome-close.svg'; -import maximizeButton from '../../assets/titlebar/chrome-maximize.svg'; -import minimizeButton from '../../assets/titlebar/chrome-minimize.svg'; -import restoreButton from '../../assets/titlebar/chrome-restore.svg'; import {playSound} from '../notificationSounds'; import '../css/components/UpgradeButton.scss'; @@ -38,7 +34,6 @@ type Props = { openMenu: () => void; darkMode: boolean; appName: string; - useNativeWindow: boolean; intl: IntlShape; }; @@ -326,23 +321,13 @@ class MainPage extends React.PureComponent { this.handleSelectTab(tab[0].id!); }; - handleClose = (e: React.MouseEvent) => { + handleExitFullScreen = (e: React.MouseEvent) => { e.stopPropagation(); // since it is our button, the event goes into MainPage's onclick event, getting focus back. - window.desktop.closeWindow(); - }; - handleMinimize = (e: React.MouseEvent) => { - e.stopPropagation(); - window.desktop.minimizeWindow(); - }; - - handleMaximize = (e: React.MouseEvent) => { - e.stopPropagation(); - window.desktop.maximizeWindow(); - }; - - handleRestore = () => { - window.desktop.restoreWindow(); + if (!this.state.fullScreen) { + return; + } + window.desktop.exitFullScreen(); }; openMenu = () => { @@ -430,60 +415,6 @@ class MainPage extends React.PureComponent { /> ) : null; - let maxButton; - if (this.state.maximized || this.state.fullScreen) { - maxButton = ( -
- -
- ); - } else { - maxButton = ( -
- -
- ); - } - - let titleBarButtons; - if (window.process.platform === 'win32' && !this.props.useNativeWindow) { - titleBarButtons = ( - -
- -
- {maxButton} -
- -
-
- ); - } - const totalMentionCount = Object.keys(this.state.mentionCounts).reduce((sum, key) => { // Strip out current server from unread and mention counts if (this.state.tabs.get(this.state.activeServerId!)?.map((tab) => tab.id).includes(key)) { @@ -541,7 +472,16 @@ class MainPage extends React.PureComponent { )} {tabsRow} {downloadsDropdownButton} - {titleBarButtons} + {window.process.platform !== 'darwin' && this.state.fullScreen && + +
+ +
+
+ } ); diff --git a/src/renderer/css/index.css b/src/renderer/css/index.css index 3605a68a..24a2286a 100644 --- a/src/renderer/css/index.css +++ b/src/renderer/css/index.css @@ -176,23 +176,8 @@ body { background: rgba(0,0,0,0.2); } -.topBar .title-bar-btns>.close-button:hover { - background: #E81123 !important; -} - -.topBar .title-bar-btns>.close-button:hover>img { - filter: invert(100%); - -webkit-filter: invert(100%); - opacity: 1; -} - -.topBar .title-bar-btns>.close-button:active { - background: #f1707a !important; -} - -.topBar .title-bar-btns>.close-button:active>img { - filter: invert(100%); - -webkit-filter: invert(100%); +.topBar .title-bar-btns>.full-screen-button { + font-size: 18px; } .topBar .title-bar-btns img { @@ -204,14 +189,7 @@ body { -webkit-filter: invert(100%); } -.topBar .title-bar-btns>.min-button { - grid-column: 1; -} -.topBar .title-bar-btns>.max-button, .topBar .title-bar-btns>.restore-button { - grid-column: 2; -} - -.topBar .title-bar-btns>.close-button { +.topBar .title-bar-btns>.close-button, .topBar .title-bar-btns>.full-screen-button { grid-column: 3; } diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 5ca646af..abe3f25d 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -81,7 +81,6 @@ class Root extends React.PureComponent, State> { openMenu={this.openMenu} darkMode={config.darkMode} appName={config.appName} - useNativeWindow={config.useNativeWindow} /> ); diff --git a/src/types/config.ts b/src/types/config.ts index 7cefe22d..cbd12e94 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -119,7 +119,6 @@ export type RegistryConfig = { export type CombinedConfig = Omit & Omit & { appName: string; - useNativeWindow: boolean; } export type LocalConfiguration = Config & { diff --git a/src/types/window.ts b/src/types/window.ts index cb8f7785..583f2fa8 100644 --- a/src/types/window.ts +++ b/src/types/window.ts @@ -34,10 +34,7 @@ declare global { openServersDropdown: () => void; switchTab: (viewId: string) => void; closeView: (viewId: string) => void; - closeWindow: () => void; - minimizeWindow: () => void; - maximizeWindow: () => void; - restoreWindow: () => void; + exitFullScreen: () => void; doubleClickOnWindow: (windowName?: string) => void; focusCurrentView: () => void; reloadCurrentView: () => void;