aboutsummaryrefslogtreecommitdiff
path: root/menubar/panel.swift
diff options
context:
space:
mode:
Diffstat (limited to 'menubar/panel.swift')
-rw-r--r--menubar/panel.swift59
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")