fix(desktop): pet window stability - no skip on click/drag, smooth scale handle

This commit is contained in:
OpenPets Dev 2026-06-14 02:46:51 +00:00
parent 03ae162716
commit 11ed7b3135
2 changed files with 34 additions and 50 deletions

View file

@ -129,10 +129,6 @@ const applyScalePreview = (scale) => {
};
const sendScalePreview = (scale) => {
if (scalePreviewTimer) return;
scalePreviewTimer = setTimeout(() => {
scalePreviewTimer = null;
}, 120);
ipcRenderer.send("openpets:pet-scale-preview", { scale });
};
@ -446,7 +442,7 @@ const installMouseInterop = () => {
}
}
if (scaling) {
const newScale = scaleStartValue + (event.clientY - scaleStartY) * 0.003;
const newScale = scaleStartValue + (event.screenY - scaleStartY) * 0.003;
const clamped = applyScalePreview(newScale);
sendScalePreview(clamped);
}
@ -461,7 +457,7 @@ const installMouseInterop = () => {
if (event.button !== 0) return;
event.preventDefault();
scaling = true;
scaleStartY = event.clientY;
scaleStartY = event.screenY;
scaleStartValue = getCurrentSpriteScale();
setInteractiveHit(true);
ipcRenderer.send("openpets:pet-scale-start");

View file

@ -433,10 +433,7 @@ function installMousePassthroughAndDrag(window: BrowserWindow, hooks: PetWindowI
dragging = null;
petWindowDragging.set(window, false);
debug("pet.window", "drag end", { windowId, position: window.isDestroyed() ? null : readWindowPosition(window) });
if (!window.isDestroyed()) {
const scale = getAppStateSnapshot().preferences.petScale as PetScaleValue;
resizePetWindowForDisplay(window, scale, petWindowLastDisplay.get(window) ?? null);
}
// Do not resize/re-anchor after a drag — the user already positioned the window.
if (wasDragging) onPetEvent?.("pet:dragEnd", {});
};
@ -1469,33 +1466,41 @@ function sendBubbleLayout(window: BrowserWindow, layout: PetWindowBubbleLayout):
});
}
function resizePetWindowForDisplay(window: BrowserWindow, scale: PetScaleValue, display: PetTransientDisplay | null): void {
if (window.isDestroyed()) return;
const nextSize = getPetWindowSize(window, scale, display);
const anchor = readWindowPosition(window);
const nextY = nextSize.bubbleBelow
? anchor.y
: anchor.y - Math.max(0, nextSize.height - defaultPetWindowSize.height);
const nextBounds = {
x: anchor.x - Math.round((nextSize.width - defaultPetWindowSize.width) / 2),
y: nextY,
width: nextSize.width,
height: nextSize.height,
};
function computePetWindowBounds(window: BrowserWindow, nextSize: { readonly width: number; readonly height: number }): Electron.Rectangle | null {
if (window.isDestroyed()) return null;
const currentBounds = window.getBounds();
const petBottom = 22;
const petCenterX = currentBounds.x + Math.round(currentBounds.width / 2);
const petFootY = currentBounds.y + currentBounds.height - petBottom;
const nextX = petCenterX - Math.round(nextSize.width / 2);
const nextY = petFootY - (nextSize.height - petBottom);
const displayForBounds = screen.getDisplayNearestPoint({
x: anchor.x + Math.round(defaultPetWindowSize.width / 2),
y: anchor.y + defaultPetWindowSize.height,
x: petCenterX,
y: currentBounds.y + currentBounds.height,
}) ?? screen.getPrimaryDisplay();
const workArea = displayForBounds.workArea;
const maxX = workArea.x + Math.max(0, workArea.width - nextSize.width);
const maxY = workArea.y + Math.max(0, workArea.height - nextSize.height);
window.setBounds({
x: Math.min(Math.max(nextBounds.x, workArea.x), maxX),
y: Math.min(Math.max(nextBounds.y, workArea.y), maxY),
return {
x: clampNumber(nextX, workArea.x, maxX),
y: clampNumber(nextY, workArea.y, maxY),
width: nextSize.width,
height: nextSize.height,
}, false);
};
}
function resizePetWindowForDisplay(window: BrowserWindow, scale: PetScaleValue, display: PetTransientDisplay | null): void {
if (window.isDestroyed()) return;
const nextSize = getPetWindowSize(window, scale, display);
const currentBounds = window.getBounds();
if (currentBounds.width === nextSize.width && currentBounds.height === nextSize.height) {
return;
}
const nextBounds = computePetWindowBounds(window, nextSize);
if (!nextBounds) return;
window.setBounds(nextBounds, false);
sendBubbleLayout(window, nextSize);
}
@ -1506,26 +1511,9 @@ function resizePetWindowForScale(window: BrowserWindow, scale: PetScaleValue): v
if (currentBounds.width === nextSize.width && currentBounds.height === nextSize.height) {
return;
}
const anchor = readWindowPosition(window);
const nextBounds = {
x: anchor.x - Math.round((nextSize.width - defaultPetWindowSize.width) / 2),
y: anchor.y - Math.max(0, nextSize.height - defaultPetWindowSize.height),
width: nextSize.width,
height: nextSize.height,
};
const displayForBounds = screen.getDisplayNearestPoint({
x: anchor.x + Math.round(defaultPetWindowSize.width / 2),
y: anchor.y + defaultPetWindowSize.height,
}) ?? screen.getPrimaryDisplay();
const workArea = displayForBounds.workArea;
const maxX = workArea.x + Math.max(0, workArea.width - nextSize.width);
const maxY = workArea.y + Math.max(0, workArea.height - nextSize.height);
window.setBounds({
x: Math.min(Math.max(nextBounds.x, workArea.x), maxX),
y: Math.min(Math.max(nextBounds.y, workArea.y), maxY),
width: nextSize.width,
height: nextSize.height,
}, false);
const nextBounds = computePetWindowBounds(window, nextSize);
if (!nextBounds) return;
window.setBounds(nextBounds, false);
}
interface PetWindowBubbleLayout {