From 5a0f8ce8546af2779922484a0209960a286f91f6 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Mon, 2 Sep 2019 23:47:08 +0200 Subject: [PATCH] initialized new menu view --- Stats.xcodeproj/project.pbxproj | 12 ++ Stats/AppDelegate.swift | 35 +++- Stats/MenuBar.swift | 86 --------- Stats/Modules/Battery/Battery.swift | 21 +++ Stats/Modules/CPU/CPU.swift | 22 +++ Stats/Modules/Disk/Disk.swift | 7 + Stats/Modules/Memory/Memory.swift | 21 +++ Stats/Modules/Module.swift | 3 + Stats/Modules/Network/Network.swift | 21 +++ .../Assets.xcassets/icons/Contents.json | 6 + .../Contents.json | 26 +++ .../baseline_build_black_18pt_1x.png | Bin 0 -> 216 bytes .../baseline_build_black_18pt_2x.png | Bin 0 -> 303 bytes .../baseline_build_black_18pt_3x.png | Bin 0 -> 444 bytes .../Base.lproj/Main.storyboard | 51 +++++- Stats/Views/MainViewController.swift | 166 ++++++++++++++++++ 16 files changed, 382 insertions(+), 95 deletions(-) create mode 100644 Stats/Supporting Files/Assets.xcassets/icons/Contents.json create mode 100644 Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/Contents.json create mode 100644 Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_1x.png create mode 100644 Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_2x.png create mode 100644 Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_3x.png create mode 100644 Stats/Views/MainViewController.swift diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index 6821e782..411d1bbe 100755 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 9AF0F32522DA92C400026AE6 /* NetworkText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0F32422DA92C400026AE6 /* NetworkText.swift */; }; 9AF0F32722DA92DD00026AE6 /* NetworkDotsText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0F32622DA92DD00026AE6 /* NetworkDotsText.swift */; }; 9AF0F32922DA92E800026AE6 /* NetworkArrowsText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0F32822DA92E800026AE6 /* NetworkArrowsText.swift */; }; + 9AF6F1FE231D732600B8E1E4 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF6F1FD231D732600B8E1E4 /* MainViewController.swift */; }; 9AFA402522AE49A200FE90BC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9AFA402422AE49A200FE90BC /* Assets.xcassets */; }; 9AFA402822AE49A200FE90BC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9AFA402622AE49A200FE90BC /* Main.storyboard */; }; 9AFA402F22AE49AE00FE90BC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AFA402E22AE49AE00FE90BC /* AppDelegate.swift */; }; @@ -96,6 +97,7 @@ 9AF0F32422DA92C400026AE6 /* NetworkText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkText.swift; sourceTree = ""; }; 9AF0F32622DA92DD00026AE6 /* NetworkDotsText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkDotsText.swift; sourceTree = ""; }; 9AF0F32822DA92E800026AE6 /* NetworkArrowsText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkArrowsText.swift; sourceTree = ""; }; + 9AF6F1FD231D732600B8E1E4 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 9AFA401E22AE49A100FE90BC /* StatsLauncher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StatsLauncher.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9AFA402422AE49A200FE90BC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 9AFA402722AE49A200FE90BC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -154,6 +156,7 @@ 9A1410F7229E721100D29793 /* Stats */ = { isa = PBXGroup; children = ( + 9AF6F1FC231D72EC00B8E1E4 /* Views */, 9A74D59522B440D4004FE1FA /* Widgets */, 9A5B1CB3229E72A7008B9D3C /* Supporting Files */, 9A5B1CBA229E7892008B9D3C /* Modules */, @@ -280,6 +283,14 @@ path = Network; sourceTree = ""; }; + 9AF6F1FC231D72EC00B8E1E4 /* Views */ = { + isa = PBXGroup; + children = ( + 9AF6F1FD231D732600B8E1E4 /* MainViewController.swift */, + ); + path = Views; + sourceTree = ""; + }; 9AFA401F22AE49A100FE90BC /* StatsLauncher */ = { isa = PBXGroup; children = ( @@ -423,6 +434,7 @@ 9A7B8F6D22A2C3D600DEB352 /* MemoryReader.swift in Sources */, 9A79B36C22D3BEF000BF1C3A /* Module.swift in Sources */, 9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */, + 9AF6F1FE231D732600B8E1E4 /* MainViewController.swift in Sources */, 9A57A19D22A1E3270033E318 /* CPU.swift in Sources */, 9A58D1B222C150D700405315 /* NetworkReader.swift in Sources */, 9A09C8A022B3A7E20018426F /* BatteryReader.swift in Sources */, diff --git a/Stats/AppDelegate.swift b/Stats/AppDelegate.swift index 67aafc0d..d72b124b 100755 --- a/Stats/AppDelegate.swift +++ b/Stats/AppDelegate.swift @@ -15,6 +15,7 @@ extension Notification.Name { let modules: Observable<[Module]> = Observable([CPU(), Memory(), Disk(), Battery(), Network()]) let updater = macAppUpdater(user: "exelban", repo: "stats") +let menu = NSPopover() let appStoreMode: Bool = false @@ -29,33 +30,37 @@ class AppDelegate: NSObject, NSApplicationDelegate { return } - _ = MenuBar(menuBarItem, menuBarButton: menuBarButton) + menuBarButton.action = #selector(toggleMenu) + menu.contentViewController = MainViewController.Init() + menu.behavior = NSPopover.Behavior.transient + _ = MenuBar(menuBarItem, menuBarButton: menuBarButton) + let launcherAppId = "eu.exelban.StatsLauncher" let runningApps = NSWorkspace.shared.runningApplications let isRunning = !runningApps.filter { $0.bundleIdentifier == launcherAppId }.isEmpty - + if defaults.object(forKey: "runAtLogin") == nil { SMLoginItemSetEnabled(launcherAppId as CFString, true) self.defaults.set(true, forKey: "runAtLogin") } - + if defaults.object(forKey: "dockIcon") != nil { let dockIconStatus = defaults.bool(forKey: "dockIcon") ? NSApplication.ActivationPolicy.regular : NSApplication.ActivationPolicy.accessory NSApp.setActivationPolicy(dockIconStatus) } - + if !appStoreMode && defaults.object(forKey: "checkUpdatesOnLogin") == nil || defaults.bool(forKey: "checkUpdatesOnLogin") { updater.check() { result, error in if error != nil && error as! String == "No internet connection" { return } - + guard error == nil, let version: version = result else { print("Error: \(error ?? "check error")") return } - + if version.newest { DispatchQueue.main.async(execute: { let updatesVC: NSWindowController? = NSStoryboard(name: "Updates", bundle: nil).instantiateController(withIdentifier: "UpdatesVC") as? NSWindowController @@ -66,7 +71,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } } - + if isRunning { DistributedNotificationCenter.default().post(name: .killLauncher, object: Bundle.main.bundleIdentifier!) } @@ -79,6 +84,22 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } } + + @objc func toggleMenu(_ sender: Any?) { + if menu.isShown { + menu.performClose(sender) + } else { + if let button = self.menuBarItem.button { + NSApplication.shared.activate(ignoringOtherApps: true) + menu.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) + menu.becomeFirstResponder() + } + } + } + + func applicationWillResignActive(_ notification: Notification) { + menu.performClose(self) + } } class AboutVC: NSViewController { diff --git a/Stats/MenuBar.swift b/Stats/MenuBar.swift index c6ed5cda..0698e0f6 100644 --- a/Stats/MenuBar.swift +++ b/Stats/MenuBar.swift @@ -26,105 +26,19 @@ class MenuBar { func generateMenuBar() { buildModulesView() - menuBarItem.menu = buildMenu() for module in modules.value { module.active.subscribe(observer: self) { (value, _) in self.buildModulesView() self.menuBarItem.menu?.removeAllItems() - self.menuBarItem.menu = self.buildMenu() } module.available.subscribe(observer: self) { (value, _) in self.buildModulesView() self.menuBarItem.menu?.removeAllItems() - self.menuBarItem.menu = self.buildMenu() } } } - func buildMenu() -> NSMenu { - let menu = NSMenu() - - for module in modules.value { - if module.available.value { - menu.addItem(module.menu) - } - } - - menu.addItem(NSMenuItem.separator()) - - let preferences = NSMenuItem(title: "Preferences", action: nil, keyEquivalent: "") - let preferencesMenu = NSMenu() - - let checkForUpdates = NSMenuItem(title: "Check for updates on start", action: #selector(toggleMenu), keyEquivalent: "") - checkForUpdates.state = defaults.bool(forKey: "checkUpdatesOnLogin") || defaults.object(forKey: "checkUpdatesOnLogin") == nil ? NSControl.StateValue.on : NSControl.StateValue.off - checkForUpdates.target = self - preferencesMenu.addItem(checkForUpdates) - - let runAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleMenu), keyEquivalent: "") - runAtLogin.state = defaults.bool(forKey: "runAtLogin") || defaults.object(forKey: "runAtLogin") == nil ? NSControl.StateValue.on : NSControl.StateValue.off - runAtLogin.target = self - preferencesMenu.addItem(runAtLogin) - - let dockIcon = NSMenuItem(title: "Show icon in dock", action: #selector(toggleMenu), keyEquivalent: "") - dockIcon.state = defaults.bool(forKey: "dockIcon") ? NSControl.StateValue.on : NSControl.StateValue.off - dockIcon.target = self - preferencesMenu.addItem(dockIcon) - - preferences.submenu = preferencesMenu - menu.addItem(preferences) - - menu.addItem(NSMenuItem.separator()) - - let updateMenu = NSMenuItem(title: "Check for updates", action: #selector(checkUpdate), keyEquivalent: "") - updateMenu.target = self - - let aboutMenu = NSMenuItem(title: "About Stats", action: #selector(openAbout), keyEquivalent: "") - aboutMenu.target = self - - if !appStoreMode { - menu.addItem(updateMenu) - } - menu.addItem(aboutMenu) - menu.addItem(NSMenuItem(title: "Quit Stats", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) - - return menu - } - - @objc func checkUpdate(_ sender : NSMenuItem) { - let updatesVC: NSWindowController? = NSStoryboard(name: "Updates", bundle: nil).instantiateController(withIdentifier: "UpdatesVC") as? NSWindowController - updatesVC?.window?.center() - updatesVC?.window?.level = .floating - updatesVC!.showWindow(self) - } - - @objc func openAbout(_ sender : NSMenuItem) { - let aboutVC: NSWindowController? = NSStoryboard(name: "About", bundle: nil).instantiateController(withIdentifier: "AboutVC") as? NSWindowController - aboutVC?.window?.center() - aboutVC?.window?.level = .floating - aboutVC!.showWindow(self) - } - - @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 "Start at login": - SMLoginItemSetEnabled(launcherId as CFString, status) - self.defaults.set(status, forKey: "runAtLogin") - case "Check for updates on start": - self.defaults.set(status, forKey: "checkUpdatesOnLogin") - case "Show icon in dock": - self.defaults.set(status, forKey: "dockIcon") - let iconStatus = status ? NSApplication.ActivationPolicy.regular : NSApplication.ActivationPolicy.accessory - NSApp.setActivationPolicy(iconStatus) - return - default: break - } - } - func buildModulesView() { for subview in self.menuBarButton.subviews { subview.removeFromSuperview() diff --git a/Stats/Modules/Battery/Battery.swift b/Stats/Modules/Battery/Battery.swift index 73dcdfd0..a42f0da5 100644 --- a/Stats/Modules/Battery/Battery.swift +++ b/Stats/Modules/Battery/Battery.swift @@ -17,6 +17,8 @@ class Battery: Module { var active: Observable var available: Observable var reader: Reader = BatteryReader() + var viewAvailable: Bool = true + var tabView: NSTabViewItem = NSTabViewItem() let defaults = UserDefaults.standard var widgetType: WidgetType = Widgets.Mini @@ -29,6 +31,25 @@ class Battery: Module { self.view = BatteryView(frame: NSMakeRect(0, 0, widgetSize.width, widgetSize.height)) initMenu() initWidget() + initTab() + } + + func initTab() { + self.tabView.view?.frame = NSRect(x: 0, y: 0, width: TabWidth, height: TabHeight) + + let text: NSTextField = NSTextField(string: self.name) + text.isEditable = false + text.isSelectable = false + text.isBezeled = false + text.wantsLayer = true + text.textColor = .labelColor + text.canDrawSubviewsIntoLayer = true + text.alignment = .natural + text.font = NSFont.systemFont(ofSize: 13, weight: .regular) + text.frame.origin.x = ((self.tabView.view?.frame.size.width)! - 50) / 2 + text.frame.origin.y = ((self.tabView.view?.frame.size.height)! - 22) / 2 + + self.tabView.view?.addSubview(text) } func start() { diff --git a/Stats/Modules/CPU/CPU.swift b/Stats/Modules/CPU/CPU.swift index 1668b308..69046759 100644 --- a/Stats/Modules/CPU/CPU.swift +++ b/Stats/Modules/CPU/CPU.swift @@ -18,6 +18,9 @@ class CPU: Module { var available: Observable var hyperthreading: Observable var reader: Reader = CPUReader() + var tabView: NSTabViewItem = NSTabViewItem() + + var viewAvailable: Bool = true let defaults = UserDefaults.standard var widgetType: WidgetType @@ -36,6 +39,25 @@ class CPU: Module { initWidget() initMenu() + initTab() + } + + func initTab() { + self.tabView.view?.frame = NSRect(x: 0, y: 0, width: TabWidth, height: TabHeight) + + let text: NSTextField = NSTextField(string: self.name) + text.isEditable = false + text.isSelectable = false + text.isBezeled = false + text.wantsLayer = true + text.textColor = .labelColor + text.canDrawSubviewsIntoLayer = true + text.alignment = .natural + text.font = NSFont.systemFont(ofSize: 13, weight: .regular) + text.frame.origin.x = ((self.tabView.view?.frame.size.width)! - 30) / 2 + text.frame.origin.y = ((self.tabView.view?.frame.size.height)! - 22) / 2 + + self.tabView.view?.addSubview(text) } func initMenu() { diff --git a/Stats/Modules/Disk/Disk.swift b/Stats/Modules/Disk/Disk.swift index c32a20df..adb0d9ff 100644 --- a/Stats/Modules/Disk/Disk.swift +++ b/Stats/Modules/Disk/Disk.swift @@ -19,6 +19,8 @@ class Disk: Module { var active: Observable var available: Observable + var viewAvailable: Bool = false + var tabView: NSTabViewItem = NSTabViewItem() var reader: Reader = DiskReader() @@ -31,6 +33,11 @@ class Disk: Module { self.initWidget() self.initMenu() + initTab() + } + + func initTab() { + self.tabView.view?.frame = NSRect(x: 0, y: 0, width: TabWidth, height: TabHeight) } func initMenu() { diff --git a/Stats/Modules/Memory/Memory.swift b/Stats/Modules/Memory/Memory.swift index c3f11e87..1ba0cf95 100644 --- a/Stats/Modules/Memory/Memory.swift +++ b/Stats/Modules/Memory/Memory.swift @@ -18,6 +18,8 @@ class Memory: Module { var available: Observable var reader: Reader = MemoryReader() var widgetType: WidgetType + var viewAvailable: Bool = true + var tabView: NSTabViewItem = NSTabViewItem() let defaults = UserDefaults.standard @@ -29,6 +31,25 @@ class Memory: Module { self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Mini initWidget() initMenu() + initTab() + } + + func initTab() { + self.tabView.view?.frame = NSRect(x: 0, y: 0, width: TabWidth, height: TabHeight) + + let text: NSTextField = NSTextField(string: self.name) + text.isEditable = false + text.isSelectable = false + text.isBezeled = false + text.wantsLayer = true + text.textColor = .labelColor + text.canDrawSubviewsIntoLayer = true + text.alignment = .natural + text.font = NSFont.systemFont(ofSize: 13, weight: .regular) + text.frame.origin.x = ((self.tabView.view?.frame.size.width)! - 50) / 2 + text.frame.origin.y = ((self.tabView.view?.frame.size.height)! - 22) / 2 + + self.tabView.view?.addSubview(text) } func initMenu() { diff --git a/Stats/Modules/Module.swift b/Stats/Modules/Module.swift index b13e9ce9..11c0bb21 100644 --- a/Stats/Modules/Module.swift +++ b/Stats/Modules/Module.swift @@ -19,6 +19,9 @@ protocol Module: class { var active: Observable { get } var available: Observable { get } + var viewAvailable: Bool { get } + var tabView: NSTabViewItem { get } + var reader: Reader { get } func start() diff --git a/Stats/Modules/Network/Network.swift b/Stats/Modules/Network/Network.swift index d8503e13..d5f9c186 100644 --- a/Stats/Modules/Network/Network.swift +++ b/Stats/Modules/Network/Network.swift @@ -18,6 +18,8 @@ class Network: Module { var available: Observable var reader: Reader = NetworkReader() var widgetType: WidgetType = 2.0 + var viewAvailable: Bool = true + var tabView: NSTabViewItem = NSTabViewItem() let defaults = UserDefaults.standard @@ -27,6 +29,25 @@ class Network: Module { self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.NetworkDots initMenu() initWidget() + initTab() + } + + func initTab() { + self.tabView.view?.frame = NSRect(x: 0, y: 0, width: TabWidth, height: TabHeight) + + let text: NSTextField = NSTextField(string: self.name) + text.isEditable = false + text.isSelectable = false + text.isBezeled = false + text.wantsLayer = true + text.textColor = .labelColor + text.canDrawSubviewsIntoLayer = true + text.alignment = .natural + text.font = NSFont.systemFont(ofSize: 13, weight: .regular) + text.frame.origin.x = ((self.tabView.view?.frame.size.width)! - 50) / 2 + text.frame.origin.y = ((self.tabView.view?.frame.size.height)! - 22) / 2 + + self.tabView.view?.addSubview(text) } func start() { diff --git a/Stats/Supporting Files/Assets.xcassets/icons/Contents.json b/Stats/Supporting Files/Assets.xcassets/icons/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Stats/Supporting Files/Assets.xcassets/icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/Contents.json b/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/Contents.json new file mode 100644 index 00000000..f9f14e7c --- /dev/null +++ b/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "baseline_build_black_18pt_1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "baseline_build_black_18pt_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "baseline_build_black_18pt_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_1x.png b/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_1x.png new file mode 100644 index 0000000000000000000000000000000000000000..792a9c06150a1654585144e5bcfa2263db236f50 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|m=6kw0hEy<4J=4g>lqlf(uvy3{ zKR|%x#sUWJ4k5uH76*=;0R8O@EEk=>Finj8f2;FgLduKX49B0!E5~t|mOo=D(qd8E z_^7?}^t~^IY&9nWV+8NYsXMPQxvYMdHN7XJ?DCO$d(Ud81qF}a!`2(wZZK6q(*u5h_ Pr!siD`njxgN@xNAStw5- literal 0 HcmV?d00001 diff --git a/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_2x.png b/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a1e7f71e7771dde9ee0d9ba52fc0aaa6646b7147 GIT binary patch literal 303 zcmV+~0nq-5P)yaxAF{Wc9r8Vn(I2 z^$%J>*2jw(W&YY8vSD8MPi31iFWe#P;N>5c%`q=#TSHd$@8G~9^THysC{B5i>jqsv z8oHqZSv;5Sq6`?_MYZtcE~=6pyQmUmF*LIL_2dCFv~!6$b!krNO2U)_)Q!8Sih!bW z+(iw5JE$viz;)`1B6N~ELo;aVjC@=!l}?gzf3IOZiHvLK^sxW{002ovPDHLkV1lO? BhAsdA literal 0 HcmV?d00001 diff --git a/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_3x.png b/Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/baseline_build_black_18pt_3x.png new file mode 100644 index 0000000000000000000000000000000000000000..34e6be7afa827c733fff673938bb22878776996c GIT binary patch literal 444 zcmV;t0YmII?+vL`DMbZBfe0lJ>8I`0|MAX!P zwmOlZyz9Ehy41cu0Dd?Zml7>-hRXM<{g zoNz^lPcBElPwtUcpWG3FPi_Wf9fU@1$9TCKR3%6nxiTX+kD>(3o&rE7deS@hlr62%@o2k&g~-xE+mPV#}v+OB9UCoLJ8wI zH-|)U6{HZ$I&rQKiCET3Jrm$u$OEF-Hma6X*flLEYA3JcnozWWsCJ>@Vx)a?&18LY z1H7oY2Au2SR>_@exN_p;{BcmnGWW>0-LcKNmb2H+7_-EQGeH{wja*u}I*OK^JNeVx znNO}BJ6G+KJO1ZfrB5!c=Sq*uSh*^SR;`s=2Y=i?_~Hr-f*-EHD1Z+xub`A%o?!qF moPttwuS!bsL))d7lIa6`({}_$U~0000 - + - + + @@ -20,5 +21,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Stats/Views/MainViewController.swift b/Stats/Views/MainViewController.swift new file mode 100644 index 00000000..aa5fcf9a --- /dev/null +++ b/Stats/Views/MainViewController.swift @@ -0,0 +1,166 @@ +// +// MainViewController.swift +// Stats +// +// Created by Serhiy Mytrovtsiy on 02/09/2019. +// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import ServiceManagement + +let TabWidth: CGFloat = 300 +let TabHeight: CGFloat = 356 + +class MainViewController: NSViewController { + let defaults = UserDefaults.standard + + @IBOutlet weak var tabView: NSTabView! + @IBOutlet weak var topStackView: NSStackView! + + var segmentsControl: NSSegmentedControl! + var settingsButton: NSButton! + + static func Init() -> MainViewController { + let storyboard = NSStoryboard.init(name: "Main", bundle: nil) + let identifier = NSStoryboard.SceneIdentifier("MainViewController") + + guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? MainViewController else { + fatalError("Why cant i find MainViewController? - Check Main.storyboard") + } + + return viewcontroller + } + + override func viewDidLoad() { + super.viewDidLoad() + + makeHeader() + } + + func makeHeader() { + var items: [String] = [] + for module in modules.value { + if module.viewAvailable && module.available.value { + items.append(module.name) + + let tab = module.tabView + tab.label = module.name + tab.identifier = module.name + tab.view?.wantsLayer = true + tab.view?.layer?.backgroundColor = NSColor.white.cgColor + + tabView.addTabViewItem(module.tabView) + } + } + + self.segmentsControl = NSSegmentedControl(labels: items, trackingMode: NSSegmentedControl.SwitchTracking.selectOne, target: self, action: #selector(switchTabs)) + self.segmentsControl.setSelected(true, forSegment: 0) + self.segmentsControl.segmentDistribution = .fillEqually + + let button = NSButton(frame: NSRect(x: 0, y: 0, width: 26, height: 20)) + button.title = "" + button.image = NSImage(named: NSImage.Name("NSActionTemplate")) + button.imagePosition = .imageOnly + button.bezelStyle = .texturedSquare + button.setButtonType(.momentaryPushIn) + button.action = #selector(showSettings) + + button.widthAnchor.constraint(equalToConstant: 26).isActive = true + button.heightAnchor.constraint(equalToConstant: 21).isActive = true + + self.topStackView.addView(self.segmentsControl, in: NSStackView.Gravity.center) + self.topStackView.addView(button, in: NSStackView.Gravity.center) + } + + @objc func switchTabs(_ sender: NSSegmentedControl) { + if let selectedLabel = self.segmentsControl.label(forSegment: sender.selectedSegment) { + let tabNumber = self.tabView.indexOfTabViewItem(withIdentifier: selectedLabel) + self.tabView.selectTabViewItem(at: tabNumber) + } + } + + @IBAction func showSettings(_ sender: NSButton) { + let settings = buildSettings() + let p = NSPoint(x: NSEvent.mouseLocation.x + 3, y: NSEvent.mouseLocation.y - 3) + settings.popUp(positioning: settings.item(at: 0), at:p , in: nil) + } + + func buildSettings() -> NSMenu { + let menu = NSMenu() + + for module in modules.value { + if module.available.value { + menu.addItem(module.menu) + } + } + + menu.addItem(NSMenuItem.separator()) + + let checkForUpdates = NSMenuItem(title: "Check for updates on start", action: #selector(toggleMenu), keyEquivalent: "") + checkForUpdates.state = defaults.bool(forKey: "checkUpdatesOnLogin") || defaults.object(forKey: "checkUpdatesOnLogin") == nil ? NSControl.StateValue.on : NSControl.StateValue.off + checkForUpdates.target = self + + let runAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleMenu), keyEquivalent: "") + runAtLogin.state = defaults.bool(forKey: "runAtLogin") || defaults.object(forKey: "runAtLogin") == nil ? NSControl.StateValue.on : NSControl.StateValue.off + runAtLogin.target = self + + let dockIcon = NSMenuItem(title: "Show icon in dock", action: #selector(toggleMenu), keyEquivalent: "") + dockIcon.state = defaults.bool(forKey: "dockIcon") ? NSControl.StateValue.on : NSControl.StateValue.off + dockIcon.target = self + + menu.addItem(checkForUpdates) + menu.addItem(runAtLogin) + menu.addItem(dockIcon) + + menu.addItem(NSMenuItem.separator()) + + let updateMenu = NSMenuItem(title: "Check for updates", action: #selector(checkUpdate), keyEquivalent: "") + updateMenu.target = self + + let aboutMenu = NSMenuItem(title: "About Stats", action: #selector(openAbout), keyEquivalent: "") + aboutMenu.target = self + + if !appStoreMode { + menu.addItem(updateMenu) + } + menu.addItem(aboutMenu) + menu.addItem(NSMenuItem(title: "Quit Stats", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "")) + + return menu + } + + @objc func checkUpdate(_ sender : NSMenuItem) { + let updatesVC: NSWindowController? = NSStoryboard(name: "Updates", bundle: nil).instantiateController(withIdentifier: "UpdatesVC") as? NSWindowController + updatesVC?.window?.center() + updatesVC?.window?.level = .floating + updatesVC!.showWindow(self) + } + + @objc func openAbout(_ sender : NSMenuItem) { + let aboutVC: NSWindowController? = NSStoryboard(name: "About", bundle: nil).instantiateController(withIdentifier: "AboutVC") as? NSWindowController + aboutVC?.window?.center() + aboutVC?.window?.level = .floating + aboutVC!.showWindow(self) + } + + @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 "Start at login": + SMLoginItemSetEnabled(launcherId as CFString, status) + self.defaults.set(status, forKey: "runAtLogin") + case "Check for updates on start": + self.defaults.set(status, forKey: "checkUpdatesOnLogin") + case "Show icon in dock": + self.defaults.set(status, forKey: "dockIcon") + let iconStatus = status ? NSApplication.ActivationPolicy.regular : NSApplication.ActivationPolicy.accessory + NSApp.setActivationPolicy(iconStatus) + return + default: break + } + } +}