히스토리 팝업 수정: ESC 닫기 + Arrow 키 네비게이션
- Arrow↑↓/Enter/Escape/1-9 키를 팝업 열릴 때 globalShortcut 등록, 닫힐 때 해제 - showHistoryPopup: 2-phase 제거 → 즉시 show (렌더러 비활성 문제 방지) - hideHistoryPopup/itemSelected/popupDismissed에서 nav 키 해제
This commit is contained in:
parent
2392d05ded
commit
6e9e8b1f29
2 changed files with 59 additions and 21 deletions
|
|
@ -117,11 +117,49 @@ async function initPopupWindows(): Promise<void> {
|
||||||
globalShortcut.register('Ctrl+Shift+V', () => {
|
globalShortcut.register('Ctrl+Shift+V', () => {
|
||||||
if (isHistoryPopupVisible()) {
|
if (isHistoryPopupVisible()) {
|
||||||
hideHistoryPopup()
|
hideHistoryPopup()
|
||||||
|
unregisterPopupNavKeys()
|
||||||
} else {
|
} else {
|
||||||
const entries = getHistoryService().list({ page: 0, pageSize: 10 }).entries
|
const entries = getHistoryService().list({ page: 0, pageSize: 10 }).entries
|
||||||
showHistoryPopup(entries as unknown as Array<Record<string, unknown>>)
|
showHistoryPopup(entries as unknown as Array<Record<string, unknown>>)
|
||||||
|
registerPopupNavKeys()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 히스토리 팝업 키 네비게이션 등록/해제
|
||||||
|
const POPUP_NAV_KEYS: Array<{ accel: string; key: string }> = [
|
||||||
|
{ accel: 'Up', key: 'ArrowUp' },
|
||||||
|
{ accel: 'Down', key: 'ArrowDown' },
|
||||||
|
{ accel: 'Return', key: 'Enter' },
|
||||||
|
{ accel: 'Escape', key: 'Escape' },
|
||||||
|
{ accel: '1', key: '1' }, { accel: '2', key: '2' }, { accel: '3', key: '3' },
|
||||||
|
{ accel: '4', key: '4' }, { accel: '5', key: '5' }, { accel: '6', key: '6' },
|
||||||
|
{ accel: '7', key: '7' }, { accel: '8', key: '8' }, { accel: '9', key: '9' },
|
||||||
|
]
|
||||||
|
|
||||||
|
function registerPopupNavKeys(): void {
|
||||||
|
for (const { accel, key } of POPUP_NAV_KEYS) {
|
||||||
|
try {
|
||||||
|
globalShortcut.register(accel, () => {
|
||||||
|
if (isHistoryPopupVisible()) {
|
||||||
|
sendKeyToHistoryPopup(key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
// 일부 키는 globalShortcut으로 등록 불가할 수 있음
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unregisterPopupNavKeys(): void {
|
||||||
|
for (const { accel } of POPUP_NAV_KEYS) {
|
||||||
|
try {
|
||||||
|
globalShortcut.unregister(accel)
|
||||||
|
} catch {
|
||||||
|
// 이미 해제된 경우 무시
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initVoiceMode(): Promise<void> {
|
async function initVoiceMode(): Promise<void> {
|
||||||
|
|
@ -188,6 +226,7 @@ function setupHistoryPopupIPC(): void {
|
||||||
// 아이템 선택 → 텍스트 삽입
|
// 아이템 선택 → 텍스트 삽입
|
||||||
ipcMainRef.on('history:itemSelected', (_event: Electron.IpcMainEvent, data: { text: string }) => {
|
ipcMainRef.on('history:itemSelected', (_event: Electron.IpcMainEvent, data: { text: string }) => {
|
||||||
hideHistoryPopup()
|
hideHistoryPopup()
|
||||||
|
unregisterPopupNavKeys()
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
await getTextInsertService().insertText(data.text)
|
await getTextInsertService().insertText(data.text)
|
||||||
|
|
@ -200,5 +239,6 @@ function setupHistoryPopupIPC(): void {
|
||||||
// 팝업 닫기
|
// 팝업 닫기
|
||||||
ipcMainRef.on('history:popupDismissed', () => {
|
ipcMainRef.on('history:popupDismissed', () => {
|
||||||
hideHistoryPopup()
|
hideHistoryPopup()
|
||||||
|
unregisterPopupNavKeys()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,32 +308,30 @@ export function getHistoryPopupWindow(): BrowserWindow {
|
||||||
export function showHistoryPopup(entries: Array<Record<string, unknown>>): void {
|
export function showHistoryPopup(entries: Array<Record<string, unknown>>): void {
|
||||||
const win = getHistoryPopupWindow()
|
const win = getHistoryPopupWindow()
|
||||||
|
|
||||||
win.webContents.send('history:showItems', { entries })
|
// 커서 위치에 즉시 배치+표시 (2-phase 제거 — 숨겨진 윈도우 렌더러 비활성 문제 방지)
|
||||||
|
const cursorPos = screen.getCursorScreenPoint()
|
||||||
|
const display = screen.getDisplayNearestPoint(cursorPos)
|
||||||
|
|
||||||
const handler = (_event: Electron.IpcMainEvent, data: { width: number; height: number }) => {
|
const popupWidth = 380
|
||||||
ipcMain.removeListener('history:popupMeasured', handler)
|
const popupHeight = Math.min(60 + entries.length * 44, 500)
|
||||||
|
|
||||||
const cursorPos = screen.getCursorScreenPoint()
|
let x = cursorPos.x - Math.round(popupWidth / 2)
|
||||||
const display = screen.getDisplayNearestPoint(cursorPos)
|
let y = cursorPos.y - popupHeight - 20
|
||||||
|
|
||||||
let x = cursorPos.x - Math.round(data.width / 2)
|
x = Math.max(display.workArea.x, Math.min(x, display.workArea.x + display.workArea.width - popupWidth))
|
||||||
let y = cursorPos.y - data.height - 20
|
if (y < display.workArea.y) {
|
||||||
|
y = cursorPos.y + 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
win.webContents.send('history:show', {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('history:popupMeasured', handler)
|
win.setBounds({ x, y, width: popupWidth, height: popupHeight })
|
||||||
|
|
||||||
|
win.webContents.send('history:showItems', { entries })
|
||||||
|
|
||||||
|
if (!win.isVisible()) {
|
||||||
|
win.showInactive()
|
||||||
|
}
|
||||||
|
|
||||||
|
win.webContents.send('history:show', {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hideHistoryPopup(): void {
|
export function hideHistoryPopup(): void {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue