Clean up the window manager and its protocols

This commit is contained in:
Stefan Ceriu
2026-03-30 14:27:31 +03:00
committed by Stefan Ceriu
parent bb61db285b
commit b7333b9b5f
3 changed files with 128 additions and 78 deletions
@@ -74,31 +74,6 @@ class WindowManager: SecureWindowManagerProtocol {
self.dismissWindowAction = dismissWindowAction self.dismissWindowAction = dismissWindowAction
} }
func registerCoordinator(_ coordinator: CoordinatorProtocol, flowCoordinator: FlowCoordinatorProtocol?, forWindowType type: WindowManagerWindowType) {
coordinators[type] = (coordinator, flowCoordinator)
openWindowAction(value: type)
}
func closeAllAuxiliaryWindows() {
for key in coordinators.keys {
dismissWindowAction(value: key)
}
coordinators.removeAll()
}
func windowForType(_ type: WindowManagerWindowType) -> AnyView {
guard let coordinator = coordinators[type]?.coordinator else {
return AnyView(InstantlyDismissingWindow())
}
// This behaves strangely and gets called late but cleans up enough
// and is self contained enough to be just good .. enough
return AnyView(coordinator.toPresentable().onDisappear { [weak self] in
self?.coordinators[type] = nil
})
}
func handleRoute(_ appRoute: AppRoute, windowType: WindowManagerWindowType) { func handleRoute(_ appRoute: AppRoute, windowType: WindowManagerWindowType) {
if let flowCoordinator = coordinators[windowType]?.flowCoordinator { if let flowCoordinator = coordinators[windowType]?.flowCoordinator {
flowCoordinator.handleAppRoute(appRoute, animated: true) flowCoordinator.handleAppRoute(appRoute, animated: true)
@@ -167,6 +142,8 @@ class WindowManager: SecureWindowManagerProtocol {
mainWindow.makeKey() mainWindow.makeKey()
} }
// MARK: - OrientationManager
func setOrientation(_ orientation: UIInterfaceOrientationMask) { func setOrientation(_ orientation: UIInterfaceOrientationMask) {
mainScene?.requestGeometryUpdate(.iOS(interfaceOrientations: orientation)) mainScene?.requestGeometryUpdate(.iOS(interfaceOrientations: orientation))
} }
@@ -175,9 +152,36 @@ class WindowManager: SecureWindowManagerProtocol {
appDelegate.orientationLock = orientation appDelegate.orientationLock = orientation
} }
// MARK: - Auxiliary window support
func windowForType(_ type: WindowManagerWindowType) -> AnyView {
guard let coordinator = coordinators[type]?.coordinator else {
return AnyView(InstantlyDismissingWindow())
}
// This behaves strangely and gets called late but cleans up enough
// and is self contained enough to be just good .. enough
return AnyView(coordinator.toPresentable().onDisappear { [weak self] in
self?.coordinators[type] = nil
})
}
func registerCoordinator(_ coordinator: CoordinatorProtocol, flowCoordinator: FlowCoordinatorProtocol?, forWindowType type: WindowManagerWindowType) {
coordinators[type] = (coordinator, flowCoordinator)
openWindowAction(value: type)
}
func closeAuxiliaryWindow(forType type: WindowManagerWindowType) { func closeAuxiliaryWindow(forType type: WindowManagerWindowType) {
dismissWindowAction(value: type) dismissWindowAction(value: type)
} }
func closeAllAuxiliaryWindows() {
for key in coordinators.keys {
dismissWindowAction(value: key)
}
coordinators.removeAll()
}
} }
private class PassthroughWindow: UIWindow { private class PassthroughWindow: UIWindow {
@@ -29,13 +29,16 @@ protocol SecureWindowManagerProtocol: WindowManagerProtocol {
func handleRoute(_ appRoute: AppRoute, windowType: WindowManagerWindowType) func handleRoute(_ appRoute: AppRoute, windowType: WindowManagerWindowType)
func windowForType(_ type: WindowManagerWindowType) -> AnyView
/// Shows the main and overlay window combo, hiding the alternate window. /// Shows the main and overlay window combo, hiding the alternate window.
func switchToMain() func switchToMain()
/// Shows the alternate window, hiding the main and overlay combo. /// Shows the alternate window, hiding the main and overlay combo.
func switchToAlternate() func switchToAlternate()
// MARK: - Auxiliary window support
/// Used by the Application to retrieve the root view for an auxiliary window
func windowForType(_ type: WindowManagerWindowType) -> AnyView
} }
/// A window manager that supports switching between a main app window with an overlay and /// A window manager that supports switching between a main app window with an overlay and
@@ -54,15 +57,22 @@ protocol WindowManagerProtocol: AnyObject, OrientationManagerProtocol {
/// All the windows being managed /// All the windows being managed
var windows: [UIWindow] { get } var windows: [UIWindow] { get }
func registerCoordinator(_ coordinator: CoordinatorProtocol, flowCoordinator: FlowCoordinatorProtocol?, forWindowType type: WindowManagerWindowType)
func closeAllAuxiliaryWindows()
/// Makes the global search window key. Used to get automatic text field focus. /// Makes the global search window key. Used to get automatic text field focus.
func showGlobalSearch() func showGlobalSearch()
func hideGlobalSearch() func hideGlobalSearch()
// MARK: - Auxiliary window support
/// Register a coordinator and it's respective flow (if any) within the WindowManager which in turn
/// invokes the Application's `OpenWindowAction`
func registerCoordinator(_ coordinator: CoordinatorProtocol,
flowCoordinator: FlowCoordinatorProtocol?,
forWindowType type: WindowManagerWindowType)
/// Closes any window previously opened by registering a coordinator
func closeAllAuxiliaryWindows()
/// Closes a previously opened window for the given type. /// Closes a previously opened window for the given type.
func closeAuxiliaryWindow(forType type: WindowManagerWindowType) func closeAuxiliaryWindow(forType type: WindowManagerWindowType)
} }
@@ -21358,6 +21358,76 @@ class WindowManagerMock: WindowManagerProtocol, @unchecked Sendable {
var alternateWindow: UIWindow! var alternateWindow: UIWindow!
var windows: [UIWindow] = [] var windows: [UIWindow] = []
//MARK: - showGlobalSearch
var showGlobalSearchUnderlyingCallsCount = 0
var showGlobalSearchCallsCount: Int {
get {
if Thread.isMainThread {
return showGlobalSearchUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = showGlobalSearchUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
showGlobalSearchUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
showGlobalSearchUnderlyingCallsCount = newValue
}
}
}
}
var showGlobalSearchCalled: Bool {
return showGlobalSearchCallsCount > 0
}
var showGlobalSearchClosure: (() -> Void)?
func showGlobalSearch() {
showGlobalSearchCallsCount += 1
showGlobalSearchClosure?()
}
//MARK: - hideGlobalSearch
var hideGlobalSearchUnderlyingCallsCount = 0
var hideGlobalSearchCallsCount: Int {
get {
if Thread.isMainThread {
return hideGlobalSearchUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = hideGlobalSearchUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
hideGlobalSearchUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
hideGlobalSearchUnderlyingCallsCount = newValue
}
}
}
}
var hideGlobalSearchCalled: Bool {
return hideGlobalSearchCallsCount > 0
}
var hideGlobalSearchClosure: (() -> Void)?
func hideGlobalSearch() {
hideGlobalSearchCallsCount += 1
hideGlobalSearchClosure?()
}
//MARK: - registerCoordinator //MARK: - registerCoordinator
var registerCoordinatorFlowCoordinatorForWindowTypeUnderlyingCallsCount = 0 var registerCoordinatorFlowCoordinatorForWindowTypeUnderlyingCallsCount = 0
@@ -21434,17 +21504,17 @@ class WindowManagerMock: WindowManagerProtocol, @unchecked Sendable {
closeAllAuxiliaryWindowsCallsCount += 1 closeAllAuxiliaryWindowsCallsCount += 1
closeAllAuxiliaryWindowsClosure?() closeAllAuxiliaryWindowsClosure?()
} }
//MARK: - showGlobalSearch //MARK: - closeAuxiliaryWindow
var showGlobalSearchUnderlyingCallsCount = 0 var closeAuxiliaryWindowForTypeUnderlyingCallsCount = 0
var showGlobalSearchCallsCount: Int { var closeAuxiliaryWindowForTypeCallsCount: Int {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return showGlobalSearchUnderlyingCallsCount return closeAuxiliaryWindowForTypeUnderlyingCallsCount
} else { } else {
var returnValue: Int? = nil var returnValue: Int? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = showGlobalSearchUnderlyingCallsCount returnValue = closeAuxiliaryWindowForTypeUnderlyingCallsCount
} }
return returnValue! return returnValue!
@@ -21452,62 +21522,28 @@ class WindowManagerMock: WindowManagerProtocol, @unchecked Sendable {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
showGlobalSearchUnderlyingCallsCount = newValue closeAuxiliaryWindowForTypeUnderlyingCallsCount = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
showGlobalSearchUnderlyingCallsCount = newValue closeAuxiliaryWindowForTypeUnderlyingCallsCount = newValue
} }
} }
} }
} }
var showGlobalSearchCalled: Bool {
return showGlobalSearchCallsCount > 0
var closeAuxiliaryWindowForTypeCalled: Bool { var closeAuxiliaryWindowForTypeCalled: Bool {
return closeAuxiliaryWindowForTypeCallsCount > 0 return closeAuxiliaryWindowForTypeCallsCount > 0
} }
var showGlobalSearchClosure: (() -> Void)?
var closeAuxiliaryWindowForTypeReceivedType: WindowManagerWindowType? var closeAuxiliaryWindowForTypeReceivedType: WindowManagerWindowType?
var closeAuxiliaryWindowForTypeReceivedInvocations: [WindowManagerWindowType] = [] var closeAuxiliaryWindowForTypeReceivedInvocations: [WindowManagerWindowType] = []
var closeAuxiliaryWindowForTypeClosure: ((WindowManagerWindowType) -> Void)? var closeAuxiliaryWindowForTypeClosure: ((WindowManagerWindowType) -> Void)?
func showGlobalSearch() { func closeAuxiliaryWindow(forType type: WindowManagerWindowType) {
showGlobalSearchCallsCount += 1 closeAuxiliaryWindowForTypeCallsCount += 1
showGlobalSearchClosure?() closeAuxiliaryWindowForTypeReceivedType = type
} DispatchQueue.main.async {
//MARK: - hideGlobalSearch self.closeAuxiliaryWindowForTypeReceivedInvocations.append(type)
var hideGlobalSearchUnderlyingCallsCount = 0
var hideGlobalSearchCallsCount: Int {
get {
if Thread.isMainThread {
return hideGlobalSearchUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = hideGlobalSearchUnderlyingCallsCount
}
return returnValue!
}
} }
set { closeAuxiliaryWindowForTypeClosure?(type)
if Thread.isMainThread {
hideGlobalSearchUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
hideGlobalSearchUnderlyingCallsCount = newValue
}
}
}
}
var hideGlobalSearchCalled: Bool {
return hideGlobalSearchCallsCount > 0
}
var hideGlobalSearchClosure: (() -> Void)?
func hideGlobalSearch() {
hideGlobalSearchCallsCount += 1
hideGlobalSearchClosure?()
} }
//MARK: - setOrientation //MARK: - setOrientation