mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Invisible widgets now dont render
This commit is contained in:
49
examples/dev_sandbox.css
Normal file
49
examples/dev_sandbox.css
Normal 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
32
examples/dev_sandbox.py
Normal 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")
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
28
tests/test_widget.py
Normal 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
|
||||||
Reference in New Issue
Block a user