feat: configure duration-aware progress in compact titles
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user