MiniPlayer/Sources/MiniPlayerApp.swift

139 lines
4.6 KiB
Swift

import SwiftUI
import SwiftBricks
@main
struct MiniPlayerApp: App {
@StateObject private var bridge = PlayerBridge()
var body: some Scene {
WindowGroup {
ContentView(bridge: bridge)
.frame(minWidth: 900, minHeight: 600)
.onAppear { bridge.setup() }
}
#if os(macOS)
.commands {
CommandGroup(replacing: .newItem) {}
CommandMenu("播放") {
Button("播放/暂停") { bridge.togglePlayPause() }
.keyboardShortcut(" ", modifiers: [])
Divider()
Button("上一首") { bridge.playPrev() }
.keyboardShortcut("[", modifiers: [])
Button("下一首") { bridge.playNext() }
.keyboardShortcut("]", modifiers: [])
Divider()
Button("全屏") { bridge.toggleFullscreen() }
.keyboardShortcut("f", modifiers: .command)
Divider()
Button("循环模式") { bridge.cycleRepeatMode() }
.keyboardShortcut("r", modifiers: .command)
}
CommandMenu("文件") {
Button("打开文件...") { bridge.openFileDialog() }
.keyboardShortcut("o", modifiers: .command)
Divider()
Button("添加URL...") { bridge.showURLDialog = true }
.keyboardShortcut("u", modifiers: .command)
}
}
#endif
}
}
/// BricksJSON + widget
struct ContentView: View {
@ObservedObject var bridge: PlayerBridge
var body: some View {
Group {
if let engine = bridge.engine, let schema = bridge.schema {
BricksView(schema: schema, engine: engine)
.overlay(alignment: .top) {
if let error = bridge.toastMessage {
Text(error)
.padding(8)
.background(.black.opacity(0.7))
.foregroundColor(.white)
.cornerRadius(6)
.padding(.top, 8)
.transition(.opacity)
}
}
.sheet(isPresented: $bridge.showURLDialog) {
URLInputDialog(bridge: bridge)
}
#if os(macOS)
.sheet(isPresented: $bridge.showTrackDialog) {
TrackSelectDialog(bridge: bridge)
}
#endif
} else {
ProgressView("初始化...")
}
}
}
}
/// URL
struct URLInputDialog: View {
@ObservedObject var bridge: PlayerBridge
@State private var url = ""
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(spacing: 16) {
Text("添加媒体URL").font(.headline)
TextField("https://example.com/video.m3u8", text: $url)
.textFieldStyle(.roundedBorder)
HStack {
Button("取消") { dismiss() }
Spacer()
Button("添加") {
if !url.isEmpty {
bridge.addURL(url)
dismiss()
}
}
.keyboardShortcut(.defaultAction)
.disabled(url.isEmpty)
}
}
.padding(20)
.frame(width: 400)
}
}
///
struct TrackSelectDialog: View {
@ObservedObject var bridge: PlayerBridge
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(spacing: 12) {
Text("选择音轨").font(.headline)
ForEach(Array(bridge.availableTracks.enumerated()), id: \.offset) { idx, track in
Button(action: {
bridge.selectTrack(index: idx)
dismiss()
}) {
HStack {
Text("Track \(idx + 1)")
Spacer()
if idx == bridge.currentTrackIndex {
Text("").foregroundColor(.green)
}
}
.padding(8)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
Divider()
Button("关闭") { dismiss() }
}
.padding(20)
.frame(minWidth: 250)
}
}