685 lines · 25.6 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport Observationimport SwiftUI extension StatusItemController {    func switcherWeeklyRemaining(for provider: UsageProvider) -> Double? {        let snapshot = self.store.snapshot(for: provider)        return Self.switcherWeeklyMetricPercent(            for: provider,            snapshot: snapshot,            showUsed: self.settings.usageBarsShowUsed,            preference: self.settings.menuBarMetricPreference(for: provider, snapshot: snapshot))    }     func applySubtitle(_ subtitle: String, to item: NSMenuItem, title: String) {        if #available(macOS 14.4, *) {            // NSMenuItem.subtitle is only available on macOS 14.4+.            item.subtitle = subtitle        } else {            item.view = self.makeMenuSubtitleView(title: title, subtitle: subtitle, isEnabled: item.isEnabled)            item.toolTip = "\(title) — \(subtitle)"        }    }     func makeMenuSubtitleView(title: String, subtitle: String, isEnabled: Bool) -> NSView {        let container = NSView()        container.translatesAutoresizingMaskIntoConstraints = false        container.alphaValue = isEnabled ? 1.0 : 0.7         let titleField = NSTextField(labelWithString: title)        titleField.font = NSFont.menuFont(ofSize: NSFont.systemFontSize)        titleField.textColor = NSColor.labelColor        titleField.lineBreakMode = .byTruncatingTail        titleField.maximumNumberOfLines = 1        titleField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)         let subtitleField = NSTextField(labelWithString: subtitle)        subtitleField.font = NSFont.menuFont(ofSize: NSFont.smallSystemFontSize)        subtitleField.textColor = NSColor.secondaryLabelColor        subtitleField.lineBreakMode = .byTruncatingTail        subtitleField.maximumNumberOfLines = 1        subtitleField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)         let stack = NSStackView(views: [titleField, subtitleField])        stack.orientation = .vertical        stack.alignment = .leading        stack.spacing = 1        stack.translatesAutoresizingMaskIntoConstraints = false        container.addSubview(stack)         NSLayoutConstraint.activate([            stack.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 18),            stack.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -10),            stack.topAnchor.constraint(equalTo: container.topAnchor, constant: 2),            stack.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -2),        ])         return container    }} @MainActorprotocol MenuCardHighlighting: AnyObject {    var allowsMenuHighlight: Bool { get }    func setHighlighted(_ highlighted: Bool)} extension MenuCardHighlighting {    var allowsMenuHighlight: Bool {        true    }} @MainActorprotocol MenuCardMeasuring: AnyObject {    func measuredHeight(width: CGFloat) -> CGFloat} @MainActor@Observablefinal class MenuCardHighlightState {    var isHighlighted = false} final class MenuHostingView<Content: View>: NSHostingView<Content> {    /// The height AppKit should give this item's menu row. NSMenu reads `intrinsicContentSize`    /// (not the explicit `frame`) when it lays out custom-view rows, so a measured height that    /// only lives in `frame` is silently reverted to the open-time row height — leaving the    /// SwiftUI content centered in a stale, oversized row. Routing the height through the    /// intrinsic size is the channel the menu actually honors.    private var measuredHeight: CGFloat?     override var allowsVibrancy: Bool {        true    }     override var intrinsicContentSize: NSSize {        guard let measuredHeight else { return super.intrinsicContentSize }        return NSSize(width: NSView.noIntrinsicMetric, height: measuredHeight)    }     func applyMeasuredHeight(width: CGFloat, height: CGFloat) {        let resolvedHeight = max(1, ceil(height))        guard self.measuredHeight != resolvedHeight || self.frame.height != resolvedHeight else { return }         self.measuredHeight = resolvedHeight        self.frame = NSRect(            origin: self.frame.origin,            size: NSSize(width: width, height: resolvedHeight))        self.invalidateIntrinsicContentSize()        self.layoutSubtreeIfNeeded()        self.superview?.layoutSubtreeIfNeeded()    }     /// Measures the true SwiftUI content height at `width`. The cached `measuredHeight` is routed    /// through `intrinsicContentSize`, so `fittingSize` would otherwise echo the stale cached value;    /// clearing it for the measurement lets the live content size drive the result. Used to resize    /// the row exactly when expandable content (e.g. status groups) toggles.    func measuredFittingHeight(width: CGFloat) -> CGFloat {        let saved = self.measuredHeight        self.measuredHeight = nil        self.frame = NSRect(origin: self.frame.origin, size: NSSize(width: width, height: 1))        self.invalidateIntrinsicContentSize()        self.layoutSubtreeIfNeeded()        let height = self.fittingSize.height        self.measuredHeight = saved        return height    }} @MainActorfinal class MenuCardItemHostingView<Content: View>: NSHostingView<Content>, MenuCardHighlighting, MenuCardMeasuring {    let highlightState: MenuCardHighlightState    private(set) var allowsMenuHighlight: Bool    private var onClick: (() -> Void)?    private var containsInteractiveControls: Bool    let interactiveRegionStore: MenuCardInteractiveRegionStore?    private var isPressed = false    private var isForwardingHostedControlPress = false    #if DEBUG    private var testForwardedHostedControlMouseDown = false    private var testForwardedHostedControlMouseUp = false    #endif     override var allowsVibrancy: Bool {        true    }     override var intrinsicContentSize: NSSize {        let size = super.intrinsicContentSize        guard self.frame.width > 0 else { return size }        return NSSize(width: self.frame.width, height: size.height)    }     init(        rootView: Content,        highlightState: MenuCardHighlightState,        allowsMenuHighlight: Bool,        containsInteractiveControls: Bool = false,        interactiveRegionStore: MenuCardInteractiveRegionStore? = nil,        onClick: (() -> Void)? = nil)    {        self.highlightState = highlightState        self.allowsMenuHighlight = allowsMenuHighlight        self.containsInteractiveControls = containsInteractiveControls        self.interactiveRegionStore = interactiveRegionStore        self.onClick = onClick        super.init(rootView: rootView)    }     /// Reuses this hosting view for a rebuilt card with the same identity: the replaced    /// `rootView` is diffed in place by SwiftUI instead of tearing down and recreating the    /// hosting view and its graph. Callers must construct `rootView` around this view's own    /// `highlightState` so menu hover highlighting keeps driving the rendered content.    func prepareForReuse(        rootView: Content,        allowsMenuHighlight: Bool,        containsInteractiveControls: Bool = false,        onClick: (() -> Void)?)    {        self.rootView = rootView        self.allowsMenuHighlight = allowsMenuHighlight        self.containsInteractiveControls = containsInteractiveControls        self.onClick = onClick        self.isPressed = false        self.isForwardingHostedControlPress = false    }     /// `NSMenu` tracking consumes keyboard events before they reach a menu item's custom view, so    /// the pointer `onClick` path has no native counterpart for assistive tech. Expose the row as an    /// accessibility button whose press mirrors a click, giving VoiceOver an activation path that runs    /// `onClick` (and therefore keeps the menu open) instead of regressing to mouse-only.    override func accessibilityRole() -> NSAccessibility.Role? {        self.onClick == nil ? super.accessibilityRole() : .button    }     override func accessibilityPerformPress() -> Bool {        guard let onClick = self.onClick else {            return super.accessibilityPerformPress()        }        onClick()        return true    }     required init(rootView: Content) {        self.highlightState = MenuCardHighlightState()        self.allowsMenuHighlight = false        self.containsInteractiveControls = false        self.interactiveRegionStore = nil        self.onClick = nil        super.init(rootView: rootView)    }     @available(*, unavailable)    required init?(coder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }     override func acceptsFirstMouse(for event: NSEvent?) -> Bool {        true    }     override func hitTest(_ point: NSPoint) -> NSView? {        let descendant = super.hitTest(point)        if let descendant {            var current: NSView? = descendant            while let view = current, view !== self {                if view is NSButton || view is NSControl {                    return descendant                }                current = view.superview            }            if self.hitsHostedInteractiveControl(at: point) {                return descendant            }            if descendant !== self, self.onClick != nil {                return self            }        }        return descendant    }     private func locationInView(for event: NSEvent) -> NSPoint {        guard self.window != nil else {            return event.locationInWindow        }        return self.convert(event.locationInWindow, from: nil)    }     override func mouseDown(with event: NSEvent) {        guard event.type == .leftMouseDown, self.onClick != nil else {            super.mouseDown(with: event)            return        }        let localPoint = self.locationInView(for: event)        if self.beginPrimaryPress(at: localPoint) {            #if DEBUG            self.testForwardedHostedControlMouseDown = true            #endif            super.mouseDown(with: event)        }    }     override func mouseUp(with event: NSEvent) {        guard event.type == .leftMouseUp, self.onClick != nil else {            super.mouseUp(with: event)            return        }        let result = self.endPrimaryPress(at: self.locationInView(for: event))        if result.forwardToHostedControl {            #if DEBUG            self.testForwardedHostedControlMouseUp = true            #endif            super.mouseUp(with: event)            return        }        if result.invokeRowAction {            self.onClick?()        }    }     /// Returns whether AppKit should forward the press into a nested SwiftUI control.    private func beginPrimaryPress(at point: NSPoint) -> Bool {        if self.hitsHostedInteractiveControl(at: point) {            self.isForwardingHostedControlPress = true            return true        }        self.isPressed = self.bounds.contains(point)        return false    }     private func endPrimaryPress(at point: NSPoint) -> (forwardToHostedControl: Bool, invokeRowAction: Bool) {        if self.isForwardingHostedControlPress {            self.isForwardingHostedControlPress = false            return (true, false)        }        defer { self.isPressed = false }        return (false, self.isPressed && self.bounds.contains(point))    }     private func hitsHostedInteractiveControl(at point: NSPoint) -> Bool {        self.containsInteractiveControls &&            (self.interactiveRegionStore?.contains(                point,                hostingBounds: self.bounds,                fittedSize: self.fittingSize) == true)    }     func measuredHeight(width: CGFloat) -> CGFloat {        self.frame = NSRect(origin: self.frame.origin, size: NSSize(width: width, height: 1))        self.layoutSubtreeIfNeeded()        return self.fittingSize.height    }     func setHighlighted(_ highlighted: Bool) {        guard self.highlightState.isHighlighted != highlighted else { return }        self.highlightState.isHighlighted = highlighted    }} @MainActorfinal class PersistentRefreshMenuView: NSView, MenuCardHighlighting {    private static let minimumShortcutColumnWidth: CGFloat = 44    private static let titleShortcutGap: CGFloat = 8    private static let shortcutReferenceText = "⌘ R"     private let selectionView = NSVisualEffectView()    private let iconView = NSImageView()    private let titleField: NSTextField    private let shortcutField: NSTextField?    private var isRowHighlighted = false    private var isRowEnabled = true    private var rowHeight = PersistentRefreshRowMetrics.defaults.rowHeight    private var onClick: (() -> Void)?     override var allowsVibrancy: Bool {        true    }     override var intrinsicContentSize: NSSize {        NSSize(width: self.frame.width, height: self.rowHeight)    }     init(        title: String,        systemImageName: String?,        shortcutText: String?,        onClick: (() -> Void)? = nil)    {        self.titleField = NSTextField(labelWithString: title)        self.shortcutField = shortcutText.map(NSTextField.init(labelWithString:))        self.onClick = onClick        super.init(frame: .zero)        self.setupSelectionView()        self.setupIconView(systemImageName: systemImageName)        self.setupTextFields()        if onClick != nil {            self.installClickRecognizer()        }        self.updateColors()    }     @available(*, unavailable)    required init?(coder _: NSCoder) {        fatalError("init(coder:) has not been implemented")    }     override func acceptsFirstMouse(for event: NSEvent?) -> Bool {        true    }     override func accessibilityRole() -> NSAccessibility.Role? {        self.onClick == nil ? super.accessibilityRole() : .button    }     override func accessibilityLabel() -> String? {        self.titleField.stringValue    }     override func isAccessibilityEnabled() -> Bool {        self.isRowEnabled    }     override func accessibilityPerformPress() -> Bool {        guard self.isRowEnabled else { return false }        guard let onClick = self.onClick else {            return super.accessibilityPerformPress()        }        onClick()        return true    }     func applySize(width: CGFloat, height: CGFloat) {        self.rowHeight = max(1, ceil(height))        self.frame = NSRect(origin: .zero, size: NSSize(width: width, height: self.rowHeight))        self.invalidateIntrinsicContentSize()        self.needsLayout = true    }     func setHighlighted(_ highlighted: Bool) {        guard self.isRowHighlighted != highlighted else { return }        self.isRowHighlighted = highlighted        self.selectionView.isHidden = !highlighted        self.updateColors()    }     func setEnabled(_ enabled: Bool) {        guard self.isRowEnabled != enabled else { return }        self.isRowEnabled = enabled        if !enabled {            self.isRowHighlighted = false            self.selectionView.isHidden = true        }        self.updateColors()    }     override func layout() {        super.layout()         let metrics = PersistentRefreshRowMetrics.defaults        self.selectionView.frame = self.bounds.insetBy(            dx: metrics.selectionHorizontalInset,            dy: metrics.selectionVerticalInset)        self.selectionView.layer?.cornerRadius = metrics.selectionCornerRadius         var leadingX = metrics.leadingPadding        if self.iconView.image != nil {            let iconSide = metrics.iconWidth            self.iconView.symbolConfiguration = Self.iconConfiguration(for: metrics)            self.iconView.frame = NSRect(                x: leadingX,                y: floor((self.bounds.height - iconSide) / 2),                width: iconSide,                height: iconSide)            leadingX += metrics.iconWidth + metrics.iconTitleSpacing        }         var titleMaxX = self.bounds.maxX - metrics.trailingPadding        if let shortcutField {            shortcutField.font = Self.shortcutFont(for: metrics)            let shortcutSize = shortcutField.intrinsicContentSize            let referenceWidth = Self.shortcutReferenceWidth(for: metrics)            let shortcutColumnWidth = max(Self.minimumShortcutColumnWidth, referenceWidth, shortcutSize.width)            let shortcutX = self.bounds.maxX                - metrics.trailingPadding                + metrics.shortcutXOffset                - referenceWidth            shortcutField.frame = NSRect(                x: shortcutX,                y: floor((self.bounds.height - shortcutSize.height) / 2) + metrics.shortcutYOffset,                width: shortcutColumnWidth,                height: shortcutSize.height)            titleMaxX = shortcutX - Self.titleShortcutGap        }         let titleSize = self.titleField.intrinsicContentSize        self.titleField.frame = NSRect(            x: leadingX,            y: floor((self.bounds.height - titleSize.height) / 2),            width: max(0, titleMaxX - leadingX),            height: titleSize.height)    }     private func setupSelectionView() {        self.selectionView.material = .selection        self.selectionView.blendingMode = .withinWindow        self.selectionView.state = .active        self.selectionView.isEmphasized = true        self.selectionView.isHidden = true        self.selectionView.wantsLayer = true        self.selectionView.layer?.masksToBounds = true        self.addSubview(self.selectionView)    }     private func setupIconView(systemImageName: String?) {        guard let systemImageName,              let baseImage = NSImage(systemSymbolName: systemImageName, accessibilityDescription: nil)        else {            self.iconView.isHidden = true            return        }         baseImage.isTemplate = true        self.iconView.image = baseImage        self.iconView.symbolConfiguration = Self.iconConfiguration(for: PersistentRefreshRowMetrics.defaults)        self.iconView.imageScaling = .scaleProportionallyDown        self.iconView.contentTintColor = .labelColor        self.addSubview(self.iconView)    }     private func setupTextFields() {        // Title truncates, shortcut clips; configuring them separately keeps the shortcut column stable.        self.titleField.font = NSFont.menuFont(ofSize: 0)        self.configureTitleField(self.titleField)         if let shortcutField {            shortcutField.font = Self.shortcutFont(for: PersistentRefreshRowMetrics.defaults)            self.configureShortcutField(shortcutField)        }

Lines 1–500 of 685.