feat: default compact mode to the top-right corner

This commit is contained in:
2026-10-10 22:07:07 +02:00
parent a6bebb05c0
commit 25d674d84d
3 changed files with 10 additions and 10 deletions
+8 -8
View File
@@ -69,10 +69,10 @@ struct CompactWindowChecks {
let usableScreen = NSRect(x: -1_200, y: 50, width: 1_200, height: 800) let usableScreen = NSRect(x: -1_200, y: 50, width: 1_200, height: 800)
let landscape = CompactWindowController.initialFrame(aspect: 16.0 / 9.0, visibleFrame: usableScreen) let landscape = CompactWindowController.initialFrame(aspect: 16.0 / 9.0, visibleFrame: usableScreen)
try require(sameFrame(landscape, NSRect(x: -336, y: 66, width: 320, height: 180)), try require(sameFrame(landscape, NSRect(x: -336, y: 654, width: 320, height: 180)),
"16:9 enters at bottom right with 320pt width and 16pt inset") "16:9 enters at top right with 320pt width and 16pt inset")
let portrait = CompactWindowController.initialFrame(aspect: 9.0 / 16.0, visibleFrame: usableScreen) let portrait = CompactWindowController.initialFrame(aspect: 9.0 / 16.0, visibleFrame: usableScreen)
try require(sameFrame(portrait, NSRect(x: -336, y: 66, width: 320, height: 5_120.0 / 9.0)), try require(sameFrame(portrait, NSRect(x: -336, y: 834 - 5_120.0 / 9.0, width: 320, height: 5_120.0 / 9.0)),
"portrait preserves exact aspect when it fits") "portrait preserves exact aspect when it fits")
for aspect in [16.0 / 9.0, 9.0 / 16.0, 100.0, 0.01] { for aspect in [16.0 / 9.0, 9.0 / 16.0, 100.0, 0.01] {
let frame = CompactWindowController.initialFrame(aspect: aspect, visibleFrame: usableScreen) let frame = CompactWindowController.initialFrame(aspect: aspect, visibleFrame: usableScreen)
@@ -84,12 +84,12 @@ struct CompactWindowChecks {
try require(frame.minX >= usableScreen.minX + 16 && frame.minY >= usableScreen.minY + 16 try require(frame.minX >= usableScreen.minX + 16 && frame.minY >= usableScreen.minY + 16
&& frame.maxX <= usableScreen.maxX - 16 && frame.maxY <= usableScreen.maxY - 16, && frame.maxX <= usableScreen.maxX - 16 && frame.maxY <= usableScreen.maxY - 16,
"aspect \(aspect) stays inside inset usable screen") "aspect \(aspect) stays inside inset usable screen")
try require(close(frame.maxX, usableScreen.maxX - 16) && close(frame.minY, usableScreen.minY + 16), try require(close(frame.maxX, usableScreen.maxX - 16) && close(frame.maxY, usableScreen.maxY - 16),
"aspect \(aspect) stays anchored bottom right") "aspect \(aspect) stays anchored top right")
} }
let smallScreen = NSRect(x: 0, y: 0, width: 300, height: 400) let smallScreen = NSRect(x: 0, y: 0, width: 300, height: 400)
let bounded = CompactWindowController.initialFrame(aspect: 16.0 / 9.0, visibleFrame: smallScreen) let bounded = CompactWindowController.initialFrame(aspect: 16.0 / 9.0, visibleFrame: smallScreen)
try require(sameFrame(bounded, NSRect(x: 16, y: 16, width: 268, height: 150.75)), try require(sameFrame(bounded, NSRect(x: 16, y: 233.25, width: 268, height: 150.75)),
"target width shrinks to usable bounds without distorting ordinary aspect") "target width shrinks to usable bounds without distorting ordinary aspect")
} }
@@ -171,9 +171,9 @@ struct CompactWindowChecks {
let visible = (window.screen ?? screen).visibleFrame let visible = (window.screen ?? screen).visibleFrame
try require(close(window.frame.maxX, visible.maxX - 16) try require(close(window.frame.maxX, visible.maxX - 16)
&& close(window.frame.minY, visible.minY + 16) && close(window.frame.maxY, visible.maxY - 16)
&& close(window.frame.width / window.frame.height, 16.0 / 9.0), && close(window.frame.width / window.frame.height, 16.0 / 9.0),
"actual compact window enters at inset bottom right with playback aspect") "actual compact window enters at inset top right with playback aspect")
let nearEdge = NSRect(x: visible.minX + 26, y: visible.minY + 27, let nearEdge = NSRect(x: visible.minX + 26, y: visible.minY + 27,
width: window.frame.width, height: window.frame.height) width: window.frame.width, height: window.frame.height)
window.setFrame(nearEdge, display: false) window.setFrame(nearEdge, display: false)
+1 -1
View File
@@ -22,7 +22,7 @@ The layout follows [IINA's video-first window](https://iina.io/img/screenshot.pn
Opening video fits the window to its display aspect ratio, including non-square pixels, and constrains subsequent window resizing. Fullscreen retains the normal letterboxed renderer; audio-only playback and close release the aspect constraint. Extreme ratios that cannot fit both the screen and usable controls keep a freely resizable letterboxed window. Opening video fits the window to its display aspect ratio, including non-square pixels, and constrains subsequent window resizing. Fullscreen retains the normal letterboxed renderer; audio-only playback and close release the aspect constraint. Extreme ratios that cannot fit both the screen and usable controls keep a freely resizable letterboxed window.
**Compact mode** uses the same window, Metal view, and playback session—not a second window or macOS system PiP. Use the PiP-shaped button in the normal playback controls, **⌃⌘P**, or **Playback Actions → Enter Compact Mode**. The compact-mode button replaces the fullscreen button; fullscreen remains available through its menu, keyboard shortcuts, and double-clicking normal-mode video. Compact mode starts at the current screen's bottom-right corner with a 16-point inset and a preferred width of 320 points, preserving display aspect where usable screen bounds and the 160 × 90-point minimum allow; extreme ratios retain letterboxing. Compact mode floats above ordinary windows, joins all Spaces, and is configured to join eligible other-app fullscreen spaces and Stage Manager sets. Small hover controls provide play/pause, restore, and Playback Actions; keyboard playback controls remain available. **Compact mode** uses the same window, Metal view, and playback session—not a second window or macOS system PiP. Use the PiP-shaped button in the normal playback controls, **⌃⌘P**, or **Playback Actions → Enter Compact Mode**. The compact-mode button replaces the fullscreen button; fullscreen remains available through its menu, keyboard shortcuts, and double-clicking normal-mode video. Compact mode starts at the current screen's top-right corner with a 16-point inset and a preferred width of 320 points, preserving display aspect where usable screen bounds and the 160 × 90-point minimum allow; extreme ratios retain letterboxing. Compact mode floats above ordinary windows, joins all Spaces, and is configured to join eligible other-app fullscreen spaces and Stage Manager sets. Small hover controls provide play/pause, restore, and Playback Actions; keyboard playback controls remain available.
Drag the compact video to move the window; on release, each axis snaps independently when within 24 points of an inset usable-screen edge. **Snap to Screen Edges** is enabled by default and remembers its toggle across launches. Disabling snapping permits free placement. Exiting compact mode restores the previous normal geometry and window behavior, including keep-on-top; entry from fullscreen waits for the native exit animation, and restoring that session returns to fullscreen. Double-clicking compact video also restores the previous mode. Video replacement updates the compact aspect; audio-only replacement, playback errors, and closing the file leave compact mode. Closing the window still quits the app. Drag the compact video to move the window; on release, each axis snaps independently when within 24 points of an inset usable-screen edge. **Snap to Screen Edges** is enabled by default and remembers its toggle across launches. Disabling snapping permits free placement. Exiting compact mode restores the previous normal geometry and window behavior, including keep-on-top; entry from fullscreen waits for the native exit animation, and restoring that session returns to fullscreen. Double-clicking compact video also restores the previous mode. Video replacement updates the compact aspect; audio-only replacement, playback errors, and closing the file leave compact mode. Closing the window still quits the app.
+1 -1
View File
@@ -333,7 +333,7 @@ final class CompactWindowController: NSObject {
nonisolated static func initialFrame(aspect: Double, visibleFrame: NSRect) -> NSRect { nonisolated static func initialFrame(aspect: Double, visibleFrame: NSRect) -> NSRect {
let usable = usableFrame(visibleFrame) let usable = usableFrame(visibleFrame)
let size = compactSize(aspect: aspect, width: 320, available: usable.size) let size = compactSize(aspect: aspect, width: 320, available: usable.size)
return NSRect(x: usable.maxX - size.width, y: usable.minY, width: size.width, height: size.height) return NSRect(x: usable.maxX - size.width, y: usable.maxY - size.height, width: size.width, height: size.height)
} }
nonisolated static func snappedFrame(_ frame: NSRect, visibleFrame: NSRect) -> NSRect { nonisolated static func snappedFrame(_ frame: NSRect, visibleFrame: NSRect) -> NSRect {