Invisible widgets now dont render

This commit is contained in:
Darren Burns
2022-01-20 11:39:56 +00:00
parent 185788b760
commit 678a67ce2d
7 changed files with 180 additions and 57 deletions

49
examples/dev_sandbox.css Normal file
View File

@@ -0,0 +1,49 @@
/* CSS file for dev_sandbox.py */
App > View {
docks: side=left/1;
text: on #20639b;
}
Widget:hover {
outline: heavy;
text: bold !important;
}
#sidebar {
text: #09312e on #3caea3;
dock: side;
width: 30;
offset-x: -100%;
transition: offset 500ms in_out_cubic;
border-right: outer #09312e;
}
#sidebar.-active {
offset-x: 0;
}
#header {
text: white on #173f5f;
height: 3;
border: hkey;
}
#header.-visible {
visibility: hidden;
}
#content {
text: white on #20639b;
border-bottom: hkey #0f2b41;
offset-y: -3;
}
#content.-content-visible {
visibility: hidden;
}
#footer {
text: #3a3009 on #f6d55c;
height: 3;
}

32
examples/dev_sandbox.py Normal file
View File

@@ -0,0 +1,32 @@
from rich.console import RenderableType
from rich.panel import Panel
from textual.app import App
from textual.widget import Widget
class PanelWidget(Widget):
def render(self) -> RenderableType:
return Panel("hello world!", title="Title")
class BasicApp(App):
"""Sandbox application used for testing/development by Textual developers"""
def on_load(self):
"""Bind keys here."""
self.bind("tab", "toggle_class('#sidebar', '-active')")
self.bind("a", "toggle_class('#header', '-visible')")
self.bind("c", "toggle_class('#content', '-content-visible')")
def on_mount(self):
"""Build layout here."""
self.mount(
header=Widget(),
content=PanelWidget(),
footer=Widget(),
sidebar=Widget(),
)
BasicApp.run(css_file="test_app.css", watch_css=True, log="textual.log")

View File

@@ -9,7 +9,7 @@ from rich.style import Style
from rich.tree import Tree from rich.tree import Tree
from .css._error_tools import friendly_list from .css._error_tools import friendly_list
from .css.constants import VALID_DISPLAY from .css.constants import VALID_DISPLAY, VALID_VISIBILITY
from .css.errors import StyleValueError from .css.errors import StyleValueError
from .css.styles import Styles from .css.styles import Styles
from .message_pump import MessagePump from .message_pump import MessagePump
@@ -160,6 +160,22 @@ class DOMNode(MessagePump):
f"expected {friendly_list(VALID_DISPLAY)})", f"expected {friendly_list(VALID_DISPLAY)})",
) )
@property
def visible(self) -> bool:
return self.styles.visibility != "hidden"
@visible.setter
def visible(self, new_value: bool) -> None:
if isinstance(new_value, bool):
self.styles.visibility = "visible" if new_value else "hidden"
elif new_value in VALID_VISIBILITY:
self.styles.visibility = new_value
else:
raise StyleValueError(
f"invalid value for visibility (received {new_value!r}, "
f"expected {friendly_list(VALID_VISIBILITY)})"
)
@property @property
def z(self) -> tuple[int, ...]: def z(self) -> tuple[int, ...]:
"""Get the z index tuple for this node. """Get the z index tuple for this node.

View File

@@ -199,6 +199,7 @@ class Layout(ABC):
raise NoWidget(f"No widget under screen coordinate ({x}, {y})") raise NoWidget(f"No widget under screen coordinate ({x}, {y})")
def get_style_at(self, x: int, y: int) -> Style: def get_style_at(self, x: int, y: int) -> Style:
"""Get the Style at the given cell or Style.null()"""
try: try:
widget, region = self.get_widget_at(x, y) widget, region = self.get_widget_at(x, y)
except NoWidget: except NoWidget:
@@ -217,6 +218,18 @@ class Layout(ABC):
return Style.null() return Style.null()
def get_widget_region(self, widget: Widget) -> Region: def get_widget_region(self, widget: Widget) -> Region:
"""Get the Region of a Widget contained in this Layout.
Args:
widget: The Widget in this layout you wish to know the Region of.
Raises:
KeyError: If the Widget is not contained in this Layout.
Returns:
Region: The Region of the Widget.
"""
try: try:
region, *_ = self.map[widget] region, *_ = self.map[widget]
except KeyError: except KeyError:
@@ -270,7 +283,7 @@ class Layout(ABC):
for widget, region, _order, clip in widget_regions: for widget, region, _order, clip in widget_regions:
if not widget.is_visual: if not (widget.is_visual and widget.visible):
continue continue
lines = widget._get_lines() lines = widget._get_lines()

View File

@@ -1,26 +1,22 @@
from __future__ import annotations from __future__ import annotations
from itertools import chain from typing import Callable, Iterable, TYPE_CHECKING
from typing import Callable, Iterable, ClassVar, TYPE_CHECKING
from rich.console import RenderableType
import rich.repr import rich.repr
from rich.console import RenderableType
from rich.style import Style from rich.style import Style
from . import events
from . import errors from . import errors
from . import log from . import events
from . import messages from . import messages
from .geometry import Size, Offset, Region
from .layout import Layout, NoWidget, WidgetPlacement from .layout import Layout, NoWidget, WidgetPlacement
from .layouts.factory import get_layout from .layouts.factory import get_layout
from .geometry import Size, Offset, Region
from .reactive import Reactive, watch from .reactive import Reactive, watch
from .widget import Widget
from .widget import Widget, Widget
if TYPE_CHECKING: if TYPE_CHECKING:
from .app import App pass
class LayoutProperty: class LayoutProperty:

View File

@@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from logging import PercentStyle, getLogger from logging import getLogger
from typing import ( from typing import (
Any, Any,
Awaitable, Awaitable,
@@ -11,35 +11,29 @@ from typing import (
NamedTuple, NamedTuple,
cast, cast,
) )
import rich.repr
from rich import box
from rich.align import Align
from rich.console import Console, RenderableType, ConsoleOptions
from rich.measure import Measurement
from rich.panel import Panel
from rich.padding import Padding
from rich.pretty import Pretty
from rich.segment import Segment
from rich.style import Style, StyleType
from rich.styled import Styled
from rich.text import Text, TextType
from . import events import rich.repr
from rich.align import Align
from rich.console import Console, RenderableType
from rich.padding import Padding
from rich.style import Style
from rich.styled import Styled
from rich.text import Text
from . import errors from . import errors
from . import events
from ._animator import BoundAnimator from ._animator import BoundAnimator
from ._border import Border, BORDER_STYLES from ._border import Border
from ._callback import invoke from ._callback import invoke
from .blank import Blank
from .dom import DOMNode
from ._context import active_app from ._context import active_app
from .geometry import Size, Spacing, SpacingDimensions from ._types import Lines
from .dom import DOMNode
from .geometry import Size, Spacing
from .message import Message from .message import Message
from .messages import Layout, Update from .messages import Layout, Update
from .reactive import Reactive, watch from .reactive import watch
from ._types import Lines
if TYPE_CHECKING: if TYPE_CHECKING:
from .app import App
from .view import View from .view import View
log = getLogger("rich") log = getLogger("rich")
@@ -163,34 +157,29 @@ class Widget(DOMNode):
styles = self.styles styles = self.styles
parent_text_style = self.parent.text_style parent_text_style = self.parent.text_style
if styles.visibility == "hidden": text_style = styles.text
renderable = Blank(parent_text_style) renderable_text_style = parent_text_style + text_style
else: if renderable_text_style:
text_style = styles.text renderable = Styled(renderable, renderable_text_style)
renderable_text_style = parent_text_style + text_style
if renderable_text_style:
renderable = Styled(renderable, renderable_text_style)
if styles.has_padding: if styles.has_padding:
renderable = Padding( renderable = Padding(
renderable, styles.padding, style=renderable_text_style renderable, styles.padding, style=renderable_text_style
) )
if styles.has_border: if styles.has_border:
renderable = Border( renderable = Border(renderable, styles.border, style=renderable_text_style)
renderable, styles.border, style=renderable_text_style
)
if styles.has_margin: if styles.has_margin:
renderable = Padding(renderable, styles.margin, style=parent_text_style) renderable = Padding(renderable, styles.margin, style=parent_text_style)
if styles.has_outline: if styles.has_outline:
renderable = Border( renderable = Border(
renderable, renderable,
styles.outline, styles.outline,
outline=True, outline=True,
style=renderable_text_style, style=renderable_text_style,
) )
return renderable return renderable

28
tests/test_widget.py Normal file
View File

@@ -0,0 +1,28 @@
import pytest
from textual.css.errors import StyleValueError
from textual.widget import Widget
@pytest.mark.parametrize(
"set_val, get_val, style_str", [
[True, True, "visible"],
[False, False, "hidden"],
["hidden", False, "hidden"],
["visible", True, "visible"],
])
def test_widget_set_visible_true(set_val, get_val, style_str):
widget = Widget()
widget.visible = set_val
assert widget.visible is get_val
assert widget.styles.visibility == style_str
def test_widget_set_visible_invalid_string():
widget = Widget()
with pytest.raises(StyleValueError):
widget.visible = "nope! no widget for me!"
assert widget.visible