2023-07-28 12:51:33 +01:00
|
|
|
/*
|
2024-09-06 17:56:18 +01:00
|
|
|
Copyright 2023, 2024 New Vector Ltd.
|
2023-07-28 12:51:33 +01:00
|
|
|
|
2025-01-17 11:44:49 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
2024-09-06 17:56:18 +01:00
|
|
|
Please see LICENSE files in the repository root for full details.
|
2023-07-28 12:51:33 +01:00
|
|
|
*/
|
|
|
|
|
|
2024-10-30 18:06:03 +00:00
|
|
|
import type { BrowserWindow } from "electron";
|
2023-07-28 12:51:33 +01:00
|
|
|
|
2026-07-17 19:25:14 +03:00
|
|
|
/**
|
2026-07-27 14:50:50 +01:00
|
|
|
* Height of the styled title bar band, matching the design spec
|
|
|
|
|
* (https://www.figma.com/design/MAUsalKv7bRRNKlAAigtNd/Community-contributions?node-id=76-12996).
|
|
|
|
|
* The `trafficLightPosition` in `electron-main.ts` vertically centres the native window controls
|
|
|
|
|
* within this band — keep the two in sync.
|
|
|
|
|
*/
|
|
|
|
|
export const TITLE_BAR_HEIGHT_PX = 32;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build the CSS injected into the renderer to draw the macOS title bar band.
|
2026-07-17 19:25:14 +03:00
|
|
|
*
|
2026-07-27 14:50:50 +01:00
|
|
|
* `electron-main.ts` uses `titleBarStyle: "hidden"`, which keeps the native window frame, rounded
|
|
|
|
|
* corners and traffic lights but removes the native bar surface. This CSS paints that surface: a
|
|
|
|
|
* full-width strip at the top of the window in the canvas background colour with a hairline
|
|
|
|
|
* separator underneath, and pushes the app content below it. The strip is the window's drag
|
|
|
|
|
* handle.
|
|
|
|
|
*
|
|
|
|
|
* The drag region is built from `-webkit-app-region` rects only: elements keep the default value
|
|
|
|
|
* (`none`) and are ignored unless they explicitly set `drag`/`no-drag`. So the band stays draggable
|
|
|
|
|
* underneath any overlay whose surface is transparent to the calc (a dialog's backdrop, a menu's
|
|
|
|
|
* container), and an overlay panel that a user clicks (dialog panels, context menus, the lightbox
|
|
|
|
|
* chrome) sets `no-drag` so its rect is subtracted and it stays interactive. An element must never
|
|
|
|
|
* be both clickable and a drag handle.
|
2026-07-17 19:25:14 +03:00
|
|
|
*
|
|
|
|
|
* Extracted as a pure helper so the string contract can be unit-tested (see macos-titlebar.test.ts).
|
|
|
|
|
*/
|
|
|
|
|
export function buildTitleBarCss(): string {
|
|
|
|
|
return `
|
2026-07-27 14:50:50 +01:00
|
|
|
/* Reserve a band at the top of the window for the title bar */
|
|
|
|
|
body {
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
height: 100%;
|
|
|
|
|
padding-top: ${TITLE_BAR_HEIGHT_PX}px !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The title bar itself: canvas background with a hairline separator below */
|
|
|
|
|
body::before {
|
|
|
|
|
content: "";
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
height: ${TITLE_BAR_HEIGHT_PX}px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
/* Fallback colours for pages loaded without the app themes (e.g. the error view) */
|
|
|
|
|
background: var(--cpd-color-bg-canvas-default, #ffffff);
|
|
|
|
|
border-bottom: 1px solid var(--cpd-color-separator-primary, #e1e6ec);
|
2023-09-14 08:55:11 +01:00
|
|
|
-webkit-app-region: drag;
|
|
|
|
|
-webkit-user-select: none;
|
|
|
|
|
}
|
2026-07-27 14:50:50 +01:00
|
|
|
|
|
|
|
|
/* Exclude floating menus and tooltips, which render in body-level portals */
|
|
|
|
|
[data-radix-popper-content-wrapper] {
|
|
|
|
|
-webkit-app-region: no-drag;
|
2023-07-28 12:51:33 +01:00
|
|
|
}
|
2026-07-27 14:50:50 +01:00
|
|
|
|
2023-07-28 12:51:33 +01:00
|
|
|
/* Mark the splash screen as a drag handle */
|
|
|
|
|
.mx_MatrixChat_splash {
|
|
|
|
|
-webkit-app-region: drag;
|
|
|
|
|
}
|
|
|
|
|
/* Exclude the splash buttons from being drag handles */
|
|
|
|
|
.mx_MatrixChat_splashButtons {
|
|
|
|
|
-webkit-app-region: no-drag;
|
|
|
|
|
}
|
2026-07-27 14:50:50 +01:00
|
|
|
|
2023-07-28 12:51:33 +01:00
|
|
|
/* Mark the background as a drag handle */
|
|
|
|
|
.mx_AuthPage {
|
|
|
|
|
-webkit-app-region: drag;
|
|
|
|
|
}
|
|
|
|
|
/* Exclude the main content elements from being drag handles */
|
2025-09-16 11:53:46 +01:00
|
|
|
.mx_AuthPage .mx_AuthPage_modalContent,
|
2023-07-28 12:51:33 +01:00
|
|
|
.mx_AuthPage .mx_AuthPage_modalBlur,
|
2025-09-05 10:17:21 +01:00
|
|
|
.mx_AuthPage .mx_AuthFooter > *,
|
|
|
|
|
.mx_AuthPage .mx_Dropdown_menu {
|
2023-07-28 12:51:33 +01:00
|
|
|
-webkit-app-region: no-drag;
|
|
|
|
|
}
|
2026-07-27 14:50:50 +01:00
|
|
|
|
|
|
|
|
/* The image lightbox covers the whole window, including the title bar band; keep its
|
|
|
|
|
sender info clear of the traffic lights, and let its header double as a drag handle */
|
|
|
|
|
.mx_ImageView_info_wrapper {
|
|
|
|
|
margin-top: ${TITLE_BAR_HEIGHT_PX}px;
|
2023-07-28 12:51:33 +01:00
|
|
|
}
|
2023-08-17 05:20:58 +01:00
|
|
|
.mx_ImageView_panel {
|
|
|
|
|
-webkit-app-region: drag;
|
|
|
|
|
}
|
2023-07-28 12:51:33 +01:00
|
|
|
/* Exclude header interactive elements from being drag handles */
|
2023-08-17 05:20:58 +01:00
|
|
|
.mx_ImageView_panel > .mx_ImageView_info_wrapper,
|
|
|
|
|
.mx_ImageView_panel > .mx_ImageView_title,
|
|
|
|
|
.mx_ImageView_panel > .mx_ImageView_toolbar > * {
|
2023-07-28 12:51:33 +01:00
|
|
|
-webkit-app-region: no-drag;
|
|
|
|
|
}
|
2026-07-27 14:50:50 +01:00
|
|
|
|
|
|
|
|
/* Exclude context menus and their backgrounds, which may open within the band */
|
2023-09-14 08:55:11 +01:00
|
|
|
.mx_ContextualMenu, .mx_ContextualMenu_background {
|
|
|
|
|
-webkit-app-region: no-drag;
|
|
|
|
|
}
|
|
|
|
|
/* Exclude iframes, such as recaptcha */
|
|
|
|
|
iframe {
|
|
|
|
|
-webkit-app-region: no-drag;
|
|
|
|
|
}
|
2026-07-17 19:25:14 +03:00
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function setupMacosTitleBar(window: BrowserWindow): void {
|
|
|
|
|
if (process.platform !== "darwin") return;
|
|
|
|
|
|
|
|
|
|
let cssKey: string | undefined;
|
|
|
|
|
|
|
|
|
|
async function applyStyling(): Promise<void> {
|
|
|
|
|
cssKey = await window.webContents.insertCSS(buildTitleBarCss());
|
2023-07-31 09:33:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.on("enter-full-screen", () => {
|
|
|
|
|
if (cssKey !== undefined) {
|
2024-06-12 17:17:24 +01:00
|
|
|
void window.webContents.removeInsertedCSS(cssKey);
|
2023-07-31 09:33:38 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
window.on("leave-full-screen", () => {
|
2024-06-12 17:17:24 +01:00
|
|
|
void applyStyling();
|
2023-07-31 09:33:38 +01:00
|
|
|
});
|
|
|
|
|
window.webContents.on("did-finish-load", () => {
|
|
|
|
|
if (!window.isFullScreen()) {
|
2024-06-12 17:17:24 +01:00
|
|
|
void applyStyling();
|
2023-07-31 09:33:38 +01:00
|
|
|
}
|
2023-07-28 12:51:33 +01:00
|
|
|
});
|
|
|
|
|
}
|