| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- import AppKit
- import SwiftUI
- final class WidgetsWindowManager {
- static let shared = WidgetsWindowManager()
- private var windowController: NSWindowController?
- private let defaultWindowSize = CGSize(width: 1120, height: 760)
- private init() {}
- func open(apps: [LauncherApp], selectedAppID: UUID?) {
- if let controller = windowController, controller.window != nil {
- if let hosting = controller.contentViewController as? NSHostingController<WidgetsWindowRootView> {
- hosting.rootView = WidgetsWindowRootView(apps: apps, selectedAppID: selectedAppID)
- }
- if let window = controller.window {
- resizeAndCenter(window: window)
- }
- controller.showWindow(nil)
- controller.window?.makeKeyAndOrderFront(nil)
- NSApp.activate(ignoringOtherApps: true)
- return
- }
- let root = WidgetsWindowRootView(apps: apps, selectedAppID: selectedAppID)
- let hosting = NSHostingController(rootView: root)
- let window = NSWindow(
- contentRect: NSRect(x: 0, y: 0, width: 980, height: 680),
- styleMask: [.titled, .closable, .miniaturizable],
- backing: .buffered,
- defer: false
- )
- window.title = "Widgets"
- window.contentViewController = hosting
- window.isReleasedWhenClosed = false
- window.minSize = NSSize(width: 900, height: 620)
- window.standardWindowButton(.zoomButton)?.isHidden = true
- resizeAndCenter(window: window)
- let controller = NSWindowController(window: window)
- self.windowController = controller
- controller.showWindow(nil)
- window.makeKeyAndOrderFront(nil)
- NSApp.activate(ignoringOtherApps: true)
- }
- private func resizeAndCenter(window: NSWindow) {
- let targetSize = NSSize(width: defaultWindowSize.width, height: defaultWindowSize.height)
- let screenFrame = (window.screen ?? NSScreen.main)?.visibleFrame ?? NSRect(x: 0, y: 0, width: 1440, height: 900)
- let clampedWidth = min(max(targetSize.width, window.minSize.width), screenFrame.width)
- let clampedHeight = min(max(targetSize.height, window.minSize.height), screenFrame.height)
- let x = screenFrame.origin.x + (screenFrame.width - clampedWidth) / 2
- let y = screenFrame.origin.y + (screenFrame.height - clampedHeight) / 2
- let frame = NSRect(x: x, y: y, width: clampedWidth, height: clampedHeight)
- window.setFrame(frame, display: true)
- }
- }
- private struct WidgetsWindowRootView: View {
- let apps: [LauncherApp]
- let selectedAppID: UUID?
- @AppStorage("widgetAppIDsData") private var widgetAppIDsData = ""
- /// Survives `NSHostingController.rootView` replacement so the sidebar stays selected like macOS widget settings.
- @AppStorage("widgetsLibrarySidebarSelectedAppID") private var persistedSidebarAppID: String = ""
- @State private var selection: UUID?
- @Environment(\.colorScheme) private var colorScheme
- var body: some View {
- ZStack {
- VisualEffectBlur(
- material: colorScheme == .dark ? .hudWindow : .underWindowBackground,
- blendingMode: .behindWindow
- )
- .ignoresSafeArea()
- (colorScheme == .dark ? Color.black.opacity(0.22) : Color.white.opacity(0.28))
- .ignoresSafeArea()
- WidgetsRootView(
- apps: apps,
- selectedAppID: $selection,
- widgetLibraryIDsData: $widgetAppIDsData
- )
- .padding(.top, 12)
- }
- .onAppear {
- restoreSelectionFromStorage()
- }
- .onChange(of: selectedAppID) { newValue in
- if let newValue {
- selection = newValue
- persistedSidebarAppID = newValue.uuidString
- }
- }
- .onChange(of: selection) { newValue in
- if let newValue {
- persistedSidebarAppID = newValue.uuidString
- }
- }
- }
- private func restoreSelectionFromStorage() {
- if let selectedAppID {
- selection = selectedAppID
- persistedSidebarAppID = selectedAppID.uuidString
- return
- }
- if let stored = UUID(uuidString: persistedSidebarAppID),
- apps.contains(where: { $0.id == stored }) {
- selection = stored
- return
- }
- if let first = apps.first {
- selection = first.id
- persistedSidebarAppID = first.id.uuidString
- }
- }
- }
|