import AppKitimport CodexBarCoreimport Observationimport SwiftUI extension StatusItemController { func switcherWeeklyRemaining(for provider: UsageProvider) -> Double? { Self.switcherWeeklyMetricPercent( for: provider, snapshot: self.store.snapshot(for: provider), showUsed: self.settings.usageBarsShowUsed) } 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 { func setHighlighted(_ highlighted: Bool)} @MainActorprotocol MenuCardMeasuring: AnyObject { func measuredHeight(width: CGFloat) -> CGFloat} @MainActor@Observablefinal class MenuCardHighlightState { var isHighlighted = false} final class MenuHostingView<Content: View>: NSHostingView<Content> { override var allowsVibrancy: Bool { true }} @MainActorfinal class MenuCardItemHostingView<Content: View>: NSHostingView<Content>, MenuCardHighlighting, MenuCardMeasuring { private let highlightState: MenuCardHighlightState private let onClick: (() -> Void)? 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, onClick: (() -> Void)? = nil) { self.highlightState = highlightState self.onClick = onClick super.init(rootView: rootView) if onClick != nil { let recognizer = NSClickGestureRecognizer(target: self, action: #selector(self.handlePrimaryClick(_:))) recognizer.buttonMask = 0x1 self.addGestureRecognizer(recognizer) } } required init(rootView: Content) { self.highlightState = MenuCardHighlightState() 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 } @objc private func handlePrimaryClick(_ recognizer: NSClickGestureRecognizer) { guard recognizer.state == .ended else { return } self.onClick?() } func measuredHeight(width: CGFloat) -> CGFloat { let controller = NSHostingController(rootView: self.rootView) let measured = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude)) return measured.height } func setHighlighted(_ highlighted: Bool) { guard self.highlightState.isHighlighted != highlighted else { return } self.highlightState.isHighlighted = highlighted }} struct MenuCardSectionContainerView<Content: View>: View { @Bindable var highlightState: MenuCardHighlightState let showsSubmenuIndicator: Bool let submenuIndicatorAlignment: Alignment let submenuIndicatorTopPadding: CGFloat @ViewBuilder let content: () -> Content var body: some View { self.content() .environment(\.menuItemHighlighted, self.highlightState.isHighlighted) .foregroundStyle(MenuHighlightStyle.primary(self.highlightState.isHighlighted)) .background(alignment: .topLeading) { if self.highlightState.isHighlighted { RoundedRectangle(cornerRadius: 6, style: .continuous) .fill(MenuHighlightStyle.selectionBackground(true)) .padding(.horizontal, 6) .padding(.vertical, 2) } } .overlay(alignment: self.submenuIndicatorAlignment) { if self.showsSubmenuIndicator { Image(systemName: "chevron.right") .font(.caption2.weight(.semibold)) .foregroundStyle(MenuHighlightStyle.secondary(self.highlightState.isHighlighted)) .padding(.top, self.submenuIndicatorTopPadding) .padding(.trailing, 10) } } }}
import AppKit
import CodexBarCore
import Observation
import SwiftUI
extension StatusItemController {
func switcherWeeklyRemaining(for provider: UsageProvider) -> Double? {
Self.switcherWeeklyMetricPercent(
for: provider,
snapshot: self.store.snapshot(for: provider),
showUsed: self.settings.usageBarsShowUsed)
}
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
}
}
@MainActor
protocol MenuCardHighlighting: AnyObject {
func setHighlighted(_ highlighted: Bool)
}
@MainActor
protocol MenuCardMeasuring: AnyObject {
func measuredHeight(width: CGFloat) -> CGFloat
}
@MainActor
@Observable
final class MenuCardHighlightState {
var isHighlighted = false
}
final class MenuHostingView<Content: View>: NSHostingView<Content> {
override var allowsVibrancy: Bool {
true
}
}
@MainActor
final class MenuCardItemHostingView<Content: View>: NSHostingView<Content>, MenuCardHighlighting, MenuCardMeasuring {
private let highlightState: MenuCardHighlightState
private let onClick: (() -> Void)?
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, onClick: (() -> Void)? = nil) {
self.highlightState = highlightState
self.onClick = onClick
super.init(rootView: rootView)
if onClick != nil {
let recognizer = NSClickGestureRecognizer(target: self, action: #selector(self.handlePrimaryClick(_:)))
recognizer.buttonMask = 0x1
self.addGestureRecognizer(recognizer)
}
}
required init(rootView: Content) {
self.highlightState = MenuCardHighlightState()
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
}
@objc private func handlePrimaryClick(_ recognizer: NSClickGestureRecognizer) {
guard recognizer.state == .ended else { return }
self.onClick?()
}
func measuredHeight(width: CGFloat) -> CGFloat {
let controller = NSHostingController(rootView: self.rootView)
let measured = controller.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
return measured.height
}
func setHighlighted(_ highlighted: Bool) {
guard self.highlightState.isHighlighted != highlighted else { return }
self.highlightState.isHighlighted = highlighted
}
}
struct MenuCardSectionContainerView<Content: View>: View {
@Bindable var highlightState: MenuCardHighlightState
let showsSubmenuIndicator: Bool
let submenuIndicatorAlignment: Alignment
let submenuIndicatorTopPadding: CGFloat
@ViewBuilder let content: () -> Content
var body: some View {
self.content()
.environment(\.menuItemHighlighted, self.highlightState.isHighlighted)
.foregroundStyle(MenuHighlightStyle.primary(self.highlightState.isHighlighted))
.background(alignment: .topLeading) {
if self.highlightState.isHighlighted {
RoundedRectangle(cornerRadius: 6, style: .continuous)
.fill(MenuHighlightStyle.selectionBackground(true))
.padding(.horizontal, 6)
.padding(.vertical, 2)
}
}
.overlay(alignment: self.submenuIndicatorAlignment) {
if self.showsSubmenuIndicator {
Image(systemName: "chevron.right")
.font(.caption2.weight(.semibold))
.foregroundStyle(MenuHighlightStyle.secondary(self.highlightState.isHighlighted))
.padding(.top, self.submenuIndicatorTopPadding)
.padding(.trailing, 10)
}
}
}
}