mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
more docs
This commit is contained in:
29
docs/examples/styles/background.py
Normal file
29
docs/examples/styles/background.py
Normal file
@@ -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()
|
||||
34
docs/examples/styles/border.py
Normal file
34
docs/examples/styles/border.py
Normal file
@@ -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()
|
||||
28
docs/examples/styles/color.py
Normal file
28
docs/examples/styles/color.py
Normal file
@@ -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()
|
||||
@@ -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()
|
||||
|
||||
@@ -2,7 +2,7 @@ from textual.app import App
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class WidthApp(App):
|
||||
class HeightApp(App):
|
||||
CSS = """
|
||||
Screen > Widget {
|
||||
background: green;
|
||||
@@ -15,4 +15,4 @@ class WidthApp(App):
|
||||
yield Widget()
|
||||
|
||||
|
||||
app = WidthApp()
|
||||
app = HeightApp()
|
||||
|
||||
27
docs/examples/styles/margin.py
Normal file
27
docs/examples/styles/margin.py
Normal file
@@ -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()
|
||||
27
docs/examples/styles/padding.py
Normal file
27
docs/examples/styles/padding.py
Normal file
@@ -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()
|
||||
41
docs/examples/styles/text_style.py
Normal file
41
docs/examples/styles/text_style.py
Normal file
@@ -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()
|
||||
27
docs/examples/styles/visibility.py
Normal file
27
docs/examples/styles/visibility.py
Normal file
@@ -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()
|
||||
41
docs/styles/background.md
Normal file
41
docs/styles/background.md
Normal file
@@ -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")
|
||||
|
||||
```
|
||||
41
docs/styles/color.md
Normal file
41
docs/styles/color.md
Normal file
@@ -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")
|
||||
|
||||
```
|
||||
@@ -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
|
||||
|
||||
|
||||
34
docs/styles/margin.md
Normal file
34
docs/styles/margin.md
Normal file
@@ -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)
|
||||
```
|
||||
34
docs/styles/padding.md
Normal file
34
docs/styles/padding.md
Normal file
@@ -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)
|
||||
```
|
||||
36
docs/styles/text_style.md
Normal file
36
docs/styles/text_style.md
Normal file
@@ -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 <u>underline text</u>
|
||||
- `"strike"` Sets <s>strikethrough text</s>
|
||||
|
||||
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"
|
||||
```
|
||||
48
docs/styles/visibility.md
Normal file
48
docs/styles/visibility.md
Normal file
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
|
||||
|
||||
29
examples/borders.py
Normal file
29
examples/borders.py
Normal file
@@ -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()
|
||||
@@ -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"
|
||||
|
||||
11
src/textual/constants.py
Normal file
11
src/textual/constants.py
Normal file
@@ -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)
|
||||
@@ -29,7 +29,7 @@ class HorizontalLayout(Layout):
|
||||
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)
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user