From 14b70b08194551aa32e51b5df169d70ec4763ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 18:53:29 +0100 Subject: [PATCH 1/8] Add control to option list messages. --- CHANGELOG.md | 5 +++++ docs/widgets/option_list.md | 13 +------------ src/textual/widgets/_option_list.py | 9 +++++++++ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbc8be178..e77683ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The DataTable cursor is now scrolled into view when the cursor coordinate is changed programmatically https://github.com/Textualize/textual/issues/2459 - run_worker exclusive parameter is now `False` by default https://github.com/Textualize/textual/pull/2470 +### Added + +- Added the `control` property to messages + - `OptionList.OptionHighlighted` and `OptionList.OptionSelected` + ## [0.23.0] - 2023-05-03 ### Fixed diff --git a/docs/widgets/option_list.md b/docs/widgets/option_list.md index 2c4a80789..a489e6b4f 100644 --- a/docs/widgets/option_list.md +++ b/docs/widgets/option_list.md @@ -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 diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py index 97744971a..c65eae740 100644 --- a/src/textual/widgets/_option_list.py +++ b/src/textual/widgets/_option_list.py @@ -253,6 +253,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 From 55e2e5d7e3dfaa316721ba1340598cd9790d9a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 18:55:12 +0100 Subject: [PATCH 2/8] Add control to tree/dir tree messages. --- CHANGELOG.md | 2 + src/textual/widgets/_directory_tree.py | 15 ++++- src/textual/widgets/_tree.py | 84 ++++++++++++++++++++------ 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e77683ec0..5126bac11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added the `control` property to messages - `OptionList.OptionHighlighted` and `OptionList.OptionSelected` + - `Tree.NodeSelected`, `Tree.NodeHighlighted`, `Tree.NodeExpanded`, and `Tree.NodeCollapsed` + - `DirectoryTree.FileSelected` ## [0.23.0] - 2023-05-03 diff --git a/src/textual/widgets/_directory_tree.py b/src/textual/widgets/_directory_tree.py index b2a8ab692..20b9117e1 100644 --- a/src/textual/widgets/_directory_tree.py +++ b/src/textual/widgets/_directory_tree.py @@ -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. diff --git a/src/textual/widgets/_tree.py b/src/textual/widgets/_tree.py index 91ffe794e..20011d804 100644 --- a/src/textual/widgets/_tree.py +++ b/src/textual/widgets/_tree.py @@ -464,57 +464,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, @@ -813,7 +857,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)) def watch_guide_depth(self, guide_depth: int) -> None: self._invalidate() @@ -1065,10 +1109,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 @@ -1155,4 +1199,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)) From 2187a1d4a3de1469493dcabd1a7859d17a377322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 18:55:51 +0100 Subject: [PATCH 3/8] Add control to list view messages. --- CHANGELOG.md | 1 + src/textual/widgets/_list_view.py | 32 +++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5126bac11..13880432a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `OptionList.OptionHighlighted` and `OptionList.OptionSelected` - `Tree.NodeSelected`, `Tree.NodeHighlighted`, `Tree.NodeExpanded`, and `Tree.NodeCollapsed` - `DirectoryTree.FileSelected` + - `ListView.Highlighted` and `ListView.Selected` ## [0.23.0] - 2023-05-03 diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index efd76d4d6..e0073a52c 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -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 [`ListView.Highlighted`][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 [`ListView.Selected`][textual.widgets.ListView.Selected.list_view] + and is used by the [`on`][textual.on] decorator. + """ + return self.list_view def __init__( self, From a10d2d9f988eb75d29c750a56da162db0330cbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 18:59:05 +0100 Subject: [PATCH 4/8] Reference container markdown document. Adding these references to the sub-widgets that make up a markdown document is necessary in order for the blocks to be able to post messages with a reference to the original document, which in turn is needed for the Message.control property to work. --- CHANGELOG.md | 2 + src/textual/widgets/_markdown.py | 73 ++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13880432a..7488e339f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### 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 - `OptionList.OptionHighlighted` and `OptionList.OptionSelected` - `Tree.NodeSelected`, `Tree.NodeHighlighted`, `Tree.NodeExpanded`, and `Tree.NodeCollapsed` diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 654f838d9..966584228 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -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) @@ -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( @@ -629,20 +631,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 +653,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 +745,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 +772,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 +829,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 +922,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 From 8bf5316d65b5e53a3bea93efeaa1d74e13ab9c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 19:00:38 +0100 Subject: [PATCH 5/8] Added control to markdown messages. --- CHANGELOG.md | 1 + src/textual/widgets/_markdown.py | 45 ++++++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7488e339f..c7320e88e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `Tree.NodeSelected`, `Tree.NodeHighlighted`, `Tree.NodeExpanded`, and `Tree.NodeCollapsed` - `DirectoryTree.FileSelected` - `ListView.Highlighted` and `ListView.Selected` + - `Markdown.TableOfContentsUpdated`, `Markdown.TableOfContentsSelected`, and `Markdown.LinkClicked` ## [0.23.0] - 2023-05-03 diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 966584228..738edd675 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -105,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): @@ -567,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) From 9d1f0283b21ffff4d71db4f7427ff13fe5eba060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 19:02:01 +0100 Subject: [PATCH 6/8] Add control to RadioSet message. --- CHANGELOG.md | 1 + src/textual/widgets/_radio_set.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7320e88e..60f70ba18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `DirectoryTree.FileSelected` - `ListView.Highlighted` and `ListView.Selected` - `Markdown.TableOfContentsUpdated`, `Markdown.TableOfContentsSelected`, and `Markdown.LinkClicked` + - `RadioSet.Changed` ## [0.23.0] - 2023-05-03 diff --git a/src/textual/widgets/_radio_set.py b/src/textual/widgets/_radio_set.py index 4003686b4..0366e42fc 100644 --- a/src/textual/widgets/_radio_set.py +++ b/src/textual/widgets/_radio_set.py @@ -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 From a30cd159c4f83378999af31689ac27e517c9a16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 19:04:10 +0100 Subject: [PATCH 7/8] Add control to TabbedContent message. --- CHANGELOG.md | 20 ++++++++++++++++---- src/textual/widgets/_tabbed_content.py | 11 +++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60f70ba18..a60d76c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,12 +21,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - 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 - - `OptionList.OptionHighlighted` and `OptionList.OptionSelected` - - `Tree.NodeSelected`, `Tree.NodeHighlighted`, `Tree.NodeExpanded`, and `Tree.NodeCollapsed` - `DirectoryTree.FileSelected` - - `ListView.Highlighted` and `ListView.Selected` - - `Markdown.TableOfContentsUpdated`, `Markdown.TableOfContentsSelected`, and `Markdown.LinkClicked` + - `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 diff --git a/src/textual/widgets/_tabbed_content.py b/src/textual/widgets/_tabbed_content.py index e842f56ce..f069e4033 100644 --- a/src/textual/widgets/_tabbed_content.py +++ b/src/textual/widgets/_tabbed_content.py @@ -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 From 73e4d5c6a79266c71fd212b82fa407fa6ae1fd65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Gir=C3=A3o=20Serr=C3=A3o?= <5621605+rodrigogiraoserrao@users.noreply.github.com> Date: Thu, 4 May 2023 19:09:31 +0100 Subject: [PATCH 8/8] Fix docs link. --- src/textual/widgets/_list_view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index e0073a52c..7c5145e96 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -57,7 +57,7 @@ class ListView(VerticalScroll, can_focus=True, can_focus_children=False): def control(self) -> ListView: """The view that contains the item highlighted. - This is an alias for [`ListView.Highlighted`][textual.widgets.ListView.Highlighted.list_view] + 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 @@ -80,7 +80,7 @@ class ListView(VerticalScroll, can_focus=True, can_focus_children=False): def control(self) -> ListView: """The view that contains the item selected. - This is an alias for [`ListView.Selected`][textual.widgets.ListView.Selected.list_view] + 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