Преглед на файлове

Add hide and restore controls for launcher apps.

This lets users remove apps from view and add them back later while persisting hidden state and cleaning up pinned/status bar/widget references.

Made-with: Cursor
huzaifahayat12 преди 4 месеца
родител
ревизия
6b7137fd31
променени са 2 файла, в които са добавени 102 реда и са изтрити 6 реда
  1. 19 0
      google_apps/AppTileView.swift
  2. 83 6
      google_apps/LauncherRootView.swift

+ 19 - 0
google_apps/AppTileView.swift

@@ -4,6 +4,7 @@ import AppKit
 struct AppTileView: View {
     let app: LauncherApp
     let onTap: () -> Void
+    let onHide: (() -> Void)?
 
     @State private var isHovering = false
     @AppStorage("pinnedTileIDsData") private var pinnedTileIDsData = ""
@@ -44,6 +45,8 @@ struct AppTileView: View {
                     Button("Add to StatusBar") { addToStatusBar() }
                     Button("Add to desktop") { addToDesktop() }
                     Button("Add Widget") { addWidget() }
+                    Divider()
+                    Button("Remove from View") { onHide?() }
                 } label: {
                     Image(systemName: "ellipsis")
                         .font(.system(size: 13, weight: .semibold))
@@ -74,6 +77,8 @@ struct AppTileView: View {
                 Button("Add to StatusBar") { addToStatusBar() }
                 Button("Add to desktop") { addToDesktop() }
                 Button("Add Widget") { addWidget() }
+                Divider()
+                Button("Remove from View") { onHide?() }
             }
         }
         .onHover { hovering in
@@ -209,6 +214,20 @@ struct AppTileView: View {
     }
 }
 
+extension AppTileView {
+    init(app: LauncherApp, onTap: @escaping () -> Void) {
+        self.app = app
+        self.onTap = onTap
+        self.onHide = nil
+    }
+
+    init(app: LauncherApp, onTap: @escaping () -> Void, onHide: @escaping () -> Void) {
+        self.app = app
+        self.onTap = onTap
+        self.onHide = onHide
+    }
+}
+
 private struct HideMenuIndicatorIfAvailable: ViewModifier {
     func body(content: Content) -> some View {
         if #available(macOS 13.0, *) {

+ 83 - 6
google_apps/LauncherRootView.swift

@@ -20,9 +20,14 @@ struct LauncherRootView: View {
     @AppStorage("customLauncherAppsData") private var customAppsData = ""
     @AppStorage("launcherAppOrderData") private var appOrderData = ""
     @AppStorage("pinnedTileIDsData") private var pinnedTileIDsData = ""
+    @AppStorage("statusBarAppIDsData") private var statusBarAppIDsData = ""
+    @AppStorage("widgetAppIDsData") private var widgetAppIDsData = ""
+    @AppStorage("hiddenLauncherAppIDsData") private var hiddenLauncherAppIDsData = ""
 
     private var apps: [LauncherApp] {
-        LauncherApp.sampleApps + customApps + [LauncherApp.createNewTile]
+        let hiddenIDs = decodeUUIDSet(from: hiddenLauncherAppIDsData)
+        let visibleApps = (LauncherApp.sampleApps + customApps).filter { !hiddenIDs.contains($0.id) }
+        return visibleApps + [LauncherApp.createNewTile]
     }
     private var columns: [GridItem] {
         [GridItem(.adaptive(minimum: 120, maximum: 145), spacing: 22)]
@@ -67,6 +72,15 @@ struct LauncherRootView: View {
         }
     }
 
+    private var hiddenApps: [LauncherApp] {
+        let hiddenIDs = decodeUUIDSet(from: hiddenLauncherAppIDsData)
+        guard !hiddenIDs.isEmpty else { return [] }
+        let allApps = LauncherApp.sampleApps + customApps
+        return allApps
+            .filter { hiddenIDs.contains($0.id) }
+            .sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
+    }
+
     var body: some View {
         ZStack {
             VisualEffectBlur(material: .hudWindow, blendingMode: .behindWindow)
@@ -79,6 +93,7 @@ struct LauncherRootView: View {
                 SearchHeader(
                     query: $query,
                     isPanelMode: layoutMode == .panel,
+                    hiddenApps: hiddenApps,
                     onToggleLayout: {
                         layoutMode = layoutMode == .grid ? .panel : .grid
                     },
@@ -89,6 +104,12 @@ struct LauncherRootView: View {
                         if let url = URL(string: "https://www.google.com") {
                             InAppBrowserWindowManager.shared.open(url: url, title: "Google")
                         }
+                    },
+                    onRestoreApp: { appID in
+                        restoreHiddenApp(id: appID)
+                    },
+                    onRestoreAll: {
+                        restoreAllHiddenApps()
                     }
                 )
                 .padding(.top, 12)
@@ -166,15 +187,18 @@ struct LauncherRootView: View {
                     },
                     draggedAppID: $draggedAppID,
                     lastDropDestinationAppID: $lastDropDestinationAppID,
-                    onMove: moveApp
+                    onMove: moveApp,
+                    onHideApp: hideApp
                 )
             } else {
                 ScrollView {
                     LazyVGrid(columns: columns, spacing: 20) {
                         ForEach(filteredApps) { app in
-                            AppTileView(app: app) {
-                                handleAppTap(app)
-                            }
+                            AppTileView(
+                                app: app,
+                                onTap: { handleAppTap(app) },
+                                onHide: { hideApp(id: app.id) }
+                            )
                             .onDrag {
                                 draggedAppID = app.id
                                 lastDropDestinationAppID = nil
@@ -286,6 +310,37 @@ struct LauncherRootView: View {
         saveAppOrderToStorage()
     }
 
+    private func hideApp(id: UUID) {
+        var hiddenIDs = decodeUUIDSet(from: hiddenLauncherAppIDsData)
+        guard hiddenIDs.insert(id).inserted else { return }
+        hiddenLauncherAppIDsData = encodeUUIDSetGlobal(hiddenIDs)
+        removeIDFromLinkedCollections(id)
+    }
+
+    private func restoreHiddenApp(id: UUID) {
+        var hiddenIDs = decodeUUIDSet(from: hiddenLauncherAppIDsData)
+        guard hiddenIDs.remove(id) != nil else { return }
+        hiddenLauncherAppIDsData = encodeUUIDSetGlobal(hiddenIDs)
+    }
+
+    private func restoreAllHiddenApps() {
+        hiddenLauncherAppIDsData = ""
+    }
+
+    private func removeIDFromLinkedCollections(_ id: UUID) {
+        var pinnedIDs = decodeUUIDSet(from: pinnedTileIDsData)
+        pinnedIDs.remove(id)
+        pinnedTileIDsData = encodeUUIDSetGlobal(pinnedIDs)
+
+        var statusBarIDs = decodeUUIDSet(from: statusBarAppIDsData)
+        statusBarIDs.remove(id)
+        statusBarAppIDsData = encodeUUIDSetGlobal(statusBarIDs)
+
+        var widgetIDs = decodeUUIDSet(from: widgetAppIDsData)
+        widgetIDs.remove(id)
+        widgetAppIDsData = encodeUUIDSetGlobal(widgetIDs)
+    }
+
     private func loadAppOrderFromStorage() {
         guard !appOrderData.isEmpty else {
             appOrder = []
@@ -377,9 +432,12 @@ private struct TileReorderDropDelegate: DropDelegate {
 private struct SearchHeader: View {
     @Binding var query: String
     let isPanelMode: Bool
+    let hiddenApps: [LauncherApp]
     let onToggleLayout: () -> Void
     let onOpenPremium: () -> Void
     let onOpenGoogle: () -> Void
+    let onRestoreApp: (UUID) -> Void
+    let onRestoreAll: () -> Void
 
     var body: some View {
         HStack {
@@ -419,6 +477,20 @@ private struct SearchHeader: View {
                     Button("Open Google") {
                         onOpenGoogle()
                     }
+                    if !hiddenApps.isEmpty {
+                        Divider()
+                        Menu("Manage Hidden Apps") {
+                            ForEach(hiddenApps) { app in
+                                Button("Add Back \(app.name)") {
+                                    onRestoreApp(app.id)
+                                }
+                            }
+                            Divider()
+                            Button("Restore All Hidden Apps") {
+                                onRestoreAll()
+                            }
+                        }
+                    }
                 } label: {
                     Image(systemName: "ellipsis")
                 }
@@ -437,6 +509,7 @@ private struct IconPanelView: View {
     @Binding var draggedAppID: UUID?
     @Binding var lastDropDestinationAppID: UUID?
     let onMove: (UUID, UUID) -> Void
+    let onHideApp: (UUID) -> Void
 
     var body: some View {
         ScrollView {
@@ -447,7 +520,8 @@ private struct IconPanelView: View {
                         onAppTap: onAppTap,
                         draggedAppID: $draggedAppID,
                         lastDropDestinationAppID: $lastDropDestinationAppID,
-                        onMove: onMove
+                        onMove: onMove,
+                        onHideApp: onHideApp
                     )
                 }
             }
@@ -463,6 +537,7 @@ private struct PanelAppRowView: View {
     @Binding var draggedAppID: UUID?
     @Binding var lastDropDestinationAppID: UUID?
     let onMove: (UUID, UUID) -> Void
+    let onHideApp: (UUID) -> Void
 
     @AppStorage("pinnedTileIDsData") private var pinnedTileIDsData = ""
     @AppStorage("statusBarAppIDsData") private var statusBarAppIDsData = ""
@@ -521,6 +596,8 @@ private struct PanelAppRowView: View {
                     Button("Add to StatusBar") { addToStatusBar() }
                     Button("Add to desktop") { addToDesktop() }
                     Button("Add Widget") { addWidget() }
+                    Divider()
+                    Button("Remove from View") { onHideApp(app.id) }
                 } label: {
                     Image(systemName: "ellipsis")
                         .font(.system(size: 13, weight: .semibold))