fix: popovers issue with mouse passthrough

fix: issues with HUD bar not registering clicks properly
This commit is contained in:
Alan Trebugeais
2026-05-07 13:15:34 +02:00
parent 9575ef3bd2
commit 13bd5d49b4
6 changed files with 67 additions and 18 deletions
+1
View File
@@ -36,6 +36,7 @@ export default function App() {
document.documentElement.classList.add("hud-overlay-window");
document.body.classList.add("hud-overlay-window");
document.getElementById("root")?.classList.add("hud-overlay-window");
window.electronAPI?.hudOverlaySetIgnoreMouse?.(true);
} else if (type === "update-toast") {
document.documentElement.style.overflow = "visible";
document.body.style.overflow = "visible";
+18 -11
View File
@@ -33,6 +33,7 @@ import { MicPopover } from "./popovers/MicPopover";
import { MorePopover } from "./popovers/MorePopover";
import { SourcePopover } from "./popovers/SourcePopover";
import { WebcamPopover } from "./popovers/WebcamPopover";
import { HudInteractionContext } from "./contexts/HudInteractionContext";
import { MarqueeText } from "./SourceSelector";
import styles from "./LaunchWindow.module.css";
@@ -173,7 +174,7 @@ function LaunchWindowContent() {
recordingWebcamPreviewContainerRef,
});
const { handleHudMouseLeave, beginInteractiveHudAction } = useLaunchHudInteractionState({
const { handleHudMouseEnter, handleHudMouseLeave, beginInteractiveHudAction } = useLaunchHudInteractionState({
openId,
projectBrowserOpen,
setProjectBrowserOpen,
@@ -428,17 +429,20 @@ function LaunchWindowContent() {
const hudMode = finalizing ? "finalizing" : recording ? "recording" : "idle";
return (
<div
className="w-full flex justify-center bg-transparent overflow-visible items-end pb-5"
style={{ height: "100vh" }}
>
<HudInteractionContext.Provider value={{ onMouseEnter: handleHudMouseEnter, onMouseLeave: handleHudMouseLeave }}>
<div
className="w-full flex justify-center bg-transparent overflow-visible items-end pb-5 pointer-events-none"
style={{ height: "100vh" }}
>
<div
ref={hudContentRef}
className="flex items-center overflow-visible flex-col-reverse"
onMouseEnter={() => window.electronAPI?.hudOverlaySetIgnoreMouse?.(false)}
onMouseLeave={handleHudMouseLeave}
className="flex items-center overflow-visible flex-col-reverse pointer-events-none"
>
<div className="flex flex-col items-center pointer-events-auto">
<div
className="flex flex-col items-center pointer-events-auto"
onMouseEnter={handleHudMouseEnter}
onMouseLeave={handleHudMouseLeave}
>
<div
ref={hudBarTransformRef}
style={{
@@ -510,7 +514,7 @@ function LaunchWindowContent() {
{showRecordingWebcamPreview && (
<div
ref={recordingWebcamPreviewContainerRef}
className={`${styles.recordingWebcamPreview} ${styles.electronNoDrag}`}
className={`${styles.recordingWebcamPreview} ${styles.electronNoDrag} pointer-events-auto`}
title={t("recording.webcam")}
style={{
transform: `translate(${webcamPreviewOffset.x}px, ${webcamPreviewOffset.y}px)`,
@@ -519,6 +523,8 @@ function LaunchWindowContent() {
onPointerMove={handleWebcamPreviewPointerMove}
onPointerUp={handleWebcamPreviewPointerUp}
onPointerCancel={handleWebcamPreviewPointerUp}
onMouseEnter={handleHudMouseEnter}
onMouseLeave={handleHudMouseLeave}
>
<video
ref={setRecordingWebcamPreviewNode}
@@ -532,7 +538,7 @@ function LaunchWindowContent() {
</div>
{projectBrowserOpen ? (
<div className={styles.electronNoDrag}>
<div className={`${styles.electronNoDrag} pointer-events-auto`}>
<ProjectBrowserDialog
open={projectBrowserOpen}
onOpenChange={setProjectBrowserOpen}
@@ -546,5 +552,6 @@ function LaunchWindowContent() {
) : null}
</div>
</div>
</HudInteractionContext.Provider>
);
}
+1 -1
View File
@@ -342,7 +342,7 @@ export const SourceSelector = React.memo(function SourceSelector({
);
return (
<Popover open={open} onOpenChange={onOpenChange}>
<Popover open={open} onOpenChange={onOpenChange} modal={false}>
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
<PopoverContent
className="launch-theme w-80 p-0 source-selector-popover"
@@ -0,0 +1,16 @@
import { createContext, useContext } from "react";
interface HudInteractionContextType {
onMouseEnter: () => void;
onMouseLeave: (event: any) => void;
}
export const HudInteractionContext = createContext<HudInteractionContextType | null>(null);
export function useHudInteraction() {
const context = useContext(HudInteractionContext);
if (!context) {
throw new Error("useHudInteraction must be used within a HudInteractionProvider");
}
return context;
}
@@ -17,9 +17,18 @@ export function useLaunchHudInteractionState({
}) {
const anyPopoverOpenRef = useRef(false);
const projectBrowserOpenRef = useRef(false);
const isMouseOverHudRef = useRef(false);
useEffect(() => {
anyPopoverOpenRef.current = openId !== null;
if (openId === null) {
// Proactively check if we should ignore mouse when popover closes
setTimeout(() => {
if (!isMouseOverHudRef.current && !anyPopoverOpenRef.current) {
window.electronAPI?.hudOverlaySetIgnoreMouse?.(true);
}
}, 150);
}
}, [openId]);
useEffect(() => {
@@ -34,29 +43,45 @@ export function useLaunchHudInteractionState({
const beginInteractiveHudAction = useCallback(() => {
setProjectBrowserOpen(false);
isMouseOverHudRef.current = true;
window.electronAPI?.hudOverlaySetIgnoreMouse?.(false);
}, [setProjectBrowserOpen]);
const handleHudMouseEnter = useCallback(() => {
isMouseOverHudRef.current = true;
if (timeoutRef.current) clearTimeout(timeoutRef.current);
window.electronAPI?.hudOverlaySetIgnoreMouse?.(false);
}, []);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleHudMouseLeave = useCallback((event: MouseEvent<HTMLDivElement>) => {
const nextTarget = event.relatedTarget;
if (nextTarget instanceof Node && event.currentTarget.contains(nextTarget)) {
return;
}
requestAnimationFrame(() => {
isMouseOverHudRef.current = false;
if (timeoutRef.current) clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => {
if (
!isHudDraggingRef.current &&
!isWebcamPreviewDraggingRef.current &&
!webcamPreviewDragStartRef.current &&
!anyPopoverOpenRef.current &&
!projectBrowserOpenRef.current
!projectBrowserOpenRef.current &&
!isMouseOverHudRef.current
) {
// If a popover is open, we can still ignore mouse if the mouse is truly gone,
// but we give a bit more breathing room (the 300ms timeout).
window.electronAPI?.hudOverlaySetIgnoreMouse?.(true);
}
});
}, 300);
}, [isHudDraggingRef, isWebcamPreviewDraggingRef, webcamPreviewDragStartRef]);
return {
handleHudMouseEnter,
handleHudMouseLeave,
beginInteractiveHudAction,
};
@@ -74,12 +74,12 @@ export function HudPopover({
align?: "start" | "center" | "end";
}) {
return (
<Popover open={open} onOpenChange={onOpenChange}>
<Popover open={open} onOpenChange={onOpenChange} modal={false}>
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
<PopoverContent
className={`launch-theme ${styles.menuCard} ${styles.electronNoDrag}`}
unstyled
side="bottom"
side="top"
align={align}
sideOffset={8}
avoidCollisions