mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
feat(customWindowTitleBar): add sexy custom window title bar <3
This commit is contained in:
parent
b070233f29
commit
c5625de6f1
17 changed files with 99 additions and 14 deletions
|
|
@ -11,6 +11,8 @@ import {
|
|||
import { lockscreen } from '../lockscreen';
|
||||
import { errorHandlerWithFrontendInform } from '../error-handler-with-frontend-inform';
|
||||
import { GlobalConfigState } from '../../src/app/features/config/global-config.model';
|
||||
import { saveSimpleStore } from '../simple-store';
|
||||
import { SimpleStoreKey } from '../shared-with-frontend/simple-store.const';
|
||||
|
||||
export const initAppControlIpc = (): void => {
|
||||
ipcMain.on(IPC.SHUTDOWN_NOW, quitApp);
|
||||
|
|
@ -19,10 +21,17 @@ export const initAppControlIpc = (): void => {
|
|||
ipcMain.on(IPC.OPEN_DEV_TOOLS, () => getWin().webContents.openDevTools());
|
||||
ipcMain.on(IPC.RELOAD_MAIN_WIN, () => getWin().reload());
|
||||
|
||||
ipcMain.on(IPC.TRANSFER_SETTINGS_TO_ELECTRON, (ev, cfg: GlobalConfigState) => {
|
||||
ipcMain.on(IPC.TRANSFER_SETTINGS_TO_ELECTRON, async (ev, cfg: GlobalConfigState) => {
|
||||
setIsMinimizeToTray(cfg.misc.isMinimizeToTray);
|
||||
setIsTrayShowCurrentTask(cfg.misc.isTrayShowCurrentTask);
|
||||
setIsTrayShowCurrentCountdown(cfg.misc.isTrayShowCurrentCountdown);
|
||||
|
||||
if (cfg.misc.isUseCustomWindowTitleBar !== undefined) {
|
||||
await saveSimpleStore(
|
||||
SimpleStoreKey.IS_USE_CUSTOM_WINDOW_TITLE_BAR,
|
||||
cfg.misc.isUseCustomWindowTitleBar,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.SHOW_OR_FOCUS, () => {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import { log } from 'electron-log/main';
|
|||
import { loadSimpleStoreAll, saveSimpleStore } from '../simple-store';
|
||||
import { getWin } from '../main-window';
|
||||
import { errorHandlerWithFrontendInform } from '../error-handler-with-frontend-inform';
|
||||
import { SimpleStoreKey } from '../shared-with-frontend/simple-store.const';
|
||||
|
||||
const COMMAND_MAP_PROP = 'allowedCommands';
|
||||
const COMMAND_MAP_PROP = SimpleStoreKey.ALLOWED_COMMANDS;
|
||||
|
||||
export const initExecIpc = (): void => {
|
||||
ipcMain.on(IPC.EXEC, execWithFrontendErrorHandlerInform);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import windowStateKeeper from 'electron-window-state';
|
|||
import {
|
||||
App,
|
||||
BrowserWindow,
|
||||
BrowserWindowConstructorOptions,
|
||||
ipcMain,
|
||||
Menu,
|
||||
MenuItem,
|
||||
|
|
@ -22,6 +23,8 @@ import {
|
|||
showOverlayWindow,
|
||||
} from './overlay-indicator/overlay-indicator';
|
||||
import { getIsMinimizeToTray, getIsQuiting, setIsQuiting } from './shared-state';
|
||||
import { loadSimpleStoreAll } from './simple-store';
|
||||
import { SimpleStoreKey } from './shared-with-frontend/simple-store.const';
|
||||
|
||||
let mainWin: BrowserWindow;
|
||||
|
||||
|
|
@ -44,7 +47,7 @@ export const getIsAppReady = (): boolean => {
|
|||
return mainWinModule.isAppReady;
|
||||
};
|
||||
|
||||
export const createWindow = ({
|
||||
export const createWindow = async ({
|
||||
IS_DEV,
|
||||
ICONS_FOLDER,
|
||||
quitApp,
|
||||
|
|
@ -56,7 +59,7 @@ export const createWindow = ({
|
|||
quitApp: () => void;
|
||||
app: App;
|
||||
customUrl?: string;
|
||||
}): BrowserWindow => {
|
||||
}): Promise<BrowserWindow> => {
|
||||
// make sure the main window isn't already created
|
||||
if (mainWin) {
|
||||
errorHandlerWithFrontendInform('Main window already exists');
|
||||
|
|
@ -73,6 +76,24 @@ export const createWindow = ({
|
|||
defaultHeight: 800,
|
||||
});
|
||||
|
||||
const simpleStore = await loadSimpleStoreAll();
|
||||
const persistedIsUseCustomWindowTitleBar =
|
||||
simpleStore[SimpleStoreKey.IS_USE_CUSTOM_WINDOW_TITLE_BAR];
|
||||
const legacyIsUseObsidianStyleHeader =
|
||||
simpleStore[SimpleStoreKey.LEGACY_IS_USE_OBSIDIAN_STYLE_HEADER];
|
||||
const isUseCustomWindowTitleBar =
|
||||
persistedIsUseCustomWindowTitleBar ?? legacyIsUseObsidianStyleHeader ?? true;
|
||||
const titleBarStyle: BrowserWindowConstructorOptions['titleBarStyle'] =
|
||||
isUseCustomWindowTitleBar || IS_MAC ? 'hidden' : 'default';
|
||||
const titleBarOverlay: BrowserWindowConstructorOptions['titleBarOverlay'] =
|
||||
isUseCustomWindowTitleBar && !IS_MAC
|
||||
? {
|
||||
color: '#00000000',
|
||||
symbolColor: '#fff',
|
||||
height: 44,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
mainWin = new BrowserWindow({
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
|
|
@ -81,7 +102,8 @@ export const createWindow = ({
|
|||
minHeight: 240,
|
||||
minWidth: 300,
|
||||
title: IS_DEV ? 'Super Productivity D' : 'Super Productivity',
|
||||
titleBarStyle: IS_MAC ? 'hidden' : 'default',
|
||||
titleBarStyle,
|
||||
titleBarOverlay,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
scrollBounce: true,
|
||||
|
|
@ -99,7 +121,7 @@ export const createWindow = ({
|
|||
},
|
||||
icon: ICONS_FOLDER + '/icon_256x256.png',
|
||||
// Wayland compatibility: disable transparent/frameless features that can cause issues
|
||||
// transparent: false,
|
||||
transparent: false,
|
||||
// frame: true,
|
||||
});
|
||||
|
||||
|
|
|
|||
6
electron/shared-with-frontend/simple-store.const.ts
Normal file
6
electron/shared-with-frontend/simple-store.const.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export enum SimpleStoreKey {
|
||||
IS_USE_CUSTOM_WINDOW_TITLE_BAR = 'isUseCustomWindowTitleBar',
|
||||
ALLOWED_COMMANDS = 'allowedCommands',
|
||||
// Legacy key kept for backwards compatibility when reading persisted settings
|
||||
LEGACY_IS_USE_OBSIDIAN_STYLE_HEADER = 'isUseObsidianStyleHeader',
|
||||
}
|
||||
|
|
@ -149,7 +149,7 @@ export const startApp = (): void => {
|
|||
|
||||
// APP EVENT LISTENERS
|
||||
// -------------------
|
||||
appIN.on('ready', createMainWin);
|
||||
appIN.on('ready', () => createMainWin());
|
||||
appIN.on('ready', () => initBackupAdapter());
|
||||
appIN.on('ready', () => initLocalFileSyncAdapter());
|
||||
appIN.on('ready', () => initFullScreenBlocker(IS_DEV));
|
||||
|
|
@ -353,8 +353,8 @@ export const startApp = (): void => {
|
|||
}
|
||||
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
function createMainWin(): void {
|
||||
mainWin = createWindow({
|
||||
async function createMainWin(): Promise<void> {
|
||||
mainWin = await createWindow({
|
||||
app,
|
||||
IS_DEV,
|
||||
ICONS_FOLDER,
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ export enum BodyClass {
|
|||
isLightTheme = 'isLightTheme',
|
||||
isDarkTheme = 'isDarkTheme',
|
||||
isDisableBackgroundTint = 'isDisableBackgroundTint',
|
||||
isEnabledBackgroundGradient = 'isEnabledBackgroundGradient',
|
||||
isDisableAnimations = 'isDisableAnimations',
|
||||
isObsidianStyleHeader = 'isObsidianStyleHeader',
|
||||
isDataImportInProgress = 'isDataImportInProgress',
|
||||
hasBgImage = 'hasBgImage',
|
||||
hasMobileBottomNav = 'hasMobileBottomNav',
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@
|
|||
z-index: 1002;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
-webkit-app-region: drag;
|
||||
|
||||
// Slight elevation and clear background for overlay/mobile mode is defined in the mobile block below
|
||||
|
||||
|
|
@ -165,6 +166,7 @@
|
|||
cursor: pointer;
|
||||
transition: var(--transition-standard);
|
||||
color: var(--sidenav-text-secondary);
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
:host-context(.isMac.isElectron) & {
|
||||
top: calc(var(--top) + var(--mac-title-bar-padding, 0px));
|
||||
|
|
@ -207,6 +209,7 @@
|
|||
|
||||
.nav-item {
|
||||
margin-bottom: 2px;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
&.has-children {
|
||||
margin-bottom: 4px;
|
||||
|
|
@ -221,6 +224,7 @@
|
|||
border: none;
|
||||
opacity: 1;
|
||||
flex-shrink: 0;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
// Resize handle (now positioned relative to host component)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,14 @@
|
|||
|
||||
:host-context(.isMac.isElectron) {
|
||||
padding-top: calc(var(--mac-title-bar-padding) - 8px);
|
||||
}
|
||||
|
||||
:host-context(.isObsidianStyleHeader.isElectron) {
|
||||
padding-right: var(--window-controls-width); // Space for window controls
|
||||
}
|
||||
|
||||
:host-context(.isMac.isElectron),
|
||||
:host-context(.isObsidianStyleHeader.isElectron) {
|
||||
-webkit-app-region: drag;
|
||||
cursor: grab;
|
||||
|
||||
|
|
|
|||
|
|
@ -281,6 +281,15 @@ export class GlobalThemeService {
|
|||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
const misc = this._globalConfigService.misc();
|
||||
if (misc?.isUseCustomWindowTitleBar !== false) {
|
||||
this.document.body.classList.add(BodyClass.isObsidianStyleHeader);
|
||||
} else {
|
||||
this.document.body.classList.remove(BodyClass.isObsidianStyleHeader);
|
||||
}
|
||||
});
|
||||
|
||||
// Add/remove hasBgImage class to body when background image changes
|
||||
effect(() => {
|
||||
if (this.backgroundImg()) {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,18 @@ export const MISC_SETTINGS_FORM_CFG: ConfigFormSection<MiscConfig> = {
|
|||
label: T.GCF.MISC.IS_OVERLAY_INDICATOR_ENABLED,
|
||||
},
|
||||
},
|
||||
...((IS_ELECTRON
|
||||
? [
|
||||
{
|
||||
key: 'isUseCustomWindowTitleBar',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: T.GCF.MISC.IS_USE_CUSTOM_WINDOW_TITLE_BAR,
|
||||
description: T.GCF.MISC.IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []) as LimitedFormlyFieldConfig<MiscConfig>[]),
|
||||
{
|
||||
key: 'customTheme',
|
||||
type: 'select',
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export type MiscConfig = Readonly<{
|
|||
isShowProductivityTipLonger?: boolean;
|
||||
isTrayShowCurrentCountdown?: boolean;
|
||||
isOverlayIndicatorEnabled?: boolean;
|
||||
isUseCustomWindowTitleBar?: boolean;
|
||||
customTheme?: string;
|
||||
defaultStartPage?: number;
|
||||
unsplashApiKey?: string | null;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<div class="mac-os-drag-bar"></div>
|
||||
<div class="electron-drag-bar"></div>
|
||||
|
||||
@if (!isPomodoroEnabled()) {
|
||||
<header>
|
||||
|
|
|
|||
|
|
@ -22,17 +22,17 @@
|
|||
--z-focus-mode-countdown: 11;
|
||||
}
|
||||
|
||||
.mac-os-drag-bar {
|
||||
.electron-drag-bar {
|
||||
-webkit-app-region: drag;
|
||||
height: 50px;
|
||||
width: 160px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: var(--z-focus-mode-drag-bar);
|
||||
display: none;
|
||||
|
||||
:host-context(.isMac) & {
|
||||
:host-context(.isElectron) & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,10 @@ main {
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
:host-context(.isObsidianStyleHeader.isElectron.isNoMac) & {
|
||||
top: calc(var(--s) + 40px);
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
font-size: 32px !important; //make it bigger, the default being 24px.
|
||||
width: 32px;
|
||||
|
|
|
|||
|
|
@ -1930,6 +1930,8 @@ const T = {
|
|||
IS_TRAY_SHOW_CURRENT_COUNTDOWN: 'GCF.MISC.IS_TRAY_SHOW_CURRENT_COUNTDOWN',
|
||||
IS_TRAY_SHOW_CURRENT_TASK: 'GCF.MISC.IS_TRAY_SHOW_CURRENT_TASK',
|
||||
IS_TURN_OFF_MARKDOWN: 'GCF.MISC.IS_TURN_OFF_MARKDOWN',
|
||||
IS_USE_CUSTOM_WINDOW_TITLE_BAR: 'GCF.MISC.IS_USE_CUSTOM_WINDOW_TITLE_BAR',
|
||||
IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT: 'GCF.MISC.IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT',
|
||||
START_OF_NEXT_DAY: 'GCF.MISC.START_OF_NEXT_DAY',
|
||||
START_OF_NEXT_DAY_HINT: 'GCF.MISC.START_OF_NEXT_DAY_HINT',
|
||||
TASK_NOTES_TPL: 'GCF.MISC.TASK_NOTES_TPL',
|
||||
|
|
|
|||
|
|
@ -1901,6 +1901,8 @@
|
|||
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Show current countdown in the tray / status menu (desktop mac only)",
|
||||
"IS_TRAY_SHOW_CURRENT_TASK": "Show current task in the tray / status menu (desktop mac/windows only)",
|
||||
"IS_TURN_OFF_MARKDOWN": "Turn off markdown parsing for task notes",
|
||||
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Use custom title bar (Windows/Linux only)",
|
||||
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Requires restart to take effect",
|
||||
"START_OF_NEXT_DAY": "Start time of the next day",
|
||||
"START_OF_NEXT_DAY_HINT": "from when (in hour) you want to count the next day has started. default is midnight which is 0.",
|
||||
"TASK_NOTES_TPL": "Task description template",
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
--mat-mini-fab-size: 48px;
|
||||
--mac-title-bar-padding: 20px;
|
||||
--card-border-radius: 4px;
|
||||
--window-controls-width: 96px;
|
||||
|
||||
// Z-index layers
|
||||
--z-focus-mode-overlay: 101;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ body {
|
|||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.isObsidianStyleHeader.isElectron {
|
||||
border: 1px solid var(--divider-color);
|
||||
}
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue