mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
initialized new menu view
This commit is contained in:
@@ -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 = "<group>"; };
|
||||
9AF0F32622DA92DD00026AE6 /* NetworkDotsText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkDotsText.swift; sourceTree = "<group>"; };
|
||||
9AF0F32822DA92E800026AE6 /* NetworkArrowsText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkArrowsText.swift; sourceTree = "<group>"; };
|
||||
9AF6F1FD231D732600B8E1E4 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
9AFA402722AE49A200FE90BC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
@@ -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 = "<group>";
|
||||
};
|
||||
9AF6F1FC231D72EC00B8E1E4 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9AF6F1FD231D732600B8E1E4 /* MainViewController.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 */,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -17,6 +17,8 @@ class Battery: Module {
|
||||
var active: Observable<Bool>
|
||||
var available: Observable<Bool>
|
||||
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() {
|
||||
|
||||
@@ -18,6 +18,9 @@ class CPU: Module {
|
||||
var available: Observable<Bool>
|
||||
var hyperthreading: Observable<Bool>
|
||||
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() {
|
||||
|
||||
@@ -19,6 +19,8 @@ class Disk: Module {
|
||||
|
||||
var active: Observable<Bool>
|
||||
var available: Observable<Bool>
|
||||
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() {
|
||||
|
||||
@@ -18,6 +18,8 @@ class Memory: Module {
|
||||
var available: Observable<Bool>
|
||||
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() {
|
||||
|
||||
@@ -19,6 +19,9 @@ protocol Module: class {
|
||||
var active: Observable<Bool> { get }
|
||||
var available: Observable<Bool> { get }
|
||||
|
||||
var viewAvailable: Bool { get }
|
||||
var tabView: NSTabViewItem { get }
|
||||
|
||||
var reader: Reader { get }
|
||||
|
||||
func start()
|
||||
|
||||
@@ -18,6 +18,8 @@ class Network: Module {
|
||||
var available: Observable<Bool>
|
||||
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() {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
26
Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/Contents.json
vendored
Normal file
26
Stats/Supporting Files/Assets.xcassets/icons/baseline_build_black_18pt.imageset/Contents.json
vendored
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 216 B |
Binary file not shown.
|
After Width: | Height: | Size: 303 B |
Binary file not shown.
|
After Width: | Height: | Size: 444 B |
@@ -1,8 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Application-->
|
||||
@@ -20,5 +21,51 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="0.0"/>
|
||||
</scene>
|
||||
<!--Main View Controller-->
|
||||
<scene sceneID="Bmi-PC-JO4">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="MainViewController" id="dfW-5f-6r7" customClass="MainViewController" customModule="Stats" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="tkB-rI-I3L">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r7W-52-3OS">
|
||||
<rect key="frame" x="10" y="364" width="280" height="26"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="280" id="ST8-py-pZs"/>
|
||||
<constraint firstAttribute="height" constant="26" id="gmW-gr-uvi"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="xUf-TB-ZyV">
|
||||
<rect key="frame" x="0.0" y="354" width="300" height="5"/>
|
||||
</box>
|
||||
<tabView type="noTabsNoBorder" translatesAutoresizingMaskIntoConstraints="NO" id="Qvr-de-co3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="356"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</tabView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="r7W-52-3OS" firstAttribute="leading" secondItem="tkB-rI-I3L" secondAttribute="leading" constant="10" id="6fT-TM-mkd"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Qvr-de-co3" secondAttribute="trailing" id="830-7Y-2vJ"/>
|
||||
<constraint firstItem="r7W-52-3OS" firstAttribute="top" secondItem="tkB-rI-I3L" secondAttribute="top" constant="10" id="9ED-Zz-TMm"/>
|
||||
<constraint firstItem="Qvr-de-co3" firstAttribute="top" secondItem="r7W-52-3OS" secondAttribute="bottom" constant="8" id="BA2-Rh-TQV"/>
|
||||
<constraint firstItem="Qvr-de-co3" firstAttribute="top" secondItem="xUf-TB-ZyV" secondAttribute="bottom" id="Iy3-bM-jjN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Qvr-de-co3" secondAttribute="bottom" id="OND-Qc-XXz"/>
|
||||
<constraint firstAttribute="trailing" secondItem="r7W-52-3OS" secondAttribute="trailing" constant="10" id="aef-o6-X4m"/>
|
||||
<constraint firstItem="xUf-TB-ZyV" firstAttribute="top" secondItem="r7W-52-3OS" secondAttribute="bottom" constant="7" id="dd6-Oy-q3o"/>
|
||||
<constraint firstAttribute="trailing" secondItem="xUf-TB-ZyV" secondAttribute="trailing" id="nyL-Ph-xdF"/>
|
||||
<constraint firstItem="xUf-TB-ZyV" firstAttribute="leading" secondItem="tkB-rI-I3L" secondAttribute="leading" id="sOd-a5-mm3"/>
|
||||
<constraint firstItem="Qvr-de-co3" firstAttribute="leading" secondItem="tkB-rI-I3L" secondAttribute="leading" id="ugc-Do-YbI"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="tabView" destination="Qvr-de-co3" id="WdB-aV-v0r"/>
|
||||
<outlet property="topStackView" destination="r7W-52-3OS" id="6Fj-HX-MBJ"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="LNG-EP-CNb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="66" y="295"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
||||
166
Stats/Views/MainViewController.swift
Normal file
166
Stats/Views/MainViewController.swift
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user