responsive desktop: shrink icons on cramped viewports so they never collide; clamp windows to the viewport

This commit is contained in:
Martin Dvořák 2026-09-24 12:48:01 +00:00
commit 194501e467
4 changed files with 136 additions and 9 deletions

View file

@ -149,11 +149,15 @@ export default function WindowsDesktop() {
const id = idRef.current++;
const vw = window.innerWidth;
const vh = window.innerHeight - TASKBAR_HEIGHT;
// Fit the window to the viewport so it never opens off-screen or under the
// panel on a narrow/short monitor.
const w = Math.min(app.width, Math.max(260, vw - 20));
const h = Math.min(app.height, Math.max(150, vh - 20));
const cascade = Math.min(wins.length, 8) * 30;
let x = Math.round((vw - app.width) / 2) + cascade;
let y = Math.round((vh - app.height) / 2) + cascade;
x = Math.max(10, Math.min(x, vw - app.width - 10));
y = Math.max(10, Math.min(y, vh - app.height - 10));
let x = Math.round((vw - w) / 2) + cascade;
let y = Math.round((vh - h) / 2) + cascade;
x = Math.max(10, Math.min(x, vw - w - 10));
y = Math.max(10, Math.min(y, vh - h - 10));
setWins((prev) => [
...prev,
{
@ -161,8 +165,8 @@ export default function WindowsDesktop() {
appId: app.id,
x,
y,
w: app.width,
h: app.height,
w,
h,
z: ++zRef.current,
minimized: false,
maximized: false,
@ -252,11 +256,43 @@ export default function WindowsDesktop() {
);
};
const resizeWin = (id: number, x: number, y: number, w: number, h: number) => {
const vw = typeof window !== "undefined" ? window.innerWidth : 1280;
const vh = (typeof window !== "undefined" ? window.innerHeight : 800) - TASKBAR_HEIGHT;
const nw = Math.min(w, Math.max(260, vw - 20));
const nh = Math.min(h, Math.max(150, vh - 20));
setWins((prev) =>
prev.map((win) => (win.id === id ? { ...win, centered: false, x, y, w, h } : win))
prev.map((win) =>
win.id === id ? { ...win, centered: false, x, y, w: nw, h: nh } : win
)
);
};
// Keep windows on-screen when the viewport shrinks (narrow monitor, or the
// user resizing the browser): clamp size to the viewport and pull anything
// that would fall off the edge back into view.
useEffect(() => {
const clampToViewport = () => {
const vw = window.innerWidth;
const vh = window.innerHeight - TASKBAR_HEIGHT;
setWins((prev) =>
prev.map((w) => {
if (w.maximized) return w;
const nw = Math.min(w.w, Math.max(260, vw - 20));
const nh = Math.min(w.h, Math.max(150, vh - 20));
if (w.centered) {
return nw === w.w && nh === w.h ? w : { ...w, w: nw, h: nh };
}
const nx = Math.max(10, Math.min(w.x, vw - nw - 10));
const ny = Math.max(10, Math.min(w.y, vh - nh - 10));
if (nw === w.w && nh === w.h && nx === w.x && ny === w.y) return w;
return { ...w, x: nx, y: ny, w: nw, h: nh };
})
);
};
window.addEventListener("resize", clampToViewport);
return () => window.removeEventListener("resize", clampToViewport);
}, []);
const taskbarClick = (w: WinState) => {
if (w.minimized) {
setWins((prev) =>