macOS: make conversation and main header draggable
This commit is contained in:
parent
ecc04d36de
commit
729d808f62
7 changed files with 119 additions and 11 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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%;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -171,6 +172,7 @@
|
|||
&__button {
|
||||
$icon-size: 32px;
|
||||
|
||||
-webkit-app-region: no-drag;
|
||||
@include button-reset;
|
||||
align-items: stretch;
|
||||
border-radius: 4px;
|
||||
|
|
|
@ -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> {
|
||||
|
@ -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(
|
||||
|
|
|
@ -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}
|
||||
|
|
74
ts/test-electron/util/isWindowDragElement_test.ts
Normal file
74
ts/test-electron/util/isWindowDragElement_test.ts
Normal file
|
@ -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
ts/util/isWindowDragElement.ts
Normal file
21
ts/util/isWindowDragElement.ts
Normal file
|
@ -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;
|
||||
}
|
Loading…
Add table
Reference in a new issue