From e6897332a5892a4ec0641ca254b8fc566f0b357b Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Mon, 12 Oct 2026 12:51:38 +0200 Subject: [PATCH] ui: frost the empty player window background --- README.md | 2 ++ Sources/App/TerebiApp.swift | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c1e8fcc..c0f991f 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,8 @@ The layout follows [IINA's video-first window](https://iina.io/img/screenshot.pn The empty player uses the same HUD-glass material, 12-point rounded corners, and muted icon/text palette in a centered welcome card. File and YouTube opening actions share a consistently sized row; the card fits the 400 × 240-point minimum window, and the full window still accepts media-file and YouTube-link drops. +With no media open, the full window uses native behind-window HUD glass to frost the desktop and windows beneath it. The idle Metal surface stays attached but hidden; opening media restores a solid black background and letterboxing, and closing it restores the frosted background. macOS controls the material’s appearance, including Reduce Transparency. + Opening video fits the window to its display aspect ratio, including non-square pixels, and constrains subsequent window resizing. Fullscreen retains the normal letterboxed renderer; audio-only playback and close release the aspect constraint. Extreme ratios that cannot fit both the screen and usable controls keep a freely resizable letterboxed window. **Compact mode** uses the same window, Metal view, and playback session—not a second window or macOS system PiP. Use the PiP-shaped button in the normal playback controls, **⌃⌘P**, or **Playback Actions → Enter Compact Mode**. The compact-mode button replaces the fullscreen button; fullscreen remains available through its menu, keyboard shortcuts, and double-clicking normal-mode video. Compact mode starts at the current screen's top-right corner with a 16-point inset and a preferred width of 320 points, preserving display aspect where usable screen bounds and the 160 × 90-point minimum allow; extreme ratios retain letterboxing. Compact mode floats above ordinary windows, joins all Spaces, and is configured to join eligible other-app fullscreen spaces and Stage Manager sets. Small hover controls provide play/pause, restore, and Playback Actions; keyboard playback controls remain available. diff --git a/Sources/App/TerebiApp.swift b/Sources/App/TerebiApp.swift index b9a759e..7844cfc 100644 --- a/Sources/App/TerebiApp.swift +++ b/Sources/App/TerebiApp.swift @@ -999,17 +999,20 @@ final class PlayerModel: ObservableObject { @MainActor private struct PlayerGlass: NSViewRepresentable { var titlebar = false + var blendingMode: NSVisualEffectView.BlendingMode = .withinWindow func makeNSView(context: Context) -> NSVisualEffectView { let view = NSVisualEffectView() - view.blendingMode = .withinWindow + view.blendingMode = blendingMode view.state = .active view.appearance = NSAppearance(named: .vibrantDark) view.material = titlebar ? .titlebar : .hudWindow return view } - func updateNSView(_ view: NSVisualEffectView, context: Context) {} + func updateNSView(_ view: NSVisualEffectView, context: Context) { + view.blendingMode = blendingMode + } } @MainActor @@ -1389,10 +1392,17 @@ private struct PlayerContent: View { var body: some View { GeometryReader { geometry in ZStack(alignment: .top) { - Color.black + if model.fileName == nil { + PlayerGlass(blendingMode: .behindWindow) + .allowsHitTesting(false) + .accessibilityHidden(true) + } else { + Color.black + } if let view = model.videoView { VideoSurface(view: view) .accessibilityLabel("Video") + .opacity(model.fileName == nil ? 0 : 1) .gesture( TapGesture(count: 2).onEnded { model.toggleFullScreen() } .exclusively(before: TapGesture().onEnded { model.hideControls() }) @@ -1607,7 +1617,8 @@ private struct WindowChrome: NSViewRepresentable { window.styleMask.insert(.fullSizeContentView) window.titleVisibility = .hidden window.titlebarAppearsTransparent = true - window.backgroundColor = .black + window.isOpaque = false + window.backgroundColor = .clear window.acceptsMouseMovedEvents = true window.collectionBehavior.insert(.fullScreenPrimary) window.appearance = NSAppearance(named: .darkAqua)