fix: expand window outward for playback queue
This commit is contained in:
@@ -140,6 +140,15 @@ struct SystemPlaybackChecks {
|
|||||||
window.isReleasedWhenClosed = false
|
window.isReleasedWhenClosed = false
|
||||||
defer { window.close() }
|
defer { window.close() }
|
||||||
model.attachWindow(window)
|
model.attachWindow(window)
|
||||||
|
let videoFrame = window.frame
|
||||||
|
model.showsQueue = true
|
||||||
|
require(abs(window.frame.width - videoFrame.width - 261) < 0.01 &&
|
||||||
|
abs(window.frame.height - videoFrame.height) < 0.01,
|
||||||
|
"opening queue grows window without shrinking video")
|
||||||
|
model.showsQueue = false
|
||||||
|
require(abs(window.frame.width - videoFrame.width) < 0.01 &&
|
||||||
|
abs(window.frame.height - videoFrame.height) < 0.01,
|
||||||
|
"closing queue restores video-only window size")
|
||||||
model.open(directory.appendingPathComponent("anamorphic.mp4"))
|
model.open(directory.appendingPathComponent("anamorphic.mp4"))
|
||||||
require(model.isBusy && model.perform(.enterCompact) && model.isCompact,
|
require(model.isBusy && model.perform(.enterCompact) && model.isCompact,
|
||||||
"compact entry is available before native media opening acknowledges")
|
"compact entry is available before native media opening acknowledges")
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ Active playback prevents idle system sleep. Local video also keeps the display a
|
|||||||
|
|
||||||
Use the titlebar's **Queue** icon or **View → Show Queue** to show the editable sidebar. **Add to Queue…** 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 titlebar's **Queue** icon or **View → Show Queue** to show the editable sidebar. **Add to Queue…** 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.
|
||||||
|
|
||||||
|
In a normal window, showing the queue expands the window outward while preserving the video's size; hiding it removes that extra width. The window shifts left when necessary near the screen's right edge. Fullscreen keeps its fixed bounds.
|
||||||
|
|
||||||
Playback advances automatically and stops at the end, without wrapping. **Previous Queue Item** and **Next Queue Item** are available in the sidebar and Playback menu and are disabled at their respective ends. Reordering or removing another item does not interrupt playback; removing the current item plays its next neighbor, or stops if there is none. A playback error stays on that item rather than skipping it.
|
Playback advances automatically and stops at the end, without wrapping. **Previous Queue Item** and **Next Queue Item** are available in the sidebar and Playback menu and are disabled at their respective ends. Reordering or removing another item does not interrupt playback; removing the current item plays its next neighbor, or stops if there is none. A playback error stays on that item rather than skipping it.
|
||||||
|
|
||||||
The queue contains local files only and lasts until it is replaced, closed or the app quits; it has no persistence, shuffle, repeat or playlist-file import. Opening a YouTube video or resolved stream clears it. The sidebar is hidden in compact mode; Playback-menu queue navigation remains available. Sidebar appearance, drag/drop interaction and physical-receiver queue transitions still need manual verification.
|
The queue contains local files only and lasts until it is replaced, closed or the app quits; it has no persistence, shuffle, repeat or playlist-file import. Opening a YouTube video or resolved stream clears it. The sidebar is hidden in compact mode; Playback-menu queue navigation remains available. Sidebar appearance, drag/drop interaction and physical-receiver queue transitions still need manual verification.
|
||||||
|
|||||||
@@ -32,7 +32,22 @@ final class PlayerModel: ObservableObject {
|
|||||||
@Published private(set) var fileIcon: NSImage?
|
@Published private(set) var fileIcon: NSImage?
|
||||||
@Published private(set) var commandError: String?
|
@Published private(set) var commandError: String?
|
||||||
@Published private(set) var queue = PlaybackQueue()
|
@Published private(set) var queue = PlaybackQueue()
|
||||||
@Published var showsQueue = false
|
@Published var showsQueue = false {
|
||||||
|
didSet {
|
||||||
|
guard showsQueue != oldValue, let window, !isFullScreen,
|
||||||
|
!isCompact, !compactWindow.isTransitioning else { return }
|
||||||
|
window.aspectRatio = .zero
|
||||||
|
var frame = window.frame
|
||||||
|
frame.size.width += showsQueue ? 261 : -261
|
||||||
|
if let screen = window.screen?.visibleFrame {
|
||||||
|
frame.origin.x = max(screen.minX, min(frame.minX, screen.maxX - frame.width))
|
||||||
|
}
|
||||||
|
window.setFrame(frame, display: true)
|
||||||
|
if !showsQueue, state.has_video, state.video_aspect_ratio > 0 {
|
||||||
|
window.aspectRatio = NSSize(width: state.video_aspect_ratio, height: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private var queuePlaybackRevision: UInt64?
|
private var queuePlaybackRevision: UInt64?
|
||||||
private var queueCompletionHandled = false
|
private var queueCompletionHandled = false
|
||||||
private var queueCastDevice: CastDevice?
|
private var queueCastDevice: CastDevice?
|
||||||
@@ -968,14 +983,15 @@ final class PlayerModel: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let screen = (window.screen ?? NSScreen.main)?.visibleFrame ?? window.frame
|
let screen = (window.screen ?? NSScreen.main)?.visibleFrame ?? window.frame
|
||||||
let width = min(window.frame.width, screen.width, screen.height * aspect)
|
let sidebarWidth: CGFloat = showsQueue ? 261 : 0
|
||||||
let size = NSSize(width: width, height: width / aspect)
|
let width = min(window.frame.width - sidebarWidth, screen.width - sidebarWidth, screen.height * aspect)
|
||||||
|
let size = NSSize(width: width + sidebarWidth, height: width / aspect)
|
||||||
// ponytail: extreme ratios retain letterboxing; compact controls if exact fitting is needed.
|
// ponytail: extreme ratios retain letterboxing; compact controls if exact fitting is needed.
|
||||||
guard size.width >= window.minSize.width, size.height >= window.minSize.height else {
|
guard size.width >= window.minSize.width, size.height >= window.minSize.height else {
|
||||||
window.aspectRatio = .zero
|
window.aspectRatio = .zero
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
window.aspectRatio = NSSize(width: aspect, height: 1)
|
window.aspectRatio = showsQueue ? .zero : NSSize(width: aspect, height: 1)
|
||||||
let frame = NSRect(
|
let frame = NSRect(
|
||||||
x: min(max(window.frame.midX - size.width / 2, screen.minX), screen.maxX - size.width),
|
x: min(max(window.frame.midX - size.width / 2, screen.minX), screen.maxX - size.width),
|
||||||
y: min(max(window.frame.midY - size.height / 2, screen.minY), screen.maxY - size.height),
|
y: min(max(window.frame.midY - size.height / 2, screen.minY), screen.maxY - size.height),
|
||||||
|
|||||||
Reference in New Issue
Block a user