Hide title bar on macOS

This commit is contained in:
Evan Hahn 2021-02-01 14:01:25 -06:00 committed by GitHub
parent b672d33f25
commit ddebbf8121
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 297 additions and 24 deletions

View File

@ -32,6 +32,8 @@
-->
<script type='text/x-tmpl-mustache' id='app-loading-screen'>
<div class='module-title-bar-drag-area'></div>
<div class='content'>
<div class="module-splash-screen__logo module-img--150"></div>
<div class='container'>
@ -44,6 +46,8 @@
</script>
<script type='text/x-tmpl-mustache' id='conversation-loading-screen'>
<div class='module-title-bar-drag-area'></div>
<div class='content'>
<div class="module-splash-screen__logo module-img--128"></div>
<div class='container'>
@ -55,6 +59,8 @@
</script>
<script type='text/x-tmpl-mustache' id='two-column'>
<div class='module-title-bar-drag-area'></div>
<div class='call-manager-placeholder'></div>
<div class='inbox-container'>
<div class='gutter'>
@ -317,6 +323,8 @@
</script>
<script type='text/javascript' src='js/components.js'></script>
<script type='text/javascript' src='ts/set_os_class.js'></script>
<script type='text/javascript' src='ts/manage_full_screen_class.js'></script>
<script type='text/javascript' src='ts/backboneJquery.js'></script>
<script type='text/javascript' src='js/reliable_trigger.js'></script>
<script type='text/javascript' src='js/database.js'></script>
@ -365,6 +373,8 @@
</head>
<body class="overflow-hidden">
<div class='app-loading-screen'>
<div class='module-title-bar-drag-area'></div>
<div class='content'>
<div class="module-splash-screen__logo module-img--150"></div>
<div class='container'>

74
main.js
View File

@ -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);

View File

@ -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');

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -14,6 +14,8 @@
<div id="tests"></div>
<script type='text/x-tmpl-mustache' id='app-loading-screen'>
<div class='module-title-bar-drag-area'></div>
<div class='content'>
<div class="module-splash-screen__logo module-img--150"></div>
<div class='container'>
@ -26,6 +28,8 @@
</script>
<script type='text/x-tmpl-mustache' id='conversation-loading-screen'>
<div class='module-title-bar-drag-area'></div>
<div class='content'>
<div class="module-splash-screen__logo module-img--128"></div>
<div class='container'>
@ -37,6 +41,8 @@
</script>
<script type='text/x-tmpl-mustache' id='two-column'>
<div class='module-title-bar-drag-area'></div>
<div class='call-manager-placeholder'></div>
<div class='inbox-container'>
<div class='gutter'>

View File

@ -70,6 +70,17 @@ type WhatIsThis = import('./window.d').WhatIsThis;
},
});
window.addEventListener('dblclick', (event: Event) => {
const target = event.target as HTMLElement;
const isDoubleClickOnTitleBar = Boolean(
target.classList.contains('module-title-bar-drag-area') ||
target.closest('module-title-bar-drag-area')
);
if (isDoubleClickOnTitleBar) {
window.titleBarDoubleClick();
}
});
// Globally disable drag and drop
document.body.addEventListener(
'dragover',

View File

@ -0,0 +1,10 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
$(document).ready(() => {
const updateFullScreenClass = (isFullScreen: boolean) => {
$(document.body).toggleClass('full-screen', isFullScreen);
};
updateFullScreenClass(window.isFullScreen());
window.onFullScreenChange = updateFullScreenClass;
});

17
ts/set_os_class.ts Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
$(document).ready(() => {
let className: string;
if (window.Signal.OS.isWindows()) {
className = 'os-windows';
} else if (window.Signal.OS.isMacOS()) {
className = 'os-macos';
} else if (window.Signal.OS.isLinux()) {
className = 'os-linux';
} else {
throw new Error('Unexpected operating system; not applying ');
}
$(document.body).addClass(className);
});

View File

@ -0,0 +1,37 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as sinon from 'sinon';
import { BrowserWindow } from 'electron';
import { toggleMaximizedBrowserWindow } from '../../util/toggleMaximizedBrowserWindow';
describe('toggleMaximizedBrowserWindow', () => {
const createFakeWindow = () => ({
isMaximized: sinon.stub(),
unmaximize: sinon.spy(),
maximize: sinon.spy(),
});
it('maximizes an unmaximized window', () => {
const browserWindow = createFakeWindow();
browserWindow.isMaximized.returns(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toggleMaximizedBrowserWindow((browserWindow as any) as BrowserWindow);
sinon.assert.calledOnce(browserWindow.maximize);
sinon.assert.notCalled(browserWindow.unmaximize);
});
it('unmaximizes a maximized window', () => {
const browserWindow = createFakeWindow();
browserWindow.isMaximized.returns(true);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toggleMaximizedBrowserWindow((browserWindow as any) as BrowserWindow);
sinon.assert.notCalled(browserWindow.maximize);
sinon.assert.calledOnce(browserWindow.unmaximize);
});
});

View File

@ -1,4 +1,4 @@
// Copyright 2018-2020 Signal Messenger, LLC
// Copyright 2018-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as OS from '../OS';
@ -34,3 +34,12 @@ export const isHideMenuBarSupported = (): boolean => !OS.isMacOS();
// the "draw attention on notification" option is specific to Windows and Linux
export const isDrawAttentionSupported = (): boolean => !OS.isMacOS();
export enum TitleBarVisibility {
Visible,
Hidden,
}
// This should match the "logic" in `stylesheets/_global.scss`.
export const getTitleBarVisibility = (): TitleBarVisibility =>
OS.isMacOS() ? TitleBarVisibility.Hidden : TitleBarVisibility.Visible;

View File

@ -15033,6 +15033,42 @@
"reasonCategory": "falseMatch",
"updated": "2020-12-17T18:08:07.752Z"
},
{
"rule": "jQuery-$(",
"path": "ts/manage_full_screen_class.js",
"line": "$(document).ready(() => {",
"lineNumber": 4,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Doesn't manipulate the DOM."
},
{
"rule": "jQuery-$(",
"path": "ts/manage_full_screen_class.js",
"line": " $(document.body).toggleClass('full-screen', isFullScreen);",
"lineNumber": 6,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Manipulates a trusted class."
},
{
"rule": "jQuery-$(",
"path": "ts/manage_full_screen_class.ts",
"line": "$(document).ready(() => {",
"lineNumber": 4,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Doesn't manipulate the DOM."
},
{
"rule": "jQuery-$(",
"path": "ts/manage_full_screen_class.ts",
"line": " $(document.body).toggleClass('full-screen', isFullScreen);",
"lineNumber": 6,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Manipulates a trusted class."
},
{
"rule": "React-createRef",
"path": "ts/quill/mentions/completion.js",
@ -15059,6 +15095,42 @@
"updated": "2020-11-06T17:43:07.381Z",
"reasonDetail": "used for figuring out clipboard contents"
},
{
"rule": "jQuery-$(",
"path": "ts/set_os_class.js",
"line": "$(document).ready(() => {",
"lineNumber": 4,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Doesn't manipulate the DOM."
},
{
"rule": "jQuery-$(",
"path": "ts/set_os_class.js",
"line": " $(document.body).addClass(className);",
"lineNumber": 18,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Adds a trusted CSS class."
},
{
"rule": "jQuery-$(",
"path": "ts/set_os_class.ts",
"line": "$(document).ready(() => {",
"lineNumber": 4,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Doesn't manipulate the DOM."
},
{
"rule": "jQuery-$(",
"path": "ts/set_os_class.ts",
"line": " $(document.body).addClass(className);",
"lineNumber": 16,
"reasonCategory": "usageTrusted",
"updated": "2021-01-21T23:06:13.270Z",
"reasonDetail": "Adds a trusted CSS class."
},
{
"rule": "jQuery-wrap(",
"path": "ts/shims/textsecure.js",

View File

@ -0,0 +1,14 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { BrowserWindow } from 'electron';
export function toggleMaximizedBrowserWindow(
browserWindow: BrowserWindow
): void {
if (browserWindow.isMaximized()) {
browserWindow.unmaximize();
} else {
browserWindow.maximize();
}
}

3
ts/window.d.ts vendored
View File

@ -162,6 +162,7 @@ declare global {
isActive: () => boolean;
isAfterVersion: (version: WhatIsThis, anotherVersion: string) => boolean;
isBeforeVersion: (version: WhatIsThis, anotherVersion: string) => boolean;
isFullScreen: () => boolean;
isValidGuid: (maybeGuid: string | null) => boolean;
isValidE164: (maybeE164: unknown) => boolean;
libphonenumber: {
@ -189,6 +190,7 @@ declare global {
};
nodeSetImmediate: typeof setImmediate;
normalizeUuids: (obj: any, paths: Array<string>, context: string) => void;
onFullScreenChange: (fullScreen: boolean) => void;
owsDesktopApp: WhatIsThis;
platform: string;
preloadedImages: Array<WhatIsThis>;
@ -230,6 +232,7 @@ declare global {
};
systemTheme: WhatIsThis;
textsecure: TextSecureType;
titleBarDoubleClick: () => void;
unregisterForActive: (handler: () => void) => void;
updateTrayIcon: (count: number) => void;