From 20d19d977df18d3e9b85221c4dabf6f670389074 Mon Sep 17 00:00:00 2001 From: Aaron Stephens Date: Thu, 25 May 2023 05:48:31 -0700 Subject: [PATCH] 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 --- CHANGELOG.md | 7 +++ src/textual/_on.py | 2 +- src/textual/message.py | 6 ++- src/textual/widgets/_directory_tree.py | 20 +++----- src/textual/widgets/_select.py | 13 +++-- src/textual/widgets/_tree.py | 66 +++++++------------------- 6 files changed, 44 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e1b0fc81..c467e5501 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/textual/_on.py b/src/textual/_on.py index 0ff0a6f59..40e2a706c 100644 --- a/src/textual/_on.py +++ b/src/textual/_on.py @@ -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" ) diff --git a/src/textual/message.py b/src/textual/message.py index d2275bf36..40b00c284 100644 --- a/src/textual/message.py +++ b/src/textual/message.py @@ -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?""" diff --git a/src/textual/widgets/_directory_tree.py b/src/textual/widgets/_directory_tree.py index dfe993b3b..e6b81abee 100644 --- a/src/textual/widgets/_directory_tree.py +++ b/src/textual/widgets/_directory_tree.py @@ -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)) diff --git a/src/textual/widgets/_select.py b/src/textual/widgets/_select.py index 16cbb6ec4..2038a46a5 100644 --- a/src/textual/widgets/_select.py +++ b/src/textual/widgets/_select.py @@ -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]], diff --git a/src/textual/widgets/_tree.py b/src/textual/widgets/_tree.py index 01cf2f180..8bb32bf96 100644 --- a/src/textual/widgets/_tree.py +++ b/src/textual/widgets/_tree.py @@ -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))