DockProgress/Sources/DockProgress/util.swift

140 lines
2.8 KiB
Swift

import Cocoa
/**
Convenience function for initializing an object and modifying its properties
```
let label = with(NSTextField()) {
$0.stringValue = "Foo"
$0.textColor = .systemBlue
view.addSubview($0)
}
```
*/
@discardableResult
func with<T>(_ item: T, update: (inout T) throws -> Void) rethrows -> T {
var this = item
try update(&this)
return this
}
extension NSBezierPath {
static func progressCircle(radius: Double, center: CGPoint) -> NSBezierPath {
let startAngle: CGFloat = 90
let path = NSBezierPath()
path.appendArc(
withCenter: center,
radius: CGFloat(radius),
startAngle: startAngle,
endAngle: startAngle - 360,
clockwise: true
)
return path
}
}
final class ProgressCircleShapeLayer: CAShapeLayer {
convenience init(radius: Double, center: CGPoint) {
self.init()
fillColor = nil
lineCap = .round
position = center
strokeEnd = 0
let cgPath = NSBezierPath.progressCircle(radius: radius, center: center).cgPath
path = cgPath
bounds = cgPath.boundingBox
}
var progress: Double {
get {
return Double(strokeEnd)
}
set {
strokeEnd = CGFloat(newValue)
}
}
}
extension NSColor {
func with(alpha: Double) -> NSColor {
return withAlphaComponent(CGFloat(alpha))
}
}
extension NSFont {
static let helveticaNeueBold = NSFont(name: "HelveticaNeue-Bold", size: 0)
}
extension CGRect {
var center: CGPoint {
get {
return CGPoint(x: midX, y: midY)
}
set {
origin = CGPoint(
x: newValue.x - (size.width / 2),
y: newValue.y - (size.height / 2)
)
}
}
}
extension NSBezierPath {
/// UIKit polyfill
var cgPath: CGPath {
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0..<elementCount {
let type = element(at: i, associatedPoints: &points)
switch type {
case .moveTo:
path.move(to: points[0])
case .lineTo:
path.addLine(to: points[0])
case .curveTo:
path.addCurve(to: points[2], control1: points[0], control2: points[1])
case .closePath:
path.closeSubpath()
}
}
return path
}
/// UIKit polyfill
convenience init(roundedRect rect: CGRect, cornerRadius: CGFloat) {
self.init(roundedRect: rect, xRadius: cornerRadius, yRadius: cornerRadius)
}
}
/// Fixes the vertical alignment issue of the `CATextLayer` class.
final class VerticallyCenteredTextLayer: CATextLayer {
convenience init(frame rect: CGRect, center: CGPoint) {
self.init()
frame = rect
frame.center = center
contentsScale = NSScreen.main?.backingScaleFactor ?? 2
}
// From https://stackoverflow.com/a/44055040/6863743
override func draw(in context: CGContext) {
let height = bounds.size.height
let deltaY = ((height - fontSize) / 2 - fontSize / 10) * -1
context.saveGState()
context.translateBy(x: 0, y: deltaY)
super.draw(in: context)
context.restoreGState()
}
}