From f345b5402cd465c58560491e41d75cf4d669a267 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Mon, 21 Sep 2026 18:06:30 -0700 Subject: [PATCH] Host notebook buttons above native title-bar event interception --- apple/aesel/Sources/ApplePlatform.swift | 57 +++++++++++++++++++ apple/aesel/Sources/ContentView.swift | 15 ++--- .../aesel/Tests/HeaderInteractionChecks.swift | 13 +++-- 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/apple/aesel/Sources/ApplePlatform.swift b/apple/aesel/Sources/ApplePlatform.swift index 0618bdcb91..8ab76ad365 100644 --- a/apple/aesel/Sources/ApplePlatform.swift +++ b/apple/aesel/Sources/ApplePlatform.swift @@ -228,3 +228,60 @@ struct AeselWindowDragArea: View { var body: some View { Color.clear } } #endif + +#if os(macOS) +/// Place header controls above AppKit's title-bar event layer. +struct AeselTitlebarAccessory: NSViewRepresentable { + let content: Content + init(@ViewBuilder content: () -> Content) { self.content = content() } + final class Carrier: NSView { + let accessory = NSTitlebarAccessoryViewController() + let hosting = NSHostingView(rootView: AnyView(EmptyView())) + weak var installedWindow: NSWindow? + var resizeObserver: NSObjectProtocol? + override init(frame: NSRect) { + super.init(frame: frame) + accessory.layoutAttribute = .right + accessory.view = hosting + } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + override func viewDidMoveToWindow() { + super.viewDidMoveToWindow() + DispatchQueue.main.async { [weak self] in self?.install() } + } + func install() { + guard let window, installedWindow !== window else { return } + detach() + installedWindow = window + resize() + window.addTitlebarAccessoryViewController(accessory) + resizeObserver = NotificationCenter.default.addObserver(forName: NSWindow.didResizeNotification, + object: window, queue: .main) { [weak self] _ in self?.resize() } + } + func resize() { + guard let installedWindow else { return } + // Standard window buttons own the leftmost 64 physical points. + hosting.setFrameSize(NSSize(width: max(0, installedWindow.frame.width - 64), height: 32)) + } + func detach() { + if let resizeObserver { NotificationCenter.default.removeObserver(resizeObserver) } + resizeObserver = nil + if let window = installedWindow, + let index = window.titlebarAccessoryViewControllers.firstIndex(of: accessory) { + window.removeTitlebarAccessoryViewController(at: index) + } + installedWindow = nil + } + } + func makeNSView(context: Context) -> Carrier { + let view = Carrier(frame: .zero) + view.hosting.rootView = AnyView(content) + return view + } + func updateNSView(_ view: Carrier, context: Context) { + view.hosting.rootView = AnyView(content) + view.install() + } + static func dismantleNSView(_ view: Carrier, coordinator: ()) { view.detach() } +} +#endif diff --git a/apple/aesel/Sources/ContentView.swift b/apple/aesel/Sources/ContentView.swift index 7de43e6cd9..406f25fe89 100644 --- a/apple/aesel/Sources/ContentView.swift +++ b/apple/aesel/Sources/ContentView.swift @@ -203,21 +203,19 @@ struct ContentView: View { } } - private var headerReservedWidth: CGFloat { + @ViewBuilder private var notebookHeader: some View { #if os(macOS) - 52 / uiScale + edgeInset * 2 + 36 + 48 + AeselTitlebarAccessory { headerControls.environment(\.paint, paint) } + .frame(height: 32 / uiScale) #else - edgeInset * 2 + 24 + 48 + headerControls.frame(height: 32) #endif } - private var notebookHeader: some View { + private var headerControls: some View { GeometryReader { geometry in HStack(spacing: 12) { - #if os(macOS) - Color.clear.frame(width: 52 / uiScale) - #endif - title(availableWidth: max(0, geometry.size.width - headerReservedWidth)) + title(availableWidth: max(0, geometry.size.width - edgeInset * 2 - 24 - 48)) Spacer(minLength: 8) .frame(height: 32) .background { AeselWindowDragArea() } @@ -226,7 +224,6 @@ struct ContentView: View { .padding(.horizontal, edgeInset) .frame(height: 32) } - .frame(height: 32) } private var titleURL: URL? { diff --git a/apple/aesel/Tests/HeaderInteractionChecks.swift b/apple/aesel/Tests/HeaderInteractionChecks.swift index 7988dceace..661ee0639f 100644 --- a/apple/aesel/Tests/HeaderInteractionChecks.swift +++ b/apple/aesel/Tests/HeaderInteractionChecks.swift @@ -10,8 +10,8 @@ private struct Header: View { let clicks: Clicks var body: some View { VStack(spacing: 0) { + AeselTitlebarAccessory { HStack(spacing: 12) { - Color.clear.frame(width: 52) Button { clicks.title += 1 } label: { AeselTitle(text: "notebook", size: 16, horizontalInset: 0) }.accessibilityLabel("Open piece").buttonStyle(AeselButtonStyle()) @@ -20,6 +20,7 @@ private struct Header: View { AeselTitle(text: "v0", size: 13, horizontalInset: 0, hoverAnchor: .trailing) }.accessibilityLabel("Open settings").buttonStyle(AeselButtonStyle()) }.padding(.horizontal, 14).frame(height: 32) + }.frame(height: 32) Color.clear }.ignoresSafeArea(.container, edges: .top).aeselWindowTitle("Test", paper: .white) } @@ -34,9 +35,11 @@ private struct Header: View { window.contentView = host try! await Task.sleep(for: .milliseconds(200)) func views(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(views) } - let cursors = views(host).filter { String(describing: type(of: $0)) == "CursorView" } + precondition(window.titlebarAccessoryViewControllers.count == 1, "Header must live in the native title bar") + let titlebarRoot = host.superview! + let cursors = views(titlebarRoot).filter { String(describing: type(of: $0)) == "CursorView" } precondition(cursors.count == 2, "Both header buttons need native cursor regions") - let frames = cursors.map { $0.convert($0.bounds, to: host) } + let frames = cursors.map { $0.convert($0.bounds, to: titlebarRoot) } for view in cursors { precondition(!view.visibleRect.isEmpty) let root = host.superview! @@ -48,7 +51,7 @@ private struct Header: View { let middle = host.convert(NSPoint(x: 300, y: 16), to: root.superview) precondition(root.hitTest(middle) is AeselWindowDragArea.DragView, "Header gap is not draggable") try! await Task.sleep(for: .milliseconds(1900)) - precondition(cursors.map { $0.convert($0.bounds, to: host) } == frames, "Letter motion changed button bounds") - print("PASS: native cursor regions, button hit targets, isolated window dragging, stable animated bounds") + precondition(cursors.map { $0.convert($0.bounds, to: titlebarRoot) } == frames, "Letter motion changed button bounds") + print("PASS: real title-bar accessory, native cursor regions, button hit targets, isolated window dragging, stable animated bounds") } } -- 2.51.2