Skip to content

Commit

Permalink
macOS: make conversation and main header draggable
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanHahn-Signal authored and josh-signal committed Mar 19, 2021
1 parent ecc04d3 commit 729d808
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 11 deletions.
2 changes: 2 additions & 0 deletions stylesheets/_global.scss
Expand Up @@ -21,8 +21,10 @@ body {
// 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()`.
--draggable-app-region: initial;
&.os-macos:not(.full-screen) {
--title-bar-drag-area-height: 28px;
--draggable-app-region: drag;
}
}

Expand Down
8 changes: 8 additions & 0 deletions stylesheets/_modules.scss
Expand Up @@ -4217,6 +4217,8 @@ button.module-conversation-details__action-button {
// Module: Main Header

.module-main-header {
-webkit-app-region: var(--draggable-app-region);

height: calc(#{$header-height} + var(--title-bar-drag-area-height));
width: 100%;

Expand All @@ -4236,11 +4238,16 @@ button.module-conversation-details__action-button {
}
}

&__avatar {
-webkit-app-region: no-drag;
}

&__search {
flex-grow: 1;
position: relative;
display: flex;
flex-direction: row;
-webkit-app-region: no-drag;

&__input {
flex-grow: 1;
Expand Down Expand Up @@ -4388,6 +4395,7 @@ button.module-conversation-details__action-button {

width: 24px;
height: 24px;
-webkit-app-region: no-drag;

@include light-theme {
@include color-svg($icon, $color-gray-90);
Expand Down
4 changes: 3 additions & 1 deletion stylesheets/components/ConversationHeader.scss
Expand Up @@ -8,11 +8,11 @@
--button-spacing: 16px;
}

-webkit-app-region: var(--draggable-app-region);
padding-top: var(--title-bar-drag-area-height);
display: flex;
flex-direction: row;
align-items: center;

height: calc(#{$header-height} + var(--title-bar-drag-area-height));

@include light-theme {
Expand Down Expand Up @@ -78,6 +78,7 @@
&--clickable {
@include button-reset;
border-radius: 4px;
-webkit-app-region: no-drag;

// These are clobbered by button-reset:
margin-left: 4px;
Expand Down Expand Up @@ -171,6 +172,7 @@
&__button {
$icon-size: 32px;

-webkit-app-region: no-drag;
@include button-reset;
align-items: stretch;
border-radius: 4px;
Expand Down
20 changes: 10 additions & 10 deletions ts/background.ts
Expand Up @@ -4,6 +4,8 @@
import { DataMessageClass } from './textsecure.d';
import { MessageAttributesType } from './model-types.d';
import { WhatIsThis } from './window.d';
import { getTitleBarVisibility, TitleBarVisibility } from './types/Settings';
import { isWindowDragElement } from './util/isWindowDragElement';
import { assert } from './util/assert';

export async function startApp(): Promise<void> {
Expand Down Expand Up @@ -78,16 +80,14 @@ export async function startApp(): Promise<void> {
},
});

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();
}
});
if (getTitleBarVisibility() === TitleBarVisibility.Hidden) {
window.addEventListener('dblclick', (event: Event) => {
const target = event.target as HTMLElement;
if (isWindowDragElement(target)) {
window.titleBarDoubleClick();
}
});
}

// Globally disable drag and drop
document.body.addEventListener(
Expand Down
1 change: 1 addition & 0 deletions ts/components/MainHeader.tsx
Expand Up @@ -367,6 +367,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
{({ ref }) => (
<Avatar
avatarPath={avatarPath}
className="module-main-header__avatar"
color={color}
conversationType="direct"
i18n={i18n}
Expand Down
74 changes: 74 additions & 0 deletions ts/test-electron/util/isWindowDragElement_test.ts
@@ -0,0 +1,74 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import { assert } from 'chai';

import { isWindowDragElement } from '../../util/isWindowDragElement';

describe('isWindowDragElement', () => {
const crel = (tagName: string, appRegion?: string): Element => {
const result = document.createElement(tagName);
if (appRegion) {
result.style.cssText = `-webkit-app-region: ${appRegion}`;
}
return result;
};

let sandboxEl: HTMLElement;

beforeEach(() => {
sandboxEl = document.createElement('div');
document.body.appendChild(sandboxEl);
});

afterEach(() => {
sandboxEl.remove();
});

it('returns false for elements with no -webkit-app-region property in the heirarchy', () => {
const root = crel('div');
const outer = crel('span');
const inner = crel('div');
root.appendChild(outer);
outer.appendChild(inner);
sandboxEl.appendChild(root);

assert.isFalse(isWindowDragElement(root));
assert.isFalse(isWindowDragElement(outer));
assert.isFalse(isWindowDragElement(inner));
});

it('returns false for elements with -webkit-app-region: drag on a sub-element', () => {
const parent = crel('div');
const child = crel('div', 'drag');
parent.appendChild(child);
sandboxEl.appendChild(parent);

assert.isFalse(isWindowDragElement(parent));
});

it('returns false if any element up the chain is found to be -webkit-app-region: no-drag', () => {
const root = crel('div', 'drag');
const outer = crel('div', 'no-drag');
const inner = crel('div');
root.appendChild(outer);
outer.appendChild(inner);
sandboxEl.appendChild(root);

assert.isFalse(isWindowDragElement(outer));
assert.isFalse(isWindowDragElement(inner));
});

it('returns true if any element up the chain is found to be -webkit-app-region: drag', () => {
const root = crel('div', 'drag');
const outer = crel('div');
const inner = crel('div');
root.appendChild(outer);
outer.appendChild(inner);
sandboxEl.appendChild(root);

assert.isTrue(isWindowDragElement(root));
assert.isTrue(isWindowDragElement(outer));
assert.isTrue(isWindowDragElement(inner));
});
});
21 changes: 21 additions & 0 deletions ts/util/isWindowDragElement.ts
@@ -0,0 +1,21 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

export function isWindowDragElement(el: Readonly<Element>): boolean {
let currentEl: Element | null = el;
do {
const appRegion = getComputedStyle(currentEl).getPropertyValue(
'-webkit-app-region'
);
switch (appRegion) {
case 'no-drag':
return false;
case 'drag':
return true;
default:
currentEl = currentEl.parentElement;
break;
}
} while (currentEl);
return false;
}

0 comments on commit 729d808

Please sign in to comment.