diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js index ee38ab8cd..b2ebef90f 100644 --- a/src/public/app/layouts/desktop_layout.js +++ b/src/public/app/layouts/desktop_layout.js @@ -1,6 +1,7 @@ import FlexContainer from "../widgets/containers/flex_container.js"; import GlobalMenuWidget from "../widgets/buttons/global_menu.js"; import TabRowWidget from "../widgets/tab_row.js"; +import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js"; import LeftPaneContainer from "../widgets/containers/left_pane_container.js"; import NoteTreeWidget from "../widgets/note_tree.js"; import NoteTitleWidget from "../widgets/note_title.js"; @@ -94,7 +95,11 @@ export default class DesktopLayout { const launcherPaneIsHorizontal = (options.get("layoutOrientation") === "horizontal"); const launcherPane = this.#buildLauncherPane(launcherPaneIsHorizontal); + const isMac = (window.glob.platform === "darwin"); + const isWindows = (window.glob.platform === "windows"); + const hasNativeTitleBar = (window.glob.hasNativeTitleBar); const fullWidthTabBar = true; + const customTitleBarButtons = (hasNativeTitleBar && !isMac && !isWindows); return new RootContainer(true) .setParent(appContext) @@ -104,6 +109,7 @@ export default class DesktopLayout { .child(new FlexContainer( "row").id("tab-row-left-spacer")) .optChild(launcherPaneIsHorizontal, new LeftPaneToggleWidget(true)) .child(new TabRowWidget().class("full-width")) + .optChild(customTitleBarButtons, new TitleBarButtonsWidget()) .css('height', '40px') .css('background-color', 'var(--launcher-pane-background-color)') .setParent(appContext) @@ -122,6 +128,7 @@ export default class DesktopLayout { .css("flex-grow", "1") .optChild(!fullWidthTabBar, new FlexContainer('row') .child(new TabRowWidget()) + .optChild(customTitleBarButtons, new TitleBarButtonsWidget()) .css('height', '40px') ) .child(new FlexContainer('row') diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js new file mode 100644 index 000000000..edb270f77 --- /dev/null +++ b/src/public/app/widgets/title_bar_buttons.js @@ -0,0 +1,112 @@ +import BasicWidget from "./basic_widget.js"; +import options from "../services/options.js"; +import utils from "../services/utils.js"; +import { t } from "../services/i18n.js"; + +const TPL = ` +
+ + + +
+
+
+
+
`; + +export default class TitleBarButtonsWidget extends BasicWidget { + doRender() { + if (!utils.isElectron() || options.is('nativeTitleBarVisible')) { + return this.$widget = $('
'); + } + + this.$widget = $(TPL); + this.contentSized(); + + const $topBtn = this.$widget.find(".top-btn"); + const $minimizeBtn = this.$widget.find(".minimize-btn"); + const $maximizeBtn = this.$widget.find(".maximize-btn"); + const $closeBtn = this.$widget.find(".close-btn"); + + // When the window is restarted, the window will not be reset when it is set to the top, + // so get the window status and set the icon background + setTimeout(() => { + const remote = utils.dynamicRequire('@electron/remote'); + if (remote.BrowserWindow.getFocusedWindow()?.isAlwaysOnTop()) { + $topBtn.addClass('active'); + } + }, 1000); + + $topBtn.on('click', () => { + $topBtn.trigger('blur'); + const remote = utils.dynamicRequire('@electron/remote'); + const focusedWindow = remote.BrowserWindow.getFocusedWindow(); + const isAlwaysOnTop = focusedWindow.isAlwaysOnTop() + if (isAlwaysOnTop) { + focusedWindow.setAlwaysOnTop(false) + $topBtn.removeClass('active'); + } else { + focusedWindow.setAlwaysOnTop(true); + $topBtn.addClass('active'); + } + }); + + $minimizeBtn.on('click', () => { + $minimizeBtn.trigger('blur'); + const remote = utils.dynamicRequire('@electron/remote'); + remote.BrowserWindow.getFocusedWindow().minimize(); + }); + + $maximizeBtn.on('click', () => { + $maximizeBtn.trigger('blur'); + const remote = utils.dynamicRequire('@electron/remote'); + const focusedWindow = remote.BrowserWindow.getFocusedWindow(); + + if (focusedWindow.isMaximized()) { + focusedWindow.unmaximize(); + } else { + focusedWindow.maximize(); + } + }); + + $closeBtn.on('click', () => { + $closeBtn.trigger('blur'); + const remote = utils.dynamicRequire('@electron/remote'); + remote.BrowserWindow.getFocusedWindow().close(); + }); + } +} diff --git a/src/services/window.ts b/src/services/window.ts index 44e71e9b7..72a337d9a 100644 --- a/src/services/window.ts +++ b/src/services/window.ts @@ -114,9 +114,21 @@ async function createMainWindow(app: App) { function getWindowExtraOpts() { const extraOpts: Partial = {}; + + const isMac = (process.platform === "darwin"); + const isWindows = (process.platform === "win32"); + if (!optionService.getOptionBool('nativeTitleBarVisible')) { - extraOpts.titleBarStyle = (process.platform !== "darwin" ? "hidden" : "hiddenInset"); - extraOpts.titleBarOverlay = true; + if (isMac) { + extraOpts.titleBarStyle = "hiddenInset"; + extraOpts.titleBarOverlay = true; + } else if (isWindows) { + extraOpts.titleBarStyle = "hidden"; + extraOpts.titleBarOverlay = true; + } else { + // Linux or other platforms. + extraOpts.frame = false; + } } return extraOpts; diff --git a/src/views/desktop.ejs b/src/views/desktop.ejs index f922e9454..3e1ce7229 100644 --- a/src/views/desktop.ejs +++ b/src/views/desktop.ejs @@ -36,6 +36,8 @@ triliumVersion: "<%= triliumVersion %>", assetPath: "<%= assetPath %>", appPath: "<%= appPath %>", + platform: "<%= platform %>", + hasNativeTitleBar: "<%= hasNativeTitleBar %>", TRILIUM_SAFE_MODE: <%= !!process.env.TRILIUM_SAFE_MODE %> };