1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2026-03-29 08:00:08 +02:00

♻️ Reworked window management

- Now a language change does no longer require an app restart
- Windows are managed via a WindowManager (alias for WindowCoordinator)
- Removed obsolete and unused `timer` from App
- Updated Japanese localization for label that was too long
This commit is contained in:
2026-02-23 13:07:17 +01:00
parent 67ad171700
commit 4490dfb79e
27 changed files with 232 additions and 162 deletions

View File

@@ -79,6 +79,10 @@
035983A12E97FA9100218DC7 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0329A9A02E92A2A800A62A12 /* Container.swift */; }; 035983A12E97FA9100218DC7 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0329A9A02E92A2A800A62A12 /* Container.swift */; };
035983A22E97FA9100218DC7 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0329A9A02E92A2A800A62A12 /* Container.swift */; }; 035983A22E97FA9100218DC7 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0329A9A02E92A2A800A62A12 /* Container.swift */; };
035983A32E97FA9100218DC7 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0329A9A02E92A2A800A62A12 /* Container.swift */; }; 035983A32E97FA9100218DC7 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0329A9A02E92A2A800A62A12 /* Container.swift */; };
036061D72F4C705B00B5998F /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036061D62F4C705800B5998F /* WindowManager.swift */; };
036061D82F4C705B00B5998F /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036061D62F4C705800B5998F /* WindowManager.swift */; };
036061D92F4C705B00B5998F /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036061D62F4C705800B5998F /* WindowManager.swift */; };
036061DA2F4C705B00B5998F /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036061D62F4C705800B5998F /* WindowManager.swift */; };
036C39022E5C883B008DAEDF /* Packagist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C39012E5C883A008DAEDF /* Packagist.swift */; }; 036C39022E5C883B008DAEDF /* Packagist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C39012E5C883A008DAEDF /* Packagist.swift */; };
036C39032E5C883B008DAEDF /* Packagist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C39012E5C883A008DAEDF /* Packagist.swift */; }; 036C39032E5C883B008DAEDF /* Packagist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C39012E5C883A008DAEDF /* Packagist.swift */; };
036C39042E5C883B008DAEDF /* Packagist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C39012E5C883A008DAEDF /* Packagist.swift */; }; 036C39042E5C883B008DAEDF /* Packagist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036C39012E5C883A008DAEDF /* Packagist.swift */; };
@@ -1069,6 +1073,7 @@
034515472EC4FB9800472561 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/Localizable.strings; sourceTree = "<group>"; }; 034515472EC4FB9800472561 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/Localizable.strings; sourceTree = "<group>"; };
034515482EC4FBB500472561 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/Localizable.strings; sourceTree = "<group>"; }; 034515482EC4FBB500472561 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/Localizable.strings; sourceTree = "<group>"; };
034515492EC4FBBD00472561 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; }; 034515492EC4FBBD00472561 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
036061D62F4C705800B5998F /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
036C39012E5C883A008DAEDF /* Packagist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Packagist.swift; sourceTree = "<group>"; }; 036C39012E5C883A008DAEDF /* Packagist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Packagist.swift; sourceTree = "<group>"; };
036C39072E5C88A2008DAEDF /* PackagistTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackagistTest.swift; sourceTree = "<group>"; }; 036C39072E5C88A2008DAEDF /* PackagistTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackagistTest.swift; sourceTree = "<group>"; };
036C39092E5C8CBD008DAEDF /* PackagistP2Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackagistP2Response.swift; sourceTree = "<group>"; }; 036C39092E5C8CBD008DAEDF /* PackagistP2Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackagistP2Response.swift; sourceTree = "<group>"; };
@@ -2242,6 +2247,7 @@
C40FE736282ABA4F00A302C2 /* AppVersion.swift */, C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
C409349C298EE8E900D25014 /* AppUpdater.swift */, C409349C298EE8E900D25014 /* AppUpdater.swift */,
03263A372E86D5E800BD0415 /* UpdateScheduler.swift */, 03263A372E86D5E800BD0415 /* UpdateScheduler.swift */,
036061D62F4C705800B5998F /* WindowManager.swift */,
); );
path = App; path = App;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2969,6 +2975,7 @@
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */, C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */,
C417DC74277614690015E6EE /* Helpers.swift in Sources */, C417DC74277614690015E6EE /* Helpers.swift in Sources */,
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
036061D92F4C705B00B5998F /* WindowManager.swift in Sources */,
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
03D846352EB64E39006EFE3C /* CrashReporter.swift in Sources */, 03D846352EB64E39006EFE3C /* CrashReporter.swift in Sources */,
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
@@ -3161,6 +3168,7 @@
C4D3660D29113F20006BD146 /* System.swift in Sources */, C4D3660D29113F20006BD146 /* System.swift in Sources */,
033D45A02B0D513900070080 /* RemovePhpExtensionCommand.swift in Sources */, 033D45A02B0D513900070080 /* RemovePhpExtensionCommand.swift in Sources */,
031D748B2F46307A00D4FF48 /* AddProxyView.swift in Sources */, 031D748B2F46307A00D4FF48 /* AddProxyView.swift in Sources */,
036061D72F4C705B00B5998F /* WindowManager.swift in Sources */,
C471E86D28F9BB650021E251 /* CustomPrefs.swift in Sources */, C471E86D28F9BB650021E251 /* CustomPrefs.swift in Sources */,
C45D654E29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */, C45D654E29F52F74004C28F9 /* BrewPermissionFixer.swift in Sources */,
C4E2E84C28FC1E70003B070C /* DataExtension.swift in Sources */, C4E2E84C28FC1E70003B070C /* DataExtension.swift in Sources */,
@@ -3370,6 +3378,7 @@
C4BF56AE2949381100379603 /* FakeValetInteractor.swift in Sources */, C4BF56AE2949381100379603 /* FakeValetInteractor.swift in Sources */,
C471E8C228F9BB8F0021E251 /* DomainListVC+Actions.swift in Sources */, C471E8C228F9BB8F0021E251 /* DomainListVC+Actions.swift in Sources */,
C4821C5D2C2DEDE200357A68 /* AppMenu.swift in Sources */, C4821C5D2C2DEDE200357A68 /* AppMenu.swift in Sources */,
036061DA2F4C705B00B5998F /* WindowManager.swift in Sources */,
C45B91562956123A00F4EC78 /* FakeServicesManager.swift in Sources */, C45B91562956123A00F4EC78 /* FakeServicesManager.swift in Sources */,
C471E8C628F9BB8F0021E251 /* PMTableView.swift in Sources */, C471E8C628F9BB8F0021E251 /* PMTableView.swift in Sources */,
C471E8C728F9BB8F0021E251 /* Warning.swift in Sources */, C471E8C728F9BB8F0021E251 /* Warning.swift in Sources */,
@@ -3640,6 +3649,7 @@
C4AFC4B429C4F43300BF4E0D /* HomebrewUpgradableTest.swift in Sources */, C4AFC4B429C4F43300BF4E0D /* HomebrewUpgradableTest.swift in Sources */,
037F441E2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */, 037F441E2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */,
C4E2E84828FC1D93003B070C /* TestableConfigurationTest.swift in Sources */, C4E2E84828FC1D93003B070C /* TestableConfigurationTest.swift in Sources */,
036061D82F4C705B00B5998F /* WindowManager.swift in Sources */,
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */, C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
C4513F962B13E30C001AD760 /* BrewExtensionsObservable.swift in Sources */, C4513F962B13E30C001AD760 /* BrewExtensionsObservable.swift in Sources */,
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,

View File

@@ -642,7 +642,7 @@ Thank you very much for your contributions, kind words and support.
### Loading info about PHP in the background ### Loading info about PHP in the background
This app runs `php-config --version` in the background periodically, usually whenever your Homebrew configuration is modified. A filesystem watcher is used to determine if anything changes in your Homebrew's `bin` directory. This app runs `php-config --version` in the background, usually whenever your Homebrew configuration is modified. A filesystem watcher is used to determine if anything changes in your Homebrew's `bin` directory.
PHP Monitor also checks your `.ini` files for extensions and loads more information about your limits (memory limit, POST limit, upload limit). See also the section on *Config change detection* below. PHP Monitor also checks your `.ini` files for extensions and loads more information about your limits (memory limit, POST limit, upload limit). See also the section on *Config change detection* below.

View File

@@ -137,7 +137,9 @@ class PhpEnvironments {
// Update the synchronized value // Update the synchronized value
_currentInstall.value = newValue _currentInstall.value = newValue
// Let the PHP extension manager, if it exists, know the version changed // Let the PHP extension manager, if it exists, know the version changed
App.shared.phpExtensionManagerWindowController?.view.didUpdatePhpVersion() WindowManager
.controller(of: PhpExtensionManagerWC.self)?
.view.didUpdatePhpVersion()
} }
} }

View File

@@ -41,4 +41,14 @@ extension App {
NSApp.setActivationPolicy(!openWindows.isEmpty ? .regular : .accessory) NSApp.setActivationPolicy(!openWindows.isEmpty ? .regular : .accessory)
} }
/**
Closes and invalidates all cached secondary window controllers (excluding preferences).
This ensures that windows are recreated fresh, with the correct localization, the next
time they are opened. Each `close()` call triggers `windowWillClose`, which automatically
removes the window from `openWindows` via the existing delegate mechanism.
*/
public func invalidateCachedWindows() {
WindowManager.closeAll(excluding: [PreferencesWC.self])
}
} }

View File

@@ -81,36 +81,12 @@ class App {
*/ */
var container: Container = Container() var container: Container = Container()
/** The window controller of the currently active preferences window. */
var preferencesWindowController: PreferencesWindowController?
/** The window controller of the currently active site list window. */
var domainListWindowController: DomainListWindowController?
/** The window controller of the onboarding window. */
var onboardingWindowController: OnboardingWindowController?
/** The window controller of the config manager window. */
var phpConfigManagerWindowController: PhpConfigManagerWindowController?
/** The window controller of the warnings window. */
var phpDoctorWindowController: PhpDoctorWindowController?
/** The window controller of the PHP version manager window. */
var phpVersionManagerWindowController: PhpVersionManagerWindowController?
/** The window controller of the PHP extension manager window. */
var phpExtensionManagerWindowController: PhpExtensionManagerWindowController?
/** URL that was received before the app finished booting. Will be processed once the startup procedure completes. */ /** URL that was received before the app finished booting. Will be processed once the startup procedure completes. */
var deferredURL: URL? var deferredURL: URL?
/** List of detected (installed) applications that PHP Monitor can work with. */ /** List of detected (installed) applications that PHP Monitor can work with. */
var detectedApplications: [Application] = [] var detectedApplications: [Application] = []
/** Timer that will periodically reload info about the user's PHP installation. */
var timer: Timer?
// MARK: - Global Hotkey // MARK: - Global Hotkey
/** /**
@@ -122,7 +98,7 @@ class App {
} }
} }
// MARK: - Activation Policy // MARK: - Windows and Activation Policy
/** /**
Variable that keeps track of which windows are currently open. Variable that keeps track of which windows are currently open.

View File

@@ -28,13 +28,14 @@ extension AppDelegate {
@IBAction func addSiteLinkPressed(_ sender: Any) { @IBAction func addSiteLinkPressed(_ sender: Any) {
DomainListVC.show() DomainListVC.show()
guard let windowController = App.shared.domainListWindowController else { return } guard let windowController = WindowManager.controller(of: DomainListWC.self) else { return }
windowController.pressedAddLink(nil) windowController.pressedAddLink(nil)
} }
@IBAction func reloadDomainListPressed(_ sender: Any) { @IBAction func reloadDomainListPressed(_ sender: Any) {
Task { // Reload domains Task { // Reload domains
let vc = App.shared.domainListWindowController? let vc = WindowManager
.controller(of: DomainListWC.self)?
.window?.contentViewController as? DomainListVC .window?.contentViewController as? DomainListVC
if vc != nil { if vc != nil {
@@ -50,7 +51,7 @@ extension AppDelegate {
@IBAction func focusSearchField(_ sender: Any) { @IBAction func focusSearchField(_ sender: Any) {
DomainListVC.show() DomainListVC.show()
guard let windowController = App.shared.domainListWindowController else { return } guard let windowController = WindowManager.controller(of: DomainListWC.self) else { return }
windowController.searchToolbarItem.searchField.becomeFirstResponder() windowController.searchToolbarItem.searchField.becomeFirstResponder()
} }

View File

@@ -0,0 +1,73 @@
//
// WindowCoordinator.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 23/02/2026.
// Copyright © 2026 Nico Verbruggen. All rights reserved.
//
import Cocoa
typealias PreferencesWC = PreferencesWindowController
typealias DomainListWC = DomainListWindowController
typealias OnboardingWC = OnboardingWindowController
typealias PhpConfigManagerWC = PhpConfigManagerWindowController
typealias PhpDoctorWC = PhpDoctorWindowController
typealias PhpVersionManagerWC = PhpVersionManagerWindowController
typealias PhpExtensionManagerWC = PhpExtensionManagerWindowController
let WindowManager = WindowCoordinator.shared
final class WindowCoordinator {
static let shared = WindowCoordinator()
private var controllers: [ObjectIdentifier: NSWindowController] = [:]
private init() {}
func setController<T: NSWindowController>(_ controller: T) {
controllers[ObjectIdentifier(T.self)] = controller
}
func hasController<T: NSWindowController>(for type: T.Type) -> Bool {
return controllers[ObjectIdentifier(T.self)] != nil
}
func controller<T: NSWindowController>(of type: T.Type) -> T? {
return controllers[ObjectIdentifier(T.self)] as? T
}
func window<T: NSWindowController>(for type: T.Type) -> NSWindow? {
return controllers[ObjectIdentifier(T.self)]?.window
}
func withWindow<T: NSWindowController>(for type: T.Type, _ handler: (NSWindow) -> Void) {
guard let window = window(for: type) else { return }
handler(window)
}
@discardableResult
func show<T: NSWindowController>(_ type: T.Type) -> T? {
guard let controller = controller(of: type) else { return nil }
controller.showWindow(self)
NSApp.activate(ignoringOtherApps: true)
controller.window?.orderFrontRegardless()
return controller
}
func close<T: NSWindowController>(_ type: T.Type) {
controllers[ObjectIdentifier(T.self)]?.close()
controllers[ObjectIdentifier(T.self)] = nil
}
func closeAll(excluding types: [NSWindowController.Type] = []) {
let excluded = Set(types.map { ObjectIdentifier($0) })
controllers.keys
.filter { !excluded.contains($0) }
.forEach { key in
controllers[key]?.close()
controllers[key] = nil
}
}
}

View File

@@ -117,7 +117,7 @@ extension MainMenu {
} }
private func reloadDomainListData() async { private func reloadDomainListData() async {
if let window = App.shared.domainListWindowController { if let window = WindowManager.controller(of: DomainListWC.self) {
await window.contentVC.reloadDomains() await window.contentVC.reloadDomains()
} else { } else {
await Valet.shared.reloadSites() await Valet.shared.reloadSites()

View File

@@ -56,15 +56,11 @@ class GenericPreferenceVC: NSViewController {
action: { action: {
MainMenu.shared.refreshIcon() MainMenu.shared.refreshIcon()
MainMenu.shared.rebuild() MainMenu.shared.rebuild()
App.shared.invalidateCachedWindows()
if let window = App.shared.preferencesWindowController?.window { // Re-open the preferences VC
let alert = NSAlert() WindowManager.close(PreferencesWC.self)
alert.messageText = "alert.language_changed.title".localized PreferencesWindowController.show()
alert.informativeText = "alert.language_changed.subtitle".localized
alert.alertStyle = .warning
alert.addButton(withTitle: "generic.ok".localized)
alert.beginSheetModal(for: window)
}
} }
) )
} }

View File

@@ -30,49 +30,52 @@ class PreferencesWindowController: PMWindowController {
window.delegate = delegate ?? windowController window.delegate = delegate ?? windowController
window.styleMask = [.titled, .closable, .miniaturizable] window.styleMask = [.titled, .closable, .miniaturizable]
App.shared.preferencesWindowController = windowController WindowManager.setController(windowController)
} }
public static func show(delegate: NSWindowDelegate? = nil) { public static func show(delegate: NSWindowDelegate? = nil) {
var justCreated = false var justCreated = false
if App.shared.preferencesWindowController == nil { if !WindowManager.hasController(for: PreferencesWC.self) {
Self.create(delegate: delegate) Self.create(delegate: delegate)
guard let preferencesWC = App.shared.preferencesWindowController else {
return
}
guard let tabVC = preferencesWC.contentViewController as? NSTabViewController else {
return
}
for vc in preferencesWC.tabVCs {
tabVC.addChild(vc.viewController)
let item = tabVC.tabViewItem(for: vc.viewController)
item?.image = NSImage(systemSymbolName: vc.icon, accessibilityDescription: "\(vc.label) Icon")
item?.label = vc.label
}
tabVC.preferredContentSize = NSSize(
width: tabVC.view.frame.size.width,
height: tabVC.view.frame.size.height
)
justCreated = true justCreated = true
} }
App.shared.preferencesWindowController?.showWindow(self) guard let preferencesWC = WindowManager.controller(of: PreferencesWC.self) else {
return
if justCreated {
App.shared.preferencesWindowController?.positionWindowInTopRightCorner()
} }
App.shared.preferencesWindowController?.window?.orderFrontRegardless() if justCreated {
addContentTabs(to: preferencesWC)
}
WindowManager.show(PreferencesWC.self)
if justCreated {
preferencesWC.positionWindowInTopRightCorner()
}
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
private static func addContentTabs(to preferencesWC: PreferencesWC) {
guard let tabVC = preferencesWC.contentViewController as? NSTabViewController else {
return
}
for vc in preferencesWC.tabVCs {
tabVC.addChild(vc.viewController)
let item = tabVC.tabViewItem(for: vc.viewController)
item?.image = NSImage(systemSymbolName: vc.icon, accessibilityDescription: "\(vc.label) Icon")
item?.label = vc.label
}
tabVC.preferredContentSize = NSSize(
width: tabVC.view.frame.size.width,
height: tabVC.view.frame.size.height
)
}
// MARK: - Tabs // MARK: - Tabs
struct PrefTabView { struct PrefTabView {

View File

@@ -48,7 +48,7 @@ struct VersionPopoverView: View {
LazyVGrid(columns: self.rows, alignment: .leading, spacing: 5, content: { LazyVGrid(columns: self.rows, alignment: .leading, spacing: 5, content: {
ForEach(validPhpVersions, id: \.self) { version in ForEach(validPhpVersions, id: \.self) { version in
Button("site_link.isolate_php".localized(version.short), action: { Button("site_link.isolate_php".localized(version.short), action: {
App.shared.domainListWindowController?.contentVC WindowManager.controller(of: DomainListWC.self)?.contentVC
.isolateSite(site: site, version: version.short) .isolateSite(site: site, version: version.short)
parent?.close() parent?.close()
}).padding(EdgeInsets(top: 3, leading: 0, bottom: 3, trailing: 0)) }).padding(EdgeInsets(top: 3, leading: 0, bottom: 3, trailing: 0))

View File

@@ -40,7 +40,7 @@
<key>LSUIElement</key> <key>LSUIElement</key>
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2019-2025 Nico Verbruggen. All rights reserved.</string> <string>Copyright © 2019-2026 Nico Verbruggen. All rights reserved.</string>
<key>NSMainStoryboardFile</key> <key>NSMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>

View File

@@ -78,8 +78,8 @@ class DomainListTLSCell: NSTableCellView, DomainListCellProtocol {
tld: Valet.shared.config.tld, tld: Valet.shared.config.tld,
expires: site.getListableCertificateExpiryDate(), expires: site.getListableCertificateExpiryDate(),
callback: { callback: {
App.shared.domainListWindowController? WindowManager.controller(of: DomainListWC.self)?.contentVC
.contentVC.checkForCertificateRenewal() .checkForCertificateRenewal()
} }
) )

View File

@@ -30,7 +30,7 @@ extension DomainListVC {
}.joined(separator: "\n- ") }.joined(separator: "\n- ")
// Ensure the window is accessible // Ensure the window is accessible
guard let window = App.shared.domainListWindowController?.window else { guard let window = WindowManager.window(for: DomainListWC.self) else {
return return
} }

View File

@@ -27,16 +27,14 @@ extension DomainListVC {
window.minSize = NSSize(width: 550, height: 200) window.minSize = NSSize(width: 550, height: 200)
window.setFrameAutosaveName("domainListWindow") window.setFrameAutosaveName("domainListWindow")
App.shared.domainListWindowController = windowController WindowManager.setController(windowController)
} }
public static func show(delegate: NSWindowDelegate? = nil) { public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.domainListWindowController == nil { if !WindowManager.hasController(for: DomainListWC.self) {
Self.create(delegate: delegate) Self.create(delegate: delegate)
} }
App.shared.domainListWindowController!.showWindow(self) WindowManager.show(DomainListWC.self)
NSApp.activate(ignoringOtherApps: true)
App.shared.domainListWindowController?.window?.orderFrontRegardless()
} }
} }

View File

@@ -123,7 +123,8 @@ class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource
icon: "globe", icon: "globe",
button: "domain_list.domains_empty.button".localized, button: "domain_list.domains_empty.button".localized,
action: { action: {
App.shared.domainListWindowController? WindowManager
.controller(of: DomainListWC.self)?
.pressedAddLink(nil) .pressedAddLink(nil)
} }
) )

View File

@@ -124,7 +124,7 @@ struct OnboardingView: View {
.lineLimit(3) .lineLimit(3)
.frame(height: 35) .frame(height: 35)
Button("onboarding.tour.close".localized) { Button("onboarding.tour.close".localized) {
App.shared.onboardingWindowController?.close() WindowManager.close(OnboardingWC.self)
} }
.padding(.bottom, 15) .padding(.bottom, 15)
.padding(.top, 10) .padding(.top, 10)

View File

@@ -29,18 +29,18 @@ class OnboardingWindowController: PMWindowController {
window.contentView = NSHostingView(rootView: OnboardingView()) window.contentView = NSHostingView(rootView: OnboardingView())
window.setContentSize(window.contentView!.fittingSize) window.setContentSize(window.contentView!.fittingSize)
App.shared.onboardingWindowController = windowController WindowManager.setController(windowController)
} }
public static func show(delegate: NSWindowDelegate? = nil) { public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.onboardingWindowController == nil { if !WindowManager.hasController(for: OnboardingWC.self) {
Self.create(delegate: delegate) Self.create(delegate: delegate)
} }
App.shared.onboardingWindowController?.showWindow(self) WindowManager.show(OnboardingWC.self)
App.shared.onboardingWindowController?.window?.setCenterPosition(offsetY: 70) WindowManager.withWindow(for: OnboardingWC.self) { window in
window.setCenterPosition(offsetY: 70)
NSApp.activate(ignoringOtherApps: true) }
} }
override func close() { override func close() {

View File

@@ -68,7 +68,7 @@ struct ConfigManagerView: View {
VStack(alignment: .trailing) { VStack(alignment: .trailing) {
Button("Close", action: { Button("Close", action: {
App.shared.phpConfigManagerWindowController?.close() WindowManager.close(PhpConfigManagerWC.self)
}) })
} }
.padding(.vertical, 10) .padding(.vertical, 10)

View File

@@ -29,18 +29,16 @@ class PhpConfigManagerWindowController: PMWindowController {
window.contentView = NSHostingView(rootView: ConfigManagerView()) window.contentView = NSHostingView(rootView: ConfigManagerView())
window.setContentSize(NSSize(width: 600, height: 480)) window.setContentSize(NSSize(width: 600, height: 480))
App.shared.phpConfigManagerWindowController = windowController WindowManager.setController(windowController)
} }
public static func show(delegate: NSWindowDelegate? = nil) { public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.phpConfigManagerWindowController == nil { if !WindowManager.hasController(for: PhpConfigManagerWC.self) {
Self.create(delegate: delegate) Self.create(delegate: delegate)
} }
App.shared.phpConfigManagerWindowController?.showWindow(self) WindowManager.show(PhpConfigManagerWC.self)
App.shared.phpConfigManagerWindowController?.positionWindowInTopRightCorner() WindowManager.controller(of: PhpConfigManagerWC.self)?
.positionWindowInTopRightCorner()
NSApp.activate(ignoringOtherApps: true)
App.shared.phpConfigManagerWindowController?.window?.orderFrontRegardless()
} }
} }

View File

@@ -142,7 +142,8 @@ extension WarningManager {
fix: { fix: {
await DomainListVC.show() await DomainListVC.show()
if let vc = await App.shared.domainListWindowController? if let vc = await WindowManager
.controller(of: DomainListWC.self)?
.window?.contentViewController as? DomainListVC { .window?.contentViewController as? DomainListVC {
await vc.checkForCertificateRenewal { await vc.checkForCertificateRenewal {
await self.checkEnvironment() await self.checkEnvironment()

View File

@@ -29,18 +29,17 @@ class PhpDoctorWindowController: PMWindowController {
window.contentView = NSHostingView(rootView: PhpDoctorView()) window.contentView = NSHostingView(rootView: PhpDoctorView())
window.setContentSize(window.contentView!.fittingSize) window.setContentSize(window.contentView!.fittingSize)
App.shared.phpDoctorWindowController = windowController WindowManager.setController(windowController)
} }
public static func show(delegate: NSWindowDelegate? = nil) { public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.phpDoctorWindowController == nil { if !WindowManager.hasController(for: PhpDoctorWC.self) {
Self.create(delegate: delegate) Self.create(delegate: delegate)
} }
App.shared.phpDoctorWindowController?.showWindow(self) WindowManager.show(PhpDoctorWC.self)
App.shared.phpDoctorWindowController?.window?.setCenterPosition(offsetY: 70) WindowManager.withWindow(for: PhpDoctorWC.self) { window in
window.setCenterPosition(offsetY: 70)
NSApp.activate(ignoringOtherApps: true) }
App.shared.phpDoctorWindowController?.window?.orderFrontRegardless()
} }
} }

View File

@@ -16,15 +16,17 @@ extension PhpExtensionManagerView {
button: String, button: String,
style: NSAlert.Style = .critical style: NSAlert.Style = .critical
) { ) {
Alert.confirm( WindowManager.withWindow(for: PhpExtensionManagerWC.self) { window in
onWindow: App.shared.phpExtensionManagerWindowController!.window!, Alert.confirm(
messageText: title, onWindow: window,
informativeText: description, messageText: title,
buttonTitle: button, informativeText: description,
secondButtonTitle: "", buttonTitle: button,
style: style, secondButtonTitle: "",
onFirstButtonPressed: {} style: style,
) onFirstButtonPressed: {}
)
}
} }
public func install(_ ext: BrewPhpExtension, onCompletion: @escaping () -> Void = {}) { public func install(_ ext: BrewPhpExtension, onCompletion: @escaping () -> Void = {}) {
@@ -35,21 +37,23 @@ extension PhpExtensionManagerView {
} }
public func confirmUninstall(_ ext: BrewPhpExtension, onCompletion: @escaping () -> Void = {}) { public func confirmUninstall(_ ext: BrewPhpExtension, onCompletion: @escaping () -> Void = {}) {
Alert.confirm( WindowManager.withWindow(for: PhpExtensionManagerWC.self) { window in
onWindow: App.shared.phpExtensionManagerWindowController!.window!, Alert.confirm(
messageText: "phpextman.warnings.removal.title".localized(ext.name), onWindow: window,
informativeText: "phpextman.warnings.removal.desc".localized(ext.name), messageText: "phpextman.warnings.removal.title".localized(ext.name),
buttonTitle: "phpextman.warnings.removal.button".localized, informativeText: "phpextman.warnings.removal.desc".localized(ext.name),
buttonIsDestructive: true, buttonTitle: "phpextman.warnings.removal.button".localized,
secondButtonTitle: "generic.cancel".localized, buttonIsDestructive: true,
style: .warning, secondButtonTitle: "generic.cancel".localized,
onFirstButtonPressed: { style: .warning,
Task { onFirstButtonPressed: {
await self.runCommand(RemovePhpExtensionCommand(container, remove: ext)) Task {
onCompletion() await self.runCommand(RemovePhpExtensionCommand(container, remove: ext))
onCompletion()
}
} }
} )
) }
} }
public func runCommand(_ command: BrewCommand) async { public func runCommand(_ command: BrewCommand) async {

View File

@@ -34,19 +34,16 @@ class PhpExtensionManagerWindowController: PMWindowController {
window.contentView = NSHostingView(rootView: windowController.view) window.contentView = NSHostingView(rootView: windowController.view)
window.setContentSize(NSSize(width: 600, height: 800)) window.setContentSize(NSSize(width: 600, height: 800))
App.shared.phpExtensionManagerWindowController = windowController WindowManager.setController(windowController)
} }
public static func show(delegate: NSWindowDelegate? = nil) { public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.phpExtensionManagerWindowController == nil { if !WindowManager.hasController(for: PhpExtensionManagerWC.self) {
Self.create(delegate: delegate) Self.create(delegate: delegate)
} }
App.shared.phpExtensionManagerWindowController?.showWindow(self) WindowManager.show(PhpExtensionManagerWC.self)
App.shared.phpExtensionManagerWindowController?.positionWindowInTopRightCorner() WindowManager.controller(of: PhpExtensionManagerWC.self)?
.positionWindowInTopRightCorner()
NSApp.activate(ignoringOtherApps: true)
App.shared.phpExtensionManagerWindowController?.window?.orderFrontRegardless()
} }
} }

View File

@@ -153,18 +153,20 @@ extension PhpVersionManagerView {
return return
} }
Alert.confirm( WindowManager.withWindow(for: PhpVersionManagerWC.self) { window in
onWindow: App.shared.phpVersionManagerWindowController!.window!, Alert.confirm(
messageText: "phpman.warnings.removal.title".localized(formula.displayName), onWindow: window,
informativeText: "phpman.warnings.removal.desc".localized(formula.displayName), messageText: "phpman.warnings.removal.title".localized(formula.displayName),
buttonTitle: "phpman.warnings.removal.button".localized, informativeText: "phpman.warnings.removal.desc".localized(formula.displayName),
buttonIsDestructive: true, buttonTitle: "phpman.warnings.removal.button".localized,
secondButtonTitle: "generic.cancel".localized, buttonIsDestructive: true,
style: .warning, secondButtonTitle: "generic.cancel".localized,
onFirstButtonPressed: { style: .warning,
Task { await self.uninstall(formula) } onFirstButtonPressed: {
} Task { await self.uninstall(formula) }
) }
)
}
} }
/** /**
@@ -176,15 +178,17 @@ extension PhpVersionManagerView {
button: String, button: String,
style: NSAlert.Style = .critical style: NSAlert.Style = .critical
) { ) {
Alert.confirm( WindowManager.withWindow(for: PhpVersionManagerWC.self) { window in
onWindow: App.shared.phpVersionManagerWindowController!.window!, Alert.confirm(
messageText: title, onWindow: window,
informativeText: description, messageText: title,
buttonTitle: button, informativeText: description,
secondButtonTitle: "", buttonTitle: button,
style: style, secondButtonTitle: "",
onFirstButtonPressed: {} style: style,
) onFirstButtonPressed: {}
)
}
} }
} }

View File

@@ -36,19 +36,16 @@ class PhpVersionManagerWindowController: PMWindowController {
window.contentView = NSHostingView(rootView: windowController.view) window.contentView = NSHostingView(rootView: windowController.view)
window.setContentSize(NSSize(width: 600, height: 800)) window.setContentSize(NSSize(width: 600, height: 800))
App.shared.phpVersionManagerWindowController = windowController WindowManager.setController(windowController)
} }
public static func show(delegate: NSWindowDelegate? = nil) { public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.phpVersionManagerWindowController == nil { if !WindowManager.hasController(for: PhpVersionManagerWC.self) {
Self.create(delegate: delegate) Self.create(delegate: delegate)
} }
App.shared.phpVersionManagerWindowController?.showWindow(self) WindowManager.show(PhpVersionManagerWC.self)
App.shared.phpVersionManagerWindowController?.positionWindowInTopRightCorner() WindowManager.controller(of: PhpVersionManagerWC.self)?
.positionWindowInTopRightCorner()
NSApp.activate(ignoringOtherApps: true)
App.shared.phpVersionManagerWindowController?.window?.orderFrontRegardless()
} }
} }

View File

@@ -265,7 +265,7 @@
"prefs.tabs.appearance" = "外観"; "prefs.tabs.appearance" = "外観";
"prefs.tabs.visibility" = "可視性"; "prefs.tabs.visibility" = "可視性";
"prefs.tabs.notifications" = "通知"; "prefs.tabs.notifications" = "通知";
"prefs.global_shortcut" = "グローバルショートカット"; "prefs.global_shortcut" = "グローバルキー";
"prefs.dynamic_icon" = "アイコンタイプ:"; "prefs.dynamic_icon" = "アイコンタイプ:";
"prefs.info_density" = "情報密度:"; "prefs.info_density" = "情報密度:";
"prefs.services" = "サービス:"; "prefs.services" = "サービス:";