diff --git a/app/src/electron.js b/app/src/electron.js index 46e4964c..553f4bc4 100644 --- a/app/src/electron.js +++ b/app/src/electron.js @@ -13,6 +13,8 @@ const BrowserWindow = electron.BrowserWindow; const path = require('path'); const url = require('url'); +let isNativeMenu = true; + // require('@electron/remote/main').initialize(); const configRootPath = path.join(app.getPath('userData'), 'config-root.json'); @@ -168,6 +170,24 @@ ipcMain.on('close-window', async (event, arg) => { ipcMain.on('set-title', async (event, arg) => { mainWindow.setTitle(arg); }); +ipcMain.on('window-action', async (event, arg) => { + switch (arg) { + case 'minimize': + mainWindow.minimize(); + break; + case 'maximize': + if (mainWindow.isMaximized()) { + mainWindow.unmaximize(); + } else { + mainWindow.maximize(); + } + break; + case 'close': + mainWindow.close(); + break; + } + mainWindow.setTitle(arg); +}); ipcMain.handle('showOpenDialog', async (event, options) => { const res = electron.dialog.showOpenDialogSync(mainWindow, options); @@ -183,13 +203,22 @@ ipcMain.handle('showItemInFolder', async (event, path) => { ipcMain.handle('openExternal', async (event, url) => { electron.shell.openExternal(url); }); +ipcMain.handle('isNativeMenu', async () => { + return isNativeMenu; +}); function createWindow() { const bounds = initialConfig['winBounds']; + isNativeMenu = os.platform() == 'darwin' ? true : false; + if (initialConfig['menuStyle'] == 'native') isNativeMenu = true; + if (initialConfig['menuStyle'] == 'client') isNativeMenu = false; + mainWindow = new BrowserWindow({ width: 1200, height: 800, title: 'DbGate', + frame: isNativeMenu, + titleBarStyle: isNativeMenu ? undefined : 'hidden', ...bounds, icon: os.platform() == 'win32' ? 'icon.ico' : path.resolve(__dirname, '../icon.png'), webPreferences: { diff --git a/packages/web/public/dimensions.css b/packages/web/public/dimensions.css index e4eec0b6..d4d74632 100644 --- a/packages/web/public/dimensions.css +++ b/packages/web/public/dimensions.css @@ -13,9 +13,14 @@ ); --dim-visible-toolbar: 1; /* set from JS */ + --dim-visible-titlebar: 0; /* set from JS */ --dim-toolbar-height: 30px; - --dim-header-top: calc(var(--dim-toolbar-height) * var(--dim-visible-toolbar)); + --dim-titlebar-height: 30px; + --dim-toolbar-top: calc(var(--dim-titlebar-height) * var(--dim-visible-titlebar)); + --dim-header-top: calc( + var(--dim-titlebar-height) * var(--dim-visible-titlebar) + var(--dim-toolbar-height) * var(--dim-visible-toolbar) + ); --dim-content-top: calc(var(--dim-header-top) + var(--dim-tabs-panel-height)); --dim-large-form-margin: 20px; diff --git a/packages/web/src/Screen.svelte b/packages/web/src/Screen.svelte index 4fd108f9..41176c5e 100644 --- a/packages/web/src/Screen.svelte +++ b/packages/web/src/Screen.svelte @@ -22,11 +22,26 @@ import ModalLayer from './modals/ModalLayer.svelte'; import DragAndDropFileTarget from './DragAndDropFileTarget.svelte'; import dragDropFileTarget from './utility/dragDropFileTarget'; + import TitleBar from './widgets/TitleBar.svelte'; + import { onMount } from 'svelte'; + import getElectron from './utility/getElectron'; $: currentThemeType = $currentThemeDefinition?.themeType == 'dark' ? 'theme-type-dark' : 'theme-type-light'; let domTabs; + let drawTitleBar = false; + onMount(async () => { + let draw = true; + const electron = getElectron(); + if (electron && (await electron.isNativeMenu())) { + draw = false; + } + drawTitleBar = draw; + document.documentElement.style.setProperty('--dim-visible-titlebar', drawTitleBar ? 1 : 0); + console.log('drawTitleBar', drawTitleBar); + }); + function handleTabsWheel(e) { if (!e.shiftKey) { e.preventDefault(); @@ -37,7 +52,7 @@ {#if $currentThemeDefinition?.themeCss} - {@html ``} + {@html ``} {/if} @@ -46,6 +61,11 @@ use:dragDropFileTarget on:contextmenu={e => e.preventDefault()} > + {#if drawTitleBar} +
+ +
+ {/if}
@@ -153,7 +173,7 @@ } .toolbar { position: fixed; - top: 0; + top: var(--dim-toolbar-top); height: var(--dim-toolbar-height); left: 0; right: 0; @@ -172,4 +192,12 @@ right: 0; bottom: var(--dim-statusbar-height); } + + .titlebar { + position: fixed; + top: 0; + left: 0; + right: 0; + height: var(--dim-titlebar-height); + } diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte index 297aa1e3..3b26a3e7 100644 --- a/packages/web/src/icons/FontIcon.svelte +++ b/packages/web/src/icons/FontIcon.svelte @@ -29,6 +29,11 @@ 'icon app': 'mdi mdi-layers-triple', 'icon open-in-new': 'mdi mdi-open-in-new', + 'icon window-restore': 'mdi mdi-window-restore', + 'icon window-close': 'mdi mdi-window-close', + 'icon window-minimize': 'mdi mdi-window-minimize', + 'img dbgate': 'mdi mdi-database color-icon-gold', + 'icon columns': 'mdi mdi-view-column', 'icon columns-outline': 'mdi mdi-view-column-outline', diff --git a/packages/web/src/utility/getElectron.ts b/packages/web/src/utility/getElectron.ts index 23e6fe9a..132a011e 100644 --- a/packages/web/src/utility/getElectron.ts +++ b/packages/web/src/utility/getElectron.ts @@ -27,6 +27,10 @@ class ElectronApi { await this.ipcRenderer.invoke('openExternal', url); } + async isNativeMenu() { + await this.ipcRenderer.invoke('isNativeMenu'); + } + async invoke(route, args) { const res = await this.ipcRenderer.invoke(route, args); return res; diff --git a/packages/web/src/widgets/TitleBar.svelte b/packages/web/src/widgets/TitleBar.svelte new file mode 100644 index 00000000..6feeb516 --- /dev/null +++ b/packages/web/src/widgets/TitleBar.svelte @@ -0,0 +1,76 @@ + + +
+
+ +
{title}
+ +
+
electron.send('window-action', 'minimize')}> + +
+
+ electron.send('window-action', 'maximize')} /> +
+
electron.send('window-action', 'close')}> + +
+
+
+ +