fix: screen fit-to-window actually scales the canvas (#133)
The fit toggle relied on canvas.fit { max-width/max-height: 100% }.
The canvas is a flex item, and a flex item's automatic minimum size
(min-width/min-height: auto → the replaced element's intrinsic size)
overrides max-*, so an oversized desktop never shrank — it just got
centred and clipped, looking like the toggle did nothing.
Replace the CSS approach with explicit JS sizing: relayoutCanvas()
computes a scale factor (min of width/height ratios, capped at 1 so
it never upscales) and sets canvas.style.width/height in px,
preserving aspect ratio. Recomputed on fit toggle, window resize,
and framebuffer-size (ServerInit). Pointer mapping is unaffected —
sendPointer already derives scale from getBoundingClientRect().
This commit is contained in:
parent
00281730bb
commit
dfee0574a5
1 changed files with 31 additions and 10 deletions
|
|
@ -57,12 +57,10 @@ html, body { height: 100%; background: var(--base); color: var(--text);
|
||||||
width: 100%; height: calc(100% - 36px); overflow: auto;
|
width: 100%; height: calc(100% - 36px); overflow: auto;
|
||||||
background: var(--crust);
|
background: var(--crust);
|
||||||
}
|
}
|
||||||
/* Fit mode: centre the canvas and let CSS scale it down to the
|
/* Fit mode: centre the canvas (relayoutCanvas() scales it in JS to
|
||||||
viewport (intrinsic resolution unchanged — see canvas.fit). */
|
fit the wrap) and clip any sub-pixel rounding overflow. */
|
||||||
#canvas-wrap.fit { align-items: center; overflow: hidden; }
|
#canvas-wrap.fit { align-items: center; overflow: hidden; }
|
||||||
canvas { display: block; cursor: default; }
|
canvas { display: block; cursor: default; }
|
||||||
/* max-* on a replaced element shrinks it preserving aspect ratio. */
|
|
||||||
canvas.fit { max-width: 100%; max-height: 100%; }
|
|
||||||
#msg {
|
#msg {
|
||||||
position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%);
|
position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%);
|
||||||
background: var(--surface0); color: var(--yellow); border-radius: 6px;
|
background: var(--surface0); color: var(--yellow); border-radius: 6px;
|
||||||
|
|
@ -114,22 +112,44 @@ canvas.fit { max-width: 100%; max-height: 100%; }
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Fit-to-window toggle ---
|
// --- Fit-to-window toggle ---
|
||||||
// Scales the canvas down via CSS so the whole desktop is visible
|
// Scales the canvas down so the whole desktop is visible without
|
||||||
// without scrolling. The canvas's intrinsic resolution is untouched;
|
// scrolling. The canvas's intrinsic resolution (width/height attrs)
|
||||||
// only its displayed size changes (see canvas.fit / #canvas-wrap.fit).
|
// is untouched — only its CSS display size changes, set explicitly
|
||||||
// Pointer coordinates are rescaled in sendPointer to stay accurate.
|
// by relayoutCanvas(). Pointer coordinates are rescaled in
|
||||||
// The choice is persisted in localStorage; default is fit-on.
|
// sendPointer to stay accurate. Persisted in localStorage; default
|
||||||
|
// is fit-on.
|
||||||
let fitMode = localStorage.getItem('screen-fit') !== 'off';
|
let fitMode = localStorage.getItem('screen-fit') !== 'off';
|
||||||
|
// Size the canvas. In fit mode, scale down (never up) to the wrap,
|
||||||
|
// preserving aspect ratio. Explicit px sizing rather than CSS
|
||||||
|
// max-width/max-height: on a flex item those are overridden by the
|
||||||
|
// automatic minimum size, so fit mode was a silent no-op — the
|
||||||
|
// oversized canvas just got centred and clipped (issue #133).
|
||||||
|
function relayoutCanvas() {
|
||||||
|
if (fitMode && canvas.width && canvas.height
|
||||||
|
&& canvasWrap.clientWidth && canvasWrap.clientHeight) {
|
||||||
|
const scale = Math.min(
|
||||||
|
canvasWrap.clientWidth / canvas.width,
|
||||||
|
canvasWrap.clientHeight / canvas.height,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
canvas.style.width = (canvas.width * scale) + 'px';
|
||||||
|
canvas.style.height = (canvas.height * scale) + 'px';
|
||||||
|
} else if (!fitMode) {
|
||||||
|
canvas.style.width = '';
|
||||||
|
canvas.style.height = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
function applyFitMode() {
|
function applyFitMode() {
|
||||||
canvas.classList.toggle('fit', fitMode);
|
|
||||||
canvasWrap.classList.toggle('fit', fitMode);
|
canvasWrap.classList.toggle('fit', fitMode);
|
||||||
fitBtn.classList.toggle('active', fitMode);
|
fitBtn.classList.toggle('active', fitMode);
|
||||||
|
relayoutCanvas();
|
||||||
}
|
}
|
||||||
fitBtn.addEventListener('click', () => {
|
fitBtn.addEventListener('click', () => {
|
||||||
fitMode = !fitMode;
|
fitMode = !fitMode;
|
||||||
localStorage.setItem('screen-fit', fitMode ? 'on' : 'off');
|
localStorage.setItem('screen-fit', fitMode ? 'on' : 'off');
|
||||||
applyFitMode();
|
applyFitMode();
|
||||||
});
|
});
|
||||||
|
window.addEventListener('resize', relayoutCanvas);
|
||||||
applyFitMode();
|
applyFitMode();
|
||||||
|
|
||||||
function hex(bytes) {
|
function hex(bytes) {
|
||||||
|
|
@ -514,6 +534,7 @@ canvas.fit { max-width: 100%; max-height: 100%; }
|
||||||
dbg('← server-init: ' + fbW + 'x' + fbH + ' bpp=' + pixelFormat.bpp, 'ok');
|
dbg('← server-init: ' + fbW + 'x' + fbH + ' bpp=' + pixelFormat.bpp, 'ok');
|
||||||
canvas.width = fbW;
|
canvas.width = fbW;
|
||||||
canvas.height = fbH;
|
canvas.height = fbH;
|
||||||
|
relayoutCanvas();
|
||||||
setStatus('connected', 'connected');
|
setStatus('connected', 'connected');
|
||||||
// Request full framebuffer update
|
// Request full framebuffer update
|
||||||
requestUpdate(0, 0, 0, fbW, fbH);
|
requestUpdate(0, 0, 0, fbW, fbH);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue