Просмотр исходного кода

Open widgets in standalone centered window

Removes the header Apps/Widgets toggle and opens the widgets page in a dedicated centered window sized for easier editing. Also disables the widgets window resize control for a fixed presentation.

Made-with: Cursor
huzaifahayat12 3 месяцев назад
Родитель
Сommit
bae4877639

+ 5 - 69
google_apps/LauncherRootView.swift

@@ -3,11 +3,6 @@ import AppKit
 import UniformTypeIdentifiers
 
 struct LauncherRootView: View {
-    enum Page: Hashable {
-        case apps
-        case widgets
-    }
-
     enum LayoutMode {
         case grid
         case panel
@@ -22,14 +17,10 @@ struct LauncherRootView: View {
     @State private var draggedAppID: UUID?
     @State private var lastDropDestinationAppID: UUID?
     @State private var layoutMode: LayoutMode = .grid
-    @State private var activePage: Page = .apps
-    @State private var selectedWidgetAppID: UUID?
     @AppStorage("customLauncherAppsData") private var customAppsData = ""
     @AppStorage("launcherAppOrderData") private var appOrderData = ""
     @AppStorage("pinnedTileIDsData") private var pinnedTileIDsData = ""
 
-    @AppStorage("widgetAppIDsData") private var widgetAppIDsData = ""
-
     private var apps: [LauncherApp] {
         LauncherApp.sampleApps + customApps + [LauncherApp.createNewTile]
     }
@@ -88,16 +79,9 @@ struct LauncherRootView: View {
                 SearchHeader(
                     query: $query,
                     isPanelMode: layoutMode == .panel,
-                    activePage: activePage,
                     onToggleLayout: {
                         layoutMode = layoutMode == .grid ? .panel : .grid
                     },
-                    onOpenWidgets: {
-                        activePage = .widgets
-                    },
-                    onOpenApps: {
-                        activePage = .apps
-                    },
                     onOpenPremium: {
                         showingPremiumScreen = true
                     },
@@ -116,16 +100,7 @@ struct LauncherRootView: View {
                 .padding(.bottom, 8)
                 .zIndex(1)
 
-                switch activePage {
-                case .apps:
-                    appsPage
-                case .widgets:
-                    WidgetsRootView(
-                        apps: orderedApps.filter { !$0.isCreateNew },
-                        selectedAppID: $selectedWidgetAppID,
-                        widgetLibraryIDsData: $widgetAppIDsData
-                    )
-                }
+                appsPage
             }
             .padding(.horizontal, 10)
             .padding(.bottom, 8)
@@ -159,10 +134,10 @@ struct LauncherRootView: View {
                 queue: .main
             ) { notification in
                 let id = notification.userInfo?["appID"] as? UUID
-                if let id {
-                    selectedWidgetAppID = id
-                }
-                activePage = .widgets
+                WidgetsWindowManager.shared.open(
+                    apps: orderedApps.filter { !$0.isCreateNew },
+                    selectedAppID: id
+                )
             }
         }
     }
@@ -402,10 +377,7 @@ private struct TileReorderDropDelegate: DropDelegate {
 private struct SearchHeader: View {
     @Binding var query: String
     let isPanelMode: Bool
-    let activePage: LauncherRootView.Page
     let onToggleLayout: () -> Void
-    let onOpenWidgets: () -> Void
-    let onOpenApps: () -> Void
     let onOpenPremium: () -> Void
     let onOpenGoogle: () -> Void
 
@@ -437,28 +409,10 @@ private struct SearchHeader: View {
                 }
                 .buttonStyle(.plain)
 
-                Button(action: {
-                    if activePage == .apps {
-                        onOpenWidgets()
-                    } else {
-                        onOpenApps()
-                    }
-                }) {
-                    Image(systemName: activePage == .apps ? "widget.small" : "square.grid.2x2")
-                }
-                .buttonStyle(.plain)
-
                 Menu {
                     Button("Clear Search") {
                         query = ""
                     }
-                    Button(activePage == .apps ? "Open Widgets" : "Open Apps") {
-                        if activePage == .apps {
-                            onOpenWidgets()
-                        } else {
-                            onOpenApps()
-                        }
-                    }
                     Button("Open Premium") {
                         onOpenPremium()
                     }
@@ -791,21 +745,3 @@ private struct HideMenuIndicatorIfAvailable: ViewModifier {
 #Preview {
     LauncherRootView()
 }
-
-private struct VisualEffectBlur: NSViewRepresentable {
-    let material: NSVisualEffectView.Material
-    let blendingMode: NSVisualEffectView.BlendingMode
-
-    func makeNSView(context: Context) -> NSVisualEffectView {
-        let view = NSVisualEffectView()
-        view.state = .active
-        view.material = material
-        view.blendingMode = blendingMode
-        return view
-    }
-
-    func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
-        nsView.material = material
-        nsView.blendingMode = blendingMode
-    }
-}

+ 21 - 0
google_apps/Shared/VisualEffectBlur.swift

@@ -0,0 +1,21 @@
+import SwiftUI
+import AppKit
+
+struct VisualEffectBlur: NSViewRepresentable {
+    let material: NSVisualEffectView.Material
+    let blendingMode: NSVisualEffectView.BlendingMode
+
+    func makeNSView(context: Context) -> NSVisualEffectView {
+        let view = NSVisualEffectView()
+        view.state = .active
+        view.material = material
+        view.blendingMode = blendingMode
+        return view
+    }
+
+    func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
+        nsView.material = material
+        nsView.blendingMode = blendingMode
+    }
+}
+

+ 92 - 0
google_apps/Widgets/WidgetsWindowManager.swift

@@ -0,0 +1,92 @@
+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 = ""
+    @State private var selection: UUID?
+
+    var body: some View {
+        ZStack {
+            VisualEffectBlur(material: .hudWindow, blendingMode: .behindWindow)
+                .ignoresSafeArea()
+            Color.black.opacity(0.22)
+                .ignoresSafeArea()
+
+            WidgetsRootView(
+                apps: apps,
+                selectedAppID: $selection,
+                widgetLibraryIDsData: $widgetAppIDsData
+            )
+            .padding(.top, 12)
+        }
+        .onAppear {
+            selection = selectedAppID ?? selection ?? apps.first?.id
+        }
+        .onChange(of: selectedAppID) { newValue in
+            if let newValue { selection = newValue }
+        }
+    }
+}
+