mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-01-30 12:23:12 -08:00
refactor: move basic menu template to each services
This commit is contained in:
parent
b7fca74956
commit
f5f4091f8d
11 changed files with 349 additions and 396 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -4,6 +4,7 @@
|
|||
"fullscreenable",
|
||||
"maximizable",
|
||||
"minimizable",
|
||||
"submenu",
|
||||
"subwiki",
|
||||
"subwiki's",
|
||||
"tiddlywiki's"
|
||||
|
|
|
|||
14
src/main.ts
14
src/main.ts
|
|
@ -6,7 +6,7 @@ import isDev from 'electron-is-dev';
|
|||
import settings from 'electron-settings';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
|
||||
import { clearMainBindings } from '@services/libs/i18n/i18next-electron-fs-backend';
|
||||
import { clearMainBindings, buildLanguageMenu } from '@services/libs/i18n/i18next-electron-fs-backend';
|
||||
import { ThemeChannel } from '@/constants/channels';
|
||||
import { container } from '@services/container';
|
||||
import { logger } from '@services/libs/log';
|
||||
|
|
@ -53,6 +53,7 @@ const viewService = container.resolve(View);
|
|||
const wikiService = container.resolve(Wiki);
|
||||
const windowService = container.resolve(Window);
|
||||
const workspaceService = container.resolve(Workspace);
|
||||
const workspaceViewService = container.resolve(WorkspaceView);
|
||||
|
||||
app.on('second-instance', () => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
|
|
@ -139,8 +140,8 @@ if (!gotTheLock) {
|
|||
proxyBypassRules,
|
||||
});
|
||||
}
|
||||
// apply theme
|
||||
nativeTheme.themeSource = themeSource;
|
||||
menuService.buildMenu();
|
||||
nativeTheme.addListener('updated', () => {
|
||||
windowService.sendToAllWindows(ThemeChannel.nativeThemeUpdated);
|
||||
viewService.reloadViewsDarkReader();
|
||||
|
|
@ -200,10 +201,10 @@ if (!gotTheLock) {
|
|||
// getContentSize is not updated immediately
|
||||
// try once after 0.2s (for fast computer), another one after 1s (to be sure)
|
||||
setTimeout(() => {
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
workspaceViewService.realignActiveWorkspace();
|
||||
}, 200);
|
||||
setTimeout(() => {
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
workspaceViewService.realignActiveWorkspace();
|
||||
}, 1000);
|
||||
};
|
||||
mainWindow.on('maximize', handleMaximize);
|
||||
|
|
@ -215,6 +216,11 @@ if (!gotTheLock) {
|
|||
.then(() => {
|
||||
// trigger whenTrulyReady
|
||||
ipcMain.emit(customCommonInitFinishedEvent);
|
||||
})
|
||||
.then(() => {
|
||||
// build menu at last, this is not noticeable to user, so do it last
|
||||
buildLanguageMenu();
|
||||
menuService.buildMenu();
|
||||
});
|
||||
};
|
||||
app.on('ready', () => {
|
||||
|
|
|
|||
|
|
@ -79,151 +79,6 @@ function createMenu() {
|
|||
},
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{
|
||||
label: global.sidebar ? 'Hide Sidebar' : 'Show Sidebar',
|
||||
accelerator: 'CmdOrCtrl+Alt+S',
|
||||
click: () => {
|
||||
ipcMain.emit('request-set-preference', null, 'sidebar', !global.sidebar);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: global.navigationBar ? 'Hide Navigation Bar' : 'Show Navigation Bar',
|
||||
accelerator: 'CmdOrCtrl+Alt+N',
|
||||
click: () => {
|
||||
ipcMain.emit('request-set-preference', null, 'navigationBar', !global.navigationBar);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: global.titleBar ? 'Hide Title Bar' : 'Show Title Bar',
|
||||
accelerator: 'CmdOrCtrl+Alt+T',
|
||||
enabled: process.platform === 'darwin',
|
||||
visible: process.platform === 'darwin',
|
||||
click: () => {
|
||||
ipcMain.emit('request-set-preference', null, 'titleBar', !global.titleBar);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
// same behavior as BrowserWindow with autoHideMenuBar: true
|
||||
// but with addition to readjust BrowserView so it won't cover the menu bar
|
||||
{
|
||||
label: 'Toggle Menu Bar',
|
||||
visible: false,
|
||||
accelerator: 'Alt+M',
|
||||
enabled: process.platform === 'win32',
|
||||
click: (menuItem: any, browserWindow: any) => {
|
||||
// if back is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
browserWindow.setMenuBarVisibility(!browserWindow.isMenuBarVisible());
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
win.setMenuBarVisibility(!win.isMenuBarVisible());
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' },
|
||||
{
|
||||
label: 'Actual Size',
|
||||
accelerator: 'CmdOrCtrl+0',
|
||||
click: (menuItem: any, browserWindow: any) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor = 1;
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const contents = win.getBrowserView().webContents;
|
||||
contents.zoomFactor = 1;
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Zoom In',
|
||||
accelerator: 'CmdOrCtrl+=',
|
||||
click: (menuItem: any, browserWindow: any) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor += 0.1;
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const contents = win.getBrowserView().webContents;
|
||||
contents.zoomFactor += 0.1;
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Zoom Out',
|
||||
accelerator: 'CmdOrCtrl+-',
|
||||
click: (menuItem: any, browserWindow: any) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor -= 0.1;
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const contents = win.getBrowserView().webContents;
|
||||
contents.zoomFactor -= 0.1;
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Reload This Page',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click: (menuItem: any, browserWindow: any) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
browserWindow.webContents.reload();
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
win.getBrowserView().webContents.reload();
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Developer Tools',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Open Developer Tools of Active Workspace',
|
||||
accelerator: 'CmdOrCtrl+Option+I',
|
||||
click: () => getActiveBrowserView().webContents.openDevTools(),
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// language menu
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { IpcRenderer, IpcMain, BrowserWindow, IpcMainInvokeEvent, IpcRendererEve
|
|||
import { Window } from '@services/windows';
|
||||
import { Preference } from '@services/preferences';
|
||||
import { View } from '@services/view';
|
||||
import { MenuService } from '@services/menu';
|
||||
import { container } from '@services/container';
|
||||
import { LOCALIZATION_FOLDER } from '@services/constants/paths';
|
||||
import { I18NChannels } from '@/constants/channels';
|
||||
|
|
@ -105,10 +106,14 @@ const whitelistMap = JSON.parse(fs.readFileSync(path.join(LOCALIZATION_FOLDER, '
|
|||
|
||||
const whiteListedLanguages = Object.keys(whitelistMap);
|
||||
|
||||
export function getLanguageMenu(): MenuItemConstructorOptions[] {
|
||||
/**
|
||||
* Register languages into language menu, call this function after container init
|
||||
*/
|
||||
export function buildLanguageMenu(): void {
|
||||
const preferenceService = container.resolve(Preference);
|
||||
const windowService = container.resolve(Window);
|
||||
const viewService = container.resolve(View);
|
||||
const menuService = container.resolve(MenuService);
|
||||
const subMenu: MenuItemConstructorOptions[] = [];
|
||||
for (const language of whiteListedLanguages) {
|
||||
subMenu.push({
|
||||
|
|
@ -127,5 +132,5 @@ export function getLanguageMenu(): MenuItemConstructorOptions[] {
|
|||
});
|
||||
}
|
||||
|
||||
return subMenu;
|
||||
menuService.insertMenu('Language', subMenu);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,41 @@
|
|||
/* eslint-disable global-require */
|
||||
import Transport from 'winston-transport';
|
||||
|
||||
import { container } from '@services/container';
|
||||
import { View } from '@services/view';
|
||||
import { Window } from '@services/windows';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
|
||||
const handlers = {
|
||||
createWikiProgress: (message: any) => {
|
||||
require('../../windows/add-workspace') // require here to prevent possible circular dependence
|
||||
.get()
|
||||
.webContents.send('create-wiki-progress', message);
|
||||
createWikiProgress: (message: string) => {
|
||||
const windowService = container.resolve(Window);
|
||||
const createWorkspaceWindow = windowService.get(WindowNames.addWorkspace);
|
||||
createWorkspaceWindow?.webContents?.send('create-wiki-progress', message);
|
||||
},
|
||||
wikiSyncProgress: (message: any) => {
|
||||
const { getActiveBrowserView } = require('../views');
|
||||
const browserView = getActiveBrowserView();
|
||||
if (browserView) {
|
||||
browserView.webContents.send('wiki-sync-progress', message);
|
||||
}
|
||||
wikiSyncProgress: (message: string) => {
|
||||
const viewService = container.resolve(View);
|
||||
const browserView = viewService.getActiveBrowserView();
|
||||
browserView?.webContents?.send('wiki-sync-progress', message);
|
||||
},
|
||||
};
|
||||
|
||||
export type IHandlers = typeof handlers;
|
||||
|
||||
export interface IInfo {
|
||||
/** which method or handler function we are logging for */
|
||||
handler: keyof IHandlers;
|
||||
/** the detailed massage for debugging */
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default class RendererTransport extends Transport {
|
||||
log(info: any, callback: any) {
|
||||
log(info: IInfo, callback: () => unknown): void {
|
||||
setImmediate(() => {
|
||||
this.emit('logged', info);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (info.handler && info.handler in handlers) {
|
||||
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||
handlers[info.handler](info.message);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,19 +5,43 @@ import serviceIdentifiers from '@services/serviceIdentifier';
|
|||
import { Preference } from '@services/preferences';
|
||||
import { View } from '@services/view';
|
||||
|
||||
interface DeferredMenuItemConstructorOptions extends Omit<MenuItemConstructorOptions, 'label' | 'enabled' | 'submenu'> {
|
||||
label?: (() => string) | string;
|
||||
enabled?: (() => boolean) | boolean;
|
||||
submenu?:
|
||||
| (() => Array<MenuItemConstructorOptions | DeferredMenuItemConstructorOptions>)
|
||||
| Array<MenuItemConstructorOptions | DeferredMenuItemConstructorOptions>;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class MenuService {
|
||||
private readonly menuTemplate: MenuItemConstructorOptions[];
|
||||
private readonly menuTemplate: DeferredMenuItemConstructorOptions[];
|
||||
|
||||
/**
|
||||
* Rebuild or create menubar from the latest menu template, will be call after some method change the menuTemplate
|
||||
* You don't need to call this after calling method like insertMenu, it will be call automatically.
|
||||
*/
|
||||
public buildMenu(): void {
|
||||
const menu = Menu.buildFromTemplate(this.menuTemplate);
|
||||
const menu = Menu.buildFromTemplate(this.getCurrentMenuItemConstructorOptions(this.menuTemplate));
|
||||
Menu.setApplicationMenu(menu);
|
||||
}
|
||||
|
||||
private getCurrentMenuItemConstructorOptions(
|
||||
submenu: Array<DeferredMenuItemConstructorOptions | MenuItemConstructorOptions> = this.menuTemplate,
|
||||
): MenuItemConstructorOptions[] {
|
||||
return submenu.map((item) => ({
|
||||
...item,
|
||||
label: typeof item.label === 'function' ? item.label() : item.label,
|
||||
enabled: typeof item.enabled === 'function' ? item.enabled() : item.enabled,
|
||||
submenu:
|
||||
typeof item.submenu === 'function'
|
||||
? this.getCurrentMenuItemConstructorOptions(item.submenu())
|
||||
: item.submenu instanceof Menu
|
||||
? item.submenu
|
||||
: this.getCurrentMenuItemConstructorOptions(item.submenu),
|
||||
}));
|
||||
}
|
||||
|
||||
constructor(
|
||||
@inject(serviceIdentifiers.Preference) private readonly preferenceService: Preference,
|
||||
@inject(serviceIdentifiers.View) private readonly viewService: View,
|
||||
|
|
@ -28,6 +52,7 @@ export class MenuService {
|
|||
this.menuTemplate = [
|
||||
{
|
||||
label: 'Edit',
|
||||
id: 'Edit',
|
||||
submenu: [
|
||||
{ role: 'undo' },
|
||||
{ role: 'redo' },
|
||||
|
|
@ -35,216 +60,37 @@ export class MenuService {
|
|||
{ role: 'cut' },
|
||||
{ role: 'copy' },
|
||||
{ role: 'paste' },
|
||||
{ role: 'pasteandmatchstyle' },
|
||||
{ role: 'pasteAndMatchStyle' },
|
||||
{ role: 'delete' },
|
||||
{ role: 'selectall' },
|
||||
{ role: 'selectAll' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Find',
|
||||
accelerator: 'CmdOrCtrl+F',
|
||||
click: () => {
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
win.webContents.focus();
|
||||
(win as any).send('open-find-in-page');
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const contentSize = win.getContentSize();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const view = win.getBrowserView();
|
||||
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||
view.setBounds(getViewBounds(contentSize, true));
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Find Next',
|
||||
accelerator: 'CmdOrCtrl+G',
|
||||
click: () => {
|
||||
const win = mainWindow.get();
|
||||
(win as any).send('request-back-find-in-page', true);
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Find Previous',
|
||||
accelerator: 'Shift+CmdOrCtrl+G',
|
||||
click: () => {
|
||||
const win = mainWindow.get();
|
||||
(win as any).send('request-back-find-in-page', false);
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{
|
||||
label: global.sidebar ? 'Hide Sidebar' : 'Show Sidebar',
|
||||
accelerator: 'CmdOrCtrl+Alt+S',
|
||||
click: () => {
|
||||
ipcMain.emit('request-set-preference', null, 'sidebar', !global.sidebar);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: global.navigationBar ? 'Hide Navigation Bar' : 'Show Navigation Bar',
|
||||
accelerator: 'CmdOrCtrl+Alt+N',
|
||||
click: () => {
|
||||
ipcMain.emit('request-set-preference', null, 'navigationBar', !global.navigationBar);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: global.titleBar ? 'Hide Title Bar' : 'Show Title Bar',
|
||||
accelerator: 'CmdOrCtrl+Alt+T',
|
||||
enabled: process.platform === 'darwin',
|
||||
visible: process.platform === 'darwin',
|
||||
click: () => {
|
||||
ipcMain.emit('request-set-preference', null, 'titleBar', !global.titleBar);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
// same behavior as BrowserWindow with autoHideMenuBar: true
|
||||
// but with addition to readjust BrowserView so it won't cover the menu bar
|
||||
{
|
||||
label: 'Toggle Menu Bar',
|
||||
visible: false,
|
||||
accelerator: 'Alt+M',
|
||||
enabled: process.platform === 'win32',
|
||||
click: (menuItem, browserWindow) => {
|
||||
// if back is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
browserWindow.setMenuBarVisibility(!browserWindow.isMenuBarVisible());
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
win.setMenuBarVisibility(!win.isMenuBarVisible());
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' },
|
||||
{
|
||||
label: 'Actual Size',
|
||||
accelerator: 'CmdOrCtrl+0',
|
||||
click: (menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor = 1;
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const contents = win.getBrowserView().webContents;
|
||||
contents.zoomFactor = 1;
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Zoom In',
|
||||
accelerator: 'CmdOrCtrl+=',
|
||||
click: (menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor += 0.1;
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const contents = win.getBrowserView().webContents;
|
||||
contents.zoomFactor += 0.1;
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Zoom Out',
|
||||
accelerator: 'CmdOrCtrl+-',
|
||||
click: (menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor -= 0.1;
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
const contents = win.getBrowserView().webContents;
|
||||
contents.zoomFactor -= 0.1;
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Reload This Page',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click: (menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow && browserWindow.isPopup) {
|
||||
browserWindow.webContents.reload();
|
||||
return;
|
||||
}
|
||||
const win = mainWindow.get();
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
if (win !== null && win.getBrowserView() !== null) {
|
||||
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||
win.getBrowserView().webContents.reload();
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Developer Tools',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Open Developer Tools of Active Workspace',
|
||||
accelerator: 'CmdOrCtrl+Option+I',
|
||||
click: () => getActiveBrowserView().webContents.openDevTools(),
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
id: 'View',
|
||||
},
|
||||
// language menu
|
||||
{
|
||||
label: 'Language',
|
||||
submenu: getLanguageMenu(),
|
||||
id: 'Language',
|
||||
},
|
||||
{
|
||||
label: 'History',
|
||||
id: 'History',
|
||||
},
|
||||
{
|
||||
label: 'Workspaces',
|
||||
id: 'Workspaces',
|
||||
submenu: [],
|
||||
},
|
||||
{
|
||||
role: 'window',
|
||||
id: 'window',
|
||||
submenu: [{ role: 'minimize' }, { role: 'close' }, { type: 'separator' }, { role: 'front' }, { type: 'separator' }],
|
||||
},
|
||||
{
|
||||
role: 'help',
|
||||
id: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'TiddlyGit Support',
|
||||
|
|
@ -269,16 +115,16 @@ export class MenuService {
|
|||
|
||||
/**
|
||||
* Insert provided sub menu items into menubar, so user and services can register custom menu items
|
||||
* @param menuName Top level menu name to insert menu items
|
||||
* @param menuID Top level menu name to insert menu items
|
||||
* @param menuItems An array of menu item to insert
|
||||
* @param afterSubMenu The name or role of a submenu you want your submenu insert after. `null` means inserted as first submenu item; `undefined` means inserted as last submenu item;
|
||||
* @param afterSubMenu The `id` or `role` of a submenu you want your submenu insert after. `null` means inserted as first submenu item; `undefined` means inserted as last submenu item;
|
||||
* @param withSeparator Need to insert a separator first, before insert menu items
|
||||
*/
|
||||
insertMenu(menuName: string, menuItems: MenuItemConstructorOptions[], afterSubMenu?: string | null, withSeparator = false): void {
|
||||
insertMenu(menuID: string, menuItems: DeferredMenuItemConstructorOptions[], afterSubMenu?: string | null, withSeparator = false): void {
|
||||
let foundMenuName = false;
|
||||
// try insert menu into an existed menu's submenu
|
||||
for (const menu of this.menuTemplate) {
|
||||
if (menu.label === menuName || menu.role === menuName) {
|
||||
if (menu.id === menuID) {
|
||||
foundMenuName = true;
|
||||
if (Array.isArray(menu.submenu)) {
|
||||
if (afterSubMenu === undefined) {
|
||||
|
|
@ -295,10 +141,12 @@ export class MenuService {
|
|||
menu.submenu = [...menuItems, ...menu.submenu];
|
||||
} else if (typeof afterSubMenu === 'string') {
|
||||
// insert after afterSubMenu
|
||||
const afterSubMenuIndex = menu.submenu.findIndex((item) => item.label === afterSubMenu || item.role === afterSubMenu);
|
||||
const afterSubMenuIndex = menu.submenu.findIndex((item) => item.id === afterSubMenu || item.role === afterSubMenu);
|
||||
if (afterSubMenuIndex === -1) {
|
||||
throw new Error(
|
||||
`You try to insert menu with afterSubMenu ${afterSubMenu}, but we can not found it in menu ${menu.label ?? menu.role ?? JSON.stringify(menu)}`,
|
||||
`You try to insert menu with afterSubMenu ${afterSubMenu}, but we can not found it in menu ${
|
||||
menu.id ?? menu.role ?? JSON.stringify(menu)
|
||||
}, please specific a menuitem with correct id attribute`,
|
||||
);
|
||||
}
|
||||
menu.submenu = [...take(menu.submenu, afterSubMenuIndex + 1), ...menuItems, ...drop(menu.submenu, afterSubMenuIndex - 1)];
|
||||
|
|
@ -312,7 +160,7 @@ export class MenuService {
|
|||
// if user wants to create a new menu in menubar
|
||||
if (!foundMenuName) {
|
||||
this.menuTemplate.push({
|
||||
label: menuName,
|
||||
label: menuID,
|
||||
submenu: menuItems,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,19 @@ import { injectable, inject } from 'inversify';
|
|||
import serviceIdentifiers from '@services/serviceIdentifier';
|
||||
import { Preference } from '@services/preferences';
|
||||
import { Workspace } from '@services/workspaces';
|
||||
import { WorkspaceView } from '@services/workspacesView';
|
||||
import { Wiki } from '@services/wiki';
|
||||
import { Authentication } from '@services/auth';
|
||||
import { Window } from '@services/windows';
|
||||
import { MenuService } from '@services/menu';
|
||||
import { WindowNames, IBrowserViewMetaData } from '@services/windows/WindowProperties';
|
||||
import i18n from '../libs/i18n';
|
||||
import getViewBounds from '@services/libs/get-view-bounds';
|
||||
import { extractDomain } from '@services/libs/url';
|
||||
import { IWorkspace } from '@services/types';
|
||||
import setupViewEventHandlers from './setupViewEventHandlers';
|
||||
import getFromRenderer from '@services/libs/getFromRenderer';
|
||||
import { MetaDataChannel } from '@/constants/channels';
|
||||
|
||||
@injectable()
|
||||
export class View {
|
||||
|
|
@ -22,11 +26,14 @@ export class View {
|
|||
@inject(serviceIdentifiers.Window) private readonly windowService: Window,
|
||||
@inject(serviceIdentifiers.Workspace) private readonly workspaceService: Workspace,
|
||||
@inject(serviceIdentifiers.Authentication) private readonly authService: Authentication,
|
||||
@inject(serviceIdentifiers.MenuService) private readonly menuService: MenuService,
|
||||
@inject(serviceIdentifiers.WorkspaceView) private readonly workspaceViewService: WorkspaceView,
|
||||
) {
|
||||
this.init();
|
||||
this.initIPCHandlers();
|
||||
this.registerMenu();
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
private initIPCHandlers(): void {
|
||||
// https://www.electronjs.org/docs/tutorial/online-offline-events
|
||||
ipcMain.handle('online-status-changed', (_event, online: boolean) => {
|
||||
if (online) {
|
||||
|
|
@ -38,6 +45,162 @@ export class View {
|
|||
});
|
||||
}
|
||||
|
||||
private registerMenu(): void {
|
||||
const hasWorkspaces = this.workspaceService.countWorkspaces() > 0;
|
||||
this.menuService.insertMenu('View', [
|
||||
{
|
||||
label: () => (this.preferenceService.get('sidebar') ? 'Hide Sidebar' : 'Show Sidebar'),
|
||||
accelerator: 'CmdOrCtrl+Alt+S',
|
||||
click: () => {
|
||||
void this.preferenceService.set('sidebar', !this.preferenceService.get('sidebar'));
|
||||
void this.workspaceViewService.realignActiveWorkspace();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: () => (this.preferenceService.get('navigationBar') ? 'Hide Navigation Bar' : 'Show Navigation Bar'),
|
||||
accelerator: 'CmdOrCtrl+Alt+N',
|
||||
click: () => {
|
||||
void this.preferenceService.set('navigationBar', !this.preferenceService.get('navigationBar'));
|
||||
void this.workspaceViewService.realignActiveWorkspace();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: () => (this.preferenceService.get('titleBar') ? 'Hide Title Bar' : 'Show Title Bar'),
|
||||
accelerator: 'CmdOrCtrl+Alt+T',
|
||||
enabled: process.platform === 'darwin',
|
||||
visible: process.platform === 'darwin',
|
||||
click: () => {
|
||||
void this.preferenceService.set('titleBar', !this.preferenceService.get('titleBar'));
|
||||
void this.workspaceViewService.realignActiveWorkspace();
|
||||
},
|
||||
},
|
||||
// same behavior as BrowserWindow with autoHideMenuBar: true
|
||||
// but with addition to readjust BrowserView so it won't cover the menu bar
|
||||
{
|
||||
label: 'Toggle Menu Bar',
|
||||
visible: false,
|
||||
accelerator: 'Alt+M',
|
||||
enabled: process.platform === 'win32',
|
||||
click: async (_menuItem, browserWindow) => {
|
||||
// if back is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow === undefined) return;
|
||||
const { isPopup } = await getFromRenderer<IBrowserViewMetaData>(MetaDataChannel.getViewMetaData, browserWindow);
|
||||
if (isPopup === true) {
|
||||
browserWindow.setMenuBarVisibility(!browserWindow.isMenuBarVisible());
|
||||
return;
|
||||
}
|
||||
const mainWindow = this.windowService.get(WindowNames.main);
|
||||
mainWindow?.setMenuBarVisibility(!mainWindow?.isMenuBarVisible());
|
||||
void this.workspaceViewService.realignActiveWorkspace();
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' },
|
||||
{
|
||||
label: 'Actual Size',
|
||||
accelerator: 'CmdOrCtrl+0',
|
||||
click: async (_menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow === undefined) return;
|
||||
const { isPopup } = await getFromRenderer<IBrowserViewMetaData>(MetaDataChannel.getViewMetaData, browserWindow);
|
||||
if (isPopup === true) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor = 1;
|
||||
return;
|
||||
}
|
||||
const mainWindow = this.windowService.get(WindowNames.main);
|
||||
const webContent = mainWindow?.getBrowserView()?.webContents;
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (webContent) {
|
||||
webContent.setZoomFactor(1);
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Zoom In',
|
||||
accelerator: 'CmdOrCtrl+=',
|
||||
click: async (_menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow === undefined) return;
|
||||
const { isPopup } = await getFromRenderer<IBrowserViewMetaData>(MetaDataChannel.getViewMetaData, browserWindow);
|
||||
if (isPopup === true) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor += 0.1;
|
||||
return;
|
||||
}
|
||||
const mainWindow = this.windowService.get(WindowNames.main);
|
||||
const webContent = mainWindow?.getBrowserView()?.webContents;
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (webContent) {
|
||||
webContent.setZoomFactor(webContent.getZoomFactor() + 0.1);
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{
|
||||
label: 'Zoom Out',
|
||||
accelerator: 'CmdOrCtrl+-',
|
||||
click: async (_menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow === undefined) return;
|
||||
const { isPopup } = await getFromRenderer<IBrowserViewMetaData>(MetaDataChannel.getViewMetaData, browserWindow);
|
||||
if (isPopup === true) {
|
||||
const contents = browserWindow.webContents;
|
||||
contents.zoomFactor -= 0.1;
|
||||
return;
|
||||
}
|
||||
const mainWindow = this.windowService.get(WindowNames.main);
|
||||
const webContent = mainWindow?.getBrowserView()?.webContents;
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (webContent) {
|
||||
webContent.setZoomFactor(webContent.getZoomFactor() - 0.1);
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Reload This Page',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
click: async (_menuItem, browserWindow) => {
|
||||
// if item is called in popup window
|
||||
// open menu bar in the popup window instead
|
||||
if (browserWindow === undefined) return;
|
||||
const { isPopup } = await getFromRenderer<IBrowserViewMetaData>(MetaDataChannel.getViewMetaData, browserWindow);
|
||||
if (isPopup === true) {
|
||||
browserWindow.webContents.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
const mainWindow = this.windowService.get(WindowNames.main);
|
||||
const webContent = mainWindow?.getBrowserView()?.webContents;
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (webContent) {
|
||||
webContent.reload();
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Developer Tools',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Open Developer Tools of Active Workspace',
|
||||
accelerator: 'CmdOrCtrl+Option+I',
|
||||
click: () => this.getActiveBrowserView()?.webContents?.openDevTools(),
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
private views: Record<string, BrowserView> = {};
|
||||
private shouldMuteAudio = false;
|
||||
private shouldPauseNotifications = false;
|
||||
|
|
@ -83,8 +246,7 @@ export class View {
|
|||
});
|
||||
} else if (proxyType === 'pacScript') {
|
||||
await sessionOfView.setProxy({
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ proxyPacScript: any; proxyBypa... Remove this comment to see the full error message
|
||||
proxyPacScript,
|
||||
pacScript: proxyPacScript,
|
||||
proxyBypassRules,
|
||||
});
|
||||
}
|
||||
|
|
@ -227,6 +389,7 @@ export class View {
|
|||
const view = this.getView(id);
|
||||
void session.fromPartition(`persist:${id}`).clearStorageData();
|
||||
// FIXME: Property 'destroy' does not exist on type 'BrowserView'.ts(2339) , might related to https://github.com/electron/electron/pull/25411 which previously cause crush when I quit the app
|
||||
// maybe use https://github.com/electron/electron/issues/10096
|
||||
// if (view !== undefined) {
|
||||
// view.destroy();
|
||||
// }
|
||||
|
|
@ -265,7 +428,6 @@ export class View {
|
|||
public hibernateView = (id: string): void => {
|
||||
if (this.getView(id) !== undefined) {
|
||||
// FIXME: remove view
|
||||
// @ts-expect-error Property 'destroy' does not exist on type 'BrowserView'.ts(2339)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
this.getView(id).destroy();
|
||||
this.removeView(id);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { buildResourcePath } from '@services/constants/paths';
|
|||
|
||||
import { Preference } from '@services/preferences';
|
||||
import { Workspace } from '@services/workspaces';
|
||||
import { WorkspaceView } from '@services/workspacesView';
|
||||
import { Window } from '@services/windows';
|
||||
import { WindowNames, IBrowserViewMetaData } from '@services/windows/WindowProperties';
|
||||
import { container } from '@services/container';
|
||||
|
|
@ -32,6 +33,7 @@ export default function setupViewEventHandlers(
|
|||
{ adjustUserAgentByUrl }: IViewModifier,
|
||||
): void {
|
||||
const workspaceService = container.resolve(Workspace);
|
||||
const workspaceViewService = container.resolve(WorkspaceView);
|
||||
const windowService = container.resolve(Window);
|
||||
const preferenceService = container.resolve(Preference);
|
||||
|
||||
|
|
@ -104,7 +106,7 @@ export default function setupViewEventHandlers(
|
|||
lastUrl: currentUrl,
|
||||
});
|
||||
// fix https://github.com/atomery/webcatalog/issues/870
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
// focus on initial load
|
||||
// https://github.com/atomery/webcatalog/issues/398
|
||||
|
|
|
|||
|
|
@ -260,13 +260,52 @@ export class Window {
|
|||
'close',
|
||||
);
|
||||
|
||||
const hasWorkspaces = this.workspaceService.countWorkspaces() > 0;
|
||||
this.menuService.insertMenu(
|
||||
'Edit',
|
||||
[
|
||||
{
|
||||
label: 'Find',
|
||||
accelerator: 'CmdOrCtrl+F',
|
||||
click: () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
if (mainWindow !== undefined) {
|
||||
mainWindow.webContents.focus();
|
||||
mainWindow.webContents.send('open-find-in-page');
|
||||
const contentSize = mainWindow.getContentSize();
|
||||
const view = mainWindow.getBrowserView();
|
||||
view?.setBounds(getViewBounds(contentSize as [number, number], true));
|
||||
}
|
||||
},
|
||||
enabled: () => this.workspaceService.countWorkspaces() > 0,
|
||||
},
|
||||
{
|
||||
label: 'Find Next',
|
||||
accelerator: 'CmdOrCtrl+G',
|
||||
click: () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
mainWindow?.webContents?.send('request-back-find-in-page', true);
|
||||
},
|
||||
enabled: () => this.workspaceService.countWorkspaces() > 0,
|
||||
},
|
||||
{
|
||||
label: 'Find Previous',
|
||||
accelerator: 'Shift+CmdOrCtrl+G',
|
||||
click: () => {
|
||||
const mainWindow = this.get(WindowNames.main);
|
||||
mainWindow?.webContents?.send('request-back-find-in-page', false);
|
||||
},
|
||||
enabled: () => this.workspaceService.countWorkspaces() > 0,
|
||||
},
|
||||
],
|
||||
'close',
|
||||
);
|
||||
|
||||
this.menuService.insertMenu('History', [
|
||||
{
|
||||
label: 'Home',
|
||||
accelerator: 'Shift+CmdOrCtrl+H',
|
||||
click: () => ipcMain.emit('request-go-home'),
|
||||
enabled: hasWorkspaces,
|
||||
enabled: () => this.workspaceService.countWorkspaces() > 0,
|
||||
},
|
||||
{
|
||||
label: 'Back',
|
||||
|
|
@ -284,7 +323,7 @@ export class Window {
|
|||
}
|
||||
ipcMain.emit('request-go-back');
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
enabled: () => this.workspaceService.countWorkspaces() > 0,
|
||||
},
|
||||
{
|
||||
label: 'Forward',
|
||||
|
|
@ -301,7 +340,7 @@ export class Window {
|
|||
}
|
||||
ipcMain.emit('request-go-forward');
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
enabled: () => this.workspaceService.countWorkspaces() > 0,
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
|
|
@ -324,7 +363,7 @@ export class Window {
|
|||
clipboard.writeText(url);
|
||||
}
|
||||
},
|
||||
enabled: hasWorkspaces,
|
||||
enabled: () => this.workspaceService.countWorkspaces() > 0,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,16 +229,16 @@ export const createAsync = async (): Promise<void> =>
|
|||
// after the UI is fully loaded
|
||||
// if not, BrowserView mouseover event won't work correctly
|
||||
// https://github.com/atomery/webcatalog/issues/812
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
|
||||
win.on('enter-full-screen', () => {
|
||||
win?.webContents.send('is-fullscreen-updated', true);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
win.on('leave-full-screen', () => {
|
||||
win?.webContents.send('is-fullscreen-updated', false);
|
||||
ipcMain.emit('request-realign-active-workspace');
|
||||
this.workspaceViewService.realignActiveWorkspace();
|
||||
});
|
||||
|
||||
// ensure redux is loaded first
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ import serviceIdentifiers from '@services/serviceIdentifier';
|
|||
import { View } from '@services/view';
|
||||
import { Workspace } from '@services/workspaces';
|
||||
import { Window } from '@services/windows';
|
||||
import sendToAllWindows from '@services/libs/send-to-all-windows';
|
||||
import { MenuService } from '@services/menu';
|
||||
import { IWorkspace } from '@services/types';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { Preference } from '@services/preferences';
|
||||
import createMenu from '@services/libs/create-menu';
|
||||
|
||||
/**
|
||||
* Deal with operations that needs to create a workspace and a browserView at once
|
||||
|
|
@ -21,41 +20,31 @@ export class WorkspaceView {
|
|||
@inject(serviceIdentifiers.Workspace) private readonly workspaceService: Workspace,
|
||||
@inject(serviceIdentifiers.Window) private readonly windowService: Window,
|
||||
@inject(serviceIdentifiers.Preference) private readonly preferenceService: Preference,
|
||||
@inject(serviceIdentifiers.MenuService) private readonly menuService: MenuService,
|
||||
) {
|
||||
this.init();
|
||||
this.initIPCHandlers();
|
||||
this.registerMenu();
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
private initIPCHandlers(): void {
|
||||
ipcMain.handle('request-create-workspace', async (_event, workspaceOptions: IWorkspace) => {
|
||||
await this.createWorkspaceView(workspaceOptions);
|
||||
createMenu();
|
||||
this.menuService.buildMenu();
|
||||
});
|
||||
ipcMain.handle('request-set-active-workspace', async (_event, id) => {
|
||||
if (this.workspaceService.get(id) !== undefined) {
|
||||
await this.setActiveWorkspaceView(id);
|
||||
createMenu();
|
||||
this.menuService.buildMenu();
|
||||
}
|
||||
});
|
||||
ipcMain.handle('request-get-active-workspace', (_event) => {
|
||||
return this.workspaceService.getActiveWorkspace();
|
||||
});
|
||||
ipcMain.handle('request-realign-active-workspace', () => {
|
||||
const { sidebar, titleBar, navigationBar } = this.preferenceService.getPreferences();
|
||||
// FIXME: global usage
|
||||
global.sidebar = sidebar;
|
||||
global.titleBar = titleBar;
|
||||
global.navigationBar = navigationBar;
|
||||
// this function only call browserView.setBounds
|
||||
// do not attempt to recall browserView.webContents.focus()
|
||||
// as it breaks page focus (cursor, scroll bar not visible)
|
||||
this.realignActiveWorkspaceView();
|
||||
createMenu();
|
||||
});
|
||||
ipcMain.handle('request-open-url-in-workspace', async (_event, url: string, id: string) => {
|
||||
if (typeof id === 'string' && id.length > 0) {
|
||||
// if id is defined, switch to that workspace
|
||||
await this.setActiveWorkspaceView(id);
|
||||
createMenu();
|
||||
this.menuService.buildMenu();
|
||||
// load url in the current workspace
|
||||
const activeWorkspace = this.workspaceService.getActiveWorkspace();
|
||||
if (activeWorkspace !== undefined) {
|
||||
|
|
@ -72,17 +61,38 @@ export class WorkspaceView {
|
|||
|
||||
ipcMain.handle('request-set-workspace', async (_event, id, options) => {
|
||||
await this.setWorkspaceView(id, options);
|
||||
createMenu();
|
||||
this.menuService.buildMenu();
|
||||
});
|
||||
ipcMain.handle('request-set-workspaces', async (_event, workspaces) => {
|
||||
await this.setWorkspaceViews(workspaces);
|
||||
createMenu();
|
||||
this.menuService.buildMenu();
|
||||
});
|
||||
ipcMain.handle('request-load-url', async (_event, url, id) => {
|
||||
await this.loadURL(url, id);
|
||||
});
|
||||
}
|
||||
|
||||
private registerMenu(): void {
|
||||
const hasWorkspaces = this.workspaceService.countWorkspaces() > 0;
|
||||
this.menuService.insertMenu(
|
||||
'window',
|
||||
[
|
||||
{
|
||||
label: 'Developer Tools',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Open Developer Tools of Active Workspace',
|
||||
accelerator: 'CmdOrCtrl+Option+I',
|
||||
click: () => this.viewService.getActiveBrowserView()?.webContents?.openDevTools(),
|
||||
enabled: hasWorkspaces,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'close',
|
||||
);
|
||||
}
|
||||
|
||||
public async createWorkspaceView(workspaceOptions: IWorkspace): Promise<void> {
|
||||
const newWorkspace = await this.workspaceService.create(workspaceOptions);
|
||||
const mainWindow = this.windowService.get(WindowNames.main);
|
||||
|
|
@ -154,7 +164,7 @@ export class WorkspaceView {
|
|||
// eslint-disable-next-line unicorn/no-null
|
||||
mainWindow.setBrowserView(null);
|
||||
mainWindow.setTitle(app.name);
|
||||
sendToAllWindows('update-title', '');
|
||||
this.windowService.sendToAllWindows('update-title', '');
|
||||
}
|
||||
} else if (this.workspaceService.countWorkspaces() > 1 && this.workspaceService.get(id)?.active === true) {
|
||||
const previousWorkspace = this.workspaceService.getPreviousWorkspace(id);
|
||||
|
|
@ -190,7 +200,20 @@ export class WorkspaceView {
|
|||
}
|
||||
}
|
||||
|
||||
public realignActiveWorkspaceView(): void {
|
||||
/**
|
||||
* Seems this is for relocating BrowserView in the electron window
|
||||
* // TODO: why we need this?
|
||||
*/
|
||||
public realignActiveWorkspace(): void {
|
||||
// this function only call browserView.setBounds
|
||||
// do not attempt to recall browserView.webContents.focus()
|
||||
// as it breaks page focus (cursor, scroll bar not visible)
|
||||
this.realignActiveWorkspaceView();
|
||||
// TODO: why we need to rebuild menu?
|
||||
this.menuService.buildMenu();
|
||||
}
|
||||
|
||||
private realignActiveWorkspaceView(): void {
|
||||
const activeWorkspace = this.workspaceService.getActiveWorkspace();
|
||||
const mainWindow = this.windowService.get(WindowNames.main);
|
||||
if (activeWorkspace !== undefined && mainWindow !== undefined) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue