mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 23:05:49 +00:00
Merge pull request #300 from meiiie/fix/hud-short-drag-passthrough
fix(launch): keep HUD interactive after short drags
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
import {
|
||||
AppWindow,
|
||||
ArrowCircleUp as ArrowUpCircle,
|
||||
ArrowClockwise as RefreshCw,
|
||||
CaretUp as ChevronUp,
|
||||
CheckCircle as CheckCircle2,
|
||||
DotsThreeVertical as MoreVertical,
|
||||
CaretUp as ChevronUp,
|
||||
Eye,
|
||||
EyeSlash as EyeOff,
|
||||
FolderOpen,
|
||||
Translate as Languages,
|
||||
Microphone as Mic,
|
||||
MicrophoneSlash as MicOff,
|
||||
Minus,
|
||||
Monitor,
|
||||
DotsThreeVertical as MoreVertical,
|
||||
Pause,
|
||||
Play,
|
||||
SpeakerHigh as Volume2,
|
||||
SpeakerX as VolumeX,
|
||||
ArrowClockwise as RefreshCw,
|
||||
Stop as Square,
|
||||
Timer,
|
||||
Translate as Languages,
|
||||
VideoCamera as Video,
|
||||
VideoCamera as VideoIcon,
|
||||
VideoCameraSlash as VideoOff,
|
||||
SpeakerHigh as Volume2,
|
||||
SpeakerX as VolumeX,
|
||||
X,
|
||||
} from "@phosphor-icons/react";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
@@ -41,6 +41,10 @@ import { ContentClamp } from "../ui/content-clamp";
|
||||
import ProjectBrowserDialog, {
|
||||
type ProjectLibraryEntry,
|
||||
} from "../video-editor/ProjectBrowserDialog";
|
||||
import {
|
||||
mergeHudInteractiveBounds,
|
||||
shouldRestoreHudMousePassthroughAfterDrag,
|
||||
} from "./hudMousePassthrough";
|
||||
import styles from "./LaunchWindow.module.css";
|
||||
|
||||
interface DesktopSource {
|
||||
@@ -465,7 +469,26 @@ export function LaunchWindow() {
|
||||
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
||||
event.currentTarget.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
if (wasDragging) {
|
||||
const hudBounds = mergeHudInteractiveBounds(
|
||||
[
|
||||
dropdownRef.current?.getBoundingClientRect(),
|
||||
hudBarRef.current?.getBoundingClientRect(),
|
||||
recordingWebcamPreviewContainerRef.current?.getBoundingClientRect(),
|
||||
].map((bounds) =>
|
||||
bounds
|
||||
? {
|
||||
left: bounds.left,
|
||||
top: bounds.top,
|
||||
right: bounds.right,
|
||||
bottom: bounds.bottom,
|
||||
}
|
||||
: null,
|
||||
),
|
||||
);
|
||||
if (
|
||||
wasDragging &&
|
||||
shouldRestoreHudMousePassthroughAfterDrag(hudBounds, event.clientX, event.clientY)
|
||||
) {
|
||||
window.electronAPI?.hudOverlaySetIgnoreMouse?.(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
mergeHudInteractiveBounds,
|
||||
shouldRestoreHudMousePassthroughAfterDrag,
|
||||
} from "./hudMousePassthrough";
|
||||
|
||||
const hudBounds = { left: 100, top: 200, right: 300, bottom: 260 };
|
||||
|
||||
describe("mergeHudInteractiveBounds", () => {
|
||||
it("returns null when there are no interactive bounds", () => {
|
||||
expect(mergeHudInteractiveBounds([null, undefined])).toBeNull();
|
||||
});
|
||||
|
||||
it("merges the dropdown, bar, and webcam preview bounds", () => {
|
||||
expect(
|
||||
mergeHudInteractiveBounds([
|
||||
{ left: 120, top: 220, right: 260, bottom: 320 },
|
||||
{ left: 100, top: 200, right: 300, bottom: 260 },
|
||||
{ left: 140, top: 280, right: 340, bottom: 430 },
|
||||
]),
|
||||
).toEqual({ left: 100, top: 200, right: 340, bottom: 430 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("shouldRestoreHudMousePassthroughAfterDrag", () => {
|
||||
it("keeps the HUD interactive when the pointer is still inside the HUD", () => {
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 180, 230)).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps the HUD interactive when the pointer ends on the HUD edge", () => {
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 100, 200)).toBe(false);
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 300, 260)).toBe(false);
|
||||
});
|
||||
|
||||
it("restores passthrough when the pointer ends outside the HUD", () => {
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 99, 230)).toBe(true);
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 180, 199)).toBe(true);
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 301, 230)).toBe(true);
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 180, 261)).toBe(true);
|
||||
});
|
||||
|
||||
it("restores passthrough when no HUD bounds are available", () => {
|
||||
expect(shouldRestoreHudMousePassthroughAfterDrag(null, 180, 230)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
export interface HudInteractiveBounds {
|
||||
left: number;
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
}
|
||||
|
||||
export function mergeHudInteractiveBounds(
|
||||
bounds: Array<HudInteractiveBounds | null | undefined>,
|
||||
): HudInteractiveBounds | null {
|
||||
const presentBounds = bounds.filter((value): value is HudInteractiveBounds => Boolean(value));
|
||||
if (presentBounds.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return presentBounds.reduce((merged, current) => ({
|
||||
left: Math.min(merged.left, current.left),
|
||||
top: Math.min(merged.top, current.top),
|
||||
right: Math.max(merged.right, current.right),
|
||||
bottom: Math.max(merged.bottom, current.bottom),
|
||||
}));
|
||||
}
|
||||
|
||||
export function shouldRestoreHudMousePassthroughAfterDrag(
|
||||
bounds: HudInteractiveBounds | null,
|
||||
clientX: number,
|
||||
clientY: number,
|
||||
): boolean {
|
||||
if (!bounds) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
clientX < bounds.left ||
|
||||
clientX > bounds.right ||
|
||||
clientY < bounds.top ||
|
||||
clientY > bounds.bottom
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user