Die Notch wird zur Insel, wo es keine Hardware gibt
Die Silhouette bestand aus zwei überlappenden Teilpfaden. Gefüllt stimmte die Vereinigung, aber die Kontur zeichnete jeden Teilpfad vollständig nach — auch dort, wo er innen lag. Dazu kippte die Form ab einer Breite in einen anderen Zweig, und die beiden Zweige waren nicht dieselbe Geometrie. Zusammen war das das Flackern an den Rundungen. Jetzt ein durchgehender Umriss, dessen Zweige ineinander übergehen statt zu springen. Neu ist die Wahl zwischen verschmolzen und schwebend. Eine echte Notch **muss** an der Kante kleben und oben eckig sein, sonst klafft ein Spalt zum Gehäuse; eine Insel **muss** Abstand halten und rundum gerundet sein, sonst sieht sie aus wie ein abgeschnittenes Rechteck. Beides zu mischen geht nicht, deshalb eine Wahl und keine zwei Schalter. Ihre Höhe rät Onyx nicht mehr, sondern misst die Menüleiste: 24 Punkte minus Luft oben und unten. Vorher hing ein festes Maß unten heraus. Dazu „nur der Hauptbildschirm" als dritte Anzeigewahl — gemeint ist der mit der Menüleiste, nicht `NSScreen.main`, das mit dem aktiven Fenster mitwandert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ecb39fbe5f
commit
c05ccb8ae1
@@ -36,6 +36,61 @@ public final class NotchCoordinator {
|
||||
didSet { guard virtualNotchSize != oldValue else { return }; rebuild() }
|
||||
}
|
||||
|
||||
/// Das Maß einer Insel, wenn nichts anderes eingestellt ist.
|
||||
///
|
||||
/// Die **Höhe** ist hier nur ein Rückfall: auf einem Bildschirm mit
|
||||
/// Menüleiste gibt deren Höhe das Maß vor, damit die Insel darin liegt
|
||||
/// statt darunter herauszuhängen.
|
||||
///
|
||||
/// Die Breite bestimmt, wie rund die Form *wirkt* — der Radius ist mit der
|
||||
/// halben Höhe ohnehin am Maximum. Bei 150 zu 26 war es ein Balken mit
|
||||
/// runden Enden; 72 zu rund 18 sind etwa 4:1, nah an der Insel am iPhone.
|
||||
/// Inhalt braucht die leere Insel keinen: für jede Live Activity wächst sie
|
||||
/// ohnehin auf deren Maß.
|
||||
public static let defaultVirtualSize = CGSize(width: 72, height: 26)
|
||||
|
||||
/// Was eine Inselbreite mindestens und höchstens sein darf.
|
||||
///
|
||||
/// Nach unten, damit die Insel nicht zum Punkt wird, den niemand mehr
|
||||
/// trifft; nach oben, weil sie sonst die halbe Menüleiste verdeckt.
|
||||
public static let widthRange: ClosedRange<CGFloat> = 48...320
|
||||
|
||||
/// Abweichende Inselbreiten, je Bildschirm.
|
||||
///
|
||||
/// **Nur die Breite.** Die Höhe ist nicht mehr frei: bei verschmolzener
|
||||
/// Notch gibt sie die Hardware vor, bei schwebender Insel die Menüleiste,
|
||||
/// damit die Insel darin liegt statt darunter herauszuhängen. Ein
|
||||
/// Höhenregler daneben wäre ein Bedienelement, das nichts tut.
|
||||
///
|
||||
/// Je Bildschirm, weil ein 27-Zoll-Monitor und ein Laptopdeckel dieselbe
|
||||
/// Breite verschieden groß aussehen lassen.
|
||||
public var virtualWidthOverrides: [CGDirectDisplayID: CGFloat] = [:] {
|
||||
didSet { guard virtualWidthOverrides != oldValue else { return }; rebuild() }
|
||||
}
|
||||
|
||||
/// Die Inselgröße, die für diesen Bildschirm gilt.
|
||||
func virtualSize(for displayID: CGDirectDisplayID) -> CGSize {
|
||||
guard let width = virtualWidthOverrides[displayID] else { return virtualNotchSize }
|
||||
return CGSize(width: width, height: virtualNotchSize.height)
|
||||
}
|
||||
|
||||
/// Notch oder Insel. Siehe ``NotchStyle``.
|
||||
public var style: NotchStyle = .automatic {
|
||||
didSet {
|
||||
guard style != oldValue else { return }
|
||||
controllers.values.forEach { $0.style = style }
|
||||
}
|
||||
}
|
||||
|
||||
/// Ob die eingeklappte Insel auf jedem Bildschirm stehen bleibt.
|
||||
/// Siehe ``NotchScreenController/showsIdleIsland``.
|
||||
public var showsIdleIsland = true {
|
||||
didSet {
|
||||
guard showsIdleIsland != oldValue else { return }
|
||||
controllers.values.forEach { $0.showsIdleIsland = showsIdleIsland }
|
||||
}
|
||||
}
|
||||
|
||||
private var controllers: [CGDirectDisplayID: NotchScreenController] = [:]
|
||||
private var monitors: [Any] = []
|
||||
private var suppressionTimer: Timer?
|
||||
@@ -50,7 +105,7 @@ public final class NotchCoordinator {
|
||||
|
||||
public init(policy: DisplayPolicy = .builtInOnly,
|
||||
trigger: TriggerArea = .standard,
|
||||
virtualNotchSize: CGSize = CGSize(width: 220, height: 32),
|
||||
virtualNotchSize: CGSize = NotchCoordinator.defaultVirtualSize,
|
||||
makeContent: @escaping (CGDirectDisplayID, NotchPresentation) -> NSView) {
|
||||
self.policy = policy
|
||||
self.trigger = trigger
|
||||
@@ -108,14 +163,17 @@ public final class NotchCoordinator {
|
||||
if let existing = controllers[metrics.displayID] {
|
||||
// Bestehenden Controller aktualisieren statt neu zu bauen:
|
||||
// ein Auflösungswechsel darf ein offenes Panel nicht wegreißen.
|
||||
existing.update(metrics: metrics, virtualSize: virtualNotchSize)
|
||||
existing.update(metrics: metrics,
|
||||
virtualSize: virtualSize(for: metrics.displayID))
|
||||
} else {
|
||||
let id = metrics.displayID
|
||||
let controller = NotchScreenController(
|
||||
metrics: metrics,
|
||||
virtualSize: virtualNotchSize,
|
||||
virtualSize: virtualSize(for: id),
|
||||
trigger: trigger,
|
||||
style: style,
|
||||
makeContent: { presentation in makeContent(id, presentation) })
|
||||
controller.showsIdleIsland = showsIdleIsland
|
||||
controller.onVisibilityChanged = { [weak self] _ in
|
||||
self?.updatePolling()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user