tray
StatusNotifierItem: registered tray items with artwork, status and menus.
list {
direction = "horizontal",
spacing = 4,
items = mantle.tray:map(function(tray)
return tray.items
end),
key = function(item) return item.id end,
each = function(item)
return rect {
on_click = function(_, which)
if which == "left" and not item.item_is_menu then
mantle.tray:activate(item.id, 0, 0) -- screen x, y; most apps ignore them
end
end,
children = { icon { name = item.icon_name or item.icon_path or "", size = 16 } },
}
end,
}
State
mantle.tray:get() returns TrayState, nil before the first push. A field marked ? may be absent.
| Field | Type | Description |
|---|---|---|
items | TrayItem[] | Registered items in registration order, oldest first; updates never reorder them. |
MenuItem
One tray.items[].menu entry.
| Field | Type | Description |
|---|---|---|
children | MenuItem[] | Submenu entries, empty for a leaf. An app that fills submenus lazily sends them only after menu_will_show. |
enabled | boolean | false for a greyed-out entry; draw it, but clicking does nothing. |
icon_name? | string | Theme icon name, a spooled PNG path from raw icon-data, or nil. |
id | integer | DBusMenu id, the second argument of activate_menu_item and menu_will_show. |
label? | string | Entry text as sent, or nil. _ mnemonic markers remain ("_Quit"); strip them to draw. |
menu_type | MenuType | |
toggle_state? | integer | 0 off, 1 on, -1 indeterminate or unreported; nil exactly when toggle_type is. |
toggle_type? | ToggleType | nil for an entry that is not a toggle. |
MenuType
tray.items[].menu[].menu_type.
| Value | Description |
|---|---|
"standard" | An ordinary entry; also any type outside the spec. |
"separator" | A divider line; it has no label and does nothing. |
ToggleType
tray.items[].menu[].toggle_type.
| Value | Description |
|---|---|
"checkmark" | A check box. |
"radio" | One of a group of mutually exclusive entries. |
TrayItem
| Field | Type | Description |
|---|---|---|
attention_icon_name? | string | Artwork to draw while status == "needs_attention", paired with attention_icon_path like the base icon; both nil when unset. |
attention_icon_path? | string | File half of the attention artwork. |
icon_name? | string | Theme icon name for icon { name = ... }. At most one of it and icon_path is set. |
icon_path? | string | Icon file for image { source = ... }: one from the item’s IconThemePath, or its pixmap spooled to a PNG. |
id | string | Item identity for every tray action, e.g. "1.234/StatusNotifierItem". Opaque. |
item_is_menu | boolean | Left click should open menu instead of activate. |
menu? | MenuItem[] | Top-level menu entries, or nil when the item exports no DBusMenu or its first fetch failed. |
name | string | SNI Title, or its Id when the title is empty. |
overlay_icon_name? | string | Badge to draw over the icon’s corner, paired with overlay_icon_path; both nil when unset. |
overlay_icon_path? | string | File half of the badge. |
status | TrayStatus | SNI Status. |
tooltip? | string | Tooltip title and text joined by a newline, or nil when both are empty. |
TrayStatus
SNI Status.
| Value | Description |
|---|---|
"active" | Normal; also any value outside the spec, or none. |
"passive" | The item asks to be hidden. |
"needs_attention" | The item wants attention; draw its attention artwork if set. |
Actions
Call each as mantle.tray:<action>(arguments...); ? marks an argument you may omit.
| Action | Arguments | Description |
|---|---|---|
activate | id: string, x: integer, y: integer | Left-click activation at screen coordinates x, y; a no-op when item_is_menu. |
context_menu | id: string, x: integer, y: integer | Context-menu activation at screen coordinates x, y. |
secondary_activate | id: string, x: integer, y: integer | Middle-click activation at screen coordinates x, y. |
scroll | id: string, delta: integer, orientation: ScrollOrientation | Scrolls the icon by delta along orientation. |
activate_menu_item | id: string, menu_item_id: integer | Clicks the item’s MenuItem.id. |
menu_will_show | id: string, submenu_id: integer | Tells the application submenu submenu_id is opening, then refetches the menu unless it answers that nothing changed. |
ScrollOrientation
The tray:scroll axis, sent to the item as spelled.
One of "vertical", "horizontal".
Backend
Mantle hosts org.kde.StatusNotifierWatcher at /StatusNotifierWatcher on the session bus and
registers itself as a host.
| Contract | Behavior |
|---|---|
| Name | Requested without DoNotQueue: if another watcher owns it, Mantle queues behind it |
| Adoption | At start, adopts items already on the bus at /StatusNotifierItem, /StatusNotifierItem/1 or /org/chromium/StatusNotifierItem/1, for apps that never re-register |
| Removal | An item leaves, and its spooled PNGs are deleted, when its bus name loses its owner |
| Icon | IconName found in the item’s IconThemePath, then IconName as a theme name, then the largest valid pixmap: square, 1 to 128 px, exactly w × h × 4 ARGB bytes, spooled as a PNG under tray/ |
| Bounds | Strings 256 bytes; menus 1024 nodes, depth 32 |
| Menus | com.canonical.dbusmenu. Drops items with visible = false. Spools raw PNG icon-data (up to 512 KB) to shared memory as icon_name. menu_will_show sends AboutToShow, activate_menu_item sends Event("clicked") |
Gotchas
| Trap | Fix |
|---|---|
activate does nothing on some items | The item set item_is_menu, and Mantle skips Activate for it. Open menu on left click |
| A dynamic menu or submenu is empty | Some apps fill menus only after AboutToShow. Send menu_will_show(item.id, 0) when opening the root menu, and with submenu_id when expanding a submenu |
| Right-clicking an item with no menu does nothing | The app provides no DBusMenu. Call mantle.tray:context_menu(item.id, x, y) on right click to trigger its native popup |
See also: System tray with menu recipe.