feat: add VU meter, segment counter and LED bar views
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import SwiftUI
|
||||
|
||||
/// Seven-segment counter on dark glass. Digits, ":" and "." are drawn; unlit segments stay faintly visible.
|
||||
public struct SegmentCounterView: View {
|
||||
private let text: String
|
||||
|
||||
public init(_ text: String) {
|
||||
self.text = text
|
||||
}
|
||||
|
||||
private var contentWidth: CGFloat {
|
||||
text.reduce(0) { total, character in
|
||||
total + (character.isNumber ? Segment.advance : Segment.colonWidth)
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Canvas { context, _ in
|
||||
var x: CGFloat = 0
|
||||
for character in text {
|
||||
if character.isNumber {
|
||||
var origin = context
|
||||
origin.translateBy(x: x, y: 0)
|
||||
let lit = SegmentGlyph.lit(for: character)
|
||||
for id in SegmentID.allCases {
|
||||
let polygon = SegmentGlyph.polygon(id)
|
||||
var path = Path()
|
||||
path.addLines(polygon)
|
||||
path.closeSubpath()
|
||||
origin.fill(path, with: .color(lit.contains(id) ? Palette.levelAmber : Palette.displaySegmentUnlit))
|
||||
}
|
||||
x += Segment.advance
|
||||
} else {
|
||||
let dot = Segment.thickness
|
||||
let inset = (Segment.colonWidth - dot) / 2
|
||||
let ys: [CGFloat] = character == ":"
|
||||
? [Segment.digitHeight * 0.28, Segment.digitHeight * 0.64]
|
||||
: [Segment.digitHeight - dot]
|
||||
for y in ys {
|
||||
context.fill(Path(CGRect(x: x + inset, y: y, width: dot, height: dot)), with: .color(Palette.levelAmber))
|
||||
}
|
||||
x += Segment.colonWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: contentWidth, height: Segment.digitHeight)
|
||||
.padding(.horizontal, Space.s4)
|
||||
.padding(.vertical, Space.s3)
|
||||
.background(DisplayBackground())
|
||||
.accessibilityElement()
|
||||
.accessibilityLabel("Tijd")
|
||||
.accessibilityValue(text)
|
||||
}
|
||||
}
|
||||
|
||||
/// Twelve-segment level bar: green, amber, then one red segment.
|
||||
public struct LEDBarView: View {
|
||||
private let fraction: Double
|
||||
|
||||
public init(fraction: Double) {
|
||||
self.fraction = fraction
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
let lit = LEDBar.litCount(forFraction: fraction)
|
||||
HStack(spacing: MeterTokens.ledGap) {
|
||||
ForEach(0..<MeterTokens.ledSegments, id: \.self) { index in
|
||||
RoundedRectangle(cornerRadius: Radius.sharp, style: .circular)
|
||||
.fill(color(for: index, lit: index < lit))
|
||||
.frame(width: MeterTokens.ledSegmentWidth, height: MeterTokens.ledSegmentHeight)
|
||||
}
|
||||
}
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
private func color(for index: Int, lit: Bool) -> Color {
|
||||
switch LEDBar.tone(forSegment: index) {
|
||||
case .green: lit ? Palette.levelGreen : Palette.levelGreenUnlit
|
||||
case .amber: lit ? Palette.levelAmber : Palette.levelAmberUnlit
|
||||
case .red: lit ? Palette.accentRecord : Palette.accentRecordUnlit
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user