Skip to content

Commit 7397356

Browse files
authored
fix: restore main window when video is open (#56)
1 parent 4eb877c commit 7397356

File tree

3 files changed

+80
-34
lines changed

3 files changed

+80
-34
lines changed

App/AppDelegate.swift

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
1111
/// Set by KasetApp after initialization.
1212
weak var playerService: PlayerService?
1313

14+
/// Reference to the main window for reliable reopen behavior.
15+
/// Using strong reference to prevent deallocation when window is hidden.
16+
private var mainWindow: NSWindow?
17+
1418
func applicationDidFinishLaunching(_: Notification) {
1519
// Set up notification center delegate to show notifications in foreground
1620
UNUserNotificationCenter.current().delegate = self
@@ -27,14 +31,26 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
2731
}
2832
}
2933

34+
func applicationDidBecomeActive(_: Notification) {
35+
// When app becomes active (e.g., dock icon clicked), ensure main window is visible.
36+
// This handles the case where video window is visible but main window is hidden.
37+
self.showMainWindowIfNeeded()
38+
}
39+
3040
private func setupWindowDelegate() {
3141
for window in NSApplication.shared.windows where window.canBecomeMain {
42+
// Skip if this is the video window (has specific identifier)
43+
if window.identifier?.rawValue == AccessibilityID.VideoWindow.container {
44+
continue
45+
}
3246
window.delegate = self
3347
// Enable automatic window frame persistence using autosave name
3448
// This ensures window size/position is restored across app launches
3549
if window.frameAutosaveName.isEmpty {
3650
window.setFrameAutosaveName("KasetMainWindow")
3751
}
52+
// Store reference to main window for reliable reopen
53+
self.mainWindow = window
3854
}
3955
}
4056

@@ -111,15 +127,42 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
111127
}
112128

113129
/// Handle reopen (clicking dock icon) when all windows are closed.
114-
func applicationShouldHandleReopen(_: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
115-
if !flag {
116-
// Reopen the main window if it was closed
117-
for window in NSApplication.shared.windows where window.canBecomeMain {
130+
func applicationShouldHandleReopen(_: NSApplication, hasVisibleWindows _: Bool) -> Bool {
131+
// Show main window when dock icon is clicked
132+
self.showMainWindowIfNeeded()
133+
return true
134+
}
135+
136+
/// Shows the main window if it's not visible.
137+
private func showMainWindowIfNeeded() {
138+
// Try stored reference first
139+
if let mainWindow {
140+
if !mainWindow.isVisible {
141+
mainWindow.makeKeyAndOrderFront(nil)
142+
}
143+
return
144+
}
145+
146+
// Fallback: find main window by frameAutosaveName
147+
for window in NSApplication.shared.windows where window.frameAutosaveName == "KasetMainWindow" {
148+
self.mainWindow = window
149+
if !window.isVisible {
118150
window.makeKeyAndOrderFront(nil)
119-
return true
120151
}
152+
return
153+
}
154+
155+
// Last resort: find any main-capable window that's not the video window
156+
for window in NSApplication.shared.windows where window.canBecomeMain {
157+
if window.identifier?.rawValue == AccessibilityID.VideoWindow.container {
158+
continue
159+
}
160+
self.mainWindow = window
161+
if !window.isVisible {
162+
window.makeKeyAndOrderFront(nil)
163+
}
164+
return
121165
}
122-
return true
123166
}
124167
}
125168

App/KasetApp.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,34 @@ struct KasetApp: App {
263263
}
264264
.keyboardShortcut("k", modifiers: .command)
265265
}
266+
267+
// Window menu - show main window
268+
CommandGroup(after: .windowArrangement) {
269+
Button("Kaset") {
270+
self.showMainWindow()
271+
}
272+
.keyboardShortcut("0", modifiers: .command)
273+
}
274+
}
275+
}
276+
277+
/// Shows the main window.
278+
private func showMainWindow() {
279+
// Find and show the main window
280+
for window in NSApplication.shared.windows where window.frameAutosaveName == "KasetMainWindow" {
281+
window.makeKeyAndOrderFront(nil)
282+
NSApplication.shared.activate(ignoringOtherApps: true)
283+
return
284+
}
285+
286+
// Fallback: find any main-capable window that's not the video window
287+
for window in NSApplication.shared.windows where window.canBecomeMain {
288+
if window.identifier?.rawValue == AccessibilityID.VideoWindow.container {
289+
continue
290+
}
291+
window.makeKeyAndOrderFront(nil)
292+
NSApplication.shared.activate(ignoringOtherApps: true)
293+
return
266294
}
267295
}
268296

App/VideoWindowController.swift

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ final class VideoWindowController {
1111

1212
private var window: NSWindow?
1313
private var hostingView: NSHostingView<AnyView>?
14-
private let logger = DiagnosticsLogger.player
1514

1615
/// Reference to PlayerService to sync showVideo state
1716
private weak var playerService: PlayerService?
@@ -35,8 +34,6 @@ final class VideoWindowController {
3534
playerService: PlayerService,
3635
webKitManager: WebKitManager
3736
) {
38-
self.logger.debug("VideoWindowController.show() called")
39-
4037
// Store reference to sync state on close
4138
self.playerService = playerService
4239

@@ -45,16 +42,13 @@ final class VideoWindowController {
4542

4643
if let existingWindow = self.window {
4744
// Window exists - just bring it to front
48-
self.logger.debug("Window already exists, bringing to front")
4945
self.isClosing = false // Reset in case of interrupted close
5046
existingWindow.makeKeyAndOrderFront(nil)
5147
// Ensure video mode is active
5248
SingletonPlayerWebView.shared.updateDisplayMode(.video)
5349
return
5450
}
5551

56-
self.logger.info("Creating new video window")
57-
5852
let contentView = VideoPlayerWindow()
5953
.environment(playerService)
6054
.environment(webKitManager)
@@ -101,27 +95,16 @@ final class VideoWindowController {
10195
)
10296

10397
// Update WebView display mode for video
104-
self.logger.info("Calling updateDisplayMode(.video)")
10598
SingletonPlayerWebView.shared.updateDisplayMode(.video)
10699
}
107100

108101
/// Closes the video window programmatically (called when showVideo becomes false).
109102
func close() {
110-
self.logger.debug("VideoWindowController.close() called")
111-
112103
// Prevent re-entrant calls
113-
guard !self.isClosing else {
114-
self.logger.debug("Already closing, skipping")
115-
return
116-
}
117-
118-
guard let window = self.window else {
119-
self.logger.debug("No window to close")
120-
return
121-
}
104+
guard !self.isClosing else { return }
105+
guard let window = self.window else { return }
122106

123107
self.isClosing = true
124-
self.logger.info("Closing video window")
125108

126109
// Remove observer before closing to prevent windowWillClose from firing
127110
NotificationCenter.default.removeObserver(self, name: NSWindow.willCloseNotification, object: window)
@@ -139,13 +122,8 @@ final class VideoWindowController {
139122

140123
/// Called when window is closed via the red X button.
141124
@objc private func windowWillClose(_ notification: Notification) {
142-
self.logger.info("windowWillClose notification received")
143-
144125
// Prevent re-entrant calls
145-
guard !self.isClosing else {
146-
self.logger.debug("Already closing, skipping windowWillClose")
147-
return
148-
}
126+
guard !self.isClosing else { return }
149127
self.isClosing = true
150128

151129
// Update corner based on final position
@@ -160,15 +138,12 @@ final class VideoWindowController {
160138
// Sync PlayerService state - this handles close via red button
161139
// This will trigger MainWindow.onChange which calls close(), but isClosing prevents re-entry
162140
if self.playerService?.showVideo == true {
163-
self.logger.debug("Syncing playerService.showVideo to false")
164141
self.playerService?.showVideo = false
165142
}
166143
}
167144

168145
/// Shared cleanup logic for both close paths.
169146
private func performCleanup() {
170-
self.logger.debug("performCleanup called")
171-
172147
// Clear grace period
173148
self.playerService?.videoWindowDidClose()
174149

0 commit comments

Comments
 (0)