mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #2490 from Textualize/messages-control
Add control to widget messages.
This commit is contained in:
24
CHANGELOG.md
24
CHANGELOG.md
@@ -35,6 +35,30 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Added `TreeNode.remove_children` https://github.com/Textualize/textual/pull/2510
|
||||
- Added `TreeNode.remove` https://github.com/Textualize/textual/pull/2510
|
||||
|
||||
### Added
|
||||
|
||||
- Markdown document sub-widgets now reference the container document
|
||||
- Table of contents of a markdown document now references the document
|
||||
- Added the `control` property to messages
|
||||
- `DirectoryTree.FileSelected`
|
||||
- `ListView`
|
||||
- `Highlighted`
|
||||
- `Selected`
|
||||
- `Markdown`
|
||||
- `TableOfContentsUpdated`
|
||||
- `TableOfContentsSelected`
|
||||
- `LinkClicked`
|
||||
- `OptionList`
|
||||
- `OptionHighlighted`
|
||||
- `OptionSelected`
|
||||
- `RadioSet.Changed`
|
||||
- `TabContent.TabActivated`
|
||||
- `Tree`
|
||||
- `NodeSelected`
|
||||
- `NodeHighlighted`
|
||||
- `NodeExpanded`
|
||||
- `NodeCollapsed`
|
||||
|
||||
## [0.23.0] - 2023-05-03
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -90,18 +90,7 @@ tables](https://rich.readthedocs.io/en/latest/tables.html):
|
||||
- [OptionList.OptionHighlight][textual.widgets.OptionList.OptionHighlighted]
|
||||
- [OptionList.OptionSelected][textual.widgets.OptionList.OptionSelected]
|
||||
|
||||
Both of the messages above inherit from this common base, which makes
|
||||
available the following properties relating to the `OptionList` and the
|
||||
related `Option`:
|
||||
|
||||
### Common message properties
|
||||
|
||||
Both of the above messages provide the following properties:
|
||||
|
||||
#### ::: textual.widgets.OptionList.OptionMessage.option
|
||||
#### ::: textual.widgets.OptionList.OptionMessage.option_id
|
||||
#### ::: textual.widgets.OptionList.OptionMessage.option_index
|
||||
#### ::: textual.widgets.OptionList.OptionMessage.option_list
|
||||
Both of the messages above inherit from the common base [`OptionList`][textual.widgets.OptionList.OptionMessage], so refer to its documentation to see what attributes are available.
|
||||
|
||||
## Bindings
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ class DirectoryTree(Tree[DirEntry]):
|
||||
`DirectoryTree` or in a parent widget in the DOM.
|
||||
"""
|
||||
|
||||
def __init__(self, node: TreeNode[DirEntry], path: Path) -> None:
|
||||
def __init__(
|
||||
self, tree: DirectoryTree, node: TreeNode[DirEntry], path: Path
|
||||
) -> None:
|
||||
"""Initialise the FileSelected object.
|
||||
|
||||
Args:
|
||||
@@ -72,11 +74,22 @@ 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
|
||||
|
||||
path: var[str | Path] = var["str | Path"](Path("."), init=False)
|
||||
"""The path that is the root of the directory tree.
|
||||
|
||||
|
||||
@@ -44,30 +44,46 @@ class ListView(VerticalScroll, can_focus=True, can_focus_children=False):
|
||||
Highlighted item is controlled using up/down keys.
|
||||
Can be handled using `on_list_view_highlighted` in a subclass of `ListView`
|
||||
or in a parent widget in the DOM.
|
||||
|
||||
Attributes:
|
||||
item: The highlighted item, if there is one highlighted.
|
||||
"""
|
||||
|
||||
def __init__(self, list_view: ListView, item: ListItem | None) -> None:
|
||||
super().__init__()
|
||||
self.list_view = list_view
|
||||
self.list_view: ListView = list_view
|
||||
"""The view that contains the item highlighted."""
|
||||
self.item: ListItem | None = item
|
||||
"""The highlighted item, if there is one highlighted."""
|
||||
|
||||
@property
|
||||
def control(self) -> ListView:
|
||||
"""The view that contains the item highlighted.
|
||||
|
||||
This is an alias for [`Highlighted.list_view`][textual.widgets.ListView.Highlighted.list_view]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.list_view
|
||||
|
||||
class Selected(Message, bubble=True):
|
||||
"""Posted when a list item is selected, e.g. when you press the enter key on it.
|
||||
|
||||
Can be handled using `on_list_view_selected` in a subclass of `ListView` or in
|
||||
a parent widget in the DOM.
|
||||
|
||||
Attributes:
|
||||
item: The selected item.
|
||||
"""
|
||||
|
||||
def __init__(self, list_view: ListView, item: ListItem) -> None:
|
||||
super().__init__()
|
||||
self.list_view = list_view
|
||||
self.list_view: ListView = list_view
|
||||
"""The view that contains the item selected."""
|
||||
self.item: ListItem = item
|
||||
"""The selected item."""
|
||||
|
||||
@property
|
||||
def control(self) -> ListView:
|
||||
"""The view that contains the item selected.
|
||||
|
||||
This is an alias for [`Selected.list_view`][textual.widgets.ListView.Selected.list_view]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.list_view
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -88,7 +88,9 @@ class MarkdownBlock(Static):
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
def __init__(self, markdown: Markdown, *args, **kwargs) -> None:
|
||||
self._markdown: Markdown = markdown
|
||||
"""A reference to the Markdown document that contains this block."""
|
||||
self._text = Text()
|
||||
self._blocks: list[MarkdownBlock] = []
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -103,7 +105,7 @@ class MarkdownBlock(Static):
|
||||
|
||||
async def action_link(self, href: str) -> None:
|
||||
"""Called on link click."""
|
||||
self.post_message(Markdown.LinkClicked(href))
|
||||
self.post_message(Markdown.LinkClicked(self._markdown, href))
|
||||
|
||||
|
||||
class MarkdownHeader(MarkdownBlock):
|
||||
@@ -453,9 +455,9 @@ class MarkdownListItem(MarkdownBlock):
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, bullet: str) -> None:
|
||||
def __init__(self, markdown: Markdown, bullet: str) -> None:
|
||||
self.bullet = bullet
|
||||
super().__init__()
|
||||
super().__init__(markdown)
|
||||
|
||||
|
||||
class MarkdownOrderedListItem(MarkdownListItem):
|
||||
@@ -484,10 +486,10 @@ class MarkdownFence(MarkdownBlock):
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, code: str, lexer: str) -> None:
|
||||
def __init__(self, markdown: Markdown, code: str, lexer: str) -> None:
|
||||
self.code = code
|
||||
self.lexer = lexer
|
||||
super().__init__()
|
||||
super().__init__(markdown)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static(
|
||||
@@ -565,27 +567,62 @@ class Markdown(Widget):
|
||||
class TableOfContentsUpdated(Message, bubble=True):
|
||||
"""The table of contents was updated."""
|
||||
|
||||
def __init__(self, table_of_contents: TableOfContentsType) -> None:
|
||||
def __init__(
|
||||
self, markdown: Markdown, table_of_contents: TableOfContentsType
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.markdown: Markdown = markdown
|
||||
"""The `Markdown` widget associated with the table of contents."""
|
||||
self.table_of_contents: TableOfContentsType = table_of_contents
|
||||
"""Table of contents."""
|
||||
|
||||
@property
|
||||
def control(self) -> Markdown:
|
||||
"""The `Markdown` widget associated with the table of contents.
|
||||
|
||||
This is an alias for [`TableOfContentsUpdated.markdown`][textual.widgets.Markdown.TableOfContentsSelected.markdown]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.markdown
|
||||
|
||||
class TableOfContentsSelected(Message, bubble=True):
|
||||
"""An item in the TOC was selected."""
|
||||
|
||||
def __init__(self, block_id: str) -> None:
|
||||
def __init__(self, markdown: Markdown, block_id: str) -> None:
|
||||
super().__init__()
|
||||
self.block_id = block_id
|
||||
self.markdown: Markdown = markdown
|
||||
"""The `Markdown` widget where the selected item is."""
|
||||
self.block_id: str = block_id
|
||||
"""ID of the block that was selected."""
|
||||
|
||||
@property
|
||||
def control(self) -> Markdown:
|
||||
"""The `Markdown` widget where the selected item is.
|
||||
|
||||
This is an alias for [`TableOfContentsSelected.markdown`][textual.widgets.Markdown.TableOfContentsSelected.markdown]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.markdown
|
||||
|
||||
class LinkClicked(Message, bubble=True):
|
||||
"""A link in the document was clicked."""
|
||||
|
||||
def __init__(self, href: str) -> None:
|
||||
def __init__(self, markdown: Markdown, href: str) -> None:
|
||||
super().__init__()
|
||||
self.markdown: Markdown = markdown
|
||||
"""The `Markdown` widget containing the link clicked."""
|
||||
self.href: str = href
|
||||
"""The link that was selected."""
|
||||
|
||||
@property
|
||||
def control(self) -> Markdown:
|
||||
"""The `Markdown` widget containing the link clicked.
|
||||
|
||||
This is an alias for [`LinkClicked.markdown`][textual.widgets.Markdown.LinkClicked.markdown]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.markdown
|
||||
|
||||
def _on_mount(self, _: Mount) -> None:
|
||||
if self._markdown is not None:
|
||||
self.update(self._markdown)
|
||||
@@ -629,20 +666,20 @@ class Markdown(Widget):
|
||||
for token in parser.parse(markdown):
|
||||
if token.type == "heading_open":
|
||||
block_id += 1
|
||||
stack.append(HEADINGS[token.tag](id=f"block{block_id}"))
|
||||
stack.append(HEADINGS[token.tag](self, id=f"block{block_id}"))
|
||||
elif token.type == "hr":
|
||||
output.append(MarkdownHorizontalRule())
|
||||
output.append(MarkdownHorizontalRule(self))
|
||||
elif token.type == "paragraph_open":
|
||||
stack.append(MarkdownParagraph())
|
||||
stack.append(MarkdownParagraph(self))
|
||||
elif token.type == "blockquote_open":
|
||||
stack.append(MarkdownBlockQuote())
|
||||
stack.append(MarkdownBlockQuote(self))
|
||||
elif token.type == "bullet_list_open":
|
||||
stack.append(MarkdownBulletList())
|
||||
stack.append(MarkdownBulletList(self))
|
||||
elif token.type == "ordered_list_open":
|
||||
stack.append(MarkdownOrderedList())
|
||||
stack.append(MarkdownOrderedList(self))
|
||||
elif token.type == "list_item_open":
|
||||
if token.info:
|
||||
stack.append(MarkdownOrderedListItem(token.info))
|
||||
stack.append(MarkdownOrderedListItem(self, token.info))
|
||||
else:
|
||||
item_count = sum(
|
||||
1
|
||||
@@ -651,22 +688,23 @@ class Markdown(Widget):
|
||||
)
|
||||
stack.append(
|
||||
MarkdownUnorderedListItem(
|
||||
self.BULLETS[item_count % len(self.BULLETS)]
|
||||
self,
|
||||
self.BULLETS[item_count % len(self.BULLETS)],
|
||||
)
|
||||
)
|
||||
|
||||
elif token.type == "table_open":
|
||||
stack.append(MarkdownTable())
|
||||
stack.append(MarkdownTable(self))
|
||||
elif token.type == "tbody_open":
|
||||
stack.append(MarkdownTBody())
|
||||
stack.append(MarkdownTBody(self))
|
||||
elif token.type == "thead_open":
|
||||
stack.append(MarkdownTHead())
|
||||
stack.append(MarkdownTHead(self))
|
||||
elif token.type == "tr_open":
|
||||
stack.append(MarkdownTR())
|
||||
stack.append(MarkdownTR(self))
|
||||
elif token.type == "th_open":
|
||||
stack.append(MarkdownTH())
|
||||
stack.append(MarkdownTH(self))
|
||||
elif token.type == "td_open":
|
||||
stack.append(MarkdownTD())
|
||||
stack.append(MarkdownTD(self))
|
||||
elif token.type.endswith("_close"):
|
||||
block = stack.pop()
|
||||
if token.type == "heading_close":
|
||||
@@ -742,12 +780,13 @@ class Markdown(Widget):
|
||||
elif token.type == "fence":
|
||||
output.append(
|
||||
MarkdownFence(
|
||||
self,
|
||||
token.content.rstrip(),
|
||||
token.info,
|
||||
)
|
||||
)
|
||||
|
||||
self.post_message(Markdown.TableOfContentsUpdated(table_of_contents))
|
||||
self.post_message(Markdown.TableOfContentsUpdated(self, table_of_contents))
|
||||
with self.app.batch_update():
|
||||
self.query("MarkdownBlock").remove()
|
||||
self.mount_all(output)
|
||||
@@ -768,6 +807,27 @@ class MarkdownTableOfContents(Widget, can_focus_children=True):
|
||||
|
||||
table_of_contents = reactive["TableOfContentsType | None"](None, init=False)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
markdown: Markdown,
|
||||
name: str | None = None,
|
||||
id: str | None = None,
|
||||
classes: str | None = None,
|
||||
disabled: bool = False,
|
||||
) -> None:
|
||||
"""Initialize a table of contents.
|
||||
|
||||
Args:
|
||||
markdown: The Markdown document associated with this table of contents.
|
||||
name: The name of the widget.
|
||||
id: The ID of the widget in the DOM.
|
||||
classes: The CSS classes for the widget.
|
||||
disabled: Whether the widget is disabled or not.
|
||||
"""
|
||||
self.markdown = markdown
|
||||
"""The Markdown document associated with this table of contents."""
|
||||
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
tree: Tree = Tree("TOC")
|
||||
tree.show_root = False
|
||||
@@ -804,8 +864,9 @@ class MarkdownTableOfContents(Widget, can_focus_children=True):
|
||||
node_data = message.node.data
|
||||
if node_data is not None:
|
||||
await self._post_message(
|
||||
Markdown.TableOfContentsSelected(node_data["block_id"])
|
||||
Markdown.TableOfContentsSelected(self.markdown, node_data["block_id"])
|
||||
)
|
||||
message.stop()
|
||||
|
||||
|
||||
class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True):
|
||||
@@ -896,8 +957,9 @@ class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True):
|
||||
self.set_class(show_table_of_contents, "-show-table-of-contents")
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield MarkdownTableOfContents()
|
||||
yield Markdown(parser_factory=self._parser_factory)
|
||||
markdown = Markdown(parser_factory=self._parser_factory)
|
||||
yield MarkdownTableOfContents(markdown)
|
||||
yield markdown
|
||||
|
||||
def _on_markdown_table_of_contents_updated(
|
||||
self, message: Markdown.TableOfContentsUpdated
|
||||
|
||||
@@ -255,6 +255,15 @@ class OptionList(ScrollView, can_focus=True):
|
||||
self.option_index: int = index
|
||||
"""The index of the option that the message relates to."""
|
||||
|
||||
@property
|
||||
def control(self) -> OptionList:
|
||||
"""The option list that sent the message.
|
||||
|
||||
This is an alias for [`OptionMessage.option_list`][textual.widgets.OptionList.OptionMessage.option_list]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.option_list
|
||||
|
||||
def __rich_repr__(self) -> Result:
|
||||
yield "option_list", self.option_list
|
||||
yield "option", self.option
|
||||
|
||||
@@ -92,6 +92,15 @@ class RadioSet(Container, can_focus=True, can_focus_children=False):
|
||||
self.index = radio_set.pressed_index
|
||||
"""The index of the [`RadioButton`][textual.widgets.RadioButton] that was pressed to make the change."""
|
||||
|
||||
@property
|
||||
def control(self) -> RadioSet:
|
||||
"""A reference to the [`RadioSet`][textual.widgets.RadioSet] that was changed.
|
||||
|
||||
This is an alias for [`Changed.radio_set`][textual.widgets.RadioSet.Changed.radio_set]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.radio_set
|
||||
|
||||
def __rich_repr__(self) -> rich.repr.Result:
|
||||
yield "radio_set", self.radio_set
|
||||
yield "pressed", self.pressed
|
||||
|
||||
@@ -96,9 +96,20 @@ class TabbedContent(Widget):
|
||||
tab: The Tab widget that was selected (contains the tab label).
|
||||
"""
|
||||
self.tabbed_content = tabbed_content
|
||||
"""The `TabbedContent` widget that contains the tab activated."""
|
||||
self.tab = tab
|
||||
"""The `Tab` widget that was selected (contains the tab label)."""
|
||||
super().__init__()
|
||||
|
||||
@property
|
||||
def control(self) -> TabbedContent:
|
||||
"""The `TabbedContent` widget that contains the tab activated.
|
||||
|
||||
This is an alias for [`TabActivated.tabbed_content`][textual.widgets.TabbedContent.TabActivated.tabbed_content]
|
||||
and is used by the [`on`][textual.on] decorator.
|
||||
"""
|
||||
return self.tabbed_content
|
||||
|
||||
def __rich_repr__(self) -> Result:
|
||||
yield self.tabbed_content
|
||||
yield self.tab
|
||||
|
||||
@@ -510,57 +510,101 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
|
||||
|
||||
Can be handled using `on_tree_node_collapsed` in a subclass of `Tree` or in a
|
||||
parent node in the DOM.
|
||||
|
||||
Attributes:
|
||||
node: The node that was collapsed.
|
||||
"""
|
||||
|
||||
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
|
||||
def __init__(
|
||||
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
|
||||
) -> None:
|
||||
self.tree = tree
|
||||
"""The tree that sent the message."""
|
||||
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
|
||||
|
||||
class NodeExpanded(Generic[EventTreeDataType], Message, bubble=True):
|
||||
"""Event sent when a node is expanded.
|
||||
|
||||
Can be handled using `on_tree_node_expanded` in a subclass of `Tree` or in a
|
||||
parent node in the DOM.
|
||||
|
||||
Attributes:
|
||||
node: The node that was expanded.
|
||||
"""
|
||||
|
||||
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
|
||||
def __init__(
|
||||
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
|
||||
) -> None:
|
||||
self.tree = tree
|
||||
"""The tree that sent the message."""
|
||||
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
|
||||
|
||||
class NodeHighlighted(Generic[EventTreeDataType], Message, bubble=True):
|
||||
"""Event sent when a node is highlighted.
|
||||
|
||||
Can be handled using `on_tree_node_highlighted` in a subclass of `Tree` or in a
|
||||
parent node in the DOM.
|
||||
|
||||
Attributes:
|
||||
node: The node that was highlighted.
|
||||
"""
|
||||
|
||||
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
|
||||
def __init__(
|
||||
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
|
||||
) -> None:
|
||||
self.tree = tree
|
||||
"""The tree that sent the message."""
|
||||
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
|
||||
|
||||
class NodeSelected(Generic[EventTreeDataType], Message, bubble=True):
|
||||
"""Event sent when a node is selected.
|
||||
|
||||
Can be handled using `on_tree_node_selected` in a subclass of `Tree` or in a
|
||||
parent node in the DOM.
|
||||
|
||||
Attributes:
|
||||
node: The node that was selected.
|
||||
"""
|
||||
|
||||
def __init__(self, node: TreeNode[EventTreeDataType]) -> None:
|
||||
def __init__(
|
||||
self, tree: Tree[EventTreeDataType], node: TreeNode[EventTreeDataType]
|
||||
) -> None:
|
||||
self.tree = tree
|
||||
"""The tree that sent the message."""
|
||||
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
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
label: TextType,
|
||||
@@ -859,7 +903,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(node))
|
||||
self.post_message(self.NodeHighlighted(self, node))
|
||||
else:
|
||||
self._cursor_node = None
|
||||
|
||||
@@ -1113,10 +1157,10 @@ class Tree(Generic[TreeDataType], ScrollView, can_focus=True):
|
||||
return
|
||||
if node.is_expanded:
|
||||
node.collapse()
|
||||
self.post_message(self.NodeCollapsed(node))
|
||||
self.post_message(self.NodeCollapsed(self, node))
|
||||
else:
|
||||
node.expand()
|
||||
self.post_message(self.NodeExpanded(node))
|
||||
self.post_message(self.NodeExpanded(self, node))
|
||||
|
||||
async def _on_click(self, event: events.Click) -> None:
|
||||
meta = event.style.meta
|
||||
@@ -1203,4 +1247,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(node))
|
||||
self.post_message(self.NodeSelected(self, node))
|
||||
|
||||
Reference in New Issue
Block a user