From 7ba36c1146cc85a2ee52250581eff4b56fb4eab5 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Jul 2022 13:03:49 +0100 Subject: [PATCH 01/30] docs --- docs/events/mount.md | 2 ++ docs/events/resize.md | 4 +++ docs/examples/styles/display.py | 24 +++++++++++++++++ docs/examples/styles/height.py | 18 +++++++++++++ docs/examples/styles/width.py | 5 ++-- docs/introduction.md | 6 ++--- docs/styles/display.md | 48 +++++++++++++++++++++++++++++++++ docs/styles/height.md | 37 +++++++++++++++++++++++++ mkdocs.yml | 2 ++ src/textual/events.py | 3 +-- src/textual/geometry.py | 34 +++++++++++++++++++++-- 11 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 docs/examples/styles/display.py create mode 100644 docs/examples/styles/height.py create mode 100644 docs/styles/display.md create mode 100644 docs/styles/height.md diff --git a/docs/events/mount.md b/docs/events/mount.md index ff393acb65..950d93a2ed 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 1c47f63b5b..36a7e75629 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 0000000000..5528b5b2f0 --- /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 0000000000..c1863d5b28 --- /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 78ee6b6990..4a1a0d0e19 100644 --- a/docs/examples/styles/width.py +++ b/docs/examples/styles/width.py @@ -4,9 +4,10 @@ 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 144497747b..0f124b8428 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 0000000000..53ecaaaf22 --- /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 0000000000..4a877e1318 --- /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 cd5aee9e34..f7ddbf67e2 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 c5b96dacb3..a2822ec602 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -87,8 +87,7 @@ def __rich_repr__(self) -> rich.repr.Result: 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 73498d8eee..8ae92c2bf3 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 @@ def __contains__(self, other: Any) -> bool: 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) From 7040d00c7bf7005c9a8486b6ac36b2e19c86994a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Jul 2022 17:01:51 +0100 Subject: [PATCH 02/30] more docs --- docs/examples/styles/background.py | 29 ++++++++++++++++++ docs/examples/styles/border.py | 34 +++++++++++++++++++++ docs/examples/styles/color.py | 28 +++++++++++++++++ docs/examples/styles/display.py | 27 +++++++++-------- docs/examples/styles/height.py | 4 +-- docs/examples/styles/margin.py | 27 +++++++++++++++++ docs/examples/styles/padding.py | 27 +++++++++++++++++ docs/examples/styles/text_style.py | 41 +++++++++++++++++++++++++ docs/examples/styles/visibility.py | 27 +++++++++++++++++ docs/styles/background.md | 41 +++++++++++++++++++++++++ docs/styles/color.md | 41 +++++++++++++++++++++++++ docs/styles/height.md | 2 +- docs/styles/margin.md | 34 +++++++++++++++++++++ docs/styles/padding.md | 34 +++++++++++++++++++++ docs/styles/text_style.md | 36 ++++++++++++++++++++++ docs/styles/visibility.md | 48 ++++++++++++++++++++++++++++++ docs/styles/width.md | 2 +- examples/borders.py | 29 ++++++++++++++++++ mkdocs.yml | 8 ++++- src/textual/constants.py | 11 +++++++ src/textual/layouts/horizontal.py | 2 +- tests/test_color.py | 33 ++++++++++++-------- 22 files changed, 534 insertions(+), 31 deletions(-) create mode 100644 docs/examples/styles/background.py create mode 100644 docs/examples/styles/border.py create mode 100644 docs/examples/styles/color.py create mode 100644 docs/examples/styles/margin.py create mode 100644 docs/examples/styles/padding.py create mode 100644 docs/examples/styles/text_style.py create mode 100644 docs/examples/styles/visibility.py create mode 100644 docs/styles/background.md create mode 100644 docs/styles/color.md create mode 100644 docs/styles/margin.md create mode 100644 docs/styles/padding.md create mode 100644 docs/styles/text_style.md create mode 100644 docs/styles/visibility.md create mode 100644 examples/borders.py create mode 100644 src/textual/constants.py diff --git a/docs/examples/styles/background.py b/docs/examples/styles/background.py new file mode 100644 index 0000000000..792cf4c852 --- /dev/null +++ b/docs/examples/styles/background.py @@ -0,0 +1,29 @@ +from textual.app import App +from textual.widgets import Static + + +class BackgroundApp(App): + CSS = """ + Static { + height:1fr; + content-align: center middle; + color: white; + } + #static1 { + background: red; + } + #static2 { + background: rgb(0, 255, 0); + } + #static3 { + background: hsl(240, 100%, 50%); + } + """ + + def compose(self): + yield Static("Hello, World!", id="static1") + yield Static("Hello, World!", id="static2") + yield Static("Hello, World!", id="static3") + + +app = BackgroundApp() diff --git a/docs/examples/styles/border.py b/docs/examples/styles/border.py new file mode 100644 index 0000000000..9589769390 --- /dev/null +++ b/docs/examples/styles/border.py @@ -0,0 +1,34 @@ +from textual.app import App +from textual.widgets import Static + + +class BorderApp(App): + CSS = """ + Screen > Static { + height:5; + content-align: center middle; + color: white; + margin: 1; + box-sizing: border-box; + } + #static1 { + background: red 20%; + border: solid red; + } + #static2 { + background: green 20%; + border: dashed green; + } + #static3 { + background: blue 20%; + border: tall blue; + } + """ + + def compose(self): + yield Static("Hello, World!", id="static1") + yield Static("Hello, World!", id="static2") + yield Static("Hello, World!", id="static3") + + +app = BorderApp() diff --git a/docs/examples/styles/color.py b/docs/examples/styles/color.py new file mode 100644 index 0000000000..6d29d9a300 --- /dev/null +++ b/docs/examples/styles/color.py @@ -0,0 +1,28 @@ +from textual.app import App +from textual.widgets import Static + + +class ColorApp(App): + CSS = """ + Static { + height:1fr; + content-align: center middle; + } + #static1 { + color: red; + } + #static2 { + color: rgb(0, 255, 0); + } + #static3 { + color: hsl(240, 100%, 50%) + } + """ + + def compose(self): + yield Static("Hello, World!", id="static1") + yield Static("Hello, World!", id="static2") + yield Static("Hello, World!", id="static3") + + +app = ColorApp() diff --git a/docs/examples/styles/display.py b/docs/examples/styles/display.py index 5528b5b2f0..cc5bd00219 100644 --- a/docs/examples/styles/display.py +++ b/docs/examples/styles/display.py @@ -1,24 +1,27 @@ from textual.app import App -from textual.widget import Widget +from textual.widgets import Static -class WidthApp(App): +class DisplayApp(App): CSS = """ - Screen > Widget { - height: 5; - background: blue; - color: white; - border: heavy white; + Screen { + background: green; } - Widget.hidden { + Static { + height: 5; + background: white; + color: blue; + border: heavy blue; + } + Static.remove { display: none; } """ def compose(self): - yield Widget(id="widget1") - yield Widget(id="widget2", classes="hidden") - yield Widget(id="widget3") + yield Static("Widget 1") + yield Static("widget 2", classes="remove") + yield Static("widget 3") -app = WidthApp() +app = DisplayApp() diff --git a/docs/examples/styles/height.py b/docs/examples/styles/height.py index c1863d5b28..f94baeeeb0 100644 --- a/docs/examples/styles/height.py +++ b/docs/examples/styles/height.py @@ -2,7 +2,7 @@ from textual.widget import Widget -class WidthApp(App): +class HeightApp(App): CSS = """ Screen > Widget { background: green; @@ -15,4 +15,4 @@ def compose(self): yield Widget() -app = WidthApp() +app = HeightApp() diff --git a/docs/examples/styles/margin.py b/docs/examples/styles/margin.py new file mode 100644 index 0000000000..c7aec1dfe5 --- /dev/null +++ b/docs/examples/styles/margin.py @@ -0,0 +1,27 @@ +from textual.app import App +from textual.widgets import Static + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain.""" + + +class MarginApp(App): + CSS = """ + + Static { + margin: 4 8; + background: blue 20%; + } + + """ + + def compose(self): + yield Static(TEXT) + + +app = MarginApp() diff --git a/docs/examples/styles/padding.py b/docs/examples/styles/padding.py new file mode 100644 index 0000000000..8b345514bf --- /dev/null +++ b/docs/examples/styles/padding.py @@ -0,0 +1,27 @@ +from textual.app import App +from textual.widgets import Static + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain.""" + + +class PaddingApp(App): + CSS = """ + + Static { + padding: 4 8; + background: blue 20%; + } + + """ + + def compose(self): + yield Static(TEXT) + + +app = PaddingApp() diff --git a/docs/examples/styles/text_style.py b/docs/examples/styles/text_style.py new file mode 100644 index 0000000000..42f9710d2b --- /dev/null +++ b/docs/examples/styles/text_style.py @@ -0,0 +1,41 @@ +from textual.app import App +from textual.widgets import Static + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain.""" + + +class TextStyleApp(App): + CSS = """ + Screen { + layout: horizontal; + } + Static { + width:1fr; + } + #static1 { + background: red 30%; + text-style: bold; + } + #static2 { + background: green 30%; + text-style: italic; + } + #static3 { + background: blue 30%; + text-style: reverse; + } + """ + + def compose(self): + yield Static(TEXT, id="static1") + yield Static(TEXT, id="static2") + yield Static(TEXT, id="static3") + + +app = TextStyleApp() diff --git a/docs/examples/styles/visibility.py b/docs/examples/styles/visibility.py new file mode 100644 index 0000000000..aeec9d228e --- /dev/null +++ b/docs/examples/styles/visibility.py @@ -0,0 +1,27 @@ +from textual.app import App +from textual.widgets import Static + + +class VisibilityApp(App): + CSS = """ + Screen { + background: green; + } + Static { + height: 5; + background: white; + color: blue; + border: heavy blue; + } + Static.invisible { + visibility: hidden; + } + """ + + def compose(self): + yield Static("Widget 1") + yield Static("widget 2", classes="invisible") + yield Static("widget 3") + + +app = VisibilityApp() diff --git a/docs/styles/background.md b/docs/styles/background.md new file mode 100644 index 0000000000..3530e0d80c --- /dev/null +++ b/docs/styles/background.md @@ -0,0 +1,41 @@ +# Background + +The `background` rule sets the background color of the widget. + +=== "background.py" + + ```python + --8<-- "docs/examples/styles/background.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/background.py"} + ``` + +## CSS + +```sass +/* Blue background */ +background: blue; + +/* 20% red backround */ +background: red 20%; + +/* RGB color */ +background: rgb(100,120,200); +``` + +## Python + +You can use the same syntax as CSS, or explicitly set a Color object. + +```python +# Set blue background +widget.styles.background = "blue" + +from textual.color import Color +# Set with a color object +widget.styles.background = Color.parse("pink") + +``` diff --git a/docs/styles/color.md b/docs/styles/color.md new file mode 100644 index 0000000000..51806d8575 --- /dev/null +++ b/docs/styles/color.md @@ -0,0 +1,41 @@ +# Color + +The `color` rule sets the text color of a Widget. + +=== "color.py" + + ```python + --8<-- "docs/examples/styles/color.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/color.py"} + ``` + +## CSS + +```sass +/* Blue background */ +color: blue; + +/* 20% red backround */ +color: red 20%; + +/* RGB color */ +color: rgb(100,120,200); +``` + +## Python + +You can use the same syntax as CSS, or explicitly set a Color object. + +```python +# Set blue background +widget.styles.color = "blue" + +from textual.color import Color +# Set with a color object +widget.styles.color = Color.parse("pink") + +``` diff --git a/docs/styles/height.md b/docs/styles/height.md index 4a877e1318..12aefac09d 100644 --- a/docs/styles/height.md +++ b/docs/styles/height.md @@ -1,6 +1,6 @@ # 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. +The `height` style 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 diff --git a/docs/styles/margin.md b/docs/styles/margin.md new file mode 100644 index 0000000000..a0cc4d4430 --- /dev/null +++ b/docs/styles/margin.md @@ -0,0 +1,34 @@ +# Margin + +The `margin` rule adds space around the entire widget. + +- `1` Sets a margin of 1 around all 4 edges +- `1 2` Sets a margin of 1 on the top and bottom edges, and a margin of 2 on the left and right edges +- `1 2 3 4` Sets a margin of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. + +## Example + +=== "margin.py" + + ```python + --8<-- "docs/examples/styles/margin.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/margin.py"} + ``` + +## CSS + +```sass +/* Set margin of 2 on the top and bottom edges, and 4 on the left and right */ +margin: 2 4; +``` + +## Python + +```python +# In Python you can set the margin as a tuple of integers +widget.styles.margin = (2, 3) +``` diff --git a/docs/styles/padding.md b/docs/styles/padding.md new file mode 100644 index 0000000000..0c8272fbc2 --- /dev/null +++ b/docs/styles/padding.md @@ -0,0 +1,34 @@ +# Padding + +The padding rule adds space around the content of a widget. You can specify padding with 1, 2 or 4 numbers. + +- `1` Sets a padding of 1 around all 4 edges +- `1 2` Sets a padding of 1 on the top and bottom edges, and a padding of two on the left and right edges +- `1 2 3 4` Sets a padding of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. + +## Example + +=== "padding.py" + + ```python + --8<-- "docs/examples/styles/padding.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/padding.py"} + ``` + +## CSS + +```sass +/* Set padding of 2 on the top and bottom edges, and 4 on the left and right */ +padding: 2 4; +``` + +## Python + +```python +# In Python you can set the padding as a tuple of integers +widget.styles.padding = (2, 3) +``` diff --git a/docs/styles/text_style.md b/docs/styles/text_style.md new file mode 100644 index 0000000000..d5869f9bfa --- /dev/null +++ b/docs/styles/text_style.md @@ -0,0 +1,36 @@ +# Text-style + +The `text-style` rule enables a number of different ways of displaying text. The value may be set to any of the following: + +- `"bold"` Sets **bold text** +- `"italic"` Sets _italic text_ +- `"reverse"` Sets reverse video text (foreground and background colors reversed) +- `"underline"` Sets underline text +- `"strike"` Sets strikethrough text + +Text styles may be set in combination. For example "bold underline" or "reverse underline strike". + +## Example + +=== "text_style.py" + + ```python + --8<-- "docs/examples/styles/text_style.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/text_style.py"} + ``` + +## CSS + +```sass +text-style: italic; +``` + +## Python + +```python +widget.styles.text_style = "italic" +``` diff --git a/docs/styles/visibility.md b/docs/styles/visibility.md new file mode 100644 index 0000000000..9db7ba10b6 --- /dev/null +++ b/docs/styles/visibility.md @@ -0,0 +1,48 @@ +# Visibility + +The `visibility` rule may be used to make a widget invisible while still reserving spacing for it. The default value is `"visible"` which will cause the Widget to be displayed as normal. Setting the value to `"hidden"` will cause the Widget to be removed from the screen. + +## Example + +Note that the second widget is hidden, while leaving a space where it would have been rendered. + +=== "visibility.py" + + ```python + --8<-- "docs/examples/styles/visibility.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/visibility.py"} + ``` + +## CSS + +```sass +/* Widget is on screen */ +visibility: visible; + +/* Widget is not on the screen */ +visibility: hidden; +``` + +## Python + +```python +# Widget is invisible +self.styles.visibility = "hidden" + +# Widget is visible +self.styles.visibility = "visible" +``` + +There is also a shortcut to set a Widget's visibility. The `visible` property on `Widget` may be set to `True` or `False`. + +```python +# Make a widget invisible +widget.visible = False + +# Make the widget visible again +widget.visible = True +``` diff --git a/docs/styles/width.md b/docs/styles/width.md index 18cc7b57e7..933f9257d2 100644 --- a/docs/styles/width.md +++ b/docs/styles/width.md @@ -1,6 +1,6 @@ # Width -The `width` property sets a widget's width. 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. +The `width` style sets a widget's width. 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 diff --git a/examples/borders.py b/examples/borders.py new file mode 100644 index 0000000000..b3f8ece6d5 --- /dev/null +++ b/examples/borders.py @@ -0,0 +1,29 @@ +from itertools import cycle + +from textual.app import App +from textual.color import Color +from textual.constants import BORDERS +from textual.widgets import Static + + +class BorderApp(App): + """Displays a pride flag.""" + + COLORS = ["red", "orange", "yellow", "green", "blue", "purple"] + + def compose(self): + self.dark = True + for border, color in zip(BORDERS, cycle(self.COLORS)): + static = Static(f"border: {border} {color};") + static.styles.height = 7 + static.styles.background = Color.parse(color).with_alpha(0.2) + static.styles.margin = (1, 2) + static.styles.border = (border, color) + static.styles.content_align = ("center", "middle") + yield static + + +app = BorderApp() + +if __name__ == "__main__": + app.run() diff --git a/mkdocs.yml b/mkdocs.yml index f7ddbf67e2..a5298665c8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,9 +11,15 @@ nav: - "events/mount.md" - "events/resize.md" - Styles: + - "styles/background.md" + - "styles/color.md" - "styles/display.md" - - "styles/width.md" - "styles/height.md" + - "styles/margin.md" + - "styles/padding.md" + - "styles/text_style.md" + - "styles/visibility.md" + - "styles/width.md" - Widgets: "/widgets/" - Reference: - "reference/app.md" diff --git a/src/textual/constants.py b/src/textual/constants.py new file mode 100644 index 0000000000..6429da7906 --- /dev/null +++ b/src/textual/constants.py @@ -0,0 +1,11 @@ +""" +Constants that we might want to expose via the public API. + +""" + +from ._border import BORDER_CHARS + +__all__ = ["BORDERS"] + + +BORDERS = list(BORDER_CHARS) diff --git a/src/textual/layouts/horizontal.py b/src/textual/layouts/horizontal.py index 251e938387..9660b9aecb 100644 --- a/src/textual/layouts/horizontal.py +++ b/src/textual/layouts/horizontal.py @@ -29,7 +29,7 @@ def arrange(self, parent: Widget, size: Size) -> ArrangeResult: total_fraction = sum( [int(style.width.value) for style in styles if style.width.is_fraction] ) - fraction_unit = Fraction(size.height, total_fraction or 1) + fraction_unit = Fraction(size.width, total_fraction or 1) box_models = [ widget.get_box_model(size, parent_size, fraction_unit) diff --git a/tests/test_color.py b/tests/test_color.py index 3f97196651..0e1e51ebbc 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -115,22 +115,28 @@ def test_color_parse(text, expected): assert Color.parse(text) == expected -@pytest.mark.parametrize("input,output", [ - ("rgb( 300, 300 , 300 )", Color(255, 255, 255)), - ("rgba( 2 , 3 , 4, 1.0 )", Color(2, 3, 4, 1.0)), - ("hsl( 45, 25% , 25% )", Color(80, 72, 48)), - ("hsla( 45, 25% , 25%, 0.35 )", Color(80, 72, 48, 0.35)), -]) +@pytest.mark.parametrize( + "input,output", + [ + ("rgb( 300, 300 , 300 )", Color(255, 255, 255)), + ("rgba( 2 , 3 , 4, 1.0 )", Color(2, 3, 4, 1.0)), + ("hsl( 45, 25% , 25% )", Color(80, 72, 48)), + ("hsla( 45, 25% , 25%, 0.35 )", Color(80, 72, 48, 0.35)), + ], +) def test_color_parse_input_has_spaces(input, output): assert Color.parse(input) == output -@pytest.mark.parametrize("input,output", [ - ("rgb(300, 300, 300)", Color(255, 255, 255)), - ("rgba(300, 300, 300, 300)", Color(255, 255, 255, 1.0)), - ("hsl(400, 200%, 250%)", Color(255, 255, 255, 1.0)), - ("hsla(400, 200%, 250%, 1.9)", Color(255, 255, 255, 1.0)), -]) +@pytest.mark.parametrize( + "input,output", + [ + ("rgb(300, 300, 300)", Color(255, 255, 255)), + ("rgba(300, 300, 300, 300)", Color(255, 255, 255, 1.0)), + ("hsl(400, 200%, 250%)", Color(255, 255, 255, 1.0)), + ("hsla(400, 200%, 250%, 1.9)", Color(255, 255, 255, 1.0)), + ], +) def test_color_parse_clamp(input, output): assert Color.parse(input) == output @@ -141,7 +147,8 @@ def test_color_parse_hsl_negative_degrees(): def test_color_parse_hsla_negative_degrees(): assert Color.parse("hsla(-45, 50%, 50%, 0.2)") == Color.parse( - "hsla(315, 50%, 50%, 0.2)") + "hsla(315, 50%, 50%, 0.2)" + ) def test_color_parse_color(): From 99ccbc5eb8f285ad708e008076ddf5be96bab8b5 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Jul 2022 22:33:55 +0100 Subject: [PATCH 03/30] moar docs --- docs/examples/light_dark.py | 1 + docs/examples/styles/README.md | 9 ++++++ docs/examples/styles/border.py | 7 +++++ docs/examples/styles/margin.py | 6 ++++ docs/examples/styles/overflow.py | 44 ++++++++++++++++++++++++++++ docs/examples/styles/padding.py | 5 ++++ docs/styles/max_height.md | 25 ++++++++++++++++ docs/styles/max_width.md | 25 ++++++++++++++++ docs/styles/min_height.md | 25 ++++++++++++++++ docs/styles/min_width.md | 25 ++++++++++++++++ docs/styles/overflow.md | 50 ++++++++++++++++++++++++++++++++ mkdocs.yml | 5 ++++ 12 files changed, 227 insertions(+) create mode 100644 docs/examples/styles/README.md create mode 100644 docs/examples/styles/overflow.py create mode 100644 docs/styles/max_height.md create mode 100644 docs/styles/max_width.md create mode 100644 docs/styles/min_height.md create mode 100644 docs/styles/min_width.md create mode 100644 docs/styles/overflow.md diff --git a/docs/examples/light_dark.py b/docs/examples/light_dark.py index feb542d449..8cd3cd78ce 100644 --- a/docs/examples/light_dark.py +++ b/docs/examples/light_dark.py @@ -17,4 +17,5 @@ def compose(self): def handle_pressed(self, event): self.dark = not self.dark + self.bell() event.button.label = "Lights ON" if self.dark else "Lights OFF" diff --git a/docs/examples/styles/README.md b/docs/examples/styles/README.md new file mode 100644 index 0000000000..8c80435825 --- /dev/null +++ b/docs/examples/styles/README.md @@ -0,0 +1,9 @@ +These are the examples from the documentation, used to generate screenshots. + +You can run them with the textual CLI. + +For example: + +``` +textual run text_style.py +``` diff --git a/docs/examples/styles/border.py b/docs/examples/styles/border.py index 9589769390..9c36c6d458 100644 --- a/docs/examples/styles/border.py +++ b/docs/examples/styles/border.py @@ -4,6 +4,9 @@ class BorderApp(App): CSS = """ + Screen { + background: white; + } Screen > Static { height:5; content-align: center middle; @@ -13,14 +16,18 @@ class BorderApp(App): } #static1 { background: red 20%; + color: red; border: solid red; } #static2 { background: green 20%; + color: green; border: dashed green; + } #static3 { background: blue 20%; + color: blue; border: tall blue; } """ diff --git a/docs/examples/styles/margin.py b/docs/examples/styles/margin.py index c7aec1dfe5..6e5a3c59ab 100644 --- a/docs/examples/styles/margin.py +++ b/docs/examples/styles/margin.py @@ -12,10 +12,16 @@ class MarginApp(App): CSS = """ + + Screen { + background: white; + color: black; + } Static { margin: 4 8; background: blue 20%; + border: blue wide; } """ diff --git a/docs/examples/styles/overflow.py b/docs/examples/styles/overflow.py new file mode 100644 index 0000000000..560541170e --- /dev/null +++ b/docs/examples/styles/overflow.py @@ -0,0 +1,44 @@ +from textual.app import App +from textual.widgets import Static +from textual.layout import Horizontal, Vertical + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain.""" + + +class OverflowApp(App): + CSS = """ + Screen { + background: $background; + color: black; + } + + Vertical { + width: 1fr; + } + + Static { + margin: 1 2; + background: blue 20%; + border: blue wide; + height: auto; + } + + #right { + overflow-y: hidden; + } + """ + + def compose(self): + yield Horizontal( + Vertical(Static(TEXT), Static(TEXT), Static(TEXT), id="left"), + Vertical(Static(TEXT), Static(TEXT), Static(TEXT), id="right"), + ) + + +app = OverflowApp() diff --git a/docs/examples/styles/padding.py b/docs/examples/styles/padding.py index 8b345514bf..b65606cf14 100644 --- a/docs/examples/styles/padding.py +++ b/docs/examples/styles/padding.py @@ -12,6 +12,11 @@ class PaddingApp(App): CSS = """ + + Screen { + background: white; + color: blue; + } Static { padding: 4 8; diff --git a/docs/styles/max_height.md b/docs/styles/max_height.md new file mode 100644 index 0000000000..dcf10193eb --- /dev/null +++ b/docs/styles/max_height.md @@ -0,0 +1,25 @@ +# Max-height + +The `max-height` rule sets a maximum width for a widget. + +## CSS + +```sass + +/* Set a maximum height of 10 rows */ +max-height: 10; + +/* Set a maximum height of 25% of the screen height */ +max-height: 25vh; +``` + +## Python + +```python +# Set the maximum width to 10 rows +widget.styles.max_height = 10 + +# Set the maximum width to 25% of the screen width +widget.styles.max_height = "25vw" + +``` diff --git a/docs/styles/max_width.md b/docs/styles/max_width.md new file mode 100644 index 0000000000..deac40f97c --- /dev/null +++ b/docs/styles/max_width.md @@ -0,0 +1,25 @@ +# Max-width + +The `max-width` rule sets a maximum width for a widget. + +## CSS + +```sass + +/* Set a maximum width of 10 cells */ +max-width: 10; + +/* Set a maximum width of 25% of the screen width */ +max-width: 25vh; +``` + +## Python + +```python +# Set the maximum width to 10 cells +widget.styles.max_width = 10 + +# Set the maximum width to 25% of the screen width +widget.styles.max_width = "25vw" + +``` diff --git a/docs/styles/min_height.md b/docs/styles/min_height.md new file mode 100644 index 0000000000..51426e3977 --- /dev/null +++ b/docs/styles/min_height.md @@ -0,0 +1,25 @@ +# Min-height + +The `min-height` rule sets a minimum height for a widget. + +## CSS + +```sass + +/* Set a minimum height of 10 rows */ +min-height: 10; + +/* Set a minimum height of 25% of the screen height */ +min-height: 25vh; +``` + +## Python + +```python +# Set the minimum height to 10 rows +self.styles.min_height = 10 + +# Set the minimum height to 25% of the screen height +self.styles.min_height = "25vh" + +``` diff --git a/docs/styles/min_width.md b/docs/styles/min_width.md new file mode 100644 index 0000000000..b6a4cf2c35 --- /dev/null +++ b/docs/styles/min_width.md @@ -0,0 +1,25 @@ +# Min-width + +The `min-width` rules sets a minimum width for a widget. + +## CSS + +```sass + +/* Set a minimum width of 10 cells */ +min-width: 10; + +/* Set a minimum width of 25% of the screen width */ +min-width: 25vh; +``` + +## Python + +```python +# Set the minimum width to 10 cells +widget.styles.min_width = 10 + +# Set the minimum width to 25% of the screen height +widget.styles.min_width = "25vh" + +``` diff --git a/docs/styles/overflow.md b/docs/styles/overflow.md new file mode 100644 index 0000000000..752ac2f648 --- /dev/null +++ b/docs/styles/overflow.md @@ -0,0 +1,50 @@ +# Overflow + +The `overflow` rule specifies if and when scrollbars should be displayed on the `x` and `y` axis. There are two values for each scrollbar, which may be set together or independently to one of the follow three values: + +- `"auto"` Automatically show the scrollbar if the content doesn't fit +- `"hidden"` Never show the scrollbar +- `"scroll"` Always show the scrollbar + +The default is "auto" which will show the scrollbar if content doesn't fit within container, otherwise the scrollbar will be hidden. + +## Example + +Here we split the screen in to left and right sections, each with three vertically scrolling widgets that do not fit in to the height of the terminal. + +The left side has `overflow-y: auto` (the default) and will automatically show a scrollbar. The right side has `overflow-y: hidden` which will prevent a scrollbar from being show. + +=== "width.py" + + ```python + --8<-- "docs/examples/styles/overflow.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/overflow.py"} + ``` + +## CSS + +```sass +/* Automatic scrollbars on both axies (the default) */ +overflow: auto auto; + +/* Hide the vertical scrollbar */ +overflow-y: hidden; + +/* Always show the horizontal scrollbar */ +overflow-x: scroll; +``` + +## Python + +```python +# Hide the vertical scrollbar +self.styles.overflow_y = "hidden" + +# Always show the horizontal scrollbar +self.styles.overflow_x = "scroll" + +``` diff --git a/mkdocs.yml b/mkdocs.yml index a5298665c8..20713abbf7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,8 +14,13 @@ nav: - "styles/background.md" - "styles/color.md" - "styles/display.md" + - "styles/min_height.md" + - "styles/max_height.md" + - "styles/min_width.md" + - "styles/max_width.md" - "styles/height.md" - "styles/margin.md" + - "styles/overflow.md" - "styles/padding.md" - "styles/text_style.md" - "styles/visibility.md" From 0fe4b970f097a3796c858667c5a3fa5b9946ccce Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Jul 2022 08:47:34 +0100 Subject: [PATCH 04/30] more docs --- docs/examples/styles/border.py | 3 +- docs/examples/styles/box_sizing.py | 32 +++++++++++++++++ docs/examples/styles/outline.py | 31 ++++++++++++++++ docs/styles/border.md | 55 ++++++++++++++++++++++++++++ docs/styles/box_sizing.md | 42 ++++++++++++++++++++++ docs/styles/color.md | 6 ++-- docs/styles/height.md | 2 +- docs/styles/margin.md | 8 +++-- docs/styles/outline.md | 57 ++++++++++++++++++++++++++++++ docs/styles/padding.md | 8 +++-- mkdocs.yml | 3 ++ 11 files changed, 235 insertions(+), 12 deletions(-) create mode 100644 docs/examples/styles/box_sizing.py create mode 100644 docs/examples/styles/outline.py create mode 100644 docs/styles/border.md create mode 100644 docs/styles/box_sizing.md create mode 100644 docs/styles/outline.md diff --git a/docs/examples/styles/border.py b/docs/examples/styles/border.py index 9c36c6d458..311dab5107 100644 --- a/docs/examples/styles/border.py +++ b/docs/examples/styles/border.py @@ -8,7 +8,7 @@ class BorderApp(App): background: white; } Screen > Static { - height:5; + height: 5; content-align: center middle; color: white; margin: 1; @@ -23,7 +23,6 @@ class BorderApp(App): background: green 20%; color: green; border: dashed green; - } #static3 { background: blue 20%; diff --git a/docs/examples/styles/box_sizing.py b/docs/examples/styles/box_sizing.py new file mode 100644 index 0000000000..3b97601b54 --- /dev/null +++ b/docs/examples/styles/box_sizing.py @@ -0,0 +1,32 @@ +from textual.app import App +from textual.widgets import Static + + +class BoxSizingApp(App): + CSS = """ + Screen { + background: white; + color: black; + } + Static { + background: blue 20%; + height: 5; + margin: 2; + padding: 1; + border: wide black; + } + #static1 { + box-sizing: border-box; + } + #static2 { + box-sizing: content-box; + } + + """ + + def compose(self): + yield Static("Hello, World!", id="static1") + yield Static("Hello, World!", id="static2") + + +app = BoxSizingApp() diff --git a/docs/examples/styles/outline.py b/docs/examples/styles/outline.py new file mode 100644 index 0000000000..0fc1a476c8 --- /dev/null +++ b/docs/examples/styles/outline.py @@ -0,0 +1,31 @@ +from textual.app import App +from textual.widgets import Static + + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain.""" + + +class OutlineApp(App): + CSS = """ + Screen { + background: white; + color: black; + } + Static { + margin: 4 8; + background: green 20%; + outline: wide green; + } + """ + + def compose(self): + yield Static(TEXT) + + +app = OutlineApp() diff --git a/docs/styles/border.md b/docs/styles/border.md new file mode 100644 index 0000000000..9ca540c5f9 --- /dev/null +++ b/docs/styles/border.md @@ -0,0 +1,55 @@ +# Border + +The `border` rule enables the drawing of a box around a widget. A border is set with a border style (see below) followed by a color. + +- `"ascii"` +- `"round"` +- `"solid"` +- `"double"` +- `"dashed"` +- `"heavy"` +- `"inner"` +- `"outer"` +- `"hkey"` +- `"vkey"` +- `"tall"` +- `"wide"` + +For examples `heavy white` would display a heavy white line around a widget. + +Borders may also be set individually with the `border-top`, `border-right`, `border-bottom` and `border-left` rules. + +## Example + +This examples shows three widgets with different border styles. + +=== "border.py" + + ```python + --8<-- "docs/examples/styles/border.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/border.py"} + ``` + +## CSS + +```sass +/* Set a heavy white border */ +border: heavy white; + +/* set a red border on the left */ +border-left: outer red; +``` + +## Python + +```python +# Set a heavy white border +widget.border = ("heavy", "white) + +# Set a red border on the left +widget.border_left = ("outer", "red) +``` diff --git a/docs/styles/box_sizing.md b/docs/styles/box_sizing.md new file mode 100644 index 0000000000..d0d5fdce2b --- /dev/null +++ b/docs/styles/box_sizing.md @@ -0,0 +1,42 @@ +# Box-sizing + +The `box-sizing` rule impacts how `width` and `height` rules are translated in to screen dimensions, when combined with `padding` and `border`. + +The default value is `border-box` which means that padding and border are included in in the width and height. This setting means that if you add padding and/or border the widget will not change in size, but you will have less space for content. + +You can set `box-sizing` to `content-box` which tells Textual that padding and border should increase the size of the widget, leaving the content area unaffected. + +## Example + +Both widgets in this example have the same height (5). The top widget has `box-sizing: border-box` which means that padding and border reduces the space for content. The bottom widget has `box-sizing: content-box` which increases the size of the widget to compensate for padding and border. + +=== "box_sizing.py" + + ```python + --8<-- "docs/examples/styles/box_sizing.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/box_sizing.py"} + ``` + +## CSS + +```sass +/* Set box sizing to border-box (default) */ +box-sizing: border-box; + +/* Set box sizing to content-box */ +box-sizing: content-box; +``` + +## Python + +```python +# Set box sizing to border-box (default) +widget.box_sizing = "border-box" + +# Set box sizing to content-box +widget.box_sizing = "content-box" +``` diff --git a/docs/styles/color.md b/docs/styles/color.md index 51806d8575..053dcd53e9 100644 --- a/docs/styles/color.md +++ b/docs/styles/color.md @@ -16,10 +16,10 @@ The `color` rule sets the text color of a Widget. ## CSS ```sass -/* Blue background */ +/* Blue text */ color: blue; -/* 20% red backround */ +/* 20% red text */ color: red 20%; /* RGB color */ @@ -31,7 +31,7 @@ color: rgb(100,120,200); You can use the same syntax as CSS, or explicitly set a Color object. ```python -# Set blue background +# Set blue text widget.styles.color = "blue" from textual.color import Color diff --git a/docs/styles/height.md b/docs/styles/height.md index 12aefac09d..b7c6b664d9 100644 --- a/docs/styles/height.md +++ b/docs/styles/height.md @@ -1,6 +1,6 @@ # Height -The `height` style 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. +The `height` style sets a widget's height. By default, it sets the height of the content area, but if `box-sizing` is set to `border-box` it sets the height of the border area. ## Example diff --git a/docs/styles/margin.md b/docs/styles/margin.md index a0cc4d4430..2bd01ba689 100644 --- a/docs/styles/margin.md +++ b/docs/styles/margin.md @@ -2,9 +2,11 @@ The `margin` rule adds space around the entire widget. -- `1` Sets a margin of 1 around all 4 edges -- `1 2` Sets a margin of 1 on the top and bottom edges, and a margin of 2 on the left and right edges -- `1 2 3 4` Sets a margin of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. +- `margin: 1;` Sets a margin of 1 around all 4 edges +- `margin: 1 2;` Sets a margin of 1 on the top and bottom edges, and a margin of 2 on the left and right edges +- `margin: 1 2 3 4;` Sets a margin of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. + +Margin may also be set individually, following the same pattern as above, by setting `margin-top`, `margin-right`, `margin-bottom`, or `margin-left`. ## Example diff --git a/docs/styles/outline.md b/docs/styles/outline.md new file mode 100644 index 0000000000..d475b95199 --- /dev/null +++ b/docs/styles/outline.md @@ -0,0 +1,57 @@ +# Outline + +The `outline` rule enables the drawing of a box around a widget. Similar to `border`, but unlike border, outline will draw over the content area. This rule can be useful for emphasis if you want to display a outline for a brief time to draw the user's attention to it. + +An outline is set with a border style (see below) followed by a color. + +- `"ascii"` +- `"round"` +- `"solid"` +- `"double"` +- `"dashed"` +- `"heavy"` +- `"inner"` +- `"outer"` +- `"hkey"` +- `"vkey"` +- `"tall"` +- `"wide"` + +For examples `heavy white` would display a heavy white line around a widget. + +Outlines may also be set individually with the `outline-top`, `outline-right`, `outline-bottom` and `outline-left` rules. + +## Example + +This examples shows a widget with an outline. Note how the outline occludes the text area. + +=== "outline.py" + + ```python + --8<-- "docs/examples/styles/outline.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/outline.py"} + ``` + +## CSS + +```sass +/* Set a heavy white outline */ +outline: heavy white; + +/* set a red outline on the left */ +outline-left: outer red; +``` + +## Python + +```python +# Set a heavy white outline +widget.outline = ("heavy", "white) + +# Set a red outline on the left +widget.outline_left = ("outer", "red) +``` diff --git a/docs/styles/padding.md b/docs/styles/padding.md index 0c8272fbc2..75207b62a5 100644 --- a/docs/styles/padding.md +++ b/docs/styles/padding.md @@ -2,9 +2,11 @@ The padding rule adds space around the content of a widget. You can specify padding with 1, 2 or 4 numbers. -- `1` Sets a padding of 1 around all 4 edges -- `1 2` Sets a padding of 1 on the top and bottom edges, and a padding of two on the left and right edges -- `1 2 3 4` Sets a padding of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. +- `padding: 1;` Sets a padding of 1 around all 4 edges +- `padding: 1 2;` Sets a padding of 1 on the top and bottom edges, and a padding of two on the left and right edges +- `padding: 1 2 3 4;` Sets a padding of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. + +Padding may also be set individually, following the same pattern as above, by setting `padding-top`, `padding-right`, `padding-bottom`, or `padding-left`. ## Example diff --git a/mkdocs.yml b/mkdocs.yml index 20713abbf7..e737b699c9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,6 +12,8 @@ nav: - "events/resize.md" - Styles: - "styles/background.md" + - "styles/border.md" + - "styles/box_sizing.md" - "styles/color.md" - "styles/display.md" - "styles/min_height.md" @@ -20,6 +22,7 @@ nav: - "styles/max_width.md" - "styles/height.md" - "styles/margin.md" + - "styles/outline.md" - "styles/overflow.md" - "styles/padding.md" - "styles/text_style.md" From 3f0f9eefb8b03a4a76071749bd6e1173bd619b20 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Jul 2022 15:56:32 +0100 Subject: [PATCH 05/30] tint style --- docs/examples/styles/tint.py | 28 +++++++++++++++++++++++ docs/styles/tint.md | 39 +++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + src/textual/color.py | 2 +- src/textual/renderables/tint.py | 17 +++++++++++--- 5 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 docs/examples/styles/tint.py create mode 100644 docs/styles/tint.md diff --git a/docs/examples/styles/tint.py b/docs/examples/styles/tint.py new file mode 100644 index 0000000000..b8f3766d43 --- /dev/null +++ b/docs/examples/styles/tint.py @@ -0,0 +1,28 @@ +from textual.app import App +from textual.color import Color +from textual.widgets import Static + + +class TintApp(App): + CSS = """ + Screen { + background: green; + } + Static { + height: 3; + text-style: bold; + background: white; + color: black; + content-align: center middle; + } + """ + + def compose(self): + color = Color.parse("green") + for tint_alpha in range(0, 101, 10): + widget = Static(f"tint: green {tint_alpha}%;") + widget.styles.tint = color.with_alpha(tint_alpha / 100) + yield widget + + +app = TintApp() diff --git a/docs/styles/tint.md b/docs/styles/tint.md new file mode 100644 index 0000000000..d2d412955b --- /dev/null +++ b/docs/styles/tint.md @@ -0,0 +1,39 @@ +# Tint + +The tint rule blends a color with the widget. The color should likely have an _alpha_ component, or the end result would obscure the widget content. + +## Example + +This examples shows a green tint with gradually increasing alpha. + +=== "tint.py" + + ```python + --8<-- "docs/examples/styles/tint.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/tint.py"} + ``` + +## CSS + +```sass +/* A red tint (could indicate an error) */ +tint: red 20% + +/* A green tint */ +tint: rgba(0, 200, 0, 0.3); +``` + +# Python + +```python +# A red tint +from textual.color import Color +widget.styles.tint = Color.parse("red").with_alpha(0.2); + +# A green tint +widget.styles.tint = "rgba(0, 200, 0, 0.3): +``` diff --git a/mkdocs.yml b/mkdocs.yml index e737b699c9..a5e0486b47 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,6 +26,7 @@ nav: - "styles/overflow.md" - "styles/padding.md" - "styles/text_style.md" + - "styles/tint.md" - "styles/visibility.md" - "styles/width.md" - Widgets: "/widgets/" diff --git a/src/textual/color.py b/src/textual/color.py index 2647ba80b0..1f356f3e78 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -198,7 +198,7 @@ def hex(self) -> str: str: A CSS hex-style color, e.g. "#46b3de" or "#3342457f" """ - r, g, b, a = self + r, g, b, a = self.clamped return ( f"#{r:02X}{g:02X}{b:02X}" if a == 1 diff --git a/src/textual/renderables/tint.py b/src/textual/renderables/tint.py index 196a07be49..022862b3a8 100644 --- a/src/textual/renderables/tint.py +++ b/src/textual/renderables/tint.py @@ -39,18 +39,29 @@ def process_segments( from_rich_color = Color.from_rich_color style_from_color = Style.from_color _Segment = Segment + + NULL_STYLE = Style() for segment in segments: text, style, control = segment - if control or style is None: + if control: yield segment else: + style = style or NULL_STYLE yield _Segment( text, ( style + style_from_color( - (from_rich_color(style.color) + color).rich_color, - (from_rich_color(style.bgcolor) + color).rich_color, + ( + (from_rich_color(style.color) + color).rich_color + if style.color is not None + else None + ), + ( + (from_rich_color(style.bgcolor) + color).rich_color + if style.bgcolor is not None + else None + ), ) ), control, From 8b5b410ab4c91dcc90c47c2ad1fa0e0a12cf12ff Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Jul 2022 16:21:42 +0100 Subject: [PATCH 06/30] added tint and offset --- docs/examples/styles/offset.py | 46 ++++++++++++++++++++++++++++++++++ docs/examples/styles/tint.py | 3 --- docs/styles/offset.md | 30 ++++++++++++++++++++++ mkdocs.yml | 1 + 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 docs/examples/styles/offset.py create mode 100644 docs/styles/offset.md diff --git a/docs/examples/styles/offset.py b/docs/examples/styles/offset.py new file mode 100644 index 0000000000..da65bc534c --- /dev/null +++ b/docs/examples/styles/offset.py @@ -0,0 +1,46 @@ +from textual.app import App +from textual.widgets import Static + + +class OffsetApp(App): + CSS = """ + Screen { + background: white; + color: black; + layout: horizontal; + } + Static { + width: 20; + height: 10; + content-align: center middle; + } + + .paul { + offset: 8 2; + background: red 20%; + border: outer red; + color: red; + } + + .duncan { + offset: 4 10; + background: green 20%; + border: outer green; + color: green; + } + + .chani { + offset: 0 5; + background: blue 20%; + border: outer blue; + color: blue; + } + """ + + def compose(self): + yield Static("Paul", classes="paul") + yield Static("Duncan", classes="duncan") + yield Static("Chani", classes="chani") + + +app = OffsetApp() diff --git a/docs/examples/styles/tint.py b/docs/examples/styles/tint.py index b8f3766d43..1d6d5678b5 100644 --- a/docs/examples/styles/tint.py +++ b/docs/examples/styles/tint.py @@ -5,9 +5,6 @@ class TintApp(App): CSS = """ - Screen { - background: green; - } Static { height: 3; text-style: bold; diff --git a/docs/styles/offset.md b/docs/styles/offset.md new file mode 100644 index 0000000000..1c794c3f77 --- /dev/null +++ b/docs/styles/offset.md @@ -0,0 +1,30 @@ +# Offset + +The `offset` rule adds an offset to the widget's position. + +## Example + +=== "offset.py" + + ```python + --8<-- "docs/examples/styles/offset.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/offset.py"} + ``` + +## CSS + +```sass +/* Move the widget 2 cells in the x direction, and 4 in the y direction. */ +offset: 2 4; +``` + +## Python + +```python +# Move the widget 2 cells in the x direction, and 4 in the y direction. +widget.styles.offset = (2, 4) +``` diff --git a/mkdocs.yml b/mkdocs.yml index a5e0486b47..9e1a76e217 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,6 +22,7 @@ nav: - "styles/max_width.md" - "styles/height.md" - "styles/margin.md" + - "styles/offset.md" - "styles/outline.md" - "styles/overflow.md" - "styles/padding.md" From a174557adb4816d60cbfa08720dcdf822cdf55a7 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Jul 2022 17:09:18 +0100 Subject: [PATCH 07/30] added borders utility --- src/textual/devtools/borders.py | 70 +++++++++++++++++++++++++++++++++ src/textual/widgets/_button.py | 8 +++- 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/textual/devtools/borders.py diff --git a/src/textual/devtools/borders.py b/src/textual/devtools/borders.py new file mode 100644 index 0000000000..09a175a1e8 --- /dev/null +++ b/src/textual/devtools/borders.py @@ -0,0 +1,70 @@ +from textual.app import App, ComposeResult +from textual.constants import BORDERS +from textual.widgets import Button, Static +from textual import layout + + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain.""" + + +class BorderButtons(layout.Vertical): + CSS = """ + BorderButtons { + dock: left; + width: 20; + } + + BorderButtons > Button { + width: 20; + } + """ + + def compose(self) -> ComposeResult: + for border in BORDERS: + if border: + yield Button(border, id=border) + + +class BorderApp(App): + """Displays a pride flag.""" + + COLORS = ["red", "orange", "yellow", "green", "blue", "purple"] + + CSS = """ + Screen { + background: $surface; + } + Static { + margin: 2 4; + padding: 2 4; + border: solid $warning; + height: auto; + background: $panel; + color: $text-panel-fade-1; + } + """ + + def compose(self): + self.dark = True + yield BorderButtons() + self.text = Static(TEXT) + yield self.text + + def handle_pressed(self, event): + self.text.styles.border = ( + event.button.id, + self.stylesheet.variables["warning"], + ) + self.bell() + + +app = BorderApp() + +if __name__ == "__main__": + app.run() diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index f1b1dba84f..3f7bb50b51 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -192,15 +192,19 @@ async def on_click(self, event: events.Click) -> None: if self.disabled: return # Manage the "active" effect: + self._start_active_affect() + # ...and let other components know that we've just been clicked: + await self.emit(Button.Pressed(self)) + + def _start_active_affect(self) -> None: self.add_class("-active") self.set_timer( self.ACTIVE_EFFECT_DURATION, partial(self.remove_class, "-active") ) - # ...and let other components know that we've just been clicked: - await self.emit(Button.Pressed(self)) async def on_key(self, event: events.Key) -> None: if event.key == "enter" and not self.disabled: + self._start_active_affect() await self.emit(Button.Pressed(self)) @classmethod From 62db35fcec2fe28a0021a53281977ce9e81299ad Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Jul 2022 17:20:20 +0100 Subject: [PATCH 08/30] add borders command --- src/textual/cli/cli.py | 10 +++++++++- src/textual/devtools/borders.py | 4 +--- src/textual/widgets/_button.py | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/textual/cli/cli.py b/src/textual/cli/cli.py index d7409a10f9..f303246f61 100644 --- a/src/textual/cli/cli.py +++ b/src/textual/cli/cli.py @@ -18,7 +18,7 @@ def run(): pass -@run.command(help="Run the Textual Devtools console") +@run.command(help="Run the Textual Devtools console.") def console(): _run_devtools() @@ -157,3 +157,11 @@ def run_app(import_name: str, dev: bool) -> None: sys.exit(1) app.run() + + +@run.command("borders") +def borders(): + """Explore the border styles available in Textual.""" + from ..devtools import borders + + borders.app.run() diff --git a/src/textual/devtools/borders.py b/src/textual/devtools/borders.py index 09a175a1e8..8732bba72e 100644 --- a/src/textual/devtools/borders.py +++ b/src/textual/devtools/borders.py @@ -32,9 +32,7 @@ def compose(self) -> ComposeResult: class BorderApp(App): - """Displays a pride flag.""" - - COLORS = ["red", "orange", "yellow", "green", "blue", "purple"] + """Demonstrates the border styles.""" CSS = """ Screen { diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index 3f7bb50b51..aa4ccf4759 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -197,6 +197,7 @@ async def on_click(self, event: events.Click) -> None: await self.emit(Button.Pressed(self)) def _start_active_affect(self) -> None: + """Start a small animation to show the button was clicked.""" self.add_class("-active") self.set_timer( self.ACTIVE_EFFECT_DURATION, partial(self.remove_class, "-active") From ae9862ce1fed7de1e30ef3cf29044c8e78b4e972 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Jul 2022 21:19:26 +0100 Subject: [PATCH 09/30] borders tweak --- src/textual/devtools/borders.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/textual/devtools/borders.py b/src/textual/devtools/borders.py index 8732bba72e..ac1dea6e70 100644 --- a/src/textual/devtools/borders.py +++ b/src/textual/devtools/borders.py @@ -17,11 +17,11 @@ class BorderButtons(layout.Vertical): CSS = """ BorderButtons { dock: left; - width: 20; + width: 24; } BorderButtons > Button { - width: 20; + width: 100%; } """ @@ -35,21 +35,17 @@ class BorderApp(App): """Demonstrates the border styles.""" CSS = """ - Screen { - background: $surface; - } Static { margin: 2 4; padding: 2 4; - border: solid $warning; + border: solid $primary; height: auto; background: $panel; - color: $text-panel-fade-1; + color: $text-panel; } """ def compose(self): - self.dark = True yield BorderButtons() self.text = Static(TEXT) yield self.text @@ -57,7 +53,7 @@ def compose(self): def handle_pressed(self, event): self.text.styles.border = ( event.button.id, - self.stylesheet.variables["warning"], + self.stylesheet.variables["primary"], ) self.bell() From fa4b971bffb9f488d155981794b7f0bf3b657c72 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Aug 2022 15:27:41 +0100 Subject: [PATCH 10/30] more docs --- docs/examples/styles/scrollbar_size.py | 38 ++++++++++++ docs/examples/styles/scrollbars.py | 49 +++++++++++++++ docs/styles/height.md | 4 +- docs/styles/overflow.md | 4 +- docs/styles/scrollbar.md | 43 +++++++++++++ docs/styles/scrollbar_size.md | 37 ++++++++++++ docs/styles/width.md | 2 +- mkdocs.yml | 2 + sandbox/will/basic.css | 2 +- sandbox/will/basic.py | 83 +++++++++++++++----------- src/textual/css/_help_text.py | 46 -------------- src/textual/css/_styles_builder.py | 1 - tests/css/test_help_text.py | 8 --- 13 files changed, 224 insertions(+), 95 deletions(-) create mode 100644 docs/examples/styles/scrollbar_size.py create mode 100644 docs/examples/styles/scrollbars.py create mode 100644 docs/styles/scrollbar.md create mode 100644 docs/styles/scrollbar_size.md diff --git a/docs/examples/styles/scrollbar_size.py b/docs/examples/styles/scrollbar_size.py new file mode 100644 index 0000000000..2caaafed63 --- /dev/null +++ b/docs/examples/styles/scrollbar_size.py @@ -0,0 +1,38 @@ +from textual.app import App +from textual import layout +from textual.widgets import Static + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain. +""" + + +class ScrollbarApp(App): + CSS = """ + Screen { + background: white; + color: blue 80%; + layout: horizontal; + } + + Static { + padding: 1 2; + width: 200; + } + + .panel { + scrollbar-size: 10 4; + padding: 1 2; + } + """ + + def compose(self): + yield layout.Vertical(Static(TEXT * 5), classes="panel") + + +app = ScrollbarApp() diff --git a/docs/examples/styles/scrollbars.py b/docs/examples/styles/scrollbars.py new file mode 100644 index 0000000000..0a024e5e8c --- /dev/null +++ b/docs/examples/styles/scrollbars.py @@ -0,0 +1,49 @@ +from textual.app import App +from textual import layout +from textual.widgets import Static + +TEXT = """I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain. +""" + + +class ScrollbarApp(App): + CSS = """ + + Screen { + background: #212121; + color: white 80%; + layout: horizontal; + } + + Static { + padding: 1 2; + } + + .panel1 { + width: 1fr; + scrollbar-color: green; + scrollbar-background: #bbb; + padding: 1 2; + } + + .panel2 { + width: 1fr; + scrollbar-color: yellow; + scrollbar-background: purple; + padding: 1 2; + } + + """ + + def compose(self): + yield layout.Vertical(Static(TEXT * 5), classes="panel1") + yield layout.Vertical(Static(TEXT * 5), classes="panel2") + + +app = ScrollbarApp() diff --git a/docs/styles/height.md b/docs/styles/height.md index b7c6b664d9..b7ba4a0db9 100644 --- a/docs/styles/height.md +++ b/docs/styles/height.md @@ -1,10 +1,10 @@ # Height -The `height` style sets a widget's height. By default, it sets the height of the content area, but if `box-sizing` is set to `border-box` it sets the height of the border area. +The `height` rule sets a widget's height. By default, it sets the height of the content area, but if `box-sizing` is set to `border-box` it sets the height of the border area. ## Example -=== "width.py" +=== "height.py" ```python --8<-- "docs/examples/styles/height.py" diff --git a/docs/styles/overflow.md b/docs/styles/overflow.md index 752ac2f648..8429403a42 100644 --- a/docs/styles/overflow.md +++ b/docs/styles/overflow.md @@ -42,9 +42,9 @@ overflow-x: scroll; ```python # Hide the vertical scrollbar -self.styles.overflow_y = "hidden" +widget.styles.overflow_y = "hidden" # Always show the horizontal scrollbar -self.styles.overflow_x = "scroll" +widget.styles.overflow_x = "scroll" ``` diff --git a/docs/styles/scrollbar.md b/docs/styles/scrollbar.md new file mode 100644 index 0000000000..7a8a3912b4 --- /dev/null +++ b/docs/styles/scrollbar.md @@ -0,0 +1,43 @@ +# Scrollbar colors + +There are a number of rules to set the colors used in Textual scrollbars. You won't typically need to do this, as the default themes have carefully chosen colors, but you can if you want to. + +| Rule | Color | +| ---------------------------- | ------------------------------------------------------- | +| `scrollbar-color` | Scrollbar "thumb" (movable part) | +| `scrollbar-color-hover` | Scrollbar thumb when the mouse is hovering over it | +| `scrollbar-color-active` | Scrollbar thumb when it is active (being dragged) | +| `scrollbar-background` | Scrollbar background | +| `scrollbar-background-hover` | Scrollbar background when the mouse is hovering over it | +| `scrollbar-color-active` | Scrollbar background when the thumb is being dragged | + +## Example + +In this example we have two panels, with different scrollbar colors set for each. + +=== "scrollbars.py" + + ```python + --8<-- "docs/examples/styles/scrollbars.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/scrollbars.py"} + ``` + +## CSS + +```sass +/* Set widget scrollbar color to yellow */ +Widget { + scrollbar-color: yellow; +} +``` + +## Python + +```python +# Set the scrollbar color to yellow +widget.styles.scrollbar_color = "yellow" +``` diff --git a/docs/styles/scrollbar_size.md b/docs/styles/scrollbar_size.md new file mode 100644 index 0000000000..f6653c2543 --- /dev/null +++ b/docs/styles/scrollbar_size.md @@ -0,0 +1,37 @@ +# Scrollbar-size + +The `scrollbar-size` rule changes the size of the scrollbars. It takes 2 integers for horizontal and vertical scrollbar size respectively. + +The scrollbar dimensions may also be set individually with `scrollbar-size-horizontal` and `scrollbar-size-vertical`. + +## Example + +In this example we modify the size of the widgets scrollbar to be _much_ larger than usual. + +=== "scrollbar_size.py" + + ```python + --8<-- "docs/examples/styles/scrollbar_size.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/styles/scrollbar_size.py"} + ``` + +## CSS + +```sass +/* Set horizontal scrollbar to 10, and vertical scrollbar to 4 */ +Widget { + scrollbar-size: 10 4; +} +``` + +## Python + +```python +# Set horizontal scrollbar to 10, and vertical scrollbar to 4 +widget.styles.horizontal_scrollbar = 10 +widget.styles.vertical_scrollbar = 10 +``` diff --git a/docs/styles/width.md b/docs/styles/width.md index 933f9257d2..6665520022 100644 --- a/docs/styles/width.md +++ b/docs/styles/width.md @@ -1,6 +1,6 @@ # Width -The `width` style sets a widget's width. 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. +The `width` rule sets a widget's width. 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 diff --git a/mkdocs.yml b/mkdocs.yml index 9e1a76e217..d35b2d6765 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,6 +26,8 @@ nav: - "styles/outline.md" - "styles/overflow.md" - "styles/padding.md" + - "styles/scrollbar.md" + - "styles/scrollbar_size.md" - "styles/text_style.md" - "styles/tint.md" - "styles/visibility.md" diff --git a/sandbox/will/basic.css b/sandbox/will/basic.css index 3c9043b6b3..fef9e4248b 100644 --- a/sandbox/will/basic.css +++ b/sandbox/will/basic.css @@ -100,7 +100,7 @@ Tweet { .scrollable { - + overflow-x: auto; overflow-y: scroll; margin: 1 2; height: 20; diff --git a/sandbox/will/basic.py b/sandbox/will/basic.py index dee825f0e7..bda44f314f 100644 --- a/sandbox/will/basic.py +++ b/sandbox/will/basic.py @@ -9,39 +9,51 @@ from textual.widgets import Static, DataTable CODE = ''' -class Offset(NamedTuple): - """A point defined by x and y coordinates.""" - - x: int = 0 - y: int = 0 - - @property - def is_origin(self) -> bool: - """Check if the point is at the origin (0, 0)""" - return self == (0, 0) - - def __bool__(self) -> bool: - return self != (0, 0) - - def __add__(self, other: object) -> Offset: - if isinstance(other, tuple): - _x, _y = self - x, y = other - return Offset(_x + x, _y + y) - return NotImplemented - - def __sub__(self, other: object) -> Offset: - if isinstance(other, tuple): - _x, _y = self - x, y = other - return Offset(_x - x, _y - y) - return NotImplemented - - def __mul__(self, other: object) -> Offset: - if isinstance(other, (float, int)): - x, y = self - return Offset(int(x * other), int(y * other)) - return NotImplemented +from __future__ import annotations + +from typing import Iterable, TypeVar + +T = TypeVar("T") + + +def loop_first(values: Iterable[T]) -> Iterable[tuple[bool, T]]: + """Iterate and generate a tuple with a flag for first value.""" + iter_values = iter(values) + try: + value = next(iter_values) + except StopIteration: + return + yield True, value + for value in iter_values: + yield False, value + + +def loop_last(values: Iterable[T]) -> Iterable[tuple[bool, T]]: + """Iterate and generate a tuple with a flag for last value.""" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + for value in iter_values: + yield False, previous_value + previous_value = value + yield True, previous_value + + +def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]: + """Iterate and generate a tuple with a flag for first and last value.""" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + first = True + for value in iter_values: + yield first, False, previous_value + first = False + previous_value = value + yield first, True, previous_value ''' @@ -111,7 +123,10 @@ def compose(self) -> ComposeResult: yield from ( Tweet(TweetBody()), Widget( - Static(Syntax(CODE, "python"), classes="code"), + Static( + Syntax(CODE, "python", line_numbers=True, indent_guides=True), + classes="code", + ), classes="scrollable", ), table, diff --git a/src/textual/css/_help_text.py b/src/textual/css/_help_text.py index 220add7dcf..d24835e933 100644 --- a/src/textual/css/_help_text.py +++ b/src/textual/css/_help_text.py @@ -443,52 +443,6 @@ def layout_property_help_text(property_name: str, context: StylingContext) -> He ) -def docks_property_help_text(property_name: str, context: StylingContext) -> HelpText: - """Help text to show when the user supplies an invalid value for docks. - - Args: - property_name (str): The name of the property - context (StylingContext | None): The context the property is being used in. - - Returns: - HelpText: Renderable for displaying the help text for this property - """ - property_name = _contextualize_property_name(property_name, context) - return HelpText( - summary=f"Invalid value for [i]{property_name}[/] property", - bullets=[ - *ContextSpecificBullets( - inline=[ - Bullet( - f"The [i]{property_name}[/] property expects an iterable of DockGroups", - examples=[ - Example( - f"widget.styles.{property_name} = [DockGroup(...), DockGroup(...)]" - ) - ], - ), - ], - css=[ - Bullet( - f"The [i]{property_name}[/] property expects a value of the form =/...", - examples=[ - Example( - f"{property_name}: lhs=left/2; [dim]# dock named [u]lhs[/], on [u]left[/] edge, with z-index [u]2[/]" - ), - Example( - f"{property_name}: top=top/3 rhs=right/2; [dim]# declaring multiple docks" - ), - ], - ), - Bullet(" can be any string you want"), - Bullet(f" must be one of {friendly_list(VALID_EDGE)}"), - Bullet(f" must be an integer"), - ], - ).get_by_context(context) - ], - ) - - def dock_property_help_text(property_name: str, context: StylingContext) -> HelpText: """Help text to show when the user supplies an invalid value for dock. diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index 8eee77d6ac..f52021c472 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -15,7 +15,6 @@ string_enum_help_text, border_property_help_text, layout_property_help_text, - docks_property_help_text, dock_property_help_text, fractional_property_help_text, align_help_text, diff --git a/tests/css/test_help_text.py b/tests/css/test_help_text.py index 1114961d80..928ca2d624 100644 --- a/tests/css/test_help_text.py +++ b/tests/css/test_help_text.py @@ -9,8 +9,6 @@ color_property_help_text, border_property_help_text, layout_property_help_text, - docks_property_help_text, - dock_property_help_text, fractional_property_help_text, offset_property_help_text, align_help_text, @@ -92,12 +90,6 @@ def test_layout_property_help_text(styling_context): assert "layout" in rendered -def test_docks_property_help_text(styling_context): - rendered = render(docks_property_help_text("docks", styling_context)) - assert "Invalid value for" in rendered - assert "docks" in rendered - - def test_fractional_property_help_text(styling_context): rendered = render(fractional_property_help_text("opacity", styling_context)) assert "Invalid value for" in rendered From 971ef9e4b193746ae4719d76d64eac2c7f3b6177 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Aug 2022 15:39:06 +0100 Subject: [PATCH 11/30] more border --- docs/styles/border.md | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/docs/styles/border.md b/docs/styles/border.md index 9ca540c5f9..724aff236e 100644 --- a/docs/styles/border.md +++ b/docs/styles/border.md @@ -1,23 +1,36 @@ # Border -The `border` rule enables the drawing of a box around a widget. A border is set with a border style (see below) followed by a color. - -- `"ascii"` -- `"round"` -- `"solid"` -- `"double"` -- `"dashed"` -- `"heavy"` -- `"inner"` -- `"outer"` -- `"hkey"` -- `"vkey"` -- `"tall"` -- `"wide"` +The `border` rule enables the drawing of a box around a widget. A border is set with a border value (see below) followed by a color. + +| Border value | Explanation | +| ------------ | ------------------------------------------------------- | +| `"ascii"` | A border with plus, hyphen, and vertical bar | +| `"blank"` | A blank border (reserves space for a border) | +| `"dashed"` | Dashed line border | +| `"double"` | Double lined border | +| `"heavy"` | Heavy border | +| `"hidden"` | Alias for "none" | +| `"hkey"` | Horizontal key-line border | +| `"inner"` | Thick solid border | +| `"none"` | Disabled border | +| `"outer"` | Think solid border with additional space around content | +| `"round"` | Rounded corners | +| `"solid"` | Solid border | +| `"tall"` | A solid border with extras space top and bottom | +| `"vkey"` | Vertical key-line border | +| `"wide"` | A solid border with additional space left and right | For examples `heavy white` would display a heavy white line around a widget. -Borders may also be set individually with the `border-top`, `border-right`, `border-bottom` and `border-left` rules. +Borders may also be set individually for the four edges of a widget with the `border-top`, `border-right`, `border-bottom` and `border-left` rules. + +## Border command + +The `textual` CLI has a subcommand which will let you explore the various border types: + +``` +textual borders +``` ## Example From ca1ec683075db5b34094023f9c844f9942d177fd Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Aug 2022 16:29:30 +0100 Subject: [PATCH 12/30] text style docs --- docs/styles/text_style.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/styles/text_style.md b/docs/styles/text_style.md index d5869f9bfa..030192b0c9 100644 --- a/docs/styles/text_style.md +++ b/docs/styles/text_style.md @@ -2,11 +2,13 @@ The `text-style` rule enables a number of different ways of displaying text. The value may be set to any of the following: -- `"bold"` Sets **bold text** -- `"italic"` Sets _italic text_ -- `"reverse"` Sets reverse video text (foreground and background colors reversed) -- `"underline"` Sets underline text -- `"strike"` Sets strikethrough text +| Style | Effect | +| ------------- | -------------------------------------------------------------- | +| `"bold"` | **bold text** | +| `"italic"` | _italic text_ | +| `"reverse"` | reverse video text (foreground and background colors reversed) | +| `"underline"` | underline text | +| `"strike"` | strikethrough text | Text styles may be set in combination. For example "bold underline" or "reverse underline strike". From a9c475ceea1fd0a02e2640cb3c28635fac6c2ef3 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Aug 2022 16:41:39 +0100 Subject: [PATCH 13/30] words and tables --- docs/examples/styles/background.py | 2 +- docs/styles/border.md | 6 ++--- docs/styles/outline.md | 37 +++++++++++++++++------------- docs/styles/overflow.md | 14 +++++++---- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/docs/examples/styles/background.py b/docs/examples/styles/background.py index 792cf4c852..90742620a0 100644 --- a/docs/examples/styles/background.py +++ b/docs/examples/styles/background.py @@ -5,7 +5,7 @@ class BackgroundApp(App): CSS = """ Static { - height:1fr; + height: 1fr; content-align: center middle; color: white; } diff --git a/docs/styles/border.md b/docs/styles/border.md index 724aff236e..413697893e 100644 --- a/docs/styles/border.md +++ b/docs/styles/border.md @@ -16,11 +16,11 @@ The `border` rule enables the drawing of a box around a widget. A border is set | `"outer"` | Think solid border with additional space around content | | `"round"` | Rounded corners | | `"solid"` | Solid border | -| `"tall"` | A solid border with extras space top and bottom | +| `"tall"` | Solid border with extras space top and bottom | | `"vkey"` | Vertical key-line border | -| `"wide"` | A solid border with additional space left and right | +| `"wide"` | Solid border with additional space left and right | -For examples `heavy white` would display a heavy white line around a widget. +For example `heavy white` would display a heavy white line around a widget. Borders may also be set individually for the four edges of a widget with the `border-top`, `border-right`, `border-bottom` and `border-left` rules. diff --git a/docs/styles/outline.md b/docs/styles/outline.md index d475b95199..8d438a095d 100644 --- a/docs/styles/outline.md +++ b/docs/styles/outline.md @@ -2,22 +2,27 @@ The `outline` rule enables the drawing of a box around a widget. Similar to `border`, but unlike border, outline will draw over the content area. This rule can be useful for emphasis if you want to display a outline for a brief time to draw the user's attention to it. -An outline is set with a border style (see below) followed by a color. - -- `"ascii"` -- `"round"` -- `"solid"` -- `"double"` -- `"dashed"` -- `"heavy"` -- `"inner"` -- `"outer"` -- `"hkey"` -- `"vkey"` -- `"tall"` -- `"wide"` - -For examples `heavy white` would display a heavy white line around a widget. +An outline is set with a border value (see below) followed by a color. + +| Border value | Explanation | +| ------------ | ------------------------------------------------------- | +| `"ascii"` | A border with plus, hyphen, and vertical bar | +| `"blank"` | A blank border (reserves space for a border) | +| `"dashed"` | Dashed line border | +| `"double"` | Double lined border | +| `"heavy"` | Heavy border | +| `"hidden"` | Alias for "none" | +| `"hkey"` | Horizontal key-line border | +| `"inner"` | Thick solid border | +| `"none"` | Disabled border | +| `"outer"` | Think solid border with additional space around content | +| `"round"` | Rounded corners | +| `"solid"` | Solid border | +| `"tall"` | Solid border with extras space top and bottom | +| `"vkey"` | Vertical key-line border | +| `"wide"` | Solid border with additional space left and right | + +For example `heavy white` would display a heavy white line around a widget. Outlines may also be set individually with the `outline-top`, `outline-right`, `outline-bottom` and `outline-left` rules. diff --git a/docs/styles/overflow.md b/docs/styles/overflow.md index 8429403a42..8d0adaf52d 100644 --- a/docs/styles/overflow.md +++ b/docs/styles/overflow.md @@ -1,12 +1,16 @@ # Overflow -The `overflow` rule specifies if and when scrollbars should be displayed on the `x` and `y` axis. There are two values for each scrollbar, which may be set together or independently to one of the follow three values: +The `overflow` rule specifies if and when scrollbars should be displayed on the `x` and `y` axis. The rule takes two overflow values; one for the horizontal bar (x axis), followed by the vertical bar (y-axis). -- `"auto"` Automatically show the scrollbar if the content doesn't fit -- `"hidden"` Never show the scrollbar -- `"scroll"` Always show the scrollbar +| Overflow value | Effect | +| -------------- | ------------------------------------------------------------------------- | +| `"auto"` | Automatically show the scrollbar if the content doesn't fit (the default) | +| `"hidden"` | Never show the scrollbar | +| `"scroll"` | Always show the scrollbar | -The default is "auto" which will show the scrollbar if content doesn't fit within container, otherwise the scrollbar will be hidden. +The default value for overflow is `"auto auto"` which will show scrollbars automatically for both scrollbars if content doesn't fit within container. + +Overflow may also be set independently by setting the `overflow-x` rule for the horizontal bar, and `overflow-y` for the vertical bar. ## Example From 676c8aecb70bccc919df6bbca58b4b9fe7fb7833 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Aug 2022 16:45:34 +0100 Subject: [PATCH 14/30] words and tables --- docs/styles/padding.md | 2 ++ docs/styles/scrollbar.md | 18 +++++++++--------- docs/styles/text_style.md | 2 ++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/styles/padding.md b/docs/styles/padding.md index 75207b62a5..79cd51954d 100644 --- a/docs/styles/padding.md +++ b/docs/styles/padding.md @@ -10,6 +10,8 @@ Padding may also be set individually, following the same pattern as above, by se ## Example +This example adds padding around static text. + === "padding.py" ```python diff --git a/docs/styles/scrollbar.md b/docs/styles/scrollbar.md index 7a8a3912b4..9a11b1cd95 100644 --- a/docs/styles/scrollbar.md +++ b/docs/styles/scrollbar.md @@ -2,18 +2,18 @@ There are a number of rules to set the colors used in Textual scrollbars. You won't typically need to do this, as the default themes have carefully chosen colors, but you can if you want to. -| Rule | Color | -| ---------------------------- | ------------------------------------------------------- | -| `scrollbar-color` | Scrollbar "thumb" (movable part) | -| `scrollbar-color-hover` | Scrollbar thumb when the mouse is hovering over it | -| `scrollbar-color-active` | Scrollbar thumb when it is active (being dragged) | -| `scrollbar-background` | Scrollbar background | -| `scrollbar-background-hover` | Scrollbar background when the mouse is hovering over it | -| `scrollbar-color-active` | Scrollbar background when the thumb is being dragged | +| Rule | Color | +| ----------------------------- | ------------------------------------------------------- | +| `scrollbar-color` | Scrollbar "thumb" (movable part) | +| `scrollbar-color-hover` | Scrollbar thumb when the mouse is hovering over it | +| `scrollbar-color-active` | Scrollbar thumb when it is active (being dragged) | +| `scrollbar-background` | Scrollbar background | +| `scrollbar-background-hover` | Scrollbar background when the mouse is hovering over it | +| `scrollbar-background-active` | Scrollbar background when the thumb is being dragged | ## Example -In this example we have two panels, with different scrollbar colors set for each. +In this example we have two panels with different scrollbar colors set for each. === "scrollbars.py" diff --git a/docs/styles/text_style.md b/docs/styles/text_style.md index 030192b0c9..3dc1037fe8 100644 --- a/docs/styles/text_style.md +++ b/docs/styles/text_style.md @@ -14,6 +14,8 @@ Text styles may be set in combination. For example "bold underline" or "reverse ## Example +Each of the three text panels has a different text style. + === "text_style.py" ```python From 396036635a71d10b0a991c56639f6ed5a042648d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Aug 2022 17:03:44 +0100 Subject: [PATCH 15/30] words --- docs/styles/margin.md | 12 +++++++----- docs/styles/padding.md | 10 ++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/styles/margin.md b/docs/styles/margin.md index 2bd01ba689..584657301f 100644 --- a/docs/styles/margin.md +++ b/docs/styles/margin.md @@ -1,12 +1,14 @@ # Margin -The `margin` rule adds space around the entire widget. +The `margin` rule adds space around the entire widget. Margin may be specified with 1, 2 or 4 values. -- `margin: 1;` Sets a margin of 1 around all 4 edges -- `margin: 1 2;` Sets a margin of 1 on the top and bottom edges, and a margin of 2 on the left and right edges -- `margin: 1 2 3 4;` Sets a margin of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. +| example | | +| ------------------ | ------------------------------------------------------------------- | +| `margin: 1;` | A single value sets a margin of 1 around all 4 edges | +| `margin: 1 2;` | Two values sets the margin for the top/bottom and left/right edges | +| `margin: 1 2 3 4;` | Four values sets top, right, bottom, and left margins independently | -Margin may also be set individually, following the same pattern as above, by setting `margin-top`, `margin-right`, `margin-bottom`, or `margin-left`. +Margin may also be set individually by setting `margin-top`, `margin-right`, `margin-bottom`, or `margin-left` to an single value. ## Example diff --git a/docs/styles/padding.md b/docs/styles/padding.md index 79cd51954d..f8f54477bb 100644 --- a/docs/styles/padding.md +++ b/docs/styles/padding.md @@ -2,11 +2,13 @@ The padding rule adds space around the content of a widget. You can specify padding with 1, 2 or 4 numbers. -- `padding: 1;` Sets a padding of 1 around all 4 edges -- `padding: 1 2;` Sets a padding of 1 on the top and bottom edges, and a padding of two on the left and right edges -- `padding: 1 2 3 4;` Sets a padding of one on the top edge, 2 on the right, 3 on the bottom, and 4 on the left. +| example | | +| ------------------- | ------------------------------------------------------------------- | +| `padding: 1;` | A single value sets a padding of 1 around all 4 edges | +| `padding: 1 2;` | Two values sets the padding for the top/bottom and left/right edges | +| `padding: 1 2 3 4;` | Four values sets top, right, bottom, and left padding independently | -Padding may also be set individually, following the same pattern as above, by setting `padding-top`, `padding-right`, `padding-bottom`, or `padding-left`. +Padding may also be set individually by setting `padding-top`, `padding-right`, `padding-bottom`, or `padding-left` to an single value. ## Example From cbd0a6ec364cecf28a5cc1121dfc70af3d179483 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:13:16 +0100 Subject: [PATCH 16/30] Update docs/examples/styles/background.py Co-authored-by: darrenburns --- docs/examples/styles/background.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/styles/background.py b/docs/examples/styles/background.py index 90742620a0..41f5d7e48f 100644 --- a/docs/examples/styles/background.py +++ b/docs/examples/styles/background.py @@ -21,9 +21,9 @@ class BackgroundApp(App): """ def compose(self): - yield Static("Hello, World!", id="static1") - yield Static("Hello, World!", id="static2") - yield Static("Hello, World!", id="static3") + yield Static("Widget 1", id="static1") + yield Static("Widget 2", id="static2") + yield Static("Widget 3", id="static3") app = BackgroundApp() From b388a1376e8483da088e1decb5420d2a6281b54c Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:13:33 +0100 Subject: [PATCH 17/30] Update docs/examples/styles/border.py Co-authored-by: darrenburns --- docs/examples/styles/border.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/styles/border.py b/docs/examples/styles/border.py index 311dab5107..2aa7af768e 100644 --- a/docs/examples/styles/border.py +++ b/docs/examples/styles/border.py @@ -32,9 +32,9 @@ class BorderApp(App): """ def compose(self): - yield Static("Hello, World!", id="static1") - yield Static("Hello, World!", id="static2") - yield Static("Hello, World!", id="static3") + yield Static("My border is solid red", id="static1") + yield Static("My border is dashed green", id="static2") + yield Static("My border is tall blue", id="static3") app = BorderApp() From 7e985b6f972530b25526f01417d7b889b5f76680 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:13:43 +0100 Subject: [PATCH 18/30] Update docs/examples/styles/box_sizing.py Co-authored-by: darrenburns --- docs/examples/styles/box_sizing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/styles/box_sizing.py b/docs/examples/styles/box_sizing.py index 3b97601b54..bc264d6c88 100644 --- a/docs/examples/styles/box_sizing.py +++ b/docs/examples/styles/box_sizing.py @@ -25,8 +25,8 @@ class BoxSizingApp(App): """ def compose(self): - yield Static("Hello, World!", id="static1") - yield Static("Hello, World!", id="static2") + yield Static("I'm using border-box!", id="static1") + yield Static("I'm using content-box!", id="static2") app = BoxSizingApp() From b4aa7ec8b967ef70f283357c3ab7eca3bf8ad10f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:13:58 +0100 Subject: [PATCH 19/30] Update docs/examples/styles/display.py Co-authored-by: darrenburns --- docs/examples/styles/display.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/styles/display.py b/docs/examples/styles/display.py index cc5bd00219..463c767593 100644 --- a/docs/examples/styles/display.py +++ b/docs/examples/styles/display.py @@ -20,8 +20,8 @@ class DisplayApp(App): def compose(self): yield Static("Widget 1") - yield Static("widget 2", classes="remove") - yield Static("widget 3") + yield Static("Widget 2", classes="remove") + yield Static("Widget 3") app = DisplayApp() From 38047e4b3e3a4c86a06f70d62ba7b90623490b0f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:14:12 +0100 Subject: [PATCH 20/30] Update docs/examples/styles/offset.py Co-authored-by: darrenburns --- docs/examples/styles/offset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/styles/offset.py b/docs/examples/styles/offset.py index da65bc534c..aee8833754 100644 --- a/docs/examples/styles/offset.py +++ b/docs/examples/styles/offset.py @@ -38,9 +38,9 @@ class OffsetApp(App): """ def compose(self): - yield Static("Paul", classes="paul") - yield Static("Duncan", classes="duncan") - yield Static("Chani", classes="chani") + yield Static("Paul (offset 8 2)", classes="paul") + yield Static("Duncan (offset 4 10)", classes="duncan") + yield Static("Chani (offset 0 5)", classes="chani") app = OffsetApp() From 6602cd7374ace06ed1a3ef8f08c4e6127278b110 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:14:39 +0100 Subject: [PATCH 21/30] Update docs/styles/visibility.md Co-authored-by: darrenburns --- docs/styles/visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/styles/visibility.md b/docs/styles/visibility.md index 9db7ba10b6..a6c843bf8e 100644 --- a/docs/styles/visibility.md +++ b/docs/styles/visibility.md @@ -1,6 +1,6 @@ # Visibility -The `visibility` rule may be used to make a widget invisible while still reserving spacing for it. The default value is `"visible"` which will cause the Widget to be displayed as normal. Setting the value to `"hidden"` will cause the Widget to be removed from the screen. +The `visibility` rule may be used to make a widget invisible while still reserving spacing for it. The default value is `"visible"` which will cause the Widget to be displayed as normal. Setting the value to `"hidden"` will cause the Widget to become invisible. ## Example From 33dc6acad02d567920be1c313549864656748dd0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:15:06 +0100 Subject: [PATCH 22/30] Update docs/styles/color.md Co-authored-by: darrenburns --- docs/styles/color.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/styles/color.md b/docs/styles/color.md index 053dcd53e9..88d02b34a5 100644 --- a/docs/styles/color.md +++ b/docs/styles/color.md @@ -28,7 +28,7 @@ color: rgb(100,120,200); ## Python -You can use the same syntax as CSS, or explicitly set a Color object. +You can use the same syntax as CSS, or explicitly set a `Color` object. ```python # Set blue text From 331cbc650efb39b3d768bfa04e77604ed9f3b4be Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:16:01 +0100 Subject: [PATCH 23/30] Update docs/examples/styles/visibility.py Co-authored-by: darrenburns --- docs/examples/styles/visibility.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/styles/visibility.py b/docs/examples/styles/visibility.py index aeec9d228e..6b30954f3a 100644 --- a/docs/examples/styles/visibility.py +++ b/docs/examples/styles/visibility.py @@ -20,8 +20,8 @@ class VisibilityApp(App): def compose(self): yield Static("Widget 1") - yield Static("widget 2", classes="invisible") - yield Static("widget 3") + yield Static("Widget 2", classes="invisible") + yield Static("Widget 3") app = VisibilityApp() From fa61ca0f641aa87eeab0d6ea0a12e1ac12a44e2f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:16:37 +0100 Subject: [PATCH 24/30] Update docs/introduction.md Co-authored-by: darrenburns --- docs/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction.md b/docs/introduction.md index 0f124b8428..a180d74e7e 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -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` method once a second. +The second line in `on_mount` calls `self.set_interval` which tells Textual to invoke the `self.refresh` method once per second. When Textual refreshes a widget it calls it's `render` method: From 3976698f4c942b181b9fb8cc7d91d851efe76ab1 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:16:57 +0100 Subject: [PATCH 25/30] Update docs/introduction.md Co-authored-by: darrenburns --- docs/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction.md b/docs/introduction.md index a180d74e7e..0cd199918b 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -44,7 +44,7 @@ The first step in all Textual applications is to import the `App` class from `te 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 the `run()` method: +The last two lines create an instance of the application and call the `run()` method: ```python hl_lines="8 9" title="intro01.py" --8<-- "docs/examples/introduction/intro01.py" From 078fc245aee9da0f6e5e0ac04040a62307d596ce Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:17:20 +0100 Subject: [PATCH 26/30] Update docs/styles/background.md Co-authored-by: darrenburns --- docs/styles/background.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/styles/background.md b/docs/styles/background.md index 3530e0d80c..9cb3fe9c7a 100644 --- a/docs/styles/background.md +++ b/docs/styles/background.md @@ -28,7 +28,7 @@ background: rgb(100,120,200); ## Python -You can use the same syntax as CSS, or explicitly set a Color object. +You can use the same syntax as CSS, or explicitly set a `Color` object for finer-grained control. ```python # Set blue background From cb0537ef8e9afa76969f7e4be27f4ae5e3bd92ec Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:17:44 +0100 Subject: [PATCH 27/30] Update docs/styles/background.md Co-authored-by: darrenburns --- docs/styles/background.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/styles/background.md b/docs/styles/background.md index 9cb3fe9c7a..8571ef1114 100644 --- a/docs/styles/background.md +++ b/docs/styles/background.md @@ -37,5 +37,5 @@ widget.styles.background = "blue" from textual.color import Color # Set with a color object widget.styles.background = Color.parse("pink") - +widget.styles.background = Color(120, 60, 100) ``` From 7fd3fe53d395374afaccf59ed32862c46fbd1ae1 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:21:48 +0100 Subject: [PATCH 28/30] Update docs/styles/border.md Co-authored-by: darrenburns --- docs/styles/border.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/styles/border.md b/docs/styles/border.md index 413697893e..03992b3249 100644 --- a/docs/styles/border.md +++ b/docs/styles/border.md @@ -61,8 +61,8 @@ border-left: outer red; ```python # Set a heavy white border -widget.border = ("heavy", "white) +widget.border = ("heavy", "white") # Set a red border on the left -widget.border_left = ("outer", "red) +widget.border_left = ("outer", "red") ``` From ce5e36e0c6ba479d113af0beb8e6a66ec742a466 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:22:18 +0100 Subject: [PATCH 29/30] Update docs/styles/box_sizing.md Co-authored-by: darrenburns --- docs/styles/box_sizing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/styles/box_sizing.md b/docs/styles/box_sizing.md index d0d5fdce2b..6ef19eadfc 100644 --- a/docs/styles/box_sizing.md +++ b/docs/styles/box_sizing.md @@ -2,7 +2,7 @@ The `box-sizing` rule impacts how `width` and `height` rules are translated in to screen dimensions, when combined with `padding` and `border`. -The default value is `border-box` which means that padding and border are included in in the width and height. This setting means that if you add padding and/or border the widget will not change in size, but you will have less space for content. +The default value is `border-box` which means that padding and border are included in the width and height. This setting means that if you add padding and/or border the widget will not change in size, but you will have less space for content. You can set `box-sizing` to `content-box` which tells Textual that padding and border should increase the size of the widget, leaving the content area unaffected. From 869731275646c138b1ef756f1d1bb290a15e2d6c Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 5 Aug 2022 09:26:26 +0100 Subject: [PATCH 30/30] Update docs/examples/styles/color.py Co-authored-by: darrenburns --- docs/examples/styles/color.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/examples/styles/color.py b/docs/examples/styles/color.py index 6d29d9a300..415dfbc285 100644 --- a/docs/examples/styles/color.py +++ b/docs/examples/styles/color.py @@ -20,9 +20,9 @@ class ColorApp(App): """ def compose(self): - yield Static("Hello, World!", id="static1") - yield Static("Hello, World!", id="static2") - yield Static("Hello, World!", id="static3") + yield Static("I'm red!", id="static1") + yield Static("I'm rgb(0, 255, 0)!", id="static2") + yield Static("I'm hsl(240, 100%, 50%)!", id="static3") app = ColorApp()