- add CircleChart

- add a dashboard to CPU popup (move chart below, add circle chart in the dashboard)
This commit is contained in:
Serhiy Mytrovtsiy
2020-08-11 22:13:00 +02:00
parent 9edb29a78b
commit 8cb76cd832
3 changed files with 143 additions and 16 deletions

View File

@@ -18,7 +18,8 @@ internal class Popup: NSView {
private var title: String
private let dashboardHeight: CGFloat = 90
private let detailsHeight: CGFloat = 66 // -26
private let chartHeight: CGFloat = 90
private let detailsHeight: CGFloat = 88
private let processesHeight: CGFloat = 22*5
private var loadField: NSTextField? = nil
@@ -28,6 +29,7 @@ internal class Popup: NSView {
private var userField: NSTextField? = nil
private var idleField: NSTextField? = nil
private var circle: CircleGraphView? = nil
private var chart: LineChartView? = nil
private var ready: Bool = false
@@ -37,9 +39,15 @@ internal class Popup: NSView {
self.store = store
self.title = title
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: dashboardHeight + (Constants.Popup.separatorHeight*2) + detailsHeight + processesHeight))
super.init(frame: NSRect(
x: 0,
y: 0,
width: Constants.Popup.width,
height: dashboardHeight + chartHeight + detailsHeight + processesHeight + (Constants.Popup.separatorHeight*3)
))
initDashboard()
initChart()
initDetails()
initProcesses()
}
@@ -53,33 +61,43 @@ internal class Popup: NSView {
}
private func initDashboard() {
let rightWidth: CGFloat = 110
let view: NSView = NSView(frame: NSRect(x: 0, y: self.frame.height - self.dashboardHeight, width: self.frame.width, height: self.dashboardHeight))
let leftPanel = NSView(frame: NSRect(x: 0, y: 0, width: view.frame.width - rightWidth - Constants.Popup.margins, height: view.frame.height))
let container: NSView = NSView(frame: NSRect(x: 0, y: 20/2, width: view.frame.width, height: self.dashboardHeight-20))
let circle: CircleGraphView = CircleGraphView(frame: NSRect(x: 0, y: 0, width: container.frame.width, height: container.frame.height), segments: [])
self.circle = circle
container.addSubview(circle)
view.addSubview(container)
self.chart = LineChartView(frame: NSRect(x: 4, y: 3, width: leftPanel.frame.width, height: leftPanel.frame.height), num: 120)
leftPanel.addSubview(self.chart!)
self.addSubview(view)
}
private func initChart() {
let y: CGFloat = self.frame.height - self.dashboardHeight - Constants.Popup.separatorHeight
let separator = SeparatorView("History", origin: NSPoint(x: 0, y: y), width: self.frame.width)
self.addSubview(separator)
let rightPanel: NSView = NSView(frame: NSRect(x: view.frame.width - rightWidth, y: 0, width: rightWidth, height: view.frame.height))
self.loadField = addFirstRow(mView: rightPanel, y: ((rightPanel.frame.height - 16)/2)+9, title: "Load:", value: "")
self.temperatureField = addFirstRow(mView: rightPanel, y: ((rightPanel.frame.height - 16)/2)-9, title: "Temperature:", value: "")
let view: NSView = NSView(frame: NSRect(x: 0, y: y - self.chartHeight, width: self.frame.width, height: self.chartHeight))
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.lightGray.withAlphaComponent(0.1).cgColor
view.layer?.cornerRadius = 3
self.chart = LineChartView(frame: NSRect(x: 1, y: 0, width: view.frame.width, height: view.frame.height), num: 120)
view.addSubview(self.chart!)
view.addSubview(leftPanel)
view.addSubview(rightPanel)
self.addSubview(view)
}
private func initDetails() {
let y: CGFloat = self.frame.height - self.dashboardHeight - Constants.Popup.separatorHeight
let y: CGFloat = self.frame.height - self.dashboardHeight - self.chartHeight - (Constants.Popup.separatorHeight*2)
let separator = SeparatorView("Details", origin: NSPoint(x: 0, y: y), width: self.frame.width)
self.addSubview(separator)
let view: NSView = NSView(frame: NSRect(x: 0, y: separator.frame.origin.y - self.detailsHeight, width: self.frame.width, height: self.detailsHeight))
self.systemField = PopupRow(view, n: 2, title: "System:", value: "")
self.userField = PopupRow(view, n: 1, title: "User:", value: "")
self.idleField = PopupRow(view, n: 0, title: "Idle:", value: "")
self.systemField = PopupWithColorRow(view, color: NSColor.systemRed, n: 3, title: "System:", value: "")
self.userField = PopupWithColorRow(view, color: NSColor.systemBlue, n: 2, title: "User:", value: "")
self.idleField = PopupWithColorRow(view, color: NSColor.lightGray.withAlphaComponent(0.5), n: 1, title: "Idle:", value: "")
self.temperatureField = PopupRow(view, n: 0, title: "Temperature:", value: "")
self.addSubview(view)
}
@@ -144,6 +162,11 @@ internal class Popup: NSView {
self.ready = true
}
self.circle?.setValue(value.totalUsage)
self.circle?.setSegments([
circle_segment(value: value.systemLoad, color: NSColor.systemRed),
circle_segment(value: value.userLoad, color: NSColor.systemBlue),
])
self.chart?.addValue(value.totalUsage)
})
}

View File

@@ -20,6 +20,16 @@ public enum chart_t: Int {
}
}
public struct circle_segment {
let value: Double
let color: NSColor
public init(value: Double, color: NSColor) {
self.value = value
self.color = color
}
}
public class LineChartView: NSView {
public var points: [Double]? = nil
public var transparent: Bool = true
@@ -48,7 +58,7 @@ public class LineChartView: NSView {
gradientColor = self.color.withAlphaComponent(0.8)
}
let context = NSGraphicsContext.current!.cgContext
guard let context = NSGraphicsContext.current?.cgContext else { return }
context.setShouldAntialias(true)
let height: CGFloat = self.frame.size.height - self.frame.origin.y - 0.5
let xRatio: CGFloat = self.frame.size.width / CGFloat(self.points!.count)
@@ -98,3 +108,78 @@ public class LineChartView: NSView {
}
}
}
public class CircleGraphView: NSView {
private var value: Double? = nil
private var segments: [circle_segment] = []
public init(frame: NSRect, segments: [circle_segment]) {
self.segments = segments
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func draw(_ rect: CGRect) {
let arcWidth: CGFloat = 7.0
let fullCircle = 2 * CGFloat.pi
var segments = self.segments
let totalAmount = segments.reduce(0) { $0 + $1.value }
if totalAmount < 1 {
segments.append(circle_segment(value: Double(1-totalAmount), color: NSColor.lightGray.withAlphaComponent(0.5)))
}
let centerPoint = CGPoint(x: rect.midX, y: rect.midY)
let radius = (min(rect.width, rect.height) - arcWidth) / 2
guard let context = NSGraphicsContext.current?.cgContext else { return }
context.setShouldAntialias(true)
context.setLineWidth(arcWidth)
context.setLineCap(.butt)
let startAngle: CGFloat = CGFloat.pi/2
var previousAngle = startAngle
for segment in segments.reversed() {
let currentAngle: CGFloat = previousAngle + (CGFloat(segment.value) * fullCircle)
context.setStrokeColor(segment.color.cgColor)
context.addArc(center: centerPoint, radius: radius, startAngle: previousAngle, endAngle: currentAngle, clockwise: false)
context.strokePath()
previousAngle = currentAngle
}
if let value = self.value {
let stringAttributes = [
NSAttributedString.Key.font: NSFont.systemFont(ofSize: 15, weight: .regular),
NSAttributedString.Key.foregroundColor: isDarkMode ? NSColor.white : NSColor.textColor,
NSAttributedString.Key.paragraphStyle: NSMutableParagraphStyle()
]
let percentage = "\(Int(value*100))%"
let width: CGFloat = percentage.widthOfString(usingFont: NSFont.systemFont(ofSize: 15))
let rect = CGRect(x: (self.frame.width-width)/2, y: (self.frame.height-12)/2, width: width, height: 12)
let str = NSAttributedString.init(string: percentage, attributes: stringAttributes)
str.draw(with: rect)
}
}
public func setValue(_ value: Double) {
self.value = value
if self.window?.isVisible ?? true {
self.display()
}
}
public func setSegments(_ segments: [circle_segment]) {
self.segments = segments
if self.window?.isVisible ?? true {
self.display()
}
}
}

View File

@@ -620,6 +620,25 @@ public func PopupRow(_ view: NSView, n: CGFloat, title: String, value: String) -
return valueView
}
public func PopupWithColorRow(_ view: NSView, color: NSColor, n: CGFloat, title: String, value: String) -> ValueField {
let rowView: NSView = NSView(frame: NSRect(x: 0, y: 22*n, width: view.frame.width, height: 22))
let colorView: NSView = NSView(frame: NSRect(x: 2, y: 5, width: 12, height: 12))
colorView.wantsLayer = true
colorView.layer?.backgroundColor = color.cgColor
colorView.layer?.cornerRadius = 2
let labelWidth = title.widthOfString(usingFont: .systemFont(ofSize: 13, weight: .regular)) + 5
let labelView: LabelField = LabelField(frame: NSRect(x: 18, y: (22-15)/2, width: labelWidth, height: 15), title)
let valueView: ValueField = ValueField(frame: NSRect(x: 18 + labelWidth, y: (22-16)/2, width: rowView.frame.width - labelWidth - 18, height: 16), value)
rowView.addSubview(colorView)
rowView.addSubview(labelView)
rowView.addSubview(valueView)
view.addSubview(rowView)
return valueView
}
public extension Array where Element : Equatable {
func allEqual() -> Bool {
if let firstElem = first {