From 0f0475f4362334437b429b6670e21be56119a39a Mon Sep 17 00:00:00 2001 From: Alin Date: Mon, 3 Feb 2025 10:26:35 -0700 Subject: [PATCH] v4.1.0 --- Pearcleaner.xcodeproj/project.pbxproj | 8 +- Pearcleaner/Logic/AppState.swift | 1 + Pearcleaner/Logic/Styles.swift | 10 +- Pearcleaner/PearcleanerApp.swift | 1 - Pearcleaner/Views/AppListItems.swift | 230 ++++++++++++++------------ Pearcleaner/Views/FilesView.swift | 44 ++--- Pearcleaner/Views/RegularMode.swift | 2 +- Pearcleaner/Views/ZombieView.swift | 4 +- 8 files changed, 160 insertions(+), 140 deletions(-) diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index 89663004..99e1b119 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -557,8 +557,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 72; - APP_VERSION = 4.0.6; + APP_BUILD = 73; + APP_VERSION = 4.1.0; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; @@ -629,8 +629,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 72; - APP_VERSION = 4.0.6; + APP_BUILD = 73; + APP_VERSION = 4.1.0; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; diff --git a/Pearcleaner/Logic/AppState.swift b/Pearcleaner/Logic/AppState.swift index e6317512..b31bdc9a 100644 --- a/Pearcleaner/Logic/AppState.swift +++ b/Pearcleaner/Logic/AppState.swift @@ -27,6 +27,7 @@ class AppState: ObservableObject { @Published var finderExtensionEnabled: Bool = false @Published var showUninstallAlert: Bool = false @Published var externalMode: Bool = false + @Published var multiMode: Bool = false @Published var externalPaths: [URL] = [] // for handling multiple app from drops or deeplinks @Published var selectedEnvironment: PathEnv? // for handling dev environments diff --git a/Pearcleaner/Logic/Styles.swift b/Pearcleaner/Logic/Styles.swift index 08ebd43f..b8538302 100644 --- a/Pearcleaner/Logic/Styles.swift +++ b/Pearcleaner/Logic/Styles.swift @@ -271,6 +271,10 @@ struct SimpleCheckboxToggleStyle: ToggleStyle { .scaleEffect(isHovered ? 0.8 : 1.0) } } + .overlay { + RoundedRectangle(cornerRadius: 4) + .strokeBorder(themeManager.pickerColor.adjustBrightness(-5.0), lineWidth: 1) + } .onTapGesture { withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { configuration.isOn.toggle() @@ -378,7 +382,7 @@ public struct SlideableDivider: View { } .contextMenu { Button("Reset Size") { - dimension = 280 + dimension = 300 } } .gesture(drag) @@ -395,8 +399,8 @@ public struct SlideableDivider: View { let newDimension = dimensionStart! + Double(delta) // Set minimum and maximum width - let minWidth: Double = 220 - let maxWidth: Double = 330 + let minWidth: Double = 240 + let maxWidth: Double = 350 dimension = max(minWidth, min(maxWidth, newDimension)) NSCursor.closedHand.set() handleWidth = 6 diff --git a/Pearcleaner/PearcleanerApp.swift b/Pearcleaner/PearcleanerApp.swift index b6e84007..f17e38d4 100644 --- a/Pearcleaner/PearcleanerApp.swift +++ b/Pearcleaner/PearcleanerApp.swift @@ -96,7 +96,6 @@ struct PearcleanerApp: App { return true } - .onOpenURL(perform: { url in let deeplinkManager = DeeplinkManager(showPopover: $showPopover, updater: updater, fsm: fsm) deeplinkManager.manage(url: url, appState: appState, locations: locations) diff --git a/Pearcleaner/Views/AppListItems.swift b/Pearcleaner/Views/AppListItems.swift index 44a5942d..0f2c0ecc 100644 --- a/Pearcleaner/Views/AppListItems.swift +++ b/Pearcleaner/Views/AppListItems.swift @@ -31,135 +31,149 @@ struct AppListItems: View { var body: some View { - Button(action: { - if !isSelected { - withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { - showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover) + HStack { + + Toggle(isOn: Binding( + get: { self.appState.externalPaths.contains(self.appInfo.path) }, + set: { isChecked in + if isChecked { + if !self.appState.externalPaths.contains(self.appInfo.path) { + let wasEmpty = self.appState.externalPaths.isEmpty + self.appState.externalPaths.append(self.appInfo.path) + + if wasEmpty && appState.currentView != .files { + appState.multiMode = true + showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover) + } + } + } else { + self.appState.externalPaths.removeAll { $0 == self.appInfo.path } + + if self.appState.externalPaths.isEmpty { + appState.multiMode = false + } + } } - } else { - withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { - updateOnMain { - appState.appInfo = .empty - appState.selectedItems = [] - appState.currentView = miniView ? .apps : .empty - showPopover = false + )) { EmptyView() } + .toggleStyle(SimpleCheckboxToggleStyle()) + .padding(.leading) + + Button(action: { + if !isSelected { + withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { + showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover) + } + } else { + withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { + updateOnMain { + appState.appInfo = .empty + appState.selectedItems = [] + appState.currentView = miniView ? .apps : .empty + showPopover = false + } } } - } - }) { - VStack() { + }) { + VStack() { - HStack(alignment: .center) { + HStack(alignment: .center) { - if let appIcon = appInfo.appIcon { - ZStack { - Image(nsImage: appIcon) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 30) - .clipShape(RoundedRectangle(cornerRadius: 8)) - } - - } - if minimalEnabled { - Text(appInfo.appName) - .font(.system(size: (isSelected) ? 14 : 12)) - .lineLimit(1) - .truncationMode(.tail) - } else { - VStack(alignment: .center, spacing: 2) { - HStack { - Text(appInfo.appName) - .font(.system(size: (isSelected) ? 14 : 12)) - .lineLimit(1) - .truncationMode(.tail) - Spacer() - } - HStack(spacing: 5) { - Text(verbatim: "v\(appInfo.appVersion)") - .font(.footnote) - .lineLimit(1) - .truncationMode(.tail) - .opacity(0.5) - Text(verbatim: "•").font(.footnote).opacity(0.5) - - Text(appInfo.bundleSize == 0 ? String(localized: "calculating") : "\(formatByte(size: appInfo.bundleSize).human)") - .font(.footnote) - .lineLimit(1) - .truncationMode(.tail) - .opacity(0.5) - Spacer() + if let appIcon = appInfo.appIcon { + ZStack { + Image(nsImage: appIcon) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 30) + .clipShape(RoundedRectangle(cornerRadius: 8)) } } - } + if minimalEnabled { + Text(appInfo.appName) + .font(.system(size: (isSelected) ? 14 : 12)) + .lineLimit(1) + .truncationMode(.tail) + } else { + VStack(alignment: .center, spacing: 2) { + HStack { + Text(appInfo.appName) + .font(.system(size: (isSelected) ? 14 : 12)) + .lineLimit(1) + .truncationMode(.tail) + Spacer() + } + + HStack(spacing: 5) { + Text(verbatim: "v\(appInfo.appVersion)") + .font(.footnote) + .lineLimit(1) + .truncationMode(.tail) + .opacity(0.5) + Text(verbatim: "•").font(.footnote).opacity(0.5) + + Text(appInfo.bundleSize == 0 ? String(localized: "calculating") : "\(formatByte(size: appInfo.bundleSize).human)") + .font(.footnote) + .lineLimit(1) + .truncationMode(.tail) + .opacity(0.5) + Spacer() + } - if appInfo.webApp { - Image(systemName: "safari") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 16, height: 16) - .foregroundStyle(.primary.opacity(0.3)) - .symbolRenderingMode(.monochrome) - .help("Web app") - .padding(.trailing, 5) - } - if appInfo.wrapped { - Image(systemName: "iphone") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 16, height: 16) - .foregroundStyle(.primary.opacity(0.3)) - .symbolRenderingMode(.monochrome) - .help("iOS app") - .padding(.trailing, 5) - } + } + } - if minimalEnabled { - Spacer() - } - if minimalEnabled && !isSelected { - // Text(appInfo.bundleSize == 0 ? "v\(appInfo.appVersion)" : (isHovered ? "v\(appInfo.appVersion)" : formatByte(size: appInfo.bundleSize).human)) - Text(appInfo.bundleSize == 0 ? "v\(appInfo.appVersion)" : formatByte(size: appInfo.bundleSize).human) - .font(.system(size: 10)) - .foregroundStyle(.primary.opacity(0.5)) - } + if appInfo.webApp { + Image(systemName: "safari") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 16, height: 16) + .foregroundStyle(.primary.opacity(0.3)) + .symbolRenderingMode(.monochrome) + .help("Web app") + .padding(.trailing, 5) + } + if appInfo.wrapped { + Image(systemName: "iphone") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 16, height: 16) + .foregroundStyle(.primary.opacity(0.3)) + .symbolRenderingMode(.monochrome) + .help("iOS app") + .padding(.trailing, 5) + } -// if isSelected && !(mini || menubarEnabled) { -// Button("Close") { -// withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { -// updateOnMain { -// appState.appInfo = .empty -// appState.selectedItems = [] -// appState.currentView = miniView ? .apps : .empty -// showPopover = false -// } -// } -// -// } -// .buttonStyle(SimpleButtonStyle(icon: "x.circle", iconFlip: "x.circle.fill", help: String(localized: "Close"), size: 16)) -// } + if minimalEnabled { + Spacer() + } + if minimalEnabled && !isSelected { + Text(appInfo.bundleSize == 0 ? "v\(appInfo.appVersion)" : formatByte(size: appInfo.bundleSize).human) + .font(.system(size: 10)) + .foregroundStyle(.primary.opacity(0.5)) + } + } } - + .frame(height: 35) + .padding(.trailing) + .padding(.vertical, 5) } - .frame(height: 35) - .padding(.horizontal) - .padding(.vertical, 5) - } - .buttonStyle(.borderless) - .foregroundStyle(.primary) - .onHover { hovering in - withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.20 : 0)) { - self.isHovered = hovering - self.hoveredItemPath = isHovered ? appInfo.path : nil + .buttonStyle(.borderless) + .foregroundStyle(.primary) + .onHover { hovering in + withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.20 : 0)) { + self.isHovered = hovering + self.hoveredItemPath = isHovered ? appInfo.path : nil + } } + + } .background{ Rectangle() diff --git a/Pearcleaner/Views/FilesView.swift b/Pearcleaner/Views/FilesView.swift index d36d9ab4..95ac6862 100644 --- a/Pearcleaner/Views/FilesView.swift +++ b/Pearcleaner/Views/FilesView.swift @@ -468,25 +468,26 @@ struct FilesView: View { } // Process the next app if in external mode - if appState.externalMode { - processNextExternalApp(appWasRemoved: appWasRemoved) - } else { - // Not in external mode - if appWasRemoved { - // Now update the UI to reflect that the app has been removed - updateOnMain { - search = "" - withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { - if mini || menubarEnabled { - appState.currentView = .apps - showPopover = false - } else { - appState.currentView = .empty - } - } - } - } - } + processNextExternalApp(appWasRemoved: appWasRemoved) +// if appState.externalMode || appState.multiMode { +// processNextExternalApp(appWasRemoved: appWasRemoved) +// } else { +// // Not in external mode +// if appWasRemoved { +// // Now update the UI to reflect that the app has been removed +// updateOnMain { +// search = "" +// withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) { +// if mini || menubarEnabled { +// appState.currentView = .apps +// showPopover = false +// } else { +// appState.currentView = .empty +// } +// } +// } +// } +// } } } } @@ -518,13 +519,14 @@ struct FilesView: View { } // Terminate if oneShotMode is enabled - if oneShotMode { + if oneShotMode && !appState.multiMode { updateOnMain(after: 2) { NSApp.terminate(nil) } } else { - // Reset external mode + // Reset external/multi mode appState.externalMode = false + appState.multiMode = false } } else if let nextPath = appState.externalPaths.first { // More paths exist; continue processing diff --git a/Pearcleaner/Views/RegularMode.swift b/Pearcleaner/Views/RegularMode.swift index 309a5cb8..a6c73df7 100644 --- a/Pearcleaner/Views/RegularMode.swift +++ b/Pearcleaner/Views/RegularMode.swift @@ -15,7 +15,7 @@ struct RegularMode: View { @EnvironmentObject var locations: Locations @EnvironmentObject var fsm: FolderSettingsManager @AppStorage("settings.general.glass") private var glass: Bool = false - @AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280 + @AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 300 @AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.interface.animationEnabled") private var animationEnabled: Bool = true diff --git a/Pearcleaner/Views/ZombieView.swift b/Pearcleaner/Views/ZombieView.swift index d380a401..14220f21 100644 --- a/Pearcleaner/Views/ZombieView.swift +++ b/Pearcleaner/Views/ZombieView.swift @@ -129,7 +129,7 @@ struct ZombieView: View { // Item selection and sorting toolbar HStack { Toggle(isOn: selectAllBinding) { EmptyView() } -// .toggleStyle(SimpleCheckboxToggleStyle()) + .toggleStyle(SimpleCheckboxToggleStyle()) .help("All checkboxes") SearchBar(search: $searchZ, darker: true, glass: glass, sidebar: false) @@ -439,7 +439,7 @@ struct ZombieFileDetailsItem: View { HStack(alignment: .center, spacing: 20) { Toggle(isOn: $isSelected) { EmptyView() } -// .toggleStyle(SimpleCheckboxToggleStyle()) + .toggleStyle(SimpleCheckboxToggleStyle()) if let appIcon = icon { appIcon