Repository navigation
Listbox
A list of rich items that can hold HTML and live widgets, with selection, actions, a filter, and drag-and-drop within and between lists. It is the column type of the planned Kanban. Added in #45.
from wapyt import ListAction, Listbox, ListboxConfig, ProgressBar, ProgressBarConfig
todo = Listbox(ListboxConfig(
label="To do", list_id="todo", group="board", draggable=True, filterable=True,
items=[
{"id": "t1", "title": "Write spec", "subtitle": "Due Friday", "badge": 2},
{"id": "t2", "title": "Review PR", "meta": "#45"},
],
actions=[ListAction("edit", "Edit", "mdi-pencil"),
ListAction("delete", "Delete", "mdi-delete", danger=True)],
), container=self.layout.getCell("todo"))
# A live widget inside an item
ProgressBar(ProgressBarConfig(value=40, compact=True), container=todo.item_body("t1"))
def moved(e): # fires on the list the item lands in
if not api.move(e["id"], e["to_list"], e["to_index"]):
lists[e["to_list"]].move_item(e["id"], e["from_index"], to=lists[e["from_list"]])
todo.on_move(moved)
todo.on_action(lambda e: open_editor(e["id"]) if e["action"] == "edit" else None)Mount it with container= (an element or a layout cell), root=, or
Layout.add_listbox(id, config).
Items are dicts with an id (id_field, default "id"). Without a template
these keys are drawn: title, subtitle, meta, icon (MDI class),
badge, and disabled (shown faded, not selectable or draggable). Any
other keys are kept and come back in events.
A template draws the item from HTML with {key} placeholders:
ListboxConfig(template='<b>{title}</b> <span class="note">{notesHtml}</span>', items=[
{"id": "x1", "title": "Ship <MenuBar>", "notesHtml": "<em>done</em> in #44"},
])Values are escaped, so data cannot inject markup. A key ending in Html
is inserted as-is, for HTML your app built and escaped itself (the same rule
as ResourceBoard).
item_body(id) returns an element inside the item to mount any wapyt widget
into (a ProgressBar, a Toolbar, a small
Form) or your own elements. The body is never redrawn: the widgets
stay alive through update_item and through drags, even into another list.
Buttons, inputs, links and anything else interactive inside an item keep
working: pressing them never selects the item or starts a drag. Add the class
wapyt-listbox-nodrag to any other element that should behave the same way.
ListAction(id, label, icon, danger) puts icon buttons on every item (shown
on hover and keyboard focus, always on touch screens); they fire on_action.
-
draggable=Truelets items be dragged to reorder.drag_handle=Truedrags only by a grip on the left. - Lists with the same
groupexchange items.accept=Falserefuses drops from others;copy=Truecopies items out and leaves them in place (a template library). A copied item keeps its id; ids only need to be unique within one list. - Mouse and touch: a drag starts after a few pixels of movement. A placeholder shows where the item will land, the list scrolls near its edges, and Escape cancels.
- Keyboard: Space picks the focused item up (Ctrl+Space then selects instead), ↑ / ↓ move it, ← / → move it to the neighbouring list of the group, Space or Enter drops it, Escape puts it back. Each step is announced to screen readers.
-
The app has the final say. A drop happens at once and
on_movefires on the list the item lands in. If your server refuses, put it back withmove_item, which fires nothing. - Dragging pauses while the filter box has text.
ListboxConfig |
|
|---|---|
items, id_field
|
The items and their id key |
template |
HTML for each item's content; see above |
selection |
"none", "single" (default) or "multi" (Ctrl / Shift clicks) |
draggable, drag_handle
|
Enable dragging; drag by a grip only |
group, list_id
|
Lists that exchange items; this list's name in on_move (defaults to label) |
accept, copy
|
Take drops from the group (default on); copy items out instead of moving them |
actions |
ListAction buttons on each item |
filterable, filter_placeholder
|
A filter box above the list |
empty_text, label
|
Empty state; accessible name |
| Event | Payload |
|---|---|
on_select |
{ids, id, items}; id is set when exactly one is selected |
on_activate |
{id, item} on double-click or Enter |
on_action |
{action, id, item} |
on_move |
{id, item, from_list, to_list, from_index, to_index, copy} |
on_drag_start |
{id, item} |
Methods: set_items, get_items (current order), get_item,
add_item(item, index), update_item(id, fields) (keeps body widgets),
remove_item, item_body(id), move_item(id, index, to=None),
get_selected_ids, select, clear_selection, set_filter,
destroy.
One tab stop per list. ↑ / ↓ / Home / End move between items, Enter
activates, Space selects (or picks up when draggable). Tab moves into the
focused item's action buttons.
Virtual rendering for very long lists (thousands of items).
wapyt is MIT-licensed · Repository · pyTincture · Issues
Getting started
Widgets
- Layout and MainWindow
- Tree
- DataTable
- Form
- ModalWindow
- Window
- TabWidget
- Toolbar
- Sidebar
- Chat
- Terminal
- CardPanel
- ResourceBoard
- File Transfer
- Message
- ContextMenu
- MenuBar
- Listbox
- Kanban
- ProgressBar
- Pagination
- Popup and tooltips
- Chart
- Scheduler
- MediaPlayer
Guides
Planning
Contributing