mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
- add option to show removable disks in Disk module
- add notification when popup view size must be recalculated
This commit is contained in:
@@ -104,6 +104,7 @@ open class Module: Module_p {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listenForWidgetSwitch), name: .switchWidget, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listenForMouseDownInSettings), name: .clickInSettings, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listenForModuleToggle), name: .toggleModule, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listenChangingPopupSize), name: .updatePopupSize, object: nil)
|
||||
|
||||
if self.config.widgetsConfig.count != 0 {
|
||||
self.setWidget()
|
||||
@@ -255,24 +256,25 @@ open class Module: Module_p {
|
||||
self.settings?.setActiveWidget(self.widget)
|
||||
}
|
||||
|
||||
@objc private func togglePopup(_ sender: NSStatusBarButton) {
|
||||
@objc private func togglePopup(_ sender: Any) {
|
||||
let openedWindows = NSApplication.shared.windows.filter{ $0 is NSPanel }
|
||||
openedWindows.forEach{ $0.setIsVisible(false) }
|
||||
|
||||
if self.popup.occlusionState.rawValue == 8192 {
|
||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||
|
||||
self.popup.contentView?.invalidateIntrinsicContentSize()
|
||||
|
||||
let buttonOrigin = self.menuBarItem.button?.window?.frame.origin
|
||||
let buttonCenter = (self.menuBarItem.button?.window?.frame.width)! / 2
|
||||
let windowCenter = self.popup.frame.width / 2
|
||||
|
||||
self.popup.contentView?.invalidateIntrinsicContentSize()
|
||||
let windowCenter = self.popup.contentView!.intrinsicContentSize.width / 2
|
||||
var x = buttonOrigin!.x - windowCenter + buttonCenter
|
||||
let y = buttonOrigin!.y - self.popup.contentView!.intrinsicContentSize.height - 3
|
||||
|
||||
let maxWidth = NSScreen.screens.map{ $0.frame.width }.reduce(0, +)
|
||||
if x + self.popup.frame.width > maxWidth {
|
||||
x = maxWidth - self.popup.frame.width - 3
|
||||
if x + self.popup.contentView!.intrinsicContentSize.width > maxWidth {
|
||||
x = maxWidth - self.popup.contentView!.intrinsicContentSize.width - 3
|
||||
}
|
||||
|
||||
self.popup.setFrameOrigin(NSPoint(x: x, y: y))
|
||||
@@ -314,4 +316,12 @@ open class Module: Module_p {
|
||||
self.popup.setIsVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func listenChangingPopupSize(_ notification: Notification) {
|
||||
if let moduleName = notification.userInfo?["module"] as? String, moduleName == self.config.name {
|
||||
if self.popup.isVisible {
|
||||
self.popup.setIsVisible(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ internal class PopupViewController: NSViewController {
|
||||
|
||||
public func setup(title: String, view: NSView?) {
|
||||
self.title = title
|
||||
self.popup.title = title
|
||||
self.popup.headerView?.titleView?.stringValue = title
|
||||
self.popup.setView(view)
|
||||
}
|
||||
@@ -76,6 +77,7 @@ internal class PopupViewController: NSViewController {
|
||||
|
||||
internal class PopupView: NSView {
|
||||
public var headerView: HeaderView? = nil
|
||||
public var title: String? = nil
|
||||
private var mainView: NSView? = nil
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
@@ -92,6 +94,7 @@ internal class PopupView: NSView {
|
||||
self.canDrawConcurrently = true
|
||||
self.layer!.cornerRadius = 3
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(listenChangingPopupSize), name: .updatePopupSize, object: nil)
|
||||
self.headerView = HeaderView(frame: NSRect(x: 0, y: frame.height - Constants.Popup.headerHeight, width: frame.width, height: Constants.Popup.headerHeight))
|
||||
|
||||
let mainView: NSView = NSView(frame: NSRect(x: Constants.Popup.margins, y: Constants.Popup.margins, width: frame.width - (Constants.Popup.margins*2), height: 0))
|
||||
@@ -143,6 +146,13 @@ internal class PopupView: NSView {
|
||||
self.mainView?.subviews.first{ !($0 is HeaderView) }?.display()
|
||||
}
|
||||
internal func disappear() {}
|
||||
|
||||
|
||||
@objc private func listenChangingPopupSize(_ notification: Notification) {
|
||||
if let moduleName = notification.userInfo?["module"] as? String, moduleName == self.title {
|
||||
self.updateLayer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class HeaderView: NSView {
|
||||
|
||||
@@ -25,6 +25,8 @@ struct diskInfo {
|
||||
|
||||
var mediaBSDName: String = ""
|
||||
var root: Bool = false
|
||||
|
||||
var removable: Bool = false
|
||||
}
|
||||
|
||||
struct DiskList: value_t {
|
||||
@@ -78,6 +80,7 @@ public class Disk: Module {
|
||||
guard self.available else { return }
|
||||
|
||||
self.capacityReader = CapacityReader()
|
||||
self.capacityReader?.store = store
|
||||
self.selectedDisk = store!.pointee.string(key: "\(self.config.name)_disk", defaultValue: self.selectedDisk)
|
||||
|
||||
self.capacityReader?.readyCallback = { [unowned self] in
|
||||
@@ -91,6 +94,9 @@ public class Disk: Module {
|
||||
self.selectedDisk = value
|
||||
self.capacityReader?.read()
|
||||
}
|
||||
self.settingsView.callback = { [unowned self] in
|
||||
self.capacityReader?.read()
|
||||
}
|
||||
|
||||
if let reader = self.capacityReader {
|
||||
self.addReader(reader)
|
||||
|
||||
@@ -26,6 +26,11 @@ internal class Popup: NSView {
|
||||
}
|
||||
|
||||
internal func usageCallback(_ value: DiskList) {
|
||||
if self.list.count != value.list.count {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
self.list = [:]
|
||||
}
|
||||
|
||||
value.list.reversed().forEach { (d: diskInfo) in
|
||||
if self.list[d.name] == nil {
|
||||
DispatchQueue.main.async(execute: {
|
||||
@@ -37,13 +42,19 @@ internal class Popup: NSView {
|
||||
path: d.path
|
||||
)
|
||||
self.addSubview(self.list[d.name]!)
|
||||
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: ((self.diskFullHeight + Constants.Popup.margins) * CGFloat(self.list.count)) - Constants.Popup.margins))
|
||||
})
|
||||
} else {
|
||||
self.list[d.name]?.update(free: d.freeSize)
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
let h: CGFloat = ((self.diskFullHeight + Constants.Popup.margins) * CGFloat(self.list.count)) - Constants.Popup.margins
|
||||
if self.frame.size.height != h {
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: h))
|
||||
NotificationCenter.default.post(name: .updatePopupSize, object: nil, userInfo: ["module": "Disk"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
|
||||
import Cocoa
|
||||
import ModuleKit
|
||||
import StatsKit
|
||||
|
||||
internal class CapacityReader: Reader<DiskList> {
|
||||
private var disks: DiskList = DiskList()
|
||||
public var store: UnsafePointer<Store>? = nil
|
||||
|
||||
public override func setup() {
|
||||
self.interval = 10000
|
||||
@@ -21,7 +23,9 @@ internal class CapacityReader: Reader<DiskList> {
|
||||
|
||||
public override func read() {
|
||||
let keys: [URLResourceKey] = [.volumeNameKey]
|
||||
let removableState = store?.pointee.bool(key: "Disk_removable", defaultValue: false) ?? false
|
||||
let paths = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: keys)!
|
||||
|
||||
if let session = DASessionCreate(kCFAllocatorDefault) {
|
||||
for url in paths {
|
||||
if url.pathComponents.count == 1 || (url.pathComponents.count > 1 && url.pathComponents[1] == "Volumes") {
|
||||
@@ -29,8 +33,13 @@ internal class CapacityReader: Reader<DiskList> {
|
||||
if let diskName = DADiskGetBSDName(disk) {
|
||||
let BSDName: String = String(cString: diskName)
|
||||
|
||||
if let _: diskInfo = self.disks.getDiskByBSDName(BSDName) {
|
||||
if let d: diskInfo = self.disks.getDiskByBSDName(BSDName) {
|
||||
if let idx = self.disks.list.firstIndex(where: { $0.mediaBSDName == BSDName }) {
|
||||
if d.removable && !removableState {
|
||||
self.disks.list.remove(at: idx)
|
||||
continue
|
||||
}
|
||||
|
||||
if let path = self.disks.list[idx].path {
|
||||
self.disks.list[idx].freeSize = freeDiskSpaceInBytes(path.absoluteString)
|
||||
}
|
||||
@@ -38,8 +47,9 @@ internal class CapacityReader: Reader<DiskList> {
|
||||
continue
|
||||
}
|
||||
|
||||
if let d = getDisk(disk) {
|
||||
if let d = getDisk(disk, removableState: removableState) {
|
||||
self.disks.list.append(d)
|
||||
self.disks.list.sort{ $1.removable }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +60,7 @@ internal class CapacityReader: Reader<DiskList> {
|
||||
self.callback(self.disks)
|
||||
}
|
||||
|
||||
private func getDisk(_ disk: DADisk) -> diskInfo? {
|
||||
private func getDisk(_ disk: DADisk, removableState: Bool) -> diskInfo? {
|
||||
var d: diskInfo = diskInfo()
|
||||
|
||||
if let bsdName = DADiskGetBSDName(disk) {
|
||||
@@ -61,7 +71,10 @@ internal class CapacityReader: Reader<DiskList> {
|
||||
if let dict = diskDescription as? [String: AnyObject] {
|
||||
if let removable = dict[kDADiskDescriptionMediaRemovableKey as String] {
|
||||
if removable as! Bool {
|
||||
return nil
|
||||
if !removableState {
|
||||
return nil
|
||||
}
|
||||
d.removable = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,13 @@ internal class Settings: NSView, Settings_v {
|
||||
private var selectedDisk: String
|
||||
private var button: NSPopUpButton?
|
||||
|
||||
private var removableState: Bool = false
|
||||
|
||||
public init(_ title: String, store: UnsafePointer<Store>) {
|
||||
self.title = title
|
||||
self.store = store
|
||||
self.selectedDisk = store.pointee.string(key: "\(self.title)_disk", defaultValue: "")
|
||||
self.removableState = store.pointee.bool(key: "\(self.title)_removable", defaultValue: self.removableState)
|
||||
super.init(frame: CGRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: Constants.Settings.width - (Constants.Settings.margin*2), height: 0))
|
||||
self.wantsLayer = true
|
||||
self.canDrawConcurrently = true
|
||||
@@ -38,13 +41,21 @@ internal class Settings: NSView, Settings_v {
|
||||
public func load(widget: widget_t) {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
|
||||
let rowHeight: CGFloat = 30
|
||||
self.addDiskSelector()
|
||||
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: 30 + (Constants.Settings.margin*2)))
|
||||
self.addSubview(ToggleTitleRow(
|
||||
frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin + (rowHeight + Constants.Settings.margin) * 0, width: self.frame.width - (Constants.Settings.margin*2), height: rowHeight),
|
||||
title: "Show removable disks",
|
||||
action: #selector(toggleRemovable),
|
||||
state: self.removableState
|
||||
))
|
||||
|
||||
self.setFrameSize(NSSize(width: self.frame.width, height: rowHeight*2 + (Constants.Settings.margin*3)))
|
||||
}
|
||||
|
||||
private func addDiskSelector() {
|
||||
let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: self.frame.width, height: 29))
|
||||
let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin*2 + 30, width: self.frame.width, height: 29))
|
||||
|
||||
let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: (view.frame.height - 16)/2, width: view.frame.width - 52, height: 17), "Disk to show")
|
||||
rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light)
|
||||
@@ -63,6 +74,10 @@ internal class Settings: NSView, Settings_v {
|
||||
internal func setList(_ list: DiskList) {
|
||||
let disks = list.list.map{ $0.name }
|
||||
DispatchQueue.main.async(execute: {
|
||||
if self.button?.itemTitles.count != disks.count {
|
||||
self.button?.removeAllItems()
|
||||
}
|
||||
|
||||
if disks != self.button?.itemTitles {
|
||||
self.button?.addItems(withTitles: disks)
|
||||
if self.selectedDisk != "" {
|
||||
@@ -72,10 +87,23 @@ internal class Settings: NSView, Settings_v {
|
||||
})
|
||||
}
|
||||
|
||||
@objc func handleSelection(_ sender: NSPopUpButton) {
|
||||
@objc private func handleSelection(_ sender: NSPopUpButton) {
|
||||
guard let item = sender.selectedItem else { return }
|
||||
self.selectedDisk = item.title
|
||||
self.store.pointee.set(key: "\(self.title)_disk", value: item.title)
|
||||
self.selectedDiskHandler(item.title)
|
||||
}
|
||||
|
||||
@objc private func toggleRemovable(_ sender: NSControl) {
|
||||
var state: NSControl.StateValue? = nil
|
||||
if #available(OSX 10.15, *) {
|
||||
state = sender is NSSwitch ? (sender as! NSSwitch).state: nil
|
||||
} else {
|
||||
state = sender is NSButton ? (sender as! NSButton).state: nil
|
||||
}
|
||||
|
||||
self.removableState = state! == .on ? true : false
|
||||
self.store.pointee.set(key: "\(self.title)_removable", value: self.removableState)
|
||||
self.callback()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +375,7 @@ public extension Notification.Name {
|
||||
static let switchWidget = Notification.Name("switchWidget")
|
||||
static let checkForUpdates = Notification.Name("checkForUpdates")
|
||||
static let clickInSettings = Notification.Name("clickInSettings")
|
||||
static let updatePopupSize = Notification.Name("updatePopupSize")
|
||||
}
|
||||
|
||||
public class NSButtonWithPadding: NSButton {
|
||||
|
||||
Reference in New Issue
Block a user