mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
rewrited battery widget
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
9A426DB822C2B5EE00C064C4 /* MacAppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A426DB722C2B5EE00C064C4 /* MacAppUpdater.swift */; };
|
||||
9A426DBE22C2BE0000C064C4 /* Updates.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A426DBD22C2BE0000C064C4 /* Updates.storyboard */; };
|
||||
9A493CDF23202B620064570C /* MemoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A493CDE23202B620064570C /* MemoryView.swift */; };
|
||||
9A54EF67232AB81800F7DC20 /* BatteryPercentageWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54EF66232AB81800F7DC20 /* BatteryPercentageWidget.swift */; };
|
||||
9A54EF69232AB82700F7DC20 /* BatteryTimeWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A54EF68232AB82700F7DC20 /* BatteryTimeWidget.swift */; };
|
||||
9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A57A18422A1D26D0033E318 /* MenuBar.swift */; };
|
||||
9A57A19D22A1E3270033E318 /* CPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A57A19C22A1E3270033E318 /* CPU.swift */; };
|
||||
9A58D1B022C150C800405315 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58D1AF22C150C800405315 /* Network.swift */; };
|
||||
@@ -87,6 +89,8 @@
|
||||
9A426DB722C2B5EE00C064C4 /* MacAppUpdater.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacAppUpdater.swift; sourceTree = "<group>"; };
|
||||
9A426DBD22C2BE0000C064C4 /* Updates.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Updates.storyboard; sourceTree = "<group>"; };
|
||||
9A493CDE23202B620064570C /* MemoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryView.swift; sourceTree = "<group>"; };
|
||||
9A54EF66232AB81800F7DC20 /* BatteryPercentageWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryPercentageWidget.swift; sourceTree = "<group>"; };
|
||||
9A54EF68232AB82700F7DC20 /* BatteryTimeWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryTimeWidget.swift; sourceTree = "<group>"; };
|
||||
9A57A18422A1D26D0033E318 /* MenuBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBar.swift; sourceTree = "<group>"; };
|
||||
9A57A19C22A1E3270033E318 /* CPU.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPU.swift; sourceTree = "<group>"; };
|
||||
9A58D1AF22C150C800405315 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
|
||||
@@ -176,6 +180,16 @@
|
||||
path = Stats;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9A54EF65232AB48100F7DC20 /* Battery */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A09C8A122B3D94D0018426F /* BatteryWidget.swift */,
|
||||
9A54EF66232AB81800F7DC20 /* BatteryPercentageWidget.swift */,
|
||||
9A54EF68232AB82700F7DC20 /* BatteryTimeWidget.swift */,
|
||||
);
|
||||
path = Battery;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9A58D1AE22C150B800405315 /* Network */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -226,9 +240,9 @@
|
||||
9A74D59522B440D4004FE1FA /* Widgets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A54EF65232AB48100F7DC20 /* Battery */,
|
||||
9AF0F31922DA923100026AE6 /* Network */,
|
||||
9AF0F31822DA922800026AE6 /* Charts */,
|
||||
9A09C8A122B3D94D0018426F /* BatteryWidget.swift */,
|
||||
9A74D59622B44498004FE1FA /* Mini.swift */,
|
||||
9A79B36922D3BEE600BF1C3A /* Widget.swift */,
|
||||
);
|
||||
@@ -444,6 +458,7 @@
|
||||
9A606B4A2321577400642F51 /* UpdatesViewController.swift in Sources */,
|
||||
9AF0F31D22DA925000026AE6 /* LineChartWithValue.swift in Sources */,
|
||||
9A09C89E22B3A7C90018426F /* Battery.swift in Sources */,
|
||||
9A54EF67232AB81800F7DC20 /* BatteryPercentageWidget.swift in Sources */,
|
||||
9A7B8F6D22A2C3D600DEB352 /* MemoryReader.swift in Sources */,
|
||||
9A79B36C22D3BEF000BF1C3A /* Module.swift in Sources */,
|
||||
9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */,
|
||||
@@ -457,6 +472,7 @@
|
||||
9A1410F9229E721100D29793 /* AppDelegate.swift in Sources */,
|
||||
9AF0F32722DA92DD00026AE6 /* NetworkDotsText.swift in Sources */,
|
||||
9AF0F32922DA92E800026AE6 /* NetworkArrowsText.swift in Sources */,
|
||||
9A54EF69232AB82700F7DC20 /* BatteryTimeWidget.swift in Sources */,
|
||||
9A606B482321025C00642F51 /* BatteryView.swift in Sources */,
|
||||
9AF0F31F22DA925700026AE6 /* BarChart.swift in Sources */,
|
||||
9AF0F31B22DA924000026AE6 /* LineChart.swift in Sources */,
|
||||
|
||||
@@ -47,6 +47,7 @@ class MenuBar {
|
||||
}
|
||||
|
||||
let newView = newViewList.first!.view
|
||||
newView.invalidateIntrinsicContentSize()
|
||||
let oldView = oldViewList.first!
|
||||
|
||||
self.stackView.replaceSubview(oldView, with: newView)
|
||||
|
||||
@@ -20,8 +20,9 @@ class Battery: Module {
|
||||
public var tabInitialized: Bool = false
|
||||
public var tabView: NSTabViewItem = NSTabViewItem()
|
||||
|
||||
public var widgetType: WidgetType = Widgets.Mini
|
||||
public var widgetType: WidgetType = Widgets.Battery
|
||||
public let percentageView: Observable<Bool>
|
||||
public let timeView: Observable<Bool>
|
||||
|
||||
private let defaults = UserDefaults.standard
|
||||
private var submenu: NSMenu = NSMenu()
|
||||
@@ -30,12 +31,14 @@ class Battery: Module {
|
||||
self.available = Observable(self.reader.available)
|
||||
self.active = Observable(defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true)
|
||||
self.percentageView = Observable(defaults.object(forKey: "\(self.name)_percentage") != nil ? defaults.bool(forKey: "\(self.name)_percentage") : false)
|
||||
self.timeView = Observable(defaults.object(forKey: "\(self.name)_time") != nil ? defaults.bool(forKey: "\(self.name)_time") : false)
|
||||
self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Battery
|
||||
}
|
||||
|
||||
func start() {
|
||||
if !self.reader.value.value.isEmpty {
|
||||
let value = self.reader.value!.value
|
||||
(self.view as! Widget).setValue(data: [abs(value.first!)])
|
||||
(self.view as! Widget).setValue(data: [abs(value.first!), value.last!])
|
||||
}
|
||||
if let view = self.view as? BatteryWidget {
|
||||
view.setCharging(value: (self.reader as! BatteryReader).usage.value.ACstatus)
|
||||
@@ -44,7 +47,7 @@ class Battery: Module {
|
||||
self.reader.start()
|
||||
self.reader.value.subscribe(observer: self) { (value, _) in
|
||||
if !value.isEmpty {
|
||||
(self.view as! Widget).setValue(data: [abs(value.first!)])
|
||||
(self.view as! Widget).setValue(data: [abs(value.first!), value.last!])
|
||||
}
|
||||
}
|
||||
(self.reader as! BatteryReader).usage.subscribe(observer: self) { (value, _) in
|
||||
@@ -52,12 +55,6 @@ class Battery: Module {
|
||||
}
|
||||
}
|
||||
|
||||
func initWidget() {
|
||||
self.view = BatteryWidget(frame: NSMakeRect(0, 0, widgetSize.width, widgetSize.height))
|
||||
(self.view as! BatteryWidget).setCharging(value: (self.reader as! BatteryReader).usage.value.ACstatus)
|
||||
(self.view as! BatteryWidget).setPercentage(value: self.percentageView.value)
|
||||
}
|
||||
|
||||
func initMenu(active: Bool) {
|
||||
menu = NSMenuItem(title: name, action: #selector(toggle), keyEquivalent: "")
|
||||
submenu = NSMenu()
|
||||
@@ -70,16 +67,24 @@ class Battery: Module {
|
||||
menu.target = self
|
||||
menu.isEnabled = true
|
||||
|
||||
let percentage = NSMenuItem(title: "Percentage", action: #selector(togglePercentage), keyEquivalent: "")
|
||||
percentage.state = defaults.bool(forKey: "\(self.name)_percentage") ? NSControl.StateValue.on : NSControl.StateValue.off
|
||||
let percentage = NSMenuItem(title: "Percentage", action: #selector(toggleWidget), keyEquivalent: "")
|
||||
percentage.state = self.widgetType == Widgets.BatteryPercentage ? NSControl.StateValue.on : NSControl.StateValue.off
|
||||
percentage.target = self
|
||||
|
||||
let time = NSMenuItem(title: "Time", action: #selector(toggleWidget), keyEquivalent: "")
|
||||
time.state = self.widgetType == Widgets.BatteryTime ? NSControl.StateValue.on : NSControl.StateValue.off
|
||||
time.target = self
|
||||
|
||||
submenu.addItem(percentage)
|
||||
submenu.addItem(time)
|
||||
|
||||
submenu.addItem(NSMenuItem.separator())
|
||||
|
||||
if let view = self.view as? Widget {
|
||||
for widgetMenu in view.menus {
|
||||
submenu.addItem(widgetMenu)
|
||||
}
|
||||
}
|
||||
submenu.addItem(percentage)
|
||||
|
||||
if active {
|
||||
menu.submenu = submenu
|
||||
@@ -102,15 +107,35 @@ class Battery: Module {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func togglePercentage(_ sender: NSMenuItem) {
|
||||
let state = sender.state != NSControl.StateValue.on
|
||||
@objc func toggleWidget(_ sender: NSMenuItem) {
|
||||
var widgetCode: Float = 0.0
|
||||
|
||||
sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on
|
||||
self.defaults.set(state, forKey: "\(self.name)_percentage")
|
||||
self.percentageView << state
|
||||
self.active << false
|
||||
switch sender.title {
|
||||
case "Percentage":
|
||||
widgetCode = Widgets.BatteryPercentage
|
||||
case "Time":
|
||||
widgetCode = Widgets.BatteryTime
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if self.widgetType == widgetCode {
|
||||
widgetCode = Widgets.Battery
|
||||
}
|
||||
|
||||
let state = sender.state == NSControl.StateValue.on
|
||||
for item in self.submenu.items {
|
||||
if item.title == "Percentage" || item.title == "Time" {
|
||||
item.state = NSControl.StateValue.off
|
||||
}
|
||||
}
|
||||
|
||||
sender.state = state ? NSControl.StateValue.off : NSControl.StateValue.on
|
||||
self.defaults.set(widgetCode, forKey: "\(name)_widget")
|
||||
self.widgetType = widgetCode
|
||||
self.initWidget()
|
||||
self.active << true
|
||||
self.initMenu(active: true)
|
||||
menuBar!.updateWidget(name: self.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,15 @@ class BatteryReader: Reader {
|
||||
if powerSource == "Battery Power" {
|
||||
cap = 0 - cap
|
||||
}
|
||||
self.value << [Double(cap)]
|
||||
|
||||
var time = 0
|
||||
if timeToEmpty != 0 && timeToCharged == 0 {
|
||||
time = timeToEmpty
|
||||
} else if timeToEmpty == 0 && timeToCharged != 0 {
|
||||
time = timeToCharged
|
||||
}
|
||||
|
||||
self.value << [Double(cap), Double(time)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,12 @@ extension Module {
|
||||
widget = NetworkArrowsTextView()
|
||||
case Widgets.BarChart:
|
||||
widget = BarChart()
|
||||
case Widgets.Battery:
|
||||
widget = BatteryWidget()
|
||||
case Widgets.BatteryPercentage:
|
||||
widget = BatteryPercentageWidget()
|
||||
case Widgets.BatteryTime:
|
||||
widget = BatteryTimeWidget()
|
||||
default:
|
||||
widget = Mini()
|
||||
}
|
||||
|
||||
59
Stats/Widgets/Battery/BatteryPercentageWidget.swift
Normal file
59
Stats/Widgets/Battery/BatteryPercentageWidget.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// BatteryPercentageWidget.swift
|
||||
// Stats
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 12/09/2019.
|
||||
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class BatteryPercentageWidget: BatteryWidget {
|
||||
private var percentageValue: NSTextField = NSTextField()
|
||||
|
||||
private let percentageLowWidth: CGFloat = 23
|
||||
private let percentageWidth: CGFloat = 30
|
||||
private let percentageFullWidth: CGFloat = 36
|
||||
|
||||
override init(frame: NSRect) {
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: widgetSize.width, height: widgetSize.height))
|
||||
self.drawPercentage()
|
||||
self.update()
|
||||
}
|
||||
|
||||
required init?(coder decoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func drawPercentage() {
|
||||
self.percentageValue = NSTextField(frame: NSMakeRect(0, 0, percentageWidth, self.frame.size.height - 2))
|
||||
percentageValue.isEditable = false
|
||||
percentageValue.isSelectable = false
|
||||
percentageValue.isBezeled = false
|
||||
percentageValue.wantsLayer = true
|
||||
percentageValue.textColor = .labelColor
|
||||
percentageValue.backgroundColor = .controlColor
|
||||
percentageValue.canDrawSubviewsIntoLayer = true
|
||||
percentageValue.alignment = .right
|
||||
percentageValue.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
percentageValue.stringValue = "\(Int(self.value * 100))%"
|
||||
|
||||
self.addSubview(percentageValue)
|
||||
}
|
||||
|
||||
override func update() {
|
||||
if self.value == 0 { return }
|
||||
self.percentageValue.stringValue = "\(Int(self.value * 100))%"
|
||||
|
||||
if self.value == 1 && self.size != self.batterySize + percentageFullWidth {
|
||||
self.changeWidth(width: percentageFullWidth)
|
||||
self.percentageValue.frame.size.width = percentageFullWidth
|
||||
} else if self.value < 0.1 && self.size != self.batterySize + percentageLowWidth {
|
||||
self.changeWidth(width: percentageLowWidth)
|
||||
self.percentageValue.frame.size.width = percentageLowWidth
|
||||
} else if self.size != self.batterySize + percentageWidth {
|
||||
self.changeWidth(width: percentageWidth)
|
||||
self.percentageValue.frame.size.width = percentageWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Stats/Widgets/Battery/BatteryTimeWidget.swift
Normal file
47
Stats/Widgets/Battery/BatteryTimeWidget.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// BatteryTimeWidget.swift
|
||||
// Stats
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 12/09/2019.
|
||||
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class BatteryTimeWidget: BatteryWidget {
|
||||
private var timeValue: NSTextField = NSTextField()
|
||||
private let timeWidth: CGFloat = 60
|
||||
|
||||
override init(frame: NSRect) {
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: widgetSize.width, height: widgetSize.height))
|
||||
self.drawTime()
|
||||
self.changeWidth(width: self.timeWidth)
|
||||
}
|
||||
|
||||
required init?(coder decoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func drawTime() {
|
||||
self.timeValue = NSTextField(frame: NSMakeRect(0, 0, timeWidth, self.frame.size.height - 2))
|
||||
timeValue.isEditable = false
|
||||
timeValue.isSelectable = false
|
||||
timeValue.isBezeled = false
|
||||
timeValue.wantsLayer = true
|
||||
timeValue.textColor = .labelColor
|
||||
timeValue.backgroundColor = .controlColor
|
||||
timeValue.canDrawSubviewsIntoLayer = true
|
||||
timeValue.alignment = .right
|
||||
timeValue.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
timeValue.stringValue = "\(Int(self.value * 100))%"
|
||||
|
||||
self.addSubview(timeValue)
|
||||
}
|
||||
|
||||
override func update() {
|
||||
if self.value == 0 { return }
|
||||
self.timeValue.stringValue = "\(Int(self.value * 100))%"
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
126
Stats/Widgets/Battery/BatteryWidget.swift
Normal file
126
Stats/Widgets/Battery/BatteryWidget.swift
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// BatteryView.swift
|
||||
// Stats
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 14/06/2019.
|
||||
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class BatteryWidget: NSView, Widget {
|
||||
public var activeModule: Observable<Bool> = Observable(false)
|
||||
public var name: String = "Battery"
|
||||
public var shortName: String = "BAT"
|
||||
public var menus: [NSMenuItem] = []
|
||||
public var size: CGFloat = 30
|
||||
public var batterySize: CGFloat = 30
|
||||
|
||||
private let defaults = UserDefaults.standard
|
||||
private var color: Bool = false
|
||||
|
||||
public var value: Double {
|
||||
didSet {
|
||||
self.redraw()
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
public var time: Double = 0
|
||||
public var charging: Bool {
|
||||
didSet {
|
||||
self.redraw()
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
return CGSize(width: self.frame.size.width, height: self.frame.size.height)
|
||||
}
|
||||
|
||||
override init(frame: NSRect) {
|
||||
self.value = 0.0
|
||||
self.charging = false
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: self.size, height: widgetSize.height))
|
||||
self.wantsLayer = true
|
||||
}
|
||||
|
||||
required init?(coder decoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func Init() {
|
||||
self.color = defaults.object(forKey: "\(name)_color") != nil ? defaults.bool(forKey: "\(name)_color") : false
|
||||
self.initMenu()
|
||||
self.redraw()
|
||||
}
|
||||
|
||||
func initMenu() {}
|
||||
func update() {}
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
|
||||
var x: CGFloat = 4.0
|
||||
let w: CGFloat = self.batterySize - (x * 2)
|
||||
let h: CGFloat = 11.0
|
||||
let y: CGFloat = (dirtyRect.size.height - h) / 2
|
||||
let r: CGFloat = 1.0
|
||||
if dirtyRect.size.width != batterySize {
|
||||
x += self.size - batterySize
|
||||
}
|
||||
|
||||
let battery = NSBezierPath(roundedRect: NSRect(x: x-1, y: y, width: w-1, height: h), xRadius: r, yRadius: r)
|
||||
|
||||
let bPX: CGFloat = x+w-2
|
||||
let bPY: CGFloat = (dirtyRect.size.height / 2) - 2
|
||||
let batteryPoint = NSBezierPath(roundedRect: NSRect(x: bPX, y: bPY, width: 2, height: 4), xRadius: r, yRadius: r)
|
||||
if self.charging {
|
||||
NSColor.systemGreen.set()
|
||||
} else {
|
||||
NSColor.labelColor.set()
|
||||
}
|
||||
batteryPoint.lineWidth = 1.1
|
||||
batteryPoint.stroke()
|
||||
batteryPoint.fill()
|
||||
|
||||
let maxWidth = w-4
|
||||
let inner = NSBezierPath(roundedRect: NSRect(x: x+0.5, y: y+1.5, width: maxWidth*CGFloat(self.value), height: h-3), xRadius: 0.5, yRadius: 0.5)
|
||||
self.value.batteryColor(color: self.color).set()
|
||||
inner.lineWidth = 0
|
||||
inner.stroke()
|
||||
inner.close()
|
||||
inner.fill()
|
||||
|
||||
if self.charging {
|
||||
NSColor.systemGreen.set()
|
||||
} else {
|
||||
NSColor.labelColor.set()
|
||||
}
|
||||
battery.lineWidth = 0.8
|
||||
battery.stroke()
|
||||
}
|
||||
|
||||
func redraw() {
|
||||
self.needsDisplay = true
|
||||
setNeedsDisplay(self.frame)
|
||||
}
|
||||
|
||||
func setValue(data: [Double]) {
|
||||
let value: Double = data.first!
|
||||
if self.value != value {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
func setCharging(value: Bool) {
|
||||
if self.charging != value {
|
||||
self.charging = value
|
||||
}
|
||||
}
|
||||
|
||||
func changeWidth(width: CGFloat) {
|
||||
self.size = batterySize + width
|
||||
self.frame.size.width = self.size
|
||||
menuBar?.updateWidget(name: self.name)
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
//
|
||||
// BatteryView.swift
|
||||
// Stats
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 14/06/2019.
|
||||
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class BatteryWidget: NSView, Widget {
|
||||
var activeModule: Observable<Bool> = Observable(false)
|
||||
var size: CGFloat = widgetSize.width
|
||||
var name: String = "Battery"
|
||||
var shortName: String = "BAT"
|
||||
var menus: [NSMenuItem] = []
|
||||
let defaults = UserDefaults.standard
|
||||
|
||||
let batteryWidth: CGFloat = 32
|
||||
let percentageWidth: CGFloat = 30
|
||||
let percentageWidthFull: CGFloat = 37
|
||||
|
||||
var color: Bool = false
|
||||
var value: Double {
|
||||
didSet {
|
||||
self.redraw()
|
||||
}
|
||||
}
|
||||
var charging: Bool {
|
||||
didSet {
|
||||
self.redraw()
|
||||
}
|
||||
}
|
||||
var percentage: Bool {
|
||||
didSet {
|
||||
self.redraw()
|
||||
}
|
||||
}
|
||||
|
||||
var percentageValue: NSTextField = NSTextField()
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
return CGSize(width: self.frame.size.width, height: self.frame.size.height)
|
||||
}
|
||||
|
||||
override init(frame: NSRect) {
|
||||
self.value = 0.0
|
||||
self.charging = false
|
||||
self.percentage = false
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: self.size, height: widgetSize.height))
|
||||
self.wantsLayer = true
|
||||
self.percentageView()
|
||||
}
|
||||
|
||||
required init?(coder decoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func Init() {
|
||||
self.color = defaults.object(forKey: "\(name)_color") != nil ? defaults.bool(forKey: "\(name)_color") : false
|
||||
self.initMenu()
|
||||
self.redraw()
|
||||
}
|
||||
|
||||
func initMenu() {
|
||||
let color = NSMenuItem(title: "Color", action: #selector(toggleColor), keyEquivalent: "")
|
||||
color.state = self.color ? NSControl.StateValue.on : NSControl.StateValue.off
|
||||
color.target = self
|
||||
|
||||
self.menus.append(color)
|
||||
}
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
|
||||
var x: CGFloat = 4.0
|
||||
var w: CGFloat = dirtyRect.size.width - (x * 2)
|
||||
let h: CGFloat = 11.0
|
||||
let y: CGFloat = (dirtyRect.size.height - h) / 2
|
||||
let r: CGFloat = 1.0
|
||||
if self.percentage {
|
||||
w = batteryWidth - (x * 2)
|
||||
if self.percentage && self.value == 1 {
|
||||
x = percentageWidthFull + x
|
||||
} else {
|
||||
x = percentageWidth + x
|
||||
}
|
||||
}
|
||||
|
||||
let battery = NSBezierPath(roundedRect: NSRect(x: x-1, y: y, width: w-1, height: h), xRadius: r, yRadius: r)
|
||||
|
||||
let bPX: CGFloat = x+w-2
|
||||
let bPY: CGFloat = (dirtyRect.size.height / 2) - 2
|
||||
let batteryPoint = NSBezierPath(roundedRect: NSRect(x: bPX, y: bPY, width: 2, height: 4), xRadius: r, yRadius: r)
|
||||
if self.charging {
|
||||
NSColor.systemGreen.set()
|
||||
} else {
|
||||
NSColor.labelColor.set()
|
||||
}
|
||||
batteryPoint.lineWidth = 1.1
|
||||
batteryPoint.stroke()
|
||||
batteryPoint.fill()
|
||||
|
||||
let maxWidth = w-4
|
||||
let inner = NSBezierPath(roundedRect: NSRect(x: x+0.5, y: y+1.5, width: maxWidth*CGFloat(self.value), height: h-3), xRadius: 0.5, yRadius: 0.5)
|
||||
self.value.batteryColor(color: self.color).set()
|
||||
inner.lineWidth = 0
|
||||
inner.stroke()
|
||||
inner.close()
|
||||
inner.fill()
|
||||
|
||||
if self.charging {
|
||||
NSColor.systemGreen.set()
|
||||
} else {
|
||||
NSColor.labelColor.set()
|
||||
}
|
||||
battery.lineWidth = 0.8
|
||||
battery.stroke()
|
||||
}
|
||||
|
||||
func percentageView() {
|
||||
if self.percentage {
|
||||
var width = percentageWidth
|
||||
if self.value == 1 {
|
||||
width = percentageWidthFull
|
||||
}
|
||||
|
||||
percentageValue = NSTextField(frame: NSMakeRect(0, 0, width, self.frame.size.height - 2))
|
||||
percentageValue.isEditable = false
|
||||
percentageValue.isSelectable = false
|
||||
percentageValue.isBezeled = false
|
||||
percentageValue.wantsLayer = true
|
||||
percentageValue.textColor = .labelColor
|
||||
percentageValue.backgroundColor = .controlColor
|
||||
percentageValue.canDrawSubviewsIntoLayer = true
|
||||
percentageValue.alignment = .right
|
||||
percentageValue.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
percentageValue.stringValue = "\(Int(self.value * 100))%"
|
||||
|
||||
self.addSubview(percentageValue)
|
||||
self.frame = CGRect(x: 0, y: 0, width: batteryWidth + width, height: self.frame.size.height)
|
||||
} else {
|
||||
for subview in self.subviews {
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
self.addSubview(NSView())
|
||||
self.frame = CGRect(x: 0, y: 0, width: batteryWidth, height: self.frame.size.height)
|
||||
}
|
||||
}
|
||||
|
||||
func redraw() {
|
||||
self.needsDisplay = true
|
||||
setNeedsDisplay(self.frame)
|
||||
}
|
||||
|
||||
func setValue(data: [Double]) {
|
||||
let value: Double = data.first!
|
||||
if self.value != value {
|
||||
self.value = value
|
||||
|
||||
if percentage {
|
||||
self.percentageValue.stringValue = "\(Int(self.value * 100))%"
|
||||
if self.value == 1 && self.frame.size.width != batteryWidth + percentageWidthFull {
|
||||
self.percentageValue.removeFromSuperview()
|
||||
self.percentageView()
|
||||
self.redraw()
|
||||
menuBar?.updateWidget(name: self.name)
|
||||
} else if self.value != 1 && self.frame.size.width != batteryWidth + percentageWidth {
|
||||
self.percentageValue.removeFromSuperview()
|
||||
self.percentageView()
|
||||
self.redraw()
|
||||
menuBar?.updateWidget(name: self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setCharging(value: Bool) {
|
||||
if self.charging != value {
|
||||
self.charging = value
|
||||
}
|
||||
}
|
||||
|
||||
func setPercentage(value: Bool) {
|
||||
if self.percentage != value {
|
||||
self.percentage = value
|
||||
self.percentageView()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func toggleColor(_ sender: NSMenuItem) {
|
||||
sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on
|
||||
self.defaults.set(sender.state == NSControl.StateValue.on, forKey: "\(name)_color")
|
||||
self.color = sender.state == NSControl.StateValue.on
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,10 @@ struct Widgets {
|
||||
static let NetworkChart: WidgetType = 2.5
|
||||
|
||||
static let BarChart: WidgetType = 3.0
|
||||
|
||||
static let Battery: WidgetType = 4.0
|
||||
static let BatteryPercentage: WidgetType = 4.1
|
||||
static let BatteryTime: WidgetType = 4.2
|
||||
}
|
||||
|
||||
struct WidgetSize {
|
||||
|
||||
Reference in New Issue
Block a user