fix(desktop): restore double-click-to-maximize on the custom title bar

startDragging() on mousedown enters the OS drag loop and swallows the browser's
dblclick, so double-click-to-maximize stopped working. Begin the drag on the
first real pointer move instead, and detect the double-click from the interval
between mousedowns (both handled in the top-strip listener).
This commit is contained in:
Connor Yoh
2026-09-02 18:14:24 +01:00
parent 178a4649fa
commit 9f1efedaf6
@@ -81,13 +81,19 @@ export function WindowTitleBar() {
return () => unlisten?.();
}, [active]);
// Let the window be dragged (and double-click-maximized) from any
// non-interactive spot in the top strip. data-tauri-drag-region only fires
// when the bare container is the click target, which leaves most of a busy
// toolbar undraggable; a document-level hit test covers the whole top.
// Let the window be dragged, and double-click-maximized, from any
// non-interactive spot in the top strip. data-tauri-drag-region only fires on
// bare container backgrounds, leaving most of a busy toolbar undraggable, so a
// document-level hit test covers the whole top instead.
//
// startDragging() must NOT run on mousedown: it enters the OS drag loop and
// swallows the browser's dblclick. So begin the drag on the first real move,
// and detect a double-click from the interval between mousedowns.
useEffect(() => {
if (!active) return;
const TOP_STRIP_PX = 48;
const DOUBLE_CLICK_MS = 500;
const DRAG_THRESHOLD_PX = 4;
const INTERACTIVE =
"button, a[href], input, textarea, select, label, summary," +
'[role="button"], [role="tab"], [role="menuitem"], [role="switch"],' +
@@ -97,17 +103,44 @@ export function WindowTitleBar() {
const el = e.target as Element | null;
return !!el && !el.closest(INTERACTIVE);
};
let pending: { x: number; y: number } | null = null;
let lastDownAt = 0;
const onMouseDown = (e: MouseEvent) => {
if (draggableAt(e)) void getCurrentWindow().startDragging();
if (!draggableAt(e)) {
pending = null;
return;
}
const now = Date.now();
if (now - lastDownAt < DOUBLE_CLICK_MS) {
pending = null;
lastDownAt = 0;
void getCurrentWindow().toggleMaximize();
return;
}
lastDownAt = now;
pending = { x: e.clientX, y: e.clientY };
};
const onDoubleClick = (e: MouseEvent) => {
if (draggableAt(e)) void getCurrentWindow().toggleMaximize();
const onMouseMove = (e: MouseEvent) => {
if (!pending) return;
if (
Math.abs(e.clientX - pending.x) > DRAG_THRESHOLD_PX ||
Math.abs(e.clientY - pending.y) > DRAG_THRESHOLD_PX
) {
pending = null;
lastDownAt = 0; // a drag is not the first half of a double-click
void getCurrentWindow().startDragging();
}
};
const onMouseUp = () => {
pending = null;
};
document.addEventListener("mousedown", onMouseDown);
document.addEventListener("dblclick", onDoubleClick);
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
return () => {
document.removeEventListener("mousedown", onMouseDown);
document.removeEventListener("dblclick", onDoubleClick);
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
};
}, [active]);