Eine Designschicht statt hundertfach gesetzter Zahlen
Token für Schrift, Symbolgröße, Abstand, Radius und Bewegung, dazu sieben Bauteile, die vorher in jeder Kachel neu gebaut wurden — Karte, Abschnittstitel, Hinweis, Pillenknopf, Regler, Ablegefläche, Haarlinie. Symbole bekommen eine **eigene** Staffel und nicht die der Schrift: ein 9-Punkt-Symbol neben 9-Punkt-Text wirkt kleiner, nicht gleich groß. Beide in eine Staffel zu zwingen hieße, an jeder Stelle gegen sie zu arbeiten. Das Flächenrezept — Tint, Bänderung, Kante — stand zweimal da, einmal für das Panel und einmal für Fenster. Es ist jetzt `OnyxStone`, generisch über die Form, damit auch die animierende Notch-Silhouette es benutzen kann. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
49a3ff0548
commit
ecb39fbe5f
@@ -20,34 +20,72 @@ public struct OnyxVisualEffect: NSViewRepresentable {
|
||||
public func updateNSView(_ view: NSVisualEffectView, context: Context) {}
|
||||
}
|
||||
|
||||
/// Der Hintergrund des Panels: Vibrancy, Tint, ein sehr flacher Glanz von oben
|
||||
/// und eine Haarlinie als Innenkante.
|
||||
public struct OnyxSurface: View {
|
||||
private let cornerRadius: CGFloat
|
||||
/// Der Stein selbst — auf einer beliebigen Form.
|
||||
///
|
||||
/// Tint, Bänderung, Kante. Drei Lagen, die zusammen den Werkstoff ergeben, und
|
||||
/// zwar unabhängig davon, welche Form sie annehmen.
|
||||
///
|
||||
/// **Warum generisch und nicht einfach ein abgerundetes Rechteck.** Das Panel
|
||||
/// zeichnet auf ``NotchShape``, einer Form, die sich beim Aus- und Einfahren
|
||||
/// verändert. Solange das Rezept an ein Rechteck gebunden war, konnte es dort
|
||||
/// nicht mitbenutzt werden — also stand es zweimal da, einmal hier und einmal
|
||||
/// im Panel. Zweimal heißt: eine Änderung an der Bänderung wirkt an einer
|
||||
/// Stelle und an der anderen nicht, und das fällt erst auf, wenn beide
|
||||
/// nebeneinander zu sehen sind.
|
||||
public struct OnyxStone<S: InsettableShape>: View {
|
||||
private let shape: S
|
||||
|
||||
public init(cornerRadius: CGFloat = Onyx.Metric.panelCornerRadius) {
|
||||
self.cornerRadius = cornerRadius
|
||||
public init(_ shape: S) {
|
||||
self.shape = shape
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
let shape = RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
||||
// Aus dem Thema gelesen und nicht als Argument gereicht: sonst müsste
|
||||
// jede Stelle, die eine Fläche zeichnet, den Werkstoff durchschleifen —
|
||||
// bis hinunter zur Kachel, die ihn gar nicht kennt.
|
||||
let material = OnyxTheme.shared.material
|
||||
|
||||
shape
|
||||
.fill(Onyx.Color.surface.opacity(Onyx.Color.surfaceOpacity))
|
||||
.background(OnyxVisualEffect().clipShape(shape))
|
||||
.fill(Onyx.Color.surface.opacity(material.fillOpacity))
|
||||
.overlay {
|
||||
// Die Bänderung des Steins: oben ein Hauch heller, nach 40 %
|
||||
// vollständig verlaufen. Mehr wirkt sofort billig.
|
||||
// vollständig verlaufen. Mehr wirkt sofort billig — wie viel
|
||||
// „ein Hauch" ist, hängt am Werkstoff.
|
||||
shape.fill(
|
||||
LinearGradient(
|
||||
stops: [
|
||||
.init(color: .white.opacity(0.02), location: 0),
|
||||
.init(color: .white.opacity(material.sheen), location: 0),
|
||||
.init(color: .clear, location: 0.4),
|
||||
],
|
||||
startPoint: .top, endPoint: .bottom))
|
||||
}
|
||||
.overlay {
|
||||
shape.strokeBorder(Onyx.Color.hairline, lineWidth: Onyx.Metric.hairlineWidth)
|
||||
// `strokeBorder` und nicht `stroke`: die Linie liegt dann
|
||||
// vollständig **innen**. Mittig gezeichnet ragte sie zur Hälfte
|
||||
// über die Kante hinaus und wurde an den Rundungen sichtbar
|
||||
// dünner.
|
||||
shape.strokeBorder(material.edge, lineWidth: Onyx.Metric.hairlineWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Der Hintergrund des Panels: Vibrancy und darauf der Stein.
|
||||
public struct OnyxSurface: View {
|
||||
private let cornerRadius: CGFloat
|
||||
|
||||
public init(cornerRadius: CGFloat = Onyx.Radius.panel) {
|
||||
self.cornerRadius = cornerRadius
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
let shape = RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
||||
OnyxStone(shape)
|
||||
.background {
|
||||
// Bei deckendem Stein trägt die Weichzeichnung nichts bei und
|
||||
// wird auch nicht berechnet.
|
||||
if OnyxTheme.shared.material.showsVibrancy {
|
||||
OnyxVisualEffect().clipShape(shape)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,10 +99,10 @@ public struct OnyxTile<Content: View>: View {
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
let shape = RoundedRectangle(cornerRadius: Onyx.Metric.tileCornerRadius, style: .continuous)
|
||||
let shape = RoundedRectangle(cornerRadius: Onyx.Radius.tile, style: .continuous)
|
||||
|
||||
content
|
||||
.padding(12)
|
||||
.padding(Onyx.Space.lg)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.background(shape.fill(Onyx.Color.elevated))
|
||||
.overlay(shape.strokeBorder(Onyx.Color.hairline, lineWidth: Onyx.Metric.hairlineWidth))
|
||||
|
||||
Reference in New Issue
Block a user