Skip to content

Commit 54a2e3e

Browse files
authored
Merge pull request #1143 from Textualize/list-view
List view
2 parents d91ea42 + 24a182c commit 54a2e3e

File tree

15 files changed

+525
-20
lines changed

15 files changed

+525
-20
lines changed

docs/api/list_item.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: textual.widgets.ListItem

docs/api/list_view.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: textual.widgets.ListView
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Screen {
2+
align: center middle;
3+
}
4+
5+
ListView {
6+
width: 30;
7+
height: auto;
8+
margin: 2 2;
9+
}
10+
11+
Label {
12+
padding: 1 2;
13+
}

docs/examples/widgets/list_view.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from textual.app import App, ComposeResult
2+
from textual.widgets import ListView, ListItem, Label, Footer
3+
4+
5+
class ListViewExample(App):
6+
def compose(self) -> ComposeResult:
7+
yield ListView(
8+
ListItem(Label("One")),
9+
ListItem(Label("Two")),
10+
ListItem(Label("Three")),
11+
)
12+
yield Footer()
13+
14+
15+
app = ListViewExample(css_path="list_view.css")
16+
if __name__ == "__main__":
17+
app.run()

docs/widgets/list_item.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# List Item
2+
3+
`ListItem` is the type of the elements in a `ListView`.
4+
5+
- [] Focusable
6+
- [] Container
7+
8+
## Example
9+
10+
The example below shows an app with a simple `ListView`, consisting
11+
of multiple `ListItem`s. The arrow keys can be used to navigate the list.
12+
13+
=== "Output"
14+
15+
```{.textual path="docs/examples/widgets/list_view.py"}
16+
```
17+
18+
=== "list_view.py"
19+
20+
```python
21+
--8<-- "docs/examples/widgets/list_view.py"
22+
```
23+
24+
## Reactive Attributes
25+
26+
| Name | Type | Default | Description |
27+
|---------------|--------|---------|--------------------------------------|
28+
| `highlighted` | `bool` | `False` | True if this ListItem is highlighted |
29+
30+
## Messages
31+
32+
### Selected
33+
34+
The `ListItem.Selected` message is sent when the item is selected.
35+
36+
- [x] Bubbles
37+
38+
#### Attributes
39+
40+
| attribute | type | purpose |
41+
|-----------|------------|-----------------------------|
42+
| `item` | `ListItem` | The item that was selected. |
43+
44+
## See Also
45+
46+
* [ListItem](../api/list_item.md) code reference

docs/widgets/list_view.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# List View
2+
3+
Displays a vertical list of `ListItem`s which can be highlighted and selected.
4+
Supports keyboard navigation.
5+
6+
- [x] Focusable
7+
- [x] Container
8+
9+
## Example
10+
11+
The example below shows an app with a simple `ListView`.
12+
13+
=== "Output"
14+
15+
```{.textual path="docs/examples/widgets/list_view.py"}
16+
```
17+
18+
=== "list_view.py"
19+
20+
```python
21+
--8<-- "docs/examples/widgets/list_view.py"
22+
```
23+
24+
## Reactive Attributes
25+
26+
| Name | Type | Default | Description |
27+
|---------|-------|---------|---------------------------------|
28+
| `index` | `int` | `0` | The currently highlighted index |
29+
30+
## Messages
31+
32+
### Highlighted
33+
34+
The `ListView.Highlighted` message is emitted when the highlight changes.
35+
This happens when you use the arrow keys on your keyboard and when you
36+
click on a list item.
37+
38+
- [x] Bubbles
39+
40+
#### Attributes
41+
42+
| attribute | type | purpose |
43+
|-----------|------------|--------------------------------|
44+
| `item` | `ListItem` | The item that was highlighted. |
45+
46+
### Selected
47+
48+
The `ListView.Selected` message is emitted when a list item is selected.
49+
You can select a list item by pressing ++enter++ while it is highlighted,
50+
or by clicking on it.
51+
52+
- [x] Bubbles
53+
54+
#### Attributes
55+
56+
| attribute | type | purpose |
57+
|-----------|------------|-----------------------------|
58+
| `item` | `ListItem` | The item that was selected. |
59+
60+
61+
### ChildrenUpdated
62+
63+
The `ListView.ChildrenUpdated` message is emitted when the elements in the `ListView`
64+
are changed (e.g. a child is added, or the list is cleared).
65+
66+
- [x] Bubbles
67+
68+
#### Attributes
69+
70+
| attribute | type | purpose |
71+
|------------|------------------|---------------------------|
72+
| `children` | `list[ListItem]` | The new ListView children |
73+
74+
75+
76+
## See Also
77+
78+
* [ListView](../api/list_view.md) code reference

mkdocs.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ nav:
9999
- "widgets/index.md"
100100
- "widgets/input.md"
101101
- "widgets/label.md"
102+
- "widgets/list_view.md"
103+
- "widgets/list_item.md"
102104
- "widgets/placeholder.md"
103105
- "widgets/static.md"
104106
- "widgets/tree.md"
@@ -119,6 +121,8 @@ nav:
119121
- "api/header.md"
120122
- "api/input.md"
121123
- "api/label.md"
124+
- "api/list_view.md"
125+
- "api/list_item.md"
122126
- "api/message_pump.md"
123127
- "api/message.md"
124128
- "api/pilot.md"

src/textual/app.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,24 +1955,6 @@ def _detach_from_dom(self, widgets: list[Widget]) -> list[Widget]:
19551955
# prune event.
19561956
return pruned_remove
19571957

1958-
async def _on_prune(self, event: events.Prune) -> None:
1959-
"""Handle a prune event.
1960-
1961-
Args:
1962-
event (events.Prune): The prune event.
1963-
"""
1964-
1965-
try:
1966-
# Prune all the widgets.
1967-
for widget in event.widgets:
1968-
await self._prune_node(widget)
1969-
finally:
1970-
# Finally, flag that we're done.
1971-
event.finished_flag.set()
1972-
1973-
# Flag that the layout needs refreshing.
1974-
self.refresh(layout=True)
1975-
19761958
def _walk_children(self, root: Widget) -> Iterable[list[Widget]]:
19771959
"""Walk children depth first, generating widgets and a list of their siblings.
19781960

src/textual/dom.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def screen(self) -> "Screen":
289289
from .screen import Screen
290290

291291
node = self
292-
while node and not isinstance(node, Screen):
292+
while node is not None and not isinstance(node, Screen):
293293
node = node._parent
294294
if not isinstance(node, Screen):
295295
raise NoScreen("node has no screen")

src/textual/widgets/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
# but also to the `__init__.pyi` file in this same folder - otherwise text editors and type checkers won't
99
# be able to "see" them.
1010
if typing.TYPE_CHECKING:
11-
1211
from ._button import Button
1312
from ._checkbox import Checkbox
1413
from ._data_table import DataTable
@@ -17,6 +16,8 @@
1716
from ._header import Header
1817
from ._input import Input
1918
from ._label import Label
19+
from ._list_item import ListItem
20+
from ._list_view import ListView
2021
from ._placeholder import Placeholder
2122
from ._pretty import Pretty
2223
from ._static import Static
@@ -26,6 +27,7 @@
2627
from ._welcome import Welcome
2728
from ..widget import Widget
2829

30+
2931
__all__ = [
3032
"Button",
3133
"Checkbox",
@@ -35,6 +37,8 @@
3537
"Header",
3638
"Input",
3739
"Label",
40+
"ListItem",
41+
"ListView",
3842
"Placeholder",
3943
"Pretty",
4044
"Static",

0 commit comments

Comments
 (0)