MiniPlayer/Sources/ProgressSlider.swift
Hermes Agent c69ec38dc3 refactor: rewrite MiniPlayer using SwiftBricks framework
- UI defined in player.json (Bricks JSON schema)
- Custom widgets: VideoPlayer (AVPlayer layer), ProgressSlider (seek bar)
- PlayerBridge connects AVPlayer to BricksEngine event bus
- All interactions via binds/events (no imperative UI code)
- Depends on SwiftBricks SPM package
2026-06-21 17:48:06 +08:00

73 lines
2.5 KiB
Swift

import SwiftUI
import AVFoundation
import SwiftBricks
/// ProgressSliderWidget +seek
struct ProgressSliderWidget: View {
let bridge: PlayerBridge
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
@State private var progress: Double = 0
@State private var duration: Double = 0
@State private var isDragging: Bool = false
var body: some View {
GeometryReader { geo in
ZStack(alignment: .leading) {
//
RoundedRectangle(cornerRadius: 2)
.fill(Color.secondary.opacity(0.3))
.frame(height: 4)
//
RoundedRectangle(cornerRadius: 2)
.fill(Color.accentColor)
.frame(width: geo.size.width * progressRatio, height: 4)
//
Circle()
.fill(Color.accentColor)
.frame(width: 14, height: 14)
.offset(x: geo.size.width * progressRatio - 7)
.shadow(radius: 2)
}
.frame(height: 20)
.contentShape(Rectangle())
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
isDragging = true
let ratio = max(0, min(1, value.location.x / geo.size.width))
if duration > 0 {
let seekTime = ratio * duration
bridge.player.seek(to: CMTime(seconds: seekTime, preferredTimescale: 600))
}
}
.onEnded { _ in
isDragging = false
}
)
}
.frame(height: 20)
.onReceive(engine.store.$values) { values in
if let val = values["progress_slider"] as? String {
let parts = val.split(separator: "/")
if parts.count == 2,
let cur = Double(parts[0]),
let total = Double(parts[1]) {
if !isDragging {
progress = cur
duration = total
}
}
}
}
}
private var progressRatio: Double {
guard duration > 0 else { return 0 }
return max(0, min(1, progress / duration))
}
}