fix(workbench): resize handles for the frameless Windows window (#414)

The frameless Windows window has no native resize borders, so it couldn't be resized from its edges. Invisible hit areas along the edges and corners start a native resize drag (minimum size and snapping handled by the system); they're omitted while the window is maximized or full screen. Not verified on a real Windows machine.

Refs #410

Co-authored-by: a-rookie-of-C-language <[email protected]>
Цей коміт міститься в:
arookieofcтаa-rookie-of-C-language authored and GitHub committed 2026-10-30 19:27:16 +08:00
1 джерело 19b96b60a4
коміт ce981e4fd6
3 files changed
+145

No files matched your search

+68
Переглянути файл
@@ -0,0 +1,68 @@
/* Invisible resize hit areas hugging the frameless window's edges. */
.n,
.s,
.e,
.w,
.ne,
.nw,
.se,
.sw {
position: fixed;
z-index: 9999;
pointer-events: auto;
}
.n,
.s {
left: 8px;
right: 8px;
height: 6px;
}
.e,
.w {
top: 8px;
bottom: 8px;
width: 6px;
}
.ne,
.nw,
.se,
.sw {
width: 12px;
height: 12px;
}
.n {
top: 0;
cursor: ns-resize;
}
.s {
bottom: 0;
cursor: ns-resize;
}
.e {
right: 0;
cursor: ew-resize;
}
.w {
left: 0;
cursor: ew-resize;
}
.ne {
top: 0;
right: 0;
cursor: nesw-resize;
}
.nw {
top: 0;
left: 0;
cursor: nwse-resize;
}
.se {
bottom: 0;
right: 0;
cursor: nwse-resize;
}
.sw {
bottom: 0;
left: 0;
cursor: nesw-resize;
}
+75
Переглянути файл
@@ -0,0 +1,75 @@
/**
* Frameless-window resize handles: Windows gives undecorated windows no
* resize borders, so the title bar's frameless look leaves the window
* stuck at its launch size. Eight invisible hit areas along the edges
* start a native resize drag, like any other desktop app.
*
* macOS renders its own overlay title bar but still provides resize
* handles, so nothing is added there.
*/
import { useEffect, useState } from "react";
import { usePlatform } from "@/platform/platformInfo";
import { isTauri } from "@/platform/ipc";
import styles from "./WindowResizeHandles.module.css";
type Dir = "East" | "North" | "NorthEast" | "NorthWest" | "South" | "SouthEast" | "SouthWest" | "West";
const EDGES: { dir: Dir; cls: string }[] = [
{ dir: "North", cls: styles.n! },
{ dir: "South", cls: styles.s! },
{ dir: "East", cls: styles.e! },
{ dir: "West", cls: styles.w! },
{ dir: "NorthEast", cls: styles.ne! },
{ dir: "NorthWest", cls: styles.nw! },
{ dir: "SouthEast", cls: styles.se! },
{ dir: "SouthWest", cls: styles.sw! },
];
async function start(dir: Dir) {
const { getCurrentWindow } = await import("@tauri-apps/api/window");
await getCurrentWindow().startResizeDragging(dir);
}
export function WindowResizeHandles() {
// A frameless window needs them; a decorated one already has native borders.
const frameless = usePlatform((s) => s.framelessTitleBar);
const enabled = isTauri() && frameless;
// A maximized or full-screen window has nothing to resize; the edges belong to the content.
const [sized, setSized] = useState(false);
useEffect(() => {
if (!enabled) return;
let disposed = false;
let unlisten: (() => void) | undefined;
void import("@tauri-apps/api/window").then(async ({ getCurrentWindow }) => {
const w = getCurrentWindow();
const sync = () =>
void Promise.all([w.isMaximized(), w.isFullscreen()]).then(([m, f]) => !disposed && setSized(m || f));
sync();
const off = await w.onResized(sync);
if (disposed) off();
else unlisten = off;
});
return () => {
disposed = true;
unlisten?.();
};
}, [enabled]);
if (!enabled || sized) return null;
return (
<>
{EDGES.map(({ dir, cls }) => (
<div
key={dir}
className={cls}
aria-hidden="true"
onMouseDown={(e) => {
// Left button only, and not on something interactive that bubbled up.
if (e.button !== 0) return;
e.preventDefault();
void start(dir);
}}
/>
))}
</>
);
}
+2
Переглянути файл
@@ -15,6 +15,7 @@ import { TitleBar } from "./TitleBar";
import { useApplyTheme } from "./theme";
import { ToolWindowLayout } from "./toolwindows/ToolWindows";
import { Welcome } from "./Welcome";
import { WindowResizeHandles } from "./WindowResizeHandles";
import styles from "./Workbench.module.css";
/** LightEdit: editor tabs only; no tool windows, toolbar widgets or project status items. */
@@ -60,6 +61,7 @@ export function Workbench() {
<Welcome />
)}
<OverlayHost />
<WindowResizeHandles />
</div>
);
}