feat: configure duration-aware progress in compact titles

This commit is contained in:
2026-10-12 15:45:22 +02:00
parent 1f5e9541e7
commit 35fa89e492
5 changed files with 96 additions and 8 deletions
+37 -4
View File
@@ -2,6 +2,21 @@ import AppKit
import SwiftUI
import TerebiConfiguration
func compactProgressText(position: Double, duration: Double, format: CompactTitleProgress) -> String? {
guard format != .none, duration.isFinite, duration > 0, position.isFinite else { return nil }
let position = min(max(0, position), duration)
if format == .percentage { return String(Int((position / duration * 100).rounded(.down))) }
let total = Int(min(duration, Double(Int.max / 2)))
let current = Int(min(position, Double(Int.max / 2)))
if duration < 60 { return "\(current) / \(total) s" }
let divisor = duration < 3600 ? 1 : 60
let currentUnits = current / divisor
let totalUnits = total / divisor
let currentTime = String(format: "%02lld:%02lld", Int64(currentUnits / 60), Int64(currentUnits % 60))
let totalTime = String(format: "%02lld:%02lld", Int64(totalUnits / 60), Int64(totalUnits % 60))
return "\(currentTime) / \(totalTime) \(duration < 3600 ? "min" : "h")"
}
@MainActor
final class PlayerModel: ObservableObject {
static var startupConfiguration = PlayerConfiguration()
@@ -30,6 +45,11 @@ final class PlayerModel: ObservableObject {
@Published private(set) var isOnTop = false
let compactWindow: CompactWindowController
let keyBindings: KeyBindings
let compactTitleProgress: CompactTitleProgress
var compactProgress: String? {
guard !isBusy, !isLiveStream, fileName != nil, state.status != PLAYER_ERROR else { return nil }
return compactProgressText(position: state.position, duration: state.duration, format: compactTitleProgress)
}
var playbackIntent: Bool? { transition.playbackIntent }
@Published private(set) var fullScreenIntent: Bool?
@Published private(set) var compactIntent: Bool?
@@ -85,6 +105,7 @@ final class PlayerModel: ObservableObject {
qualityClock: @escaping () -> Double = { ProcessInfo.processInfo.systemUptime }) {
self.qualityClock = qualityClock
keyBindings = configuration.keys
compactTitleProgress = configuration.compactTitleProgress
automaticYouTubeQuality = configuration.automaticQuality
showsBufferInfo = configuration.showBufferInfo
compactWindow = CompactWindowController(snappingEnabled: configuration.snapToEdges)
@@ -1639,11 +1660,23 @@ private struct PlayerContent: View {
}
ZStack(alignment: .top) {
Text(model.fileName ?? "terebi")
let progress = model.isCompact || model.compactWindow.isTransitioning ? model.compactProgress : nil
HStack(spacing: 0) {
Text(model.fileName ?? "terebi")
.lineLimit(1)
.truncationMode(.middle)
.frame(minWidth: 0, maxWidth: progress == nil ? nil : .infinity, alignment: .leading)
.clipped()
if let progress {
Text(" · \(progress)")
.monospacedDigit()
.accessibilityLabel(model.compactTitleProgress == .percentage ? "\(progress) percent" : progress)
.fixedSize(horizontal: true, vertical: false)
}
}
.font(.system(size: 11, weight: .medium))
.lineLimit(1)
.truncationMode(.middle)
.help(model.fileName ?? "terebi")
.accessibilityElement(children: .combine)
.help((model.fileName ?? "terebi") + (progress.map { " · \($0)" } ?? ""))
.padding(8)
.background(PlayerGlass(cornerRadius: 10).clipShape(RoundedRectangle(cornerRadius: 10)).allowsHitTesting(false))
.frame(maxWidth: .infinity, alignment: .leading)