···11+import Foundation
22+import AppKit
33+44+public struct AquaScrollState {
55+ public var scrollPosition: NSPoint
66+ public var contentSize: NSSize
77+ public var visibleSize: NSSize
88+99+ public init(scrollPosition: NSPoint, contentSize: NSSize, visibleSize: NSSize) {
1010+ self.scrollPosition = scrollPosition
1111+ self.contentSize = contentSize
1212+ self.visibleSize = visibleSize
1313+ }
1414+1515+ public var maxOffset: NSSize {
1616+ NSSize(
1717+ width: max(contentSize.width - visibleSize.width, 0),
1818+ height: max(contentSize.height - visibleSize.height, 0)
1919+ )
2020+ }
2121+2222+ public var verticalOverscroll: CGFloat {
2323+ absoluteExcess(of: scrollPosition.y, in: 0..<maxOffset.height)
2424+ }
2525+2626+ public func absoluteExcess(of position: CGFloat, in range: Range<CGFloat>) -> CGFloat {
2727+ if position < range.lowerBound {
2828+ range.lowerBound - position
2929+ } else if position > range.upperBound {
3030+ position - range.upperBound
3131+ } else {
3232+ 0
3333+ }
3434+ }
3535+3636+ public func absoluteExcess(of point: NSPoint, in size: NSSize) -> NSSize {
3737+ NSSize(
3838+ width: absoluteExcess(of: point.x, in: 0..<size.width),
3939+ height: absoluteExcess(of: point.y, in: 0..<size.height)
4040+ )
4141+ }
4242+4343+ public var effectiveContentSize: NSSize {
4444+ contentSize + absoluteExcess(of: scrollPosition, in: maxOffset)
4545+ }
4646+}
4747+4848+4949+extension NSScroller {
5050+ public func adopt(state: AquaScrollState, orientation: Orientation) {
5151+ let maxOffset = state.maxOffset
5252+ let effectiveContentSize = state.effectiveContentSize
5353+ knobProportion = min(max(state.visibleSize[orientation] / effectiveContentSize[orientation], 0.0), 1.0)
5454+ doubleValue = maxOffset[orientation] > 0 ? min(max(state.scrollPosition[orientation] / maxOffset[orientation], 0.0), 1.0) : 0.0
5555+ isEnabled = maxOffset[orientation] > 0
5656+ isHidden = maxOffset[orientation] <= 0
5757+ }
5858+}
5959+6060+extension AquaScroller {
6161+ public func adopt(state: AquaScrollState) {
6262+ adopt(state: state, orientation: orientation)
6363+ }
6464+}
+1-5
Sources/AquaKit/Scroll Views/AquaScroller.swift
···22import AquaKit
3344public class AquaScroller: NSScroller {
55- public enum Orientation {
66- case vertical, horizontal
77- }
88-95 public let orientation: Orientation
106117 public override var floatValue: Float {
···269265 }
270266}
271267272272-extension AquaScroller.Orientation {
268268+fileprivate extension Orientation {
273269 /// The affine transform that maps between screen space and canonical (vertical)
274270 /// coordinate space. For vertical this is the identity; for horizontal it swaps
275271 /// the x and y axes. This transform is its own inverse.