mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(embedded-mpv): expose popover area by shrinking MPV view, not just hiding
Same root cause as the modal-dialog occlusion: control popovers (volume, audio, subtitle, speed, aspect) extend upward from the controls strip into the area covered by the MPV NSView. They render in DOM but the native view paints over them. Replace the simple boolean overlayActiveProvider with a richer boundsProvider closure on the session controller. The component drives it from both the modal overlay state and the popover menu state: - Modal dialog open (command palette, MatDialog) -> HIDDEN_BOUNDS, MPV fully off-screen so the dialog has the whole window. - Popover menu open -> shrink MPV from the bottom by 300 px so the popover region lives in DOM-receiving space; video keeps playing in the upper region instead of disappearing entirely. - Otherwise -> full host bounds. Bounds-resync effect now tracks menus.anyOpen() in addition to overlayActive() so opening or closing a popover triggers an immediate re-sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Entire-Checkpoint: f957cd9849e0
This commit is contained in:
1 parent
0249a3a9f7
commit
126025aa41
3 files changed
+39
-10
No files matched your search
@@ -7,6 +7,16 @@ export const HIDDEN_BOUNDS: EmbeddedMpvBounds = Object.freeze({
|
||||
height: 1,
|
||||
}) as EmbeddedMpvBounds;
|
||||
|
||||
/**
|
||||
* Vertical pixels to subtract from the MPV view's height when a control
|
||||
* popover (volume, audio, subtitle, speed, aspect) is open above the
|
||||
* controls strip. The native NSView paints over the WebContents, so we
|
||||
* shrink it from the bottom to expose the popover region in DOM. Sized to
|
||||
* cover the tallest popover (audio/subtitle list capped at ~240 px plus
|
||||
* title + padding); video keeps playing in the upper region.
|
||||
*/
|
||||
export const MENU_OPEN_BOTTOM_CUTOUT_PX = 300;
|
||||
|
||||
export const SPEED_PRESETS: ReadonlyArray<{ value: number; label: string }> = [
|
||||
{ value: 0.5, label: '0.5×' },
|
||||
{ value: 0.75, label: '0.75×' },
|
||||
|
||||
@@ -29,10 +29,13 @@ import {
|
||||
} from './embedded-mpv-ui-state';
|
||||
import {
|
||||
ASPECT_PRESETS,
|
||||
HIDDEN_BOUNDS,
|
||||
MENU_OPEN_BOTTOM_CUTOUT_PX,
|
||||
SPEED_PRESETS,
|
||||
aspectLabel,
|
||||
audioTrackLabel,
|
||||
formatTime,
|
||||
measureBounds,
|
||||
persistVolume,
|
||||
readStoredVolume,
|
||||
speedLabel,
|
||||
@@ -226,9 +229,22 @@ export class EmbeddedMpvPlayerComponent implements OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
this.controller.setOverlayActiveProvider(() =>
|
||||
this.overlayVisibility.overlayActive()
|
||||
);
|
||||
this.controller.setBoundsProvider((host) => {
|
||||
if (this.overlayVisibility.overlayActive()) {
|
||||
return HIDDEN_BOUNDS;
|
||||
}
|
||||
const rect = measureBounds(host);
|
||||
if (this.menus.anyOpen()) {
|
||||
return {
|
||||
...rect,
|
||||
height: Math.max(
|
||||
1,
|
||||
rect.height - MENU_OPEN_BOTTOM_CUTOUT_PX
|
||||
),
|
||||
};
|
||||
}
|
||||
return rect;
|
||||
});
|
||||
|
||||
this.shortcuts.attach({
|
||||
isAvailable: () => !this.overlayVisibility.overlayActive(),
|
||||
@@ -265,6 +281,7 @@ export class EmbeddedMpvPlayerComponent implements OnDestroy {
|
||||
|
||||
effect(() => {
|
||||
this.overlayVisibility.overlayActive();
|
||||
this.menus.anyOpen();
|
||||
this.controller.triggerBoundsSync();
|
||||
});
|
||||
|
||||
|
||||
@@ -7,11 +7,14 @@ import {
|
||||
untracked,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
EmbeddedMpvBounds,
|
||||
EmbeddedMpvSession,
|
||||
EmbeddedMpvSupport,
|
||||
ResolvedPortalPlayback,
|
||||
} from 'shared-interfaces';
|
||||
import { HIDDEN_BOUNDS, measureBounds } from './embedded-mpv-format.utils';
|
||||
import { measureBounds } from './embedded-mpv-format.utils';
|
||||
|
||||
export type EmbeddedMpvBoundsProvider = (host: HTMLElement) => EmbeddedMpvBounds;
|
||||
|
||||
const STALLED_TIMEOUT_MS = 30_000;
|
||||
|
||||
@@ -26,7 +29,8 @@ export class EmbeddedMpvSessionController {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly unsubscribeSessionUpdate?: () => void;
|
||||
|
||||
private overlayActiveProvider: () => boolean = () => false;
|
||||
private boundsProvider: EmbeddedMpvBoundsProvider = (host) =>
|
||||
measureBounds(host);
|
||||
private activeBoundsSync: (() => void) | null = null;
|
||||
private boundsAnimationFrame: number | null = null;
|
||||
private stalledTimer: number | null = null;
|
||||
@@ -65,8 +69,8 @@ export class EmbeddedMpvSessionController {
|
||||
});
|
||||
}
|
||||
|
||||
setOverlayActiveProvider(provider: () => boolean): void {
|
||||
this.overlayActiveProvider = provider;
|
||||
setBoundsProvider(provider: EmbeddedMpvBoundsProvider): void {
|
||||
this.boundsProvider = provider;
|
||||
}
|
||||
|
||||
triggerBoundsSync(): void {
|
||||
@@ -98,9 +102,7 @@ export class EmbeddedMpvSessionController {
|
||||
if (!activeSessionId) {
|
||||
return;
|
||||
}
|
||||
const bounds = this.overlayActiveProvider()
|
||||
? HIDDEN_BOUNDS
|
||||
: measureBounds(host);
|
||||
const bounds = this.boundsProvider(host);
|
||||
void window.electron
|
||||
?.setEmbeddedMpvBounds(activeSessionId, bounds)
|
||||
.catch(() => undefined);
|
||||
|
||||
Reference in new issue
Block a user