diff options
Diffstat (limited to 'menubar/panel.swift')
| -rw-r--r-- | menubar/panel.swift | 59 |
1 files changed, 55 insertions, 4 deletions
diff --git a/menubar/panel.swift b/menubar/panel.swift index 6619dc0..85cc81e 100644 --- a/menubar/panel.swift +++ b/menubar/panel.swift @@ -265,13 +265,37 @@ private func singleLine(_ label: NSTextField) -> NSTextField { return label } -/// One agent row: colored symbol and name, then a dim subtitle. +/// A menu item that runs a closure. +final class ActionMenuItem: NSMenuItem { + private let handler: () -> Void + + init(_ title: String, handler: @escaping () -> Void) { + self.handler = handler + super.init(title: title, action: #selector(run), keyEquivalent: "") + target = self + } + + required init(coder: NSCoder) { fatalError("not used") } + + @objc private func run() { handler() } +} + +/// One agent row: colored symbol and name, then a dim subtitle. Right-click +/// offers "Mark as unread" for an idle agent. final class PanelRowView: ClickView { private let selected: Bool + private let contextMenu: NSMenu? init(row: Row, title titleText: NSAttributedString? = nil, selected: Bool = false, - onClick: @escaping (String) -> Void) { + onClick: @escaping (String) -> Void, onMarkUnread: ((String) -> Void)? = nil) { self.selected = selected + if let onMarkUnread, row.canMarkUnread { + let menu = NSMenu() + menu.addItem(ActionMenuItem("Mark as unread") { onMarkUnread(row.paneId) }) + contextMenu = menu + } else { + contextMenu = nil + } super.init(frame: .zero) self.onClick = { onClick(row.paneId) } let title = singleLine(NSTextField( @@ -299,6 +323,8 @@ final class PanelRowView: ClickView { required init?(coder: NSCoder) { fatalError("not used") } + override func menu(for event: NSEvent) -> NSMenu? { contextMenu } + override func draw(_ dirtyRect: NSRect) { guard hovered || selected else { return } NSColor.white.withAlphaComponent(selected ? 0.14 : 0.08).setFill() @@ -372,14 +398,17 @@ final class PanelController { private let panel: NSPanel private let stack = NSStackView() private let onClick: (String) -> Void + private let onMarkUnread: (String) -> Void private let onToggleFull: () -> Void private let onRestore: () -> Void private let onDismissRestore: () -> Void private var rendered: String? - init(persistFrame: Bool = true, onClick: @escaping (String) -> Void, onToggleFull: @escaping () -> Void, + init(persistFrame: Bool = true, onClick: @escaping (String) -> Void, + onMarkUnread: @escaping (String) -> Void = { _ in }, onToggleFull: @escaping () -> Void, onRestore: @escaping () -> Void = {}, onDismissRestore: @escaping () -> Void = {}) { self.onClick = onClick + self.onMarkUnread = onMarkUnread self.onToggleFull = onToggleFull self.onRestore = onRestore self.onDismissRestore = onDismissRestore @@ -438,7 +467,7 @@ final class PanelController { } for (index, row) in content.rows.enumerated() { if index == content.separatorBefore { add(separatorLine()) } - add(PanelRowView(row: row, onClick: onClick), fullWidth: true) + add(PanelRowView(row: row, onClick: onClick, onMarkUnread: onMarkUnread), fullWidth: true) } if content.overflow > 0 { add(caption(NSAttributedString( @@ -582,6 +611,28 @@ func panelSelftest() -> [String] { let t0 = Date(timeIntervalSince1970: 1_000) func at(_ seconds: TimeInterval) -> Date { t0.addingTimeInterval(seconds) } + // Right-click: "Mark as unread" for idle agents only. + func agent(_ status: Status) -> Row { + Row(paneId: "%7", agent: "pi", name: "n", status: status, session: "s", window: "w", since: 1, path: "") + } + if let click = NSEvent.mouseEvent(with: .rightMouseDown, location: .zero, modifierFlags: [], timestamp: 0, + windowNumber: 0, context: nil, eventNumber: 0, clickCount: 1, pressure: 1) { + var marked: [String] = [] + let idleMenu = PanelRowView(row: agent(.idle), onClick: { _ in }, + onMarkUnread: { marked.append($0) }).menu(for: click) + check(idleMenu?.items.map(\.title) == ["Mark as unread"], "right-click: an idle agent offers Mark as unread") + if let item = idleMenu?.items.first, let target = item.target, let action = item.action { + _ = target.perform(action, with: item) + } + check(marked == ["%7"], "right-click: Mark as unread marks that agent") + for status in [Status.done, .working, .blocked] { + check(PanelRowView(row: agent(status), onClick: { _ in }, onMarkUnread: { _ in }).menu(for: click) == nil, + "right-click: nothing to offer for a \(status.rawValue) agent") + } + } else { + check(false, "right-click: could not make an event") + } + var panel = PanelVisibility() check(panel.enabled && panel.policy == .whenWaiting, "defaults: on, only when agents are waiting") check(!panel.visible(at: t0), "waiting policy: hidden with nothing waiting") |
