Skip to content

Commit

Permalink
fix borderless drag issues and add debug overlay to better test issue…
Browse files Browse the repository at this point in the history
…s about draging area (#4300)

* use only two classes, add exp.showDragAreas dev api to make drag areas visible.

* make dialogs no-drag and dialog headers drag

* make background image on "no account selected"- and welcome screen a drag area

* make context menu not dragable
  • Loading branch information
Simon-Laux authored Nov 28, 2024
1 parent d1fbc7a commit 5e07d5e
Show file tree
Hide file tree
Showing 20 changed files with 148 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- fix missing linebreaks in quotes #4360
- avoid showing wrong menu items for blocked users #4353
- fix: save message draft every 200ms if message text changed #3733
- fix mac drag window issues #4300

<a id="1_48_0"></a>

Expand Down
3 changes: 1 addition & 2 deletions packages/frontend/scss/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ $z-index: (
account-hover-info: 200,

// toast for errors and success messages
absolute-positioning-helper: 1000,
context-menu-layer: 9999
absolute-positioning-helper: 1000
);

// setting z-index to 0 creates a new scope for children without changing the ordering of the parent element
Expand Down
26 changes: 21 additions & 5 deletions packages/frontend/scss/main_screen/_navbar_wrapper.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
.navbar-wrapper {
// so it can be used to drag the window around
-webkit-app-region: drag;

position: relative;

.navbar-heading__button {
Expand Down Expand Up @@ -54,8 +51,27 @@
}
}

.drag {
// so it can be used to drag the window around
-webkit-app-region: drag;
}

.no-drag,
#hamburger-menu-button,
#chat-list-search {
input,
button {
-webkit-app-region: no-drag;
}

#drag-area-visualisation {
background-color: transparent;
opacity: 0.5;
pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
border: 0;
}
2 changes: 1 addition & 1 deletion packages/frontend/scss/misc/_context_menu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
max-height: unset;
max-width: unset;
// position: absolute;
// z-index: map-get($z-index, context-menu-layer);
background-color: transparent;
padding: 0;
margin: 0;
Expand All @@ -33,6 +32,7 @@
pointer-events: auto;
background-color: var(--bgPrimary);
color: var(--textPrimary);
-webkit-app-region: no-drag;

border-radius: 3px;
overflow: hidden;
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const Dialog = React.memo<Props>(
onClose={onClose}
onCancel={onCancel}
ref={dialog}
className={classNames(styles.dialog, props.className, {
className={classNames(styles.dialog, props.className, 'no-drag', {
[styles.unstyled]: unstyled,
})}
style={style}
Expand Down
5 changes: 4 additions & 1 deletion packages/frontend/src/components/Dialog/HeaderButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export default function HeaderButton({
...props
}: Props) {
return (
<button className={classNames(styles.headerButton, className)} {...props}>
<button
className={classNames(styles.headerButton, 'no-drag', className)}
{...props}
>
<Icon
className={styles.headerButtonIcon}
icon={icon}
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/src/components/Dialog/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ $paddingVertical: 20px;
justify-content: center;
padding: 0;
width: $headerButtonSize;
-webkit-app-region: no-drag; // make it clickable, even when it is on top of navbar

& + & {
margin-left: 5px;
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/ImageBackdrop/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function ImageBackdrop({
variant = 'welcome',
}: PropsWithChildren<Props>) {
return (
<div className={classNames(styles.imageBackdrop, styles[variant])}>
<div className={classNames(styles.imageBackdrop, styles[variant], 'drag')}>
{children}
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/SearchInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function SearchInput(props: Props) {
autoFocus
onChange={onChange}
value={value}
className={classNames(styles.searchInput)}
className={classNames(styles.searchInput, 'no-drag')}
ref={props.inputRef}
spellCheck={false}
aria-keyshortcuts='Control+K'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ export function SmallScreenModeMacOSTitleBar() {
height: '26px',
lineHeight: '26px',
textAlign: 'center',
//@ts-ignore
'-webkit-app-region': 'drag',
flexShrink: 0,
flexGrow: 0,
backgroundColor: '#2c2c2c',
color: hasFocus ? '#d2d2d2' : '#565656',
fontWeight: 'bold',
}}
className='drag'
>
{appWindowTitle}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { VerifiedContactsRequiredDialog } from '../ProtectionStatusDialog'
import InfiniteLoader from 'react-window-infinite-loader'
import { AddMemberChip } from './AddMemberDialog'
import styles from './styles.module.scss'
import classNames from 'classnames'

export function AddMemberInnerDialog({
onCancel,
Expand Down Expand Up @@ -236,7 +237,7 @@ export function AddMemberInnerDialog({
})}
<input
ref={inputRef}
className={'search-input ' + styles.groupMemberSearch}
className={classNames('search-input', styles.groupMemberSearch)}
onChange={onSearchChange}
onKeyDown={event => {
addContactOnKeyDown(event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ function CreateChatMain(props: CreateChatMainProps) {
<>
<DialogHeader>
<input
className='search-input'
className='search-input no-drag'
onChange={e => setQueryStr(e.target.value)}
value={queryStr}
placeholder={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default function ForwardMessage(props: Props) {
<DialogBody className={styles.forwardMessageDialogBody}>
<div className='forward-message-account-input'>
<input
className='search-input'
className='search-input no-drag'
onChange={onSearchChange}
value={queryStr}
placeholder={tx('search')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default function MailtoDialog(props: Props & DialogProps) {
<div className='mailto-dialog'>
<div className='select-chat-chat-list'>
<input
className='search-input'
className='search-input no-drag'
onChange={onSearchChange}
value={queryStr}
placeholder={tx('search')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default function SelectContactDialog({
<Dialog width={400} onClose={onClose} fixed>
<DialogHeader>
<input
className='search-input'
className='search-input no-drag'
onChange={e => setQueryStr(e.target.value)}
value={queryStr}
placeholder={tx('search')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function WebxdcSaveToChatDialog(props: Props) {
)}
>
<input
className='search-input'
className='search-input no-drag'
onChange={onSearchChange}
value={queryStr}
placeholder={tx('search')}
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/screens/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export default function MainScreen({ accountId }: Props) {
!messageSectionShouldBeHidden ? 'chat-view-open' : ''
}`}
>
<div className='navbar-wrapper'>
<div className='navbar-wrapper drag'>
<Navbar>
{!chatListShouldBeHidden && (
<NavbarGroupLeft>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import React from 'react'
import useTranslationFunction from '../../../hooks/useTranslationFunction'

import styles from './styles.module.scss'
import classNames from 'classnames'

export function NoAccountSelectedScreen() {
const tx = useTranslationFunction()

return (
<div className={styles.noAccountSelectedScreen}>
<div className={classNames(styles.noAccountSelectedScreen, 'drag')}>
<div className={styles.background}>
<div className={styles.infoBox}>{tx('no_account_selected')}</div>
<div className={classNames(styles.infoBox, 'no-drag')}>
{tx('no_account_selected')}
</div>
</div>
</div>
)
Expand Down
93 changes: 93 additions & 0 deletions packages/frontend/src/debug-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,96 @@ export function countCall(label: string) {
export function printCallCounterResult() {
console.table(countCalls)
}

/** Visualize drag/no-drag regions for debuging.
* This is important because on MacOS there is no window title bar,
* so the navbar and other elements need to take that role to make the window dragable.
* But whats draggable is not clickable, so buttons need to be excluded.
*
* See https://github.com/deltachat/deltachat-desktop/issues/4018 to get an idea why this is important to get right.
*/
export class DragRegionOverlay {
debugDragAreaUpdateInterval: ReturnType<typeof setInterval> | null = null
toggle() {
let element: HTMLCanvasElement | undefined = document.getElementById(
'drag-area-visualisation'
) as HTMLCanvasElement
if (!element) {
if (!window.visualViewport) {
throw new Error(
'failed to init DragRegionOverlay: window.visualViewport is undefined, this is unexpected.'
)
}

element = document.createElement('canvas')
element.height = window.visualViewport.height
element.width = window.visualViewport.width
element.id = 'drag-area-visualisation'
element.popover = 'manual'
document.body.append(element)
}

if (this.debugDragAreaUpdateInterval) {
element.hidePopover()
clearInterval(this.debugDragAreaUpdateInterval)
this.debugDragAreaUpdateInterval = null
} else {
element.showPopover()
const drawing = element.getContext('2d')
if (!drawing) {
throw new Error(
'failed to init DragRegionOverlay: canvas context not set'
)
}
drawing.translate(0.5, 0.5)
this.debugDragAreaUpdateInterval = setInterval(() => {
if (!window.visualViewport) {
throw new Error(
'failed to init DragRegionOverlay: window.visualViewport is undefined, this is unexpected.'
)
}
element.height = window.visualViewport.height
element.width = window.visualViewport.width
// hack to make it show on top of dialogs
element.hidePopover()
element.showPopover()

const { height, width } = element.getBoundingClientRect()
drawing.clearRect(0, 0, width, height)

function drawElement(rect: DOMRect | undefined, color: string) {
if (!rect || !drawing) {
return
}
drawing.fillStyle = color
drawing.fillRect(rect.x, rect.y, rect.width, rect.height)
}

function traverseDOMRecursively(element: HTMLElement) {
// element.computedStyleMap
const styles = window.getComputedStyle(element)

//@ts-ignore
const role = styles.webkitAppRegion

if (role !== 'none') {
const boundingRect = element.getClientRects()[0]
if (role === 'no-drag') {
drawElement(boundingRect, 'green')
} else if (role === 'drag') {
drawElement(boundingRect, 'red')
}
}

for (const child of element.children) {
if (child instanceof HTMLElement) {
traverseDOMRecursively(child)
}
}
}

traverseDOMRecursively(document.documentElement)
}, 50)
}
}
}
11 changes: 10 additions & 1 deletion packages/frontend/src/experimental.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getLogger } from '../../shared/logger'
import { BackendRemote } from './backend-com'
import { printCallCounterResult } from './debug-tools'
import { DragRegionOverlay, printCallCounterResult } from './debug-tools'
import { runtime } from '@deltachat-desktop/runtime-interface'
import { selectedAccountId } from './ScreenController'

Expand All @@ -21,6 +21,7 @@ only for debugging:
- printCallCounterResult() // for profiling you can track what is called how often with 'countCall(label: string)'
- .rpc // only available in devmode, gives full access to jsonrpc
- .runtime // only available in devmode, gives full access to runtime
- showDragAreas() // toggle drag region overlay
`)
}
constructor() {
Expand Down Expand Up @@ -77,6 +78,14 @@ only for debugging:
}
return runtime
}

dragRegionOverlay?: DragRegionOverlay
showDragAreas() {
if (!this.dragRegionOverlay) {
this.dragRegionOverlay = new DragRegionOverlay()
}
this.dragRegionOverlay.toggle()
}
}

export const exp = new Experimental()

0 comments on commit 5e07d5e

Please sign in to comment.