diff --git a/Onyx/SettingsView.swift b/Onyx/SettingsView.swift index aa278a5..6ae98a8 100644 --- a/Onyx/SettingsView.swift +++ b/Onyx/SettingsView.swift @@ -48,7 +48,7 @@ struct SettingsView: View { } // Nur ein Mindestmaß, keine feste Größe: sonst kann das Fenster // wachsen, der Inhalt aber nicht mit. - .frame(minWidth: 520, minHeight: 420) + .frame(minWidth: 740, minHeight: 460) } } diff --git a/Onyx/SettingsWindowController.swift b/Onyx/SettingsWindowController.swift index ee66ded..9bc1806 100644 --- a/Onyx/SettingsWindowController.swift +++ b/Onyx/SettingsWindowController.swift @@ -62,8 +62,15 @@ final class SettingsWindowController: NSObject, NSWindowDelegate { // mehr, und ein Fenster, in dem man scrollen muss, obwohl der // Bildschirm frei ist, ist eine Zumutung. window.styleMask = [.titled, .closable, .miniaturizable, .resizable] - window.setContentSize(NSSize(width: 620, height: 620)) - window.contentMinSize = NSSize(width: 520, height: 420) + window.setContentSize(NSSize(width: 780, height: 660)) + // Breit genug für die Reiterleiste. + // + // Sechs Reiter mit deutschen Beschriftungen brauchen mehr, als man + // beim Nachrechnen der reinen Textbreiten herausbekommt: macOS legt + // je Reiter Innenabstand und Trenner dazu. Ist das Fenster zu + // schmal, schneidet es die Leiste rechts einfach ab — der letzte + // Reiter ist dann nicht mehr erreichbar. + window.contentMinSize = NSSize(width: 740, height: 460) window.isReleasedWhenClosed = false window.delegate = self Self.place(window) diff --git a/Packages/OnyxKit/Sources/OnyxNotch/TriggerAreaSettings.swift b/Packages/OnyxKit/Sources/OnyxNotch/TriggerAreaSettings.swift index b877ec7..617c819 100644 --- a/Packages/OnyxKit/Sources/OnyxNotch/TriggerAreaSettings.swift +++ b/Packages/OnyxKit/Sources/OnyxNotch/TriggerAreaSettings.swift @@ -60,9 +60,10 @@ public struct TriggerAreaSettings: View { /// Der nachgebildete Bildschirm. /// -/// Absichtlich karg: es geht nur darum, wo die Notch sitzt und wie weit die -/// Fläche darum herum reicht. Ein Schreibtischhintergrund oder Fenster darin -/// wären Beiwerk, das von der einen Frage ablenkt. +/// 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 @@ -79,32 +80,23 @@ private struct TriggerPreview: View { let notchWidth = width * notchWidthFraction let notchHeight = height * notchHeightFraction - let areaWidth = notchWidth + 2 * trigger.extraWidth * scale - let areaHeight = notchHeight + trigger.extraHeight * scale + let areaWidth = min(notchWidth + 2 * trigger.extraWidth * scale, width) + let areaHeight = min(notchHeight + trigger.extraHeight * scale, height) ZStack(alignment: .top) { - RoundedRectangle(cornerRadius: 8, style: .continuous) - .fill(Onyx.Color.surface) - .overlay { - RoundedRectangle(cornerRadius: 8, style: .continuous) - .strokeBorder(Onyx.Color.hairline, lineWidth: 1) - } + wallpaper - // Die Menüleiste als dünner Streifen: sie erklärt, warum die + // Die Menüleiste als heller Streifen: sie erklärt, warum die // Fläche nicht beliebig breit werden sollte. - VStack(spacing: 0) { - Rectangle() - .fill(.white.opacity(0.06)) - .frame(height: notchHeight) - Spacer(minLength: 0) - } + 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.3)) - .overlay { - Rectangle().strokeBorder(Onyx.Color.accent, lineWidth: 1) - } + .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. @@ -114,9 +106,39 @@ private struct TriggerPreview: View { .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) + }() }