mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 03:48:55 +00:00
v3.6.0
This commit is contained in:
@@ -8,7 +8,6 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A272B03E71D00376976 /* MiniMode.swift */; };
|
||||
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A292B03FAF000376976 /* TopBarMini.swift */; };
|
||||
C7045A322B068AD700376976 /* NewWin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A312B068AD700376976 /* NewWin.swift */; };
|
||||
C7045A342B068B2900376976 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A332B068B2900376976 /* UpdateView.swift */; };
|
||||
C7045A362B068DD700376976 /* PermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A352B068DD700376976 /* PermView.swift */; };
|
||||
@@ -39,7 +38,6 @@
|
||||
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A9CE462B89164700EB6E78 /* Authorization.swift */; };
|
||||
C7ACADE62B92A737000B5845 /* Features.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ACADE52B92A737000B5845 /* Features.swift */; };
|
||||
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CF47232B3B3F1700979C5F /* WindowSettings.swift */; };
|
||||
C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D472AFEB23700C7ED9E /* TopBar.swift */; };
|
||||
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
|
||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
|
||||
C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */ = {isa = PBXBuildFile; fileRef = C7DC1FED2B9F7D3D009AC317 /* features.json */; };
|
||||
@@ -47,11 +45,11 @@
|
||||
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */; };
|
||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49EF2BABA14400CCBA16 /* Folders.swift */; };
|
||||
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */; };
|
||||
C7DE672D2BA6356500EB1633 /* MenuBarMiniAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672C2BA6356500EB1633 /* MenuBarMiniAppView.swift */; };
|
||||
C7ED86B12BA8A35500F13DE4 /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B02BA8A35500F13DE4 /* Interface.swift */; };
|
||||
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B22BA8ED8800F13DE4 /* Trash.swift */; };
|
||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
||||
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; };
|
||||
C7FEBA112BDC422200AE195F /* Searchbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FEBA102BDC422200AE195F /* Searchbar.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -99,7 +97,6 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
C7045A272B03E71D00376976 /* MiniMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMode.swift; sourceTree = "<group>"; };
|
||||
C7045A292B03FAF000376976 /* TopBarMini.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBarMini.swift; sourceTree = "<group>"; };
|
||||
C7045A312B068AD700376976 /* NewWin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewWin.swift; sourceTree = "<group>"; };
|
||||
C7045A332B068B2900376976 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; };
|
||||
C7045A352B068DD700376976 /* PermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermView.swift; sourceTree = "<group>"; };
|
||||
@@ -135,7 +132,6 @@
|
||||
C7A9CE462B89164700EB6E78 /* Authorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = "<group>"; };
|
||||
C7ACADE52B92A737000B5845 /* Features.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Features.swift; sourceTree = "<group>"; };
|
||||
C7CF47232B3B3F1700979C5F /* WindowSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowSettings.swift; sourceTree = "<group>"; };
|
||||
C7D31D472AFEB23700C7ED9E /* TopBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBar.swift; sourceTree = "<group>"; };
|
||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = "<group>"; };
|
||||
C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; };
|
||||
C7DC1FED2B9F7D3D009AC317 /* features.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = features.json; sourceTree = SOURCE_ROOT; };
|
||||
@@ -143,11 +139,11 @@
|
||||
C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReversePathsFetch.swift; sourceTree = "<group>"; };
|
||||
C7DD49EF2BABA14400CCBA16 /* Folders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folders.swift; sourceTree = "<group>"; };
|
||||
C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarItem.swift; sourceTree = "<group>"; };
|
||||
C7DE672C2BA6356500EB1633 /* MenuBarMiniAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarMiniAppView.swift; sourceTree = "<group>"; };
|
||||
C7ED86B02BA8A35500F13DE4 /* Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interface.swift; sourceTree = "<group>"; };
|
||||
C7ED86B22BA8ED8800F13DE4 /* Trash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trash.swift; sourceTree = "<group>"; };
|
||||
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
||||
C7FB173A2B96321300B96F9A /* AppsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsListView.swift; sourceTree = "<group>"; };
|
||||
C7FEBA102BDC422200AE195F /* Searchbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Searchbar.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -300,12 +296,10 @@
|
||||
C77B90222AF2D616009CC655 /* FilesView.swift */,
|
||||
C71848432B8D2D600046CB13 /* ZombieView.swift */,
|
||||
C76D08542AF89CDE00D07867 /* RegularMode.swift */,
|
||||
C7D31D472AFEB23700C7ED9E /* TopBar.swift */,
|
||||
C7045A292B03FAF000376976 /* TopBarMini.swift */,
|
||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
||||
C7045A272B03E71D00376976 /* MiniMode.swift */,
|
||||
C7FB173A2B96321300B96F9A /* AppsListView.swift */,
|
||||
C7DE672C2BA6356500EB1633 /* MenuBarMiniAppView.swift */,
|
||||
C7FEBA102BDC422200AE195F /* Searchbar.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -467,11 +461,9 @@
|
||||
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */,
|
||||
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */,
|
||||
C7DE672D2BA6356500EB1633 /* MenuBarMiniAppView.swift in Sources */,
|
||||
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */,
|
||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
||||
C77B901A2AF1938F009CC655 /* Logic.swift in Sources */,
|
||||
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */,
|
||||
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */,
|
||||
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */,
|
||||
C7ED86B12BA8A35500F13DE4 /* Interface.swift in Sources */,
|
||||
@@ -488,12 +480,12 @@
|
||||
C771CB762BD028AF00C33A18 /* SegmentedPicker.swift in Sources */,
|
||||
C74412C72BBF249000DDFCA8 /* AppPathsFetch.swift in Sources */,
|
||||
C79D05202BC99EB40083F976 /* ThemeManager.swift in Sources */,
|
||||
C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */,
|
||||
C74FDC632BCD833D00B8960F /* Conditions.swift in Sources */,
|
||||
C7ACADE62B92A737000B5845 /* Features.swift in Sources */,
|
||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */,
|
||||
C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */,
|
||||
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */,
|
||||
C7FEBA112BDC422200AE195F /* Searchbar.swift in Sources */,
|
||||
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */,
|
||||
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */,
|
||||
);
|
||||
@@ -564,8 +556,8 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUILD = 38;
|
||||
APP_VERSION = 3.5.4;
|
||||
APP_BUILD = 39;
|
||||
APP_VERSION = 3.6.0;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
@@ -634,8 +626,8 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUILD = 38;
|
||||
APP_VERSION = 3.5.4;
|
||||
APP_BUILD = 39;
|
||||
APP_VERSION = 3.6.0;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
|
||||
@@ -69,7 +69,7 @@ struct AppCommands: Commands {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
appState.sortedApps = sortedApps
|
||||
for app in appState.trashedFiles {
|
||||
AppPathFinder(appInfo: app, appState: appState, locations: locations).findPaths()
|
||||
AppPathFinder(appInfo: app, appState: appState, locations: locations, undo: true).findPaths()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,18 @@ class AppPathFinder {
|
||||
private var locations: Locations
|
||||
private var backgroundRun: Bool
|
||||
private var reverseAddon: Bool
|
||||
private var undo: Bool
|
||||
private var completion: () -> Void = {}
|
||||
private var collection: [URL] = []
|
||||
private let collectionAccessQueue = DispatchQueue(label: "com.alienator88.Pearcleaner.appPathFinder.collectionAccess")
|
||||
|
||||
init(appInfo: AppInfo = .empty, appState: AppState, locations: Locations, backgroundRun: Bool = false, reverseAddon: Bool = false, completion: @escaping () -> Void = {}) {
|
||||
init(appInfo: AppInfo = .empty, appState: AppState, locations: Locations, backgroundRun: Bool = false, reverseAddon: Bool = false, undo: Bool = false, completion: @escaping () -> Void = {}) {
|
||||
self.appInfo = appInfo
|
||||
self.appState = appState
|
||||
self.locations = locations
|
||||
self.backgroundRun = backgroundRun
|
||||
self.reverseAddon = reverseAddon
|
||||
self.undo = undo
|
||||
self.completion = completion
|
||||
}
|
||||
|
||||
@@ -306,7 +308,10 @@ class AppPathFinder {
|
||||
|
||||
if !self.backgroundRun {
|
||||
self.appState.appInfo = self.appInfo
|
||||
self.appState.selectedItems = Set(updatedCollection)
|
||||
if !self.undo {
|
||||
self.appState.selectedItems = Set(updatedCollection)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Append object to store if running reverse search with empty store
|
||||
|
||||
@@ -24,14 +24,13 @@ class AppState: ObservableObject
|
||||
@Published var currentView = CurrentDetailsView.empty
|
||||
@Published var showAlert: Bool = false
|
||||
@Published var sidebar: Bool = true
|
||||
// @Published var isReminderVisible: Bool = false
|
||||
@Published var releases = [Release]()
|
||||
@Published var progressBar: (String, Double) = ("Ready", 0.0)
|
||||
@Published var reload: Bool = false
|
||||
@Published var showProgress: Bool = false
|
||||
@Published var finderExtensionEnabled: Bool = false
|
||||
|
||||
|
||||
|
||||
init() {
|
||||
self.appInfo = AppInfo(
|
||||
id: UUID(),
|
||||
|
||||
@@ -39,6 +39,7 @@ class ReversePathsSearcher {
|
||||
let allNames = appState.appInfoStore.map { $0.appName.pearFormat() }
|
||||
|
||||
for location in locations.reverse.paths where fileManager.fileExists(atPath: location) {
|
||||
|
||||
dispatchGroup.enter()
|
||||
processLocation(location, allPaths: allPaths, allNames: allNames)
|
||||
dispatchGroup.leave()
|
||||
|
||||
@@ -11,22 +11,26 @@ import SwiftUI
|
||||
struct SimpleButtonStyle: ButtonStyle {
|
||||
@State private var hovered = false
|
||||
let icon: String
|
||||
let iconFlip: String
|
||||
let label: String
|
||||
let help: String
|
||||
let color: Color
|
||||
let size: CGFloat
|
||||
let padding: CGFloat
|
||||
|
||||
init(icon: String, label: String = "", help: String, color: Color = Color("mode"), size: CGFloat = 20) {
|
||||
init(icon: String, iconFlip: String = "", label: String = "", help: String, color: Color = Color("mode"), size: CGFloat = 20, padding: CGFloat = 5) {
|
||||
self.icon = icon
|
||||
self.iconFlip = iconFlip
|
||||
self.label = label
|
||||
self.help = help
|
||||
self.color = color
|
||||
self.size = size
|
||||
self.padding = padding
|
||||
}
|
||||
|
||||
func makeBody(configuration: Self.Configuration) -> some View {
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
HStack(alignment: .center) {
|
||||
Image(systemName: (hovered && !iconFlip.isEmpty) ? iconFlip : icon)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: size, height: size)
|
||||
@@ -35,7 +39,7 @@ struct SimpleButtonStyle: ButtonStyle {
|
||||
}
|
||||
}
|
||||
.foregroundColor(hovered ? color : color.opacity(0.5))
|
||||
.padding(5)
|
||||
.padding(padding)
|
||||
.onHover { hovering in
|
||||
withAnimation() {
|
||||
hovered = hovering
|
||||
@@ -121,6 +125,35 @@ struct ResetSettingsButtonStyle: ButtonStyle {
|
||||
}
|
||||
|
||||
|
||||
struct SimpleCheckboxToggleStyle: ToggleStyle {
|
||||
@EnvironmentObject private var themeSettings: ThemeSettings
|
||||
@State private var isHovered: Bool = false
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
HStack {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.fill(themeSettings.themeColor.darker(by: 5))
|
||||
.frame(width: 18, height: 18)
|
||||
.overlay {
|
||||
if configuration.isOn {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundStyle(Color("mode"))
|
||||
.scaleEffect(isHovered ? 0.8 : 1.0)
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation(.smooth()) {
|
||||
configuration.isOn.toggle()
|
||||
}
|
||||
}
|
||||
configuration.label
|
||||
}
|
||||
.onHover(perform: { hovering in
|
||||
self.isHovered = hovering
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct InfoButton: View {
|
||||
@State private var isPopoverPresented: Bool = false
|
||||
@@ -192,10 +225,40 @@ struct UninstallButton: ButtonStyle {
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 4)
|
||||
.background(configuration.isPressed ? Color("uninstall").opacity(0.8) : Color("uninstall"))
|
||||
// .background(
|
||||
// configuration.isPressed ? Color("uninstall").opacity(0.8) :
|
||||
// (isEnabled ? Color("uninstall") : Color.gray)
|
||||
// )
|
||||
.cornerRadius(8)
|
||||
.onHover { over in
|
||||
if over {
|
||||
NSCursor.pointingHand.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct RescanButton: ButtonStyle {
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
Image(systemName: "arrow.counterclockwise.circle")
|
||||
.foregroundColor(.white)
|
||||
|
||||
Divider()
|
||||
.frame(height: 24)
|
||||
.foregroundColor(.white)
|
||||
.opacity(0.5)
|
||||
|
||||
configuration.label
|
||||
.foregroundColor(.white)
|
||||
|
||||
}
|
||||
.frame(height: 24)
|
||||
.frame(minWidth: 75)
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 4)
|
||||
.background(configuration.isPressed ? Color("button").opacity(0.8) : Color("button"))
|
||||
.cornerRadius(8)
|
||||
.onHover { over in
|
||||
if over {
|
||||
@@ -475,68 +538,6 @@ struct AnimatedSearchStyle: TextFieldStyle {
|
||||
|
||||
|
||||
|
||||
|
||||
struct SimpleSearchStyle: TextFieldStyle {
|
||||
@State private var isHovered = false
|
||||
@FocusState private var isFocused: Bool
|
||||
@State var icon: Image?
|
||||
@State var trash: Bool = false
|
||||
@Binding var text: String
|
||||
@EnvironmentObject var appState: AppState
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
|
||||
func _body(configuration: TextField<Self._Label>) -> some View {
|
||||
|
||||
HStack(spacing: 5) {
|
||||
|
||||
HStack(spacing: 5) {
|
||||
if icon != nil {
|
||||
icon
|
||||
.foregroundColor(Color("mode").opacity(0.5))
|
||||
}
|
||||
|
||||
configuration
|
||||
.font(.title3)
|
||||
.textFieldStyle(PlainTextFieldStyle())
|
||||
|
||||
|
||||
if trash && text != "" {
|
||||
Image(systemName: "xmark")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 10, height: 10)
|
||||
.padding(.trailing, 5)
|
||||
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
||||
.onTapGesture {
|
||||
text = ""
|
||||
}
|
||||
.help("Clear search")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.padding(6)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.strokeBorder(Color("mode").opacity(0.2), lineWidth: 1)
|
||||
.allowsHitTesting(false)
|
||||
)
|
||||
.onHover { hovering in
|
||||
withAnimation(Animation.easeInOut(duration: 0.15)) {
|
||||
self.isHovered = hovering
|
||||
self.isFocused = true
|
||||
}
|
||||
}
|
||||
.focused($isFocused)
|
||||
.onAppear {
|
||||
updateOnMain {
|
||||
self.isFocused = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct GlassEffect: NSViewRepresentable {
|
||||
var material: NSVisualEffectView.Material // Choose the material you want
|
||||
var blendingMode: NSVisualEffectView.BlendingMode // Choose the blending mode
|
||||
|
||||
@@ -386,6 +386,9 @@ func removeApp(appState: AppState, withId id: UUID) {
|
||||
if brew {
|
||||
caskCleanup(app: appState.appInfo.appName)
|
||||
}
|
||||
|
||||
appState.appInfo = AppInfo.empty
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ struct PearcleanerApp: App {
|
||||
// Enable menubar item
|
||||
if menubarEnabled {
|
||||
MenuBarExtraManager.shared.addMenuBarExtra(withView: {
|
||||
MenuBarMiniAppView(search: $search, showPopover: $showPopover)
|
||||
MiniAppView(search: $search, showPopover: $showPopover)
|
||||
.environmentObject(locations)
|
||||
.environmentObject(appState)
|
||||
.environmentObject(fsm)
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x83",
|
||||
"red" : "0x24"
|
||||
"blue" : "0xE8",
|
||||
"green" : "0x7D",
|
||||
"red" : "0x4D"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
@@ -23,9 +23,9 @@
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x83",
|
||||
"red" : "0x24"
|
||||
"blue" : "0xBE",
|
||||
"green" : "0x67",
|
||||
"red" : "0x3F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
|
||||
@@ -428,7 +428,7 @@ struct InterfaceSettingsTab: View {
|
||||
.onChange(of: menubarEnabled) { newVal in
|
||||
if newVal {
|
||||
MenuBarExtraManager.shared.addMenuBarExtra(withView: {
|
||||
MenuBarMiniAppView(search: $search, showPopover: $showPopover)
|
||||
MiniAppView(search: $search, showPopover: $showPopover)
|
||||
.environmentObject(locations)
|
||||
.environmentObject(appState)
|
||||
.environmentObject(fsm)
|
||||
|
||||
@@ -25,7 +25,7 @@ struct AppListItems: View {
|
||||
let itemId = UUID()
|
||||
let appInfo: AppInfo
|
||||
var isSelected: Bool {
|
||||
appState.appInfo.id == appInfo.id
|
||||
appState.appInfo.path == appInfo.path
|
||||
}
|
||||
@State private var hoveredItemPath: URL? = nil
|
||||
var body: some View {
|
||||
@@ -39,7 +39,8 @@ struct AppListItems: View {
|
||||
Image(nsImage: appIcon)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: (isHovered || isSelected) ? 32 : 30, height: (isHovered || isSelected) ? 32 : 30)
|
||||
.frame(width: 30)
|
||||
// .frame(width: (isHovered || isSelected) ? 32 : 30, height: (isHovered || isSelected) ? 32 : 30)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
// .shadow(color: Color("pop").opacity(0.8), radius: isSelected ? 2 : 0)
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ struct AppsListView: View {
|
||||
SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover)
|
||||
}
|
||||
}
|
||||
// .padding(.horizontal)
|
||||
.padding(.top, !mini ? 4 : 0)
|
||||
}
|
||||
.scrollIndicators(.never)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SectionView: View {
|
||||
var title: String
|
||||
var count: Int
|
||||
@@ -51,7 +51,6 @@ struct SectionView: View {
|
||||
ForEach(apps, id: \.self) { appInfo in
|
||||
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
|
||||
if appInfo != apps.last {
|
||||
// Divider().padding(.horizontal, 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +58,72 @@ struct SectionView: View {
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct Header: View {
|
||||
let title: String
|
||||
let count: Int
|
||||
@State private var hovered = false
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var fsm: FolderSettingsManager
|
||||
@Binding var showPopover: Bool
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text("\(title)").foregroundStyle(Color("mode")).opacity(0.5)
|
||||
|
||||
Text("\(count)")
|
||||
.font(.system(size: 10))
|
||||
.monospacedDigit()
|
||||
.frame(minWidth: count > 99 ? 30 : 24, minHeight: 17)
|
||||
.background(Color("mode").opacity(0.1))
|
||||
.clipShape(.capsule)
|
||||
.padding(.leading, 2)
|
||||
|
||||
Spacer()
|
||||
|
||||
if hovered {
|
||||
Text("REFRESH")
|
||||
.font(.system(size: 10))
|
||||
.monospaced()
|
||||
.foregroundStyle(Color("mode").opacity(0.8))
|
||||
}
|
||||
|
||||
}
|
||||
.onHover { hovering in
|
||||
withAnimation() {
|
||||
hovered = hovering
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
// Refresh Apps list
|
||||
appState.reload.toggle()
|
||||
showPopover = false
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
appState.sortedApps = sortedApps
|
||||
appState.reload.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 20)
|
||||
.help("Click header or ⌘+R to refresh apps list")
|
||||
.padding(5)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//List {
|
||||
// Section {
|
||||
// ForEach(filteredUserApps, id:\.self) { element in
|
||||
|
||||
@@ -25,7 +25,7 @@ struct FilesView: View {
|
||||
var regularWin: Bool
|
||||
@State private var elapsedTime = 0
|
||||
@State private var timer: Timer? = nil
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
||||
@@ -80,25 +80,25 @@ struct FilesView: View {
|
||||
}
|
||||
} else {
|
||||
// Titlebar
|
||||
if !regularWin {
|
||||
HStack(spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
|
||||
Spacer()
|
||||
Spacer()
|
||||
|
||||
Button("Close") {
|
||||
updateOnMain {
|
||||
appState.appInfo = AppInfo.empty
|
||||
search = ""
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
}
|
||||
Button("Close") {
|
||||
updateOnMain {
|
||||
appState.appInfo = AppInfo.empty
|
||||
search = ""
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close"))
|
||||
}
|
||||
.padding([.horizontal, .top], 5)
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle", iconFlip: "x.circle.fill", help: "Close"))
|
||||
}
|
||||
.padding(.top, 6)
|
||||
.padding(.trailing, (mini || menubarEnabled) ? 6 : 0)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
|
||||
|
||||
// Main Group
|
||||
HStack(alignment: .center) {
|
||||
|
||||
@@ -149,7 +149,7 @@ struct FilesView: View {
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.top, 0)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,8 @@ struct FilesView: View {
|
||||
}
|
||||
}
|
||||
))
|
||||
.toggleStyle(SimpleCheckboxToggleStyle())
|
||||
.help("All checkboxes")
|
||||
|
||||
|
||||
Spacer()
|
||||
@@ -244,9 +246,11 @@ struct FilesView: View {
|
||||
let iconImage = fileIcon.map(Image.init(nsImage:))
|
||||
VStack {
|
||||
FileDetailsItem(size: fileSize, sizeL: fileSizeL, icon: iconImage, path: path)
|
||||
if index < sort.count - 1 {
|
||||
Divider().padding(.leading, 40).opacity(0.5)
|
||||
}
|
||||
.padding(.vertical, 5)
|
||||
// .padding(.leading, 40)
|
||||
// if index < sort.count - 1 {
|
||||
// Divider().padding(.leading, 40).opacity(0.5)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,25 +273,27 @@ struct FilesView: View {
|
||||
|
||||
Button("\(sizeType == "Logical" ? totalSelectedSize.logical : sizeType == "Finder" ? totalSelectedSize.finder : totalSelectedSize.real)") {
|
||||
Task {
|
||||
updateOnMain {
|
||||
search = ""
|
||||
if !regularWin {
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
} else {
|
||||
appState.currentView = .empty
|
||||
if appState.selectedItems.count == appState.appInfo.fileSize.keys.count {
|
||||
updateOnMain {
|
||||
search = ""
|
||||
if mini || menubarEnabled {
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
} else {
|
||||
appState.currentView = .empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let selectedItemsArray = Array(appState.selectedItems)
|
||||
|
||||
killApp(appId: appState.appInfo.bundleIdentifier) {
|
||||
moveFilesToTrash(at: selectedItemsArray) {
|
||||
withAnimation {
|
||||
showPopover = false
|
||||
updateOnMain {
|
||||
appState.currentView = mini ? .apps : .empty
|
||||
// appState.isReminderVisible.toggle()
|
||||
}
|
||||
// updateOnMain {
|
||||
// appState.currentView = mini ? .apps : .empty
|
||||
// }
|
||||
if sentinel {
|
||||
launchctl(load: true)
|
||||
}
|
||||
@@ -300,15 +306,20 @@ struct FilesView: View {
|
||||
removeApp(appState: appState, withId: appState.appInfo.id)
|
||||
} else {
|
||||
// Add deleted appInfo object to trashed array
|
||||
appState.appInfo.fileSize = [:]
|
||||
appState.trashedFiles.append(appState.appInfo)
|
||||
|
||||
// Clear out appInfoStore object
|
||||
// Clear out appInfoStore object (Used for leftover file search)
|
||||
if let index = appState.appInfoStore.firstIndex(where: { $0.path == appState.appInfo.path }) {
|
||||
appState.appInfoStore[index] = .empty
|
||||
}
|
||||
|
||||
updateOnMain {
|
||||
// Remove items from the list
|
||||
appState.appInfo.fileSize = appState.appInfo.fileSize.filter { !appState.selectedItems.contains($0.key) }
|
||||
// Update the selectedFiles to remove references that are no longer present
|
||||
appState.selectedItems.removeAll()
|
||||
}
|
||||
}
|
||||
appState.appInfo = AppInfo.empty
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,8 +331,6 @@ struct FilesView: View {
|
||||
.padding(.top)
|
||||
|
||||
|
||||
// Spacer()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -360,7 +369,7 @@ struct FileDetailsItem: View {
|
||||
}
|
||||
}
|
||||
))
|
||||
|
||||
.toggleStyle(SimpleCheckboxToggleStyle())
|
||||
.disabled(self.path.path.contains(".Trash"))
|
||||
|
||||
if let appIcon = icon {
|
||||
@@ -369,8 +378,6 @@ struct FileDetailsItem: View {
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 30, height: 30)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
.brightness(isHovered ? 0.2 : 0)
|
||||
.shadow(color: Color("mode"), radius: isHovered ? 2 : 0)
|
||||
|
||||
}
|
||||
|
||||
@@ -382,6 +389,17 @@ struct FileDetailsItem: View {
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.help(path.lastPathComponent)
|
||||
.overlay{
|
||||
if (isHovered) {
|
||||
VStack {
|
||||
Spacer()
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(Color("mode").opacity(0.5))
|
||||
.frame(height: 1.5)
|
||||
.offset(y: 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isNested(path: path) {
|
||||
InfoButton(text: "Application file is nested within subdirectories. To prevent deleting incorrect folders, Pearcleaner will leave these alone. You may manually delete the remaining folders if required.", color: nil, label: "")
|
||||
@@ -398,7 +416,17 @@ struct FileDetailsItem: View {
|
||||
.truncationMode(.tail)
|
||||
.opacity(0.5)
|
||||
.help(path.path)
|
||||
|
||||
}
|
||||
.onTapGesture {
|
||||
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
|
||||
}
|
||||
.onHover { hovering in
|
||||
withAnimation(Animation.easeIn(duration: 0.2)) {
|
||||
self.isHovered = hovering
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -409,14 +437,6 @@ struct FileDetailsItem: View {
|
||||
Text("\(displaySize)")
|
||||
|
||||
}
|
||||
.onHover { hovering in
|
||||
withAnimation(Animation.easeIn(duration: 0.2)) {
|
||||
self.isHovered = hovering
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
|
||||
}
|
||||
.contextMenu {
|
||||
if path.pathExtension == "app" {
|
||||
Button("Open \(path.deletingPathExtension().lastPathComponent)") {
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
//
|
||||
// MenuBarAppsListView.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 3/16/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct MenuBarMiniAppView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@State private var animateGradient: Bool = false
|
||||
@Binding var search: String
|
||||
@State private var showSys: Bool = true
|
||||
@State private var showUsr: Bool = true
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||
@AppStorage("settings.general.popover") private var popoverStay: Bool = true
|
||||
@Binding var showPopover: Bool
|
||||
@State private var showMenu = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
var filteredApps: [AppInfo] {
|
||||
if search.isEmpty {
|
||||
return appState.sortedApps
|
||||
} else {
|
||||
return appState.sortedApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||
}
|
||||
}
|
||||
|
||||
ZStack {
|
||||
HStack(spacing: 0){
|
||||
|
||||
if appState.reload {
|
||||
VStack {
|
||||
Spacer()
|
||||
ProgressView("Refreshing app list")
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
VStack(alignment: .center) {
|
||||
|
||||
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps).padding(.top, 8)
|
||||
|
||||
HStack(spacing: 10) {
|
||||
|
||||
Button("Leftover Files") {
|
||||
showMenu = false
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
showPopover = false
|
||||
updateOnMain() {
|
||||
appState.appInfo = .empty
|
||||
appState.selectedZombieItems = []
|
||||
if appState.zombieFile.fileSize.keys.count == 0 {
|
||||
appState.currentView = .zombie
|
||||
appState.showProgress.toggle()
|
||||
showPopover.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
} else {
|
||||
appState.currentView = .zombie
|
||||
showPopover.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
|
||||
.padding(.leading, 10)
|
||||
|
||||
SearchBarMiniBottom(search: $search)
|
||||
|
||||
Button("More") {
|
||||
self.showMenu.toggle()
|
||||
}
|
||||
.padding(.trailing, 10)
|
||||
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More"))
|
||||
.popover(isPresented: $showMenu) {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
if #available(macOS 14.0, *) {
|
||||
SettingsLink{
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "gear", label: "Settings", help: "Settings"))
|
||||
} else {
|
||||
Button("Settings") {
|
||||
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: NSApp.delegate, from: nil)
|
||||
showMenu = false
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "gear", label: "Settings", help: "Settings"))
|
||||
}
|
||||
|
||||
Button("Quit") {
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", label: "Quit Pearcleaner", help: "Quit Pearcleaner"))
|
||||
}
|
||||
.padding()
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.top, 5)
|
||||
|
||||
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 300, minHeight: 370)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
|
||||
.transition(.opacity)
|
||||
.popover(isPresented: $showPopover, arrowEdge: .leading) {
|
||||
VStack {
|
||||
if appState.currentView == .files {
|
||||
FilesView(showPopover: $showPopover, search: $search, regularWin: false)
|
||||
.id(appState.appInfo.id)
|
||||
} else if appState.currentView == .zombie {
|
||||
ZombieView(showPopover: $showPopover, search: $search, regularWin: false)
|
||||
.id(appState.appInfo.id)
|
||||
}
|
||||
|
||||
}
|
||||
.interactiveDismissDisabled(popoverStay)
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
|
||||
.frame(width: 650, height: 500)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,56 +28,17 @@ struct MiniMode: View {
|
||||
VStack(spacing: 0) {
|
||||
Group {
|
||||
if appState.currentView == .empty {
|
||||
TopBarMini(search: $search, showPopover: $showPopover)
|
||||
MiniEmptyView(showPopover: $showPopover)
|
||||
} else {
|
||||
TopBarMini(search: $search, showPopover: $showPopover)
|
||||
MiniAppView(search: $search, showPopover: $showPopover)
|
||||
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
}
|
||||
.popover(isPresented: $showPopover, arrowEdge: .trailing) {
|
||||
VStack {
|
||||
if appState.currentView == .files {
|
||||
FilesView(showPopover: $showPopover, search: $search, regularWin: false)
|
||||
.id(appState.appInfo.id)
|
||||
} else if appState.currentView == .zombie {
|
||||
ZombieView(showPopover: $showPopover, search: $search, regularWin: false)
|
||||
.id(appState.appInfo.id)
|
||||
}
|
||||
|
||||
}
|
||||
.interactiveDismissDisabled(popoverStay)
|
||||
// .background(Color("pop"))
|
||||
// .background(backgroundView(themeSettings: themeSettings, glass: false).padding(-80))
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
|
||||
// .background(
|
||||
// Group {
|
||||
// if glass {
|
||||
// backgroundView(themeSettings: themeSettings).padding(-80)
|
||||
//// GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all)
|
||||
// } else {
|
||||
// Rectangle()
|
||||
// .fill(Color("pop"))
|
||||
// .padding(-80)
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
.frame(width: 650, height: 550)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.frame(minWidth: 300, minHeight: 345)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass))
|
||||
// .background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
|
||||
// MARK: Background for whole app
|
||||
// .background(Color("bg").opacity(1))
|
||||
// .background(VisualEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all))
|
||||
|
||||
}
|
||||
}
|
||||
@@ -95,6 +56,7 @@ struct MiniEmptyView: View {
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true
|
||||
@Binding var showPopover: Bool
|
||||
@State private var animationStart = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
@@ -102,7 +64,7 @@ struct MiniEmptyView: View {
|
||||
Spacer()
|
||||
|
||||
if #available(macOS 14, *) {
|
||||
if animateLogo {
|
||||
if animateLogo && animationStart {
|
||||
LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
|
||||
.mask(
|
||||
Image(systemName: "plus.square.dashed")
|
||||
@@ -144,37 +106,29 @@ struct MiniEmptyView: View {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Text("Drop an app here")
|
||||
.font(.title3)
|
||||
.opacity(0.7)
|
||||
|
||||
Text("Click for apps list")
|
||||
.font(.footnote)
|
||||
.padding(.bottom, 25)
|
||||
.opacity(0.5)
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
// if appState.isReminderVisible {
|
||||
// Text("CMD + Z to undo")
|
||||
// .font(.title2)
|
||||
// .foregroundStyle(Color("mode").opacity(0.5))
|
||||
// .fontWeight(.medium)
|
||||
// .onAppear {
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||
// withAnimation {
|
||||
// updateOnMain {
|
||||
// appState.isReminderVisible = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Spacer()
|
||||
|
||||
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
appState.currentView = .apps
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
animationStart = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,52 +139,58 @@ struct MiniEmptyView: View {
|
||||
struct MiniAppView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@State private var animateGradient: Bool = false
|
||||
@Binding var search: String
|
||||
@State private var showSys: Bool = true
|
||||
@State private var showUsr: Bool = true
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||
@AppStorage("settings.general.popover") private var popoverStay: Bool = true
|
||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||
@AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280
|
||||
@Binding var showPopover: Bool
|
||||
|
||||
@State private var showMenu = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
var filteredApps: [AppInfo] {
|
||||
if search.isEmpty {
|
||||
return appState.sortedApps
|
||||
} else {
|
||||
return appState.sortedApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ZStack {
|
||||
HStack(spacing: 0){
|
||||
|
||||
if appState.reload {
|
||||
VStack {
|
||||
Spacer()
|
||||
ProgressView("Refreshing app list")
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
VStack(alignment: .center) {
|
||||
|
||||
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps)
|
||||
|
||||
if appState.currentView != .empty {
|
||||
SearchBarMiniBottom(search: $search)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 5)
|
||||
}
|
||||
}
|
||||
.padding(.bottom)
|
||||
if appState.reload {
|
||||
VStack {
|
||||
Spacer()
|
||||
ProgressView("Refreshing app list")
|
||||
Spacer()
|
||||
}
|
||||
|
||||
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
Searchbar(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover)
|
||||
}
|
||||
|
||||
}
|
||||
.transition(.opacity)
|
||||
.frame(minWidth: 300, minHeight: 370)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
|
||||
.transition(.opacity)
|
||||
.popover(isPresented: $showPopover, arrowEdge: .trailing) {
|
||||
VStack {
|
||||
if appState.currentView == .files {
|
||||
FilesView(showPopover: $showPopover, search: $search, regularWin: false)
|
||||
.id(appState.appInfo.id)
|
||||
} else if appState.currentView == .zombie {
|
||||
ZombieView(showPopover: $showPopover, search: $search, regularWin: false)
|
||||
.id(appState.appInfo.id)
|
||||
}
|
||||
|
||||
}
|
||||
.interactiveDismissDisabled(popoverStay)
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
|
||||
.frame(width: 650, height: 500)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,26 +11,22 @@ import SwiftUI
|
||||
struct RegularMode: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@EnvironmentObject var locations: Locations
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = false
|
||||
@AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280
|
||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@Binding var search: String
|
||||
@State private var showSys: Bool = true
|
||||
@State private var showUsr: Bool = true
|
||||
@Binding var showPopover: Bool
|
||||
|
||||
|
||||
var filteredApps: [AppInfo] {
|
||||
if search.isEmpty {
|
||||
return appState.sortedApps
|
||||
} else {
|
||||
return appState.sortedApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||
}
|
||||
}
|
||||
@State private var showMenu = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
HStack(alignment: .center, spacing: 0) {
|
||||
|
||||
// App List
|
||||
HStack(spacing: 0){
|
||||
|
||||
@@ -43,26 +39,10 @@ struct RegularMode: View {
|
||||
.frame(width: sidebarWidth)
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
VStack(alignment: .center) {
|
||||
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
HStack {
|
||||
SearchBarMiniBottom(search: $search)
|
||||
.padding(.horizontal)
|
||||
|
||||
}
|
||||
}
|
||||
.padding(.top, 20)
|
||||
|
||||
|
||||
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps)
|
||||
|
||||
}
|
||||
.frame(width: sidebarWidth)
|
||||
.padding(.vertical, 7)
|
||||
Searchbar(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover)
|
||||
.frame(width: sidebarWidth)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.background(backgroundView(themeSettings: themeSettings, darker: true, glass: glass))
|
||||
.transition(.opacity)
|
||||
@@ -73,22 +53,21 @@ struct RegularMode: View {
|
||||
|
||||
|
||||
// Details View
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
Group {
|
||||
if appState.currentView == .empty || appState.currentView == .apps {
|
||||
TopBar(showPopover: $showPopover)
|
||||
AppDetailsEmptyView(showPopover: $showPopover)
|
||||
} else if appState.currentView == .files {
|
||||
TopBar(showPopover: $showPopover)
|
||||
FilesView(showPopover: $showPopover, search: $search, regularWin: true)
|
||||
.id(appState.appInfo.id)
|
||||
} else if appState.currentView == .zombie {
|
||||
TopBar(showPopover: $showPopover)
|
||||
ZombieView(showPopover: $showPopover, search: $search, regularWin: true)
|
||||
.id(appState.appInfo.id)
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
Spacer()
|
||||
}
|
||||
.zIndex(2)
|
||||
.background(backgroundView(themeSettings: themeSettings))
|
||||
@@ -109,13 +88,14 @@ struct AppDetailsEmptyView: View {
|
||||
@EnvironmentObject var locations: Locations
|
||||
@AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true
|
||||
@Binding var showPopover: Bool
|
||||
@State private var animationStart = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Spacer()
|
||||
if #available(macOS 14, *) {
|
||||
if animateLogo {
|
||||
if animateLogo && animationStart {
|
||||
PearDropView()
|
||||
.phaseAnimator([false, true]) { wwdc24, chromaRotate in
|
||||
wwdc24
|
||||
@@ -139,75 +119,10 @@ struct AppDetailsEmptyView: View {
|
||||
.padding(.bottom, 25)
|
||||
.opacity(0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SearchBar: View {
|
||||
@Binding var search: String
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
TextField("Search", text: $search)
|
||||
.textFieldStyle(SimpleSearchStyle(icon: Image(systemName: "magnifyingglass"),trash: true, text: $search))
|
||||
}
|
||||
.frame(height: 30)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Header: View {
|
||||
let title: String
|
||||
let count: Int
|
||||
@State private var hovered = false
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var fsm: FolderSettingsManager
|
||||
@Binding var showPopover: Bool
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text("\(title)").foregroundStyle(Color("mode")).opacity(0.5)
|
||||
|
||||
Text("\(count)")
|
||||
.font(.system(size: 10))
|
||||
.monospacedDigit()
|
||||
.frame(minWidth: count > 99 ? 30 : 24, minHeight: 17)
|
||||
.background(Color("mode").opacity(0.1))
|
||||
.clipShape(.capsule)
|
||||
.padding(.leading, 2)
|
||||
|
||||
Spacer()
|
||||
|
||||
if hovered {
|
||||
Text("REFRESH")
|
||||
.font(.system(size: 10))
|
||||
.monospaced()
|
||||
.foregroundStyle(Color("mode").opacity(0.8))
|
||||
}
|
||||
|
||||
}
|
||||
.onHover { hovering in
|
||||
withAnimation() {
|
||||
hovered = hovering
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
animationStart = true
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
// Refresh Apps list
|
||||
appState.reload.toggle()
|
||||
showPopover = false
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
appState.sortedApps = sortedApps
|
||||
appState.reload.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 20)
|
||||
.help("Click header or ⌘+R to refresh apps list")
|
||||
.padding(5)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
//
|
||||
// Searchbar.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 4/26/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Searchbar: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@EnvironmentObject var locations: Locations
|
||||
var glass: Bool
|
||||
var sidebarWidth: Double
|
||||
var menubarEnabled: Bool
|
||||
var mini: Bool
|
||||
@Binding var search: String
|
||||
@Binding var showPopover: Bool
|
||||
@State private var showMenu = false
|
||||
@State private var showSys: Bool = true
|
||||
@State private var showUsr: Bool = true
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .center, spacing: 0) {
|
||||
Spacer()
|
||||
.frame(height: 10)
|
||||
|
||||
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps)
|
||||
.padding(.top, 25)
|
||||
|
||||
Divider()
|
||||
|
||||
HStack(spacing: 10) {
|
||||
|
||||
|
||||
SearchBar(search: $search, darker: (mini || menubarEnabled) ? false : true, glass: glass)
|
||||
|
||||
|
||||
if search.isEmpty {
|
||||
Button("More") {
|
||||
self.showMenu.toggle()
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More"))
|
||||
.popover(isPresented: $showMenu) {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
if mini && !menubarEnabled {
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
appState.currentView = .empty
|
||||
appState.appInfo = AppInfo.empty
|
||||
showPopover = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", label: "Drop Target", help: "Drop Target"))
|
||||
}
|
||||
|
||||
|
||||
Button("Leftover Files") {
|
||||
showMenu = false
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
showPopover = false
|
||||
appState.appInfo = .empty
|
||||
appState.selectedZombieItems = []
|
||||
if appState.zombieFile.fileSize.keys.isEmpty {
|
||||
appState.currentView = .zombie
|
||||
appState.showProgress.toggle()
|
||||
showPopover.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
} else {
|
||||
appState.currentView = .zombie
|
||||
showPopover.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "Leftover Files", help: "Leftover Files", size: 5))
|
||||
|
||||
|
||||
if #available(macOS 14.0, *) {
|
||||
SettingsLink {}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "Settings", help: "Settings", size: 5))
|
||||
} else {
|
||||
Button("Settings") {
|
||||
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: NSApp.delegate, from: nil)
|
||||
showMenu = false
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "Settings", help: "Settings", size: 5))
|
||||
}
|
||||
|
||||
if menubarEnabled {
|
||||
Button("Quit") {
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", label: "Quit Pearcleaner", help: "Quit Pearcleaner"))
|
||||
}
|
||||
|
||||
}
|
||||
.padding()
|
||||
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
.padding(.horizontal, search.isEmpty ? 10 : 5)
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
}
|
||||
|
||||
private var filteredApps: [AppInfo] {
|
||||
if search.isEmpty {
|
||||
return appState.sortedApps
|
||||
} else {
|
||||
return appState.sortedApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct SimpleSearchStyle: TextFieldStyle {
|
||||
@State private var isHovered = false
|
||||
@FocusState private var isFocused: Bool
|
||||
@State var trash: Bool = false
|
||||
@Binding var text: String
|
||||
@State var darker: Bool = false
|
||||
@State var glass: Bool = false
|
||||
@State var padding: CGFloat = 5
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
|
||||
func _body(configuration: TextField<Self._Label>) -> some View {
|
||||
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(darker ? themeSettings.themeColor.darker(by: 5) : themeSettings.themeColor)
|
||||
.allowsHitTesting(false)
|
||||
.frame(height: 30)
|
||||
.opacity(glass ? 0.0 : 1.0)
|
||||
|
||||
|
||||
ZStack {
|
||||
if text.isEmpty {
|
||||
HStack {
|
||||
Spacer()
|
||||
Text(isFocused ? "Type to search" : "Click to search")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color("mode").opacity(0.2))
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
configuration
|
||||
.font(.title3)
|
||||
.textFieldStyle(PlainTextFieldStyle())
|
||||
|
||||
Spacer()
|
||||
|
||||
if trash && text != "" {
|
||||
Button("") {
|
||||
text = ""
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "delete.left.fill", help: "Clear text", size: 14, padding: padding))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
|
||||
}
|
||||
.onHover { hovering in
|
||||
withAnimation(Animation.easeInOut(duration: 0.15)) {
|
||||
self.isHovered = hovering
|
||||
self.isFocused = true
|
||||
}
|
||||
}
|
||||
.focused($isFocused)
|
||||
.onAppear {
|
||||
updateOnMain {
|
||||
self.isFocused = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide blinking textfield caret
|
||||
extension NSTextView {
|
||||
open override var frame: CGRect {
|
||||
didSet {
|
||||
insertionPointColor = .clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SearchBar: View {
|
||||
@Binding var search: String
|
||||
@State var darker: Bool = false
|
||||
@State var glass: Bool = false
|
||||
@State var padding: CGFloat = 5
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
TextField("", text: $search)
|
||||
.textFieldStyle(SimpleSearchStyle(trash: true, text: $search, darker: darker, glass: glass, padding: padding))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
//
|
||||
// TopBar.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 11/10/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct TopBar: View {
|
||||
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = false
|
||||
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@EnvironmentObject var appState: AppState
|
||||
@Binding var showPopover: Bool
|
||||
@EnvironmentObject var locations: Locations
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: 0) {
|
||||
|
||||
// Spacer()
|
||||
//
|
||||
// if appState.isReminderVisible {
|
||||
// Text("CMD + Z to undo")
|
||||
// .font(.title2)
|
||||
// .foregroundStyle(Color("mode").opacity(0.5))
|
||||
// .fontWeight(.medium)
|
||||
// .onAppear {
|
||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||
// withAnimation {
|
||||
// appState.isReminderVisible = false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack() {
|
||||
Spacer()
|
||||
|
||||
if appState.currentView == .zombie {
|
||||
Button("Rescan") {
|
||||
updateOnMain {
|
||||
appState.zombieFile = .empty
|
||||
appState.showProgress.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
|
||||
}
|
||||
|
||||
if appState.currentView != .zombie {
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
updateOnMain {
|
||||
appState.appInfo = .empty
|
||||
appState.selectedZombieItems = []
|
||||
if appState.zombieFile.fileSize.keys.count == 0 {
|
||||
appState.currentView = .zombie
|
||||
appState.showProgress.toggle()
|
||||
showPopover.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
} else {
|
||||
appState.currentView = .zombie
|
||||
showPopover.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
|
||||
}
|
||||
|
||||
|
||||
if appState.currentView == .files || appState.currentView == .zombie {
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
appState.currentView = .empty
|
||||
appState.appInfo = AppInfo.empty
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target"))
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.padding(6)
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
//
|
||||
// TopBarMini.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 11/14/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct TopBarMini: View {
|
||||
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@Binding var search: String
|
||||
@Binding var showPopover: Bool
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: 0) {
|
||||
|
||||
Spacer()
|
||||
|
||||
if appState.currentView == .empty {
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
showPopover = false
|
||||
updateOnMain {
|
||||
if appState.zombieFile.fileSize.keys.count == 0 {
|
||||
appState.currentView = .zombie
|
||||
appState.showProgress.toggle()
|
||||
showPopover.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
} else {
|
||||
appState.currentView = .zombie
|
||||
showPopover.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
|
||||
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
appState.currentView = .apps
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "square.grid.3x3.square", help: "Apps List"))
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if appState.currentView != .empty {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
showPopover = false
|
||||
updateOnMain {
|
||||
appState.appInfo = .empty
|
||||
appState.selectedZombieItems = []
|
||||
if appState.zombieFile.fileSize.keys.count == 0 {
|
||||
appState.currentView = .zombie
|
||||
appState.showProgress.toggle()
|
||||
showPopover.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
} else {
|
||||
appState.currentView = .zombie
|
||||
showPopover.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
|
||||
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
appState.currentView = .empty
|
||||
appState.appInfo = AppInfo.empty
|
||||
showPopover = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct SearchBarMini: View {
|
||||
@Binding var search: String
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
TextField("Search", text: $search)
|
||||
.textFieldStyle(AnimatedSearchStyle(text: $search))
|
||||
}
|
||||
.frame(height: 20)
|
||||
}
|
||||
}
|
||||
|
||||
struct SearchBarMiniBottom: View {
|
||||
@Binding var search: String
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
TextField(" Search", text: $search)
|
||||
.textFieldStyle(SimpleSearchStyle(trash: true, text: $search))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ struct ZombieView: View {
|
||||
@EnvironmentObject var locations: Locations
|
||||
@State private var showPop: Bool = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = false
|
||||
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||
@@ -108,31 +109,22 @@ struct ZombieView: View {
|
||||
}
|
||||
} else {
|
||||
// Titlebar
|
||||
if !regularWin {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
Button("Rescan") {
|
||||
updateOnMain {
|
||||
appState.zombieFile = .empty
|
||||
appState.showProgress.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
}
|
||||
Button("Close") {
|
||||
updateOnMain {
|
||||
appState.appInfo = AppInfo.empty
|
||||
search = ""
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
|
||||
|
||||
Button("Close") {
|
||||
updateOnMain {
|
||||
appState.appInfo = AppInfo.empty
|
||||
search = ""
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close"))
|
||||
}
|
||||
.padding([.horizontal, .top], 5)
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle", iconFlip: "x.circle.fill", help: "Close"))
|
||||
}
|
||||
.padding(.top, 6)
|
||||
.padding(.trailing, (mini || menubarEnabled) ? 6 : 0)
|
||||
|
||||
|
||||
VStack() {
|
||||
// Main Group
|
||||
@@ -141,7 +133,7 @@ struct ZombieView: View {
|
||||
VStack(alignment: .center) {
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Image(systemName: "clock.arrow.circlepath")
|
||||
Image(systemName: "doc.badge.clock.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 50, height: 50)
|
||||
@@ -185,9 +177,11 @@ struct ZombieView: View {
|
||||
}
|
||||
}
|
||||
))
|
||||
.toggleStyle(SimpleCheckboxToggleStyle())
|
||||
.help("All checkboxes")
|
||||
|
||||
SearchBarMiniBottom(search: $searchZ)
|
||||
// .padding(.top)
|
||||
|
||||
SearchBar(search: $searchZ, darker: true, glass: glass)
|
||||
.padding(.horizontal)
|
||||
|
||||
|
||||
@@ -212,11 +206,7 @@ struct ZombieView: View {
|
||||
let iconImage = fileIcon.map(Image.init(nsImage:))
|
||||
VStack {
|
||||
ZombieFileDetailsItem(size: fileSize, sizeL: fileSizeL, icon: iconImage, path: file)
|
||||
.padding(.trailing)
|
||||
|
||||
if index < filteredAndSortedFiles.0.count - 1 {
|
||||
Divider().padding(.leading, 40).opacity(0.5)
|
||||
}
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,8 +216,9 @@ struct ZombieView: View {
|
||||
.onChange(of: sizeType) { _ in
|
||||
localKey = UUID()
|
||||
}
|
||||
.id(localKey)
|
||||
}
|
||||
.id(localKey)
|
||||
|
||||
|
||||
|
||||
Spacer()
|
||||
@@ -236,30 +227,45 @@ struct ZombieView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
Button("Rescan") {
|
||||
updateOnMain {
|
||||
appState.zombieFile = .empty
|
||||
appState.showProgress.toggle()
|
||||
reversePreloader(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
|
||||
}
|
||||
}
|
||||
.buttonStyle(RescanButton())
|
||||
|
||||
Button("\(sizeType == "Logical" ? totalSelectedZombieSize.logical : sizeType == "Finder" ? totalSelectedZombieSize.finder : totalSelectedZombieSize.real)") {
|
||||
Task {
|
||||
updateOnMain {
|
||||
appState.zombieFile = .empty
|
||||
search = ""
|
||||
if !regularWin {
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
} else {
|
||||
appState.currentView = .empty
|
||||
if appState.selectedZombieItems.count == appState.zombieFile.fileSize.keys.count {
|
||||
updateOnMain {
|
||||
appState.zombieFile = .empty
|
||||
search = ""
|
||||
searchZ = ""
|
||||
if mini || menubarEnabled {
|
||||
appState.currentView = .apps
|
||||
showPopover = false
|
||||
} else {
|
||||
appState.currentView = .empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let selectedItemsArray = Array(appState.selectedZombieItems)
|
||||
|
||||
killApp(appId: appState.appInfo.bundleIdentifier) {
|
||||
moveFilesToTrash(at: selectedItemsArray) {
|
||||
withAnimation {
|
||||
showPopover = false
|
||||
// updateOnMain {
|
||||
// appState.isReminderVisible.toggle()
|
||||
// }
|
||||
}
|
||||
moveFilesToTrash(at: selectedItemsArray) {
|
||||
withAnimation {
|
||||
showPopover = false
|
||||
}
|
||||
updateOnMain {
|
||||
// Remove items from the list
|
||||
appState.zombieFile.fileSize = appState.zombieFile.fileSize.filter { !appState.selectedZombieItems.contains($0.key) }
|
||||
// Update the selectedZombieFiles to remove references that are no longer present
|
||||
appState.selectedZombieItems.removeAll()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -267,20 +273,16 @@ struct ZombieView: View {
|
||||
}
|
||||
.buttonStyle(UninstallButton(isEnabled: !appState.selectedZombieItems.isEmpty))
|
||||
.disabled(appState.selectedZombieItems.isEmpty)
|
||||
.padding(.top)
|
||||
|
||||
|
||||
// Spacer()
|
||||
|
||||
}
|
||||
|
||||
.padding(.top)
|
||||
}
|
||||
.transition(.opacity)
|
||||
.padding([.horizontal, .bottom], 20)
|
||||
.padding(.top, !mini ? 10 : 0)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,6 +311,7 @@ struct ZombieFileDetailsItem: View {
|
||||
}
|
||||
}
|
||||
))
|
||||
.toggleStyle(SimpleCheckboxToggleStyle())
|
||||
|
||||
if let appIcon = icon {
|
||||
appIcon
|
||||
@@ -316,10 +319,9 @@ struct ZombieFileDetailsItem: View {
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 30, height: 30)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
.brightness(isHovered ? 0.2 : 0)
|
||||
.shadow(color: Color("mode"), radius: isHovered ? 2 : 0)
|
||||
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
|
||||
HStack(alignment: .center) {
|
||||
@@ -328,6 +330,17 @@ struct ZombieFileDetailsItem: View {
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.help(path.lastPathComponent)
|
||||
.overlay{
|
||||
if (isHovered) {
|
||||
VStack {
|
||||
Spacer()
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(Color("mode").opacity(0.5))
|
||||
.frame(height: 1.5)
|
||||
.offset(y: 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let imageView = folderImages(for: path.path) {
|
||||
imageView
|
||||
@@ -341,6 +354,14 @@ struct ZombieFileDetailsItem: View {
|
||||
.opacity(0.5)
|
||||
.help(path.path)
|
||||
}
|
||||
.onHover { hovering in
|
||||
withAnimation(Animation.easeIn(duration: 0.2)) {
|
||||
self.isHovered = hovering
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -351,14 +372,6 @@ struct ZombieFileDetailsItem: View {
|
||||
Text("\(displaySize)")
|
||||
|
||||
}
|
||||
.onHover { hovering in
|
||||
withAnimation(Animation.easeIn(duration: 0.2)) {
|
||||
self.isHovered = hovering
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
|
||||
}
|
||||
.contextMenu {
|
||||
if path.pathExtension == "app" {
|
||||
Button("Open \(path.deletingPathExtension().lastPathComponent)") {
|
||||
|
||||
Reference in New Issue
Block a user