diff --git a/docs/events/mount.md b/docs/events/mount.md index ff393acb6..950d93a2e 100644 --- a/docs/events/mount.md +++ b/docs/events/mount.md @@ -2,6 +2,8 @@ The `Mount` event is sent to a widget and Application when it is first mounted. +The mount event is typically used to set the initial state of a widget or to add new children widgets. + - [ ] Bubbles ## Parameters diff --git a/docs/events/resize.md b/docs/events/resize.md index 1c47f63b5..36a7e7562 100644 --- a/docs/events/resize.md +++ b/docs/events/resize.md @@ -17,3 +17,7 @@ The `Resize` event is sent to a widget when its size changes and when it is firs `event.container_size` : The size of the widget's container. + +## Code + +::: textual.events.Mount diff --git a/docs/examples/styles/display.py b/docs/examples/styles/display.py new file mode 100644 index 000000000..5528b5b2f --- /dev/null +++ b/docs/examples/styles/display.py @@ -0,0 +1,24 @@ +from textual.app import App +from textual.widget import Widget + + +class WidthApp(App): + CSS = """ + Screen > Widget { + height: 5; + background: blue; + color: white; + border: heavy white; + } + Widget.hidden { + display: none; + } + """ + + def compose(self): + yield Widget(id="widget1") + yield Widget(id="widget2", classes="hidden") + yield Widget(id="widget3") + + +app = WidthApp() diff --git a/docs/examples/styles/height.py b/docs/examples/styles/height.py new file mode 100644 index 000000000..c1863d5b2 --- /dev/null +++ b/docs/examples/styles/height.py @@ -0,0 +1,18 @@ +from textual.app import App +from textual.widget import Widget + + +class WidthApp(App): + CSS = """ + Screen > Widget { + background: green; + height: 50%; + color: white; + } + """ + + def compose(self): + yield Widget() + + +app = WidthApp() diff --git a/docs/examples/styles/width.py b/docs/examples/styles/width.py index 78ee6b699..4a1a0d0e1 100644 --- a/docs/examples/styles/width.py +++ b/docs/examples/styles/width.py @@ -4,9 +4,10 @@ from textual.widget import Widget class WidthApp(App): CSS = """ - Widget { - background: blue 50%; + Screen > Widget { + background: green; width: 50%; + color: white; } """ diff --git a/docs/introduction.md b/docs/introduction.md index 144497747..0f124b842 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -42,9 +42,9 @@ The first step in all Textual applications is to import the `App` class from `te --8<-- "docs/examples/introduction/intro01.py" ``` -This App class is responsible for loading data, setting up the screen, managing events etc. In a real app most of the core logic of your application will be contained within methods on the this class. +This App class is responsible for loading data, setting up the screen, managing events etc. In a real app most of the core logic of your application will be contained within methods on this class. -The last two lines create an instance of the application and calls `run()`: +The last two lines create an instance of the application and calls the `run()` method: ```python hl_lines="8 9" title="intro01.py" --8<-- "docs/examples/introduction/intro01.py" @@ -130,7 +130,7 @@ This script imports App as before, but also the `Widget` class from `textual.wid Widgets support many of the same events as the Application itself, and can be thought of as mini-applications in their own right. The Clock widget responds to a Mount event which is the first event received when a widget is _mounted_ (added to the App). The code in `Clock.on_mount` sets `styles.content_align` to tuple of `("center", "middle")` which tells Textual to display the Widget's content aligned to the horizontal center, and in the middle vertically. If you resize the terminal, you should find the time remains in the center. -The second line in `on_mount` calls `self.set_interval` which tells Textual to invoke the `self.refresh` function once a second to refresh the Clock widget. +The second line in `on_mount` calls `self.set_interval` which tells Textual to invoke the `self.refresh` method once a second. When Textual refreshes a widget it calls it's `render` method: diff --git a/docs/styles/display.md b/docs/styles/display.md new file mode 100644 index 000000000..53ecaaaf2 --- /dev/null +++ b/docs/styles/display.md @@ -0,0 +1,48 @@ +# Display + +The `display` property defines if a Widget is displayed or not. The default value is `"block"` which will display the widget as normal. Setting the property to `"none"` will effectively make it invisible. + +## Example + +Note that the second widget is hidden by adding the "hidden" class which sets the display style to None. + +=== "display.py" + + ```python + --8<-- "docs/examples/styles/display.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/display.py"} + ``` + +## CSS + +```sass +/* Widget is on screen */ +display: block; + +/* Widget is not on the screen */ +display: none; +``` + +## Python + +```python +# Hide the widget +self.styles.display = "none" + +# Show the widget again +self.styles.display = "block" +``` + +There is also a shortcut to show / hide a widget. The `display` property on `Widget` may be set to `True` or `False` to show or hide the widget. + +```python +# Hide the widget +widget.display = False + +# Show the widget +widget.display = True +``` diff --git a/docs/styles/height.md b/docs/styles/height.md new file mode 100644 index 000000000..4a877e131 --- /dev/null +++ b/docs/styles/height.md @@ -0,0 +1,37 @@ +# Height + +The `height` property sets a widget's height. By default, it sets the width of the content area, but if `box-sizing` is set to `border-box` it sets the width of the border area. + +## Example + +=== "width.py" + + ```python + --8<-- "docs/examples/styles/height.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/height.py"} + ``` + +## CSS + +```sass +/* Explicit cell height */ +height: 10; + +/* Percentage height */ +height: 50%; + +/* Automatic height */ +width: auto +``` + +## Python + +```python +self.styles.height = 10 +self.styles.height = "50% +self.styles.height = "auto" +``` diff --git a/mkdocs.yml b/mkdocs.yml index cd5aee9e3..f7ddbf67e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,7 +11,9 @@ nav: - "events/mount.md" - "events/resize.md" - Styles: + - "styles/display.md" - "styles/width.md" + - "styles/height.md" - Widgets: "/widgets/" - Reference: - "reference/app.md" diff --git a/src/textual/events.py b/src/textual/events.py index c5b96dacb..a2822ec60 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -87,8 +87,7 @@ class Action(Event): class Resize(Event, verbosity=2, bubble=False): """Sent when the app or widget has been resized.""" - __slots__ = ["size"] - size: Size + __slots__ = ["size", "virtual_size", "container_size"] def __init__( self, diff --git a/src/textual/geometry.py b/src/textual/geometry.py index 73498d8ee..8ae92c2bf 100644 --- a/src/textual/geometry.py +++ b/src/textual/geometry.py @@ -45,10 +45,13 @@ def clamp(value: T, minimum: T, maximum: T) -> T: class Offset(NamedTuple): - """A point defined by x and y coordinates.""" + """A cell offset defined by x and y coordinates. Offsets are typically relative to the + top left of the terminal or other container.""" x: int = 0 + """Offset in the x-axis (horizontal)""" y: int = 0 + """Offset in the y-axis (vertical)""" @property def is_origin(self) -> bool: @@ -118,7 +121,10 @@ class Size(NamedTuple): """An area defined by its width and height.""" width: int = 0 + """The width in cells.""" + height: int = 0 + """The height in cells.""" def __bool__(self) -> bool: """A Size is Falsy if it has area 0.""" @@ -196,12 +202,32 @@ class Size(NamedTuple): class Region(NamedTuple): - """Defines a rectangular region.""" + """Defines a rectangular region. + + A Region consists a coordinate (x and y) and dimensions (width and height). + + ``` + (x, y) + ┌────────────────────┐ ▲ + │ │ │ + │ │ │ + │ │ height + │ │ │ + │ │ │ + └────────────────────┘ ▼ + ◀─────── width ──────▶ + ``` + + """ x: int = 0 + """Offset in the x-axis (horizontal)""" y: int = 0 + """Offset in the y-axis (vertical)""" width: int = 0 + """The widget of the region""" height: int = 0 + """The height of the region""" @classmethod def from_union( @@ -754,9 +780,13 @@ class Spacing(NamedTuple): """The spacing around a renderable.""" top: int = 0 + """Space from the top of a region.""" right: int = 0 + """Space from the left of a region.""" bottom: int = 0 + """Space from the bottom of a region.""" left: int = 0 + """Space from the left of a region.""" def __bool__(self) -> bool: return self != (0, 0, 0, 0)