fix(stage-tamagotchi): harden caption overlay follow+mouse handling (#1402)
This commit is contained in:
@@ -194,9 +194,12 @@ export function setupCaptionWindowManager(params: {
|
||||
const settleTo = (toX: number, toY: number) => {
|
||||
if (win.isDestroyed())
|
||||
return
|
||||
if (!Number.isFinite(toX) || !Number.isFinite(toY))
|
||||
return
|
||||
|
||||
const b = win.getBounds()
|
||||
state.x = b.x
|
||||
state.y = b.y
|
||||
state.x = Number.isFinite(b.x) ? b.x : 0
|
||||
state.y = Number.isFinite(b.y) ? b.y : 0
|
||||
animation?.pause()
|
||||
animation = animate(state, {
|
||||
x: toX,
|
||||
@@ -207,8 +210,13 @@ export function setupCaptionWindowManager(params: {
|
||||
onRender: () => {
|
||||
if (win.isDestroyed())
|
||||
return
|
||||
if (!Number.isFinite(state.x) || !Number.isFinite(state.y))
|
||||
return
|
||||
|
||||
const toX = Math.round(state.x)
|
||||
const toY = Math.round(state.y)
|
||||
lastProgrammaticMoveAt = Date.now()
|
||||
win.setPosition(state.x, state.y)
|
||||
win.setPosition(toX, toY)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -219,6 +227,9 @@ export function setupCaptionWindowManager(params: {
|
||||
let lastAppliedTy = Number.NaN
|
||||
|
||||
const moveThrottled = throttle(() => {
|
||||
if (win.isDestroyed())
|
||||
return
|
||||
|
||||
const stored = getConfig()?.matrices[matrixHash]?.relativeToMain ?? initialOffset
|
||||
const main = params.mainWindow.getBounds()
|
||||
const b = win.getBounds()
|
||||
@@ -277,6 +288,18 @@ export function setupCaptionWindowManager(params: {
|
||||
}
|
||||
}
|
||||
|
||||
function applyIgnoreMouseEvents(win: BrowserWindow, ignore: boolean) {
|
||||
try {
|
||||
if (ignore)
|
||||
win.setIgnoreMouseEvents(true, { forward: true })
|
||||
else
|
||||
win.setIgnoreMouseEvents(false)
|
||||
}
|
||||
catch {
|
||||
// ignore failures during early window lifecycle
|
||||
}
|
||||
}
|
||||
|
||||
const reusable = createReusableWindow(async () => {
|
||||
// TODO: once we refactored eventa to support window-namespaced contexts,
|
||||
// we can remove the setMaxListeners call below since eventa will be able to dispatch and
|
||||
@@ -290,6 +313,8 @@ export function setupCaptionWindowManager(params: {
|
||||
|
||||
await setupBaseWindowElectronInvokes({ context, window, serverChannel: params.serverChannel, i18n: params.i18n })
|
||||
|
||||
applyIgnoreMouseEvents(window, isFollowing)
|
||||
|
||||
const cfg = getConfig()
|
||||
const saved = cfg?.matrices?.[matrixHash]?.bounds
|
||||
|
||||
@@ -359,22 +384,26 @@ export function setupCaptionWindowManager(params: {
|
||||
async function setFollowWindow(isFollowingWindow: boolean) {
|
||||
isFollowing = isFollowingWindow
|
||||
const window = await reusable.getWindow()
|
||||
|
||||
applyIgnoreMouseEvents(window, isFollowing)
|
||||
|
||||
if (isFollowing) {
|
||||
// Compute and persist current relative offset based on existing positions
|
||||
const rel = computeRelativeOffset(window)
|
||||
const cfg = getConfig() ?? { isFollowing, matrices: {} }
|
||||
cfg.matrices[matrixHash] = { ...cfg.matrices[matrixHash], relativeToMain: rel }
|
||||
updateConfig(cfg)
|
||||
const config = getConfig() ?? { isFollowing, matrices: {} }
|
||||
config.isFollowing = isFollowing
|
||||
config.matrices[matrixHash] = { ...config.matrices[matrixHash], relativeToMain: rel }
|
||||
updateConfig(config)
|
||||
|
||||
// Start following main without re-docking; keep current position
|
||||
followMainWindow(window)
|
||||
}
|
||||
else {
|
||||
detachFromMain()
|
||||
}
|
||||
|
||||
const config = getConfig() ?? { isFollowing, matrices: {} }
|
||||
config.isFollowing = isFollowing
|
||||
updateConfig(config)
|
||||
const config = getConfig() ?? { isFollowing, matrices: {} }
|
||||
config.isFollowing = isFollowing
|
||||
updateConfig(config)
|
||||
}
|
||||
|
||||
// Keep window visible after toggle
|
||||
window.show()
|
||||
@@ -399,6 +428,8 @@ export function setupCaptionWindowManager(params: {
|
||||
async function resetToSide() {
|
||||
const window = await reusable.getWindow()
|
||||
|
||||
applyIgnoreMouseEvents(window, isFollowing)
|
||||
|
||||
// Prevent user-move persistence from overwriting our programmatic move
|
||||
lastProgrammaticMoveAt = Date.now()
|
||||
const initialBounds = computeInitialCaptionBounds({ mainWindow: params.mainWindow })
|
||||
|
||||
Reference in New Issue
Block a user