mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: added fixed scale option for Network popup (#1787)
This commit is contained in:
@@ -1487,3 +1487,76 @@ public func restartApp(_ sender: Any, afterDelay seconds: TimeInterval = 0.5) ->
|
||||
NSApp.terminate(sender)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
public class StepperInput: NSStackView, NSTextFieldDelegate {
|
||||
public var callback: ((Int) -> Void) = {_ in }
|
||||
|
||||
private let value: NSTextField = NSTextField()
|
||||
private let stepper: NSStepper = NSStepper()
|
||||
|
||||
private let range: NSRange?
|
||||
|
||||
public init(_ value: Int, range: NSRange? = nil) {
|
||||
self.range = range
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.orientation = .horizontal
|
||||
self.spacing = 2
|
||||
|
||||
self.value.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
self.value.textColor = .textColor
|
||||
self.value.isEditable = true
|
||||
self.value.isSelectable = true
|
||||
self.value.usesSingleLineMode = true
|
||||
self.value.maximumNumberOfLines = 1
|
||||
self.value.focusRingType = .none
|
||||
self.value.delegate = self
|
||||
self.value.stringValue = "\(value)"
|
||||
|
||||
self.stepper.font = NSFont.systemFont(ofSize: 12, weight: .regular)
|
||||
self.stepper.doubleValue = Double(value)/100
|
||||
if let range {
|
||||
self.stepper.minValue = Double(range.lowerBound)/100
|
||||
self.stepper.maxValue = Double(range.upperBound)/100
|
||||
}
|
||||
self.stepper.increment = 0.01
|
||||
self.stepper.valueWraps = false
|
||||
self.stepper.target = self
|
||||
self.stepper.action = #selector(self.onStepperChange)
|
||||
|
||||
self.addArrangedSubview(self.value)
|
||||
self.addArrangedSubview(self.stepper)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func controlTextDidChange(_ obj: Notification) {
|
||||
guard let field = obj.object as? NSTextField else { return }
|
||||
let filtered = field.stringValue.filter{"0123456789".contains($0)}
|
||||
if filtered != field.stringValue {
|
||||
field.stringValue = filtered
|
||||
}
|
||||
guard var v = Int(field.stringValue) else { return }
|
||||
|
||||
if let range = self.range {
|
||||
if v > range.upperBound {
|
||||
field.stringValue = "\(range.upperBound)"
|
||||
v = range.upperBound
|
||||
} else if v < range.lowerBound {
|
||||
field.stringValue = "\(range.lowerBound)"
|
||||
v = range.lowerBound
|
||||
}
|
||||
}
|
||||
|
||||
self.callback(v)
|
||||
}
|
||||
|
||||
@objc private func onStepperChange(_ sender: NSStepper) {
|
||||
let value = Int(sender.doubleValue*100)
|
||||
self.value.stringValue = "\(value)"
|
||||
self.callback(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,3 +337,44 @@ public var LineChartHistory: [KeyValue_p] = [
|
||||
KeyValue_t(key: "300", value: "5 minutes"),
|
||||
KeyValue_t(key: "600", value: "10 minutes")
|
||||
]
|
||||
|
||||
public struct SizeUnit: KeyValue_p, Equatable {
|
||||
public let key: String
|
||||
public let value: String
|
||||
public var additional: Any?
|
||||
|
||||
public static func == (lhs: SizeUnit, rhs: SizeUnit) -> Bool {
|
||||
return lhs.key == rhs.key
|
||||
}
|
||||
}
|
||||
|
||||
extension SizeUnit: CaseIterable {
|
||||
public static var byte: SizeUnit { return SizeUnit(key: "byte", value: "Bytes") }
|
||||
public static var KB: SizeUnit { return SizeUnit(key: "KB", value: "KB") }
|
||||
public static var MB: SizeUnit { return SizeUnit(key: "MB", value: "MB") }
|
||||
public static var GB: SizeUnit { return SizeUnit(key: "GB", value: "GB") }
|
||||
public static var TB: SizeUnit { return SizeUnit(key: "TB", value: "TB") }
|
||||
|
||||
public static var allCases: [SizeUnit] {
|
||||
[.byte, .KB, .MB, .GB, .TB]
|
||||
}
|
||||
|
||||
public static func fromString(_ key: String, defaultValue: SizeUnit = .byte) -> SizeUnit {
|
||||
return SizeUnit.allCases.first{ $0.key == key } ?? defaultValue
|
||||
}
|
||||
|
||||
public func toBytes(_ value: Int) -> Int {
|
||||
switch self {
|
||||
case .KB:
|
||||
return value * 1_024
|
||||
case .MB:
|
||||
return value * 1_024 * 1_024
|
||||
case .GB:
|
||||
return value * 1_024 * 1_024 * 1_024
|
||||
case .TB:
|
||||
return value * 1_024 * 1_024 * 1_024 * 1_024
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ internal class Popup: PopupWrapper {
|
||||
private var chart: NetworkChartView? = nil
|
||||
private var reverseOrderState: Bool = false
|
||||
private var chartScale: Scale = .none
|
||||
private var chartFixedScale: Int = 12
|
||||
private var chartFixedScaleSize: SizeUnit = .MB
|
||||
private var chartPrefSection: PreferencesSection? = nil
|
||||
private var connectivityChart: GridChartView? = nil
|
||||
private var processes: ProcessesView? = nil
|
||||
private var sliderView: NSView? = nil
|
||||
@@ -108,6 +111,8 @@ internal class Popup: PopupWrapper {
|
||||
self.uploadColorState = Color.fromString(Store.shared.string(key: "\(self.title)_uploadColor", defaultValue: self.uploadColorState.key))
|
||||
self.reverseOrderState = Store.shared.bool(key: "\(self.title)_reverseOrder", defaultValue: self.reverseOrderState)
|
||||
self.chartScale = Scale.fromString(Store.shared.string(key: "\(self.title)_chartScale", defaultValue: self.chartScale.key))
|
||||
self.chartFixedScale = Store.shared.int(key: "\(self.title)_chartFixedScale", defaultValue: self.chartFixedScale)
|
||||
self.chartFixedScaleSize = SizeUnit.fromString(Store.shared.string(key: "\(self.title)_chartFixedScaleSize", defaultValue: self.chartFixedScaleSize.key))
|
||||
self.publicIPState = Store.shared.bool(key: "\(self.title)_publicIP", defaultValue: self.publicIPState)
|
||||
|
||||
self.spacing = 0
|
||||
@@ -183,7 +188,9 @@ internal class Popup: PopupWrapper {
|
||||
|
||||
let chart = NetworkChartView(
|
||||
frame: NSRect(x: 0, y: 1, width: container.frame.width, height: container.frame.height - 2),
|
||||
num: 120, reversedOrder: self.reverseOrderState, outColor: self.uploadColor, inColor: self.downloadColor, scale: self.chartScale
|
||||
num: 120, reversedOrder: self.reverseOrderState, outColor: self.uploadColor, inColor: self.downloadColor,
|
||||
scale: self.chartScale,
|
||||
fixedScale: Double(self.chartFixedScaleSize.toBytes(self.chartFixedScale))
|
||||
)
|
||||
chart.base = self.base
|
||||
container.addSubview(chart)
|
||||
@@ -523,13 +530,31 @@ internal class Popup: PopupWrapper {
|
||||
))
|
||||
]))
|
||||
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
self.chartPrefSection = PreferencesSection([
|
||||
PreferencesRow(localizedString("Main chart scaling"), component: selectView(
|
||||
action: #selector(self.toggleChartScale),
|
||||
items: Scale.allCases.filter({ $0 != .fixed }),
|
||||
items: Scale.allCases,
|
||||
selected: self.chartScale.key
|
||||
))
|
||||
]))
|
||||
)),
|
||||
PreferencesRow(localizedString("Scale value"), component: {
|
||||
let view: NSStackView = NSStackView()
|
||||
view.orientation = .horizontal
|
||||
view.spacing = 2
|
||||
let valueField = StepperInput(self.chartFixedScale, range: NSRange(location: 1, length: 1023))
|
||||
valueField.callback = self.toggleFixedScale
|
||||
valueField.widthAnchor.constraint(equalToConstant: 80).isActive = true
|
||||
view.addArrangedSubview(NSView())
|
||||
view.addArrangedSubview(valueField)
|
||||
view.addArrangedSubview(selectView(
|
||||
action: #selector(self.toggleUploadScaleSize),
|
||||
items: SizeUnit.allCases,
|
||||
selected: self.chartFixedScaleSize.key
|
||||
))
|
||||
return view
|
||||
}())
|
||||
])
|
||||
view.addArrangedSubview(self.chartPrefSection!)
|
||||
self.chartPrefSection?.toggleVisibility(1, newState: self.chartScale == .fixed)
|
||||
|
||||
view.addArrangedSubview(PreferencesSection([
|
||||
PreferencesRow(localizedString("Public IP"), component: switchView(
|
||||
@@ -579,7 +604,8 @@ internal class Popup: PopupWrapper {
|
||||
guard let key = sender.representedObject as? String,
|
||||
let value = Scale.allCases.first(where: { $0.key == key }) else { return }
|
||||
self.chartScale = value
|
||||
self.chart?.setScale(self.chartScale)
|
||||
self.chart?.setScale(self.chartScale, Double(self.chartFixedScaleSize.toBytes(self.chartFixedScale)))
|
||||
self.chartPrefSection?.toggleVisibility(1, newState: self.chartScale == .fixed)
|
||||
Store.shared.set(key: "\(self.title)_chartScale", value: key)
|
||||
self.display()
|
||||
}
|
||||
@@ -596,6 +622,18 @@ internal class Popup: PopupWrapper {
|
||||
self.recalculateHeight()
|
||||
})
|
||||
}
|
||||
@objc private func toggleFixedScale(_ newValue: Int) {
|
||||
self.chart?.setScale(self.chartScale, Double(self.chartFixedScaleSize.toBytes(newValue)))
|
||||
Store.shared.set(key: "\(self.title)_chartFixedScale", value: newValue)
|
||||
}
|
||||
@objc private func toggleUploadScaleSize(_ sender: NSMenuItem) {
|
||||
guard let key = sender.representedObject as? String,
|
||||
let value = SizeUnit.allCases.first(where: { $0.key == key }) else { return }
|
||||
self.chartFixedScaleSize = value
|
||||
self.chart?.setScale(self.chartScale, Double(self.chartFixedScaleSize.toBytes(self.chartFixedScale)))
|
||||
Store.shared.set(key: "\(self.title)_chartFixedScaleSize", value: key)
|
||||
self.display()
|
||||
}
|
||||
|
||||
// MARK: - helpers
|
||||
|
||||
|
||||
Reference in New Issue
Block a user