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 - 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 ## [0.25.0] - 2023-05-17
### Changed ### Changed

View File

@@ -65,7 +65,7 @@ def on(
parsed_selectors: dict[str, tuple[SelectorSet, ...]] = {} parsed_selectors: dict[str, tuple[SelectorSet, ...]] = {}
for attribute, css_selector in selectors.items(): for attribute, css_selector in selectors.items():
if attribute == "control": if attribute == "control":
if message_type.control is None: if message_type.control == Message.control:
raise OnDecoratorError( raise OnDecoratorError(
"The message class must have a 'control' to match with the on decorator" "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 verbose: ClassVar[bool] = False # Message is verbose
no_dispatch: ClassVar[bool] = False # Message may not be handled by client code no_dispatch: ClassVar[bool] = False # Message may not be handled by client code
namespace: ClassVar[str] = "" # Namespace to disambiguate messages namespace: ClassVar[str] = "" # Namespace to disambiguate messages
control: Widget | None = None
def __init__(self) -> None: def __init__(self) -> None:
self.__post_init__() self.__post_init__()
@@ -79,6 +78,11 @@ class Message:
if namespace is not None: if namespace is not None:
cls.namespace = namespace cls.namespace = namespace
@property
def control(self) -> Widget | None:
"""The widget associated with this message, or None by default."""
return None
@property @property
def is_forwarded(self) -> bool: def is_forwarded(self) -> bool:
"""Has the message been forwarded?""" """Has the message been forwarded?"""

View File

@@ -69,9 +69,7 @@ class DirectoryTree(Tree[DirEntry]):
`DirectoryTree` or in a parent widget in the DOM. `DirectoryTree` or in a parent widget in the DOM.
""" """
def __init__( def __init__(self, node: TreeNode[DirEntry], path: Path) -> None:
self, tree: DirectoryTree, node: TreeNode[DirEntry], path: Path
) -> None:
"""Initialise the FileSelected object. """Initialise the FileSelected object.
Args: Args:
@@ -79,21 +77,15 @@ class DirectoryTree(Tree[DirEntry]):
path: The path of the file that was selected. path: The path of the file that was selected.
""" """
super().__init__() super().__init__()
self.tree: DirectoryTree = tree
"""The `DirectoryTree` that had a file selected."""
self.node: TreeNode[DirEntry] = node self.node: TreeNode[DirEntry] = node
"""The tree node of the file that was selected.""" """The tree node of the file that was selected."""
self.path: Path = path self.path: Path = path
"""The path of the file that was selected.""" """The path of the file that was selected."""
@property @property
def control(self) -> DirectoryTree: def control(self) -> Tree[DirEntry]:
"""The `DirectoryTree` that had a file selected. """The `Tree` that had a file selected."""
return self.node.tree
This is an alias for [`FileSelected.tree`][textual.widgets.DirectoryTree.FileSelected.tree]
which is used by the [`on`][textual.on] decorator.
"""
return self.tree
path: var[str | Path] = var["str | Path"](PATH("."), init=False, always_update=True) path: var[str | Path] = var["str | Path"](PATH("."), init=False, always_update=True)
"""The path that is the root of the directory tree. """The path that is the root of the directory tree.
@@ -361,7 +353,7 @@ class DirectoryTree(Tree[DirEntry]):
if not dir_entry.loaded: if not dir_entry.loaded:
self._add_to_load_queue(event.node) self._add_to_load_queue(event.node)
else: 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: def _on_tree_node_selected(self, event: Tree.NodeSelected) -> None:
event.stop() event.stop()
@@ -369,4 +361,4 @@ class DirectoryTree(Tree[DirEntry]):
if dir_entry is None: if dir_entry is None:
return return
if not self._safe_is_dir(dir_entry.path): 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. """Posted when the select value was changed.
This message can be handled using a `on_select_changed` method. 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. Initialize the Changed message.
""" """
super().__init__() super().__init__()
self.control = control self.select = select
"""The select control.""" """The select widget."""
self.value = value self.value = value
"""The value of the Select when it changed.""" """The value of the Select when it changed."""
@property
def control(self) -> Select:
"""The Select that sent the message."""
return self.select
def __init__( def __init__(
self, self,
options: Iterable[tuple[str, SelectType]], options: Iterable[tuple[str, SelectType]],

View File

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