From ec0d9b5062c5543c5c12df95eb33bb27faaa4e06 Mon Sep 17 00:00:00 2001 From: Brendan Szymanski Date: Wed, 24 Jun 2026 00:40:13 -0400 Subject: [PATCH] Address code quality review issues across multiple files --- Sources/LuminateCore/PlayerState.swift | 2 +- Sources/LuminatePlayer/PlayerControls.swift | 3 +- Sources/LuminatePlayer/PlayerView.swift | 46 ++++++++++++++++----- Sources/LuminateUI/Components/SeekBar.swift | 2 +- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/Sources/LuminateCore/PlayerState.swift b/Sources/LuminateCore/PlayerState.swift index 0ededc4..1be25c5 100644 --- a/Sources/LuminateCore/PlayerState.swift +++ b/Sources/LuminateCore/PlayerState.swift @@ -21,7 +21,7 @@ import Foundation -public struct PlayerState: Hashable { +public struct PlayerState: Hashable, Sendable { public var itemId: String public var mediaSourceId: String diff --git a/Sources/LuminatePlayer/PlayerControls.swift b/Sources/LuminatePlayer/PlayerControls.swift index 1a59809..5d06925 100644 --- a/Sources/LuminatePlayer/PlayerControls.swift +++ b/Sources/LuminatePlayer/PlayerControls.swift @@ -20,9 +20,7 @@ // import Adwaita -import LuminateCore import LuminateUI -import LuminateDI public struct PlayerControls: View { @@ -106,6 +104,7 @@ public struct PlayerControls: View { // Volume popover — future } .style("flat") + .insensitive() Button(icon: .default(icon: .viewFullscreen)) { onFullscreen() diff --git a/Sources/LuminatePlayer/PlayerView.swift b/Sources/LuminatePlayer/PlayerView.swift index e9bf79c..f95e2da 100644 --- a/Sources/LuminatePlayer/PlayerView.swift +++ b/Sources/LuminatePlayer/PlayerView.swift @@ -26,6 +26,11 @@ import LuminateDI import CModules import CAdw +final class PlayerTasks { + var progress: Task? + var controls: Task? +} + public struct PlayerView: View { public var playerState: PlayerState @@ -38,6 +43,9 @@ public struct PlayerView: View { @State private var position: Double = 0 @State private var duration: Double = 0 @State private var showControls = true + @State private var isFullscreen = false + @State private var mpvWidget: OpaquePointer? + @State private var tasks = PlayerTasks() public init(playerState: PlayerState, onClose: @escaping () -> Void) { self.playerState = playerState @@ -49,7 +57,8 @@ public struct PlayerView: View { url: playerState.streamURL.absoluteString, isPlaying: $isPlaying, position: $position, - duration: $duration + duration: $duration, + onWidgetCreated: { ptr in mpvWidget = ptr } ) .vexpand(true) .hexpand(true) @@ -105,8 +114,8 @@ public struct PlayerView: View { } private func stopPlayback() { - progressTask?.cancel() - controlsTask?.cancel() + tasks.progress?.cancel() + tasks.controls?.cancel() Task { try? await client.reportPlaybackStopped( info: .init( @@ -120,7 +129,7 @@ public struct PlayerView: View { } private func startProgressTimer() { - progressTask = Task { + tasks.progress = Task { while !Task.isCancelled { try? await Task.sleep(for: .seconds(10)) try? await client.reportPlaybackProgress( @@ -141,22 +150,23 @@ public struct PlayerView: View { } } - @State private var progressTask: Task? - @State private var controlsTask: Task? - // MARK: - Seeking private func seekBy(_ seconds: Double) { + guard let mpvWidget else { return } + mpv_widget_seek_relative(mpvWidget, seconds) } private func seekTo(_ position: Double) { + guard let mpvWidget else { return } + mpv_widget_seek_absolute(mpvWidget, position) } // MARK: - Controls visibility private func startControlsTimer() { - controlsTask?.cancel() - controlsTask = Task { + tasks.controls?.cancel() + tasks.controls = Task { try? await Task.sleep(for: .seconds(3)) showControls = false } @@ -165,6 +175,15 @@ public struct PlayerView: View { // MARK: - Fullscreen private func toggleFullscreen() { + guard let widget = mpvWidget else { return } + let root = gtk_widget_get_root(widget.cast()) + guard let root else { return } + if isFullscreen { + gtk_window_unfullscreen(root.cast()) + } else { + gtk_window_fullscreen(root.cast()) + } + isFullscreen.toggle() } } @@ -186,11 +205,14 @@ struct VideoPlayerWidget: Widget { @Binding var position: Double @Binding var duration: Double - init(url: String?, isPlaying: Binding, position: Binding, duration: Binding) { + var onWidgetCreated: ((OpaquePointer) -> Void)? + + init(url: String?, isPlaying: Binding, position: Binding, duration: Binding, onWidgetCreated: ((OpaquePointer) -> Void)? = nil) { self.url = url self._isPlaying = isPlaying self._position = position self._duration = duration + self.onWidgetCreated = onWidgetCreated } func container(data: WidgetData, type: Data.Type) -> ViewStorage @@ -255,6 +277,10 @@ struct VideoPlayerWidget: Widget { GConnectFlags(rawValue: 1)) } + if let ptr = storage.opaquePointer, let onWidgetCreated { + onWidgetCreated(ptr) + } + for function in appearFunctions { function(storage, data) } diff --git a/Sources/LuminateUI/Components/SeekBar.swift b/Sources/LuminateUI/Components/SeekBar.swift index 0f8be54..10f4717 100644 --- a/Sources/LuminateUI/Components/SeekBar.swift +++ b/Sources/LuminateUI/Components/SeekBar.swift @@ -61,7 +61,7 @@ public struct SeekBar: Widget { public func container(data: WidgetData, type: Data.Type) -> ViewStorage where Data: ViewRenderData { let widgetPtr = gtk_scale_new_with_range( - GtkOrientation(rawValue: 0)!, + .GTK_ORIENTATION_HORIZONTAL, range.lowerBound, range.upperBound, 1.0