This commit is contained in:
Alin
2024-04-12 16:44:53 -06:00
parent 9124d85c5d
commit bf7771cf61
28 changed files with 1014 additions and 412 deletions
+5 -4
View File
@@ -41,15 +41,16 @@ struct AppCommands: Commands {
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps = []
// appState.sortedApps.systemApps = []
appState.sortedApps = sortedApps
// appState.sortedApps.systemApps = sortedApps.systemApps
if instantSearch {
Task(priority: .high){
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations)
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations) {
appState.reload.toggle()
}
}
} else {
appState.reload.toggle()
}
appState.reload.toggle()
}
}
} label: {
+17
View File
@@ -7,6 +7,7 @@
import Foundation
import SwiftUI
import FinderSync
let home = FileManager.default.homeDirectoryForCurrentUser.path
@@ -31,6 +32,7 @@ class AppState: ObservableObject
@Published var popCount: Int = 0
@Published var instantProgress: Double = 0.0
@Published var instantTotal: Double = 0.0
@Published var finderExtensionEnabled: Bool = false
init() {
@@ -54,6 +56,21 @@ class AppState: ObservableObject
fileSize: [:],
fileIcon: [:]
)
updateExtensionStatus()
NotificationCenter.default.addObserver(
self,
selector: #selector(updateExtensionStatus),
name: NSApplication.didBecomeActiveNotification,
object: nil
)
}
@objc func updateExtensionStatus() {
let extentionStatus = FIFinderSyncController.isExtensionEnabled
DispatchQueue.main.async {
self.finderExtensionEnabled = extentionStatus
}
}
}
-1
View File
@@ -26,7 +26,6 @@ class DeeplinkManager {
// This handles dropping an app onto Pearcleaner
if url.pathExtension == "app" {
handleAppBundle(url: url, appState: appState, locations: locations)
// This handles sentinel monitor launch
} else if url.scheme == DeepLinkConstants.scheme,
url.host == DeepLinkConstants.host,
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
+76 -65
View File
@@ -11,15 +11,17 @@ import SwiftUI
struct SimpleButtonStyle: ButtonStyle {
@State private var hovered = false
let icon: String
let label: String?
let label: String
let help: String
let color: Color
let size: CGFloat
init(icon: String, label: String? = "", help: String, color: Color) {
init(icon: String, label: String = "", help: String, color: Color = Color("mode"), size: CGFloat = 20) {
self.icon = icon
self.label = label
self.help = help
self.color = color
self.size = size
}
func makeBody(configuration: Self.Configuration) -> some View {
@@ -27,8 +29,8 @@ struct SimpleButtonStyle: ButtonStyle {
Image(systemName: icon)
.resizable()
.scaledToFit()
.frame(width: 20)
if let label = label, !label.isEmpty {
.frame(width: size, height: size)
if !label.isEmpty {
Text(label)
}
}
@@ -45,34 +47,6 @@ struct SimpleButtonStyle: ButtonStyle {
}
struct NavButtonBottomBarStyle: ButtonStyle {
@State private var isHovered = false
var image: String
var help: String
func makeBody(configuration: Configuration) -> some View {
ZStack {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
Rectangle()
.foregroundColor(.clear)
.frame(width: 36, height: 36)
.cornerRadius(6)
.contentShape(Rectangle())
}
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.buttonStyle(.plain)
.onHover { inside in
isHovered = inside
}
.help(help)
}
}
struct InfoButton: View {
@State private var isPopoverPresented: Bool = false
@@ -215,7 +189,7 @@ public struct NewFeatureView: View {
showFeature = false
}
}
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: .gray))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: (Color("mode").opacity(0.5))))
.onHover { isHovered in
if isHovered {
NSCursor.pointingHand.push()
@@ -249,43 +223,36 @@ public struct NewFeatureView: View {
public struct SlideableDivider: View {
@EnvironmentObject private var themeSettings: ThemeSettings
@Binding var dimension: Double
@State private var dimensionStart: Double?
@State private var handleWidth: Double = 2
@State private var handleWidth: Double = 4
@State private var handleHeight: Double = 30
@State private var isHovered: Bool = false
public init(dimension: Binding<Double>) {
self._dimension = dimension
}
public var body: some View {
Rectangle()
.foregroundStyle(Color.gray.opacity(0.3))
.frame(width: 1)
.overlay(
VStack(spacing: 0) {
Spacer()
RoundedRectangle(cornerRadius: 50)
.fill(Color("mode").opacity(0.3))
.frame(width: handleWidth, height: handleHeight)
.onHover { inside in
if inside {
NSCursor.resizeLeftRight.push()
.foregroundStyle(Color("mode").opacity(0.0))
.frame(width: 10)
.onHover { inside in
if inside {
NSCursor.resizeLeftRight.push()
} else {
NSCursor.pop()
} else {
NSCursor.pop()
}
}
.contextMenu {
Button("Reset Size") {
dimension = 280
}
}
.gesture(drag)
Spacer()
}
.offset(x: 5)
)
}
.contextMenu {
Button("Reset Size") {
dimension = 280
}
}
.gesture(drag)
.help("Right click to reset size")
}
var drag: some Gesture {
@@ -302,13 +269,13 @@ public struct SlideableDivider: View {
let maxWidth: Double = 350
dimension = max(minWidth, min(maxWidth, newDimension))
NSCursor.closedHand.set()
handleWidth = 4
handleWidth = 6
handleHeight = 40
}
.onEnded { val in
dimensionStart = nil
NSCursor.arrow.set()
handleWidth = 2
handleWidth = 4
handleHeight = 30
}
}
@@ -323,18 +290,18 @@ struct LabeledDivider: View {
HStack(spacing: 0) {
Rectangle()
.frame(height: 1)
.foregroundColor(.gray.opacity(0.2))
.foregroundColor(Color("mode").opacity(0.2))
Text(label)
.textCase(.uppercase)
.font(.title2)
.foregroundColor(.gray.opacity(0.6))
.foregroundColor(Color("mode").opacity(0.6))
.padding(.horizontal, 10)
.frame(minWidth: 80)
Rectangle()
.frame(height: 1)
.foregroundColor(.gray.opacity(0.2))
.foregroundColor(Color("mode").opacity(0.2))
}
.frame(minHeight: 35)
}
@@ -497,7 +464,7 @@ struct SimpleSearchStyle: TextFieldStyle {
}
RoundedRectangle(cornerRadius: 6)
.strokeBorder(Color.gray.opacity(0.2), lineWidth: 1)
.strokeBorder(Color("mode").opacity(0.2), lineWidth: 1)
.allowsHitTesting(false)
}
}
@@ -767,7 +734,7 @@ struct PillPicker: View {
Text(i == 0 ? "Apps" : (i == 1 ? "Widgets" : "Plugins"))
.font(.system(size: 12))
.foregroundColor(index == i ? (colorScheme == .dark ? .black : Color("AccentColor")) : .gray)
.foregroundColor(index == i ? (colorScheme == .dark ? .black : Color("AccentColor")) : Color("mode"))
}
.padding(5)
.frame(height: 25)
@@ -826,3 +793,47 @@ struct PearDropView: View {
}
}
// Used for testing UI bounds
extension View {
func bounds() -> some View {
self.border(Color.red, width: 1)
}
}
@ViewBuilder
func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: Bool = false) -> some View {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
darker ? themeSettings.themeColor.darker(by: 5).edgesIgnoringSafeArea(.all) : themeSettings.themeColor.edgesIgnoringSafeArea(.all)
}
}
struct PresetColor: ButtonStyle {
var fillColor: Color
var label: String
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .center, spacing: 10) {
Circle()
.frame(width: 20, height: 20)
.fixedSize(horizontal: true, vertical: true)
.foregroundColor(fillColor)
.background(fillColor)
.clipShape(Circle())
.help(label)
.overlay(
RoundedRectangle(cornerRadius: 25)
.strokeBorder(Color("mode").opacity(0.8), lineWidth: 1.5))
// Text(label)
}
.onHover { inside in inside ? NSCursor.pointingHand.push() : NSCursor.pop() }
}
}
+59
View File
@@ -0,0 +1,59 @@
//
// ThemeManager.swift
// Pearcleaner
//
// Created by Alin Lupascu on 4/12/24.
//
import Foundation
import SwiftUI
import AppKit
class ThemeSettings: ObservableObject {
static let shared = ThemeSettings()
private let userDefaults = UserDefaults.standard
private let colorKey = "themeColor"
@Published var themeColor: Color {
didSet {
saveThemeColor()
}
}
// light - Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
// dark - Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1)
init() {
// Initialize color from UserDefaults or use a default value
if let components = UserDefaults.standard.object(forKey: colorKey) as? [CGFloat], components.count >= 4 {
themeColor = Color(.sRGB, red: components[0], green: components[1], blue: components[2], opacity: components[3])
} else {
themeColor = Color.clear
}
}
func saveThemeColor() {
let nsColor = NSColor(themeColor)
if let components = nsColor.cgColor.components {
UserDefaults.standard.set(components, forKey: colorKey)
}
}
func resetToDefault(dark: Bool = true) {
themeColor = dark ? Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
saveThemeColor()
}
}
extension Color {
func darker(by percentage: CGFloat = 30.0) -> Color {
var hsb = (hue: CGFloat(0), saturation: CGFloat(0), brightness: CGFloat(0), alpha: CGFloat(0))
NSColor(self).getHue(&hsb.hue, saturation: &hsb.saturation, brightness: &hsb.brightness, alpha: &hsb.alpha)
return Color(hue: hsb.hue, saturation: hsb.saturation, brightness: max(hsb.brightness - percentage/100, 0), opacity: hsb.alpha)
}
func lighter(by percentage: CGFloat = 30.0) -> Color {
var hsb = (hue: CGFloat(0), saturation: CGFloat(0), brightness: CGFloat(0), alpha: CGFloat(0))
NSColor(self).getHue(&hsb.hue, saturation: &hsb.saturation, brightness: &hsb.brightness, alpha: &hsb.alpha)
return Color(hue: hsb.hue, saturation: hsb.saturation, brightness: min(hsb.brightness + percentage / 100, 1.0), opacity: hsb.alpha)
}
}
+4
View File
@@ -59,6 +59,7 @@ struct PearcleanerApp: App {
.environmentObject(appState)
.environmentObject(locations)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
.onOpenURL(perform: { url in
@@ -114,6 +115,7 @@ struct PearcleanerApp: App {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}, icon: selectedMenubarIcon)
}
@@ -170,6 +172,7 @@ struct PearcleanerApp: App {
.environmentObject(appState)
.environmentObject(locations)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.toolbarBackground(.clear)
.preferredColorScheme(displayMode.colorScheme)
}
@@ -192,6 +195,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
findAndHideWindows(named: ["Pearcleaner"])
NSApplication.shared.setActivationPolicy(.accessory)
}
}
+4
View File
@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleShortVersionString</key>
<string>$(APP_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(APP_BUILD)</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
+8 -8
View File
@@ -53,14 +53,14 @@ struct AboutSettingsTab: View {
Text("Microsoft Designer")
Text("Application icon resource")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Button(""){
NSWorkspace.shared.open(URL(string: "https://designer.microsoft.com/image-creator")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
@@ -78,14 +78,14 @@ struct AboutSettingsTab: View {
Text("Namelix")
Text("Logo and branding generation")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Button(""){
NSWorkspace.shared.open(URL(string: "https://namelix.com/")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
@@ -103,7 +103,7 @@ struct AboutSettingsTab: View {
Text("Privacy Guides")
Text("Inspired by open-source appcleaner script from Sun Knudsen")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
.lineLimit(2)
}
@@ -112,7 +112,7 @@ struct AboutSettingsTab: View {
Button("") {
NSWorkspace.shared.open(URL(string: "https://sunknudsen.com/privacy-guides/how-to-clean-uninstall-macos-apps-using-appcleaner-open-source-alternative")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
@@ -130,14 +130,14 @@ struct AboutSettingsTab: View {
Text("AppCleaner")
Text("Inspired by AppCleaner from Freemacsoft")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Button(""){
NSWorkspace.shared.open(URL(string: "https://freemacsoft.net/appcleaner/")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View"))
}
.padding(5)
+53 -35
View File
@@ -8,6 +8,7 @@
import Foundation
import SwiftUI
import ServiceManagement
import FinderSync
struct GeneralSettingsTab: View {
@EnvironmentObject var appState: AppState
@@ -25,7 +26,7 @@ struct GeneralSettingsTab: View {
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
// @State private var finderExtensionEnabled: Bool = false
@State private var diskStatus: Bool = false
@State private var accessStatus: Bool = false
private let themes = ["Auto", "Dark", "Light"]
@@ -49,11 +50,11 @@ struct GeneralSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(instantSearch ? "Instant search is enabled" : "Instant search is disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When instant search is enabled, all application files are gathered and cached for later use on startup instead of on each app click. There might be a slight delay of a few seconds when launching Pearcleaner depending on the amount of apps you have installed.", color: nil, label: "")
@@ -73,11 +74,11 @@ struct GeneralSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(brew ? "Homebrew cleanup is enabled" : "Homebrew cleanup is disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force", color: nil, label: "")
@@ -97,11 +98,11 @@ struct GeneralSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("File list sorting mode")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When searching for app files or leftover files, the list will be sorted based on this choice", color: nil, label: "")
Spacer()
@@ -114,28 +115,6 @@ struct GeneralSettingsTab: View {
}
.padding(5)
.padding(.leading)
// HStack(spacing: 0) {
// Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123")
// .resizable()
// .scaledToFit()
// .frame(width: 20, height: 20)
// .padding(.trailing)
// .foregroundStyle(.gray)
// VStack(alignment: .leading, spacing: 5) {
// Text("\(selectedSortAlpha ? "File list sorted alphabetically" : "File list sorted by size")")
// .font(.callout)
// .foregroundStyle(.gray)
// }
//
// InfoButton(text: "When searching for app files or leftover files, the list will be sorted based on this choice", color: nil, label: "")
//
// Spacer()
// Toggle(isOn: $selectedSortAlpha, label: {
// })
// .toggleStyle(.switch)
// }
// .padding(5)
// .padding(.leading)
// === Perms ================================================================================================
@@ -163,7 +142,7 @@ struct GeneralSettingsTab: View {
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1)
Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission **NOT** granted")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Spacer()
Button("") {
@@ -171,7 +150,7 @@ struct GeneralSettingsTab: View {
NSWorkspace.shared.open(url)
}
}
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View disk permissions pane", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View disk permissions pane"))
}
.padding(5)
@@ -188,7 +167,7 @@ struct GeneralSettingsTab: View {
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1)
Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission **NOT** granted")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Spacer()
Button("") {
@@ -196,7 +175,7 @@ struct GeneralSettingsTab: View {
NSWorkspace.shared.open(url)
}
}
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View accessibility permissions pane", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "View accessibility permissions pane"))
}
.padding(5)
@@ -224,7 +203,7 @@ struct GeneralSettingsTab: View {
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1)
Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Spacer()
Toggle(isOn: $sentinel, label: {
@@ -244,6 +223,44 @@ struct GeneralSettingsTab: View {
// === Finder Extension =============================================================================================
Divider()
.padding()
HStack() {
Text("Finder Extension").font(.title2)
Spacer()
}
.padding(.leading)
HStack(spacing: 0) {
Image(systemName: appState.finderExtensionEnabled ? "puzzlepiece.extension.fill" : "puzzlepiece.extension")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(appState.finderExtensionEnabled ? .green : .red)
Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled")
.font(.callout)
.foregroundStyle(Color("mode").opacity(0.5))
InfoButton(text: "Enabling the extension will allow you to right click apps in Finder and quickly uninstall them with Pearcleaner", color: nil, label: "")
Spacer()
Button("Extensions") {
FIFinderSyncController.showExtensionManagementInterface()
}
.buttonStyle(SimpleButtonStyle(icon: "folder", help: "Show Extensions Pane"))
}
.padding(5)
.padding(.leading)
@@ -253,11 +270,12 @@ struct GeneralSettingsTab: View {
.onAppear {
diskStatus = checkAndRequestFullDiskAccess(appState: appState, skipAlert: true)
accessStatus = checkAndRequestAccessibilityAccess(appState: appState)
appState.updateExtensionStatus()
}
}
.padding(20)
.frame(width: 500, height: 460)
.frame(width: 500, height: 560)
}
+94 -49
View File
@@ -14,7 +14,9 @@ struct InterfaceSettingsTab: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations
@EnvironmentObject var fsm: FolderSettingsManager
@EnvironmentObject var themeSettings: ThemeSettings
@State private var windowSettings = WindowSettings()
@Environment(\.colorScheme) var colorScheme: ColorScheme
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("displayMode") var displayMode: DisplayMode = .system
@@ -31,7 +33,16 @@ struct InterfaceSettingsTab: View {
let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"]
private let themes = ["Auto", "Dark", "Light"]
// @State private var slate = Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1)
// @State private var solarized = Color(.sRGB, red: 0.117257, green: 0.22506, blue: 0.249171, opacity: 1)
// @State private var dracula = Color(.sRGB, red: 0.268614, green: 0.264737, blue: 0.383503, opacity: 1)
var body: some View {
let slate = displayMode.colorScheme == .light ? Color(.sRGB, red: 0.499549, green: 0.545169, blue: 0.682028, opacity: 1) : Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1)
let solarized = displayMode.colorScheme == .light ? Color(.sRGB, red: 0.554372, green: 0.6557, blue: 0.734336, opacity: 1) : Color(.sRGB, red: 0.117257, green: 0.22506, blue: 0.249171, opacity: 1)
let dracula = displayMode.colorScheme == .light ? Color(.sRGB, red: 0.567094, green: 0.562125, blue: 0.81285, opacity: 1) : Color(.sRGB, red: 0.268614, green: 0.264737, blue: 0.383503, opacity: 1)
Form {
VStack {
@@ -47,11 +58,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(glass ? "Transparent material enabled" : "Transparent material disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Toggle(isOn: $glass, label: {
@@ -65,6 +76,57 @@ struct InterfaceSettingsTab: View {
.padding(.leading)
HStack(spacing: 0) {
Image(systemName: "paintbrush")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("Application base color")
.font(.callout)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When using a custom color, you might need to change the application color mode below to Dark or Light so text is readable", color: nil, label: "")
Spacer()
Button("") {
themeSettings.themeColor = slate
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: slate, label: "Slate"))
Button("") {
themeSettings.themeColor = dracula
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: dracula, label: "Dracula"))
.padding(.horizontal)
Button("") {
themeSettings.themeColor = solarized
themeSettings.saveThemeColor()
}
.buttonStyle(PresetColor(fillColor: solarized, label: "Solarized"))
Spacer()
ColorPicker("", selection: $themeSettings.themeColor, supportsOpacity: false)
.onChange(of: themeSettings.themeColor) { newValue in
themeSettings.saveThemeColor()
}
.padding(.horizontal, 5)
Button("") {
themeSettings.resetToDefault(dark: colorScheme == .dark)
}
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reset color to default"))
}
.padding(5)
.padding(.leading)
HStack(spacing: 0) {
Image(systemName: {
@@ -81,12 +143,13 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("Application color mode")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "Changing the color mode will reset the base color to defaults", color: nil, label: "")
Spacer()
Picker("", selection: $selectedTheme) {
ForEach(themes, id: \.self) { theme in
@@ -99,6 +162,7 @@ struct InterfaceSettingsTab: View {
switch newTheme {
case "Auto":
displayMode.colorScheme = nil
themeSettings.resetToDefault(dark: isDarkModeEnabled())
// Refresh foreground colors
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface
@@ -108,11 +172,19 @@ struct InterfaceSettingsTab: View {
}
case "Dark":
displayMode.colorScheme = .dark
themeSettings.resetToDefault(dark: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface
}
if menubarEnabled{
MenuBarExtraManager.shared.restartMenuBarExtra()
}
case "Light":
displayMode.colorScheme = .light
themeSettings.resetToDefault(dark: false)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.selectedTab = .interface
}
if menubarEnabled{
MenuBarExtraManager.shared.restartMenuBarExtra()
}
@@ -145,11 +217,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(mini ? "Mini window mode" : "Full size window mode")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Toggle(isOn: $mini, label: {
@@ -167,6 +239,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
updateOnMain(after: 0.1, {
@@ -184,6 +257,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
updateOnMain(after: 0.1, {
@@ -204,11 +278,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(miniView ? "Show apps list on startup" : "Show drop target on startup")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.", color: nil, label: "")
@@ -231,11 +305,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(popoverStay ? "Popover window will stay on top" : "Popover window will not stay on top")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.", color: nil, label: "")
@@ -267,11 +341,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(menubarEnabled ? "Menubar icon enabled" : "Menubar icon disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "When menubar icon is enabled, the main app window and dock icon will be disabled since the app will be put in accessory mode.", color: nil, label: "")
Spacer()
@@ -285,6 +359,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}, icon: selectedMenubarIcon)
NSApplication.shared.setActivationPolicy(.accessory)
@@ -300,6 +375,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner")
@@ -309,6 +385,7 @@ struct InterfaceSettingsTab: View {
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}
resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner")
@@ -329,11 +406,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("\(isLaunchAtLoginEnabled ? "Launch at login enabled" : "Launch at login disabled")")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
InfoButton(text: "This setting will affect Pearcleaner whether you're running in menubar mode or regular mode. If you disable menubar icon, you might want to disable this as well so Pearcleaner doesn't start on login.", color: nil, label: "")
Spacer()
@@ -370,11 +447,11 @@ struct InterfaceSettingsTab: View {
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) {
Text("Menubar icon")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
Picker("", selection: $selectedMenubarIcon) {
@@ -403,44 +480,12 @@ struct InterfaceSettingsTab: View {
.padding(5)
.padding(.leading)
// HStack(spacing: 0) {
// Image(systemName: "dock.rectangle")
// .resizable()
// .scaledToFit()
// .frame(width: 20, height: 20)
// .padding(.trailing)
// .foregroundStyle(.gray)
// VStack(alignment: .leading, spacing: 5) {
// Text("\(dockEnabled ? "Show dock icon" : "Hide dock icon")")
// .font(.callout)
// .foregroundStyle(.gray)
// }
// InfoButton(text: "This setting only affects Pearcleaner when the menubar icon is enabled, otherwise the dock icon will always show", color: nil, label: "")
// Spacer()
// Toggle(isOn: $dockEnabled, label: {
// })
// .toggleStyle(.switch)
// .onChange(of: dockEnabled) { newValue in
// if newValue {
// NSApplication.shared.setActivationPolicy(.regular)
// } else {
// if menubarEnabled {
// NSApplication.shared.setActivationPolicy(.accessory)
// }
//
// }
// }
//
// }
// .padding(5)
// .padding(.leading)
Spacer()
}
}
.padding(20)
.frame(width: 500, height: 520)
.frame(width: 500, height: 550)
}
+3 -1
View File
@@ -10,6 +10,7 @@ import SwiftUI
struct SettingsView: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var fsm: FolderSettingsManager
@EnvironmentObject var themeSettings: ThemeSettings
@Binding var showPopover: Bool
@Binding var search: String
@Binding var showFeature: Bool
@@ -49,7 +50,8 @@ struct SettingsView: View {
}
.tag(CurrentTabView.about)
}
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
.background(backgroundView(themeSettings: themeSettings, glass: glass))
// .background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
}
+5 -5
View File
@@ -66,7 +66,7 @@ struct UpdateSettingsTab: View {
Text("Showing last 3 releases")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
@@ -76,28 +76,28 @@ struct UpdateSettingsTab: View {
Button(""){
loadGithubReleases(appState: appState)
}
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reload release notes", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reload release notes"))
Spacer()
Button(""){
showFeature.toggle()
}
.buttonStyle(SimpleButtonStyle(icon: "star", help: "Show last feature alert", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "star", help: "Show last feature alert"))
Spacer()
Button(""){
loadGithubReleases(appState: appState, manual: true)
}
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.square", help: "Check for updates", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.square", help: "Check for updates"))
Spacer()
Button(""){
NSWorkspace.shared.open(URL(string: "https://github.com/alienator88/Pearcleaner/releases")!)
}
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View releases on GitHub", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "link", help: "View releases on GitHub"))
Spacer()
}
+52 -24
View File
@@ -10,13 +10,14 @@ import SwiftUI
struct AppListItems: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var themeSettings: ThemeSettings
@Binding var search: String
@State private var isHovered = false
@State private var windowSettings = WindowSettings()
@Environment(\.colorScheme) var colorScheme
// @AppStorage("settings.general.mini") private var mini: Bool = false
// @AppStorage("settings.general.popover") private var popoverStay: Bool = true
@AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.miniview") private var miniView: Bool = true
@AppStorage("settings.general.mini") private var mini: Bool = false
@Binding var showPopover: Bool
@EnvironmentObject var locations: Locations
let itemId = UUID()
@@ -24,15 +25,12 @@ struct AppListItems: View {
var isSelected: Bool {
appState.appInfo.path == appInfo.path
}
@State private var hoveredItemPath: URL? = nil
var body: some View {
// VStack(alignment: .leading, spacing: 5) {
HStack(alignment: .center) {
if isHovered || isSelected {
if (isHovered || isSelected) && mini {
RoundedRectangle(cornerRadius: 50)
.fill(isSelected ? Color("pear") : Color("mode").opacity(0.5))
.frame(width: isSelected ? 4 : 2, height: 25)
@@ -41,24 +39,12 @@ struct AppListItems: View {
if let appIcon = appInfo.appIcon {
ZStack {
// RoundedRectangle(cornerRadius: 8)
// .fill(Color(appIcon.averageColor!))
// .frame(width: 35, height: 35)
// .saturation(3)
// .opacity(0.5)
//// .brightness(displayMode.colorScheme == .dark ? 0 : 0.5)
//// .shadow(color: .black, radius: 1, y: 2)
// RoundedRectangle(cornerRadius: 8)
// .strokeBorder(Color("mode").opacity(0.1), lineWidth: 1)
// .frame(width: 35, height: 35)
Image(nsImage: appIcon)
.resizable()
.aspectRatio(contentMode: .fit)
// .scaledToFit()
.frame(width: 30, height: 30)
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(color: .black.opacity(0.5), radius: 2, y: 2)
// .grayscale(opacityForItem())
}
}
@@ -100,31 +86,73 @@ struct AppListItems: View {
.font(.footnote)
.foregroundStyle(Color("mode").opacity(0.5))
if (isHovered || isSelected) && !mini {
Triangle()
.fill(isSelected ? themeSettings.themeColor : Color("mode").opacity(0.1))
.frame(width: isSelected ? 16 : 8, height: 30)
.padding(.leading, 5)
.offset(x: 22)
.zIndex(5)
}
}
.padding(.horizontal, 5)
.help(appInfo.appName)
.onHover { hovering in
withAnimation(Animation.easeIn(duration: 0.2)) {
self.isHovered = hovering
self.hoveredItemPath = isHovered ? appInfo.path : nil
}
}
.onTapGesture {
withAnimation(Animation.easeInOut(duration: 0.4)) {
showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover)
if isSelected {
appState.appInfo = .empty
appState.currentView = miniView ? .apps : .empty
showPopover = false
} else {
showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover)
}
}
}
// .grayscale(opacityForItem(appInfo.path))
}
func opacityForItem() -> Double {
func opacityForItem(_ path: URL) -> Double {
// Check if any item is selected
let isAnyItemSelected = appState.sortedApps.contains(where: { $0.path == appState.appInfo.path })
// If this item is selected or no items are selected, keep full opacity
// Otherwise, reduce opacity
return isAnyItemSelected ? (appState.appInfo.path == appInfo.path ? 0 : 1.0) : 0
let isItemSelected = appState.appInfo.path == path
let isItemHovered = hoveredItemPath == path
// Logic to determine grayscale level
if isItemSelected || !isAnyItemSelected || isItemHovered {
return 0 // No grayscale
} else {
return 1.0 // Full grayscale
}
// return isAnyItemSelected ? (appState.appInfo.path == appInfo.path ? 0 : 1.0) : 0
}
}
struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
// Start at the top right
path.move(to: CGPoint(x: rect.maxX, y: rect.minY))
// Add line to the bottom right
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
// Add line to the left point
path.addLine(to: CGPoint(x: rect.minX, y: rect.midY))
// Close the path
path.closeSubpath()
return path
}
}
+4
View File
@@ -11,6 +11,8 @@ import SwiftUI
struct AppsListView: View {
@Binding var search: String
@Binding var showPopover: Bool
@AppStorage("settings.general.mini") private var mini: Bool = false
var filteredApps: [AppInfo]
var body: some View {
@@ -23,11 +25,13 @@ struct AppsListView: View {
SectionView(title: "User", count: filteredUserApps.count, apps: filteredUserApps, search: $search, showPopover: $showPopover)
}
if !filteredSystemApps.isEmpty {
SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover)
}
}
.padding(.horizontal)
.padding(.top, !mini ? 4 : 0)
}
.scrollIndicators(.never)
}
+20 -24
View File
@@ -38,23 +38,17 @@ struct FilesView: View {
VStack {
Spacer()
Text("Searching the file system").font(.title3)
.foregroundStyle((.gray.opacity(0.8)))
.foregroundStyle((Color("mode").opacity(0.5)))
HStack {
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.caption)
.foregroundStyle((.gray.opacity(0.8)))
.opacity(elapsedTime == 0 ? 0 : 1)
// Image(systemName: "\(elapsedTime).circle")
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: 16, height: 16)
// .foregroundStyle((.gray.opacity(0.8)))
// .opacity(elapsedTime == 0 ? 0 : 1)
}
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.title).monospacedDigit()
.foregroundStyle((Color("mode").opacity(0.5)))
.opacity(elapsedTime == 0 ? 0 : 1)
.contentTransition(.numericText())
Spacer()
}
@@ -62,7 +56,9 @@ struct FilesView: View {
.transition(.opacity)
.onAppear {
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.elapsedTime += 1
withAnimation {
self.elapsedTime += 1
}
}
}
.onDisappear {
@@ -89,7 +85,7 @@ struct FilesView: View {
pathFinder.findPaths()
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
}
@@ -101,7 +97,7 @@ struct FilesView: View {
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
@@ -130,7 +126,7 @@ struct FilesView: View {
VStack(alignment: .leading, spacing: 5){
HStack {
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold)
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold).lineLimit(1)
Text("").foregroundStyle(Color("AccentColor"))
Text("\(appState.appInfo.appVersion)").font(.title3)
if appState.appInfo.appName.count < 5 {
@@ -141,7 +137,7 @@ struct FilesView: View {
Text("\(appState.appInfo.bundleIdentifier)")
.lineLimit(1)
.font(.title3)
.foregroundStyle((.gray.opacity(0.8)))
.foregroundStyle((Color("mode").opacity(0.5)))
}
.padding(.leading)
@@ -150,7 +146,7 @@ struct FilesView: View {
VStack(alignment: .trailing, spacing: 5) {
Text("\(formatByte(size: appState.appInfo.totalSize))").font(.title).fontWeight(.bold)
Text("\(appState.appInfo.fileSize.count == 1 ? "\(appState.selectedItems.count) / \(appState.appInfo.fileSize.count) item" : "\(appState.selectedItems.count) / \(appState.appInfo.fileSize.count) items")")
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
.font(.callout).foregroundStyle((Color("mode").opacity(0.5)))
}
}
@@ -218,7 +214,7 @@ struct FilesView: View {
selectedSortAlpha.toggle()
// selectedOption = selectedOption == "Alpha" ? "Size" : "Alpha"
}
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size"))
}
.padding()
@@ -407,7 +403,7 @@ struct FileDetailsItem: View {
Button("") {
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
}
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder"))
}
.background(
+51 -44
View File
@@ -12,7 +12,7 @@ struct MenuBarMiniAppView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations
// @State private var windowSettings = WindowSettings()
@EnvironmentObject var themeSettings: ThemeSettings
@State private var animateGradient: Bool = false
@Binding var search: String
@State private var showSys: Bool = true
@@ -55,7 +55,8 @@ struct MenuBarMiniAppView: View {
} else {
VStack(alignment: .center) {
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps).padding(0)
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps).padding(.top, 8)
HStack(spacing: 10) {
Button("Leftover Files") {
@@ -84,7 +85,7 @@ struct MenuBarMiniAppView: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
.padding(.leading, 10)
SearchBarMiniBottom(search: $search)
@@ -94,7 +95,7 @@ struct MenuBarMiniAppView: View {
self.showMenu.toggle()
}
.padding(.trailing, 10)
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More"))
.popover(isPresented: $showMenu) {
VStack(alignment: .leading) {
@@ -102,34 +103,36 @@ struct MenuBarMiniAppView: View {
SettingsLink{
Label("Settings", systemImage: "gear")
}
.buttonStyle(SimpleButtonStyle(icon: "gear", label: "Settings", help: "Settings", color: Color("mode")))
.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", color: Color("mode")))
.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", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", label: "Quit Pearcleaner", help: "Quit Pearcleaner"))
}
.padding()
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
}
}
)
// .background(
// Group {
// if glass {
// GlassEffect(material: .sidebar, blendingMode: .behindWindow)
// .edgesIgnoringSafeArea(.all)
// } else {
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
//
// }
// }
// )
}
@@ -139,7 +142,7 @@ struct MenuBarMiniAppView: View {
// .padding(.horizontal)
}
.padding(.vertical, 5)
.padding(.vertical, 8)
.padding(.bottom, 8)
}
@@ -148,19 +151,21 @@ struct MenuBarMiniAppView: View {
}
.frame(minWidth: 300, minHeight: 370)
.edgesIgnoringSafeArea(.all)
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
}
}
)
// .background(
// Group {
// if glass {
// GlassEffect(material: .sidebar, blendingMode: .behindWindow)
// .edgesIgnoringSafeArea(.all)
// } else {
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
//
// }
// }
// )
.transition(.opacity)
.popover(isPresented: $showPopover, arrowEdge: .leading) {
VStack {
@@ -174,19 +179,21 @@ struct MenuBarMiniAppView: View {
}
.interactiveDismissDisabled(popoverStay)
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
.edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
.background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80))
}
}
)
// .background(
// Group {
// if glass {
// GlassEffect(material: .sidebar, blendingMode: .behindWindow)
// .edgesIgnoringSafeArea(.all)
// } else {
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
//
// }
// }
// )
.frame(width: 650, height: 500)
}
+30 -30
View File
@@ -11,6 +11,7 @@ import SwiftUI
struct MiniMode: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var themeSettings: ThemeSettings
@Binding var search: String
@State private var showSys: Bool = true
@State private var showUsr: Bool = true
@@ -49,20 +50,21 @@ struct MiniMode: View {
}
.interactiveDismissDisabled(popoverStay)
.background(
Group {
if glass {
GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all)
} else {
Rectangle()
.fill(Color("pop"))
.padding(-80)
}
}
// Rectangle()
// .fill(Color("pop"))
// .padding(-80)
)
// .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)
}
@@ -71,7 +73,8 @@ struct MiniMode: View {
}
.frame(minWidth: 300, minHeight: 345)
.edgesIgnoringSafeArea(.all)
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
.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))
@@ -93,22 +96,20 @@ struct MiniEmptyView: View {
@Binding var showPopover: Bool
var body: some View {
VStack() {
VStack(alignment: .center) {
Spacer()
ZStack {
LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.mask(
Image(systemName: "plus.square.dashed")
.resizable()
.scaledToFit()
.frame(width: 120, height: 120)
.padding()
.fontWeight(.ultraLight)
)
.frame(width: 160)
}
LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing)
.mask(
Image(systemName: "plus.square.dashed")
.resizable()
.scaledToFit()
.frame(width: 120, height: 120, alignment: .center)
.padding()
.fontWeight(.ultraLight)
.offset(x: 5, y: 5)
)
Text("Drop your app here")
.font(.title3)
@@ -133,7 +134,6 @@ struct MiniEmptyView: View {
}
}
}
Spacer()
}
Spacer()
+48 -75
View File
@@ -10,6 +10,7 @@ import SwiftUI
struct RegularMode: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var themeSettings: ThemeSettings
@AppStorage("settings.general.glass") private var glass: Bool = false
@AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
@@ -69,21 +70,16 @@ struct RegularMode: View {
}
.frame(width: sidebarWidth)
.padding(.vertical)
// .onHover { hovering in
// withAnimation(Animation.easeInOut(duration: 0.4)) {
// isHovering = hovering
// }
// }
// .opacity(!isHovering ? 1 : 0.5)
}
}
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
.background(backgroundView(themeSettings: themeSettings, darker: true, glass: glass))
.transition(.opacity)
SlideableDivider(dimension: $sidebarWidth)
.background(backgroundView(themeSettings: themeSettings))
.zIndex(3)
// Details View
@@ -104,30 +100,11 @@ struct RegularMode: View {
}
.transition(.opacity)
}
// .background(slate)
// .onHover { hovering in
// withAnimation(Animation.easeInOut(duration: 0.4)) {
// isHovering = hovering
// }
// }
// .opacity((isHovering2 || (!isHovering && !isHovering2)) ? 1 : 0.5)
.zIndex(2)
.background(backgroundView(themeSettings: themeSettings))
}
.frame(minWidth: 900, minHeight: 600)
.edgesIgnoringSafeArea(.all)
// MARK: Background for whole app
// .background{
// Image("peakpx")
// .resizable()
// .scaledToFill()
// .edgesIgnoringSafeArea(.all)
// GlassEffect(material: .sidebar, blendingMode: .withinWindow)
// .edgesIgnoringSafeArea(.all)
// }
// .background(Color("bg").opacity(1))
// .background(VisualEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all))
}
}
@@ -188,56 +165,52 @@ struct Header: View {
var body: some View {
HStack {
Text(title).foregroundStyle(Color("mode")).opacity(0.5)
Text("\(title)").foregroundStyle(Color("mode")).opacity(0.5)
HStack {
if hovered {
withAnimation() {
Image(systemName: "arrow.circlepath")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 19, height: 19)
.onTapGesture {
withAnimation {
// Refresh Apps list
appState.reload.toggle()
showPopover = false
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps = sortedApps
if instantSearch {
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations) {
appState.reload.toggle()
}
} else {
appState.reload.toggle()
}
}
}
}
.help("Refresh apps")
}
} else {
Text("\(count)")
.font(.system(size: 10))
.frame(minWidth: count > 99 ? 30 : 20, minHeight: 15)
.padding(2)
.background(Color("mode").opacity(0.1))
.clipShape(.capsule)
}
}
.onHover { hovering in
withAnimation() {
hovered = hovering
}
}
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))
}
}
.padding(.vertical, 9)
.padding(.leading, 6)
// .background(!glass ? Color("windowBG") : .clear)
.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
if instantSearch {
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations) {
appState.reload.toggle()
}
} else {
appState.reload.toggle()
}
}
}
}
.frame(minHeight: 20)
.help("Click header or ⌘+R to refresh apps list")
.padding(5)
}
}
+4 -9
View File
@@ -50,13 +50,12 @@ struct TopBar: View {
if instantSearch {
let reverse = ReversePathsSearcher(appState: appState, locations: locations)
reverse.reversePathsSearch()
// reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps, appState: appState, locations: locations, reverseAddon: true)
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
}
if appState.currentView != .zombie {
@@ -84,7 +83,7 @@ struct TopBar: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
}
@@ -95,19 +94,15 @@ struct TopBar: View {
appState.appInfo = AppInfo.empty
}
}
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target"))
}
}
}
.padding(.horizontal, 10)
.padding(.top, 15)
.padding(6)
}
}
+4 -4
View File
@@ -51,14 +51,14 @@ struct TopBarMini: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.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", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "square.grid.3x3.square", help: "Apps List"))
}
@@ -93,7 +93,7 @@ struct TopBarMini: View {
}
}
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "clock.arrow.circlepath", help: "Leftover Files"))
Button("") {
withAnimation(.easeInOut(duration: 0.5)) {
@@ -102,7 +102,7 @@ struct TopBarMini: View {
showPopover = false
}
}
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "plus.square.dashed", help: "Drop Target"))
}
}
+19 -23
View File
@@ -21,7 +21,6 @@ struct ZombieView: View {
@Binding var showPopover: Bool
@Binding var search: String
@State private var searchZ: String = ""
// @State private var selectedOption = "Alpha"
var regularWin: Bool
@State private var elapsedTime = 0
@State private var timer: Timer? = nil
@@ -57,21 +56,17 @@ struct ZombieView: View {
Spacer()
Text("Searching the file system").font(.title3)
.foregroundStyle((.gray.opacity(0.8)))
.foregroundStyle(Color("mode").opacity(0.5))
HStack {
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.caption)
.foregroundStyle((.gray.opacity(0.8)))
// Image(systemName: "\(elapsedTime).circle")
// .resizable()
// .aspectRatio(contentMode: .fit)
// .frame(width: 16, height: 16)
// .foregroundStyle((.gray.opacity(0.8)))
}
ProgressView()
.progressViewStyle(.linear)
.frame(width: 400, height: 10)
Text("\(elapsedTime)")
.font(.title).monospacedDigit()
.foregroundStyle(Color("mode").opacity(0.5))
.opacity(elapsedTime == 0 ? 0 : 1)
.contentTransition(.numericText())
Spacer()
@@ -82,7 +77,9 @@ struct ZombieView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear {
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.elapsedTime += 1
withAnimation {
self.elapsedTime += 1
}
}
}
.onDisappear {
@@ -109,7 +106,7 @@ struct ZombieView: View {
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
Button("Close") {
updateOnMain {
@@ -119,7 +116,7 @@ struct ZombieView: View {
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
@@ -146,7 +143,7 @@ struct ZombieView: View {
}
Text("Files and folders remaining from previously installed applications")
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
.font(.callout).foregroundStyle(Color("mode").opacity(0.5))
}
Spacer()
@@ -155,7 +152,7 @@ struct ZombieView: View {
Text("\(formatByte(size: filteredAndSortedFiles.1))").font(.title).fontWeight(.bold)
Text("\(appState.zombieFile.fileSize.count == 1 ? "\(appState.selectedZombieItems.count) / \(appState.zombieFile.fileSize.count) item" : "\(appState.selectedZombieItems.count) / \(appState.zombieFile.fileSize.count) items")")
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
.font(.callout).foregroundStyle(Color("mode").opacity(0.5))
}
}
@@ -184,7 +181,7 @@ struct ZombieView: View {
Button("") {
selectedSortAlpha.toggle()
}
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size"))
}
@@ -311,7 +308,6 @@ struct ZombieFileDetailsItem: View {
.lineLimit(1)
.truncationMode(.tail)
.opacity(0.5)
// .foregroundStyle(Color("AccentColor"))
.help(path.path)
}
@@ -324,7 +320,7 @@ struct ZombieFileDetailsItem: View {
Button("") {
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
}
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder", color: Color("mode")))
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder"))
}
.background(
+6 -6
View File
@@ -38,10 +38,10 @@ struct PermView: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Text("Full Disk Access permission to find and delete files in system paths")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
HStack(alignment: .top, spacing: 20) {
@@ -49,10 +49,10 @@ struct PermView: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Text("Accessibility permission to delete files via Finder")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
HStack(alignment: .top, spacing: 20) {
@@ -60,10 +60,10 @@ struct PermView: View {
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
Text("Add Pearcleaner in both Privacy panes via the + or by dragging the app over the pane. If the app is already pre-populated in the list, just toggle On if neeeded. Restart app when both permissions are granted")
.font(.callout)
.foregroundStyle(.gray)
.foregroundStyle(Color("mode").opacity(0.5))
}
}
.padding(.horizontal, 20)