Refactor Message.control and Tree Messages (#2602)

* refactor(message): make control a property

* refactor(_tree): remove tree parameter on messages

* refactor(_directory_tree): remove tree parameter on message

* fix: tree message calls

* fix(_select): make Changed.control a property

* refactor(_on): control check

* refactor(_select): rename Changed.widget to select

* docs: changelog entry
This commit is contained in:
Aaron Stephens
2023-05-25 05:48:31 -07:00
committed by GitHub
parent 3ab315beb6
commit 20d19d977d
6 changed files with 44 additions and 70 deletions

View File

@@ -36,6 +36,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Textual will now scroll focused widgets to center if not in view
## Unreleased
### Changed
- `Message.control` is now a property instead of a class variable. https://github.com/Textualize/textual/issues/2528
- `Tree` and `DirectoryTree` Messages no longer accept a `tree` parameter, using `self.node.tree` instead. https://github.com/Textualize/textual/issues/2529
## [0.25.0] - 2023-05-17
### Changed

View File

@@ -65,7 +65,7 @@ def on(
parsed_selectors: dict[str, tuple[SelectorSet, ...]] = {}
for attribute, css_selector in selectors.items():
if attribute == "control":
if message_type.control is None:
if message_type.control == Message.control:
raise OnDecoratorError(
"The message class must have a 'control' to match with the on decorator"
)

View File

@@ -42,7 +42,6 @@ class Message:
verbose: ClassVar[bool] = False # Message is verbose
no_dispatch: ClassVar[bool] = False # Message may not be handled by client code
namespace: ClassVar[str] = "" # Namespace to disambiguate messages
control: Widget | None = None
def __init__(self) -> None:
self.__post_init__()
@@ -79,6 +78,11 @@ class Message:
if namespace is not None:
cls.namespace = namespace
@property
def control(self) -> Widget | None:
"""The widget associated with this message, or None by default."""
return None
@property
def is_forwarded(self) -> bool:
"""Has the message been forwarded?"""

View File

@@ -69,9 +69,7 @@ class DirectoryTree(Tree[DirEntry]):
`DirectoryTree` or in a parent widget in the DOM.
"""
def __init__(
self, tree: DirectoryTree, node: TreeNode[DirEntry], path: Path
) -> None:
def __init__(self, node: TreeNode[DirEntry], path: Path) -> None:
"""Initialise the FileSelected object.
Args:
@@ -79,21 +77,15 @@ class DirectoryTree(Tree[DirEntry]):
path: The path of the file that was selected.
"""
super().__init__()
self.tree: DirectoryTree = tree
"""The `DirectoryTree` that had a file selected."""
self.node: TreeNode[DirEntry] = node
"""The tree node of the file that was selected."""
self.path: Path = path
"""The path of the file that was selected."""
@property
def control(self) -> DirectoryTree:
"""The `DirectoryTree` that had a file selected.
This is an alias for [`FileSelected.tree`][textual.widgets.DirectoryTree.FileSelected.tree]
which is used by the [`on`][textual.on] decorator.
"""
return self.tree
def control(self) -> Tree[DirEntry]:
"""The `Tree` that had a file selected."""
return self.node.tree
path: var[str | Path] = var["str | Path"](PATH("."), init=False, always_update=True)
"""The path that is the root of the directory tree.
@@ -361,7 +353,7 @@ class DirectoryTree(Tree[DirEntry]):
if not dir_entry.loaded:
self._add_to_load_queue(event.node)
else:
self.post_message(self.FileSelected(self, event.node, dir_entry.path))
self.post_message(self.FileSelected(event.node, dir_entry.path))
def _on_tree_node_selected(self, event: Tree.NodeSelected) -> None:
event.stop()
@@ -369,4 +361,4 @@ class DirectoryTree(Tree[DirEntry]):
if dir_entry is None:
return
if not self._safe_is_dir(dir_entry.path):
self.post_message(self.FileSelected(self, event.node, dir_entry.path))
self.post_message(self.FileSelected(event.node, dir_entry.path))

View File

@@ -227,20 +227,23 @@ class Select(Generic[SelectType], Vertical, can_focus=True):
"""Posted when the select value was changed.
This message can be handled using a `on_select_changed` method.
"""
def __init__(self, control: Select, value: SelectType | None) -> None:
def __init__(self, select: Select, value: SelectType | None) -> None:
"""
Initialize the Changed message.
"""
super().__init__()
self.control = control
"""The select control."""
self.select = select
"""The select widget."""
self.value = value
"""The value of the Select when it changed."""
@property
def control(self) -> Select:
"""The Select that sent the message."""
return self.select
def __init__(
self,
options: Iterable[tuple[str, SelectType]],

View File

@@ -207,7 +207,7 @@ class TreeNode(Generic[TreeDataType]):
"""
self._expanded = True
self._updates += 1
self._tree.post_message(Tree.NodeExpanded(self._tree, self))
self._tree.post_message(Tree.NodeExpanded(self))
if expand_all:
for child in self.children:
child._expand(expand_all)
@@ -240,7 +240,7 @@ class TreeNode(Generic[TreeDataType]):
"""
self._expanded = False
self._updates += 1
self._tree.post_message(Tree.NodeCollapsed(self._tree, self))
self._tree.post_message(Tree.NodeCollapsed(self))
if collapse_all:
for child in self.children:
child._collapse(collapse_all)
@@ -514,23 +514,15 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
parent node in the DOM.
"""
def __init__(
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
) -> None:
self.tree = tree
"""The tree that sent the message."""
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
self.node: TreeNode[EventTreeDataType] = node
"""The node that was collapsed."""
super().__init__()
@property
def control(self) -> Tree[EventTreeDataType]:
"""The tree that sent the message.
This is an alias for [`NodeCollapsed.tree`][textual.widgets.Tree.NodeCollapsed.tree]
and is used by the [`on`][textual.on] decorator.
"""
return self.tree
"""The tree that sent the message."""
return self.node.tree
class NodeExpanded(Generic[EventTreeDataType], Message, bubble=True):
"""Event sent when a node is expanded.
@@ -539,23 +531,15 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
parent node in the DOM.
"""
def __init__(
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
) -> None:
self.tree = tree
"""The tree that sent the message."""
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
self.node: TreeNode[EventTreeDataType] = node
"""The node that was expanded."""
super().__init__()
@property
def control(self) -> Tree[EventTreeDataType]:
"""The tree that sent the message.
This is an alias for [`NodeExpanded.tree`][textual.widgets.Tree.NodeExpanded.tree]
and is used by the [`on`][textual.on] decorator.
"""
return self.tree
"""The tree that sent the message."""
return self.node.tree
class NodeHighlighted(Generic[EventTreeDataType], Message, bubble=True):
"""Event sent when a node is highlighted.
@@ -564,23 +548,15 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
parent node in the DOM.
"""
def __init__(
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
) -> None:
self.tree = tree
"""The tree that sent the message."""
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
self.node: TreeNode[EventTreeDataType] = node
"""The node that was highlighted."""
super().__init__()
@property
def control(self) -> Tree[EventTreeDataType]:
"""The tree that sent the message.
This is an alias for [`NodeHighlighted.tree`][textual.widgets.Tree.NodeHighlighted.tree]
and is used by the [`on`][textual.on] decorator.
"""
return self.tree
"""The tree that sent the message."""
return self.node.tree
class NodeSelected(Generic[EventTreeDataType], Message, bubble=True):
"""Event sent when a node is selected.
@@ -589,23 +565,15 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
parent node in the DOM.
"""
def __init__(
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
) -> None:
self.tree = tree
"""The tree that sent the message."""
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
self.node: TreeNode[EventTreeDataType] = node
"""The node that was selected."""
super().__init__()
@property
def control(self) -> Tree[EventTreeDataType]:
"""The tree that sent the message.
This is an alias for [`NodeSelected.tree`][textual.widgets.Tree.NodeSelected.tree]
and is used by the [`on`][textual.on] decorator.
"""
return self.tree
"""The tree that sent the message."""
return self.node.tree
def __init__(
self,
@@ -905,7 +873,7 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
node._selected = True
self._cursor_node = node
if previous_node != node:
self.post_message(self.NodeHighlighted(self, node))
self.post_message(self.NodeHighlighted(node))
else:
self._cursor_node = None
@@ -1236,7 +1204,7 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
Note:
If `auto_expand` is `True` use of this action on a non-leaf node
will cause both an expand/collapse event to occour, as well as a
will cause both an expand/collapse event to occur, as well as a
selected event.
"""
try:
@@ -1247,4 +1215,4 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
node = line.path[-1]
if self.auto_expand:
self._toggle_node(node)
self.post_message(self.NodeSelected(self, node))
self.post_message(self.NodeSelected(node))