initialized new menu view

This commit is contained in:
Serhiy Mytrovtsiy
2019-09-02 23:47:08 +02:00
parent dbb400c688
commit 5a0f8ce854
16 changed files with 382 additions and 95 deletions

View File

@@ -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 */,

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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()

View File

@@ -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() {

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View 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

View File

@@ -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>

View 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
}
}
}