From fa8c0e8f95d28ad1c5c2ec77b2067660098134d0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 5 Feb 2024 21:16:26 +0000 Subject: [PATCH] fix refresh lines --- docs/guide/queries.md | 12 +++++++----- src/textual/app.py | 3 ++- src/textual/css/query.py | 4 ++-- src/textual/dom.py | 14 +++++++++++--- src/textual/renderables/background_screen.py | 9 ++++++++- src/textual/renderables/tint.py | 6 +++++- src/textual/scroll_view.py | 6 ++++-- src/textual/widgets/_tabbed_content.py | 3 +++ 8 files changed, 42 insertions(+), 15 deletions(-) diff --git a/docs/guide/queries.md b/docs/guide/queries.md index 0b1e5fc10..d87a78a39 100644 --- a/docs/guide/queries.md +++ b/docs/guide/queries.md @@ -161,10 +161,12 @@ for widget in self.query("Button"): Here are the other loop-free methods on query objects: -- [set_class][textual.css.query.DOMQuery.set_class] Sets a CSS class (or classes) on matched widgets. - [add_class][textual.css.query.DOMQuery.add_class] Adds a CSS class (or classes) to matched widgets. -- [remove_class][textual.css.query.DOMQuery.remove_class] Removes a CSS class (or classes) from matched widgets. -- [toggle_class][textual.css.query.DOMQuery.toggle_class] Sets a CSS class (or classes) if it is not set, or removes the class (or classes) if they are set on the matched widgets. -- [remove][textual.css.query.DOMQuery.remove] Removes matched widgets from the DOM. +- [blur][textual.css.query.DOMQuery.focus] Blurs (removes focus) from matching widgets. +- [focus][textual.css.query.DOMQuery.focus] Focuses the first matching widgets. - [refresh][textual.css.query.DOMQuery.refresh] Refreshes matched widgets. - +- [remove_class][textual.css.query.DOMQuery.remove_class] Removes a CSS class (or classes) from matched widgets. +- [remove][textual.css.query.DOMQuery.remove] Removes matched widgets from the DOM. +- [set_class][textual.css.query.DOMQuery.set_class] Sets a CSS class (or classes) on matched widgets. +- [set][textual.css.query.DOMQuery.set] Sets common attributes on a widget. +- [toggle_class][textual.css.query.DOMQuery.toggle_class] Sets a CSS class (or classes) if it is not set, or removes the class (or classes) if they are set on the matched widgets. diff --git a/src/textual/app.py b/src/textual/app.py index 5c54cb898..68499fec5 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -424,8 +424,9 @@ class App(Generic[ReturnType], DOMNode): environ = dict(os.environ) no_color = environ.pop("NO_COLOR", None) if no_color is not None: - from .filter import Monochrome + from .filter import ANSIToTruecolor, Monochrome + self._filters.append(ANSIToTruecolor(terminal_theme.DIMMED_MONOKAI)) self._filters.append(Monochrome()) for filter_name in constants.FILTERS.split(","): diff --git a/src/textual/css/query.py b/src/textual/css/query.py index a8520af50..05aa74962 100644 --- a/src/textual/css/query.py +++ b/src/textual/css/query.py @@ -223,7 +223,7 @@ class DOMQuery(Generic[QueryType]): ) return first else: - raise NoMatches(f"No nodes match {self!r}") + raise NoMatches(f"No nodes match {self!r} on {self.node!r}") @overload def only_one(self) -> QueryType: ... @@ -293,7 +293,7 @@ class DOMQuery(Generic[QueryType]): The matching Widget. """ if not self.nodes: - raise NoMatches(f"No nodes match {self!r}") + raise NoMatches(f"No nodes match {self!r} on dom{self.node!r}") last = self.nodes[-1] if expect_type is not None and not isinstance(last, expect_type): raise WrongType( diff --git a/src/textual/dom.py b/src/textual/dom.py index 873630156..becd83029 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -290,6 +290,7 @@ class DOMNode(MessagePump): def setter(value: object) -> None: """Set bound data.""" + _rich_traceback_omit = True Reactive._initialize_object(self) setattr(self, variable_name, value) @@ -1405,6 +1406,13 @@ class DOMNode(MessagePump): def refresh(self, *, repaint: bool = True, layout: bool = False) -> Self: return self - async def action_toggle(self, value_name: str) -> None: - value = getattr(self, value_name) - setattr(self, value_name, not value) + async def action_toggle(self, attribute_name: str) -> None: + """Toggle an attribute on the node. + + Assumes the attribute is a bool. + + Args: + attribute_name: Name of the attribute. + """ + value = getattr(self, attribute_name) + setattr(self, attribute_name, not value) diff --git a/src/textual/renderables/background_screen.py b/src/textual/renderables/background_screen.py index 70ed79f1b..7dd4bc04d 100644 --- a/src/textual/renderables/background_screen.py +++ b/src/textual/renderables/background_screen.py @@ -2,11 +2,13 @@ from __future__ import annotations from typing import TYPE_CHECKING, Iterable +from rich import terminal_theme from rich.console import Console, ConsoleOptions, RenderResult from rich.segment import Segment from rich.style import Style from ..color import Color +from ..filter import ANSIToTruecolor if TYPE_CHECKING: from ..screen import Screen @@ -49,12 +51,17 @@ class BackgroundScreen: _Segment = Segment NULL_STYLE = Style() + truecolor_style = ANSIToTruecolor(terminal_theme.DIMMED_MONOKAI).truecolor_style for segment in segments: text, style, control = segment if control: yield segment else: - style = NULL_STYLE if style is None else style.clear_meta_and_links() + style = ( + NULL_STYLE + if style is None + else truecolor_style(style.clear_meta_and_links()) + ) yield _Segment( text, ( diff --git a/src/textual/renderables/tint.py b/src/textual/renderables/tint.py index ebdd38105..85d15e217 100644 --- a/src/textual/renderables/tint.py +++ b/src/textual/renderables/tint.py @@ -2,11 +2,13 @@ from __future__ import annotations from typing import Iterable +from rich import terminal_theme from rich.console import Console, ConsoleOptions, RenderableType, RenderResult from rich.segment import Segment from rich.style import Style from ..color import Color +from ..filter import ANSIToTruecolor class Tint: @@ -43,13 +45,15 @@ class Tint: style_from_color = Style.from_color _Segment = Segment + ansi_filter = ANSIToTruecolor(terminal_theme.DIMMED_MONOKAI) + NULL_STYLE = Style() for segment in segments: text, style, control = segment if control: yield segment else: - style = style or NULL_STYLE + style = ansi_filter.truecolor_style(style) or NULL_STYLE yield _Segment( text, ( diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index c77ef7bdc..4ac8619a9 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -152,7 +152,7 @@ class ScrollView(ScrollableContainer): """ width = self.virtual_size.width scroll_x, scroll_y = self.scroll_offset - self.refresh(Region(0, y - scroll_y, width, 1)) + self.refresh(Region(0, y - scroll_y, max(width, self.size.width), 1)) def refresh_lines(self, y_start: int, line_count: int = 1) -> None: """Refresh one or more lines. @@ -164,5 +164,7 @@ class ScrollView(ScrollableContainer): width = self.virtual_size.width scroll_x, scroll_y = self.scroll_offset - refresh_region = Region(0, y_start - scroll_y, width, line_count) + refresh_region = Region( + 0, y_start - scroll_y, max(width, self.size.width), line_count + ) self.refresh(refresh_region) diff --git a/src/textual/widgets/_tabbed_content.py b/src/textual/widgets/_tabbed_content.py index 5f05064b1..e7ae3eb25 100644 --- a/src/textual/widgets/_tabbed_content.py +++ b/src/textual/widgets/_tabbed_content.py @@ -324,6 +324,9 @@ class TabbedContent(Widget): @property def active_pane(self) -> TabPane | None: """The currently active pane, or `None` if no pane is active.""" + active = self.active + if not active: + return None return self.get_pane(self.active) def validate_active(self, active: str) -> str: