mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
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.
This commit is contained in:
@@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
### Added
|
### 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
|
- Added the `control` property to messages
|
||||||
- `OptionList.OptionHighlighted` and `OptionList.OptionSelected`
|
- `OptionList.OptionHighlighted` and `OptionList.OptionSelected`
|
||||||
- `Tree.NodeSelected`, `Tree.NodeHighlighted`, `Tree.NodeExpanded`, and `Tree.NodeCollapsed`
|
- `Tree.NodeSelected`, `Tree.NodeHighlighted`, `Tree.NodeExpanded`, and `Tree.NodeCollapsed`
|
||||||
|
|||||||
@@ -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._text = Text()
|
||||||
self._blocks: list[MarkdownBlock] = []
|
self._blocks: list[MarkdownBlock] = []
|
||||||
super().__init__(*args, **kwargs)
|
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
|
self.bullet = bullet
|
||||||
super().__init__()
|
super().__init__(markdown)
|
||||||
|
|
||||||
|
|
||||||
class MarkdownOrderedListItem(MarkdownListItem):
|
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.code = code
|
||||||
self.lexer = lexer
|
self.lexer = lexer
|
||||||
super().__init__()
|
super().__init__(markdown)
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield Static(
|
yield Static(
|
||||||
@@ -629,20 +631,20 @@ class Markdown(Widget):
|
|||||||
for token in parser.parse(markdown):
|
for token in parser.parse(markdown):
|
||||||
if token.type == "heading_open":
|
if token.type == "heading_open":
|
||||||
block_id += 1
|
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":
|
elif token.type == "hr":
|
||||||
output.append(MarkdownHorizontalRule())
|
output.append(MarkdownHorizontalRule(self))
|
||||||
elif token.type == "paragraph_open":
|
elif token.type == "paragraph_open":
|
||||||
stack.append(MarkdownParagraph())
|
stack.append(MarkdownParagraph(self))
|
||||||
elif token.type == "blockquote_open":
|
elif token.type == "blockquote_open":
|
||||||
stack.append(MarkdownBlockQuote())
|
stack.append(MarkdownBlockQuote(self))
|
||||||
elif token.type == "bullet_list_open":
|
elif token.type == "bullet_list_open":
|
||||||
stack.append(MarkdownBulletList())
|
stack.append(MarkdownBulletList(self))
|
||||||
elif token.type == "ordered_list_open":
|
elif token.type == "ordered_list_open":
|
||||||
stack.append(MarkdownOrderedList())
|
stack.append(MarkdownOrderedList(self))
|
||||||
elif token.type == "list_item_open":
|
elif token.type == "list_item_open":
|
||||||
if token.info:
|
if token.info:
|
||||||
stack.append(MarkdownOrderedListItem(token.info))
|
stack.append(MarkdownOrderedListItem(self, token.info))
|
||||||
else:
|
else:
|
||||||
item_count = sum(
|
item_count = sum(
|
||||||
1
|
1
|
||||||
@@ -651,22 +653,23 @@ class Markdown(Widget):
|
|||||||
)
|
)
|
||||||
stack.append(
|
stack.append(
|
||||||
MarkdownUnorderedListItem(
|
MarkdownUnorderedListItem(
|
||||||
self.BULLETS[item_count % len(self.BULLETS)]
|
self,
|
||||||
|
self.BULLETS[item_count % len(self.BULLETS)],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif token.type == "table_open":
|
elif token.type == "table_open":
|
||||||
stack.append(MarkdownTable())
|
stack.append(MarkdownTable(self))
|
||||||
elif token.type == "tbody_open":
|
elif token.type == "tbody_open":
|
||||||
stack.append(MarkdownTBody())
|
stack.append(MarkdownTBody(self))
|
||||||
elif token.type == "thead_open":
|
elif token.type == "thead_open":
|
||||||
stack.append(MarkdownTHead())
|
stack.append(MarkdownTHead(self))
|
||||||
elif token.type == "tr_open":
|
elif token.type == "tr_open":
|
||||||
stack.append(MarkdownTR())
|
stack.append(MarkdownTR(self))
|
||||||
elif token.type == "th_open":
|
elif token.type == "th_open":
|
||||||
stack.append(MarkdownTH())
|
stack.append(MarkdownTH(self))
|
||||||
elif token.type == "td_open":
|
elif token.type == "td_open":
|
||||||
stack.append(MarkdownTD())
|
stack.append(MarkdownTD(self))
|
||||||
elif token.type.endswith("_close"):
|
elif token.type.endswith("_close"):
|
||||||
block = stack.pop()
|
block = stack.pop()
|
||||||
if token.type == "heading_close":
|
if token.type == "heading_close":
|
||||||
@@ -742,12 +745,13 @@ class Markdown(Widget):
|
|||||||
elif token.type == "fence":
|
elif token.type == "fence":
|
||||||
output.append(
|
output.append(
|
||||||
MarkdownFence(
|
MarkdownFence(
|
||||||
|
self,
|
||||||
token.content.rstrip(),
|
token.content.rstrip(),
|
||||||
token.info,
|
token.info,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.post_message(Markdown.TableOfContentsUpdated(table_of_contents))
|
self.post_message(Markdown.TableOfContentsUpdated(self, table_of_contents))
|
||||||
with self.app.batch_update():
|
with self.app.batch_update():
|
||||||
self.query("MarkdownBlock").remove()
|
self.query("MarkdownBlock").remove()
|
||||||
self.mount_all(output)
|
self.mount_all(output)
|
||||||
@@ -768,6 +772,27 @@ class MarkdownTableOfContents(Widget, can_focus_children=True):
|
|||||||
|
|
||||||
table_of_contents = reactive["TableOfContentsType | None"](None, init=False)
|
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:
|
def compose(self) -> ComposeResult:
|
||||||
tree: Tree = Tree("TOC")
|
tree: Tree = Tree("TOC")
|
||||||
tree.show_root = False
|
tree.show_root = False
|
||||||
@@ -804,8 +829,9 @@ class MarkdownTableOfContents(Widget, can_focus_children=True):
|
|||||||
node_data = message.node.data
|
node_data = message.node.data
|
||||||
if node_data is not None:
|
if node_data is not None:
|
||||||
await self._post_message(
|
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):
|
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")
|
self.set_class(show_table_of_contents, "-show-table-of-contents")
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
yield MarkdownTableOfContents()
|
markdown = Markdown(parser_factory=self._parser_factory)
|
||||||
yield Markdown(parser_factory=self._parser_factory)
|
yield MarkdownTableOfContents(markdown)
|
||||||
|
yield markdown
|
||||||
|
|
||||||
def _on_markdown_table_of_contents_updated(
|
def _on_markdown_table_of_contents_updated(
|
||||||
self, message: Markdown.TableOfContentsUpdated
|
self, message: Markdown.TableOfContentsUpdated
|
||||||
|
|||||||
Reference in New Issue
Block a user