import AppKit import SwiftUI /// A shape with a fill, layered drop shadows, layered inset shadows and a hairline edge: a physical part. public struct PhysicalSurface: View { private let fill: AnyShapeStyle private let cornerRadius: CGFloat private let shadows: [ShadowLayer] private let edge: Color? @Environment(\.colorScheme) private var scheme public init(fill: AnyShapeStyle, cornerRadius: CGFloat, shadows: [ShadowLayer], edge: Color? = Palette.lineSeamDark) { self.fill = fill self.cornerRadius = cornerRadius self.shadows = shadows self.edge = edge } public var body: some View { let shape = RoundedRectangle(cornerRadius: cornerRadius, style: .circular) ZStack { shadows.filter { !$0.inset }.reduce(AnyView(shape.fill(fill))) { view, layer in AnyView(view.shadow(color: layer.color(scheme), radius: layer.blur / 2, x: layer.x, y: layer.y)) } ForEach(Array(shadows.filter(\.inset).enumerated()), id: \.offset) { _, layer in InsetShadow(layer: layer, cornerRadius: cornerRadius) } if let edge { shape.strokeBorder(edge, lineWidth: Border.hairline) } } } } /// A shadow cast inwards from the edge of a rounded rectangle. struct InsetShadow: View { let layer: ShadowLayer let cornerRadius: CGFloat @Environment(\.colorScheme) private var scheme var body: some View { Canvas { context, size in let rect = CGRect(origin: .zero, size: size) context.clip(to: Path(roundedRect: rect, cornerRadius: cornerRadius)) context.drawLayer { inner in inner.addFilter(.blur(radius: layer.blur / 2)) let hole = rect.insetBy(dx: layer.spread, dy: layer.spread).offsetBy(dx: layer.x, dy: layer.y) var frame = Path(rect.insetBy(dx: -size.width, dy: -size.height)) frame.addPath(Path(roundedRect: hole, cornerRadius: max(0, cornerRadius - layer.spread))) inner.fill(frame, with: .color(layer.color(scheme)), style: FillStyle(eoFill: true)) } } .allowsHitTesting(false) } } /// Faceplate of matt plastic, sitting above the chassis. public struct PanelBackground: View { public init() {} public var body: some View { PhysicalSurface(fill: AnyShapeStyle(Palette.surfacePanel), cornerRadius: Radius.none, shadows: Shadows.panel) } } /// A recessed area (input fields, display windows). public struct WellBackground: View { public init() {} public var body: some View { PhysicalSurface(fill: AnyShapeStyle(Palette.surfaceWell), cornerRadius: Radius.none, shadows: Shadows.well) } } /// Dark display glass for the counter. public struct DisplayBackground: View { public init() {} public var body: some View { PhysicalSurface(fill: AnyShapeStyle(Palette.surfaceDisplay), cornerRadius: Radius.none, shadows: Shadows.well) } } /// Cassette-label cream, for lists of transcripts and dictionary entries. public struct PaperBackground: View { public init() {} public var body: some View { PhysicalSurface(fill: AnyShapeStyle(Palette.surfacePaper), cornerRadius: Radius.none, shadows: Shadows.well) } } /// An engraved groove: a dark line with a light line below it. public struct SeamDivider: View { public init() {} public var body: some View { VStack(spacing: 0) { Rectangle().fill(Palette.lineSeamDark).frame(height: Border.hairline) Rectangle().fill(Palette.lineSeamLight).frame(height: Border.hairline) } .accessibilityHidden(true) } } /// Small capitals silkscreen label. public struct SilkLabel: View { private let text: String private let style: TextStyle public init(_ text: String, style: TextStyle = Typography.label) { self.text = text self.style = style } public var body: some View { Text(text).textStyle(style).foregroundStyle(Palette.inkTertiary) } } /// Brushed aluminium: a vertical gradient with a horizontal grain. public struct BrushedBackground: View { @Environment(\.colorScheme) private var scheme public init() {} public var body: some View { ZStack { LinearGradient(colors: [Palette.surfaceMetalHigh, Palette.surfaceMetalLow], startPoint: .top, endPoint: .bottom) Image(nsImage: scheme == .dark ? BrushedTexture.dark : BrushedTexture.light) .resizable(resizingMode: .tile) } } } enum BrushedTexture { private static let side = 256 private static let gain = 2.2 // makes the token grain value visible as streak opacity static let light = make(grain: Texture.brushedGrainLight) static let dark = make(grain: Texture.brushedGrainDark) private struct SplitMix64: RandomNumberGenerator { var state: UInt64 mutating func next() -> UInt64 { state &+= 0x9E37_79B9_7F4A_7C15 var z = state z = (z ^ (z >> 30)) &* 0xBF58_476D_1CE4_E5B9 z = (z ^ (z >> 27)) &* 0x94D0_49BB_1331_11EB return z ^ (z >> 31) } } /// A seeded grain: every row has its own brightness and pixels vary only slightly, which gives fine horizontal streaks. private static func make(grain: Double) -> NSImage { var rng = SplitMix64(state: 7) let rows = (0.. 0.5 ? 1.0 : 0.0 let index = (y * side + x) * 4 let premultiplied = UInt8(shade * alpha * 255) pixels[index] = premultiplied pixels[index + 1] = premultiplied pixels[index + 2] = premultiplied pixels[index + 3] = UInt8(alpha * 255) } } let provider = CGDataProvider(data: Data(pixels) as CFData)! let image = CGImage( width: side, height: side, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: side * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue), provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)! return NSImage(cgImage: image, size: NSSize(width: side, height: side)) } }