Die Vorschau der Auslösefläche war eine schwarze Fläche — als Bildschirm kaum zu erkennen. Jetzt liegt der echte Schreibtischhintergrund darunter. Apples eigene Bilder lassen sich nicht mitliefern (Lizenz, und ein Tahoe-Hintergrund wiegt zweistellige Megabyte); der eigene ist ohnehin der bessere Anker, weil man genau ihn wiedererkennt. Fehlt er, bleibt ein Verlauf in Onyx-Farben. Dabei fiel auf, dass Notch und Auslösefläche oberhalb des Bildschirms schwebten statt in ihm zu sitzen: die Rundung lag auf dem Hintergrund allein, nicht auf dem Ganzen. Sie schneidet jetzt Hintergrund, Menüleiste und Fläche gemeinsam ab. Und das Einstellungsfenster war zu schmal für sechs Reiter — die Leiste wurde rechts abgeschnitten, „Berechtigungen" war nicht mehr erreichbar. 780 statt 620 Punkte, Mindestbreite 740. Beim Nachrechnen der reinen Textbreiten kommt man auf 560 und liegt damit deutlich daneben: macOS legt je Reiter Innenabstand und Trenner dazu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
145 lines
5.7 KiB
Swift
145 lines
5.7 KiB
Swift
import SwiftUI
|
|
import OnyxDesign
|
|
|
|
/// Einstellung der Auslösefläche — mit Vorschau.
|
|
///
|
|
/// Zwei Zahlen in Punkten sagen niemandem etwas: „24 Punkte unter der Notch"
|
|
/// ist keine Vorstellung, die man im Kopf hat. Ein nachgebildeter Bildschirm
|
|
/// mit der Notch und der eingefärbten Fläche darüber schon — man sieht sofort,
|
|
/// wie weit man greifen muss.
|
|
public struct TriggerAreaSettings: View {
|
|
|
|
@Binding private var trigger: TriggerArea
|
|
|
|
public init(trigger: Binding<TriggerArea>) {
|
|
self._trigger = trigger
|
|
}
|
|
|
|
public var body: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text("trigger.hint", bundle: .module)
|
|
.font(.callout)
|
|
.foregroundStyle(.secondary)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
|
|
TriggerPreview(trigger: trigger)
|
|
.frame(height: 150)
|
|
.frame(maxWidth: .infinity)
|
|
|
|
LabeledContent {
|
|
Slider(value: Binding(
|
|
get: { trigger.extraWidth },
|
|
set: { trigger = TriggerArea(extraWidth: $0,
|
|
extraHeight: trigger.extraHeight) }),
|
|
in: 0...TriggerArea.maximumExtraWidth)
|
|
} label: {
|
|
Text("trigger.width \(Int(trigger.extraWidth))", bundle: .module)
|
|
.monospacedDigit()
|
|
}
|
|
|
|
LabeledContent {
|
|
Slider(value: Binding(
|
|
get: { trigger.extraHeight },
|
|
set: { trigger = TriggerArea(extraWidth: trigger.extraWidth,
|
|
extraHeight: $0) }),
|
|
in: 0...TriggerArea.maximumExtraHeight)
|
|
} label: {
|
|
Text("trigger.height \(Int(trigger.extraHeight))", bundle: .module)
|
|
.monospacedDigit()
|
|
}
|
|
|
|
Button {
|
|
trigger = .standard
|
|
} label: {
|
|
Text("trigger.reset", bundle: .module)
|
|
}
|
|
.buttonStyle(.borderless)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Der nachgebildete Bildschirm.
|
|
///
|
|
/// Mit dem echten Schreibtischhintergrund: Apples Bilder lassen sich nicht
|
|
/// mitliefern — Lizenz, und ein Tahoe-Hintergrund wiegt zweistellige Megabyte.
|
|
/// Der eigene ist ohnehin der bessere Anker, weil man genau ihn wiedererkennt.
|
|
/// Fehlt er, bleibt ein Verlauf in Onyx-Farben.
|
|
private struct TriggerPreview: View {
|
|
let trigger: TriggerArea
|
|
|
|
/// Maße eines MacBook Pro mit Notch, auf die Vorschau heruntergerechnet.
|
|
private let screenAspect: CGFloat = 1800 / 1169
|
|
private let notchWidthFraction: CGFloat = 220 / 1800
|
|
private let notchHeightFraction: CGFloat = 38 / 1169
|
|
|
|
var body: some View {
|
|
GeometryReader { geometry in
|
|
let height = min(geometry.size.height, geometry.size.width / screenAspect)
|
|
let width = height * screenAspect
|
|
let scale = width / 1800
|
|
|
|
let notchWidth = width * notchWidthFraction
|
|
let notchHeight = height * notchHeightFraction
|
|
let areaWidth = min(notchWidth + 2 * trigger.extraWidth * scale, width)
|
|
let areaHeight = min(notchHeight + trigger.extraHeight * scale, height)
|
|
|
|
ZStack(alignment: .top) {
|
|
wallpaper
|
|
|
|
// Die Menüleiste als heller Streifen: sie erklärt, warum die
|
|
// Fläche nicht beliebig breit werden sollte.
|
|
Rectangle()
|
|
.fill(.black.opacity(0.35))
|
|
.frame(height: notchHeight)
|
|
.frame(maxWidth: .infinity, alignment: .top)
|
|
|
|
// Die Auslösefläche.
|
|
Rectangle()
|
|
.fill(Onyx.Color.accent.opacity(0.35))
|
|
.overlay { Rectangle().strokeBorder(Onyx.Color.accent, lineWidth: 1) }
|
|
.frame(width: areaWidth, height: areaHeight)
|
|
|
|
// Die Notch selbst — undurchsichtig, wie in echt.
|
|
UnevenRoundedRectangle(bottomLeadingRadius: 5, bottomTrailingRadius: 5,
|
|
style: .continuous)
|
|
.fill(.black)
|
|
.frame(width: notchWidth, height: notchHeight)
|
|
}
|
|
.frame(width: width, height: height)
|
|
// Die Rundung zuletzt: sie schneidet Hintergrund, Menüleiste und
|
|
// Fläche gemeinsam ab, sonst ragt etwas über die Ecken hinaus.
|
|
.clipShape(.rect(cornerRadius: 8, style: .continuous))
|
|
.overlay {
|
|
RoundedRectangle(cornerRadius: 8, style: .continuous)
|
|
.strokeBorder(Onyx.Color.hairline, lineWidth: 1)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.animation(Onyx.Motion.value, value: areaWidth)
|
|
.animation(Onyx.Motion.value, value: areaHeight)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var wallpaper: some View {
|
|
if let image = Self.desktopImage {
|
|
Image(nsImage: image)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fill)
|
|
} else {
|
|
LinearGradient(colors: [Onyx.Color.elevated, Onyx.Color.surface],
|
|
startPoint: .top, endPoint: .bottom)
|
|
}
|
|
}
|
|
|
|
/// Der Schreibtischhintergrund des Hauptbildschirms.
|
|
///
|
|
/// Einmal geladen und behalten: es ist eine Datei von mehreren Megabyte,
|
|
/// und die Vorschau zeichnet sich bei jedem Zug am Regler neu.
|
|
private static let desktopImage: NSImage? = {
|
|
guard let screen = NSScreen.main,
|
|
let url = NSWorkspace.shared.desktopImageURL(for: screen)
|
|
else { return nil }
|
|
return NSImage(contentsOf: url)
|
|
}()
|
|
}
|