fixes for state updates

This commit is contained in:
Will McGugan
2022-09-07 17:54:23 +01:00
parent 8b6605453b
commit 04d3fc1408
5 changed files with 71 additions and 50 deletions

View File

@@ -30,7 +30,7 @@ App > Screen {
overflow-y: auto; overflow-y: auto;
height: 20; height: 20;
margin: 1 2; margin: 1 2;
background: $panel; background: $surface;
padding: 1 2; padding: 1 2;
} }

View File

@@ -899,11 +899,12 @@ class App(Generic[ReturnType], DOMNode):
self.log.system(f"{self.screen} is active") self.log.system(f"{self.screen} is active")
return previous_screen 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. """Focus (or unfocus) a widget. A focused widget will receive key events first.
Args: Args:
widget (Widget): [description] widget (Widget): Widget to focus.
scroll_visible (bool, optional): Scroll widget in to view.
""" """
if widget == self.focused: if widget == self.focused:
# Widget is already focused # Widget is already focused
@@ -924,7 +925,8 @@ class App(Generic[ReturnType], DOMNode):
# Change focus # Change focus
self.focused = widget self.focused = widget
# Send focus event # 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.post_message_no_wait(events.Focus(self))
widget.emit_no_wait(events.DescendantFocus(self)) widget.emit_no_wait(events.DescendantFocus(self))

View File

@@ -4,7 +4,6 @@ from asyncio import Lock
from fractions import Fraction from fractions import Fraction
from itertools import islice from itertools import islice
from operator import attrgetter from operator import attrgetter
from types import GeneratorType
from typing import TYPE_CHECKING, ClassVar, Collection, Iterable, NamedTuple from typing import TYPE_CHECKING, ClassVar, Collection, Iterable, NamedTuple
import rich.repr import rich.repr

View File

@@ -11,9 +11,8 @@ from rich.text import Text
from .. import events from .. import events
from ..message import Message from ..message import Message
from ..reactive import Reactive
from .._types import MessageTarget from .._types import MessageTarget
from ._tree_control import TreeControl, TreeClick, TreeNode, NodeID from ._tree_control import TreeControl, TreeNode
@dataclass @dataclass
@@ -91,6 +90,9 @@ class DirectoryTree(TreeControl[DirEntry]):
icon_label.apply_meta(meta) icon_label.apply_meta(meta)
return icon_label return icon_label
def on_styles_updated(self) -> None:
self.render_tree_label.cache_clear()
def on_mount(self) -> None: def on_mount(self) -> None:
self.call_later(self.load_directory, self.root) self.call_later(self.load_directory, self.root)
@@ -105,7 +107,9 @@ class DirectoryTree(TreeControl[DirEntry]):
node.expand() node.expand()
self.refresh(layout=True) 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 dir_entry = message.node.data
if not dir_entry.is_dir: if not dir_entry.is_dir:
await self.emit(FileClick(self, dir_entry.path)) await self.emit(FileClick(self, dir_entry.path))

View File

@@ -5,6 +5,7 @@ from typing import ClassVar, Generic, Iterator, NewType, TypeVar
import rich.repr import rich.repr
from rich.console import RenderableType from rich.console import RenderableType
from rich.style import Style
from rich.text import Text, TextType from rich.text import Text, TextType
from rich.tree import Tree from rich.tree import Tree
@@ -21,6 +22,7 @@ NodeID = NewType("NodeID", int)
NodeDataType = TypeVar("NodeDataType") NodeDataType = TypeVar("NodeDataType")
EventNodeDataType = TypeVar("EventNodeDataType")
@rich.repr.auto @rich.repr.auto
@@ -159,21 +161,11 @@ class TreeNode(Generic[NodeDataType]):
return self._control.render_node(self) 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): class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
DEFAULT_CSS = """ DEFAULT_CSS = """
TreeControl { TreeControl {
background: $panel; background: $surface;
color: $text-panel; color: $text-surface;
height: auto; height: auto;
width: 100%; width: 100%;
} }
@@ -183,7 +175,13 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
} }
TreeControl > .tree--guides-highlight { TreeControl > .tree--guides-highlight {
color: $secondary; color: $success;
text-style: uu;
}
TreeControl > .tree--guides-cursor {
color: $secondary;
text-style: bold; text-style: bold;
} }
@@ -201,12 +199,15 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
COMPONENT_CLASSES: ClassVar[set[str]] = { COMPONENT_CLASSES: ClassVar[set[str]] = {
"tree--guides", "tree--guides",
"tree--guides-highlight", "tree--guides-highlight",
"tree--guides-cursor",
"tree--labels", "tree--labels",
"tree--cursor", "tree--cursor",
} }
class NodeSelected(Message, bubble=False): class NodeSelected(Generic[EventNodeDataType], Message, bubble=False):
def __init__(self, sender: MessageTarget, node: TreeNode[NodeDataType]) -> None: def __init__(
self, sender: MessageTarget, node: TreeNode[EventNodeDataType]
) -> None:
self.node = node self.node = node
super().__init__(sender) super().__init__(sender)
@@ -238,33 +239,10 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
cursor_line: Reactive[int] = Reactive(0) cursor_line: Reactive[int] = Reactive(0)
show_cursor: Reactive[bool] = Reactive(False) 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: def watch_cursor_line(self, value: int) -> None:
line_region = Region(0, value, self.size.width, 1) line_region = Region(0, value, self.size.width, 1)
self.emit_no_wait(messages.ScrollToRegion(self, line_region)) 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_content_height(self, container: Size, viewport: Size, width: int) -> int:
def get_size(tree: Tree) -> int: def get_size(tree: Tree) -> int:
return 1 + sum( return 1 + sum(
@@ -321,6 +299,23 @@ class TreeControl(Generic[NodeDataType], Widget, can_focus=True):
return None return None
def render(self) -> RenderableType: 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 return self._tree
def render_node(self, node: TreeNode[NodeDataType]) -> RenderableType: 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)) self.post_message_no_wait(self.NodeSelected(self, node))
def on_mount(self) -> None: 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 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: def on_mouse_move(self, event: events.MouseMove) -> None:
self.hover_node = event.style.meta.get("tree_node") self.hover_node = event.style.meta.get("tree_node")