From 8a74938b98c4690e660ff94f2b50c596a6c5e356 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Thu, 6 Jan 2022 16:35:45 +0100 Subject: [PATCH] Improve windows handling on macOS --- Model/NavigationModel.swift | 2 +- Model/Player/PlayerModel.swift | 18 +++++++---- Shared/OpenURLHandler.swift | 4 +-- Shared/YatteeApp.swift | 23 +++++++++---- Yattee.xcodeproj/project.pbxproj | 8 ++--- macOS/OpenWindow.swift | 40 ----------------------- macOS/Windows.swift | 55 ++++++++++++++++++++++++++++++++ 7 files changed, 91 insertions(+), 59 deletions(-) delete mode 100644 macOS/OpenWindow.swift create mode 100644 macOS/Windows.swift diff --git a/Model/NavigationModel.swift b/Model/NavigationModel.swift index aaa4809e..11a85316 100644 --- a/Model/NavigationModel.swift +++ b/Model/NavigationModel.swift @@ -50,7 +50,7 @@ final class NavigationModel: ObservableObject { ) { let recent = RecentItem(from: channel) #if os(macOS) - OpenWindow.main.open() + Windows.main.open() #else player.hide() #endif diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index 0de6af12..e312ee4d 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -33,7 +33,7 @@ final class PlayerModel: ObservableObject { @Published var streamSelection: Stream? { didSet { rebuildTVMenu() } } @Published var queue = [PlayerQueueItem]() { didSet { Defaults[.queue] = queue } } - @Published var currentItem: PlayerQueueItem! + @Published var currentItem: PlayerQueueItem! { didSet { updateWindowTitle() }} @Published var historyVideos = [Video]() @Published var preservedTime: CMTime? @@ -103,13 +103,13 @@ final class PlayerModel: ObservableObject { func show() { guard !presentingPlayer else { #if os(macOS) - OpenWindow.player.focus() + Windows.player.focus() #endif return } #if os(macOS) - OpenWindow.player.open() - OpenWindow.player.focus() + Windows.player.open() + Windows.player.focus() #endif presentingPlayer = true } @@ -122,9 +122,9 @@ final class PlayerModel: ObservableObject { func togglePlayer() { #if os(macOS) if !presentingPlayer { - OpenWindow.player.open() + Windows.player.open() } - OpenWindow.player.focus() + Windows.player.focus() #else if presentingPlayer { hide() @@ -804,6 +804,12 @@ final class PlayerModel: ObservableObject { } #endif + func updateWindowTitle() { + #if os(macOS) + Windows.player.window?.title = windowTitle + #endif + } + #if os(macOS) var windowTitle: String { currentVideo.isNil ? "Not playing" : "\(currentVideo!.title) - \(currentVideo!.author)" diff --git a/Shared/OpenURLHandler.swift b/Shared/OpenURLHandler.swift index 556d04a4..582addee 100644 --- a/Shared/OpenURLHandler.swift +++ b/Shared/OpenURLHandler.swift @@ -14,7 +14,7 @@ struct OpenURLHandler { } #if os(macOS) - guard url.host != OpenWindow.player.location else { + guard url.host != Windows.player.location else { return } #endif @@ -28,7 +28,7 @@ struct OpenURLHandler { } #if os(macOS) - OpenWindow.main.open() + Windows.main.open() #endif accounts.api.video(id).load().onSuccess { response in diff --git a/Shared/YatteeApp.swift b/Shared/YatteeApp.swift index 6d52600b..066556d5 100644 --- a/Shared/YatteeApp.swift +++ b/Shared/YatteeApp.swift @@ -47,12 +47,18 @@ struct YatteeApp: App { .environmentObject(thumbnails) .environmentObject(menu) .environmentObject(search) - #if !os(macOS) - .onReceive( - NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification) - ) { _ in - player.handleEnterForeground() - } + #if os(macOS) + .background( + HostingWindowFinder { window in + Windows.mainWindow = window + } + ) + #else + .onReceive( + NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification) + ) { _ in + player.handleEnterForeground() + } #endif #if os(iOS) .handlesExternalEvents(preferring: Set(["*"]), allowing: Set(["*"])) @@ -81,6 +87,11 @@ struct YatteeApp: App { #if os(macOS) WindowGroup(player.windowTitle) { VideoPlayerView() + .background( + HostingWindowFinder { window in + Windows.playerWindow = window + } + ) .onAppear { player.presentingPlayer = true } .onDisappear { player.presentingPlayer = false } .environment(\.managedObjectContext, persistenceController.container.viewContext) diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 4a9ad14a..2965d0d2 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -240,7 +240,7 @@ 37732FF22703A26300F04329 /* AccountValidationStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37732FEF2703A26300F04329 /* AccountValidationStatus.swift */; }; 37732FF42703D32400F04329 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37732FF32703D32400F04329 /* Sidebar.swift */; }; 37732FF52703D32400F04329 /* Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37732FF32703D32400F04329 /* Sidebar.swift */; }; - 37737786276F9858000521C1 /* OpenWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37737785276F9858000521C1 /* OpenWindow.swift */; }; + 37737786276F9858000521C1 /* Windows.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37737785276F9858000521C1 /* Windows.swift */; }; 3774122A27387B6C00423605 /* InstancesModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3774122927387B6C00423605 /* InstancesModelTests.swift */; }; 3774122F27387C7600423605 /* VideosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376A33DF2720CAD6000C1D6B /* VideosApp.swift */; }; 3774123327387CB000423605 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; }; @@ -690,7 +690,7 @@ 376CD21526FBE18D001E1AC1 /* Instance+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Instance+Fixtures.swift"; sourceTree = ""; }; 37732FEF2703A26300F04329 /* AccountValidationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidationStatus.swift; sourceTree = ""; }; 37732FF32703D32400F04329 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = ""; }; - 37737785276F9858000521C1 /* OpenWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenWindow.swift; sourceTree = ""; }; + 37737785276F9858000521C1 /* Windows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Windows.swift; sourceTree = ""; }; 3774122927387B6C00423605 /* InstancesModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModelTests.swift; sourceTree = ""; }; 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedContentAccessors.swift; sourceTree = ""; }; 3782B94E27553A6700990149 /* SearchSuggestions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSuggestions.swift; sourceTree = ""; }; @@ -1182,7 +1182,7 @@ 37BE7AF227601DBF00DBECED /* Updates */, 374C0542272496E4009BDDBE /* AppDelegate.swift */, 37FD43DB270470B70073EE42 /* InstancesSettings.swift */, - 37737785276F9858000521C1 /* OpenWindow.swift */, + 37737785276F9858000521C1 /* Windows.swift */, 374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */, 37BE0BDB26A2367F0092E2DB /* Player.swift */, 37BE0BD926A214630092E2DB /* PlayerViewController.swift */, @@ -2060,7 +2060,7 @@ 374710062755291C00CE0F87 /* SearchField.swift in Sources */, 378AE93F274EDFB5006A4EE1 /* Tint+Backport.swift in Sources */, 37C194C826F6A9C8005D3B96 /* RecentsModel.swift in Sources */, - 37737786276F9858000521C1 /* OpenWindow.swift in Sources */, + 37737786276F9858000521C1 /* Windows.swift in Sources */, 37BE0BDC26A2367F0092E2DB /* Player.swift in Sources */, 3743CA53270F284F00E4D32B /* View+Borders.swift in Sources */, 37599F39272B4D740087F250 /* FavoriteButton.swift in Sources */, diff --git a/macOS/OpenWindow.swift b/macOS/OpenWindow.swift deleted file mode 100644 index 103b7554..00000000 --- a/macOS/OpenWindow.swift +++ /dev/null @@ -1,40 +0,0 @@ -import AppKit -import Foundation - -enum OpenWindow: String, CaseIterable { - case player, main - - var window: NSWindow? { - // this is not solid but works as long as there is only two windows in the app - // needs to be changed in case we ever have more windows to handle - - switch self { - case .player: - return NSApplication.shared.windows.last - case .main: - return NSApplication.shared.windows.first - } - } - - func focus() { - window?.makeKeyAndOrderFront(self) - } - - var location: String { - switch self { - case .player: - return rawValue - case .main: - return "" - } - } - - func open() { - switch self { - case .player: - NSWorkspace.shared.open(URL(string: "yattee://\(location)")!) - case .main: - Self.main.focus() - } - } -} diff --git a/macOS/Windows.swift b/macOS/Windows.swift new file mode 100644 index 00000000..c9901ffc --- /dev/null +++ b/macOS/Windows.swift @@ -0,0 +1,55 @@ +import AppKit +import Foundation +import SwiftUI + +enum Windows: String, CaseIterable { + case player, main + + static var mainWindow: NSWindow? + static var playerWindow: NSWindow? + + weak var window: NSWindow? { + switch self { + case .player: + return Self.playerWindow + case .main: + return Self.mainWindow + } + } + + func focus() { + window?.makeKeyAndOrderFront(self) + } + + var location: String { + switch self { + case .player: + return rawValue + case .main: + return "" + } + } + + func open() { + switch self { + case .player: + NSWorkspace.shared.open(URL(string: "yattee://\(location)")!) + case .main: + Self.main.focus() + } + } +} + +struct HostingWindowFinder: NSViewRepresentable { + var callback: (NSWindow?) -> Void + + func makeNSView(context _: Self.Context) -> NSView { + let view = NSView() + DispatchQueue.main.async { [weak view] in + self.callback(view?.window) + } + return view + } + + func updateNSView(_: NSView, context _: Context) {} +}