diff --git a/Sources/MiniPlayerApp.swift b/Sources/MiniPlayerApp.swift index 924d8a0..3ef39d6 100644 --- a/Sources/MiniPlayerApp.swift +++ b/Sources/MiniPlayerApp.swift @@ -131,36 +131,82 @@ struct BricksAppView: View { @ObservedObject var engine: BricksEngine @State private var showToolbar = false @State private var isHoveringIcon = false + @State private var hideTimer: Timer? + @State private var isHoveringToolbar = false + @State private var jsonString: String? + + private func resetHideTimer() { + hideTimer?.invalidate() + guard showToolbar else { return } + hideTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: false) { _ in + withAnimation(.easeInOut(duration: 0.5)) { + showToolbar = false + } + } + } var body: some View { ZStack(alignment: .topLeading) { - // 视频占满全屏 - VideoPlayerRepresentable(player: bridge.player) - .ignoresSafeArea() + // Bricks 渲染视频播放器(占满全屏) + if let json = jsonString { + BricksView(json: json, engine: engine) + } else { + ProgressView("Loading UI...") + .task { + jsonString = loadJSONString() + } + } // 左上角应用图标 — 点击切换控制栏 MiniPlayerIcon() .frame(width: 32, height: 32) .padding(12) + .background( + RoundedRectangle(cornerRadius: 6) + .fill(Color.black.opacity(0.3)) + ) .opacity(isHoveringIcon ? 1.0 : 0.6) .onHover { isHoveringIcon = $0 } - .onTapGesture { - withAnimation(.easeInOut(duration: 0.25)) { - showToolbar.toggle() + .contentShape(Rectangle()) + .highPriorityGesture( + TapGesture().onEnded { + withAnimation(.easeInOut(duration: 0.25)) { + showToolbar.toggle() + } + resetHideTimer() } - } + ) - // 底部控制栏 — 点击图标显示/隐藏 + // 底部控制栏 — 半透明,30秒自动隐藏 VStack { Spacer() if showToolbar { - ControlToolbar(bridge: bridge, isHovering: .constant(false)) { - // onTouch: 保持显示 + ControlToolbar(bridge: bridge, isHovering: $isHoveringToolbar) { + resetHideTimer() + } + .background( + RoundedRectangle(cornerRadius: 12) + .fill(Color.black.opacity(0.6)) + ) + .padding(12) + .onHover { hovering in + isHoveringToolbar = hovering + if hovering { resetHideTimer() } } .transition(.move(edge: .bottom).combined(with: .opacity)) } } } + .onChange(of: showToolbar) { _, newValue in + if newValue { + resetHideTimer() + } else { + hideTimer?.invalidate() + } + } + .onDisappear { + hideTimer?.invalidate() + } .sheet(isPresented: $bridge.showURLDialog) { URLInputDialog(bridge: bridge) } @@ -171,6 +217,15 @@ struct BricksAppView: View { PlaylistWindowView(bridge: bridge) } } + + private func loadJSONString() -> String { + if let url = Bundle.module.url(forResource: "miniplayer", withExtension: "ui"), + let data = try? Data(contentsOf: url), + let str = String(data: data, encoding: .utf8) { + return str + } + return "{\"widgettype\":\"VideoPlayer\",\"id\":\"videoPlayer\",\"options\":{\"width\":\"100%\",\"height\":\"100%\"}}" + } } diff --git a/Sources/Resources/miniplayer.ui b/Sources/Resources/miniplayer.ui index c36bfeb..d49b276 100644 --- a/Sources/Resources/miniplayer.ui +++ b/Sources/Resources/miniplayer.ui @@ -1,164 +1,8 @@ { - "widgettype": "VBox", + "widgettype": "VideoPlayer", + "id": "videoPlayer", "options": { "width": "100%", - "height": "100%", - "bgcolor": "#000000" - }, - "subwidgets": [ - { - "widgettype": "VideoPlayer", - "id": "videoPlayer", - "options": { - "width": "100%", - "height": "85%" - } - }, - { - "widgettype": "HBox", - "options": { - "width": "100%", - "height": "15%", - "bgcolor": "#1a1a1a", - "spacing": 6, - "padding": "8px 12px" - }, - "subwidgets": [ - { - "widgettype": "Button", - "id": "openBtn", - "options": { - "label": "open", - "icon": "folder", - "cwidth": 8, - "cheight": 4, - "bgcolor": "#555555", - "color": "#ffffff" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "event", - "target": "videoPlayer.openFile" - } - ] - }, - { - "widgettype": "Button", - "id": "prevBtn", - "options": { - "label": "prev", - "icon": "backward.fill", - "cwidth": 8, - "cheight": 4, - "bgcolor": "#333333", - "color": "#ffffff" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "event", - "target": "videoPlayer.prev" - } - ] - }, - { - "widgettype": "Button", - "id": "toggleBtn", - "options": { - "label": "play_pause", - "icon": "playpause.fill", - "cwidth": 12, - "cheight": 4, - "bgcolor": "#007aff", - "color": "#ffffff" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "event", - "target": "videoPlayer.toggle" - } - ] - }, - { - "widgettype": "Button", - "id": "nextBtn", - "options": { - "label": "next", - "icon": "forward.fill", - "cwidth": 8, - "cheight": 4, - "bgcolor": "#333333", - "color": "#ffffff" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "event", - "target": "videoPlayer.next" - } - ] - }, - { - "widgettype": "Filler" - }, - { - "widgettype": "Input", - "id": "videoUrlInput", - "options": { - "name": "url", - "placeholder": "url_placeholder", - "cwidth": 30, - "cheight": 4 - } - }, - { - "widgettype": "Button", - "id": "loadBtn", - "options": { - "label": "load", - "icon": "arrow.down.circle.fill", - "cwidth": 8, - "cheight": 4, - "bgcolor": "#5856d6", - "color": "#ffffff" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "method", - "target": "videoPlayer", - "method": "setURL" - } - ] - }, - { - "widgettype": "Button", - "id": "fullscreenBtn", - "options": { - "label": "fullscreen", - "icon": "arrow.up.left.and.arrow.down.right", - "cwidth": 10, - "cheight": 4, - "bgcolor": "#ff9500", - "color": "#ffffff" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "event", - "target": "videoPlayer.fullscreen" - } - ] - } - ] - } - ] + "height": "100%" + } }