From 67a6ee242a8bdc3c45a629e8ad9f8661be2960bd Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Sun, 5 Apr 2026 11:09:55 +0900 Subject: [PATCH] =?UTF-8?q?RecordingTip=202=ED=9A=8C=EC=B0=A8=20=EC=95=88?= =?UTF-8?q?=20=EB=B3=B4=EC=9E=84=20=EC=88=98=EC=A0=95:=202-phase=20?= =?UTF-8?q?=EB=A6=AC=EC=82=AC=EC=9D=B4=EC=A6=88=20=EC=A0=9C=EA=B1=B0,=20?= =?UTF-8?q?=EC=A6=89=EC=8B=9C=20show=20=EB=B0=A9=EC=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/windows/WindowManager.ts | 70 +++++++++---------------------- 1 file changed, 19 insertions(+), 51 deletions(-) diff --git a/src/main/windows/WindowManager.ts b/src/main/windows/WindowManager.ts index 18e95c5..f7fc811 100644 --- a/src/main/windows/WindowManager.ts +++ b/src/main/windows/WindowManager.ts @@ -126,71 +126,39 @@ export function getRecordingTipWindow(): BrowserWindow { * Phase 1: prepare → 측정 * Phase 2: resize → show */ -// RecordingTip 측정 리스너 관리 (race condition 방지) -let _tipMeasuredHandler: ((_event: Electron.IpcMainEvent, data: { width: number; height: number }) => void) | null = null -let _tipHidden = false +// RecordingTip 기본 크기 (고정 — 2-phase 복잡도 제거) +const TIP_WIDTH = 280 +const TIP_HEIGHT = 80 export function showRecordingTip( state: string, params?: { text?: string; errorMessage?: string } ): void { const win = getRecordingTipWindow() - _tipHidden = false - // 이전 측정 리스너 제거 (중복 방지) - if (_tipMeasuredHandler) { - ipcMain.removeListener('window:tipMeasured', _tipMeasuredHandler) - _tipMeasuredHandler = null + // 커서 위치에 배치 + const cursorPos = screen.getCursorScreenPoint() + const display = screen.getDisplayNearestPoint(cursorPos) + + let x = cursorPos.x - Math.round(TIP_WIDTH / 2) + let y = cursorPos.y - TIP_HEIGHT - 20 + + // 화면 밖 보정 + x = Math.max(display.workArea.x, Math.min(x, display.workArea.x + display.workArea.width - TIP_WIDTH)) + if (y < display.workArea.y) { + y = cursorPos.y + 20 } - // Phase 1: prepare (크기 측정) - win.webContents.send('window:tipPrepare', { state, ...params }) + win.setBounds({ x, y, width: TIP_WIDTH, height: TIP_HEIGHT }) - // tipMeasured 이벤트를 한번만 처리 - _tipMeasuredHandler = (_event: Electron.IpcMainEvent, data: { width: number; height: number }) => { - if (_tipMeasuredHandler) { - ipcMain.removeListener('window:tipMeasured', _tipMeasuredHandler) - _tipMeasuredHandler = null - } - - // hide가 이미 호출되었으면 show하지 않음 - if (_tipHidden) return - - // 커서 위치에 표시 - const cursorPos = screen.getCursorScreenPoint() - const display = screen.getDisplayNearestPoint(cursorPos) - - let x = cursorPos.x - Math.round(data.width / 2) - let y = cursorPos.y - data.height - 20 - - // 화면 밖 보정 - x = Math.max(display.workArea.x, Math.min(x, display.workArea.x + display.workArea.width - data.width)) - if (y < display.workArea.y) { - y = cursorPos.y + 20 - } - - win.setBounds({ x, y, width: data.width, height: data.height }) - - if (!win.isVisible()) { - win.showInactive() - } - - // Phase 2: show - win.webContents.send('window:tipShow', { state }) + // 상태 전송 후 즉시 show (2-phase 제거 — 숨겨진 윈도우의 렌더러 비활성 문제 방지) + win.webContents.send('window:tipStateChanged', { state, ...params }) + if (!win.isVisible()) { + win.showInactive() } - - ipcMain.on('window:tipMeasured', _tipMeasuredHandler) } export function hideRecordingTip(): void { - _tipHidden = true - - // pending 측정 리스너 제거 (늦게 도착하면 다시 show되는 버그 방지) - if (_tipMeasuredHandler) { - ipcMain.removeListener('window:tipMeasured', _tipMeasuredHandler) - _tipMeasuredHandler = null - } - if (recordingTipWindow && !recordingTipWindow.isDestroyed()) { recordingTipWindow.hide() }