Two keys on a container widget report what arrived and what left, through the :on(key, fn) every widget answers, instead of polling widget:items(). Subscribing is unprotected.
local function label(item) return item:name() or item:res() or "?" end
hafen.session():current():ui():on("window[title=Chest]", "Added", function(chest)
chest:on("ItemAdded", function(item) hafen.log():write("in: " .. label(item)) end)
chest:on("ItemRemoved", function(item) hafen.log():write("out: " .. label(item)) end)
chest:on("Removed", function() hafen.log():write("chest closed") end)
end)
Two chests can be open at once, so take each as it opens rather than naming it from the root.
| Key | Handler receives | Cancelable | Fires |
|---|---|---|---|
ItemAdded |
Item |
No | An item enters this container. |
ItemRemoved |
Item |
No | An item leaves it. |
Removed |
— | No | This widget leaves the tree (universal to any widget, stated here because a container closing is the usual reason to hold one). |
| Rule | Detail |
|---|---|
| The subscription is the registration | A container nobody subscribed to is watched for nothing. Dropping the last ItemAdded/ItemRemoved subscription stops the watching. |
| A widget create or destroy | An item entering or leaving is seen the moment the client puts its icon into the tree or takes it out. It is not seen on a server message. |
| Seeding | The items already inside fire ItemAdded while you subscribe, before :on returns. That is the top-level items alone, exactly what widget:items() answers, never what is inside them. |
| Hidden containers still fire | Which is why a hidden grid still reads. |
| A widget that has left the tree | Subscribing to any of these keys fires Removed there and then and drops every subscription on it. |
The ItemRemoved payload |
The same object the add reported, and it answers after it has left — a stale item. |
| Worn equipment | Also has the global EquipChanged event, carrying the whole new list. |
:items()The keys answer what entered this container, not what it draws — the one place the read and the events differ.
| Rule | Detail |
|---|---|
| Any depth | An item dropped into a stack or a creel this container holds fires here, since it did arrive in your inventory. widget:items() stays as shallow as ever, a stack being one cell. item:container() places the item a handler was handed. |
| Only the outermost thing that moved | A stack arriving with three dandelions inside fires ItemAdded once, for the stack. A dandelion dropped into a stack already there fires its own, the stack not having moved. |
| A contained item | Reported only when it later moves on its own. It never appears in the seeding read. |