A collection of user interface components and drawing routines for building tasteful apps using AppKit.
appkit swift aqua ui mac

Working on window bottom bar

+79 -34
+18 -19
Sources/AquaKit/Controls and Views/AquaResizeHandle.swift Sources/AquaKit/Controls and Views/AquaSplitViewResizeHandle.swift
··· 1 1 import AppKit 2 2 3 - open class AquaResizeHandle: NSControl { 3 + open class AquaSplitViewResizeHandle: NSControl { 4 4 public struct Drag { 5 5 public var initialLocation: NSPoint = .zero 6 - public var delta: CGFloat = 0 6 + public var finalLocation: NSPoint = .zero 7 7 } 8 8 9 9 override open var mouseDownCanMoveWindow: Bool { false } ··· 34 34 35 35 public required init?(coder: NSCoder) { 36 36 fatalError("init(coder:) has not been implemented") 37 - } 38 - 39 - override open func draw(_ dirtyRect: NSRect) { 40 - let path = NSBezierPath() 41 - let bounds = bounds.insetBy(dx: 4, dy: 0) 42 - path.move(to: NSPoint(x: bounds.minX, y: bounds.minY)) 43 - path.line(to: NSPoint(x: bounds.minX, y: bounds.maxY)) 44 - path.move(to: NSPoint(x: bounds.midX, y: bounds.minY)) 45 - path.line(to: NSPoint(x: bounds.midX, y: bounds.maxY)) 46 - path.move(to: NSPoint(x: bounds.maxX, y: bounds.minY)) 47 - path.line(to: NSPoint(x: bounds.maxX, y: bounds.maxY)) 48 - path.lineWidth = 1 49 - 50 - NSColor.aquaSeparatorColor.setStroke() 51 - path.stroke() 52 37 } 53 38 54 39 override open func mouseDown(with event: NSEvent) { ··· 57 42 58 43 override open func mouseDragged(with event: NSEvent) { 59 44 guard var drag = activeDrag else { return } 60 - let location = convert(event.locationInWindow, from: nil) 61 - drag.delta = location.x - drag.initialLocation.x 45 + drag.finalLocation = convert(event.locationInWindow, from: nil) 62 46 activeDrag = drag 63 47 sendAction(action, to: target) 64 48 } ··· 69 53 70 54 override open func acceptsFirstMouse(for event: NSEvent?) -> Bool { 71 55 true 56 + } 57 + 58 + override open func draw(_ dirtyRect: NSRect) { 59 + let path = NSBezierPath() 60 + let bounds = bounds.insetBy(dx: 4, dy: 0) 61 + path.move(to: NSPoint(x: bounds.minX, y: bounds.minY)) 62 + path.line(to: NSPoint(x: bounds.minX, y: bounds.maxY)) 63 + path.move(to: NSPoint(x: bounds.midX, y: bounds.minY)) 64 + path.line(to: NSPoint(x: bounds.midX, y: bounds.maxY)) 65 + path.move(to: NSPoint(x: bounds.maxX, y: bounds.minY)) 66 + path.line(to: NSPoint(x: bounds.maxX, y: bounds.maxY)) 67 + path.lineWidth = 1 68 + 69 + NSColor.aquaSeparatorColor.setStroke() 70 + path.stroke() 72 71 } 73 72 }
+3 -2
Sources/AquaKit/Controls and Views/SplitViewResizeCoordinator.swift
··· 9 9 } 10 10 11 11 @MainActor 12 - @objc public func resize(_ sender: AquaResizeHandle) { 12 + @objc public func resize(_ sender: AquaSplitViewResizeHandle) { 13 13 guard let drag = sender.activeDrag else { return } 14 14 let subview = splitView.arrangedSubviews[dividerIndex] 15 15 let currentPosition = splitView.isVertical ? subview.frame.maxX : subview.frame.maxY 16 - splitView.setPosition(currentPosition + drag.delta, ofDividerAt: dividerIndex) 16 + let delta = drag.finalLocation.x - drag.initialLocation.x 17 + splitView.setPosition(currentPosition + delta, ofDividerAt: dividerIndex) 17 18 } 18 19 }
+4
Sources/AquaKit/Scroll Views/AquaScroller.swift
··· 139 139 } else { 140 140 baseColor = NSColor.graphiteColor.highlight(withLevel: 0.5)! 141 141 } 142 + 143 + if effectiveAppearance.isDarkAqua { 144 + baseColor = baseColor.blended(withFraction: 0.3, of: .graphiteColor.shadow(withLevel: 0.8)!)! 145 + } 142 146 143 147 do { 144 148 NSGraphicsContext.saveGraphicsState()
+4 -8
Sources/AquaKit/Source Lists/SourceListBottomBarViewController.swift
··· 1 1 import AppKit 2 2 3 3 open class SourceListBottomBarViewController: NSSplitViewItemAccessoryViewController { 4 - public let resizeHandle = AquaResizeHandle() 4 + public let resizeHandle = AquaSplitViewResizeHandle() 5 5 6 6 public override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) { 7 7 super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) ··· 56 56 gradient?.draw(in: bounds, angle: 270) 57 57 NSGraphicsContext.restoreGraphicsState() 58 58 59 - let borderPath = NSBezierPath() 60 - borderPath.move(to: NSPoint(x: bounds.minX, y: bounds.maxY)) 61 - borderPath.line(to: NSPoint(x: bounds.maxX, y: bounds.maxY)) 62 - borderPath.lineWidth = 1 63 - NSColor.aquaSeparatorColor.setStroke() 64 - borderPath.stroke() 59 + NSColor.aquaSeparatorColor.setFill() 60 + NSBezierPath.fill(NSRect(x: bounds.minX, y: bounds.maxY - 1, width: bounds.width, height: 1)) 65 61 } 66 62 } 67 63 ··· 82 78 ]) 83 79 84 80 automaticallyAppliesContentInsets = false 85 - view.heightAnchor.constraint(equalToConstant: 30).isActive = true 81 + view.heightAnchor.constraint(equalToConstant: AquaWindow.bottomBarThickness).isActive = true 86 82 } 87 83 }
+2 -2
Sources/AquaKit/Windows/AquaTitlebarBackgroundView.swift
··· 10 10 fatalError("init(coder:) has not been implemented") 11 11 } 12 12 13 - private var gradient: NSGradient? { 13 + static var gradient: NSGradient! { 14 14 let color1 = NSColor(light: #colorLiteral(red: 0.9262896776, green: 0.9262896776, blue: 0.9262896776, alpha: 1), dark: .underPageBackgroundColor.highlight(withLevel: 0.3)!.withAlphaComponent(0.2)) 15 15 let color2 = NSColor(light: #colorLiteral(red: 0.8432617784, green: 0.8432616591, blue: 0.8432617784, alpha: 1), dark: .underPageBackgroundColor.withAlphaComponent(0.2)) 16 16 return NSGradient(colors: [color1, color2])! ··· 25 25 } 26 26 27 27 override open func draw(_ dirtyRect: NSRect) { 28 - gradient!.draw(in: bounds, angle: 270) 28 + Self.gradient.draw(in: bounds, angle: 270) 29 29 30 30 NSColor.aquaSeparatorColor.setStroke() 31 31 dividerPath.stroke()
+6 -3
Sources/AquaKit/Windows/AquaTrafficLightButton.swift
··· 65 65 66 66 override open func draw(_ dirtyRect: NSRect) { 67 67 NSGraphicsContext.saveGraphicsState() 68 - 68 + 69 69 var color = kind.baseColor 70 70 if NSColor.controlAccentColor.isGrayscale { 71 71 color = .graphiteColor ··· 120 120 121 121 color.shadow(withLevel: 0.7)!.setStroke() 122 122 ellipse.stroke() 123 - 123 + 124 124 NSGraphicsContext.restoreGraphicsState() 125 125 } 126 126 ··· 163 163 case .zoom: #colorLiteral(red: 0.5023847222, green: 0.7764200568, blue: 0.07384926826, alpha: 1) 164 164 } 165 165 166 - return NSColor(light: unblended, dark: unblended.blended(withFraction: 0.5, of: .black)!) 166 + return NSColor( 167 + light: unblended, 168 + dark: unblended.blended(withFraction: 0.6, of: .graphiteColor.shadow(withLevel: 0.9)!)! 169 + ) 167 170 } 168 171 }
+2
Sources/AquaKit/Windows/AquaWindow.swift
··· 1 1 import AppKit 2 2 3 3 open class AquaWindow: NSWindow { 4 + public static var bottomBarThickness: CGFloat { 30.0 } 5 + 4 6 private var _showsToolbarButton: Bool = false 5 7 override open var showsToolbarButton: Bool { 6 8 get { _showsToolbarButton }
+40
Sources/AquaKit/Windows/AquaWindowBottomBarViewController.swift
··· 1 + import AppKit 2 + 3 + open class AquaWindowBottomBarViewController: ContainerViewController { 4 + class View: NSView { 5 + override init(frame frameRect: NSRect) { 6 + super.init(frame: frameRect) 7 + addSubview(WindowStateSentinelView()) 8 + } 9 + 10 + required init?(coder: NSCoder) { 11 + fatalError("init(coder:) has not been implemented") 12 + } 13 + 14 + private var dividerPath: NSBezierPath { 15 + let dividerPath = NSBezierPath() 16 + dividerPath.move(to: NSPoint(x: bounds.minX, y: bounds.maxY)) 17 + dividerPath.line(to: NSPoint(x: bounds.maxX, y: bounds.maxY)) 18 + dividerPath.lineWidth = 1 19 + return dividerPath 20 + } 21 + 22 + override open func draw(_ dirtyRect: NSRect) { 23 + AquaTitlebarBackgroundView.gradient.draw(in: bounds, angle: 270) 24 + 25 + if let window, !window.isKeyWindow { 26 + NSColor.pinstripes.setFill() 27 + bounds.fill() 28 + } 29 + 30 + NSColor.aquaSeparatorColor.setFill() 31 + NSBezierPath.fill(NSRect(x: bounds.minX, y: bounds.maxY - 1, width: bounds.width, height: 1)) 32 + 33 + // TODO: using NSColor.aquaSeparatorColor, draw a window resize handle with four diagonal lines in the style of Mac OS X, in the lower-right corner. 34 + } 35 + } 36 + 37 + open override func loadView() { 38 + view = View() 39 + } 40 + }