diff --git a/main.js b/main.js
index a14f1bb6f..e1aa5286e 100644
--- a/main.js
+++ b/main.js
@@ -97,8 +97,16 @@ const {
installWebHandler,
} = require('./app/protocol_filter');
const { installPermissionsHandler } = require('./app/permissions');
+const OS = require('./ts/OS');
const { isBeta } = require('./ts/util/version');
const { isSgnlHref, parseSgnlHref } = require('./ts/util/sgnlHref');
+const {
+ toggleMaximizedBrowserWindow,
+} = require('./ts/util/toggleMaximizedBrowserWindow');
+const {
+ getTitleBarVisibility,
+ TitleBarVisibility,
+} = require('./ts/types/Settings');
let appStartInitialSpellcheckSetting = true;
@@ -268,11 +276,10 @@ function isVisible(window, bounds) {
}
let windowIcon;
-const OS = process.platform;
-if (OS === 'win32') {
+if (OS.isWindows()) {
windowIcon = path.join(__dirname, 'build', 'icons', 'win', 'icon.ico');
-} else if (OS === 'linux') {
+} else if (OS.isLinux()) {
windowIcon = path.join(__dirname, 'images', 'signal-logo-desktop-linux.png');
} else {
windowIcon = path.join(__dirname, 'build', 'icons', 'png', '512x512.png');
@@ -287,6 +294,10 @@ async function createWindow() {
minWidth: MIN_WIDTH,
minHeight: MIN_HEIGHT,
autoHideMenuBar: false,
+ titleBarStyle:
+ getTitleBarVisibility() === TitleBarVisibility.Hidden
+ ? 'hidden'
+ : 'default',
backgroundColor:
config.environment === 'test' || config.environment === 'test-lib'
? '#ffffff' // Tests should always be rendered on a white background
@@ -383,14 +394,18 @@ async function createWindow() {
// This is a fallback in case we drop an event for some reason.
setInterval(setWindowFocus, 10000);
+ const moreKeys = {
+ isFullScreen: String(Boolean(mainWindow.isFullScreen())),
+ };
+
if (config.environment === 'test') {
- mainWindow.loadURL(prepareURL([__dirname, 'test', 'index.html']));
+ mainWindow.loadURL(prepareURL([__dirname, 'test', 'index.html'], moreKeys));
} else if (config.environment === 'test-lib') {
mainWindow.loadURL(
- prepareURL([__dirname, 'libtextsecure', 'test', 'index.html'])
+ prepareURL([__dirname, 'libtextsecure', 'test', 'index.html'], moreKeys)
);
} else {
- mainWindow.loadURL(prepareURL([__dirname, 'background.html']));
+ mainWindow.loadURL(prepareURL([__dirname, 'background.html'], moreKeys));
}
if (config.get('openDevTools')) {
@@ -440,10 +455,7 @@ async function createWindow() {
// On Mac, or on other platforms when the tray icon is in use, the window
// should be only hidden, not closed, when the user clicks the close button
- if (
- !windowState.shouldQuit() &&
- (usingTrayIcon || process.platform === 'darwin')
- ) {
+ if (!windowState.shouldQuit() && (usingTrayIcon || OS.isMacOS())) {
// toggle the visibility of the show/hide tray icon menu entries
if (tray) {
tray.updateContextMenu();
@@ -471,12 +483,46 @@ async function createWindow() {
// when you should delete the corresponding element.
mainWindow = null;
});
+
+ mainWindow.on('enter-full-screen', () => {
+ mainWindow.webContents.send('full-screen-change', true);
+ });
+ mainWindow.on('leave-full-screen', () => {
+ mainWindow.webContents.send('full-screen-change', false);
+ });
}
ipc.on('show-window', () => {
showWindow();
});
+ipc.on('title-bar-double-click', () => {
+ if (!mainWindow) {
+ return;
+ }
+
+ if (OS.isMacOS()) {
+ switch (
+ systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string')
+ ) {
+ case 'Minimize':
+ mainWindow.minimize();
+ break;
+ case 'Maximize':
+ toggleMaximizedBrowserWindow(mainWindow);
+ break;
+ default:
+ // If this is disabled, it'll be 'None'. If it's anything else, that's unexpected,
+ // but we'll just no-op.
+ break;
+ }
+ } else {
+ // This is currently only supported on macOS. This `else` branch is just here when/if
+ // we add support for other operating systems.
+ toggleMaximizedBrowserWindow(mainWindow);
+ }
+});
+
let isReadyForUpdates = false;
async function readyForUpdates() {
if (isReadyForUpdates) {
@@ -886,7 +932,7 @@ app.on('ready', async () => {
protocol: electronProtocol,
userDataPath,
installPath,
- isWindows: process.platform === 'win32',
+ isWindows: OS.isWindows(),
});
}
@@ -1138,7 +1184,7 @@ app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
const shouldAutoClose =
- process.platform !== 'darwin' ||
+ !OS.isMacOS() ||
config.environment === 'test' ||
config.environment === 'test-lib';
@@ -1203,7 +1249,7 @@ ipc.on('draw-attention', () => {
return;
}
- if (process.platform === 'win32' || process.platform === 'linux') {
+ if (OS.isWindows() || OS.isLinux()) {
mainWindow.flashFrame(true);
}
});
@@ -1232,7 +1278,7 @@ ipc.on('close-about', () => {
if (aboutWindow) {
// Exiting child window when on full screen mode (MacOs only) hides the main window
// Fix to issue #4540
- if (mainWindow.isFullScreen() && process.platform === 'darwin') {
+ if (mainWindow.isFullScreen() && OS.isMacOS()) {
mainWindow.setFullScreen(false);
mainWindow.show();
mainWindow.setFullScreen(true);
diff --git a/preload.js b/preload.js
index bbc45422b..e659ba6c5 100644
--- a/preload.js
+++ b/preload.js
@@ -113,6 +113,10 @@ try {
ipc.send('show-window');
};
+ window.titleBarDoubleClick = () => {
+ ipc.send('title-bar-double-click');
+ };
+
window.setAutoHideMenuBar = autoHide =>
ipc.send('set-auto-hide-menu-bar', autoHide);
@@ -142,6 +146,19 @@ try {
Whisper.events.trigger('setupAsStandalone');
});
+ {
+ let isFullScreen = config.isFullScreen === 'true';
+
+ window.isFullScreen = () => isFullScreen;
+ // This is later overwritten.
+ window.onFullScreenChange = _.noop;
+
+ ipc.on('full-screen-change', (_event, isFull) => {
+ isFullScreen = Boolean(isFull);
+ window.onFullScreenChange(isFullScreen);
+ });
+ }
+
// Settings-related events
window.showSettings = () => ipc.send('show-settings');
diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss
index fc0b45836..d06d23e12 100644
--- a/stylesheets/_conversation.scss
+++ b/stylesheets/_conversation.scss
@@ -31,14 +31,13 @@
}
.panel {
- height: calc(100% - #{$header-height});
+ height: calc(100% - #{$header-height} - var(--title-bar-drag-area-height));
overflow-y: scroll;
z-index: 1;
position: absolute;
left: 0;
- top: $header-height;
+ top: calc(#{$header-height} + var(--title-bar-drag-area-height));
width: 100%;
- height: calc(100% - $header-height);
@include light-theme() {
background-color: $color-white;
diff --git a/stylesheets/_global.scss b/stylesheets/_global.scss
index 8ca6bd336..b8b1fff9d 100644
--- a/stylesheets/_global.scss
+++ b/stylesheets/_global.scss
@@ -1,4 +1,4 @@
-// Copyright 2015-2020 Signal Messenger, LLC
+// Copyright 2015-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
html {
@@ -15,6 +15,15 @@ body {
color: $color-gray-90;
@include font-body-1;
+
+ // These should match the logic in `ts/types/Settings.ts`'s `getTitleBarVisibility`.
+ //
+ // It'd be great if we could use the `:fullscreen` selector here, but that does not seem
+ // to work with Electron, at least on macOS.
+ --title-bar-drag-area-height: 0px; // Needs to have a unit to work with `calc()`.
+ &.os-macos:not(.full-screen) {
+ --title-bar-drag-area-height: 28px;
+ }
}
body.light-theme {
diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss
index 9a7031aee..6f6d8b178 100644
--- a/stylesheets/_modules.scss
+++ b/stylesheets/_modules.scss
@@ -3,6 +3,16 @@
// Using BEM syntax explained here: https://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/
+.module-title-bar-drag-area {
+ -webkit-app-region: drag;
+ height: var(--title-bar-drag-area-height);
+ left: 0;
+ position: fixed;
+ top: 0;
+ width: 100%;
+ z-index: 999;
+}
+
.module-splash-screen {
display: flex;
flex-direction: column;
@@ -2839,11 +2849,12 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
.module-conversation-header {
padding-left: 16px;
padding-right: 16px;
+ padding-top: var(--title-bar-drag-area-height);
display: flex;
flex-direction: row;
align-items: center;
- height: $header-height;
+ height: calc(#{$header-height} + var(--title-bar-drag-area-height));
@include light-theme {
color: $color-gray-90;
@@ -4788,11 +4799,12 @@ button.module-conversation-details__action-button {
// Module: Main Header
.module-main-header {
- height: $header-height;
+ height: calc(#{$header-height} + var(--title-bar-drag-area-height));
width: 100%;
padding-left: 16px;
padding-right: 16px;
+ padding-top: var(--title-bar-drag-area-height);
display: flex;
flex-direction: row;
@@ -6470,7 +6482,7 @@ button.module-image__border-overlay:focus {
color: #ffffff;
font-style: normal;
padding-bottom: 24px;
- padding-top: 24px;
+ padding-top: calc(24px + var(--title-bar-drag-area-height));
text-align: center;
text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
width: 100%;
@@ -7190,7 +7202,7 @@ button.module-image__border-overlay:focus {
display: flex;
justify-content: flex-end;
position: absolute;
- top: 24px;
+ top: calc(24px + var(--title-bar-drag-area-height));
width: 100%;
&__button {
@@ -7537,12 +7549,13 @@ button.module-image__border-overlay:focus {
}
.module-left-pane__archive-header {
- height: $header-height;
+ height: calc(#{$header-height} + var(--title-bar-drag-area-height));
width: 100%;
display: inline-flex;
flex-direction: row;
align-items: center;
+ padding-top: var(--title-bar-drag-area-height);
}
.module-left-pane__header-row {
diff --git a/test/index.html b/test/index.html
index f2b3930ea..a95268288 100644
--- a/test/index.html
+++ b/test/index.html
@@ -14,6 +14,8 @@