diff --git a/examples/basic.py b/examples/basic.py index d9a20e989..965332876 100644 --- a/examples/basic.py +++ b/examples/basic.py @@ -15,11 +15,17 @@ class BasicApp(App): #sidebar { text: bold #09312e on #3CAEA3; - /* dock-group: header; */ + dock-group: side; width: 30; height: 1fr; layer: panels; border-right: vkey #09312e; + display: block; + offset-x: -15 + } + + #sidebar.-active { + display: block; } #header { @@ -27,7 +33,6 @@ class BasicApp(App): dock-group: header; height: 3; border: hkey white; - } #footer { @@ -45,6 +50,9 @@ class BasicApp(App): """ + async def on_load(self) -> None: + await self.bind("t", "toggle('#sidebar', '-active')") + async def on_mount(self) -> None: """Build layout here.""" diff --git a/src/textual/app.py b/src/textual/app.py index ff068226f..6440b1e37 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -572,6 +572,9 @@ class App(DOMNode): async def action_bell(self) -> None: self.console.bell() + async def action_toggle(self, selector: str, class_name: str) -> None: + self.view.query(selector).toggle_class(class_name) + if __name__ == "__main__": import asyncio diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index e05682394..4ac470897 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -57,7 +57,8 @@ class StylesBuilder: def process_display(self, name: str, tokens: list[Token]) -> None: for token in tokens: - _, _, location, name, value = token + name, value, _, _, location = token + if name == "token": value = value.lower() if value in VALID_DISPLAY: diff --git a/src/textual/css/query.py b/src/textual/css/query.py index 7d4c65d24..9081b9efb 100644 --- a/src/textual/css/query.py +++ b/src/textual/css/query.py @@ -11,7 +11,6 @@ from .parse import parse_selectors if TYPE_CHECKING: from ..dom import DOMNode - from ..widget import Widget @rich.repr.auto(angular=True) @@ -65,5 +64,6 @@ class DOMQuery: node.remove_class(*class_names) def toggle_class(self, *class_names: str) -> None: + for node in self._nodes: node.remove_class(*class_names) diff --git a/src/textual/css/tokenize.py b/src/textual/css/tokenize.py index 146b9faf8..b5912398b 100644 --- a/src/textual/css/tokenize.py +++ b/src/textual/css/tokenize.py @@ -45,7 +45,7 @@ expect_declaration_content = Expect( whitespace=r"\s+", comment_start=r"\/\*", percentage=r"\d+\%", - scalar=r"\d+\.?\d*(?:fr|%)?", + scalar=r"\-?\d+\.?\d*(?:fr|%)?", color=r"\#[0-9a-fA-F]{6}|color\([0-9]{1,3}\)|rgb\(\d{1,3}\,\s?\d{1,3}\,\s?\d{1,3}\)", key_value=r"[a-zA-Z_-][a-zA-Z0-9_-]*=[0-9a-zA-Z_\-\/]+", token="[a-zA-Z_-]+", diff --git a/src/textual/widget.py b/src/textual/widget.py index e46bc66a5..355c2d3cd 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -77,12 +77,12 @@ class Widget(DOMNode): super().__init__(name=name, id=id) - visible: Reactive[bool] = Reactive(True, layout=True) + # visible: Reactive[bool] = Reactive(True, layout=True) layout_size: Reactive[int | None] = Reactive(None, layout=True) layout_fraction: Reactive[int] = Reactive(1, layout=True) layout_min_size: Reactive[int] = Reactive(1, layout=True) - layout_offset_x: Reactive[float] = Reactive(0.0, layout=True) - layout_offset_y: Reactive[float] = Reactive(0.0, layout=True) + # layout_offset_x: Reactive[float] = Reactive(0.0, layout=True) + # layout_offset_y: Reactive[float] = Reactive(0.0, layout=True) style: Reactive[str | None] = Reactive(None) padding: Reactive[Spacing | None] = Reactive(None, layout=True) @@ -97,11 +97,11 @@ class Widget(DOMNode): def validate_margin(self, margin: SpacingDimensions) -> Spacing: return Spacing.unpack(margin) - def validate_layout_offset_x(self, value) -> int: - return int(value) + # def validate_layout_offset_x(self, value) -> int: + # return int(value) - def validate_layout_offset_y(self, value) -> int: - return int(value) + # def validate_layout_offset_y(self, value) -> int: + # return int(value) def __init_subclass__(cls, can_focus: bool = True) -> None: super().__init_subclass__() @@ -144,21 +144,15 @@ class Widget(DOMNode): if styles.has_border: renderable = Border(renderable, styles.border) - # _border_style = self.console.get_style(self.border_style) - # renderable = Border( - # renderable, - # ( - # ("heavy", _border_style), - # ("heavy", _border_style), - # ("heavy", _border_style), - # ("heavy", _border_style), - # ), - # ) if self.margin is not None: renderable = Padding(renderable, self.margin) renderable = Styled(renderable, styles.text) return renderable + @property + def visible(self) -> bool: + return self.styles.display == "block" + @property def size(self) -> Size: return self._size @@ -187,7 +181,8 @@ class Widget(DOMNode): @property def layout_offset(self) -> tuple[int, int]: """Get the layout offset as a tuple.""" - return (round(self.layout_offset_x), round(self.layout_offset_y)) + x, y = self.styles.offset + return round(x), round(y) @property def gutter(self) -> Spacing: