mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-05 18:20:39 -08:00
164 lines
6 KiB
TypeScript
164 lines
6 KiB
TypeScript
import { MENUBAR_ICON_PATH } from '@/constants/paths';
|
|
import { isMac } from '@/helpers/system';
|
|
import { container } from '@services/container';
|
|
import { i18n } from '@services/libs/i18n';
|
|
import { logger } from '@services/libs/log';
|
|
import { IMenuService } from '@services/menu/interface';
|
|
import serviceIdentifier from '@services/serviceIdentifier';
|
|
import { IViewService } from '@services/view/interface';
|
|
import { BrowserWindowConstructorOptions, Menu, nativeImage, Tray } from 'electron';
|
|
import windowStateKeeper from 'electron-window-state';
|
|
import { debounce, merge as mergeDeep } from 'lodash';
|
|
import { Menubar, menubar } from 'menubar';
|
|
import { IWindowService } from './interface';
|
|
import { WindowNames } from './WindowProperties';
|
|
|
|
export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstructorOptions, windowWithBrowserViewState: windowStateKeeper.State | undefined): Promise<Menubar> {
|
|
const menuService = container.get<IMenuService>(serviceIdentifier.MenuService);
|
|
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
|
const viewService = container.get<IViewService>(serviceIdentifier.View);
|
|
|
|
// setImage after Tray instance is created to avoid
|
|
// "Segmentation fault (core dumped)" bug on Linux
|
|
// https://github.com/electron/electron/issues/22137#issuecomment-586105622
|
|
// https://github.com/atomery/translatium/issues/164
|
|
const tray = new Tray(nativeImage.createEmpty());
|
|
// icon template is not supported on Windows & Linux
|
|
tray.setImage(MENUBAR_ICON_PATH);
|
|
|
|
const menuBar = menubar({
|
|
index: MAIN_WINDOW_WEBPACK_ENTRY,
|
|
tray,
|
|
activateWithApp: false,
|
|
showDockIcon: true,
|
|
preloadWindow: true,
|
|
tooltip: i18n.t('Menu.TidGiMenuBar'),
|
|
browserWindow: mergeDeep(windowConfig, {
|
|
show: false,
|
|
minHeight: 100,
|
|
minWidth: 250,
|
|
}),
|
|
});
|
|
|
|
menuBar.on('after-create-window', () => {
|
|
if (menuBar.window !== undefined) {
|
|
menuBar.window.on('focus', async () => {
|
|
logger.debug('restore window position');
|
|
if (windowWithBrowserViewState === undefined) {
|
|
logger.debug('windowWithBrowserViewState is undefined for menuBar');
|
|
} else {
|
|
if (menuBar.window === undefined) {
|
|
logger.debug('menuBar.window is undefined');
|
|
} else {
|
|
const haveXYValue = [windowWithBrowserViewState.x, windowWithBrowserViewState.y].every((value) => Number.isFinite(value));
|
|
const haveWHValue = [windowWithBrowserViewState.width, windowWithBrowserViewState.height].every((value) => Number.isFinite(value));
|
|
if (haveXYValue) {
|
|
menuBar.window.setPosition(windowWithBrowserViewState.x, windowWithBrowserViewState.y, false);
|
|
}
|
|
if (haveWHValue) {
|
|
menuBar.window.setSize(windowWithBrowserViewState.width, windowWithBrowserViewState.height, false);
|
|
}
|
|
}
|
|
}
|
|
const view = await viewService.getActiveBrowserView();
|
|
view?.webContents?.focus?.();
|
|
});
|
|
menuBar.window.removeAllListeners('close');
|
|
menuBar.window.on('close', (event) => {
|
|
event.preventDefault();
|
|
menuBar.hideWindow();
|
|
});
|
|
}
|
|
});
|
|
menuBar.on('hide', async () => {
|
|
// on mac, calling `menuBar.app.hide()` with main window open will bring background main window up, which we don't want. We want to bring previous other app up. So close main window first.
|
|
if (isMac) {
|
|
const mainWindow = windowService.get(WindowNames.main);
|
|
if (mainWindow?.isVisible() === true) {
|
|
await windowService.hide(WindowNames.main);
|
|
}
|
|
}
|
|
});
|
|
// https://github.com/maxogden/menubar/issues/120
|
|
menuBar.on('after-hide', () => {
|
|
if (isMac) {
|
|
menuBar.app.hide();
|
|
}
|
|
});
|
|
|
|
// manually save window state https://github.com/mawie81/electron-window-state/issues/64
|
|
const debouncedSaveWindowState = debounce(
|
|
() => {
|
|
if (menuBar.window !== undefined) {
|
|
windowWithBrowserViewState?.saveState(menuBar.window);
|
|
}
|
|
},
|
|
500,
|
|
);
|
|
// menubar is hide, not close, so not managed by windowStateKeeper, need to save manually
|
|
menuBar.window?.on('resize', debouncedSaveWindowState);
|
|
menuBar.window?.on('move', debouncedSaveWindowState);
|
|
|
|
return await new Promise<Menubar>((resolve) => {
|
|
menuBar.on('ready', async () => {
|
|
// right on tray icon
|
|
menuBar.tray.on('right-click', () => {
|
|
const contextMenu = Menu.buildFromTemplate([
|
|
{
|
|
label: i18n.t('ContextMenu.OpenTidGi'),
|
|
click: async () => {
|
|
await windowService.open(WindowNames.main);
|
|
},
|
|
},
|
|
{
|
|
label: i18n.t('ContextMenu.OpenTidGiMenuBar'),
|
|
click: async () => {
|
|
await menuBar.showWindow();
|
|
},
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
label: i18n.t('ContextMenu.About'),
|
|
click: async () => {
|
|
await windowService.open(WindowNames.about);
|
|
},
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
label: i18n.t('ContextMenu.Preferences'),
|
|
click: async () => {
|
|
await windowService.open(WindowNames.preferences);
|
|
},
|
|
},
|
|
{
|
|
label: i18n.t('ContextMenu.Notifications'),
|
|
click: async () => {
|
|
await windowService.open(WindowNames.notifications);
|
|
},
|
|
},
|
|
{ type: 'separator' },
|
|
{
|
|
label: i18n.t('ContextMenu.Quit'),
|
|
click: () => {
|
|
menuBar.app.quit();
|
|
},
|
|
},
|
|
]);
|
|
|
|
menuBar.tray.popUpContextMenu(contextMenu);
|
|
});
|
|
|
|
// right click on window content
|
|
if (menuBar.window?.webContents !== undefined) {
|
|
const unregisterContextMenu = await menuService.initContextMenuForWindowWebContents(menuBar.window.webContents);
|
|
menuBar.on('after-close', () => {
|
|
unregisterContextMenu();
|
|
});
|
|
}
|
|
|
|
resolve(menuBar);
|
|
});
|
|
});
|
|
}
|