fix(electron): optimize electron open/close on mac (#6224)
1. never close main window on mac to allow it to be quickly open 1. make the browser show a bit faster 2. brought up app window when clicking some menu items
This commit is contained in:
@@ -2,6 +2,7 @@ import { app, Menu } from 'electron';
|
|||||||
|
|
||||||
import { isMacOS } from '../../shared/utils';
|
import { isMacOS } from '../../shared/utils';
|
||||||
import { revealLogFile } from '../logger';
|
import { revealLogFile } from '../logger';
|
||||||
|
import { initAndShowMainWindow } from '../main-window';
|
||||||
import { checkForUpdates } from '../updater';
|
import { checkForUpdates } from '../updater';
|
||||||
import { applicationMenuSubjects } from './subject';
|
import { applicationMenuSubjects } from './subject';
|
||||||
|
|
||||||
@@ -42,7 +43,9 @@ export function createApplicationMenu() {
|
|||||||
id: MENUITEM_NEW_PAGE,
|
id: MENUITEM_NEW_PAGE,
|
||||||
label: 'New Doc',
|
label: 'New Doc',
|
||||||
accelerator: isMac ? 'Cmd+N' : 'Ctrl+N',
|
accelerator: isMac ? 'Cmd+N' : 'Ctrl+N',
|
||||||
click: () => {
|
click: async () => {
|
||||||
|
await initAndShowMainWindow();
|
||||||
|
// fixme: if the window is just created, the new page action will not be triggered
|
||||||
applicationMenuSubjects.newPageAction.next();
|
applicationMenuSubjects.newPageAction.next();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -100,7 +103,12 @@ export function createApplicationMenu() {
|
|||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ role: 'front' },
|
{ role: 'front' },
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ role: 'window' },
|
{
|
||||||
|
role: 'window',
|
||||||
|
click: async () => {
|
||||||
|
await initAndShowMainWindow();
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
: [{ role: 'close' }]),
|
: [{ role: 'close' }]),
|
||||||
],
|
],
|
||||||
@@ -125,6 +133,7 @@ export function createApplicationMenu() {
|
|||||||
{
|
{
|
||||||
label: 'Check for Updates',
|
label: 'Check for Updates',
|
||||||
click: async () => {
|
click: async () => {
|
||||||
|
await initAndShowMainWindow();
|
||||||
await checkForUpdates();
|
await checkForUpdates();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,9 +52,7 @@ if (!isSingleInstance) {
|
|||||||
* Shout down background process if all windows was closed
|
* Shout down background process if all windows was closed
|
||||||
*/
|
*/
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,6 +27,14 @@ const getWindowAdditionalArguments = async () => {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function closeAllWindows() {
|
||||||
|
BrowserWindow.getAllWindows().forEach(w => {
|
||||||
|
if (!w.isDestroyed()) {
|
||||||
|
w.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function createWindow(additionalArguments: string[]) {
|
async function createWindow(additionalArguments: string[]) {
|
||||||
logger.info('create window');
|
logger.info('create window');
|
||||||
const mainWindowState = electronWindowState({
|
const mainWindowState = electronWindowState({
|
||||||
@@ -82,12 +90,7 @@ async function createWindow(additionalArguments: string[]) {
|
|||||||
* @see https://github.com/electron/electron/issues/25012
|
* @see https://github.com/electron/electron/issues/25012
|
||||||
*/
|
*/
|
||||||
browserWindow.on('ready-to-show', () => {
|
browserWindow.on('ready-to-show', () => {
|
||||||
if (IS_DEV) {
|
helperConnectionUnsub?.();
|
||||||
// do not gain focus in dev mode
|
|
||||||
browserWindow.showInactive();
|
|
||||||
} else {
|
|
||||||
browserWindow.show();
|
|
||||||
}
|
|
||||||
helperConnectionUnsub = helperProcessManager.connectRenderer(
|
helperConnectionUnsub = helperProcessManager.connectRenderer(
|
||||||
browserWindow.webContents
|
browserWindow.webContents
|
||||||
);
|
);
|
||||||
@@ -96,15 +99,26 @@ async function createWindow(additionalArguments: string[]) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
browserWindow.on('close', e => {
|
browserWindow.on('close', e => {
|
||||||
e.preventDefault();
|
|
||||||
// close and destroy all windows
|
|
||||||
BrowserWindow.getAllWindows().forEach(w => {
|
|
||||||
if (!w.isDestroyed()) {
|
|
||||||
w.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
helperConnectionUnsub?.();
|
|
||||||
// TODO: gracefully close the app, for example, ask user to save unsaved changes
|
// TODO: gracefully close the app, for example, ask user to save unsaved changes
|
||||||
|
e.preventDefault();
|
||||||
|
if (!isMacOS()) {
|
||||||
|
closeAllWindows();
|
||||||
|
} else {
|
||||||
|
// hide window on macOS
|
||||||
|
// application quit will be handled by closing the hidden window
|
||||||
|
//
|
||||||
|
// explanation:
|
||||||
|
// - closing the top window (by clicking close button or CMD-w)
|
||||||
|
// - will be captured in "close" event here
|
||||||
|
// - hiding the app to make the app open faster when user click the app icon
|
||||||
|
// - quit the app by "cmd+q" or right click on the dock icon and select "quit"
|
||||||
|
// - all browser windows will capture the "close" event
|
||||||
|
// - the hidden window will close all windows
|
||||||
|
// - "window-all-closed" event will be emitted and eventually quit the app
|
||||||
|
browserWindow.hide();
|
||||||
|
}
|
||||||
|
helperConnectionUnsub?.();
|
||||||
|
helperConnectionUnsub = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
browserWindow.on('leave-full-screen', () => {
|
browserWindow.on('leave-full-screen', () => {
|
||||||
@@ -148,15 +162,37 @@ async function createWindow(additionalArguments: string[]) {
|
|||||||
// singleton
|
// singleton
|
||||||
let browserWindow$: Promise<BrowserWindow> | undefined;
|
let browserWindow$: Promise<BrowserWindow> | undefined;
|
||||||
|
|
||||||
|
// a hidden window that prevents the app from quitting on MacOS
|
||||||
|
let hiddenMacWindow: BrowserWindow | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init main BrowserWindow. Will create a new window if it's not created yet.
|
* Init main BrowserWindow. Will create a new window if it's not created yet.
|
||||||
*/
|
*/
|
||||||
export async function initMainWindow() {
|
export async function initAndShowMainWindow() {
|
||||||
if (!browserWindow$ || (await browserWindow$.then(w => w.isDestroyed()))) {
|
if (!browserWindow$ || (await browserWindow$.then(w => w.isDestroyed()))) {
|
||||||
const additionalArguments = await getWindowAdditionalArguments();
|
const additionalArguments = await getWindowAdditionalArguments();
|
||||||
browserWindow$ = createWindow(additionalArguments);
|
browserWindow$ = createWindow(additionalArguments);
|
||||||
}
|
}
|
||||||
const mainWindow = await browserWindow$;
|
const mainWindow = await browserWindow$;
|
||||||
|
|
||||||
|
if (IS_DEV) {
|
||||||
|
// do not gain focus in dev mode
|
||||||
|
mainWindow.showInactive();
|
||||||
|
} else {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hiddenMacWindow && isMacOS()) {
|
||||||
|
hiddenMacWindow = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
});
|
||||||
|
hiddenMacWindow.on('close', () => {
|
||||||
|
closeAllWindows();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { getLinkPreview } from 'link-preview-js';
|
|||||||
import { isMacOS } from '../../shared/utils';
|
import { isMacOS } from '../../shared/utils';
|
||||||
import { persistentConfig } from '../config-storage/persist';
|
import { persistentConfig } from '../config-storage/persist';
|
||||||
import { logger } from '../logger';
|
import { logger } from '../logger';
|
||||||
import { getMainWindow, initMainWindow } from '../main-window';
|
import { getMainWindow, initAndShowMainWindow } from '../main-window';
|
||||||
import { getOnboardingWindow } from '../onboarding';
|
import { getOnboardingWindow } from '../onboarding';
|
||||||
import type { NamespaceHandlers } from '../type';
|
import type { NamespaceHandlers } from '../type';
|
||||||
import { launchStage } from '../windows-manager/stage';
|
import { launchStage } from '../windows-manager/stage';
|
||||||
@@ -58,7 +58,7 @@ export const uiHandlers = {
|
|||||||
try {
|
try {
|
||||||
const onboarding = await getOnboardingWindow();
|
const onboarding = await getOnboardingWindow();
|
||||||
onboarding?.hide();
|
onboarding?.hide();
|
||||||
await initMainWindow();
|
await initAndShowMainWindow();
|
||||||
// need to destroy onboarding window after main window is ready
|
// need to destroy onboarding window after main window is ready
|
||||||
// otherwise the main window will be closed as well
|
// otherwise the main window will be closed as well
|
||||||
onboarding?.destroy();
|
onboarding?.destroy();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { logger } from '../logger';
|
import { logger } from '../logger';
|
||||||
import { initMainWindow } from '../main-window';
|
import { initAndShowMainWindow } from '../main-window';
|
||||||
import {
|
import {
|
||||||
getOnboardingWindow,
|
getOnboardingWindow,
|
||||||
getOrCreateOnboardingWindow,
|
getOrCreateOnboardingWindow,
|
||||||
@@ -12,7 +12,7 @@ import { launchStage } from './stage';
|
|||||||
export async function launch() {
|
export async function launch() {
|
||||||
const stage = launchStage.value;
|
const stage = launchStage.value;
|
||||||
if (stage === 'main') {
|
if (stage === 'main') {
|
||||||
initMainWindow().catch(e => {
|
initAndShowMainWindow().catch(e => {
|
||||||
logger.error('Failed to restore or create window:', e);
|
logger.error('Failed to restore or create window:', e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user