mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
fixes for state updates
This commit is contained in:
@@ -30,7 +30,7 @@ App > Screen {
|
||||
overflow-y: auto;
|
||||
height: 20;
|
||||
margin: 1 2;
|
||||
background: $panel;
|
||||
background: $surface;
|
||||
padding: 1 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -899,11 +899,12 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.log.system(f"{self.screen} is active")
|
||||
return previous_screen
|
||||
|
||||
def set_focus(self, widget: Widget | None) -> None:
|
||||
def set_focus(self, widget: Widget | None, scroll_visible: bool = False) -> None:
|
||||
"""Focus (or unfocus) a widget. A focused widget will receive key events first.
|
||||
|
||||
Args:
|
||||
widget (Widget): [description]
|
||||
widget (Widget): Widget to focus.
|
||||
scroll_visible (bool, optional): Scroll widget in to view.
|
||||
"""
|
||||
if widget == self.focused:
|
||||
# Widget is already focused
|
||||
@@ -924,7 +925,8 @@ class App(Generic[ReturnType], DOMNode):
|
||||
# Change focus
|
||||
self.focused = widget
|
||||
# Send focus event
|
||||
self.screen.scroll_to_widget(widget)
|
||||
if scroll_visible:
|
||||
self.screen.scroll_to_widget(widget)
|
||||
widget.post_message_no_wait(events.Focus(self))
|
||||
widget.emit_no_wait(events.DescendantFocus(self))
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from asyncio import Lock
|
||||
from fractions import Fraction
|
||||
from itertools import islice
|
||||
from operator import attrgetter
|
||||
from types import GeneratorType
|
||||
from typing import TYPE_CHECKING, ClassVar, Collection, Iterable, NamedTuple
|
||||
|
||||
import rich.repr
|
||||
|
||||
@@ -11,9 +11,8 @@ from rich.text import Text
|
||||
|
||||
from .. import events
|
||||
from ..message import Message
|
||||
from ..reactive import Reactive
|
||||
from .._types import MessageTarget
|
||||
from ._tree_control import TreeControl, TreeClick, TreeNode, NodeID
|
||||
from ._tree_control import TreeControl, TreeNode
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -91,6 +90,9 @@ class DirectoryTree(TreeControl[DirEntry]):
|
||||
icon_label.apply_meta(meta)
|
||||
return icon_label
|
||||
|
||||
def on_styles_updated(self) -> None:
|
||||
self.render_tree_label.cache_clear()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.call_later(self.load_directory, self.root)
|
||||
|
||||
@@ -105,7 +107,9 @@ class DirectoryTree(TreeControl[DirEntry]):
|
||||
node.expand()
|
||||
self.refresh(layout=True)
|
||||
|
||||
async def on_tree_control_node_selected(self, message: TreeClick[DirEntry]) -> None:
|
||||
async def on_tree_control_node_selected(
|
||||
self, message: TreeControl.NodeSelected[DirEntry]
|
||||
) -> None:
|
||||
dir_entry = message.node.data
|
||||
if not dir_entry.is_dir:
|
||||
await self.emit(FileClick(self, dir_entry.path))
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import ClassVar, Generic, Iterator, NewType, TypeVar
|
||||
|
||||
import rich.repr
|
||||
from rich.console import RenderableType
|
||||
from rich.style import Style
|
||||
from rich.text import Text, TextType
|
||||
from rich.tree import Tree
|
||||
|
||||
@@ -21,6 +22,7 @@ NodeID = NewType("NodeID", int)
|
||||
|
||||
|
||||
NodeDataType = TypeVar("NodeDataType")
|
||||
EventNodeDataType = TypeVar("EventNodeDataType")
|
||||
|
||||
|
||||
@rich.repr.auto
|
||||
@@ -159,21 +161,11 @@ class TreeNode(Generic[NodeDataType]):
|
||||
return self._control.render_node(self)
|
||||
|
||||
|
||||
@rich.repr.auto
|
||||
class TreeClick(Generic[NodeDataType], Message, bubble=True):
|
||||
def __init__(self, sender: MessageTarget, node: TreeNode[NodeDataType]) -> None:
|
||||
self.node = node
|
||||
super().__init__(sender)
|
||||
|
||||
def __rich_repr__(self) -> rich.repr.Result:
|
||||
yield "node", self.node
|
||||
|
||||
|
||||
class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
|
||||
DEFAULT_CSS = """
|
||||
TreeControl {
|
||||
background: $panel;
|
||||
color: $text-panel;
|
||||
background: $surface;
|
||||
color: $text-surface;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -183,7 +175,13 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
|
||||
}
|
||||
|
||||
TreeControl > .tree--guides-highlight {
|
||||
color: $secondary;
|
||||
color: $success;
|
||||
text-style: uu;
|
||||
|
||||
}
|
||||
|
||||
TreeControl > .tree--guides-cursor {
|
||||
color: $secondary;
|
||||
text-style: bold;
|
||||
}
|
||||
|
||||
@@ -201,12 +199,15 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
|
||||
COMPONENT_CLASSES: ClassVar[set[str]] = {
|
||||
"tree--guides",
|
||||
"tree--guides-highlight",
|
||||
"tree--guides-cursor",
|
||||
"tree--labels",
|
||||
"tree--cursor",
|
||||
}
|
||||
|
||||
class NodeSelected(Message, bubble=False):
|
||||
def __init__(self, sender: MessageTarget, node: TreeNode[NodeDataType]) -> None:
|
||||
class NodeSelected(Generic[EventNodeDataType], Message, bubble=False):
|
||||
def __init__(
|
||||
self, sender: MessageTarget, node: TreeNode[EventNodeDataType]
|
||||
) -> None:
|
||||
self.node = node
|
||||
super().__init__(sender)
|
||||
|
||||
@@ -238,33 +239,10 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
|
||||
cursor_line: Reactive[int] = Reactive(0)
|
||||
show_cursor: Reactive[bool] = Reactive(False)
|
||||
|
||||
def watch_show_cursor(self, value: bool) -> None:
|
||||
line_region = Region(0, self.cursor_line, self.size.width, 1)
|
||||
self.emit_no_wait(messages.ScrollToRegion(self, line_region))
|
||||
|
||||
def watch_cursor_line(self, value: int) -> None:
|
||||
line_region = Region(0, value, self.size.width, 1)
|
||||
self.emit_no_wait(messages.ScrollToRegion(self, line_region))
|
||||
|
||||
def watch_hover_node(self, previous_hover_node: NodeID, hover_node: NodeID) -> None:
|
||||
previous_hover = self.nodes.get(previous_hover_node)
|
||||
if previous_hover is not None:
|
||||
previous_hover._tree.guide_style = self._guide_style
|
||||
hover = self.nodes.get(hover_node)
|
||||
if hover is not None:
|
||||
hover._tree.guide_style = self._highlight_guide_style
|
||||
self.refresh()
|
||||
|
||||
def watch_cursor(self, previous_cursor_node: NodeID, cursor_node: NodeID) -> None:
|
||||
|
||||
previous_cursor = self.nodes.get(previous_cursor_node)
|
||||
if previous_cursor is not None:
|
||||
previous_cursor._tree.guide_style = self._guide_style
|
||||
cursor = self.nodes.get(cursor_node)
|
||||
if cursor is not None:
|
||||
cursor._tree.guide_style = self._highlight_guide_style
|
||||
self.refresh()
|
||||
|
||||
def get_content_height(self, container: Size, viewport: Size, width: int) -> int:
|
||||
def get_size(tree: Tree) -> int:
|
||||
return 1 + sum(
|
||||
@@ -321,6 +299,23 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
|
||||
return None
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
guide_style = self._guide_style
|
||||
|
||||
def update_guide_style(tree: Tree) -> None:
|
||||
tree.guide_style = guide_style
|
||||
for child in tree.children:
|
||||
if child.expanded:
|
||||
update_guide_style(child)
|
||||
|
||||
update_guide_style(self._tree)
|
||||
if self.hover_node is not None:
|
||||
hover = self.nodes.get(self.hover_node)
|
||||
if hover is not None:
|
||||
hover._tree.guide_style = self._highlight_guide_style
|
||||
if self.cursor is not None and self.show_cursor:
|
||||
cursor = self.nodes.get(self.cursor)
|
||||
if cursor is not None:
|
||||
cursor._tree.guide_style = self._cursor_guide_style
|
||||
return self._tree
|
||||
|
||||
def render_node(self, node: TreeNode[NodeDataType]) -> RenderableType:
|
||||
@@ -343,12 +338,33 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
|
||||
self.post_message_no_wait(self.NodeSelected(self, node))
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self._guide_style = self.get_component_styles("tree--guides").rich_style
|
||||
self._highlight_guide_style = self.get_component_styles(
|
||||
"tree--guides-highlight"
|
||||
).rich_style
|
||||
self._tree.guide_style = self._guide_style
|
||||
|
||||
@property
|
||||
def _guide_style(self) -> Style:
|
||||
return self.get_component_styles("tree--guides").rich_style
|
||||
|
||||
@property
|
||||
def _highlight_guide_style(self) -> Style:
|
||||
return self.get_component_styles("tree--guides-highlight").rich_style
|
||||
|
||||
@property
|
||||
def _cursor_guide_style(self) -> Style:
|
||||
return self.get_component_styles("tree--guides-cursor").rich_style
|
||||
|
||||
def on_styles_updated(self) -> None:
|
||||
guide_style = self._guide_style
|
||||
|
||||
def update_guide_style(tree: Tree) -> None:
|
||||
tree.guide_style = guide_style
|
||||
for child in tree.children:
|
||||
if child.expanded:
|
||||
update_guide_style(child)
|
||||
|
||||
update_guide_style(self._tree)
|
||||
self._render_cache = None
|
||||
self.refresh()
|
||||
|
||||
def on_mouse_move(self, event: events.MouseMove) -> None:
|
||||
self.hover_node = event.style.meta.get("tree_node")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user