mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 09:38:51 +00:00
TreeMap updates
This commit is contained in:
@@ -65,7 +65,8 @@ class AppState: ObservableObject {
|
||||
id: UUID(),
|
||||
fileSize: [:],
|
||||
fileSizeLogical: [:],
|
||||
fileIcon: [:]
|
||||
fileIcon: [:],
|
||||
isDirectory: [:]
|
||||
)
|
||||
|
||||
updateExtensionStatus()
|
||||
@@ -133,6 +134,7 @@ struct ZombieFile: Identifiable, Equatable, Hashable {
|
||||
var fileSize: [URL:Int64]
|
||||
var fileSizeLogical: [URL:Int64]
|
||||
var fileIcon: [URL:NSImage?]
|
||||
var isDirectory: [URL:Bool]
|
||||
var totalSize: Int64
|
||||
{
|
||||
return fileSize.values.reduce(0, +)
|
||||
@@ -143,10 +145,30 @@ struct ZombieFile: Identifiable, Equatable, Hashable {
|
||||
}
|
||||
|
||||
|
||||
static let empty = ZombieFile(id: UUID(), fileSize: [:], fileSizeLogical: [:], fileIcon: [:])
|
||||
static let empty = ZombieFile(id: UUID(), fileSize: [:], fileSizeLogical: [:], fileIcon: [:], isDirectory: [:])
|
||||
|
||||
}
|
||||
|
||||
extension ZombieFile {
|
||||
/// Converts the data in ZombieFile into a list of Item instances.
|
||||
func toItems() -> [Item] {
|
||||
var items = [Item]()
|
||||
|
||||
// Iterate over each URL in fileSize to get corresponding properties
|
||||
for (url, size) in fileSize {
|
||||
let name = url.lastPathComponent
|
||||
let isDir = isDirectory[url] ?? false
|
||||
let parent = url.deletingLastPathComponent()
|
||||
|
||||
// Create an Item instance for each URL
|
||||
let item = Item(url: url, name: name, size: size, isDirectory: isDir, parentURL: parent)
|
||||
items.append(item)
|
||||
}
|
||||
|
||||
return items//.sorted { $0.size > $1.size }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum Arch {
|
||||
case arm
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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()
|
||||
private var lastView: (() -> AnyView)?
|
||||
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "bubbles.and.sparkles.fill"
|
||||
|
||||
func addMenuBarExtra<V: View>(withView view: @escaping () -> V) {
|
||||
guard statusItem == nil else { return }
|
||||
|
||||
// Remember the last view
|
||||
lastView = { AnyView(view()) }
|
||||
|
||||
// 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: selectedMenubarIcon, accessibilityDescription: "Pearcleaner")
|
||||
button.action = #selector(togglePopover(_:))
|
||||
button.target = self
|
||||
button.sendAction(on: [.leftMouseDown, .rightMouseDown])
|
||||
}
|
||||
|
||||
// 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 swapMenuBarIcon(icon: String) {
|
||||
guard let button = statusItem?.button else { return }
|
||||
if let image = NSImage(systemSymbolName: icon, accessibilityDescription: nil) {
|
||||
button.image = image
|
||||
}
|
||||
}
|
||||
|
||||
func restartMenuBarExtra() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
self.removeMenuBarExtra()
|
||||
|
||||
// Ensure the last view and icon are available before re-adding
|
||||
if let lastView = self.lastView {
|
||||
self.addMenuBarExtra(withView: lastView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ class ReversePathsSearcher {
|
||||
private var fileSize: [URL: Int64] = [:]
|
||||
private var fileSizeLogical: [URL: Int64] = [:]
|
||||
private var fileIcon: [URL: NSImage?] = [:]
|
||||
private var isDirectory: [URL: Bool] = [:]
|
||||
private let dispatchGroup = DispatchGroup()
|
||||
private let sortedApps: [AppInfo]
|
||||
|
||||
@@ -112,6 +113,7 @@ class ReversePathsSearcher {
|
||||
fileSize[path] = size.real
|
||||
fileSizeLogical[path] = size.logical
|
||||
fileIcon[path] = getIconForFileOrFolderNS(atPath: path)
|
||||
isDirectory[path] = path.hasDirectoryPath
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +123,7 @@ class ReversePathsSearcher {
|
||||
updatedZombieFile.fileSize = self.fileSize
|
||||
updatedZombieFile.fileSizeLogical = self.fileSizeLogical
|
||||
updatedZombieFile.fileIcon = self.fileIcon
|
||||
updatedZombieFile.isDirectory = self.isDirectory
|
||||
self.appState.zombieFile = updatedZombieFile
|
||||
self.appState.showProgress = false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// WindowSettings.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 12/26/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AlinFoundation
|
||||
import Combine
|
||||
|
||||
|
||||
class WindowSettings {
|
||||
private let windowWidthKey = "windowWidthKey"
|
||||
private let windowHeightKey = "windowHeightKey"
|
||||
private let windowWidthKeyMini = "windowWidthKeyMini"
|
||||
private let windowHeightKeyMini = "windowHeightKeyMini"
|
||||
private let windowXKey = "windowXKey"
|
||||
private let windowYKey = "windowYKey"
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
var windows: [NSWindow] = []
|
||||
|
||||
func registerDefaultWindowSettings(completion: @escaping () -> Void = {}) {
|
||||
|
||||
let defaults = UserDefaults.standard
|
||||
|
||||
// Get primary screen
|
||||
let screenFrame = NSScreen.main?.visibleFrame ?? NSRect(x: 0, y: 0, width: 800, height: 600)
|
||||
|
||||
// Calculate default window sizes and x/y coordinates
|
||||
let defaultWidth = Float(900) // Default width for regular window
|
||||
let defaultHeight = Float(600) // Default height for regular window
|
||||
let defaultWidthMini = Float(300) // Default width for mini window
|
||||
let defaultHeightMini = Float(370) // Default height for mini window
|
||||
let defaultX = Float((screenFrame.width - CGFloat(defaultWidth)) / 2 + screenFrame.origin.x) // Default X coordinate
|
||||
let defaultY = Float((screenFrame.height - CGFloat(defaultHeight)) / 2 + screenFrame.origin.y) // Default Y coordinate
|
||||
|
||||
// Set defaults only if they are not already set
|
||||
if defaults.object(forKey: windowWidthKey) == nil {
|
||||
defaults.set(defaultWidth, forKey: windowWidthKey)
|
||||
}
|
||||
if defaults.object(forKey: windowHeightKey) == nil {
|
||||
defaults.set(defaultHeight, forKey: windowHeightKey)
|
||||
}
|
||||
if defaults.object(forKey: windowWidthKeyMini) == nil {
|
||||
defaults.set(defaultWidthMini, forKey: windowWidthKeyMini)
|
||||
}
|
||||
if defaults.object(forKey: windowHeightKeyMini) == nil {
|
||||
defaults.set(defaultHeightMini, forKey: windowHeightKeyMini)
|
||||
}
|
||||
if defaults.object(forKey: windowXKey) == nil {
|
||||
defaults.set(defaultX, forKey: windowXKey)
|
||||
}
|
||||
if defaults.object(forKey: windowYKey) == nil {
|
||||
defaults.set(defaultY, forKey: windowYKey)
|
||||
}
|
||||
|
||||
completion()
|
||||
|
||||
}
|
||||
|
||||
// Save user window settings
|
||||
func saveWindowSettings(frame: NSRect) {
|
||||
UserDefaults.standard.set(Float(frame.size.width), forKey: mini ? windowWidthKeyMini : windowWidthKey)
|
||||
UserDefaults.standard.set(Float(frame.size.height), forKey: mini ? windowHeightKeyMini : windowHeightKey)
|
||||
UserDefaults.standard.set(Float(frame.origin.x), forKey: windowXKey)
|
||||
UserDefaults.standard.set(Float(frame.origin.y), forKey: windowYKey)
|
||||
}
|
||||
|
||||
// Load default window settings or user defined settings
|
||||
func loadWindowSettings() -> NSRect {
|
||||
let width = CGFloat(UserDefaults.standard.float(forKey: mini ? windowWidthKeyMini : windowWidthKey))
|
||||
let height = CGFloat(UserDefaults.standard.float(forKey: mini ? windowHeightKeyMini : windowHeightKey))
|
||||
let x = CGFloat(UserDefaults.standard.float(forKey: windowXKey))
|
||||
let y = CGFloat(UserDefaults.standard.float(forKey: windowYKey))
|
||||
|
||||
return NSRect(x: x, y: y, width: width, height: height)
|
||||
}
|
||||
|
||||
// Launch new app windows on demand
|
||||
func newWindow<V: View>(withView view: @escaping () -> V) {
|
||||
findAndHideWindows(named: ["Pearcleaner"])
|
||||
let contentView = view
|
||||
let frame = self.loadWindowSettings()
|
||||
let newWindow = NSWindow(
|
||||
contentRect: frame,
|
||||
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
||||
backing: .buffered, defer: false)
|
||||
newWindow.titlebarAppearsTransparent = true
|
||||
newWindow.isMovableByWindowBackground = true
|
||||
newWindow.center()
|
||||
newWindow.title = "Pearcleaner"
|
||||
newWindow.isRestorable = false
|
||||
newWindow.titleVisibility = .hidden
|
||||
newWindow.setFrameAutosaveName("Pearcleaner")
|
||||
newWindow.contentView = NSHostingView(rootView: contentView())
|
||||
self.windows.append(newWindow)
|
||||
newWindow.makeKeyAndOrderFront(nil)
|
||||
resizeWindowAuto(windowSettings: self, title: "Pearcleaner")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user