diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index c6b2256f..c781f601 100755 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -9,16 +9,20 @@ /* Begin PBXBuildFile section */ 9A1410F9229E721100D29793 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1410F8229E721100D29793 /* AppDelegate.swift */; }; 9A141100229E721200D29793 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A1410FE229E721200D29793 /* Main.storyboard */; }; - 9A5B1CB6229E73BB008B9D3C /* StatusBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CB4229E73BB008B9D3C /* StatusBarView.swift */; }; - 9A5B1CB7229E73BB008B9D3C /* StatusBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A5B1CB5229E73BB008B9D3C /* StatusBarView.xib */; }; - 9A5B1CB9229E7664008B9D3C /* CpuUsage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CB8229E7664008B9D3C /* CpuUsage.swift */; }; - 9A5B1CBC229E78B3008B9D3C /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CBB229E78B3008B9D3C /* Store.swift */; }; + 9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A57A18422A1D26D0033E318 /* MenuBar.swift */; }; + 9A57A19B22A1E1C50033E318 /* Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A57A19A22A1E1C50033E318 /* Module.swift */; }; + 9A57A19D22A1E3270033E318 /* CPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A57A19C22A1E3270033E318 /* CPU.swift */; }; 9A5B1CBF229E78F0008B9D3C /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CBE229E78F0008B9D3C /* Observable.swift */; }; - 9A5B1CC1229E7A1C008B9D3C /* NilLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CC0229E7A1C008B9D3C /* NilLoadable.swift */; }; 9A5B1CC5229E7B40008B9D3C /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CC4229E7B40008B9D3C /* Extensions.swift */; }; - 9A5B1CC7229E7F44008B9D3C /* MemoryUsage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CC6229E7F44008B9D3C /* MemoryUsage.swift */; }; - 9A5B1CC9229E8621008B9D3C /* DiskUsage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A5B1CC8229E8621008B9D3C /* DiskUsage.swift */; }; 9A6CFC0122A1C9F5001E782D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9A6CFC0022A1C9F5001E782D /* Assets.xcassets */; }; + 9A7B8F5B22A290A200DEB352 /* CPU.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A7B8F5A22A290A200DEB352 /* CPU.xib */; }; + 9A7B8F5E22A2A57600DEB352 /* CPUReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7B8F5D22A2A57600DEB352 /* CPUReader.swift */; }; + 9A7B8F6522A2C19D00DEB352 /* Memory.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A7B8F6422A2C19D00DEB352 /* Memory.xib */; }; + 9A7B8F6722A2C1B900DEB352 /* Disk.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A7B8F6622A2C1B900DEB352 /* Disk.xib */; }; + 9A7B8F6922A2C3A100DEB352 /* Memory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7B8F6822A2C3A100DEB352 /* Memory.swift */; }; + 9A7B8F6B22A2C3A700DEB352 /* Disk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7B8F6A22A2C3A700DEB352 /* Disk.swift */; }; + 9A7B8F6D22A2C3D600DEB352 /* MemoryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7B8F6C22A2C3D600DEB352 /* MemoryReader.swift */; }; + 9A7B8F6F22A2C57000DEB352 /* DiskReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7B8F6E22A2C57000DEB352 /* DiskReader.swift */; }; 9A82C38F22A1A41700B914CA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A82C38E22A1A41700B914CA /* Main.storyboard */; }; 9AB54D9E22A19EC7006192E0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB54D9D22A19EC7006192E0 /* AppDelegate.swift */; }; 9AB54DA222A19EC8006192E0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9AB54DA122A19EC8006192E0 /* Assets.xcassets */; }; @@ -56,16 +60,20 @@ 9A1410FF229E721200D29793 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 9A141101229E721200D29793 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9A141102229E721200D29793 /* Stats.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Stats.entitlements; sourceTree = ""; }; - 9A5B1CB4229E73BB008B9D3C /* StatusBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarView.swift; sourceTree = ""; }; - 9A5B1CB5229E73BB008B9D3C /* StatusBarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StatusBarView.xib; sourceTree = ""; }; - 9A5B1CB8229E7664008B9D3C /* CpuUsage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CpuUsage.swift; sourceTree = ""; }; - 9A5B1CBB229E78B3008B9D3C /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; + 9A57A18422A1D26D0033E318 /* MenuBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBar.swift; sourceTree = ""; }; + 9A57A19A22A1E1C50033E318 /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = ""; }; + 9A57A19C22A1E3270033E318 /* CPU.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPU.swift; sourceTree = ""; }; 9A5B1CBE229E78F0008B9D3C /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; - 9A5B1CC0229E7A1C008B9D3C /* NilLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NilLoadable.swift; sourceTree = ""; }; 9A5B1CC4229E7B40008B9D3C /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; - 9A5B1CC6229E7F44008B9D3C /* MemoryUsage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryUsage.swift; sourceTree = ""; }; - 9A5B1CC8229E8621008B9D3C /* DiskUsage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskUsage.swift; sourceTree = ""; }; 9A6CFC0022A1C9F5001E782D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 9A7B8F5A22A290A200DEB352 /* CPU.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CPU.xib; sourceTree = ""; }; + 9A7B8F5D22A2A57600DEB352 /* CPUReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPUReader.swift; sourceTree = ""; }; + 9A7B8F6422A2C19D00DEB352 /* Memory.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Memory.xib; sourceTree = ""; }; + 9A7B8F6622A2C1B900DEB352 /* Disk.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Disk.xib; sourceTree = ""; }; + 9A7B8F6822A2C3A100DEB352 /* Memory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Memory.swift; sourceTree = ""; }; + 9A7B8F6A22A2C3A700DEB352 /* Disk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disk.swift; sourceTree = ""; }; + 9A7B8F6C22A2C3D600DEB352 /* MemoryReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryReader.swift; sourceTree = ""; }; + 9A7B8F6E22A2C57000DEB352 /* DiskReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskReader.swift; sourceTree = ""; }; 9A82C38E22A1A41700B914CA /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 9A998CD722A199920087ADE7 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 9A998CD922A199970087ADE7 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; }; @@ -116,13 +124,11 @@ 9A1410F7229E721100D29793 /* Stats */ = { isa = PBXGroup; children = ( - 9A5B1CBD229E78D2008B9D3C /* libs */, - 9A5B1CBA229E7892008B9D3C /* Readers */, 9A5B1CB3229E72A7008B9D3C /* Supporting Files */, + 9A5B1CBA229E7892008B9D3C /* Modules */, + 9A5B1CBD229E78D2008B9D3C /* libs */, 9A1410F8229E721100D29793 /* AppDelegate.swift */, - 9A5B1CB4229E73BB008B9D3C /* StatusBarView.swift */, - 9A5B1CB5229E73BB008B9D3C /* StatusBarView.xib */, - 9A5B1CBB229E78B3008B9D3C /* Store.swift */, + 9A57A18422A1D26D0033E318 /* MenuBar.swift */, ); path = Stats; sourceTree = ""; @@ -130,34 +136,64 @@ 9A5B1CB3229E72A7008B9D3C /* Supporting Files */ = { isa = PBXGroup; children = ( + 9A6CFC0022A1C9F5001E782D /* Assets.xcassets */, 9A1410FE229E721200D29793 /* Main.storyboard */, 9A141101229E721200D29793 /* Info.plist */, - 9A6CFC0022A1C9F5001E782D /* Assets.xcassets */, 9A141102229E721200D29793 /* Stats.entitlements */, ); path = "Supporting Files"; sourceTree = ""; }; - 9A5B1CBA229E7892008B9D3C /* Readers */ = { + 9A5B1CBA229E7892008B9D3C /* Modules */ = { isa = PBXGroup; children = ( - 9A5B1CB8229E7664008B9D3C /* CpuUsage.swift */, - 9A5B1CC6229E7F44008B9D3C /* MemoryUsage.swift */, - 9A5B1CC8229E8621008B9D3C /* DiskUsage.swift */, + 9A7B8F5C22A2926500DEB352 /* CPU */, + 9A7B8F6222A2C17000DEB352 /* Memory */, + 9A7B8F6322A2C17500DEB352 /* Disk */, ); - path = Readers; + path = Modules; sourceTree = ""; }; 9A5B1CBD229E78D2008B9D3C /* libs */ = { isa = PBXGroup; children = ( 9A5B1CBE229E78F0008B9D3C /* Observable.swift */, - 9A5B1CC0229E7A1C008B9D3C /* NilLoadable.swift */, + 9A57A19A22A1E1C50033E318 /* Module.swift */, 9A5B1CC4229E7B40008B9D3C /* Extensions.swift */, ); path = libs; sourceTree = ""; }; + 9A7B8F5C22A2926500DEB352 /* CPU */ = { + isa = PBXGroup; + children = ( + 9A7B8F5A22A290A200DEB352 /* CPU.xib */, + 9A57A19C22A1E3270033E318 /* CPU.swift */, + 9A7B8F5D22A2A57600DEB352 /* CPUReader.swift */, + ); + path = CPU; + sourceTree = ""; + }; + 9A7B8F6222A2C17000DEB352 /* Memory */ = { + isa = PBXGroup; + children = ( + 9A7B8F6422A2C19D00DEB352 /* Memory.xib */, + 9A7B8F6822A2C3A100DEB352 /* Memory.swift */, + 9A7B8F6C22A2C3D600DEB352 /* MemoryReader.swift */, + ); + path = Memory; + sourceTree = ""; + }; + 9A7B8F6322A2C17500DEB352 /* Disk */ = { + isa = PBXGroup; + children = ( + 9A7B8F6622A2C1B900DEB352 /* Disk.xib */, + 9A7B8F6A22A2C3A700DEB352 /* Disk.swift */, + 9A7B8F6E22A2C57000DEB352 /* DiskReader.swift */, + ); + path = Disk; + sourceTree = ""; + }; 9A998CD622A199920087ADE7 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -260,9 +296,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9A5B1CB7229E73BB008B9D3C /* StatusBarView.xib in Resources */, 9AB54DAB22A19F8B006192E0 /* StatsLauncher.app in Resources */, + 9A7B8F5B22A290A200DEB352 /* CPU.xib in Resources */, 9A6CFC0122A1C9F5001E782D /* Assets.xcassets in Resources */, + 9A7B8F6722A2C1B900DEB352 /* Disk.xib in Resources */, + 9A7B8F6522A2C19D00DEB352 /* Memory.xib in Resources */, 9A141100229E721200D29793 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -283,14 +321,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9A5B1CB6229E73BB008B9D3C /* StatusBarView.swift in Sources */, - 9A5B1CC1229E7A1C008B9D3C /* NilLoadable.swift in Sources */, - 9A5B1CC7229E7F44008B9D3C /* MemoryUsage.swift in Sources */, + 9A7B8F6F22A2C57000DEB352 /* DiskReader.swift in Sources */, + 9A7B8F6922A2C3A100DEB352 /* Memory.swift in Sources */, + 9A7B8F5E22A2A57600DEB352 /* CPUReader.swift in Sources */, + 9A7B8F6D22A2C3D600DEB352 /* MemoryReader.swift in Sources */, + 9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */, + 9A57A19D22A1E3270033E318 /* CPU.swift in Sources */, + 9A57A19B22A1E1C50033E318 /* Module.swift in Sources */, 9A5B1CBF229E78F0008B9D3C /* Observable.swift in Sources */, + 9A7B8F6B22A2C3A700DEB352 /* Disk.swift in Sources */, 9A1410F9229E721100D29793 /* AppDelegate.swift in Sources */, - 9A5B1CC9229E8621008B9D3C /* DiskUsage.swift in Sources */, - 9A5B1CB9229E7664008B9D3C /* CpuUsage.swift in Sources */, - 9A5B1CBC229E78B3008B9D3C /* Store.swift in Sources */, 9A5B1CC5229E7B40008B9D3C /* Extensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Stats/AppDelegate.swift b/Stats/AppDelegate.swift index d03aeb44..c0eb7d58 100755 --- a/Stats/AppDelegate.swift +++ b/Stats/AppDelegate.swift @@ -7,84 +7,30 @@ // import Cocoa -import ServiceManagement -import os.log -extension Notification.Name { - static let killLauncher = Notification.Name("killLauncher") -} +let modules: Observable<[Module]> = Observable([CPU(), Memory(), Disk()]) +let colors: Observable = Observable(true) @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { - var statusItem = NSStatusBar.system.statusItem(withLength: CGFloat(84)) - let statusBarView: StatusBarView = StatusBarView.createFromNib()! let defaults = UserDefaults.standard - let launcherAppId = "eu.exelban.StatsLauncher" - + var menuBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) + func applicationDidFinishLaunching(_ aNotification: Notification) { - if NSRunningApplication.runningApplications(withBundleIdentifier: "eu.exelban.StatsLauncher").isEmpty { - DistributedNotificationCenter.default().post(name: .killLauncher, object: Bundle.main.bundleIdentifier!) - } - if defaults.object(forKey: "startOnLogin") != nil { - SMLoginItemSetEnabled(launcherAppId as CFString, defaults.bool(forKey: "startOnLogin")) - } else { - SMLoginItemSetEnabled(launcherAppId as CFString, true) + guard let menuBarButton = self.menuBarItem.button else { + NSApp.terminate(nil) + return } - self.statusItem.length = CGFloat(28 * store.activeWidgets.value) - - let _ = CpuUsage() - let _ = MemoryUsage() - let _ = DiskUsage() - - if let button = statusItem.button { - button.addSubview(statusBarView) - } - statusItem.menu = statusBarView.buildMenu() - - store.activeWidgets.subscribe(observer: self) { (newValue, oldValue) in - self.statusItem.length = CGFloat(28 * newValue) - - if let button = self.statusItem.button { - if newValue == 0 { - self.statusItem.length = NSStatusItem.squareLength - for view in button.subviews { - view.removeFromSuperview() - } - button.image = NSImage(named:NSImage.Name("tray_icon")) - } else { - button.image = nil - button.addSubview(self.statusBarView) - } - } - } + colors << (defaults.object(forKey: "colors") != nil ? defaults.bool(forKey: "colors") : false) + _ = MenuBar(menuBarItem, menuBarButton: menuBarButton) } func applicationWillTerminate(_ aNotification: Notification) { - } - - @objc func toggleStatus(_ sender : NSMenuItem) { - let status = sender.state != NSControl.StateValue.on - sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on - - switch sender.title { - case "CPU": - store.cpuStatus << status - case "Memory": - store.memoryStatus << status - case "Disk": - store.diskStatus << status - case "Colors": - store.colors << status - return - default: break + if modules.value.count != 0 { + for module in modules.value{ + module.stop() + } } - - store.activeWidgets << (status ? store.activeWidgets.value+1 : store.activeWidgets.value-1) - } - - @objc func toggleStartOnLogin(_ sender : NSMenuItem) { - sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on - SMLoginItemSetEnabled(launcherAppId as CFString, sender.state == NSControl.StateValue.on) } } diff --git a/Stats/MenuBar.swift b/Stats/MenuBar.swift new file mode 100644 index 00000000..783598bd --- /dev/null +++ b/Stats/MenuBar.swift @@ -0,0 +1,124 @@ +// +// MenuBar.swift +// Stats +// +// Created by Serhiy Mytrovtsiy on 31.05.2019. +// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import ServiceManagement + +let MODULE_HEIGHT = CGFloat(NSApplication.shared.mainMenu?.menuBarHeight ?? 22) +let MODULE_WIDTH = CGFloat(28) + +class MenuBar { + let defaults = UserDefaults.standard + let menuBarItem: NSStatusItem + lazy var menuBarButton: NSButton = NSButton() + + init(_ menuBarItem: NSStatusItem, menuBarButton: NSButton) { + self.menuBarItem = menuBarItem + self.menuBarButton = menuBarButton + + generateMenuBar() + modules.subscribe(observer: self) { (_, _) in + self.generateMenuBar() + } + } + + func generateMenuBar() { + buildModulesView() + menuBarItem.menu = buildMenu() + + for module in modules.value { + module.active.subscribe(observer: self) { (value, _) in + self.buildModulesView() + } + } + } + + func buildMenu() -> NSMenu { + let menu = NSMenu() + + for module in modules.value { + menu.addItem(module.menu()) + } + + menu.addItem(NSMenuItem.separator()) + + let preferences = NSMenuItem(title: "Preferences", action: nil, keyEquivalent: "") + let preferencesMenu = NSMenu() + + let colorStatus = NSMenuItem(title: "Colors", action: #selector(toggleMenu), keyEquivalent: "") + colorStatus.state = defaults.object(forKey: "colors") != nil && !defaults.bool(forKey: "colors") ? NSControl.StateValue.off : NSControl.StateValue.on + colorStatus.target = self + preferencesMenu.addItem(colorStatus) + + let runAtLogin = NSMenuItem(title: "Run at login", action: #selector(toggleMenu), keyEquivalent: "") + runAtLogin.state = defaults.object(forKey: "runAtLogin") != nil && !defaults.bool(forKey: "runAtLogin") ? NSControl.StateValue.off : NSControl.StateValue.on + runAtLogin.target = self + preferencesMenu.addItem(runAtLogin) + + preferences.submenu = preferencesMenu + menu.addItem(preferences) + + menu.addItem(NSMenuItem.separator()) + menu.addItem(NSMenuItem(title: "Quit Stats", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) + + return menu + } + + @objc func toggleMenu(_ sender : NSMenuItem) { + let launcherId = "eu.exelban.StatsLauncher" + let status = sender.state != NSControl.StateValue.on + sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on + + switch sender.title { + case "Run at login": + SMLoginItemSetEnabled(launcherId as CFString, !status) + self.defaults.set(status, forKey: "runAtLogin") + case "Colors": + self.defaults.set(status, forKey: "colors") + colors << status + return + default: break + } + } + + func buildModulesView() { + for subview in self.menuBarButton.subviews { + subview.removeFromSuperview() + } + + self.menuBarButton.image = NSImage(named:NSImage.Name("tray_icon")) + var WIDTH = CGFloat(modules.value.count * 28) + + let view: NSView = NSView(frame: NSMakeRect(0, 0, WIDTH, MODULE_HEIGHT)) + + let stack: NSStackView = NSStackView(frame: NSMakeRect(0, 0, WIDTH, MODULE_HEIGHT)) + stack.orientation = NSUserInterfaceLayoutOrientation.horizontal + stack.distribution = NSStackView.Distribution.fillEqually + stack.spacing = 0 + + WIDTH = 0 + for module in modules.value { + if module.active.value { + module.start() + WIDTH = WIDTH + module.view.frame.size.width + stack.addView(module.view, in: NSStackView.Gravity.center) + } + } + + if stack.subviews.count != 0 { + view.frame.size.width = WIDTH + stack.frame.size.width = WIDTH + self.menuBarItem.length = WIDTH + + view.addSubview(stack) + + self.menuBarButton.image = nil + self.menuBarButton.addSubview(view) + } + } +} diff --git a/Stats/Modules/CPU/CPU.swift b/Stats/Modules/CPU/CPU.swift new file mode 100644 index 00000000..61d1dff0 --- /dev/null +++ b/Stats/Modules/CPU/CPU.swift @@ -0,0 +1,72 @@ +// +// CPU.swift +// Stats +// +// Created by Serhiy Mytrovtsiy on 01.06.2019. +// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa + +class CPU: Module { + let name: String = "CPU" + var view: NSView = NSView() + let defaults = UserDefaults.standard + + var active: Observable + var reader: Reader = CPUReader() + + @IBOutlet weak var value: NSTextField! + + init() { + self.active = Observable(defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true) + self.view = loadViewFromNib() + +// self.view.frame = CGRect(x: 20, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height ) + } + + func start() { + if !self.reader.usage.value.isNaN { + self.value.stringValue = "\(Int(Float(self.reader.usage.value.roundTo(decimalPlaces: 2))! * 100))%" + self.value.textColor = self.reader.usage.value.usageColor() + } + + self.reader.start() + self.reader.usage.subscribe(observer: self) { (value, _) in + if !value.isNaN { + self.value.stringValue = "\(Int(Float(value.roundTo(decimalPlaces: 2))! * 100))%" + self.value.textColor = value.usageColor() + } + } + + colors.subscribe(observer: self) { (value, _) in + self.value.textColor = self.reader.usage.value.usageColor() + } + } + + func menu() -> NSMenuItem { + let menu = NSMenuItem(title: name, action: #selector(toggle), keyEquivalent: "") + if defaults.object(forKey: name) != nil { + menu.state = defaults.bool(forKey: name) ? NSControl.StateValue.on : NSControl.StateValue.off + } else { + menu.state = NSControl.StateValue.on + } + menu.target = self + menu.isEnabled = true + return menu + } + + @objc func toggle(_ sender: NSMenuItem) { + let state = sender.state != NSControl.StateValue.on + + sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on + self.defaults.set(state, forKey: name) + self.active << state + + if !state { + self.stop() + } else { + self.start() + } + } +} diff --git a/Stats/Modules/CPU/CPU.xib b/Stats/Modules/CPU/CPU.xib new file mode 100644 index 00000000..d88e8dad --- /dev/null +++ b/Stats/Modules/CPU/CPU.xib @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stats/Readers/CpuUsage.swift b/Stats/Modules/CPU/CPUReader.swift old mode 100755 new mode 100644 similarity index 82% rename from Stats/Readers/CpuUsage.swift rename to Stats/Modules/CPU/CPUReader.swift index e555cae8..877231ab --- a/Stats/Readers/CpuUsage.swift +++ b/Stats/Modules/CPU/CPUReader.swift @@ -1,14 +1,15 @@ // -// CpuUsage.swift -// Mini Stats +// reader.swift +// Stats // -// Created by Serhiy Mytrovtsiy on 29/05/2019. +// Created by Serhiy Mytrovtsiy on 01.06.2019. // Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. // import Foundation -class CpuUsage { +class CPUReader: Reader { + var usage: Observable! var cpuInfo: processor_info_array_t! var prevCpuInfo: processor_info_array_t? var numCpuInfo: mach_msg_type_number_t = 0 @@ -19,6 +20,7 @@ class CpuUsage { init() { let mibKeys: [Int32] = [ CTL_HW, HW_NCPU ] + self.usage = Observable(0) mibKeys.withUnsafeBufferPointer() { mib in var sizeOfNumCPUs: size_t = MemoryLayout.size let status = sysctl(processor_info_array_t(mutating: mib.baseAddress), 2, &numCPUs, &sizeOfNumCPUs, nil, 0) @@ -26,10 +28,24 @@ class CpuUsage { numCPUs = 1 } read() - updateTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(read), userInfo: nil, repeats: true) } } + func start() { + if updateTimer != nil { + return + } + updateTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(read), userInfo: nil, repeats: true) + } + + func stop() { + if updateTimer == nil { + return + } + updateTimer.invalidate() + updateTimer = nil + } + @objc func read() { var numCPUsU: natural_t = 0 let err: kern_return_t = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo); @@ -60,7 +76,7 @@ class CpuUsage { inUseOnAllCores = inUseOnAllCores + inUse totalOnAllCores = totalOnAllCores + total } - store.cpuUsage << (Float(inUseOnAllCores) / Float(totalOnAllCores)) + self.usage << (Float(inUseOnAllCores) / Float(totalOnAllCores)) CPUUsageLock.unlock() diff --git a/Stats/Modules/Disk/Disk.swift b/Stats/Modules/Disk/Disk.swift new file mode 100644 index 00000000..8f2d49c4 --- /dev/null +++ b/Stats/Modules/Disk/Disk.swift @@ -0,0 +1,70 @@ +// +// Disk.swift +// Stats +// +// Created by Serhiy Mytrovtsiy on 01.06.2019. +// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa + +class Disk: Module { + let name: String = "Disk" + var view: NSView = NSView() + let defaults = UserDefaults.standard + + var active: Observable + var reader: Reader = DiskReader() + + @IBOutlet weak var value: NSTextField! + + init() { + self.active = Observable(defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true) + self.view = loadViewFromNib() + } + + func start() { + if !self.reader.usage.value.isNaN { + self.value.stringValue = "\(Int(Float(self.reader.usage.value.roundTo(decimalPlaces: 2))! * 100))%" + self.value.textColor = self.reader.usage.value.usageColor() + } + + self.reader.start() + self.reader.usage.subscribe(observer: self) { (value, _) in + if !value.isNaN { + self.value.stringValue = "\(Int(Float(value.roundTo(decimalPlaces: 2))! * 100))%" + self.value.textColor = value.usageColor() + } + } + + colors.subscribe(observer: self) { (value, _) in + self.value.textColor = self.reader.usage.value.usageColor() + } + } + + func menu() -> NSMenuItem { + let menu = NSMenuItem(title: name, action: #selector(toggle), keyEquivalent: "") + if defaults.object(forKey: name) != nil { + menu.state = defaults.bool(forKey: name) ? NSControl.StateValue.on : NSControl.StateValue.off + } else { + menu.state = NSControl.StateValue.on + } + menu.target = self + menu.isEnabled = true + return menu + } + + @objc func toggle(_ sender: NSMenuItem) { + let state = sender.state != NSControl.StateValue.on + + sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on + self.defaults.set(state, forKey: name) + self.active << state + + if !state { + self.stop() + } else { + self.start() + } + } +} diff --git a/Stats/Modules/Disk/Disk.xib b/Stats/Modules/Disk/Disk.xib new file mode 100644 index 00000000..d70326bb --- /dev/null +++ b/Stats/Modules/Disk/Disk.xib @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stats/Readers/DiskUsage.swift b/Stats/Modules/Disk/DiskReader.swift old mode 100755 new mode 100644 similarity index 63% rename from Stats/Readers/DiskUsage.swift rename to Stats/Modules/Disk/DiskReader.swift index e05943f8..2fbf0ada --- a/Stats/Readers/DiskUsage.swift +++ b/Stats/Modules/Disk/DiskReader.swift @@ -1,19 +1,35 @@ // -// DiskUsage.swift -// Mini Stats +// DiskReader.swift +// Stats // -// Created by Serhiy Mytrovtsiy on 29/05/2019. +// Created by Serhiy Mytrovtsiy on 01.06.2019. // Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. // import Foundation -class DiskUsage { +class DiskReader: Reader { + var usage: Observable! var updateTimer: Timer! init() { + self.usage = Observable(0) read() - updateTimer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(read), userInfo: nil, repeats: true) + } + + func start() { + if updateTimer != nil { + return + } + updateTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(read), userInfo: nil, repeats: true) + } + + func stop() { + if updateTimer == nil { + return + } + updateTimer.invalidate() + updateTimer = nil } @objc func read() { @@ -21,7 +37,7 @@ class DiskUsage { let free = freeDiskSpaceInBytes() let usedSpace = total - free - store.diskUsage << (Float(usedSpace) / Float(total)) + self.usage << (Float(usedSpace) / Float(total)) } func totalDiskSpaceInBytes() -> Int64 { diff --git a/Stats/Modules/Memory/Memory.swift b/Stats/Modules/Memory/Memory.swift new file mode 100644 index 00000000..21354ce8 --- /dev/null +++ b/Stats/Modules/Memory/Memory.swift @@ -0,0 +1,70 @@ +// +// Memory.swift +// Stats +// +// Created by Serhiy Mytrovtsiy on 01.06.2019. +// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa + +class Memory: Module { + let name: String = "Memory" + var view: NSView = NSView() + let defaults = UserDefaults.standard + + var active: Observable + var reader: Reader = MemoryReader() + + @IBOutlet weak var value: NSTextField! + + init() { + self.active = Observable(defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true) + self.view = loadViewFromNib() + } + + func start() { + if !self.reader.usage.value.isNaN { + self.value.stringValue = "\(Int(Float(self.reader.usage.value.roundTo(decimalPlaces: 2))! * 100))%" + self.value.textColor = self.reader.usage.value.usageColor() + } + + self.reader.start() + self.reader.usage.subscribe(observer: self) { (value, _) in + if !value.isNaN { + self.value.stringValue = "\(Int(Float(value.roundTo(decimalPlaces: 2))! * 100))%" + self.value.textColor = value.usageColor() + } + } + + colors.subscribe(observer: self) { (value, _) in + self.value.textColor = self.reader.usage.value.usageColor() + } + } + + func menu() -> NSMenuItem { + let menu = NSMenuItem(title: name, action: #selector(toggle), keyEquivalent: "") + if defaults.object(forKey: name) != nil { + menu.state = defaults.bool(forKey: name) ? NSControl.StateValue.on : NSControl.StateValue.off + } else { + menu.state = NSControl.StateValue.on + } + menu.target = self + menu.isEnabled = true + return menu + } + + @objc func toggle(_ sender: NSMenuItem) { + let state = sender.state != NSControl.StateValue.on + + sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on + self.defaults.set(state, forKey: name) + self.active << state + + if !state { + self.stop() + } else { + self.start() + } + } +} diff --git a/Stats/Modules/Memory/Memory.xib b/Stats/Modules/Memory/Memory.xib new file mode 100644 index 00000000..3cf7dd3d --- /dev/null +++ b/Stats/Modules/Memory/Memory.xib @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stats/Readers/MemoryUsage.swift b/Stats/Modules/Memory/MemoryReader.swift old mode 100755 new mode 100644 similarity index 77% rename from Stats/Readers/MemoryUsage.swift rename to Stats/Modules/Memory/MemoryReader.swift index c18252a3..5ba24bfd --- a/Stats/Readers/MemoryUsage.swift +++ b/Stats/Modules/Memory/MemoryReader.swift @@ -1,18 +1,20 @@ // -// MemoryUsage.swift -// Mini Stats +// reader.swift +// Stats // -// Created by Serhiy Mytrovtsiy on 29/05/2019. +// Created by Serhiy Mytrovtsiy on 01.06.2019. // Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. // import Foundation -class MemoryUsage { +class MemoryReader: Reader { + var usage: Observable! var updateTimer: Timer! var totalSize: Float init() { + self.usage = Observable(0) var stats = host_basic_info() var count = UInt32(MemoryLayout.size / MemoryLayout.size) @@ -31,9 +33,23 @@ class MemoryUsage { } read() + } + + func start() { + if updateTimer != nil { + return + } updateTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(read), userInfo: nil, repeats: true) } + func stop() { + if updateTimer == nil { + return + } + updateTimer.invalidate() + updateTimer = nil + } + @objc func read() { var stats = vm_statistics64() var count = UInt32(MemoryLayout.size / MemoryLayout.size) @@ -46,12 +62,12 @@ class MemoryUsage { if kerr == KERN_SUCCESS { let active = Float(stats.active_count) * Float(PAGE_SIZE) -// let inactive = Float(stats.inactive_count) * Float(PAGE_SIZE) + // let inactive = Float(stats.inactive_count) * Float(PAGE_SIZE) let wired = Float(stats.wire_count) * Float(PAGE_SIZE) let compressed = Float(stats.compressor_page_count) * Float(PAGE_SIZE) - + let free = totalSize - (active + wired + compressed) - store.memoryUsage << ((totalSize - free) / totalSize) + self.usage << ((totalSize - free) / totalSize) } else { print("Error with host_statistics64(): " + (String(cString: mach_error_string(kerr), encoding: String.Encoding.ascii) ?? "unknown error")) diff --git a/Stats/StatusBarView.swift b/Stats/StatusBarView.swift deleted file mode 100755 index 3cc8f705..00000000 --- a/Stats/StatusBarView.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// StatusBarView.swift -// Mini Stats -// -// Created by Serhiy Mytrovtsiy on 28.05.2019. -// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. -// - -import Cocoa - -class StatusBarView: NSView, NibLoadable { - @IBOutlet weak var CPUView: NSView! - @IBOutlet weak var CPUTitleLabel: NSTextField! - @IBOutlet weak var CPUValueLabel: NSTextField! - @IBOutlet weak var MemoryView: NSView! - @IBOutlet weak var MemoryTitleLabel: NSTextField! - @IBOutlet weak var MemoryValueLabel: NSTextField! - @IBOutlet weak var DiskView: NSView! - @IBOutlet weak var DiskTitleLabel: NSTextField! - @IBOutlet weak var DiskValueLabel: NSTextField! - - let defaults = UserDefaults.standard - - override init(frame: CGRect) { - super.init(frame: frame) - prepare() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - prepare() - } - - func prepare() { - if store.cpuUsage.value != 0 { - self.CPUValueLabel.stringValue = "\(Int(Float(store.cpuUsage.value.roundTo(decimalPlaces: 2))! * 100))%" - self.CPUValueLabel.textColor = store.cpuUsage.value.usageColor() - } - if store.memoryUsage.value != 0 { - self.MemoryValueLabel.stringValue = "\(Int(Float(store.memoryUsage.value.roundTo(decimalPlaces: 2))! * 100))%" - self.MemoryValueLabel.textColor = store.memoryUsage.value.usageColor() - } - if store.diskUsage.value != 0 { - self.DiskValueLabel.stringValue = "\(Int(Float(store.diskUsage.value.roundTo(decimalPlaces: 2))! * 100))%" - self.DiskValueLabel.textColor = store.diskUsage.value.usageColor() - } - - store.cpuUsage.subscribe(observer: self) { (newValue, _) in - let percentage = Int(Float(newValue.roundTo(decimalPlaces: 2))! * 100) - self.CPUValueLabel.stringValue = "\(percentage)%" - if store.colors.value { - self.CPUValueLabel.textColor = newValue.usageColor() - } - } - store.memoryUsage.subscribe(observer: self) { (newValue, _) in - let percentage = Int(Float(newValue.roundTo(decimalPlaces: 2))! * 100) - self.MemoryValueLabel.stringValue = "\(percentage)%" - if store.colors.value { - self.MemoryValueLabel.textColor = newValue.usageColor() - } - } - store.diskUsage.subscribe(observer: self) { (newValue, _) in - let percentage = Int(Float(newValue.roundTo(decimalPlaces: 2))! * 100) - self.DiskValueLabel.stringValue = "\(percentage)%" - if store.colors.value { - self.DiskValueLabel.textColor = newValue.usageColor() - } - } - - store.cpuStatus.subscribe(observer: self) { (newValue, _) in - self.CPUView.isHidden = !newValue - } - store.memoryStatus.subscribe(observer: self) { (newValue, _) in - self.MemoryView.isHidden = !newValue - } - store.diskStatus.subscribe(observer: self) { (newValue, _) in - self.DiskView.isHidden = !newValue - } - - store.activeWidgets.subscribe(observer: self) { (newValue, _) in - self.frame = CGRect(x: 0 , y: 0, width: CGFloat(28 * newValue), height: self.frame.height) - } - - store.colors.subscribe(observer: self) { (newValue, _) in - if newValue { - self.CPUValueLabel.textColor = store.cpuUsage.value.usageColor() - self.MemoryValueLabel.textColor = store.memoryUsage.value.usageColor() - self.DiskValueLabel.textColor = store.diskUsage.value.usageColor() - } else { - self.CPUValueLabel.textColor = NSColor.labelColor - self.MemoryValueLabel.textColor = NSColor.labelColor - self.DiskValueLabel.textColor = NSColor.labelColor - } - } - } - - func buildMenu() -> NSMenu { - let menu = NSMenu() - - let cpuStatus = NSMenuItem(title: "CPU", action: #selector(AppDelegate.toggleStatus(_:)), keyEquivalent: "") - cpuStatus.state = NSControl.StateValue.on - cpuStatus.isEnabled = true - - let memoryStatus = NSMenuItem(title: "Memory", action: #selector(AppDelegate.toggleStatus(_:)), keyEquivalent: "") - memoryStatus.state = NSControl.StateValue.on - - let diskStatus = NSMenuItem(title: "Disk", action: #selector(AppDelegate.toggleStatus(_:)), keyEquivalent: "") - diskStatus.state = NSControl.StateValue.on - - menu.addItem(cpuStatus) - menu.addItem(memoryStatus) - menu.addItem(diskStatus) - - menu.addItem(NSMenuItem.separator()) - - let colorStatus = NSMenuItem(title: "Colors", action: #selector(AppDelegate.toggleStatus(_:)), keyEquivalent: "") - colorStatus.state = store.colors.value ? NSControl.StateValue.on : NSControl.StateValue.off - menu.addItem(colorStatus) - - let runAtLogin = NSMenuItem(title: "Run at login", action: #selector(AppDelegate.toggleStartOnLogin(_:)), keyEquivalent: "") - if defaults.object(forKey: "startOnLogin") != nil { - runAtLogin.state = defaults.bool(forKey: "startOnLogin") ? NSControl.StateValue.on : NSControl.StateValue.off - } else { - runAtLogin.state = NSControl.StateValue.on - } - menu.addItem(runAtLogin) - - menu.addItem(NSMenuItem.separator()) - menu.addItem(NSMenuItem(title: "Quit Stats", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) - - return menu - } -} diff --git a/Stats/StatusBarView.xib b/Stats/StatusBarView.xib deleted file mode 100755 index bb03c13f..00000000 --- a/Stats/StatusBarView.xib +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Stats/Store.swift b/Stats/Store.swift deleted file mode 100755 index 5481725d..00000000 --- a/Stats/Store.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// Store.swift -// Mini Stats -// -// Created by Serhiy Mytrovtsiy on 29/05/2019. -// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. -// - -import Foundation - -class Store { - let defaults = UserDefaults.standard - - let cpuUsage: Observable - let memoryUsage: Observable - let diskUsage: Observable - - let cpuStatus: Observable - let memoryStatus: Observable - let diskStatus: Observable - - let colors: Observable - - let activeWidgets: Observable - - init() { - cpuUsage = Observable(0) - memoryUsage = Observable(0) - diskUsage = Observable(0) - - cpuStatus = Observable(true) - memoryStatus = Observable(true) - diskStatus = Observable(true) - - activeWidgets = Observable(3) - - colors = Observable(false) - - if defaults.object(forKey: "cpuStatus") != nil { - cpuStatus << defaults.bool(forKey: "cpuStatus") - } - if defaults.object(forKey: "memoryStatus") != nil { - memoryStatus << defaults.bool(forKey: "memoryStatus") - } - if defaults.object(forKey: "diskStatus") != nil { - diskStatus << defaults.bool(forKey: "diskStatus") - } - if defaults.object(forKey: "colors") != nil { - colors << defaults.bool(forKey: "colors") - } - - cpuStatus.subscribe(observer: self) { (newValue, _) in - self.defaults.set(newValue, forKey: "cpuStatus") - } - memoryStatus.subscribe(observer: self) { (newValue, _) in - self.defaults.set(newValue, forKey: "memoryStatus") - } - diskStatus.subscribe(observer: self) { (newValue, _) in - self.defaults.set(newValue, forKey: "diskStatus") - } - - colors.subscribe(observer: self) { (newValue, _) in - self.defaults.set(newValue, forKey: "colors") - } - } -} - -var store = Store() diff --git a/Stats/Supporting Files/Base.lproj/Main.storyboard b/Stats/Supporting Files/Base.lproj/Main.storyboard index 162c8acd..d5df9d6e 100755 --- a/Stats/Supporting Files/Base.lproj/Main.storyboard +++ b/Stats/Supporting Files/Base.lproj/Main.storyboard @@ -1,6 +1,7 @@ + @@ -8,672 +9,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + diff --git a/Stats/libs/Extensions.swift b/Stats/libs/Extensions.swift index 73e033e1..7a7f94b0 100755 --- a/Stats/libs/Extensions.swift +++ b/Stats/libs/Extensions.swift @@ -15,6 +15,10 @@ extension Float { } func usageColor() -> NSColor { + if !colors.value { + return NSColor.textColor + } + switch self { case 0.6...0.8: return NSColor.systemOrange @@ -32,3 +36,4 @@ public enum Unit : Float { case megabyte = 1048576 case gigabyte = 1073741824 } + diff --git a/Stats/libs/Module.swift b/Stats/libs/Module.swift new file mode 100644 index 00000000..f4389b67 --- /dev/null +++ b/Stats/libs/Module.swift @@ -0,0 +1,42 @@ +// +// Module.swift +// Stats +// +// Created by Serhiy Mytrovtsiy on 01.06.2019. +// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa + +protocol Module { + var name: String { get } + var active: Observable { get } + var reader: Reader { get } + var view: NSView { get } + + func menu() -> NSMenuItem + func start() + func stop() +} + +extension Module where Self: Module { + func stop() { + self.reader.stop() + self.reader.usage.unsubscribe(observer: self as AnyObject) + } + + func loadViewFromNib() -> NSView { + var topLevelObjects: NSArray? + if Bundle.main.loadNibNamed(NSNib.Name(String(describing: Self.self)), owner: self, topLevelObjects: &topLevelObjects) { + return (topLevelObjects?.first(where: { $0 is NSView } ) as? NSView)! + } + return NSView() + } +} + +protocol Reader { + var usage: Observable! { get } + func start() + func read() + func stop() +} diff --git a/Stats/libs/NilLoadable.swift b/Stats/libs/NilLoadable.swift deleted file mode 100755 index 8fa3c854..00000000 --- a/Stats/libs/NilLoadable.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// NilLoadable.swift -// Mini Stats -// -// Created by Serhiy Mytrovtsiy on 29/05/2019. -// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. -// - -import Cocoa - -protocol NibLoadable { - static var nibName: String? { get } - static func createFromNib(in bundle: Bundle) -> Self? -} - -extension NibLoadable where Self: NSView { - - static var nibName: String? { - return String(describing: Self.self) - } - - static func createFromNib(in bundle: Bundle = Bundle.main) -> Self? { - guard let nibName = nibName else { return nil } - var topLevelArray: NSArray? = nil - bundle.loadNibNamed(NSNib.Name(nibName), owner: self, topLevelObjects: &topLevelArray) - guard let results = topLevelArray else { return nil } - let views = Array(results).filter { $0 is Self } - return views.last as? Self - } -} diff --git a/Stats/libs/Observable.swift b/Stats/libs/Observable.swift index 3e3843f8..98ccd942 100755 --- a/Stats/libs/Observable.swift +++ b/Stats/libs/Observable.swift @@ -13,12 +13,15 @@ protocol ObservableProtocol { var value: T { get set } func subscribe(observer: AnyObject, block: @escaping (_ newValue: T, _ oldValue: T) -> ()) func unsubscribe(observer: AnyObject) + func userDefaults(key: String) } public final class Observable: ObservableProtocol { typealias ObserverBlock = (_ newValue: T, _ oldValue: T) -> () typealias ObserversEntry = (observer: AnyObject, block: ObserverBlock) private var observers: Array + private let defaults = UserDefaults.standard + private var userDefaultsKey: String = "" init(_ value: T) { self.value = value @@ -31,6 +34,7 @@ public final class Observable: ObservableProtocol { let (_, block) = entry block(value, oldValue) } + updateUserDefaults() } } @@ -47,6 +51,14 @@ public final class Observable: ObservableProtocol { observers = filtered } + + func userDefaults(key: String) { + self.userDefaultsKey = key + } + + func updateUserDefaults() { + self.defaults.set(self.value, forKey: self.userDefaultsKey) + } } func <<(observable: Observable, value: T) {