Merge pull request #7 from exelban/dev

v1.2.2
This commit is contained in:
Serhiy Mytrovtsiy
2019-07-05 08:49:28 +02:00
committed by GitHub
16 changed files with 262 additions and 114 deletions

87
Makefile Normal file
View File

@@ -0,0 +1,87 @@
APP = Stats
BUNDLE_ID = eu.exelban.Stats
ITC_USERNAME = $(AC_USERNAME)
ITC_PASSWORD = @keychain:AC_PASSWORD
ITC_PROVIDER = $(AC_PROVIDER)
RequestUUID = e6c7b954-d9fa-4c74-8927-ba2172c9526e
BUILD_PATH = $(PWD)/build
ARCHIVE_PATH = $(BUILD_PATH)/$(APP).xcarchive
APP_PATH = "$(BUILD_PATH)/$(APP).app"
ZIP_PATH = "$(BUILD_PATH)/$(APP).zip"
DMG_PATH = $(PWD)/$(APP).dmg
all: clean archive notarize sign build clean
clean:
rm -rf $(BUILD_PATH)
.PHONY: archive
archive: clean
xcodebuild \
-scheme $(APP) \
-destination 'platform=OS X,arch=x86_64' \
-configuration AppStoreDistribution archive \
-archivePath $(ARCHIVE_PATH)
xcodebuild \
-exportArchive \
-exportOptionsPlist "$(PWD)/exportOptions.plist" \
-archivePath $(ARCHIVE_PATH) \
-exportPath $(BUILD_PATH)
ditto -c -k --keepParent $(APP_PATH) $(ZIP_PATH)
.PHONY: notarize
notarize:
xcrun altool \
--notarize-app \
--primary-bundle-id $(BUNDLE_ID)\
-itc_provider $(ITC_PROVIDER) \
-u $(ITC_USERNAME) \
-p $(ITC_PASSWORD) \
--file $(ZIP_PATH)
sleep 380
.PHONY: sign
sign:
xcrun stapler staple $(APP_PATH)
spctl -a -t exec -vvv $(APP_PATH)
.PHONY: build
build: sign
if [ ! -d $(PWD)/create-dmg ]; then \
git clone https://github.com/andreyvit/create-dmg; \
fi
./create-dmg/create-dmg \
--volname $(APP) \
--background "./resources/background.png" \
--window-pos 200 120 \
--window-size 500 320 \
--icon-size 80 \
--icon "Stats.app" 125 175 \
--hide-extension "Stats.app" \
--app-drop-link 375 175 \
$(DMG_PATH) \
$(APP_PATH)
rm -rf ./create-dmg
open $(PWD)
check:
xcrun altool \
--notarization-info $(RequestUUID) \
-itc_provider $(ITC_PROVIDER) \
-u $(ITC_USERNAME) \
-p $(ITC_PASSWORD)
history:
xcrun altool \
--notarization-history 0 \
-itc_provider $(ITC_PROVIDER) \
-u $(ITC_USERNAME) \
-p $(ITC_PASSWORD)

View File

@@ -49,6 +49,16 @@ You can download latest version [here](https://github.com/exelban/stats/releases
## What's new
### v1.2.2
- fully automated build and sign app process
- fixed update and about visibility window in dark mode
- added name of the indicators in the Chart/Chart with value
- added check for new version on start
- removed charts and charts with value to Disk module
- now module submenu is disabled if module is disabled
- fixed bug when network module stop working after turn on/of
- fixed few bugs
### v1.2.1
- added charts and charts with value to Disk module
- fixed bug when Chart with value does not shows

View File

@@ -15,6 +15,9 @@ extension Notification.Name {
let modules: Observable<[Module]> = Observable([CPU(), Memory(), Disk(), Battery(), Network()])
let colors: Observable<Bool> = Observable(true)
let labelForChart: Observable<Bool> = Observable(true)
let updater = macAppUpdater(user: "exelban", repo: "stats")
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@@ -27,7 +30,28 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return
}
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
updatesVC?.window?.center()
updatesVC?.window?.level = .floating
updatesVC!.showWindow(self)
})
}
}
colors << (defaults.object(forKey: "colors") != nil ? defaults.bool(forKey: "colors") : false)
labelForChart << (defaults.object(forKey: "labelForChart") != nil ? defaults.bool(forKey: "labelForChart") : false)
_ = MenuBar(menuBarItem, menuBarButton: menuBarButton)
let launcherAppId = "eu.exelban.StatsLauncher"
@@ -41,6 +65,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self.defaults.set(true, forKey: "runAtLogin")
}
if defaults.object(forKey: "labelForChart") == nil {
self.defaults.set(true, forKey: "labelForChart")
labelForChart << true
}
if isRunning {
DistributedNotificationCenter.default().post(name: .killLauncher, object: Bundle.main.bundleIdentifier!)
}
@@ -73,9 +102,7 @@ class AboutVC: NSViewController {
override func awakeFromNib() {
if self.view.layer != nil {
self.view.window?.backgroundColor = .white
self.view.layer?.backgroundColor = .white
self.view.window?.backgroundColor = .windowBackgroundColor
let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
versionLabel.stringValue = "Version \(versionNumber)"
}
@@ -92,7 +119,6 @@ class UpdatesVC: NSViewController {
@IBOutlet weak var downloadButton: NSButton!
@IBOutlet weak var spinner: NSProgressIndicator!
let updater = macAppUpdater(user: "exelban", repo: "stats")
var url: String?
override func viewDidLoad() {
@@ -133,8 +159,7 @@ class UpdatesVC: NSViewController {
override func awakeFromNib() {
if self.view.layer != nil {
self.view.window?.backgroundColor = .white
self.view.layer?.backgroundColor = .white
self.view.window?.backgroundColor = .windowBackgroundColor
}
}

View File

@@ -65,6 +65,11 @@ class MenuBar {
colorStatus.target = self
preferencesMenu.addItem(colorStatus)
let chartLabels = NSMenuItem(title: "Label in chart", action: #selector(toggleMenu), keyEquivalent: "")
chartLabels.state = defaults.bool(forKey: "labelForChart") || defaults.object(forKey: "labelForChart") == nil ? NSControl.StateValue.on : NSControl.StateValue.off
chartLabels.target = self
preferencesMenu.addItem(chartLabels)
let runAtLogin = NSMenuItem(title: "Run 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
@@ -115,6 +120,10 @@ class MenuBar {
self.defaults.set(status, forKey: "colors")
colors << status
return
case "Label in chart":
self.defaults.set(status, forKey: "labelForChart")
labelForChart << status
return
default: break
}
}

View File

@@ -27,6 +27,15 @@ class CPU: Module {
self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Mini
initMenu()
initWidget()
labelForChart.subscribe(observer: self) { (value, _) in
guard let chartView: Chart = self.view as? Chart else {
return
}
self.active << false
chartView.toggleLabel(value: value)
self.active << true
}
}
func initMenu() {

View File

@@ -29,37 +29,21 @@ class Disk: Module {
self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Mini
self.initMenu()
initWidget()
let widget = Mini(frame: NSMakeRect(0, 0, MODULE_WIDTH, MODULE_HEIGHT))
widget.label = self.shortName
self.view = widget
}
func initMenu() {
menu = NSMenuItem(title: name, action: #selector(toggle), keyEquivalent: "")
submenu = NSMenu()
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
let mini = NSMenuItem(title: "Mini", action: #selector(toggleWidget), keyEquivalent: "")
mini.state = self.widgetType == Widgets.Mini ? NSControl.StateValue.on : NSControl.StateValue.off
mini.target = self
let chart = NSMenuItem(title: "Chart", action: #selector(toggleWidget), keyEquivalent: "")
chart.state = self.widgetType == Widgets.Chart ? NSControl.StateValue.on : NSControl.StateValue.off
chart.target = self
let chartWithValue = NSMenuItem(title: "Chart with value", action: #selector(toggleWidget), keyEquivalent: "")
chartWithValue.state = self.widgetType == Widgets.ChartWithValue ? NSControl.StateValue.on : NSControl.StateValue.off
chartWithValue.target = self
submenu.addItem(mini)
submenu.addItem(chart)
submenu.addItem(chartWithValue)
menu.submenu = submenu
menu.isEnabled = true
}
@objc func toggle(_ sender: NSMenuItem) {
@@ -75,36 +59,4 @@ class Disk: Module {
self.start()
}
}
@objc func toggleWidget(_ sender: NSMenuItem) {
var widgetCode: Float = 0.0
switch sender.title {
case "Mini":
widgetCode = Widgets.Mini
case "Chart":
widgetCode = Widgets.Chart
case "Chart with value":
widgetCode = Widgets.ChartWithValue
default:
break
}
if self.widgetType == widgetCode {
return
}
for item in self.submenu.items {
if item.title == "Mini" || item.title == "Chart" || item.title == "Chart with value" {
item.state = NSControl.StateValue.off
}
}
sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on
self.defaults.set(widgetCode, forKey: "\(name)_widget")
self.widgetType = widgetCode
self.active << false
initWidget()
self.active << true
}
}

View File

@@ -29,6 +29,15 @@ class Memory: Module {
self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Mini
initMenu()
initWidget()
labelForChart.subscribe(observer: self) { (value, _) in
guard let chartView: Chart = self.view as? Chart else {
return
}
self.active << false
chartView.toggleLabel(value: value)
self.active << true
}
}
func initMenu() {
@@ -69,8 +78,10 @@ class Memory: Module {
self.active << state
if !state {
menu.submenu = nil
self.stop()
} else {
menu.submenu = submenu
self.start()
}
}

View File

@@ -87,8 +87,10 @@ class Network: Module {
self.active << state
if !state {
menu.submenu = nil
self.stop()
} else {
menu.submenu = submenu
self.start()
}
}

View File

@@ -33,33 +33,37 @@ class NetworkReader: Reader {
defer {
self.pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
}
let output = self.pipe.fileHandleForReading.availableData
if output.isEmpty {
return
}
let outputString = String(data: output, encoding: String.Encoding.utf8) ?? ""
let arr = outputString.condenseWhitespace().split(separator: " ")
if !arr.isEmpty && Int64(arr[0]) != nil {
guard let download = Int64(arr[2]), let upload = Int64(arr[5]) else {
return
}
guard let value: Double = Double("\(download).\(upload)") else {
return
}
self.usage << value
}
}
netProcess.launch()
do {
try netProcess.run()
} catch let error {
print(error)
}
}
func stop() {
netProcess.interrupt()
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NSFileHandleDataAvailable, object: nil)
}
func read() {}

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="QiE-cC-2Ak">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="QiE-cC-2Ak">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -85,7 +85,7 @@
</customSpacing>
</stackView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="hXC-tE-nSc">
<rect key="frame" x="-2" y="41" width="256" height="34"/>
<rect key="frame" x="-2" y="41" width="234" height="34"/>
<textFieldCell key="cell" title="Simple macOS system monitor in your menu bar" id="w6j-75-PxU">
<font key="font" metaFont="systemLight" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>

View File

@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.1</string>
<string>1.2.2</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>7</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>

View File

@@ -9,6 +9,10 @@
import Cocoa
class Chart: NSView, Widget {
var labelPadding: CGFloat = 10.0
var labelEnabled: Bool = false
var label: String = ""
var height: CGFloat = 0.0
var points: [Double] {
didSet {
@@ -21,6 +25,11 @@ class Chart: NSView, Widget {
super.init(frame: frame)
self.wantsLayer = true
self.addSubview(NSView())
self.labelEnabled = labelForChart.value
if self.labelEnabled {
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width + labelPadding, height: self.frame.size.height)
}
}
required init?(coder decoder: NSCoder) {
@@ -34,12 +43,19 @@ class Chart: NSView, Widget {
let gradientColor: NSColor = NSColor(red: (26/255.0), green: (126/255.0), blue: (252/255.0), alpha: 0.5)
let context = NSGraphicsContext.current!.cgContext
let xOffset: CGFloat = 4.0
var xOffset: CGFloat = 4.0
if labelEnabled {
xOffset = xOffset + labelPadding
}
let yOffset: CGFloat = 3.0
if height == 0 {
height = self.frame.size.height - CGFloat((yOffset * 2))
}
let xRatio = Double(self.frame.size.width - (xOffset * 2)) / (Double(self.points.count) - 1)
var xRatio = Double(self.frame.size.width - (xOffset * 2)) / (Double(self.points.count) - 1)
if labelEnabled {
xRatio = Double(self.frame.size.width - (xOffset * 2) + labelPadding) / (Double(self.points.count) - 1)
}
let columnXPoint = { (point: Int) -> CGFloat in
return CGFloat((Double(point) * xRatio)) + xOffset
@@ -76,6 +92,30 @@ class Chart: NSView, Widget {
graphPath.lineWidth = 0.5
graphPath.stroke()
if !self.labelEnabled {
return
}
let style = NSMutableParagraphStyle()
style.alignment = .center
let stringAttributes = [
NSAttributedString.Key.font: NSFont.systemFont(ofSize: 7.2, weight: .bold),
NSAttributedString.Key.foregroundColor: NSColor.labelColor,
NSAttributedString.Key.paragraphStyle: style
]
let letterHeight = (self.frame.size.height - (MODULE_MARGIN*2)) / 3
let letterWidth: CGFloat = 10.0
var yMargin = MODULE_MARGIN
for char in self.label.reversed() {
let rect = CGRect(x: MODULE_MARGIN, y: yMargin, width: letterWidth, height: letterHeight)
let str = NSAttributedString.init(string: "\(char)", attributes: stringAttributes)
str.draw(with: rect)
yMargin += letterHeight
}
}
func redraw() {
@@ -97,6 +137,15 @@ class Chart: NSView, Widget {
}
}
}
func toggleLabel(value: Bool) {
labelEnabled = value
if value {
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width + labelPadding, height: self.frame.size.height)
} else {
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width - labelPadding, height: self.frame.size.height)
}
}
}
class ChartWithValue: Chart {
@@ -104,10 +153,12 @@ class ChartWithValue: Chart {
override init(frame: NSRect) {
super.init(frame: frame)
self.wantsLayer = true
valueLabel = NSTextField(frame: NSMakeRect(2, MODULE_HEIGHT - 11, self.frame.size.width, 10))
if labelEnabled {
valueLabel = NSTextField(frame: NSMakeRect(labelPadding + 2, MODULE_HEIGHT - 11, self.frame.size.width, 10))
}
valueLabel.textColor = NSColor.red
valueLabel.isEditable = false
valueLabel.isSelectable = false
@@ -146,4 +197,15 @@ class ChartWithValue: Chart {
}
}
}
override func toggleLabel(value: Bool) {
labelEnabled = value
if value {
valueLabel.frame = NSMakeRect(labelPadding + 2, MODULE_HEIGHT - 11, self.frame.size.width, 10)
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width + labelPadding, height: self.frame.size.height)
} else {
valueLabel.frame = NSMakeRect(2, MODULE_HEIGHT - 11, self.frame.size.width, 10)
self.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width - labelPadding, height: self.frame.size.height)
}
}
}

View File

@@ -141,16 +141,3 @@ extension NSBezierPath {
self.line(to: arrowLine2)
}
}
//extension NSView {
// var backgroundColor: NSColor? {
// get {
// guard let color = layer?.backgroundColor else { return nil }
// return NSColor(cgColor: color)
// }
// set {
// wantsLayer = true
// layer?.backgroundColor = newValue?.cgColor
// }
// }
//}

View File

@@ -13,6 +13,7 @@ protocol Module: class {
var shortName: String { get }
var view: NSView { get set }
var menu: NSMenuItem { get }
var submenu: NSMenu { get }
var active: Observable<Bool> { get }
var available: Observable<Bool> { get }
var reader: Reader { get }
@@ -31,10 +32,14 @@ extension Module {
self.view = widget
break
case Widgets.Chart:
self.view = Chart(frame: NSMakeRect(0, 0, MODULE_WIDTH + 7, MODULE_HEIGHT))
let widget = Chart(frame: NSMakeRect(0, 0, MODULE_WIDTH + 7, MODULE_HEIGHT))
widget.label = self.shortName
self.view = widget
break
case Widgets.ChartWithValue:
self.view = ChartWithValue(frame: NSMakeRect(0, 0, MODULE_WIDTH + 7, MODULE_HEIGHT))
let widget = ChartWithValue(frame: NSMakeRect(0, 0, MODULE_WIDTH + 7, MODULE_HEIGHT))
widget.label = self.shortName
self.view = widget
break
case Widgets.Dots:
self.view = NetworkDotsView(frame: NSMakeRect(0, 0, MODULE_WIDTH, MODULE_HEIGHT))

View File

@@ -1,25 +0,0 @@
#!/bin/sh
if [ ! -d "./create-dmg" ]; then
git clone https://github.com/andreyvit/create-dmg
fi
# xcodebuild -configuration Distribution clean build
# cp -rf $PWD/build/Release/Stats.app ./
# rm -rf echo $PWD/build
./create-dmg/create-dmg \
--volname "Stats" \
--background "./resources/background.png" \
--window-pos 200 120 \
--window-size 500 320 \
--icon-size 80 \
--icon "Stats.app" 125 175 \
--hide-extension "Stats.app" \
--app-drop-link 375 175 \
"Stats.dmg" \
"Stats.app"
rm -rf ./create-dmg
rm -rf Stats.app

10
exportOptions.plist Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>developer-id</string>
<key>signingStyle</key>
<string>automatic</string>
</dict>
</plist>