Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Sources/MikuCodeApp/Companion/AppPresentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,27 @@ enum DisplayFrameSelector {
}
}

/// Decides where the reused workspace window must be placed before each
/// presentation. The window is created once, but the idle Miku panel can move
/// to another display between opens (screen-parameter changes rebind it to the
/// display under the pointer) — without this, the workspace reopens on
/// whichever display it was first created on.
enum WorkspaceDisplayPlacement {
/// Returns the frame the window should adopt, or nil to keep its current
/// frame (same display — preserves the user's manual size/position — or
/// mid-full-screen, where reframing fights the system transition).
static func frameForPresentation(
windowScreenFrame: CGRect?,
targetScreenFrame: CGRect,
targetVisibleFrame: CGRect,
isFullScreen: Bool
) -> CGRect? {
guard !isFullScreen else { return nil }
guard windowScreenFrame != targetScreenFrame else { return nil }
return targetVisibleFrame
}
}

enum PresentationLayout {
static func metrics(in size: CGSize) -> PresentationLayoutMetrics {
let width = max(1, size.width)
Expand Down
15 changes: 15 additions & 0 deletions Sources/MikuCodeApp/Companion/MikuPanelCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,28 @@ final class MikuPanelCoordinator: NSObject, NSWindowDelegate {
guard presentation.requestOpen() else { return }
refreshScreenFrameFromPanel()
let window = ensureWorkspaceWindow()
moveWorkspaceWindowToPanelDisplay(window)
workspacePresentedAt = ProcessInfo.processInfo.systemUptime
panel.orderOut(nil)
guard ordersFront else { return }
NSApplication.shared.activate(ignoringOtherApps: true)
window.makeKeyAndOrderFront(nil)
}

/// The workspace window is created once and reused across opens; present it
/// on the display the idle Miku currently occupies, not wherever it was
/// created.
private func moveWorkspaceWindowToPanelDisplay(_ window: WorkspaceWindow) {
guard let targetScreen = panel.screen ?? NSScreen.main else { return }
guard let frame = WorkspaceDisplayPlacement.frameForPresentation(
windowScreenFrame: window.screen?.frame,
targetScreenFrame: targetScreen.frame,
targetVisibleFrame: targetScreen.visibleFrame,
isFullScreen: window.styleMask.contains(.fullScreen)
) else { return }
window.setFrame(frame, display: true)
}

/// Close requested by clicking the workspace Miku. The click that opened the
/// workspace lands on the same screen region the Miku close target appears
/// in, and stale/replayed events from that press can fire the close button
Expand Down
49 changes: 49 additions & 0 deletions Tests/MikuCodeAppTests/MikuCodeAppTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,55 @@ final class PresentationLayoutTests: XCTestCase {
)
}

func testWorkspacePresentsOnTheDisplayTheIdleMikuOccupies() {
let builtIn = CGRect(x: 0, y: 0, width: 1_728, height: 1_117)
let external = CGRect(x: 1_728, y: -152, width: 1_920, height: 1_080)
let externalVisible = external.insetBy(dx: 0, dy: 12)

// Window created on the built-in display, Miku now on the external one:
// the presentation must move it to the external display's visible frame.
XCTAssertEqual(
WorkspaceDisplayPlacement.frameForPresentation(
windowScreenFrame: builtIn,
targetScreenFrame: external,
targetVisibleFrame: externalVisible,
isFullScreen: false
),
externalVisible
)

// A window that has never been on screen also moves to Miku's display.
XCTAssertEqual(
WorkspaceDisplayPlacement.frameForPresentation(
windowScreenFrame: nil,
targetScreenFrame: external,
targetVisibleFrame: externalVisible,
isFullScreen: false
),
externalVisible
)

// Same display: keep the user's manually adjusted frame.
XCTAssertNil(
WorkspaceDisplayPlacement.frameForPresentation(
windowScreenFrame: external,
targetScreenFrame: external,
targetVisibleFrame: externalVisible,
isFullScreen: false
)
)

// Never fight an in-flight full-screen transition.
XCTAssertNil(
WorkspaceDisplayPlacement.frameForPresentation(
windowScreenFrame: builtIn,
targetScreenFrame: external,
targetVisibleFrame: externalVisible,
isFullScreen: true
)
)
}

func testAdaptiveMetricsKeepTerminalLargeAndMikuFarRight() {
let displaySizes = [
CGSize(width: 1_024, height: 768),
Expand Down