From c57f6b90259c8193e3aa9c3453a02b4043873381 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Mon, 21 Nov 2022 16:42:53 +0000 Subject: [PATCH] Fix click handler --- docs/examples/widgets/list_view.css | 2 +- docs/widgets/list_item.md | 40 +++++++++++++++++++++++++++++ docs/widgets/list_view.md | 18 ++++++++++++- src/textual/widgets/_list_item.py | 6 +++-- src/textual/widgets/_list_view.py | 17 +++++++++++- 5 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 docs/widgets/list_item.md diff --git a/docs/examples/widgets/list_view.css b/docs/examples/widgets/list_view.css index 9f85bc875..5becec1cc 100644 --- a/docs/examples/widgets/list_view.css +++ b/docs/examples/widgets/list_view.css @@ -4,7 +4,7 @@ Screen { ListView { width: 30; - height: auto; + height: 3; margin: 2 2; } diff --git a/docs/widgets/list_item.md b/docs/widgets/list_item.md new file mode 100644 index 000000000..6b11a5bc2 --- /dev/null +++ b/docs/widgets/list_item.md @@ -0,0 +1,40 @@ +# List Item + +`ListItem` is the type of the elements in a `ListView`. + +- [] Focusable +- [] Container + +## Example + +The example below shows an app with a simple `ListView`, consisting +of multiple `ListItem`s. The arrow keys can be used to navigate the list. + +=== "Output" + + ```{.textual path="docs/examples/widgets/list_view.py"} + ``` + +=== "list_view.py" + + ```python + --8<-- "docs/examples/widgets/list_view.py" + ``` + +## Reactive Attributes + +| Name | Type | Default | Description | +|---------------|--------|---------|--------------------------------------| +| `highlighted` | `bool` | `False` | True if this ListItem is highlighted | + +## Messages + +#### Attributes + +| attribute | type | purpose | +|-----------|------------|-----------------------------| +| `item` | `ListItem` | The item that was selected. | + +## See Also + +* [ListView](../api/list_view.md) code reference diff --git a/docs/widgets/list_view.md b/docs/widgets/list_view.md index 8ed64bddb..ef4aef32a 100644 --- a/docs/widgets/list_view.md +++ b/docs/widgets/list_view.md @@ -1,6 +1,6 @@ # List View -Displays a vertical list of widgets which can be highlighted and selected. +Displays a vertical list of `ListItem`s which can be highlighted and selected. Supports keyboard navigation. - [x] Focusable @@ -57,6 +57,22 @@ or by clicking on it. |-----------|------------|-----------------------------| | `item` | `ListItem` | The item that was selected. | + +### ChildrenUpdated + +The `ListView.ChildrenUpdated` message is emitted when the elements in the `ListView` +are changed (e.g. a child is added, or the list is cleared) + +- [x] Bubbles + +#### Attributes + +| attribute | type | purpose | +|------------|------------------|---------------------------| +| `children` | `list[ListItem]` | The new ListView children | + + + ## See Also * [ListView](../api/list_view.md) code reference diff --git a/src/textual/widgets/_list_item.py b/src/textual/widgets/_list_item.py index 0d4cee285..8788a1ed4 100644 --- a/src/textual/widgets/_list_item.py +++ b/src/textual/widgets/_list_item.py @@ -28,10 +28,12 @@ class ListItem(Widget, can_focus=False): highlighted = reactive(False) def on_click(self, event: events.Click) -> None: - self.emit_no_wait(self.ChildSelected(self)) + self.emit_no_wait(self._ChildClicked(self)) def watch_highlighted(self, value: bool) -> None: self.set_class(value, "--highlight") - class ChildSelected(Message): + class _ChildClicked(Message): + """For informing with the parent ListView that we were clicked""" + pass diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index 7a701c208..e84e48ea6 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -40,6 +40,11 @@ class ListView(Vertical, can_focus=True, can_focus_children=False): @property def highlighted_child(self) -> ListItem | None: + """Get the currently highlighted ListItem + + Returns: + ListItem | None: The currently highlighted ListItem, or None if nothing is highlighted. + """ if self.index is None: return None elif 0 <= self.index < len(self.children): @@ -51,10 +56,12 @@ class ListView(Vertical, can_focus=True, can_focus_children=False): return self._clamp_index(index) def _clamp_index(self, index: int) -> int: + """Clamp the index to a valid value given the current list of children""" last_index = max(len(self.children) - 1, 0) return clamp(index, 0, last_index) def _is_valid_index(self, index: int | None) -> bool: + """Return True if the current index is valid given the current list of children""" if index is None: return False return 0 <= index < len(self.children) @@ -92,7 +99,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False): selected_child = self.highlighted_child self.emit_no_wait(self.Selected(self, selected_child)) - def on_list_item_child_selected(self, event: ListItem.ChildSelected) -> None: + def on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None: self.focus() self.index = self.children.index(event.sender) self.emit_no_wait(self.Selected(self, event.sender)) @@ -108,6 +115,7 @@ class ListView(Vertical, can_focus=True, can_focus_children=False): self.index += 1 def _scroll_highlighted_region(self) -> None: + """Used to keep the highlighted index within vision""" if self.highlighted_child is not None: self.scroll_to_widget(self.highlighted_child, animate=False) @@ -115,16 +123,23 @@ class ListView(Vertical, can_focus=True, can_focus_children=False): return len(self.children) class Highlighted(Message, bubble=True): + """Emitted when the highlighted item changes. Highlighted item is controlled using up/down keys""" + def __init__(self, sender: ListView, item: ListItem | None) -> None: super().__init__(sender) self.item = item class Selected(Message, bubble=True): + """Emitted when a list item is selected, e.g. when you press the enter key on it""" + def __init__(self, sender: ListView, item: ListItem) -> None: super().__init__(sender) self.item = item class ChildrenUpdated(Message, bubble=True): + """Emitted when the elements in the `ListView` are changed (e.g. a child is + added, or the list is cleared)""" + def __init__(self, sender: ListView, children: list[ListItem]) -> None: super().__init__(sender) self.children = children