mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: initialized battery details widget (#1293)
This commit is contained in:
@@ -35,6 +35,7 @@ identifier_name:
|
||||
- AppleSiliconSensorsList
|
||||
- FanValues
|
||||
- CombinedModulesSpacings
|
||||
- BatteryInfo
|
||||
|
||||
line_length: 200
|
||||
|
||||
|
||||
@@ -457,3 +457,165 @@ public class BatteryWidget: WidgetWrapper {
|
||||
self.display()
|
||||
}
|
||||
}
|
||||
|
||||
public class BatteryDetailsWidget: WidgetWrapper {
|
||||
private var mode: String = "percentage"
|
||||
private var timeFormat: String = "short"
|
||||
|
||||
private var percentage: Double? = nil
|
||||
private var time: Int = 0
|
||||
|
||||
public init(title: String, config: NSDictionary?, preview: Bool = false) {
|
||||
super.init(.batteryDetails, title: title, frame: CGRect(
|
||||
x: Constants.Widget.margin.x,
|
||||
y: Constants.Widget.margin.y,
|
||||
width: 20 + (2*Constants.Widget.margin.x),
|
||||
height: Constants.Widget.height - (2*Constants.Widget.margin.y)
|
||||
))
|
||||
|
||||
self.canDrawConcurrently = true
|
||||
|
||||
if preview {
|
||||
self.percentage = 0.72
|
||||
self.time = 415
|
||||
self.mode = "percentageAndTime"
|
||||
} else {
|
||||
self.mode = Store.shared.string(key: "\(self.title)_\(self.type.rawValue)_mode", defaultValue: self.mode)
|
||||
self.timeFormat = Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: self.timeFormat)
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
|
||||
var width: CGFloat = Constants.Widget.margin.x*2
|
||||
let x: CGFloat = Constants.Widget.margin.x
|
||||
let isShortTimeFormat: Bool = self.timeFormat == "short"
|
||||
|
||||
switch self.mode {
|
||||
case "percentage":
|
||||
var value = "n/a"
|
||||
if let percentage = self.percentage {
|
||||
value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
|
||||
}
|
||||
width = self.drawOneRow(value: value, x: x).rounded(.up)
|
||||
case "time":
|
||||
width = self.drawOneRow(
|
||||
value: Double(self.time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
|
||||
x: x
|
||||
).rounded(.up)
|
||||
case "percentageAndTime":
|
||||
var value = "n/a"
|
||||
if let percentage = self.percentage {
|
||||
value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
|
||||
}
|
||||
width = self.drawTwoRows(
|
||||
first: value,
|
||||
second: Double(self.time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
|
||||
x: x
|
||||
).rounded(.up)
|
||||
case "timeAndPercentage":
|
||||
var value = "n/a"
|
||||
if let percentage = self.percentage {
|
||||
value = "\(Int((percentage.rounded(toPlaces: 2)) * 100))%"
|
||||
}
|
||||
width = self.drawTwoRows(
|
||||
first: Double(self.time*60).printSecondsToHoursMinutesSeconds(short: isShortTimeFormat),
|
||||
second: value,
|
||||
x: x
|
||||
).rounded(.up)
|
||||
default: break
|
||||
}
|
||||
|
||||
self.setWidth(width)
|
||||
}
|
||||
|
||||
private func drawOneRow(value: String, x: CGFloat) -> CGFloat {
|
||||
let attributes = [
|
||||
NSAttributedString.Key.font: NSFont.systemFont(ofSize: 12, weight: .regular),
|
||||
NSAttributedString.Key.foregroundColor: isDarkMode ? NSColor.white : NSColor.textColor,
|
||||
NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
|
||||
]
|
||||
|
||||
let rowWidth = value.widthOfString(usingFont: .systemFont(ofSize: 12, weight: .regular))
|
||||
let rect = CGRect(x: x, y: (Constants.Widget.height-12)/2, width: rowWidth, height: 12)
|
||||
let str = NSAttributedString.init(string: value, attributes: attributes)
|
||||
str.draw(with: rect)
|
||||
|
||||
return rowWidth
|
||||
}
|
||||
|
||||
private func drawTwoRows(first: String, second: String, x: CGFloat) -> CGFloat {
|
||||
let style = NSMutableParagraphStyle()
|
||||
style.alignment = .center
|
||||
let attributes = [
|
||||
NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9, weight: .regular),
|
||||
NSAttributedString.Key.foregroundColor: NSColor.textColor,
|
||||
NSAttributedString.Key.paragraphStyle: style
|
||||
]
|
||||
let rowHeight: CGFloat = self.frame.height / 2
|
||||
|
||||
let rowWidth = max(
|
||||
first.widthOfString(usingFont: .systemFont(ofSize: 9, weight: .regular)),
|
||||
second.widthOfString(usingFont: .systemFont(ofSize: 9, weight: .regular))
|
||||
)
|
||||
|
||||
var str = NSAttributedString.init(string: first, attributes: attributes)
|
||||
str.draw(with: CGRect(x: x, y: rowHeight+1, width: rowWidth, height: rowHeight))
|
||||
|
||||
str = NSAttributedString.init(string: second, attributes: attributes)
|
||||
str.draw(with: CGRect(x: x, y: 1, width: rowWidth, height: rowHeight))
|
||||
|
||||
return rowWidth
|
||||
}
|
||||
|
||||
public func setValue(percentage: Double? = nil, time: Int? = nil) {
|
||||
var updated: Bool = false
|
||||
let timeFormat: String = Store.shared.string(key: "\(self.title)_timeFormat", defaultValue: self.timeFormat)
|
||||
|
||||
if self.percentage != percentage {
|
||||
self.percentage = percentage
|
||||
updated = true
|
||||
}
|
||||
if let time = time, self.time != time {
|
||||
self.time = time
|
||||
updated = true
|
||||
}
|
||||
if self.timeFormat != timeFormat {
|
||||
self.timeFormat = timeFormat
|
||||
updated = true
|
||||
}
|
||||
|
||||
if updated {
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.display()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Settings
|
||||
|
||||
public override func settings() -> NSView {
|
||||
let view = SettingsContainerView()
|
||||
|
||||
view.addArrangedSubview(selectSettingsRow(
|
||||
title: localizedString("Mode"),
|
||||
action: #selector(self.toggleMode),
|
||||
items: BatteryInfo,
|
||||
selected: self.mode
|
||||
))
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@objc private func toggleMode(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String else { return }
|
||||
self.mode = key
|
||||
Store.shared.set(key: "\(self.title)_\(self.type.rawValue)_mode", value: key)
|
||||
self.display()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public enum widget_t: String {
|
||||
case networkChart = "network_chart"
|
||||
case speed = "speed"
|
||||
case battery = "battery"
|
||||
case batteryDetails = "battery_details"
|
||||
case sensors = "sensors"
|
||||
case memory = "memory"
|
||||
case label = "label"
|
||||
@@ -57,6 +58,9 @@ public enum widget_t: String {
|
||||
case .battery:
|
||||
preview = BatteryWidget(title: module, config: widgetConfig, preview: true)
|
||||
item = BatteryWidget(title: module, config: widgetConfig, preview: false)
|
||||
case .batteryDetails:
|
||||
preview = BatteryDetailsWidget(title: module, config: widgetConfig, preview: true)
|
||||
item = BatteryDetailsWidget(title: module, config: widgetConfig, preview: false)
|
||||
case .sensors:
|
||||
preview = SensorsWidget(title: module, config: widgetConfig, preview: true)
|
||||
item = SensorsWidget(title: module, config: widgetConfig, preview: false)
|
||||
@@ -128,6 +132,7 @@ public enum widget_t: String {
|
||||
case .networkChart: return localizedString("Network chart widget")
|
||||
case .speed: return localizedString("Speed widget")
|
||||
case .battery: return localizedString("Battery widget")
|
||||
case .batteryDetails: return localizedString("Battery details widget")
|
||||
case .sensors: return localizedString("Text widget")
|
||||
case .memory: return localizedString("Memory widget")
|
||||
case .label: return localizedString("Label widget")
|
||||
|
||||
@@ -101,6 +101,13 @@ public let BatteryAdditionals: [KeyValue_t] = [
|
||||
KeyValue_t(key: "timeAndPercentage", value: "Time and percentage")
|
||||
]
|
||||
|
||||
public let BatteryInfo: [KeyValue_t] = [
|
||||
KeyValue_t(key: "percentage", value: "Percentage"),
|
||||
KeyValue_t(key: "time", value: "Time"),
|
||||
KeyValue_t(key: "percentageAndTime", value: "Percentage and time"),
|
||||
KeyValue_t(key: "timeAndPercentage", value: "Time and percentage")
|
||||
]
|
||||
|
||||
public let ShortLong: [KeyValue_t] = [
|
||||
KeyValue_t(key: "short", value: "Short"),
|
||||
KeyValue_t(key: "long", value: "Long")
|
||||
|
||||
@@ -69,6 +69,13 @@
|
||||
<key>Order</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>battery_details</key>
|
||||
<dict>
|
||||
<key>Default</key>
|
||||
<false/>
|
||||
<key>Order</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -141,6 +141,11 @@ public class Battery: Module {
|
||||
optimizedCharging: value.optimizedChargingEngaged,
|
||||
time: value.timeToEmpty == 0 && value.timeToCharge != 0 ? value.timeToCharge : value.timeToEmpty
|
||||
)
|
||||
case let widget as BatteryDetailsWidget:
|
||||
widget.setValue(
|
||||
percentage: value.level,
|
||||
time: value.timeToEmpty == 0 && value.timeToCharge != 0 ? value.timeToCharge : value.timeToEmpty
|
||||
)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user