Add read-only access to the children of a TreeNode

See #1398.
This commit is contained in:
Dave Pearson
2023-01-05 21:24:47 +00:00
parent 30d5c1e66b
commit 7779211dcf
3 changed files with 49 additions and 3 deletions

View File

@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.10.0] - Unreleased ## [0.10.0] - Unreleased
### Added
- Added read-only public access to the children of a `TreeNode` via `TreeNode.children` https://github.com/Textualize/textual/issues/1398
### Changed ### Changed
- `MouseScrollUp` and `MouseScrollDown` now inherit from `MouseEvent` and have attached modifier keys. https://github.com/Textualize/textual/pull/1458 - `MouseScrollUp` and `MouseScrollDown` now inherit from `MouseEvent` and have attached modifier keys. https://github.com/Textualize/textual/pull/1458
@@ -29,8 +33,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Widget.render_line now returns a Strip - Widget.render_line now returns a Strip
- Fix for slow updates on Windows - Fix for slow updates on Windows
- Bumped Rich dependency - Bumped Rich dependency
## [0.8.2] - 2022-12-28 ## [0.8.2] - 2022-12-28
### Fixed ### Fixed

View File

@@ -14,6 +14,7 @@ from .._loop import loop_last
from .._segment_tools import line_crop, line_pad from .._segment_tools import line_crop, line_pad
from .._types import MessageTarget from .._types import MessageTarget
from .._typing import TypeAlias from .._typing import TypeAlias
from .._collections import ImmutableSequence
from ..binding import Binding from ..binding import Binding
from ..geometry import Region, Size, clamp from ..geometry import Region, Size, clamp
from ..message import Message from ..message import Message
@@ -53,6 +54,10 @@ class _TreeLine:
return guides return guides
class TreeNodes(ImmutableSequence["TreeNode[TreeDataType]"]):
"""An immutable collection of `TreeNode`."""
@rich.repr.auto @rich.repr.auto
class TreeNode(Generic[TreeDataType]): class TreeNode(Generic[TreeDataType]):
"""An object that represents a "node" in a tree control.""" """An object that represents a "node" in a tree control."""
@@ -74,7 +79,7 @@ class TreeNode(Generic[TreeDataType]):
self._label = label self._label = label
self.data = data self.data = data
self._expanded = expanded self._expanded = expanded
self._children: list[TreeNode] = [] self._children: list[TreeNode[TreeDataType]] = []
self._hover_ = False self._hover_ = False
self._selected_ = False self._selected_ = False
@@ -91,6 +96,11 @@ class TreeNode(Generic[TreeDataType]):
self._selected_ = False self._selected_ = False
self._updates += 1 self._updates += 1
@property
def children(self) -> TreeNodes[TreeDataType]:
"""TreeNodes[TreeDataType]: The child nodes of a TreeNode."""
return TreeNodes(self._children)
@property @property
def line(self) -> int: def line(self) -> int:
"""int: Get the line number for this node, or -1 if it is not displayed.""" """int: Get the line number for this node, or -1 if it is not displayed."""

View File

@@ -0,0 +1,32 @@
import pytest
from textual.widgets import Tree, TreeNode
def label_of(node: TreeNode[None]):
"""Get the label of a node.
TODO: This is just a helper function to reduce the number of type
errors, which can and will be remove once this code is merged with a
version of main that also has the TreeNode.label PR merged.
"""
return str(node._label)
def test_tree_node_children() -> None:
"""A node's children property should act like an immutable list."""
CHILDREN=23
tree = Tree[None]("Root")
for child in range(CHILDREN):
tree.root.add(str(child))
assert len(tree.root.children)==CHILDREN
for child in range(CHILDREN):
assert label_of(tree.root.children[child]) == str(child)
assert label_of(tree.root.children[0]) == "0"
assert label_of(tree.root.children[-1]) == str(CHILDREN-1)
assert [label_of(node) for node in tree.root.children] == [str(n) for n in range(CHILDREN)]
assert [label_of(node) for node in tree.root.children[:2]] == [str(n) for n in range(2)]
with pytest.raises(TypeError):
tree.root.children[0] = tree.root.children[1]
with pytest.raises(TypeError):
del tree.root.children[0]
with pytest.raises(TypeError):
del tree.root.children[0:2]