fix(client): keep fullscreen sidebar strips draggable via fine-grained dockkit subtraction
Drop the fullscreen panel's whole-box no-drag: it swallowed the strips' blank-run window drag and made no-drag the steady state, turning the open/close app-region nudge into a computed-value no-op (electron#32341) exactly when a hidden panel opens straight into fullscreen. The panel now declares no steady-state app-region in either presentation, and ui-dockkit subtracts the one box base.css never matches — the plain-div split divider — alongside tabs, strip-end chrome, and pane bodies. New specs pin both contracts against the stylesheets on disk.
This commit is contained in:
1 parent
c6c1df6869
commit
9e95a7b183
4 files changed
+100
-19
No files matched your search
@@ -223,15 +223,18 @@
|
||||
|
||||
/* macOS desktop: an embedder may pin this surface under the window's top drag
|
||||
band (ui-layout .leadingBand — full-window geometry that Electron composes
|
||||
in DOM order, not by stacking). The chip run, the strip-end chrome, and the
|
||||
pane body subtract their whole boxes so tabs, controls, and content stay
|
||||
clickable even where they overlap the band; the strip's blank fill and top
|
||||
inset keep the band's drag, so the row moves the window like a native tab
|
||||
bar. Hidden surfaces contribute nothing: Blink skips non-visible boxes when
|
||||
it collects draggable regions. */
|
||||
in DOM order, not by stacking). The chip run, the strip-end chrome, the
|
||||
pane body, and the split divider subtract their whole boxes so tabs,
|
||||
controls, content, and the resize grab stay usable even where they overlap
|
||||
the band; the strip's blank fill and top inset keep the band's drag, so the
|
||||
row moves the window like a native tab bar. The divider must be named here:
|
||||
it is a plain div, so ui-web base.css's interactive-element subtraction
|
||||
never matches it. Hidden surfaces contribute nothing: Blink skips
|
||||
non-visible boxes when it collects draggable regions. */
|
||||
:global(html[data-platform='darwin']) .stripTabs,
|
||||
:global(html[data-platform='darwin']) .stripChrome,
|
||||
:global(html[data-platform='darwin']) .paneBody {
|
||||
:global(html[data-platform='darwin']) .paneBody,
|
||||
:global(html[data-platform='darwin']) .divider {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* macOS drag-band subtraction, asserted against dockkit.module.css on disk:
|
||||
* an embedder may pin the surface under the window's top drag band, and
|
||||
* Electron composes app-regions from geometry in DOM order — every box that
|
||||
* must stay usable subtracts itself. The divider must be listed explicitly:
|
||||
* it is a plain div, so ui-web base.css's interactive-element subtraction
|
||||
* never matches it, and an unsubtracted vertical divider's top run would
|
||||
* drag the window instead of resizing the split.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const css = readFileSync(resolve(import.meta.dirname, '../src/components/dockkit.module.css'), 'utf8')
|
||||
|
||||
describe('macOS app-region subtraction', () => {
|
||||
it('subtracts the chip run, strip-end chrome, pane body, and divider; never declares drag', () => {
|
||||
const rules = [...css.matchAll(/(?<sel>[^{}]+)\{(?<body>[^{}]*)\}/g)]
|
||||
.map(match => [match.groups!['sel']!.trim(), match.groups!['body']!] as const)
|
||||
.filter(([, body]) => body.includes('-webkit-app-region'))
|
||||
expect(rules).toHaveLength(1)
|
||||
const [selector, body] = rules[0]!
|
||||
expect(/-webkit-app-region:\s*([^;]+);/.exec(body)?.[1]?.trim()).toBe('no-drag')
|
||||
for (const part of ['.stripTabs', '.stripChrome', '.paneBody', '.divider']) {
|
||||
expect(selector, part).toContain(`:global(html[data-platform='darwin']) ${part}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -49,9 +49,12 @@
|
||||
/* One-frame app-region pulse at each open/close edge (RightbarSeat): changing
|
||||
this computed value is what makes Electron recollect the window's drag
|
||||
rects, which the slide's transform/visibility flips alone never dirty
|
||||
(electron#32341). While set, the whole panel is out of the drag band for
|
||||
that frame, which is also the desired end state everywhere but the strip's
|
||||
blank fill. */
|
||||
(electron#32341). The panel's steady state sets no app-region in either
|
||||
presentation, so this write is a real change at both edges — including a
|
||||
hidden panel opening straight into fullscreen (a narrow window's first
|
||||
open), whose rects were skipped while hidden. While set, the whole panel is
|
||||
out of the drag band for that frame; the removal frame recollects the
|
||||
steady-state geometry below. */
|
||||
:global(html[data-platform='darwin']) .panel[data-sidebar-right-region-nudge] {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
@@ -63,15 +66,16 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Electron drag regions are window geometry, not stacking: the frame's leading
|
||||
band and the sidebar logo row's drag rects underneath keep taking the pointer
|
||||
through this fixed overlay, turning non-interactive panel content (file
|
||||
preview text, images, webviews) into window drag. The panel appears after
|
||||
both in DOM order, so this subtracts its whole box; the strips' blank runs
|
||||
give up the drag affordance in exchange. */
|
||||
:global(html[data-platform='darwin']) .panel[data-sidebar-right-panel='fullscreen'] {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
/* No whole-box subtraction here: Electron drag regions are window geometry,
|
||||
not stacking, so the frame's leading band does keep acting through this
|
||||
fixed overlay — but the kit already subtracts every box that must stay
|
||||
usable (tabs, strip-end chrome, pane bodies, dividers; ui-dockkit), and
|
||||
ui-web base.css subtracts interactive elements and portalled overlays.
|
||||
What remains of the band under the strips' blank runs is the window drag
|
||||
affordance, the same native-tab-bar reading as the push presentation. A
|
||||
panel-wide no-drag would trade it away and, worse, make no-drag the
|
||||
steady state, turning the open/close pulse above into a computed-value
|
||||
no-op exactly when a hidden panel opens straight into fullscreen. */
|
||||
|
||||
/* Windows keeps caption controls above fullscreen file and terminal panels. */
|
||||
:global([data-windows-titlebar]) .panel[data-sidebar-right-panel='fullscreen'] {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* App-region nudge pulse, asserted against SidebarRight.module.css on disk:
|
||||
* Electron recollects the window's drag rects only when a style pass changes
|
||||
* a computed app-region value (electron#32341), so the panel's steady state
|
||||
* must declare NO app-region in either presentation — the open/close no-drag
|
||||
* pulse is then a real computed-value change at both edges, including a
|
||||
* hidden panel opening straight into fullscreen (a narrow window's first
|
||||
* open, whose rects were skipped while hidden). A steady-state value on the
|
||||
* panel (the old fullscreen whole-box no-drag) makes the pulse a silent
|
||||
* no-op and leaves the frame's drag band swallowing the strip's top-right
|
||||
* controls; fine-grained subtraction belongs to ui-dockkit (tabs, strip-end
|
||||
* chrome, pane bodies, dividers), leaving the strips' blank runs draggable.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const css = readFileSync(new URL('../src/client/shell/SidebarRight.module.css', import.meta.url), 'utf8')
|
||||
|
||||
/**
|
||||
* Every `<selector> -> value` pair of rules declaring `-webkit-app-region`.
|
||||
* @returns one entry per declaring rule, in source order.
|
||||
*/
|
||||
function appRegionRules(): [selector: string, value: string][] {
|
||||
const rules: [string, string][] = []
|
||||
for (const match of css.matchAll(/(?<sel>[^{}]+)\{(?<body>[^{}]*)\}/g)) {
|
||||
const value = /-webkit-app-region:\s*([^;]+);/.exec(match.groups!['body']!)?.[1]
|
||||
if (value !== undefined) rules.push([match.groups!['sel']!.trim(), value.trim()])
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
describe('app-region nudge pulse styles', () => {
|
||||
it('pulses the panel to no-drag', () => {
|
||||
const pulses = appRegionRules()
|
||||
.filter(([selector]) => selector.includes('[data-sidebar-right-region-nudge]'))
|
||||
expect(pulses).toHaveLength(1)
|
||||
expect(pulses[0]![0]).toMatch(/\.panel\[data-sidebar-right-region-nudge\]\s*$/)
|
||||
expect(pulses[0]![1]).toBe('no-drag')
|
||||
})
|
||||
|
||||
it('declares no steady-state app-region, so the pulse is a real change', () => {
|
||||
const steady = appRegionRules()
|
||||
.filter(([selector]) => !selector.includes('[data-sidebar-right-region-nudge]'))
|
||||
expect(steady).toEqual([])
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user