fix hover state bug

This commit is contained in:
Boof2015
2026-04-22 00:35:23 -04:00
parent 7044a331ae
commit 8d8adaa551
4 changed files with 77 additions and 3 deletions
+15
View File
@@ -695,6 +695,16 @@ function stopWindowResizeController(): void {
resizeEdge = null
}
function isCursorInsideWindow(window: BrowserWindow): boolean {
const cursor = screen.getCursorScreenPoint()
const bounds = window.getBounds()
return cursor.x > bounds.x
&& cursor.x < bounds.x + bounds.width
&& cursor.y > bounds.y
&& cursor.y < bounds.y + bounds.height
}
function getFramelessWindowChromeOptions(): Pick<
BrowserWindowConstructorOptions,
'frame' | 'transparent' | 'backgroundColor' | 'roundedCorners' | 'hasShadow' | 'thickFrame' | 'backgroundMaterial'
@@ -1445,6 +1455,11 @@ function setupIPC(): void {
return getWindowFromSender(event.sender)?.isAlwaysOnTop() ?? false
})
ipcMain.handle('window:is-cursor-inside', (event) => {
const targetWindow = getWindowFromSender(event.sender)
return targetWindow ? isCursorInsideWindow(targetWindow) : false
})
ipcMain.handle('app:get-build-info', () => {
return getAppBuildInfo()
})
+1
View File
@@ -59,6 +59,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
repositionWindow: (position: 'top' | 'bottom') => ipcRenderer.send('window:reposition', position),
toggleAlwaysOnTop: () => ipcRenderer.send('window:toggle-always-on-top'),
isAlwaysOnTop: () => ipcRenderer.invoke('window:is-always-on-top'),
isCursorInsideWindow: () => ipcRenderer.invoke('window:is-cursor-inside') as Promise<boolean>,
getCaptureBackendSupport: async () => getCaptureBackendSupport(process.platform, nativeCaptureAPI) as CaptureBackendSupport,
getNowPlayingState: () => ipcRenderer.invoke('now-playing:get-state') as Promise<NowPlayingState>,
setNowPlayingConsumerActive: (active: boolean) => ipcRenderer.invoke('now-playing:set-active', active) as Promise<NowPlayingState>,
+60 -3
View File
@@ -20,6 +20,7 @@ export default function App(): JSX.Element {
const [settingsPanelHeight, setSettingsPanelHeight] = useState(0)
const [bottomBarHeight, setBottomBarHeight] = useState(0)
const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const settingsOpenRef = useRef(false)
const externalProfileOpenQueueRef = useRef(Promise.resolve())
const initializeProfiles = useSettingsStore((s) => s.initializeProfiles)
@@ -175,12 +176,67 @@ export default function App(): JSX.Element {
setToolbarVisible(true)
}, [])
useEffect(() => {
settingsOpenRef.current = settingsOpen
if (settingsOpen) {
showToolbar()
}
}, [settingsOpen, showToolbar])
const scheduleHide = useCallback(() => {
if (settingsOpen) return
if (settingsOpenRef.current || hideTimeoutRef.current) return
hideTimeoutRef.current = setTimeout(() => {
hideTimeoutRef.current = null
if (settingsOpenRef.current) return
setToolbarVisible(false)
}, 400)
}, [settingsOpen])
}, [])
const handleToolbarHoverLeave = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
const relatedTarget = event.relatedTarget
if (relatedTarget instanceof Node && event.currentTarget.contains(relatedTarget)) {
return
}
const bounds = event.currentTarget.getBoundingClientRect()
const isPointerInside = event.clientX > bounds.left
&& event.clientX < bounds.right
&& event.clientY > bounds.top
&& event.clientY < bounds.bottom
if (isPointerInside) {
showToolbar()
return
}
scheduleHide()
}, [scheduleHide, showToolbar])
useEffect(() => {
if (!toolbarVisible || settingsOpen) return
let isDisposed = false
const checkCursorInsideWindow = (): void => {
void window.electronAPI.isCursorInsideWindow()
.then((isInside) => {
if (!isDisposed && !isInside) {
scheduleHide()
}
})
.catch(() => {
// Renderer leave events still handle auto-hide if the cursor query is unavailable.
})
}
checkCursorInsideWindow()
const interval = setInterval(checkCursorInsideWindow, 120)
return () => {
isDisposed = true
clearInterval(interval)
}
}, [scheduleHide, settingsOpen, toolbarVisible])
const handleToggleSettings = useCallback(() => {
toggleSettings()
@@ -222,7 +278,8 @@ export default function App(): JSX.Element {
<div
className="prism-app"
onMouseEnter={showToolbar}
onMouseLeave={scheduleHide}
onMouseMove={showToolbar}
onMouseLeave={handleToolbarHoverLeave}
onMouseDown={useNativeDragRegions ? undefined : handleAltDragStart}
onMouseUp={useNativeDragRegions ? undefined : handleAltDragEnd}
>
+1
View File
@@ -55,6 +55,7 @@ declare global {
repositionWindow: (position: 'top' | 'bottom') => void
toggleAlwaysOnTop: () => void
isAlwaysOnTop: () => Promise<boolean>
isCursorInsideWindow: () => Promise<boolean>
getCaptureBackendSupport: () => Promise<CaptureBackendSupport>
getNowPlayingState: () => Promise<NowPlayingState>
setNowPlayingConsumerActive: (active: boolean) => Promise<NowPlayingState>