Merge branch 'main' into get-treenode-by-id

This commit is contained in:
Dave Pearson
2023-01-10 14:59:36 +00:00
committed by GitHub
201 changed files with 11096 additions and 1124 deletions

View File

@@ -13,12 +13,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added public `TreeNode` label access via `TreeNode.label` https://github.com/Textualize/textual/issues/1396
- Added read-only public access to the children of a `TreeNode` via `TreeNode.children` https://github.com/Textualize/textual/issues/1398
- Added `Tree.get_node_by_id` to allow getting a node by its ID https://github.com/Textualize/textual/pull/1535
- Added a `Tree.NodeHighlighted` message, giving a `on_tree_node_highlighted` event handler https://github.com/Textualize/textual/issues/1400
### Changed
- `MouseScrollUp` and `MouseScrollDown` now inherit from `MouseEvent` and have attached modifier keys. https://github.com/Textualize/textual/pull/1458
- Fail-fast and print pretty tracebacks for Widget compose errors https://github.com/Textualize/textual/pull/1505
- Added Widget._refresh_scroll to avoid expensive layout when scrolling https://github.com/Textualize/textual/pull/1524
- `events.Paste` now bubbles https://github.com/Textualize/textual/issues/1434
### Fixed

View File

@@ -396,7 +396,7 @@ Below you can see the code I wrote and a short animation of the app working.
=== "CSS"
```css
```sass
Screen {
align: center middle;
}

View File

@@ -0,0 +1,70 @@
<!-- Template file for a Textual CSS type reference page. -->
# &lt;type-name&gt;
<!-- Short description of the type. -->
## Syntax
<!--
For a simple type like <integer>:
Describe the type in a short paragraph with an absolute link to the type page.
E.g., “The [`<my-type>`](/css_types/my_type) type is such and such with sprinkles on top.”
-->
<!--
For a type with many different values like <color>:
Introduce the type with a link to [`<my-type>`](/css_types/my_type).
Then, a bullet list with the variants accepted:
- you can create this type with X Y Z;
- you can also do A B C; and
- also use D E F.
-->
<!--
For a type that accepts specific options like <border>:
Add a sentence and a table. Consider ordering values in alphabetical order if there is no other obvious ordering. See below:
The [`<my-type>`](/css_types/my_type) type can take any of the following values:
| Value | Description |
|---------------|-----------------------------------------------|
| `abc` | Describe here. |
| `other val` | Describe this one also. |
| `value three` | Please use full stops. |
| `zyx` | Describe the value without assuming any rule. |
-->
## Examples
### CSS
<!--
Include a good variety of examples.
If the type has many different syntaxes, cover all of them.
Add comments when needed/if helpful.
-->
```sass
.some-class {
rule: type-value-1;
rule: type-value-2;
rule: type-value-3;
}
```
### Python
<!-- Same examples as above. -->
```py
widget.styles.rule = type_value_1
widget.styles.rule = type_value_2
widget.styles.rule = type_value_3
```

54
docs/css_types/border.md Normal file
View File

@@ -0,0 +1,54 @@
# &lt;border&gt;
The `<border>` CSS type represents a border style.
## Syntax
The [`<border>`](/css_types/border) type can take any of the following values:
| Border type | Description |
|-------------|----------------------------------------------------------|
| `ascii` | A border with plus, hyphen, and vertical bar characters. |
| `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` | Solid border with additional space around content. |
| `round` | Rounded corners. |
| `solid` | Solid border. |
| `tall` | Solid border with additional space top and bottom. |
| `vkey` | Vertical key-line border. |
| `wide` | Solid border with additional space left and right. |
## Border command
The `textual` CLI has a subcommand which will let you explore the various border types interactively, when applied to the CSS rule [`border`](../styles/border.md):
```
textual borders
```
## Examples
### CSS
```sass
#container {
border: heavy red;
}
#heading {
border-bottom: solid blue;
}
```
### Python
```py
widget.styles.border = ("heavy", "red")
widget.styles.border_bottom = ("solid", "blue")
```

138
docs/css_types/color.md Normal file
View File

@@ -0,0 +1,138 @@
# &lt;color&gt;
The `<color>` CSS type represents a color.
!!! warning
Not to be confused with the [`color`](../styles/color.md) CSS rule to set text color.
## Syntax
A [`<color>`](/css_types/color) should be in one of the formats explained in this section.
A bullet point summary of the formats available follows:
- a recognised [named color](#named-colors) (e.g., `red`);
- a 3 or 6 hexadecimal digit number representing the [RGB values](#hex-rgb-value) of the color (e.g., `#F35573`);
- a 4 or 8 hexadecimal digit number representing the [RGBA values](#hex-rgba-value) of the color (e.g., `#F35573A0`);
- a color description in the RGB system, [with](#rgba-description) or [without](#rgb-description) transparency (e.g., `rgb(23, 78, 200)`);
- a color description in the HSL system, [with](#hsla-description) or [without](#hsl-description) transparency (e.g., `hsl(290, 70%, 80%)`);
[Textual's default themes](../../guide/design#theme-reference) also provide many CSS variables with colors that can be used out of the box.
### Named colors
A named color is a [`<name>`](./name.md) that Textual recognises.
Below, you can find a (collapsed) list of all of the named colors that Textual recognises, along with their hexadecimal values, their RGB values, and a visual sample.
<details>
<summary>All named colors available.</summary>
```{.rich columns="80" title="colors"}
from textual._color_constants import COLOR_NAME_TO_RGB
from textual.color import Color
from rich.table import Table
from rich.text import Text
table = Table("Name", "hex", "RGB", "Color", expand=True, highlight=True)
for name, triplet in sorted(COLOR_NAME_TO_RGB.items()):
if len(triplet) != 3:
continue
color = Color(*triplet)
r, g, b = triplet
table.add_row(
f'"{name}"',
Text(f"{color.hex}", "bold green"),
f"rgb({r}, {g}, {b})",
Text(" ", style=f"on rgb({r},{g},{b})")
)
output = table
```
</details>
### Hex RGB value
The hexadecimal RGB format starts with an octothorpe `#` and is then followed by 3 or 6 hexadecimal digits: `0123456789ABCDEF`.
Casing is ignored.
- If 6 digits are used, the format is `#RRGGBB`:
- `RR` represents the red channel;
- `GG` represents the green channel; and
- `BB` represents the blue channel.
- If 3 digits are used, the format is `#RGB`.
In a 3 digit color, each channel is represented by a single digit which is duplicated when converting to the 6 digit format.
For example, the color `#A2F` is the same as `#AA22FF`.
### Hex RGBA value
This is the same as the [hex RGB value](#hex-rgb-value), but with an extra channel for the alpha component (that sets transparency).
- If 8 digits are used, the format is `#RRGGBBAA`, equivalent to the format `#RRGGBB` with two extra digits for transparency.
- If 4 digits are used, the format is `#RGBA`, equivalent to the format `#RGB` with an extra digit for transparency.
### `rgb` description
The `rgb` format description is a functional description of a color in the RGB color space.
This description follows the format `rgb(red, green, blue)`, where `red`, `green`, and `blue` are decimal integers between 0 and 255.
They represent the value of the channel with the same name.
For example, `rgb(0, 255, 32)` is equivalent to `#00FF20`.
### `rgba` description
The `rgba` format description is the same as the `rgb` with an extra parameter for transparency, which should be a value between `0` and `1`.
For example, `rgba(0, 255, 32, 0.5)` is the color `rgb(0, 255, 32)` with 50% transparency.
### `hsl` description
The `hsl` format description is a functional description of a color in the HSL color space.
This description follows the format `hsl(hue, saturation, lightness)`, where
- `hue` is a float between 0 and 360;
- `saturation` is a percentage between `0%` and `100%`; and
- `lightness` is a percentage between `0%` and `100%`.
For example, the color `#00FF20` would be represented as `hsl(128, 100%, 50%)` in the HSL color space.
### `hsla` description
The `hsla` format description is the same as the `hsl` with an extra parameter for transparency, which should be a value between `0` and `1`.
For example, `hsla(128, 100%, 50%, 0.5)` is the color `hsl(128, 100%, 50%)` with 50% transparency.
## Examples
### CSS
```sass
Header {
background: red; /* Color name */
}
.accent {
color: $accent; /* Textual variable */
}
#footer {
tint: hsl(300, 20%, 70%); /* HSL description */
}
```
### Python
In Python, rules that expect a `<color>` can also accept an instance of the type [`Color`][textual.color.Color].
```py
# Mimicking the CSS syntax
widget.styles.background = "red" # Color name
widget.styles.color = "$accent" # Textual variable
widget.styles.tint = "hsl(300, 20%, 70%)" # HSL description
from textual.color import Color
# Using a Color object directly...
color = Color(16, 200, 45)
# ... which can also parse the CSS syntax
color = Color.parse("#A8F")
```

View File

@@ -0,0 +1,29 @@
# &lt;horizontal&gt;
The `<horizontal>` CSS type represents a position along the horizontal axis.
## Syntax
The [`<horizontal>`](/css_types/horizontal) type can take any of the following values:
| Value | Description |
| ---------------- | -------------------------------------------- |
| `center` | Aligns in the center of the horizontal axis. |
| `left` (default) | Aligns on the left of the horizontal axis. |
| `right` | Aligns on the right of the horizontal axis. |
## Examples
### CSS
```sass
.container {
align-horizontal: right;
}
```
### Python
```py
widget.styles.align_horizontal = "right"
```

12
docs/css_types/index.md Normal file
View File

@@ -0,0 +1,12 @@
# CSS Types
CSS types define the values that Textual CSS styles accept.
CSS types will be linked from within the [styles reference](../styles/index.md) in the "Formal Syntax" section of each style.
The CSS types will be denoted by a keyword enclosed by angle brackets `<` and `>`.
For example, the style [`align-horizontal`](../styles/align.md) references the CSS type [`<horizontal>`](./horizontal.md):
--8<-- "docs/snippets/syntax_block_start.md"
align-horizontal: <a href="./horizontal.md">&lt;horizontal&gt;</a>;
--8<-- "docs/snippets/syntax_block_end.md"

29
docs/css_types/integer.md Normal file
View File

@@ -0,0 +1,29 @@
# &lt;integer&gt;
The `<integer>` CSS type represents an integer number.
## Syntax
An [`<integer>`](/css_types/integer) is any valid integer number like `-10` or `42`.
!!! note
Some CSS rules may expect an `<integer>` within certain bounds. If that is the case, it will be noted in that rule.
## Examples
### CSS
```sass
.classname {
offset: 10 -20
}
```
### Python
In Python, a rule that expects a CSS type `<integer>` will expect a value of the type `int`:
```py
widget.styles.offset = (10, -20)
```

26
docs/css_types/name.md Normal file
View File

@@ -0,0 +1,26 @@
# &lt;name&gt;
The `<name>` type represents a sequence of characters that identifies something.
## Syntax
A [`<name>`](/css_types/name) is any non-empty sequence of characters:
- starting with a letter `a-z`, `A-Z`, or underscore `_`; and
- followed by zero or more letters `a-zA-Z`, digits `0-9`, underscores `_`, and hiphens `-`.
## Examples
### CSS
```sass
Screen {
layers: onlyLetters Letters-and-hiphens _lead-under letters-1-digit;
}
```
### Python
```py
widget.styles.layers = "onlyLetters Letters-and-hiphens _lead-under letters-1-digit"
```

30
docs/css_types/number.md Normal file
View File

@@ -0,0 +1,30 @@
# &lt;number&gt;
The `<number>` CSS type represents a real number, which can be an integer or a number with a decimal part (akin to a `float` in Python).
## Syntax
A [`<number>`](/css_types/number) is an [`<integer>`](/css_types/integer), optionally followed by the decimal point `.` and a decimal part composed of one or more digits.
## Examples
### CSS
```sass
Grid {
grid-size: 3 6 /* Integers are numbers */
}
.translucid {
opacity: 0.5 /* Numbers can have a decimal part */
}
```
### Python
In Python, a rule that expects a CSS type `<number>` will accept an `int` or a `float`:
```py
widget.styles.grid_size = (3, 6) # Integers are numbers
widget.styles.opacity = 0.5 # Numbers can have a decimal part
```

View File

@@ -0,0 +1,29 @@
# &lt;overflow&gt;
The `<overflow>` CSS type represents overflow modes.
## Syntax
The [`<overflow>`](/css_types/overflow) type can take any of the following values:
| Value | Description |
|----------|----------------------------------------|
| `auto` | Determine overflow mode automatically. |
| `hidden` | Don't overflow. |
| `scroll` | Allow overflowing. |
## Examples
### CSS
```sass
#container {
overflow-y: hidden; /* Don't overflow */
}
```
### Python
```py
widget.styles.overflow_y = "hidden" # Don't overflow
```

View File

@@ -0,0 +1,37 @@
# &lt;percentage&gt;
The `<percentage>` CSS type represents a percentage value.
It is often used to represent values that are relative to the parent's values.
!!! warning
Not to be confused with the [`<scalar>`](./scalar.md) type.
## Syntax
A [`<percentage>`](/css_types/percentage) is a [`<number>`](/css_types/number) followed by the percent sign `%` (without spaces).
Some rules may clamp the values between `0%` and `100%`.
## Examples
### CSS
```sass
#footer {
/* Integer followed by % */
color: red 70%;
/* The number can be negative/decimal, although that may not make sense */
offset: -30% 12.5%;
}
```
### Python
```py
# Integer followed by %
widget.styles.color = "red 70%"
# The number can be negative/decimal, althought that may not make sense
widget.styles.offset = ("-30%", "12.5%")
```

113
docs/css_types/scalar.md Normal file
View File

@@ -0,0 +1,113 @@
# &lt;scalar&gt;
The `<scalar>` CSS type represents a length.
It can be a [`<number>`](./number.md) and a unit, or the special value `auto`.
It is used to represent lengths, for example in the [`width`](../styles/width.md) and [`height`](../styles/height.md) rules.
!!! warning
Not to be confused with the [`<number>`](./number.md) or [`<percentage>`](./percentage.md) types.
## Syntax
A [`<scalar>`](/css_types/scalar) can be any of the following:
- a fixed number of cells (e.g., `10`);
- a fractional proportion relative to the sizes of the other widgets (e.g., `1fr`);
- a percentage relative to the container widget (e.g., `50%`);
- a percentage relative to the container width/height (e.g., `25w`/`75h`);
- a percentage relative to the viewport width/height (e.g., `25vw`/`75vh`); or
- the special value `auto` to compute the optimal size to fit without scrolling.
A complete reference table and detailed explanations follow.
You can [skip to the examples](#examples).
| Unit symbol | Unit | Example | Description |
|-------------|-----------------|---------|-------------------------------------------------------------|
| `""` | Cell | `10` | Number of cells (rows or columns). |
| `"fr"` | Fraction | `1fr` | Specifies the proportion of space the widget should occupy. |
| `"%"` | Percent | `75%` | Length relative to the container widget. |
| `"w"` | Width | `25w` | Percentage relative to the width of the container widget. |
| `"h"` | Height | `75h` | Percentage relative to the height of the container widget. |
| `"vw"` | Viewport width | `25vw` | Percentage relative to the viewport width. |
| `"vh"` | Viewport height | `75vh` | Percentage relative to the viewport height. |
| - | Auto | `auto` | Tries to compute the optimal size to fit without scrolling. |
### Cell
The number of cells is the only unit for a scalar that is _absolute_.
This can be an integer or a float but floats are truncated to integers.
If used to specify a horizontal length, it corresponds to the number of columns.
For example, in `width: 15`, this sets the width of a widget to be equal to 15 cells, which translates to 15 columns.
If used to specify a vertical length, it corresponds to the number of lines.
For example, in `height: 10`, this sets the height of a widget to be equal to 10 cells, which translates to 10 lines.
### Fraction
The unit fraction is used to represent proportional sizes.
For example, if two widgets are side by side and one has `width: 1fr` and the other has `width: 3fr`, the second one will be three times as wide as the first one.
### Percent
The percent unit matches a [`<percentage>`](./percentage.md) and is used to specify a total length relative to the space made available by the container widget.
If used to specify a horizontal length, it will be relative to the width of the container.
For example, `width: 50%` sets the width of a widget to 50% of the width of its container.
If used to specify a vertical length, it will be relative to the height of the container.
For example, `height: 50%` sets the height of a widget to 50% of the height of its container.
### Width
The width unit is similar to the percent unit, except it sets the percentage to be relative to the width of the container.
For example, `width: 25w` sets the width of a widget to 25% of the width of its container and `height: 25w` sets the height of a widget to 25% of the width of its container.
So, if the container has a width of 100 cells, the width and the height of the child widget will be of 25 cells.
### Height
The height unit is similar to the percent unit, except it sets the percentage to be relative to the height of the container.
For example, `height: 75h` sets the height of a widget to 75% of the height of its container and `width: 75h` sets the width of a widget to 75% of the height of its container.
So, if the container has a height of 100 cells, the width and the height of the child widget will be of 75 cells.
### Viewport width
This is the same as the [width unit](#width), except that it is relative to the width of the viewport instead of the width of the immediate container.
The width of the viewport is the width of the terminal minus the widths of widgets that are docked left or right.
For example, `width: 25vw` will try to set the width of a widget to be 25% of the viewport width, regardless of the widths of its containers.
### Viewport height
This is the same as the [height unit](#height), except that it is relative to the height of the viewport instead of the height of the immediate container.
The height of the viewport is the height of the terminal minus the heights of widgets that are docked top or bottom.
For example, `height: 75vh` will try to set the height of a widget to be 75% of the viewport height, regardless of the height of its containers.
### Auto
This special value will try to calculate the optimal size to fit the contents of the widget without scrolling.
For example, if its container is big enough, a label with `width: auto` will be just as wide as its text.
## Examples
### CSS
```sass
Horizontal {
width: 60; /* 60 cells */
height: 1fr; /* proportional size of 1 */
}
```
### Python
```py
widget.styles.width = 16 # Cell unit can be specified with an int/float
widget.styles.height = "1fr" # proportional size of 1
```

View File

@@ -0,0 +1,40 @@
# &lt;text-align&gt;
The `<text-align>` CSS type represents alignments that can be applied to text.
!!! warning
Not to be confused with the [`text-align`](../styles/text_align.md) CSS rule that sets the alignment of text in a widget.
## Syntax
A [`<text-align>`](/css_types/text_align) can be any of the following values:
| Value | Alignment type |
|-----------|--------------------------------------|
| `center` | Center alignment. |
| `end` | Alias for `right`. |
| `justify` | Text is justified inside the widget. |
| `left` | Left alignment. |
| `right` | Right alignment. |
| `start` | Alias for `left`. |
!!! tip
The meanings of `start` and `end` will likely change when RTL languages become supported by Textual.
## Examples
### CSS
```sass
Label {
rule: justify;
}
```
### Python
```py
widget.styles.text_align = "justify"
```

View File

@@ -0,0 +1,46 @@
# &lt;text-style&gt;
The `<text-style>` CSS type represents styles that can be applied to text.
!!! warning
Not to be confused with the [`text-style`](../styles/text_style.md) CSS rule that sets the style of text in a widget.
## Syntax
A [`<text-style>`](/css_types/text_style) can be any _space-separated_ combination of the following values:
| Value | Description |
|-------------|-----------------------------------------------------------------|
| `bold` | **Bold text.** |
| `italic` | _Italic text._ |
| `none` | Plain text with no styling. |
| `reverse` | Reverse video text (foreground and background colors reversed). |
| `strike` | <s>Strikethrough text.</s> |
| `underline` | <u>Underline text.</u> |
## Examples
### CSS
```sass
#label1 {
/* You can specify any value by itself. */
rule: strike;
}
#label2 {
/* You can also combine multiple values. */
rule: strike bold italic reverse;
}
```
### Python
```py
# You can specify any value by itself
widget.styles.text_style = "strike"
# You can also combine multiple values
widget.styles.text_style = "bold underline italic"
```

View File

@@ -0,0 +1,29 @@
# &lt;vertical&gt;
The `<vertical>` CSS type represents a position along the vertical axis.
## Syntax
The [`<vertical>`](/css_types/vertical) type can take any of the following values:
| Value | Description |
| --------------- | ------------------------------------------ |
| `bottom` | Aligns at the bottom of the vertical axis. |
| `middle` | Aligns in the middle of the vertical axis. |
| `top` (default) | Aligns at the top of the vertical axis. |
## Examples
### CSS
```sass
.container {
align-vertical: top;
}
```
### Python
```py
widget.styles.align_vertical = "top"
```

View File

@@ -12,7 +12,7 @@ from textual.widgets import Static, Input
class DictionaryApp(App):
"""Searches ab dictionary API as-you-type."""
"""Searches a dictionary API as-you-type."""
CSS_PATH = "dictionary.css"

View File

@@ -11,8 +11,8 @@ Static {
}
#box1 {
background: darkcyan;
layer: above;
background: darkcyan;
}
#box2 {

View File

@@ -1,11 +1,11 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
class AlignApp(App):
def compose(self):
yield Static("Vertical alignment with [b]Textual[/]", classes="box")
yield Static("Take note, browsers.", classes="box")
yield Label("Vertical alignment with [b]Textual[/]", classes="box")
yield Label("Take note, browsers.", classes="box")
app = AlignApp(css_path="align.css")

View File

@@ -0,0 +1,53 @@
#left-top {
/* align: left top; this is the default value and is implied. */
}
#center-top {
align: center top;
}
#right-top {
align: right top;
}
#left-middle {
align: left middle;
}
#center-middle {
align: center middle;
}
#right-middle {
align: right middle;
}
#left-bottom {
align: left bottom;
}
#center-bottom {
align: center bottom;
}
#right-bottom {
align: right bottom;
}
Screen {
layout: grid;
grid-size: 3 3;
grid-gutter: 1;
}
Container {
background: $boost;
border: solid gray;
height: 100%;
}
Label {
width: auto;
height: 1;
background: $accent;
}

View File

@@ -0,0 +1,20 @@
from textual.app import App, ComposeResult
from textual.containers import Container
from textual.widgets import Label
class AlignAllApp(App):
"""App that illustrates all alignments."""
CSS_PATH = "align_all.css"
def compose(self) -> ComposeResult:
yield Container(Label("left top"), id="left-top")
yield Container(Label("center top"), id="center-top")
yield Container(Label("right top"), id="right-top")
yield Container(Label("left middle"), id="left-middle")
yield Container(Label("center middle"), id="center-middle")
yield Container(Label("right middle"), id="right-middle")
yield Container(Label("left bottom"), id="left-bottom")
yield Container(Label("center bottom"), id="center-bottom")
yield Container(Label("right bottom"), id="right-bottom")

View File

@@ -1,14 +1,18 @@
Static {
Label {
width: 100%;
height: 1fr;
content-align: center middle;
color: white;
}
}
#static1 {
background: red;
}
#static2 {
background: rgb(0, 255, 0);
}
#static3 {
background: hsl(240, 100%, 50%);
}

View File

@@ -1,12 +1,12 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
class BackgroundApp(App):
def compose(self):
yield Static("Widget 1", id="static1")
yield Static("Widget 2", id="static2")
yield Static("Widget 3", id="static3")
yield Label("Widget 1", id="static1")
yield Label("Widget 2", id="static2")
yield Label("Widget 3", id="static3")
app = BackgroundApp(css_path="background.css")

View File

@@ -0,0 +1,49 @@
#t10 {
background: red 10%;
}
#t20 {
background: red 20%;
}
#t30 {
background: red 30%;
}
#t40 {
background: red 40%;
}
#t50 {
background: red 50%;
}
#t60 {
background: red 60%;
}
#t70 {
background: red 70%;
}
#t80 {
background: red 80%;
}
#t90 {
background: red 90%;
}
#t100 {
background: red 100%;
}
Screen {
layout: horizontal;
}
Static {
height: 100%;
width: 1fr;
content-align: center middle;
}

View File

@@ -0,0 +1,20 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class BackgroundTransparencyApp(App):
"""Simple app to exemplify different transparency settings."""
def compose(self) -> ComposeResult:
yield Static("10%", id="t10")
yield Static("20%", id="t20")
yield Static("30%", id="t30")
yield Static("40%", id="t40")
yield Static("50%", id="t50")
yield Static("60%", id="t60")
yield Static("70%", id="t70")
yield Static("80%", id="t80")
yield Static("90%", id="t90")
yield Static("100%", id="t100")
app = BackgroundTransparencyApp(css_path="background_transparency.css")

View File

@@ -1,25 +1,30 @@
#label1 {
background: red 20%;
color: red;
border: solid red;
}
#label2 {
background: green 20%;
color: green;
border: dashed green;
}
#label3 {
background: blue 20%;
color: blue;
border: tall blue;
}
Screen {
background: white;
}
Screen > Static {
Screen > Label {
width: 100%;
height: 5;
content-align: center middle;
color: white;
margin: 1;
box-sizing: border-box;
}
#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;
}

View File

@@ -1,12 +1,12 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
class BorderApp(App):
def compose(self):
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")
yield Label("My border is solid red", id="label1")
yield Label("My border is dashed green", id="label2")
yield Label("My border is tall blue", id="label3")
app = BorderApp(css_path="border.css")

View File

@@ -0,0 +1,71 @@
#ascii {
border: ascii $accent;
}
#blank {
border: blank $accent;
}
#dashed {
border: dashed $accent;
}
#double {
border: double $accent;
}
#heavy {
border: heavy $accent;
}
#hidden {
border: hidden $accent;
}
#hkey {
border: hkey $accent;
}
#inner {
border: inner $accent;
}
#none {
border: none $accent;
}
#outer {
border: outer $accent;
}
#round {
border: round $accent;
}
#solid {
border: solid $accent;
}
#tall {
border: tall $accent;
}
#vkey {
border: vkey $accent;
}
#wide {
border: wide $accent;
}
Grid {
grid-size: 3 5;
align: center middle;
grid-gutter: 1 2;
}
Label {
width: 20;
height: 3;
content-align: center middle;
}

View File

@@ -0,0 +1,26 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Label
class AllBordersApp(App):
def compose(self):
yield Grid(
Label("ascii", id="ascii"),
Label("blank", id="blank"),
Label("dashed", id="dashed"),
Label("double", id="double"),
Label("heavy", id="heavy"),
Label("hidden/none", id="hidden"),
Label("hkey", id="hkey"),
Label("inner", id="inner"),
Label("none", id="none"),
Label("outer", id="outer"),
Label("round", id="round"),
Label("solid", id="solid"),
Label("tall", id="tall"),
Label("vkey", id="vkey"),
Label("wide", id="wide"),
)
app = AllBordersApp(css_path="border_all.css")

View File

@@ -1,7 +1,16 @@
#static1 {
box-sizing: border-box;
}
#static2 {
box-sizing: content-box;
}
Screen {
background: white;
color: black;
}
App Static {
background: blue 20%;
height: 5;
@@ -9,9 +18,3 @@ App Static {
padding: 1;
border: wide black;
}
#static1 {
box-sizing: border-box;
}
#static2 {
box-sizing: content-box;
}

View File

@@ -1,13 +1,17 @@
Static {
height:1fr;
Label {
height: 1fr;
content-align: center middle;
}
#static1 {
width: 100%;
}
#label1 {
color: red;
}
#static2 {
#label2 {
color: rgb(0, 255, 0);
}
#static3 {
color: hsl(240, 100%, 50%)
#label3 {
color: hsl(240, 100%, 50%);
}

View File

@@ -1,12 +1,12 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
class ColorApp(App):
def compose(self):
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")
yield Label("I'm red!", id="label1")
yield Label("I'm rgb(0, 255, 0)!", id="label2")
yield Label("I'm hsl(240, 100%, 50%)!", id="label3")
app = ColorApp(css_path="color.css")

View File

@@ -0,0 +1,26 @@
Label {
color: auto 80%;
content-align: center middle;
height: 1fr;
width: 100%;
}
#lbl1 {
background: red 80%;
}
#lbl2 {
background: yellow 80%;
}
#lbl3 {
background: blue 80%;
}
#lbl4 {
background: pink 80%;
}
#lbl5 {
background: green 80%;
}

View File

@@ -0,0 +1,14 @@
from textual.app import App
from textual.widgets import Label
class ColorApp(App):
def compose(self):
yield Label("The quick brown fox jumps over the lazy dog!", id="lbl1")
yield Label("The quick brown fox jumps over the lazy dog!", id="lbl2")
yield Label("The quick brown fox jumps over the lazy dog!", id="lbl3")
yield Label("The quick brown fox jumps over the lazy dog!", id="lbl4")
yield Label("The quick brown fox jumps over the lazy dog!", id="lbl5")
app = ColorApp(css_path="color_auto.css")

View File

@@ -0,0 +1,30 @@
#p1 {
column-span: 4;
}
#p2 {
column-span: 3;
}
#p3 {
column-span: 1; /* Didn't need to be set explicitly. */
}
#p4 {
column-span: 2;
}
#p5 {
column-span: 2;
}
#p6 {
/* Default value is 1. */
}
#p7 {
column-span: 3;
}
Grid {
grid-size: 4 4;
grid-gutter: 1 2;
}
Placeholder {
height: 100%;
}

View File

@@ -0,0 +1,19 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Placeholder
class MyApp(App):
def compose(self):
yield Grid(
Placeholder(id="p1"),
Placeholder(id="p2"),
Placeholder(id="p3"),
Placeholder(id="p4"),
Placeholder(id="p5"),
Placeholder(id="p6"),
Placeholder(id="p7"),
)
app = MyApp(css_path="column_span.css")

View File

@@ -4,7 +4,8 @@
}
#box2 {
content-align: center middle;
content-align-horizontal: center;
content-align-vertical: middle;
background: green;
}
@@ -13,7 +14,8 @@
background: blue;
}
Static {
Label {
width: 100%;
height: 1fr;
padding: 1;
color: white;

View File

@@ -1,12 +1,12 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
class ContentAlignApp(App):
def compose(self):
yield Static("With [i]content-align[/] you can...", id="box1")
yield Static("...[b]Easily align content[/]...", id="box2")
yield Static("...Horizontally [i]and[/] vertically!", id="box3")
yield Label("With [i]content-align[/] you can...", id="box1")
yield Label("...[b]Easily align content[/]...", id="box2")
yield Label("...Horizontally [i]and[/] vertically!", id="box3")
app = ContentAlignApp(css_path="content_align.css")

View File

@@ -0,0 +1,39 @@
#left-top {
/* content-align: left top; this is the default implied value. */
}
#center-top {
content-align: center top;
}
#right-top {
content-align: right top;
}
#left-middle {
content-align: left middle;
}
#center-middle {
content-align: center middle;
}
#right-middle {
content-align: right middle;
}
#left-bottom {
content-align: left bottom;
}
#center-bottom {
content-align: center bottom;
}
#right-bottom {
content-align: right bottom;
}
Screen {
layout: grid;
grid-size: 3 3;
grid-gutter: 1;
}
Label {
width: 100%;
height: 100%;
background: $primary;
}

View File

@@ -0,0 +1,18 @@
from textual.app import App
from textual.widgets import Label
class AllContentAlignApp(App):
def compose(self):
yield Label("left top", id="left-top")
yield Label("center top", id="center-top")
yield Label("right top", id="right-top")
yield Label("left middle", id="left-middle")
yield Label("center middle", id="center-middle")
yield Label("right middle", id="right-middle")
yield Label("left bottom", id="left-bottom")
yield Label("center bottom", id="center-bottom")
yield Label("right bottom", id="right-bottom")
app = AllContentAlignApp(css_path="content_align_all.css")

View File

@@ -1,12 +1,14 @@
Screen {
background: green;
}
Static {
height: 5;
background: white;
color: blue;
border: heavy blue;
Static {
height: 5;
background: white;
color: blue;
border: heavy blue;
}
Static.remove {
display: none;
}

View File

@@ -0,0 +1,34 @@
#left {
dock: left;
height: 100%;
width: auto;
align-vertical: middle;
}
#top {
dock: top;
height: auto;
width: 100%;
align-horizontal: center;
}
#right {
dock: right;
height: 100%;
width: auto;
align-vertical: middle;
}
#bottom {
dock: bottom;
height: auto;
width: 100%;
align-horizontal: center;
}
Screen {
align: center middle;
}
#big_container {
width: 75%;
height: 75%;
border: round white;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App
from textual.containers import Container
from textual.widgets import Label
class DockAllApp(App):
def compose(self):
yield Container(
Container(Label("left"), id="left"),
Container(Label("top"), id="top"),
Container(Label("right"), id="right"),
Container(Label("bottom"), id="bottom"),
id="big_container",
)
app = DockAllApp(css_path="dock_all.css")

View File

@@ -0,0 +1,11 @@
Grid {
grid-size: 5 2;
grid-columns: 1fr 16 2fr;
}
Label {
border: round white;
content-align-horizontal: center;
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,22 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Label
class MyApp(App):
def compose(self):
yield Grid(
Label("1fr"),
Label("width = 16"),
Label("2fr"),
Label("1fr"),
Label("width = 16"),
Label("1fr"),
Label("width = 16"),
Label("2fr"),
Label("1fr"),
Label("width = 16"),
)
app = MyApp(css_path="grid_columns.css")

View File

@@ -0,0 +1,11 @@
Grid {
grid-size: 2 4;
grid-gutter: 1 2; /* (1)! */
}
Label {
border: round white;
content-align: center middle;
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,20 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Label
class MyApp(App):
def compose(self):
yield Grid(
Label("1"),
Label("2"),
Label("3"),
Label("4"),
Label("5"),
Label("6"),
Label("7"),
Label("8"),
)
app = MyApp(css_path="grid_gutter.css")

View File

@@ -0,0 +1,11 @@
Grid {
grid-size: 2 5;
grid-rows: 1fr 6 25%;
}
Label {
border: round white;
content-align: center middle;
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,22 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Label
class MyApp(App):
def compose(self):
yield Grid(
Label("1fr"),
Label("1fr"),
Label("height = 6"),
Label("height = 6"),
Label("25%"),
Label("25%"),
Label("1fr"),
Label("1fr"),
Label("height = 6"),
Label("height = 6"),
)
app = MyApp(css_path="grid_rows.css")

View File

@@ -0,0 +1,10 @@
Grid {
grid-size: 2 4; /* (1)! */
}
Label {
border: round white;
content-align: center middle;
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Label
class MyApp(App):
def compose(self):
yield Grid(
Label("1"),
Label("2"),
Label("3"),
Label("4"),
Label("5"),
)
app = MyApp(css_path="grid_size_both.css")

View File

@@ -0,0 +1,10 @@
Grid {
grid-size: 2; /* (1)! */
}
Label {
border: round white;
content-align: center middle;
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Label
class MyApp(App):
def compose(self):
yield Grid(
Label("1"),
Label("2"),
Label("3"),
Label("4"),
Label("5"),
)
app = MyApp(css_path="grid_size_columns.css")

View File

@@ -1,4 +1,4 @@
Screen > Widget {
Screen > Widget {
background: green;
height: 50%;
color: white;

View File

@@ -0,0 +1,39 @@
#cells {
height: 2; /* (1)! */
}
#percent {
height: 12.5%; /* (2)! */
}
#w {
height: 5w; /* (3)! */
}
#h {
height: 12.5h; /* (4)! */
}
#vw {
height: 6.25vw; /* (5)! */
}
#vh {
height: 12.5vh; /* (6)! */
}
#auto {
height: auto; /* (7)! */
}
#fr1 {
height: 1fr; /* (8)! */
}
#fr2 {
height: 2fr; /* (9)! */
}
Screen {
layers: ruler;
}
Ruler {
layer: ruler;
dock: right;
overflow: hidden;
width: 1;
background: $accent;
}

View File

@@ -0,0 +1,28 @@
from textual.app import App
from textual.containers import Vertical
from textual.widgets import Placeholder, Label, Static
class Ruler(Static):
def compose(self):
ruler_text = "·\n·\n·\n·\n\n" * 100
yield Label(ruler_text)
class HeightComparisonApp(App):
def compose(self):
yield Vertical(
Placeholder(id="cells"), # (1)!
Placeholder(id="percent"),
Placeholder(id="w"),
Placeholder(id="h"),
Placeholder(id="vw"),
Placeholder(id="vh"),
Placeholder(id="auto"),
Placeholder(id="fr1"),
Placeholder(id="fr2"),
)
yield Ruler()
app = HeightComparisonApp(css_path="height_comparison.css")

View File

@@ -10,7 +10,7 @@
height: auto;
}
Static {
Label {
margin: 1;
width: 12;
color: black;

View File

@@ -1,20 +1,20 @@
from textual.app import App
from textual.containers import Container
from textual.widgets import Static
from textual.widgets import Label
class LayoutApp(App):
def compose(self):
yield Container(
Static("Layout"),
Static("Is"),
Static("Vertical"),
Label("Layout"),
Label("Is"),
Label("Vertical"),
id="vertical-layout",
)
yield Container(
Static("Layout"),
Static("Is"),
Static("Horizontal"),
Label("Layout"),
Label("Is"),
Label("Horizontal"),
id="horizontal-layout",
)

View File

@@ -0,0 +1,11 @@
#lbl1, #lbl2 {
link-background: red; /* (1)! */
}
#lbl3 {
link-background: hsl(60,100%,50%) 50%;
}
#lbl4 {
link-background: $accent;
}

View File

@@ -0,0 +1,25 @@
from textual.app import App
from textual.widgets import Label
class LinkBackgroundApp(App):
def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
"Click [@click=app.bell]here[/] for the bell sound.",
id="lbl2", # (2)!
)
yield Label(
"You can also click [@click=app.bell]here[/] for the bell sound.",
id="lbl3", # (3)!
)
yield Label(
"[@click=app.quit]Exit this application.[/]",
id="lbl4", # (4)!
)
app = LinkBackgroundApp(css_path="link_background.css")

View File

@@ -0,0 +1,11 @@
#lbl1, #lbl2 {
link-color: red; /* (1)! */
}
#lbl3 {
link-color: hsl(60,100%,50%) 50%;
}
#lbl4 {
link-color: $accent;
}

View File

@@ -0,0 +1,25 @@
from textual.app import App
from textual.widgets import Label
class LinkColorApp(App):
def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
"Click [@click=app.bell]here[/] for the bell sound.",
id="lbl2", # (2)!
)
yield Label(
"You can also click [@click=app.bell]here[/] for the bell sound.",
id="lbl3", # (3)!
)
yield Label(
"[@click=app.quit]Exit this application.[/]",
id="lbl4", # (4)!
)
app = LinkColorApp(css_path="link_color.css")

View File

@@ -0,0 +1,11 @@
#lbl1, #lbl2 {
link-hover-background: red; /* (1)! */
}
#lbl3 {
link-hover-background: hsl(60,100%,50%) 50%;
}
#lbl4 {
/* Empty to show the default hover background */ /* (2)! */
}

View File

@@ -0,0 +1,25 @@
from textual.app import App
from textual.widgets import Label
class LinkHoverBackgroundApp(App):
def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
"Click [@click=app.bell]here[/] for the bell sound.",
id="lbl2", # (2)!
)
yield Label(
"You can also click [@click=app.bell]here[/] for the bell sound.",
id="lbl3", # (3)!
)
yield Label(
"[@click=app.quit]Exit this application.[/]",
id="lbl4", # (4)!
)
app = LinkHoverBackgroundApp(css_path="link_hover_background.css")

View File

@@ -0,0 +1,11 @@
#lbl1, #lbl2 {
link-hover-color: red; /* (1)! */
}
#lbl3 {
link-hover-color: hsl(60,100%,50%) 50%;
}
#lbl4 {
link-hover-color: black;
}

View File

@@ -0,0 +1,25 @@
from textual.app import App
from textual.widgets import Label
class LinkHoverColorApp(App):
def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
"Click [@click=app.bell]here[/] for the bell sound.",
id="lbl2", # (2)!
)
yield Label(
"You can also click [@click=app.bell]here[/] for the bell sound.",
id="lbl3", # (3)!
)
yield Label(
"[@click=app.quit]Exit this application.[/]",
id="lbl4", # (4)!
)
app = LinkHoverColorApp(css_path="link_hover_color.css")

View File

@@ -0,0 +1,11 @@
#lbl1, #lbl2 {
link-hover-style: bold italic; /* (1)! */
}
#lbl3 {
link-hover-style: reverse strike;
}
#lbl4 {
link-hover-style: bold;
}

View File

@@ -0,0 +1,25 @@
from textual.app import App
from textual.widgets import Label
class LinkHoverStyleApp(App):
def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
"Click [@click=app.bell]here[/] for the bell sound.",
id="lbl2", # (2)!
)
yield Label(
"You can also click [@click=app.bell]here[/] for the bell sound.",
id="lbl3", # (3)!
)
yield Label(
"[@click=app.quit]Exit this application.[/]",
id="lbl4", # (4)!
)
app = LinkHoverStyleApp(css_path="link_hover_style.css")

View File

@@ -0,0 +1,11 @@
#lbl1, #lbl2 {
link-style: bold italic; /* (1)! */
}
#lbl3 {
link-style: reverse strike;
}
#lbl4 {
link-style: bold;
}

View File

@@ -0,0 +1,25 @@
from textual.app import App
from textual.widgets import Label
class LinkStyleApp(App):
def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
"Click [@click=app.bell]here[/] for the bell sound.",
id="lbl2", # (2)!
)
yield Label(
"You can also click [@click=app.bell]here[/] for the bell sound.",
id="lbl3", # (3)!
)
yield Label(
"[@click=app.quit]Exit this application.[/]",
id="lbl4", # (4)!
)
app = LinkStyleApp(css_path="link_style.css")

View File

@@ -3,8 +3,9 @@ Screen {
color: black;
}
Static {
margin: 4 8;
background: blue 20%;
Label {
margin: 4 8;
background: blue 20%;
border: blue wide;
}
width: 100%;
}

View File

@@ -1,5 +1,5 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
TEXT = """I must not fear.
Fear is the mind-killer.
@@ -12,7 +12,7 @@ Where the fear has gone there will be nothing. Only I will remain."""
class MarginApp(App):
def compose(self):
yield Static(TEXT)
yield Label(TEXT)
app = MarginApp(css_path="margin.css")

View File

@@ -0,0 +1,54 @@
Screen {
background: $background;
}
Grid {
grid-size: 4;
grid-gutter: 1 2;
}
Placeholder {
width: 100%;
height: 100%;
}
Container {
width: 100%;
height: 100%;
}
.bordered {
border: white round;
}
#p1 {
/* default is no margin */
}
#p2 {
margin: 1;
}
#p3 {
margin: 1 5;
}
#p4 {
margin: 1 1 2 6;
}
#p5 {
margin-top: 4;
}
#p6 {
margin-right: 3;
}
#p7 {
margin-bottom: 4;
}
#p8 {
margin-left: 3;
}

View File

@@ -0,0 +1,20 @@
from textual.app import App
from textual.containers import Container, Grid
from textual.widgets import Placeholder
class MarginAllApp(App):
def compose(self):
yield Grid(
Container(Placeholder("no margin", id="p1"), classes="bordered"),
Container(Placeholder("margin: 1", id="p2"), classes="bordered"),
Container(Placeholder("margin: 1 5", id="p3"), classes="bordered"),
Container(Placeholder("margin: 1 1 2 6", id="p4"), classes="bordered"),
Container(Placeholder("margin-top: 4", id="p5"), classes="bordered"),
Container(Placeholder("margin-right: 3", id="p6"), classes="bordered"),
Container(Placeholder("margin-bottom: 4", id="p7"), classes="bordered"),
Container(Placeholder("margin-left: 3", id="p8"), classes="bordered"),
)
app = MarginAllApp(css_path="margin_all.css")

View File

@@ -0,0 +1,25 @@
Horizontal {
height: 100%;
width: 100%;
}
Placeholder {
height: 100%;
width: 1fr;
}
#p1 {
max-height: 10w;
}
#p2 {
max-height: 999; /* (1)! */
}
#p3 {
max-height: 50%;
}
#p4 {
max-height: 10;
}

View File

@@ -0,0 +1,16 @@
from textual.app import App
from textual.containers import Horizontal
from textual.widgets import Placeholder
class MaxHeightApp(App):
def compose(self):
yield Horizontal(
Placeholder("max-height: 10w", id="p1"),
Placeholder("max-height: 999", id="p2"),
Placeholder("max-height: 50%", id="p3"),
Placeholder("max-height: 10", id="p4"),
)
app = MaxHeightApp(css_path="max_height.css")

View File

@@ -0,0 +1,25 @@
Horizontal {
height: 100%;
width: 100%;
}
Placeholder {
width: 100%;
height: 1fr;
}
#p1 {
max-width: 50h;
}
#p2 {
max-width: 999; /* (1)! */
}
#p3 {
max-width: 50%;
}
#p4 {
max-width: 30;
}

View File

@@ -0,0 +1,16 @@
from textual.app import App
from textual.containers import Vertical
from textual.widgets import Placeholder
class MaxWidthApp(App):
def compose(self):
yield Vertical(
Placeholder("max-width: 50h", id="p1"),
Placeholder("max-width: 999", id="p2"),
Placeholder("max-width: 50%", id="p3"),
Placeholder("max-width: 30", id="p4"),
)
app = MaxWidthApp(css_path="max_width.css")

View File

@@ -0,0 +1,26 @@
Horizontal {
height: 100%;
width: 100%;
overflow-y: auto;
}
Placeholder {
width: 1fr;
height: 50%;
}
#p1 {
min-height: 25%; /* (1)! */
}
#p2 {
min-height: 75%;
}
#p3 {
min-height: 30;
}
#p4 {
min-height: 40w;
}

View File

@@ -0,0 +1,16 @@
from textual.app import App
from textual.containers import Horizontal
from textual.widgets import Placeholder
class MinHeightApp(App):
def compose(self):
yield Horizontal(
Placeholder("min-height: 25%", id="p1"),
Placeholder("min-height: 75%", id="p2"),
Placeholder("min-height: 30", id="p3"),
Placeholder("min-height: 40w", id="p4"),
)
app = MinHeightApp(css_path="min_height.css")

View File

@@ -0,0 +1,26 @@
Vertical {
height: 100%;
width: 100%;
overflow-x: auto;
}
Placeholder {
height: 1fr;
width: 50%;
}
#p1 {
min-width: 25%; /* (1)! */
}
#p2 {
min-width: 75%;
}
#p3 {
min-width: 100;
}
#p4 {
min-width: 400h;
}

View File

@@ -0,0 +1,16 @@
from textual.app import App
from textual.containers import Vertical
from textual.widgets import Placeholder
class MinWidthApp(App):
def compose(self):
yield Vertical(
Placeholder("min-width: 25%", id="p1"),
Placeholder("min-width: 75%", id="p2"),
Placeholder("min-width: 100", id="p3"),
Placeholder("min-width: 400h", id="p4"),
)
app = MinWidthApp(css_path="min_width.css")

View File

@@ -3,10 +3,10 @@ Screen {
color: black;
layout: horizontal;
}
Static {
Label {
width: 20;
height: 10;
content-align: center middle;
content-align: center middle;
}
.paul {
@@ -24,7 +24,7 @@ Static {
}
.chani {
offset: 0 5;
offset: 0 -3;
background: blue 20%;
border: outer blue;
color: blue;

View File

@@ -1,12 +1,12 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
class OffsetApp(App):
def compose(self):
yield Static("Paul (offset 8 2)", classes="paul")
yield Static("Duncan (offset 4 10)", classes="duncan")
yield Static("Chani (offset 0 5)", classes="chani")
yield Label("Paul (offset 8 2)", classes="paul")
yield Label("Duncan (offset 4 10)", classes="duncan")
yield Label("Chani (offset 0 -3)", classes="chani")
app = OffsetApp(css_path="offset.css")

View File

@@ -19,10 +19,11 @@
}
Screen {
background: antiquewhite;
background: black;
}
Static {
Label {
width: 100%;
height: 1fr;
border: outer dodgerblue;
background: lightseagreen;

View File

@@ -1,14 +1,14 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
class OpacityApp(App):
def compose(self):
yield Static("opacity: 0%", id="zero-opacity")
yield Static("opacity: 25%", id="quarter-opacity")
yield Static("opacity: 50%", id="half-opacity")
yield Static("opacity: 75%", id="three-quarter-opacity")
yield Static("opacity: 100%", id="full-opacity")
yield Label("opacity: 0%", id="zero-opacity")
yield Label("opacity: 25%", id="quarter-opacity")
yield Label("opacity: 50%", id="half-opacity")
yield Label("opacity: 75%", id="three-quarter-opacity")
yield Label("opacity: 100%", id="full-opacity")
app = OpacityApp(css_path="opacity.css")

View File

@@ -2,8 +2,10 @@ Screen {
background: white;
color: black;
}
Static {
Label {
margin: 4 8;
background: green 20%;
outline: wide green;
width: 100%;
}

View File

@@ -1,5 +1,5 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
TEXT = """I must not fear.
@@ -13,7 +13,7 @@ Where the fear has gone there will be nothing. Only I will remain."""
class OutlineApp(App):
def compose(self):
yield Static(TEXT)
yield Label(TEXT)
app = OutlineApp(css_path="outline.css")

View File

@@ -0,0 +1,71 @@
#ascii {
outline: ascii $accent;
}
#blank {
outline: blank $accent;
}
#dashed {
outline: dashed $accent;
}
#double {
outline: double $accent;
}
#heavy {
outline: heavy $accent;
}
#hidden {
outline: hidden $accent;
}
#hkey {
outline: hkey $accent;
}
#inner {
outline: inner $accent;
}
#none {
outline: none $accent;
}
#outer {
outline: outer $accent;
}
#round {
outline: round $accent;
}
#solid {
outline: solid $accent;
}
#tall {
outline: tall $accent;
}
#vkey {
outline: vkey $accent;
}
#wide {
outline: wide $accent;
}
Grid {
grid-size: 3 5;
align: center middle;
grid-gutter: 1 2;
}
Label {
width: 20;
height: 3;
content-align: center middle;
}

View File

@@ -0,0 +1,26 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Label
class AllOutlinesApp(App):
def compose(self):
yield Grid(
Label("ascii", id="ascii"),
Label("blank", id="blank"),
Label("dashed", id="dashed"),
Label("double", id="double"),
Label("heavy", id="heavy"),
Label("hidden/none", id="hidden"),
Label("hkey", id="hkey"),
Label("inner", id="inner"),
Label("none", id="none"),
Label("outer", id="outer"),
Label("round", id="round"),
Label("solid", id="solid"),
Label("tall", id="tall"),
Label("vkey", id="vkey"),
Label("wide", id="wide"),
)
app = AllOutlinesApp(css_path="outline_all.css")

View File

@@ -0,0 +1,11 @@
Label {
height: 8;
}
.outline {
outline: $error round;
}
.border {
border: $success heavy;
}

View File

@@ -0,0 +1,21 @@
from textual.app import App
from textual.widgets import Label
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 OutlineBorderApp(App):
def compose(self):
yield Label(TEXT, classes="outline")
yield Label(TEXT, classes="border")
yield Label(TEXT, classes="outline border")
app = OutlineBorderApp(css_path="outline_vs_border.css")

View File

@@ -8,8 +8,8 @@ Vertical {
}
Static {
margin: 1 2;
background: green 80%;
margin: 1 2;
background: green 80%;
border: green wide;
color: white 90%;
height: auto;

View File

@@ -3,7 +3,8 @@ Screen {
color: blue;
}
Static {
padding: 4 8;
background: blue 20%;
}
Label {
padding: 4 8;
background: blue 20%;
width: 100%;
}

View File

@@ -1,5 +1,5 @@
from textual.app import App
from textual.widgets import Static
from textual.widgets import Label
TEXT = """I must not fear.
Fear is the mind-killer.
@@ -12,7 +12,7 @@ Where the fear has gone there will be nothing. Only I will remain."""
class PaddingApp(App):
def compose(self):
yield Static(TEXT)
yield Label(TEXT)
app = PaddingApp(css_path="padding.css")

View File

@@ -0,0 +1,45 @@
Screen {
background: $background;
}
Grid {
grid-size: 4;
grid-gutter: 1 2;
}
Placeholder {
width: auto;
height: auto;
}
#p1 {
/* default is no padding */
}
#p2 {
padding: 1;
}
#p3 {
padding: 1 5;
}
#p4 {
padding: 1 1 2 6;
}
#p5 {
padding-top: 4;
}
#p6 {
padding-right: 3;
}
#p7 {
padding-bottom: 4;
}
#p8 {
padding-left: 3;
}

View File

@@ -0,0 +1,20 @@
from textual.app import App
from textual.containers import Container, Grid
from textual.widgets import Placeholder
class PaddingAllApp(App):
def compose(self):
yield Grid(
Placeholder("no padding", id="p1"),
Placeholder("padding: 1", id="p2"),
Placeholder("padding: 1 5", id="p3"),
Placeholder("padding: 1 1 2 6", id="p4"),
Placeholder("padding-top: 4", id="p5"),
Placeholder("padding-right: 3", id="p6"),
Placeholder("padding-bottom: 4", id="p7"),
Placeholder("padding-left: 3", id="p8"),
)
app = PaddingAllApp(css_path="padding_all.css")

View File

@@ -0,0 +1,30 @@
#p1 {
row-span: 4;
}
#p2 {
row-span: 3;
}
#p3 {
row-span: 2;
}
#p4 {
row-span: 1; /* Didn't need to be set explicitly. */
}
#p5 {
row-span: 3;
}
#p6 {
row-span: 2;
}
#p7 {
/* Default value is 1. */
}
Grid {
grid-size: 4 4;
grid-gutter: 1 2;
}
Placeholder {
height: 100%;
}

View File

@@ -0,0 +1,19 @@
from textual.app import App
from textual.containers import Grid
from textual.widgets import Placeholder
class MyApp(App):
def compose(self):
yield Grid(
Placeholder(id="p1"),
Placeholder(id="p2"),
Placeholder(id="p3"),
Placeholder(id="p4"),
Placeholder(id="p5"),
Placeholder(id="p6"),
Placeholder(id="p7"),
)
app = MyApp(css_path="row_span.css")

View File

@@ -0,0 +1,4 @@
Screen {
overflow: auto auto;
scrollbar-corner-color: white;
}

Some files were not shown because too many files have changed in this diff Show More