diff --git a/Checks/app-startup.py b/Checks/app-startup.py index c4173e3..9375728 100644 --- a/Checks/app-startup.py +++ b/Checks/app-startup.py @@ -48,7 +48,7 @@ def check_queue_drag(root, app, fixtures): if result.returncode != 0 or not report.get("passed"): log = diagnostics.read_text(errors="replace") if diagnostics.exists() else "" raise RuntimeError(f"native queue drag failed: {report}; {result.stdout}{result.stderr}{log}") - print("PASS native queue drag: slide down/up, hold without pointer events, release to nearest slot, current item preserved") + print("PASS native queue: drag down/up without changing playback, release to nearest slot, single click plays without reordering") def check_window_layout(root, app, fixtures): diff --git a/Checks/app-startup.swift b/Checks/app-startup.swift index 2d0614f..3d0d57e 100644 --- a/Checks/app-startup.swift +++ b/Checks/app-startup.swift @@ -35,6 +35,7 @@ private enum QueueDragCheck { static func run(result: URL) { resultURL = result + _ = NSApplication.shared _ = NSEvent.addLocalMonitorForEvents(matching: [.leftMouseDown, .leftMouseDragged, .leftMouseUp]) { NSLog("[DEBUG-queue-drag] received %@", String(describing: $0)) return $0 @@ -42,6 +43,8 @@ private enum QueueDragCheck { timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { _ in MainActor.assumeIsolated { advance() } } + // Keep injecting mouse events while SwiftUI tracks a pressed row. + RunLoop.main.add(timer!, forMode: .common) terebiAppMain() } @@ -68,13 +71,17 @@ private enum QueueDragCheck { static func advance() { let model = PlayerModel.shared - guard ContinuousClock.now < deadline else { finish("Queue drag did not complete at step \(step)"); return } + guard ContinuousClock.now < deadline else { + finish("Queue interaction did not complete at step \(step): status \(model.state.status), media \(model.fileName ?? "none"), windows \(NSApp.windows.count)") + return + } guard model.canTogglePlayback, let window = NSApp.windows.first(where: { $0.canBecomeMain && !($0 is NSPanel) }), let content = window.contentView else { return } if step == 0 { model.setVolume(0) - model.addToQueue(["B", "C"].map { URL(fileURLWithPath: "/queue-drag-check/\($0).mp4") }) + let fixtures = model.queue.currentURL!.deletingLastPathComponent() + model.addToQueue(["av.mp4", "cache.mp4"].map { fixtures.appendingPathComponent($0) }) current = model.queue.currentID original = model.queue.items.map(\.id) NSApp.activate(ignoringOtherApps: true) @@ -124,6 +131,16 @@ private enum QueueDragCheck { guard model.queue.items.map(\.id) == original, model.queue.currentID == current else { finish("Stationary upward release did not restore the original order"); return } + let secondRow = NSPoint(x: start.x, y: start.y - 60) + mouse(.leftMouseDown, at: secondRow, in: window) + } else if step == 28 { + mouse(.leftMouseUp, at: NSPoint(x: start.x, y: start.y - 60), in: window) + } else if step == 35 { + guard model.queue.currentID == original[1], + model.queue.items.map(\.id) == original, + model.state.status == PLAYER_PLAYING else { + finish("Single click did not play the second row without reordering"); return + } finish() return } diff --git a/README.md b/README.md index bd8a9a7..f921a5f 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Active playback prevents idle system sleep. Local video also keeps the display a ### Local-file queue -Use the **Queue** icon immediately left of the compact-mode button in the playback controls, or **View → Show Queue**, to show the editable sidebar. The **+** button beside the queue count opens **Add to Queue…**; it and dropping files onto the sidebar append without replacing the queue or interrupting the current item. Drag rows to reorder, remove them with the minus button, and double-click a row to play it. The current item is marked; context menus also offer Play, Remove and Move Up/Down. +Use the **Queue** icon immediately left of the compact-mode button in the playback controls, or **View → Show Queue**, to show the editable sidebar. The **+** button beside the queue count opens **Add to Queue…**; it and dropping files onto the sidebar append without replacing the queue or interrupting the current item. Drag rows to reorder, remove them with the minus button, and click a row to play it. Dragging does not change playback. The current item is marked; context menus also offer Play, Remove and Move Up/Down. The fixed bottom row holds the queue label/count and **+** on the left, with previous/next controls on the right. Its text matches the rows' 13-point medium-weight filenames, and its icons match the rows' 12-point icons. The **+** is a 20-point rounded-square button with an accent-tinted background and an 8-point gap after the queue count; navigation buttons retain their 24-point hit areas. diff --git a/Sources/App/TerebiApp.swift b/Sources/App/TerebiApp.swift index 6b426be..0e50f7a 100644 --- a/Sources/App/TerebiApp.swift +++ b/Sources/App/TerebiApp.swift @@ -2454,8 +2454,8 @@ private struct PlayerContent: View { } } .contentShape(Rectangle()) - .onTapGesture(count: 2) { model.selectQueueItem(item.id) } - .help(item.url.path + "\nDouble-click to play; drag to reorder.") + .onTapGesture { model.selectQueueItem(item.id) } + .help(item.url.path + "\nClick to play; drag to reorder.") .contextMenu { Button("Play") { model.selectQueueItem(item.id) } Button("Remove from Queue") { model.removeQueueItem(item.id) }