[MM-59841] Migrate to titleBarOverlay for Windows (#3111)

This commit is contained in:
Devin Binnie 2024-07-31 14:58:33 -04:00 committed by GitHub
parent b2cd2dc60d
commit 8ff64d285a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 33 additions and 194 deletions

View file

@ -1,3 +0,0 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.16763 0.00233459L9.98984 0.825763L5.8259 4.99587L9.99767 9.16147L9.17424 9.98368L5.0037 5.8193L0.831506 9.99768L0.0092966 9.17425L4.18027 4.99709L0.00232887 0.82533L0.825757 0.00311899L5.00248 4.17366L9.16763 0.00233459Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 392 B

View file

@ -1,3 +0,0 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="9" height="9" stroke="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 163 B

View file

@ -1,3 +0,0 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="4" width="10" height="1" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 152 B

View file

@ -1,3 +0,0 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 8V10H0V2H2V0H10V8H8ZM7 3H1V9H7V3ZM3 2H8V7H9V1H3V2Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 222 B

View file

@ -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';

View file

@ -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,
});

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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),

View file

@ -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
*/

View file

@ -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<Props, State> {
this.handleSelectTab(tab[0].id!);
};
handleClose = (e: React.MouseEvent<HTMLDivElement>) => {
handleExitFullScreen = (e: React.MouseEvent<HTMLDivElement>) => {
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<HTMLDivElement>) => {
e.stopPropagation();
window.desktop.minimizeWindow();
};
handleMaximize = (e: React.MouseEvent<HTMLDivElement>) => {
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<Props, State> {
/>
) : null;
let maxButton;
if (this.state.maximized || this.state.fullScreen) {
maxButton = (
<div
className='button restore-button'
onClick={this.handleRestore}
>
<img
src={restoreButton}
draggable={false}
/>
</div>
);
} else {
maxButton = (
<div
className='button max-button'
onClick={this.handleMaximize}
>
<img
src={maximizeButton}
draggable={false}
/>
</div>
);
}
let titleBarButtons;
if (window.process.platform === 'win32' && !this.props.useNativeWindow) {
titleBarButtons = (
<span className='title-bar-btns'>
<div
className='button min-button'
onClick={this.handleMinimize}
>
<img
src={minimizeButton}
draggable={false}
/>
</div>
{maxButton}
<div
className='button close-button'
onClick={this.handleClose}
>
<img
src={closeButton}
draggable={false}
/>
</div>
</span>
);
}
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<Props, State> {
)}
{tabsRow}
{downloadsDropdownButton}
{titleBarButtons}
{window.process.platform !== 'darwin' && this.state.fullScreen &&
<span className='title-bar-btns'>
<div
className='button full-screen-button'
onClick={this.handleExitFullScreen}
>
<i className='icon icon-arrow-collapse'/>
</div>
</span>
}
</div>
</Row>
);

View file

@ -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;
}

View file

@ -81,7 +81,6 @@ class Root extends React.PureComponent<Record<string, never>, State> {
openMenu={this.openMenu}
darkMode={config.darkMode}
appName={config.appName}
useNativeWindow={config.useNativeWindow}
/>
</IntlProvider>
);

View file

@ -119,7 +119,6 @@ export type RegistryConfig = {
export type CombinedConfig = Omit<Config, 'teams'> & Omit<BuildConfig, 'defaultServers'> & {
appName: string;
useNativeWindow: boolean;
}
export type LocalConfiguration = Config & {

View file

@ -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;