TreeMap updates

This commit is contained in:
Alin
2024-08-01 18:09:32 -06:00
parent e0d41e76f4
commit e425219c99
7 changed files with 379 additions and 77 deletions
+24 -2
View File
@@ -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
+78
View File
@@ -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
}
+101
View File
@@ -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")
}
}