mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 09:38:51 +00:00
v3.2.0
This commit is contained in:
@@ -37,6 +37,9 @@
|
|||||||
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
|
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
|
||||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
|
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
|
||||||
C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */ = {isa = PBXBuildFile; fileRef = C7DC1FED2B9F7D3D009AC317 /* features.json */; };
|
C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */ = {isa = PBXBuildFile; fileRef = C7DC1FED2B9F7D3D009AC317 /* features.json */; };
|
||||||
|
C7DE67272BA61E8D00EB1633 /* MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE67262BA61E8D00EB1633 /* MenuBar.swift */; };
|
||||||
|
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */; };
|
||||||
|
C7DE672D2BA6356500EB1633 /* MenuBarMiniAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672C2BA6356500EB1633 /* MenuBarMiniAppView.swift */; };
|
||||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
||||||
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; };
|
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -97,6 +100,9 @@
|
|||||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.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>"; };
|
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; };
|
C7DC1FED2B9F7D3D009AC317 /* features.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = features.json; sourceTree = SOURCE_ROOT; };
|
||||||
|
C7DE67262BA61E8D00EB1633 /* MenuBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBar.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>"; };
|
||||||
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.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>"; };
|
C7FB173A2B96321300B96F9A /* AppsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsListView.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -134,6 +140,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C77B90192AF1938F009CC655 /* Logic.swift */,
|
C77B90192AF1938F009CC655 /* Logic.swift */,
|
||||||
|
C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */,
|
||||||
C77B90172AF19382009CC655 /* AppCommands.swift */,
|
C77B90172AF19382009CC655 /* AppCommands.swift */,
|
||||||
C77B90152AF19377009CC655 /* AppState.swift */,
|
C77B90152AF19377009CC655 /* AppState.swift */,
|
||||||
C7F539372AF60865007DF1B2 /* Utilities.swift */,
|
C7F539372AF60865007DF1B2 /* Utilities.swift */,
|
||||||
@@ -168,6 +175,7 @@
|
|||||||
children = (
|
children = (
|
||||||
C77B901D2AF1A9DB009CC655 /* SettingsWindow.swift */,
|
C77B901D2AF1A9DB009CC655 /* SettingsWindow.swift */,
|
||||||
C76D08492AF83C6E00D07867 /* General.swift */,
|
C76D08492AF83C6E00D07867 /* General.swift */,
|
||||||
|
C7DE67262BA61E8D00EB1633 /* MenuBar.swift */,
|
||||||
C76D08472AF83C3F00D07867 /* Update.swift */,
|
C76D08472AF83C3F00D07867 /* Update.swift */,
|
||||||
C76D08502AF850F300D07867 /* About.swift */,
|
C76D08502AF850F300D07867 /* About.swift */,
|
||||||
);
|
);
|
||||||
@@ -229,6 +237,7 @@
|
|||||||
C7045A272B03E71D00376976 /* MiniMode.swift */,
|
C7045A272B03E71D00376976 /* MiniMode.swift */,
|
||||||
C71848432B8D2D600046CB13 /* ZombieView.swift */,
|
C71848432B8D2D600046CB13 /* ZombieView.swift */,
|
||||||
C7FB173A2B96321300B96F9A /* AppsListView.swift */,
|
C7FB173A2B96321300B96F9A /* AppsListView.swift */,
|
||||||
|
C7DE672C2BA6356500EB1633 /* MenuBarMiniAppView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -347,6 +356,7 @@
|
|||||||
C7045A342B068B2900376976 /* UpdateView.swift in Sources */,
|
C7045A342B068B2900376976 /* UpdateView.swift in Sources */,
|
||||||
C77B90162AF19377009CC655 /* AppState.swift in Sources */,
|
C77B90162AF19377009CC655 /* AppState.swift in Sources */,
|
||||||
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
||||||
|
C7DE672D2BA6356500EB1633 /* MenuBarMiniAppView.swift in Sources */,
|
||||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
||||||
C77B901A2AF1938F009CC655 /* Logic.swift in Sources */,
|
C77B901A2AF1938F009CC655 /* Logic.swift in Sources */,
|
||||||
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */,
|
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */,
|
||||||
@@ -354,6 +364,7 @@
|
|||||||
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */,
|
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */,
|
||||||
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */,
|
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */,
|
||||||
C71848442B8D2D600046CB13 /* ZombieView.swift in Sources */,
|
C71848442B8D2D600046CB13 /* ZombieView.swift in Sources */,
|
||||||
|
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */,
|
||||||
C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
|
C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
|
||||||
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */,
|
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */,
|
||||||
C7045A362B068DD700376976 /* PermView.swift in Sources */,
|
C7045A362B068DD700376976 /* PermView.swift in Sources */,
|
||||||
@@ -367,6 +378,7 @@
|
|||||||
C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */,
|
C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */,
|
||||||
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */,
|
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */,
|
||||||
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */,
|
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */,
|
||||||
|
C7DE67272BA61E8D00EB1633 /* MenuBar.swift in Sources */,
|
||||||
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */,
|
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ struct ZombieFile: Identifiable, Equatable, Hashable {
|
|||||||
enum CurrentTabView:Int
|
enum CurrentTabView:Int
|
||||||
{
|
{
|
||||||
case general
|
case general
|
||||||
|
case menubar
|
||||||
// case permissions
|
// case permissions
|
||||||
// case sentinel
|
// case sentinel
|
||||||
case update
|
case update
|
||||||
@@ -113,6 +114,7 @@ enum CurrentTabView:Int
|
|||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .general: return "General"
|
case .general: return "General"
|
||||||
|
case .menubar: return "MenuBar"
|
||||||
// case .permissions: return "Permissions"
|
// case .permissions: return "Permissions"
|
||||||
// case .sentinel: return "Sentinel"
|
// case .sentinel: return "Sentinel"
|
||||||
case .update: return "Update"
|
case .update: return "Update"
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// MenuBarItem.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 3/16/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
class MenuBarExtraManager {
|
||||||
|
static let shared = MenuBarExtraManager()
|
||||||
|
private var statusItem: NSStatusItem?
|
||||||
|
private var popover = NSPopover()
|
||||||
|
|
||||||
|
func addMenuBarExtra<V: View>(withView view: @escaping () -> V) {
|
||||||
|
guard statusItem == nil else { return }
|
||||||
|
|
||||||
|
// Initialize the status item
|
||||||
|
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||||
|
|
||||||
|
// Set up the status item's button
|
||||||
|
if let button = statusItem?.button {
|
||||||
|
button.image = NSImage(systemSymbolName: "menubar.rectangle", accessibilityDescription: "Pearcleaner")
|
||||||
|
button.action = #selector(togglePopover(_:))
|
||||||
|
button.target = self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the popover
|
||||||
|
popover.contentSize = NSSize(width: 300, height: 370)
|
||||||
|
popover.behavior = .transient
|
||||||
|
popover.contentViewController = NSHostingController(rootView: view())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func removeMenuBarExtra() {
|
||||||
|
if let item = statusItem {
|
||||||
|
NSStatusBar.system.removeStatusItem(item)
|
||||||
|
statusItem = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStatus() -> Bool {
|
||||||
|
if let item = statusItem {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func togglePopover(_ sender: AnyObject?) {
|
||||||
|
if let button = statusItem?.button {
|
||||||
|
if popover.isShown {
|
||||||
|
popover.performClose(sender)
|
||||||
|
} else {
|
||||||
|
popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ class WindowSettings {
|
|||||||
private let windowXKey = "windowXKey"
|
private let windowXKey = "windowXKey"
|
||||||
private let windowYKey = "windowYKey"
|
private let windowYKey = "windowYKey"
|
||||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
|
var window: NSWindow?
|
||||||
|
|
||||||
func saveWindowSettings(frame: NSRect) {
|
func saveWindowSettings(frame: NSRect) {
|
||||||
|
|
||||||
@@ -32,4 +33,21 @@ class WindowSettings {
|
|||||||
let y = CGFloat(UserDefaults.standard.float(forKey: windowYKey))
|
let y = CGFloat(UserDefaults.standard.float(forKey: windowYKey))
|
||||||
return NSRect(x: x, y: y, width: width, height: height)
|
return NSRect(x: x, y: y, width: width, height: height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newWindow<V: View>(withView view: @escaping () -> V) {
|
||||||
|
let contentView = view
|
||||||
|
let frame = self.loadWindowSettings()
|
||||||
|
// Create the window and set the content view
|
||||||
|
let newWindow = NSWindow(
|
||||||
|
contentRect: frame,
|
||||||
|
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
||||||
|
backing: .buffered, defer: false)
|
||||||
|
newWindow.titlebarAppearsTransparent = true
|
||||||
|
newWindow.isMovableByWindowBackground = true
|
||||||
|
newWindow.center()
|
||||||
|
newWindow.setFrameAutosaveName("Main Window")
|
||||||
|
newWindow.contentView = NSHostingView(rootView: contentView())
|
||||||
|
self.window = newWindow
|
||||||
|
newWindow.makeKeyAndOrderFront(nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import ServiceManagement
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct PearcleanerApp: App {
|
struct PearcleanerApp: App {
|
||||||
@@ -24,10 +25,15 @@ struct PearcleanerApp: App {
|
|||||||
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
|
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
|
||||||
@AppStorage("settings.general.features") private var features: String = ""
|
@AppStorage("settings.general.features") private var features: String = ""
|
||||||
@AppStorage("settings.general.brew") private var brew: Bool = false
|
@AppStorage("settings.general.brew") private var brew: Bool = false
|
||||||
|
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||||
|
@AppStorage("settings.menubar.mainWin") private var mainWinEnabled: Bool = false
|
||||||
|
|
||||||
@State private var search = ""
|
@State private var search = ""
|
||||||
@State private var showPopover: Bool = false
|
@State private var showPopover: Bool = false
|
||||||
@State private var showFeature: Bool = false
|
@State private var showFeature: Bool = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
@@ -100,6 +106,15 @@ struct PearcleanerApp: App {
|
|||||||
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations)
|
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if menubarEnabled {
|
||||||
|
MenuBarExtraManager.shared.addMenuBarExtra {
|
||||||
|
MenuBarMiniAppView(search: $search, showPopover: $showPopover)
|
||||||
|
.environmentObject(locations)
|
||||||
|
.environmentObject(appState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
Task {
|
Task {
|
||||||
@@ -135,6 +150,7 @@ struct PearcleanerApp: App {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.windowStyle(.hiddenTitleBar)
|
.windowStyle(.hiddenTitleBar)
|
||||||
.windowResizability(.contentMinSize)
|
.windowResizability(.contentMinSize)
|
||||||
.commands {
|
.commands {
|
||||||
@@ -144,9 +160,12 @@ struct PearcleanerApp: App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
SettingsView(showPopover: $showPopover, showFeature: $showFeature)
|
SettingsView(showPopover: $showPopover, search: $search, showFeature: $showFeature)
|
||||||
.environmentObject(appState)
|
.environmentObject(appState)
|
||||||
|
.environmentObject(locations)
|
||||||
.toolbarBackground(.clear)
|
.toolbarBackground(.clear)
|
||||||
.preferredColorScheme(displayMode.colorScheme)
|
.preferredColorScheme(displayMode.colorScheme)
|
||||||
}
|
}
|
||||||
@@ -157,14 +176,24 @@ struct PearcleanerApp: App {
|
|||||||
|
|
||||||
|
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return true
|
let menubarEnabled = UserDefaults.standard.bool(forKey: "settings.menubar.enabled")
|
||||||
|
return !menubarEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// func applicationDidFinishLaunching(_ notification: Notification, win: WindowSettings) {
|
|
||||||
// let frame = win.loadWindowSettings()
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
// NSApplication.shared.windows.first?.setFrame(frame, display: true)
|
let menubarEnabled = UserDefaults.standard.bool(forKey: "settings.menubar.enabled")
|
||||||
// }
|
|
||||||
|
#if !DEBUG
|
||||||
|
if menubarEnabled {
|
||||||
|
NSApp.windows.first?.close()
|
||||||
|
NSApplication.shared.setActivationPolicy(.accessory)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import ServiceManagement
|
||||||
|
|
||||||
struct GeneralSettingsTab: View {
|
struct GeneralSettingsTab: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
|
@EnvironmentObject var locations: Locations
|
||||||
@State private var windowSettings = WindowSettings()
|
@State private var windowSettings = WindowSettings()
|
||||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@@ -26,6 +28,7 @@ struct GeneralSettingsTab: View {
|
|||||||
@State private var accessStatus: Bool = false
|
@State private var accessStatus: Bool = false
|
||||||
private let themes = ["Auto", "Dark", "Light"]
|
private let themes = ["Auto", "Dark", "Light"]
|
||||||
@Binding var showPopover: Bool
|
@Binding var showPopover: Bool
|
||||||
|
@Binding var search: String
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
@@ -148,7 +151,7 @@ struct GeneralSettingsTab: View {
|
|||||||
.padding(5)
|
.padding(5)
|
||||||
.padding(.leading)
|
.padding(.leading)
|
||||||
|
|
||||||
// =========================================================================================================
|
// === Mini =================================================================================================
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
.padding()
|
.padding()
|
||||||
@@ -247,7 +250,7 @@ struct GeneralSettingsTab: View {
|
|||||||
.padding(.leading)
|
.padding(.leading)
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================================================
|
// === Perms ================================================================================================
|
||||||
|
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
@@ -312,7 +315,7 @@ struct GeneralSettingsTab: View {
|
|||||||
.padding(.leading)
|
.padding(.leading)
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================================================
|
// === Sentinel =============================================================================================
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
.padding()
|
.padding()
|
||||||
@@ -362,11 +365,6 @@ struct GeneralSettingsTab: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
diskStatus = checkAndRequestFullDiskAccess(appState: appState, skipAlert: true)
|
diskStatus = checkAndRequestFullDiskAccess(appState: appState, skipAlert: true)
|
||||||
accessStatus = checkAndRequestAccessibilityAccess(appState: appState)
|
accessStatus = checkAndRequestAccessibilityAccess(appState: appState)
|
||||||
// if displayMode.colorScheme == .dark {
|
|
||||||
// selectedTheme = "Dark"
|
|
||||||
// } else if displayMode.colorScheme == .light {
|
|
||||||
// selectedTheme = "Light"
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// MenuBar.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 3/16/24.
|
||||||
|
//
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import ServiceManagement
|
||||||
|
|
||||||
|
struct MenuBarSettingsTab: View {
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
@EnvironmentObject var locations: Locations
|
||||||
|
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||||
|
@State private var isLaunchAtLoginEnabled: Bool = false
|
||||||
|
|
||||||
|
@Binding var showPopover: Bool
|
||||||
|
@Binding var search: String
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
VStack {
|
||||||
|
|
||||||
|
HStack() {
|
||||||
|
Text("Configuration").font(.title2)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.leading)
|
||||||
|
|
||||||
|
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Image(systemName: "menubar.rectangle")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
.padding(.trailing)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
|
Text("\(menubarEnabled ? "Menubar icon enabled" : "Menubar icon disabled")")
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
Toggle(isOn: $menubarEnabled, label: {
|
||||||
|
})
|
||||||
|
.toggleStyle(.switch)
|
||||||
|
.onChange(of: menubarEnabled) { newVal in
|
||||||
|
if newVal {
|
||||||
|
MenuBarExtraManager.shared.addMenuBarExtra {
|
||||||
|
MenuBarMiniAppView(search: $search, showPopover: $showPopover)
|
||||||
|
.environmentObject(locations)
|
||||||
|
.environmentObject(appState)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MenuBarExtraManager.shared.removeMenuBarExtra()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(5)
|
||||||
|
.padding(.leading)
|
||||||
|
|
||||||
|
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Image(systemName: isLaunchAtLoginEnabled ? "person.crop.circle.badge.checkmark" : "person.crop.circle")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
.padding(.trailing)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
|
Text("\(isLaunchAtLoginEnabled ? "Launch at login enabled" : "Launch at login disabled")")
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
Toggle(isOn: $isLaunchAtLoginEnabled, label: {
|
||||||
|
})
|
||||||
|
.toggleStyle(.switch)
|
||||||
|
.onAppear {
|
||||||
|
isLaunchAtLoginEnabled = SMAppService.mainApp.status == .enabled
|
||||||
|
}
|
||||||
|
.onChange(of: isLaunchAtLoginEnabled) { newValue in
|
||||||
|
do {
|
||||||
|
if newValue {
|
||||||
|
if SMAppService.mainApp.status == .enabled {
|
||||||
|
try? SMAppService.mainApp.unregister()
|
||||||
|
}
|
||||||
|
|
||||||
|
try SMAppService.mainApp.register()
|
||||||
|
} else {
|
||||||
|
try SMAppService.mainApp.unregister()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
printOS("Failed to \(newValue ? "enable" : "disable") launch at login: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(5)
|
||||||
|
.padding(.leading)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(20)
|
||||||
|
.frame(width: 500, height: 690)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,18 +10,25 @@ import SwiftUI
|
|||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
@Binding var showPopover: Bool
|
@Binding var showPopover: Bool
|
||||||
|
@Binding var search: String
|
||||||
@Binding var showFeature: Bool
|
@Binding var showFeature: Bool
|
||||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
TabView() {
|
TabView() {
|
||||||
GeneralSettingsTab(showPopover: $showPopover)
|
GeneralSettingsTab(showPopover: $showPopover, search: $search)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Label(CurrentTabView.general.title, systemImage: "gear")
|
Label(CurrentTabView.general.title, systemImage: "gear")
|
||||||
}
|
}
|
||||||
.tag(CurrentTabView.general)
|
.tag(CurrentTabView.general)
|
||||||
|
|
||||||
|
MenuBarSettingsTab(showPopover: $showPopover, search: $search)
|
||||||
|
.tabItem {
|
||||||
|
Label(CurrentTabView.menubar.title, systemImage: "menubar.rectangle")
|
||||||
|
}
|
||||||
|
.tag(CurrentTabView.menubar)
|
||||||
|
|
||||||
UpdateSettingsTab(showFeature: $showFeature)
|
UpdateSettingsTab(showFeature: $showFeature)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Label(CurrentTabView.update.title, systemImage: "cloud")
|
Label(CurrentTabView.update.title, systemImage: "cloud")
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
@State private var windowSettings = WindowSettings()
|
||||||
|
@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.instant") private var instantSearch: Bool = true
|
||||||
|
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||||
|
@AppStorage("settings.general.popover") private var popoverStay: Bool = true
|
||||||
|
@Binding var showPopover: Bool
|
||||||
|
|
||||||
|
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(instantSearch ? "Refreshing app list and caching files" : "Refreshing app list")
|
||||||
|
if instantSearch {
|
||||||
|
Image(systemName: "bolt.fill")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.foregroundStyle(Color("mode").opacity(0.5))
|
||||||
|
.frame(width: 30, height: 30)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical)
|
||||||
|
} else {
|
||||||
|
VStack(alignment: .center) {
|
||||||
|
|
||||||
|
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps).padding(0)
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
if #available(macOS 14.0, *) {
|
||||||
|
SettingsLink()
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "gear", help: "Settings", color: Color("mode")))
|
||||||
|
} else {
|
||||||
|
Button("Settings") {
|
||||||
|
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: NSApp.delegate, from: nil)
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "gear", help: "Settings", color: Color("mode")))
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchBarMiniBottom(search: $search)
|
||||||
|
|
||||||
|
Button("Main") {
|
||||||
|
windowSettings.newWindow {
|
||||||
|
MiniMode(search: $search, showPopover: $showPopover)
|
||||||
|
.environmentObject(locations)
|
||||||
|
.environmentObject(appState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "macwindow", help: "Show Main Window", color: Color("mode")))
|
||||||
|
|
||||||
|
Button("Kill") {
|
||||||
|
NSApp.terminate(nil)
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Quit", color: Color("mode")))
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.padding(.bottom, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(minWidth: 300, minHeight: 370)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.background(
|
||||||
|
Group {
|
||||||
|
if glass {
|
||||||
|
GlassEffect(material: .sidebar, blendingMode: .behindWindow)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
} else {
|
||||||
|
Color("pop")
|
||||||
|
.padding(-80)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.transition(.opacity)
|
||||||
|
.popover(isPresented: $showPopover, arrowEdge: .trailing) {
|
||||||
|
VStack {
|
||||||
|
if appState.currentView == .files {
|
||||||
|
FilesView(showPopover: $showPopover, search: $search)
|
||||||
|
.id(appState.appInfo.id)
|
||||||
|
} else if appState.currentView == .zombie {
|
||||||
|
ZombieView(showPopover: $showPopover, search: $search)
|
||||||
|
.id(appState.appInfo.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.interactiveDismissDisabled(popoverStay)
|
||||||
|
.background(
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color("pop"))
|
||||||
|
.padding(-80)
|
||||||
|
)
|
||||||
|
.frame(width: 650, height: 550)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -183,6 +183,7 @@ struct MiniAppView: View {
|
|||||||
|
|
||||||
if appState.currentView != .empty {
|
if appState.currentView != .empty {
|
||||||
SearchBarMiniBottom(search: $search)
|
SearchBarMiniBottom(search: $search)
|
||||||
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom)
|
.padding(.bottom)
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ struct RegularMode: View {
|
|||||||
VStack(alignment: .center, spacing: 20) {
|
VStack(alignment: .center, spacing: 20) {
|
||||||
HStack {
|
HStack {
|
||||||
SearchBarMiniBottom(search: $search)
|
SearchBarMiniBottom(search: $search)
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ struct SearchBarMiniBottom: View {
|
|||||||
TextField(" Search", text: $search)
|
TextField(" Search", text: $search)
|
||||||
.textFieldStyle(SimpleSearchStyle(trash: true, text: $search))
|
.textFieldStyle(SimpleSearchStyle(trash: true, text: $search))
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
// .padding(.horizontal)
|
||||||
.padding(.bottom, 0)
|
// .padding(.bottom, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user