initial: inventory-list-display v0.1.0 — vertical list widget on lib-core.panel
5 public functions (create, register_action, unregister_action, set_label_resolver, set_icon_resolver) plus 2 test backdoors (_test_get_rows, _test_resolve_icon). Reads lib-core.inventory-list contents each render frame; right-click rows open context-menu via panel.show_context_menu. Default action-set is empty.
This commit is contained in:
224
README.md
Normal file
224
README.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# lib-core.inventory-list-display
|
||||
|
||||
Inventory vertical-list widget. Reads a composition list-container via
|
||||
`lib-core.inventory-list` and renders its contents as a panel widget
|
||||
managed by `lib-core.panel`. Provides icon + label + count rows with
|
||||
right-click context-menu support.
|
||||
|
||||
**Version:** 0.1.0
|
||||
**Lib-ID:** lib-core.inventory-list-display
|
||||
**Requires:** lib-core.panel v0.1.0, lib-core.inventory-list v0.1.0, lib-core.composition v0.2.0
|
||||
**Tags:** inventory, ui, panel, widget, list
|
||||
|
||||
## Topology
|
||||
|
||||
<!-- topology:start (auto-generated; do not edit) -->
|
||||
```mermaid
|
||||
graph LR
|
||||
this["lib-core.inventory-list-display"]
|
||||
panel["lib-core.panel"]
|
||||
inv["lib-core.inventory-list"]
|
||||
comp["lib-core.composition"]
|
||||
this --> panel
|
||||
this --> inv
|
||||
this --> comp
|
||||
```
|
||||
<!-- topology:end -->
|
||||
|
||||
## Scope (v0.1.0)
|
||||
|
||||
v0.1 ships a minimal vertical-list inventory widget:
|
||||
|
||||
- `create(container_entity, opts)` — builds a panel-compatible widget_def
|
||||
- `register_action` / `unregister_action` — context-menu actions (right-click on row)
|
||||
- `set_label_resolver` / `set_icon_resolver` — override default row content
|
||||
- Render: icon (Atlas-UV via `draw_sprite_transform`) + label + "×1" count per row
|
||||
- Input: right-click row opens context-menu via `panel.show_context_menu`
|
||||
|
||||
**Intentional non-goals (deferred):**
|
||||
- Row scrolling (wheel event silently ignored)
|
||||
- Stack-count display (`stack_mode="stack"` not supported in v0.1)
|
||||
- Pretty item names (no `composition.template_of` accessor yet; override via `set_label_resolver`)
|
||||
- Row selection highlight
|
||||
- Multi-column layout
|
||||
|
||||
## API
|
||||
|
||||
### `display.create(container_entity, opts)`
|
||||
|
||||
**Syntax:** `display.create(container_entity: entity, opts: table|nil) -> widget_def`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
local display = require("lib-core.inventory-list-display")
|
||||
local panel = require("lib-core.panel")
|
||||
|
||||
local widget = display.create(backpack_entity, {
|
||||
title = "Backpack",
|
||||
widget_id = "backpack",
|
||||
pause_on_open = true,
|
||||
})
|
||||
panel.register(widget.widget_id, widget)
|
||||
panel.bind_default_trigger("i", widget.widget_id)
|
||||
```
|
||||
|
||||
Creates a widget_def bound to `container_entity`. `opts` keys:
|
||||
- `title` (string, default `"Inventory"`) — panel title bar text
|
||||
- `widget_id` (string, default `"inventory"`) — key for `panel.register`
|
||||
- `pause_on_open` (bool, default `false`) — passed to panel for `is_pausing()`
|
||||
- `icon_resolver` (function) — overrides default icon resolver at creation time
|
||||
- `label_resolver` (function) — overrides default label resolver at creation time
|
||||
|
||||
Loud-error if `container_entity` is `nil` or has no container block with `kind="list"`.
|
||||
|
||||
---
|
||||
|
||||
### `display.register_action(widget_def, label, callback)`
|
||||
|
||||
**Syntax:** `display.register_action(widget_def: table, label: string, callback: function) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
display.register_action(widget, "Drop", function(item, ctx)
|
||||
inventory.remove(ctx.container, item)
|
||||
ctx.close_menu()
|
||||
end)
|
||||
```
|
||||
|
||||
Registers a context-menu action shown on right-click of any row. `callback` receives
|
||||
`(item, context)` where `context = { container, close_menu, refresh }`.
|
||||
|
||||
Loud-error on duplicate `label` or if `callback` is not a function.
|
||||
The default action-set is empty — all actions must be registered explicitly.
|
||||
|
||||
---
|
||||
|
||||
### `display.unregister_action(widget_def, label)`
|
||||
|
||||
**Syntax:** `display.unregister_action(widget_def: table, label: string) -> void`
|
||||
|
||||
Removes a context-menu action. Idempotent: no error if `label` was never registered.
|
||||
|
||||
---
|
||||
|
||||
### `display.set_label_resolver(widget_def, fn)`
|
||||
|
||||
**Syntax:** `display.set_label_resolver(widget_def: table, fn: function) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
display.set_label_resolver(widget, function(item)
|
||||
return item:get_property("display_name") or "Unknown"
|
||||
end)
|
||||
```
|
||||
|
||||
Replaces the label resolver. `fn(item) -> string`. Called each render frame per row.
|
||||
Default returns `"Item"` for all items (no `composition.template_of` in v0.1).
|
||||
Loud-error if `fn` is not a function.
|
||||
|
||||
---
|
||||
|
||||
### `display.set_icon_resolver(widget_def, fn)`
|
||||
|
||||
**Syntax:** `display.set_icon_resolver(widget_def: table, fn: function) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
display.set_icon_resolver(widget, function(item)
|
||||
local atlas = item:get_property("sprite_atlas")
|
||||
if not atlas then return nil end
|
||||
return {
|
||||
atlas = atlas,
|
||||
uv = { x=0, y=0, w=16, h=16 },
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
Replaces the icon resolver. `fn(item) -> {atlas=string, uv={x,y,w,h}} or nil`.
|
||||
Default reads `sprite_atlas` and `sprite_uv.*` directly from item properties.
|
||||
Loud-error if `fn` is not a function.
|
||||
|
||||
---
|
||||
|
||||
### Test backdoors
|
||||
|
||||
- `display._test_get_rows(widget_def)` — returns `inventory.contents(container)` directly
|
||||
- `display._test_resolve_icon(widget_def, item)` — invokes the current icon resolver
|
||||
|
||||
Both are for test modules only and should not be called in production code.
|
||||
|
||||
## Row Layout
|
||||
|
||||
```
|
||||
ctx.bounds.x
|
||||
|
|
||||
+--+----------------------------------+------+
|
||||
| | Label ×1 | |
|
||||
|32| | | row_height (24px)
|
||||
+--+----------------------------------+------+
|
||||
^36px ^right-aligned (measure_text + padding)
|
||||
icon (32×32 or placeholder rect + "?")
|
||||
```
|
||||
|
||||
- Icon column: 32 px wide, left-aligned at `ctx.bounds.x`
|
||||
- Label: at `ctx.bounds.x + 36`, `row_y + 4`
|
||||
- Count ("×1"): right-aligned using `measure_text`; `ctx.bounds.x + ctx.bounds.w - text_w - padding`
|
||||
- Row height: `ctx.theme.row_height` (default 24 px from panel theme)
|
||||
|
||||
## Item-Template-Convention
|
||||
|
||||
Items displayed by this widget must satisfy the inventory-list contract:
|
||||
|
||||
| Property | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `stack_mode` | yes (`"individual"`) | inventory-list requirement |
|
||||
| `sprite_atlas` | recommended | path to atlas PNG (default icon resolver) |
|
||||
| `sprite_uv.x/y/w/h` | recommended | UV rect within atlas (default icon resolver) |
|
||||
| `position` | yes | world anchor (inventory-list requirement) |
|
||||
|
||||
Tags: `{"renderable", "item"}` (renderable tag removed on add, restored on remove).
|
||||
|
||||
If `sprite_atlas` is absent or empty, the default icon resolver returns `nil` and a
|
||||
placeholder rect + "?" glyph is drawn instead.
|
||||
|
||||
## Default Action-Set
|
||||
|
||||
The default action-set is **empty**. No "Drop", "Use", or "Inspect" actions are
|
||||
registered automatically. Consuming modules must call `display.register_action` to
|
||||
populate the right-click context-menu.
|
||||
|
||||
## Glue-Pattern
|
||||
|
||||
Minimal module setup:
|
||||
|
||||
```lua
|
||||
local display = require("lib-core.inventory-list-display")
|
||||
local panel = require("lib-core.panel")
|
||||
local inventory = require("lib-core.inventory-list")
|
||||
|
||||
-- 1. Create widget (assumes backpack_entity is a list-container)
|
||||
local widget = display.create(backpack_entity, {
|
||||
title = "Backpack",
|
||||
widget_id = "backpack",
|
||||
pause_on_open = true,
|
||||
})
|
||||
|
||||
-- 2. Register actions
|
||||
display.register_action(widget, "Drop", function(item, ctx)
|
||||
inventory.remove(ctx.container, item)
|
||||
ctx.close_menu()
|
||||
end)
|
||||
|
||||
-- 3. Override label if template names are available
|
||||
display.set_label_resolver(widget, function(item)
|
||||
return item:get_property("display_name") or "Item"
|
||||
end)
|
||||
|
||||
-- 4. Register with panel + bind toggle key
|
||||
panel.register(widget.widget_id, widget)
|
||||
panel.bind_default_trigger("i", widget.widget_id)
|
||||
|
||||
-- 5. Wire into update + render
|
||||
function M.update(dt) panel.update(dt) end
|
||||
function M.render() panel.render() end
|
||||
```
|
||||
Reference in New Issue
Block a user