Commit fac385f5 authored by Kourser's avatar Kourser
Browse files

fix(macos): stable Touch Bar structure — no live rebuilds

Replacing NSApp.touchBar when playback started proved glitchy: the bar
could vanish entirely (no controls, no title) and recreating the items
reset the subscriptions scrubber's scroll position. The bar now has a
fixed item structure installed once: the title label and the position
slider always exist and are simply emptied/disabled while idle, so
starting playback only mutates content — never the bar itself.

Co-Authored-By: Claude (RCA)
parent 2dba0eff
Loading
Loading
Loading
Loading
+10 −18
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ final class TouchBarController: NSObject {
    private var podcasts: [Podcast] = []
    private var artwork: [UUID: NSImage] = [:]
    private var artworkRequests: Set<UUID> = []
    private var hadEpisode = false

    private static let scrubberCell = NSUserInterfaceItemIdentifier("podcast-artwork")

@@ -40,8 +39,9 @@ final class TouchBarController: NSObject {

    /// Installs the bar app-wide: the application object sits at the end of
    /// the Touch Bar responder chain, so this is the fallback for every view.
    /// The bar is installed exactly once, with a fixed item structure —
    /// replacing it live proved glitchy (controls vanishing, scrubber reset).
    func install() {
        hadEpisode = playback.currentEpisode != nil
        NSApp.touchBar = makeTouchBar()
    }

@@ -64,24 +64,19 @@ final class TouchBarController: NSObject {
    }

    private func refresh() {
        // The now-playing items (title + position) only exist while an
        // episode is loaded: rebuild the bar when that changes.
        let hasEpisode = playback.currentEpisode != nil
        if hasEpisode != hadEpisode {
            hadEpisode = hasEpisode
            NSApp.touchBar = makeTouchBar()
        }

        playPauseItem?.image = Self.symbol(playback.isPlaying ? "pause.fill" : "play.fill")
        titleLabel?.stringValue = playback.currentEpisode?.title ?? ""

        if let item = positionItem {
            let duration = playback.duration
            item.slider.isEnabled = duration > 0 // a live radio has no position
            // Disabled while idle, and for live radio (no position to seek).
            item.slider.isEnabled = playback.currentEpisode != nil && duration > 0
            item.slider.maxValue = max(duration, 1)
            // Don't fight the user's finger mid-drag.
            if item.slider.cell?.isHighlighted != true {
                item.slider.doubleValue = min(playback.currentTime, item.slider.maxValue)
                item.slider.doubleValue = playback.currentEpisode == nil
                    ? 0
                    : min(playback.currentTime, item.slider.maxValue)
            }
        }

@@ -110,12 +105,9 @@ extension TouchBarController: NSTouchBarDelegate {
    private func makeTouchBar() -> NSTouchBar {
        let bar = NSTouchBar()
        bar.delegate = self
        var identifiers: [NSTouchBarItem.Identifier] = [.skingomzSkipBack, .skingomzPlayPause, .skingomzSkipForward]
        if playback.currentEpisode != nil {
            identifiers += [.skingomzTitle, .skingomzPosition]
        }
        identifiers += [.fixedSpaceSmall, .skingomzPodcasts]
        bar.defaultItemIdentifiers = identifiers
        bar.defaultItemIdentifiers = [.skingomzSkipBack, .skingomzPlayPause, .skingomzSkipForward,
                                      .skingomzTitle, .skingomzPosition,
                                      .fixedSpaceSmall, .skingomzPodcasts]
        return bar
    }