Allow setting a new label when performing a clear on a Tree

See #1437 for background. While it would be ideal to allow for the complete
emptying of a Tree, the root node is required (and it's part of the
construction of a Tree). So, here, when clearing the Tree we optionally
allow for a new label to be given.

Ideally we'll also allow for fresh data to be provided too; but there's a
wrinkle there in knowing the difference between the data being None, and no
data being provided (so the current root's data being carried over).
Following the method of defaulting used in __init__ would cause problems. As
such, rather than roll all of this into one commit, this goes with the basic
requirement and the solution for data will follow.

Note this also starts some unit tests for the clearing of a Tree.
This commit is contained in:
Dave Pearson
2023-02-01 12:33:36 +00:00
parent 6f24331564
commit 7563d4cb7e
2 changed files with 70 additions and 3 deletions

View File

@@ -560,12 +560,17 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
label = self.render_label(node, NULL_STYLE, NULL_STYLE) label = self.render_label(node, NULL_STYLE, NULL_STYLE)
return label.cell_len return label.cell_len
def clear(self) -> None: def clear(self, label: TextType | None = None) -> None:
"""Clear all nodes under root.""" """Clear all nodes under root.
Args:
label: An optional new label for the root node. If not provided
the current root node's label will be used.
"""
self._line_cache.clear() self._line_cache.clear()
self._tree_lines_cached = None self._tree_lines_cached = None
self._current_id = 0 self._current_id = 0
root_label = self.root._label root_label = self.root._label if label is None else label
root_data = self.root.data root_data = self.root.data
self.root = TreeNode( self.root = TreeNode(
self, self,

View File

@@ -0,0 +1,62 @@
from __future__ import annotations
from textual.app import App, ComposeResult
from textual.widgets import Tree
class VerseBody:
pass
class VerseStar(VerseBody):
pass
class VersePlanet(VerseBody):
pass
class VerseMoon(VerseBody):
pass
class TestTree(Tree[VerseBody]):
pass
class TreeClearApp(App[None]):
"""Tree clearing test app."""
def compose(self) -> ComposeResult:
yield TestTree("White Sun", data=VerseStar())
def on_mount(self) -> None:
tree = self.query_one(TestTree)
node = tree.root.add("Londinium", VersePlanet())
node.add_leaf("Balkerne", VerseMoon())
node.add_leaf("Colchester", VerseMoon())
node = tree.root.add("Sihnon", VersePlanet())
node.add_leaf("Airen", VerseMoon())
node.add_leaf("Xiaojie", VerseMoon())
async def test_tree_simple_clear() -> None:
"""Clearing a tree should keep the old label and data."""
async with TreeClearApp().run_test() as pilot:
tree = pilot.app.query_one(TestTree)
assert len(tree.root.children) > 1
pilot.app.query_one(TestTree).clear()
assert len(tree.root.children) == 0
assert str(tree.root.label) == "White Sun"
assert isinstance(tree.root.data, VerseStar)
async def test_tree_new_label_clear() -> None:
"""Clearing a tree with a new label should use the new label and keep the old data."""
async with TreeClearApp().run_test() as pilot:
tree = pilot.app.query_one(TestTree)
assert len(tree.root.children) > 1
pilot.app.query_one(TestTree).clear("Jiangyin")
assert len(tree.root.children) == 0
assert str(tree.root.label) == "Jiangyin"
assert isinstance(tree.root.data, VerseStar)