Border style (#2292)

* border styles

* docs for border styles

* fix tests

* tests

* tests and docs

* changelog

* implement auto

* style information fix
This commit is contained in:
Will McGugan
2023-04-16 12:31:39 +01:00
committed by GitHub
parent 9fb63f9b53
commit 0509cf8948
27 changed files with 1807 additions and 1247 deletions

View File

@@ -20,6 +20,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- option `--port` to the command `textual console` to specify which port the console should connect to https://github.com/Textualize/textual/pull/2258 - option `--port` to the command `textual console` to specify which port the console should connect to https://github.com/Textualize/textual/pull/2258
- `Widget.scroll_to_center` method to scroll children to the center of container widget https://github.com/Textualize/textual/pull/2255 and https://github.com/Textualize/textual/pull/2276 - `Widget.scroll_to_center` method to scroll children to the center of container widget https://github.com/Textualize/textual/pull/2255 and https://github.com/Textualize/textual/pull/2276
- Added `TabActivated` message to `TabbedContent` https://github.com/Textualize/textual/pull/2260 - Added `TabActivated` message to `TabbedContent` https://github.com/Textualize/textual/pull/2260
- Added "panel" border style https://github.com/Textualize/textual/pull/2292
- Added `border-title-color`, `border-title-background`, `border-title-style` rules https://github.com/Textualize/textual/issues/2289
- Added `border-subtitle-color`, `border-subtitle-background`, `border-subtitle-style` rules https://github.com/Textualize/textual/issues/2289
### Fixed ### Fixed

View File

@@ -0,0 +1,16 @@
Screen {
align: center middle;
}
Label {
padding: 4 8;
border: heavy red;
border-title-color: green;
border-title-background: white;
border-title-style: bold;
border-subtitle-color: magenta;
border-subtitle-background: yellow;
border-subtitle-style: italic;
}

View File

@@ -0,0 +1,19 @@
from textual.app import App, ComposeResult
from textual.widgets import Label
class BorderTitleApp(App):
CSS_PATH = "border_title_colors.css"
def compose(self) -> ComposeResult:
yield Label("Hello, World!")
def on_mount(self) -> None:
label = self.query_one(Label)
label.border_title = "Textual Rocks"
label.border_subtitle = "Textual Rocks"
if __name__ == "__main__":
app = BorderTitleApp()
app.run()

View File

@@ -0,0 +1,18 @@
The following examples demonstrates customization of the border color and text style rules.
=== "Output"
```{.textual path="docs/examples/styles/border_title_colors.py"}
```
=== "border_title_colors.py"
```python
--8<-- "docs/examples/styles/border_title_colors.py"
```
=== "border_title_colors.css"
```sass
--8<-- "docs/examples/styles/border_title_colors.css"
```

View File

@@ -0,0 +1,9 @@
- [`border-title-align`](./border_title_align.md) to set the title's alignment.
- [`border-title-color`](./border_subtitle_color.md) to set the title's color.
- [`border-title-background`](./border_subtitle_background.md) to set the title's background color.
- [`border-title-style`](./border_subtitle_style.md) to set the title's text style.
- [`border-subtitle-align`](./border_subtitle_align.md) to set the sub-title's alignment.
- [`border-subtitle-color`](./border_subtitle_color.md) to set the sub-title's color.
- [`border-subtitle-background`](./border_subtitle_background.md) to set the sub-title's background color.
- [`border-subtitle-style`](./border_subtitle_style.md) to set the sub-title's text style.

View File

@@ -13,12 +13,7 @@ align-vertical: <a href="../../css_types/vertical">&lt;vertical&gt;</a>;
The `align` style takes a [`<horizontal>`](../../css_types/horizontal) followed by a [`<vertical>`](../../css_types/vertical). The `align` style takes a [`<horizontal>`](../../css_types/horizontal) followed by a [`<vertical>`](../../css_types/vertical).
You can specify the alignment of children on both the horizontal and vertical axes at the same time, You can also set the alignment for each axis individually with `align-horizontal` and `align-vertical`.
or on each of the axis separately.
To specify alignment on a single axis, use the respective style and type:
- `align-horizontal` takes a [`<horizontal>`](../../css_types/horizontal) and does alignment along the horizontal axis; and
- `align-vertical` takes a [`<vertical>`](../../css_types/vertical) and does alignment along the vertical axis.
## Examples ## Examples

View File

@@ -2,9 +2,11 @@
The `border` style enables the drawing of a box around a widget. The `border` style enables the drawing of a box around a widget.
A border style may also be applied to individual edges with `border-top`, `border-right`, `border-bottom`, and `border-left`.
!!! note !!! note
Due to a Textual limitation, [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget. [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget.
## Syntax ## Syntax
@@ -17,31 +19,10 @@ border-bottom: [<a href="../../css_types/border">&lt;border&gt;</a>] [<a href=".
border-left: [<a href="../../css_types/border">&lt;border&gt;</a>] [<a href="../../css_types/color">&lt;color&gt;</a> [<a href="../../css_types/percentage">&lt;percentage&gt;</a>]]; border-left: [<a href="../../css_types/border">&lt;border&gt;</a>] [<a href="../../css_types/color">&lt;color&gt;</a> [<a href="../../css_types/percentage">&lt;percentage&gt;</a>]];
--8<-- "docs/snippets/syntax_block_end.md" --8<-- "docs/snippets/syntax_block_end.md"
The `border` style accepts an optional [`<border>`](../../css_types/border) that sets the visual style of the widget border, an optional [`<color>`](../../css_types/color) to set the color of the border, and an optional [`<percentage>`](../../css_types/percentage) to specify the color transparency. In CSS, the border is set with a [border style](./border.md) and a color. Both are optional. An optional percentage may be added to blend the border with the background color.
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. In Python, the border is set with a tuple of [border style](./border.md) and a color.
### Multiple edge rules
If multiple border styles target the same edge, the last style that targets a specific edge is the one that is applied to that edge.
For example, consider the CSS below:
```sass
Static {
border-top: dashed red;
border: solid green; /* overrides the border-top rule above */
/* Change the border but just for the bottom edge: */
border-bottom: double blue;
}
```
The CSS snippet above will add a solid green border around `Static` widgets, except for the bottom edge, which will be double blue.
### Defaults
If `<color>` is specified but `<border>` is not, it defaults to `"solid"`.
If `<border>` is specified but `<color>`is not, it defaults to green (RGB color `"#00FF00"`).
If `<percentage>` is not specified it defaults to `100%`.
## Border command ## Border command
@@ -128,3 +109,4 @@ widget.styles.border_left = ("outer", "red")
- [`box-sizing`](./box_sizing.md) to specify how to account for the border in a widget's dimensions. - [`box-sizing`](./box_sizing.md) to specify how to account for the border in a widget's dimensions.
- [`outline`](./outline.md) to add an outline around the content of a widget. - [`outline`](./outline.md) to add an outline around the content of a widget.
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -60,3 +60,7 @@ widget.styles.border_subtitle_align = "left"
widget.styles.border_subtitle_align = "center" widget.styles.border_subtitle_align = "center"
widget.styles.border_subtitle_align = "right" widget.styles.border_subtitle_align = "right"
``` ```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -0,0 +1,34 @@
# Border-subtitle-background
The `border-subtitle-background` style sets the *background* color of the [border_subtitle][textual.widget.Widget.border_subtitle].
## Syntax
--8<-- "docs/snippets/syntax_block_start.md"
border-subtitle-background: (<a href="../../css_types/color">&lt;color&gt;</a> | auto) [<a href="../../css_types/percentage">&lt;percentage&gt;</a>];
--8<-- "docs/snippets/syntax_block_end.md"
## Example
--8<-- "docs/snippets/border_title_color.md"
## CSS
```sass
border-subtitle-background: blue;
```
## Python
```python
widget.styles.border_subtitle_background = "blue"
```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -0,0 +1,33 @@
# Border-subtitle-color
The `border-subtitle-color` style sets the color of the [border_subtitle][textual.widget.Widget.border_subtitle].
## Syntax
--8<-- "docs/snippets/syntax_block_start.md"
border-subtitle-color: (<a href="../../css_types/color">&lt;color&gt;</a> | auto) [<a href="../../css_types/percentage">&lt;percentage&gt;</a>];
--8<-- "docs/snippets/syntax_block_end.md"
## Example
--8<-- "docs/snippets/border_title_color.md"
## CSS
```sass
border-subtitle-color: red;
```
## Python
```python
widget.styles.border_subtitle_color = "red"
```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -0,0 +1,35 @@
# Border-subtitle-style
The `border-subtitle-style` style sets the text style of the [border_subtitle][textual.widget.Widget.border_subtitle].
## Syntax
--8<-- "docs/snippets/syntax_block_start.md"
border-subtitle-style: <a href="../../css_types/text_style">&lt;text-style&gt;</a>;
--8<-- "docs/snippets/syntax_block_end.md"
## Example
--8<-- "docs/snippets/border_title_color.md"
## CSS
```sass
border-subtitle-style: bold underline;
```
## Python
```python
widget.styles.border_subtitle_style = "bold underline"
```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -60,3 +60,7 @@ widget.styles.border_title_align = "left"
widget.styles.border_title_align = "center" widget.styles.border_title_align = "center"
widget.styles.border_title_align = "right" widget.styles.border_title_align = "right"
``` ```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -0,0 +1,33 @@
# Border-title-background
The `border-title-background` style sets the *background* color of the [border_title][textual.widget.Widget.border_title].
## Syntax
--8<-- "docs/snippets/syntax_block_start.md"
border-title-background: (<a href="../../css_types/color">&lt;color&gt;</a> | auto) [<a href="../../css_types/percentage">&lt;percentage&gt;</a>];
--8<-- "docs/snippets/syntax_block_end.md"
## Example
--8<-- "docs/snippets/border_title_color.md"
## CSS
```sass
border-title-background: blue;
```
## Python
```python
widget.styles.border_title_background = "blue"
```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -0,0 +1,31 @@
# Border-title-color
The `border-title-color` style sets the color of the [border_title][textual.widget.Widget.border_title].
## Syntax
--8<-- "docs/snippets/syntax_block_start.md"
border-title-color: (<a href="../../css_types/color">&lt;color&gt;</a> | auto) [<a href="../../css_types/percentage">&lt;percentage&gt;</a>];
--8<-- "docs/snippets/syntax_block_end.md"
## Example
--8<-- "docs/snippets/border_title_color.md"
## CSS
```sass
border-title-color: red;
```
## Python
```python
widget.styles.border_title_color = "red"
```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -0,0 +1,35 @@
# Border-title-style
The `border-title-style` style sets the text style of the [border_title][textual.widget.Widget.border_title].
## Syntax
--8<-- "docs/snippets/syntax_block_start.md"
border-title-style: <a href="../../css_types/text_style">&lt;text-style&gt;</a>;
--8<-- "docs/snippets/syntax_block_end.md"
## Example
--8<-- "docs/snippets/border_title_color.md"
## CSS
```sass
border-title-style: bold underline;
```
## Python
```python
widget.styles.border_title_style = "bold underline"
```
## See also
--8<-- "docs/snippets/see_also_border.md"

View File

@@ -4,7 +4,7 @@ The `outline` style enables the drawing of a box around the content of a widget,
!!! note !!! note
Due to a Textual limitation, [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget. [`border`](./border.md) and [`outline`](./outline.md) cannot coexist in the same edge of a widget.
## Syntax ## Syntax

View File

@@ -1,6 +1,6 @@
# Text-style # Text-style
The `text-style` sets the style for the text in a widget. The `text-style` style sets the style for the text in a widget.
## Syntax ## Syntax

View File

@@ -64,17 +64,23 @@ nav:
- "events/screen_suspend.md" - "events/screen_suspend.md"
- "events/show.md" - "events/show.md"
- Styles: - Styles:
- "styles/index.md"
- "styles/align.md" - "styles/align.md"
- "styles/background.md" - "styles/background.md"
- "styles/border.md"
- "styles/border_subtitle_align.md" - "styles/border_subtitle_align.md"
- "styles/border_subtitle_background.md"
- "styles/border_subtitle_color.md"
- "styles/border_subtitle_style.md"
- "styles/border_title_align.md" - "styles/border_title_align.md"
- "styles/border_title_background.md"
- "styles/border_title_color.md"
- "styles/border_title_style.md"
- "styles/border.md"
- "styles/box_sizing.md" - "styles/box_sizing.md"
- "styles/color.md" - "styles/color.md"
- "styles/content_align.md" - "styles/content_align.md"
- "styles/display.md" - "styles/display.md"
- "styles/dock.md" - "styles/dock.md"
- "styles/index.md"
- Grid: - Grid:
- "styles/grid/index.md" - "styles/grid/index.md"
- "styles/grid/column_span.md" - "styles/grid/column_span.md"

View File

@@ -102,9 +102,14 @@ BORDER_CHARS: dict[
("", " ", ""), ("", " ", ""),
("", "", ""), ("", "", ""),
), ),
"panel": (
("", "", ""),
("", " ", ""),
("", "", ""),
),
"wide": ( "wide": (
("", "", ""), ("", "", ""),
("", " ", ""), ("", " ", ""),
("", "", ""), ("", "", ""),
), ),
} }
@@ -195,6 +200,11 @@ BORDER_LOCATIONS: dict[
(2, 0, 1), (2, 0, 1),
(2, 0, 1), (2, 0, 1),
), ),
"panel": (
(2, 0, 1),
(2, 0, 1),
(2, 0, 1),
),
"wide": ( "wide": (
(1, 1, 1), (1, 1, 1),
(0, 1, 3), (0, 1, 3),
@@ -202,6 +212,11 @@ BORDER_LOCATIONS: dict[
), ),
} }
# Some borders (such as panel) require that the title (and subtitle) be draw in reverse.
# This is a mapping of the border type on to a tuple for the top and bottom borders, to indicate
# reverse colors is required.
BORDER_TITLE_FLIP: dict[str, tuple[bool, bool]] = {"panel": (True, False)}
# In a similar fashion, we extract the border _label_ locations for easier access when # In a similar fashion, we extract the border _label_ locations for easier access when
# rendering a border label. # rendering a border label.
# The values are a pair with (title location, subtitle location). # The values are a pair with (title location, subtitle location).
@@ -283,7 +298,7 @@ def get_box(
def render_border_label( def render_border_label(
label: Text, label: tuple[Text, Style],
is_title: bool, is_title: bool,
name: EdgeType, name: EdgeType,
width: int, width: int,
@@ -300,7 +315,7 @@ def render_border_label(
account the inner, outer, and border-specific, styles. account the inner, outer, and border-specific, styles.
Args: Args:
label: The label to display (that may contain markup). label: Tuple of label and style to render in the border.
is_title: Whether we are rendering the title (`True`) or the subtitle (`False`). is_title: Whether we are rendering the title (`True`) or the subtitle (`False`).
name: Name of the box type. name: Name of the box type.
width: The width, in cells, of the space available for the whole edge. width: The width, in cells, of the space available for the whole edge.
@@ -323,14 +338,21 @@ def render_border_label(
# How many cells do we need to reserve for surrounding blanks and corners? # How many cells do we need to reserve for surrounding blanks and corners?
corners_needed = has_left_corner + has_right_corner corners_needed = has_left_corner + has_right_corner
cells_reserved = 2 * corners_needed cells_reserved = 2 * corners_needed
if not label.cell_len or width <= cells_reserved:
text_label, label_style = label
if not text_label.cell_len or width <= cells_reserved:
return return
text_label = label.copy() text_label = text_label.copy()
text_label.truncate(width - cells_reserved, overflow="ellipsis") text_label.truncate(width - cells_reserved, overflow="ellipsis")
segments = text_label.render(console) if has_left_corner:
text_label.pad_left(1)
if has_right_corner:
text_label.pad_right(1)
text_label.stylize_before(label_style)
label_style_location = BORDER_LABEL_LOCATIONS[name][0 if is_title else 1] label_style_location = BORDER_LABEL_LOCATIONS[name][0 if is_title else 1]
flip_top, flip_bottom = BORDER_TITLE_FLIP.get(name, (False, False))
inner = inner_style + style inner = inner_style + style
outer = outer_style + style outer = outer_style + style
@@ -347,15 +369,14 @@ def render_border_label(
else: else:
assert False assert False
styled_segments = [ if (flip_top and is_title) or (flip_bottom and not is_title):
Segment(segment.text, base_style + segment.style) for segment in segments base_style = base_style.without_color + Style.from_color(
] base_style.bgcolor, base_style.color
blank = Segment(" ", base_style) )
if has_left_corner:
yield blank text_label.stylize_before(base_style + label_style)
yield from styled_segments segments = text_label.render(console)
if has_right_corner: yield from segments
yield blank
def render_row( def render_row(

View File

@@ -107,6 +107,10 @@ class StylesCache:
Returns: Returns:
Rendered lines. Rendered lines.
""" """
border_title = widget._border_title
border_subtitle = widget._border_subtitle
base_background, background = widget.background_colors base_background, background = widget.background_colors
styles = widget.styles styles = widget.styles
strips = self.render( strips = self.render(
@@ -116,8 +120,22 @@ class StylesCache:
background, background,
widget.render_line, widget.render_line,
widget.app.console, widget.app.console,
widget._border_title, (
widget._border_subtitle, None
if border_title is None
else (
border_title,
*widget._get_title_style_information(base_background),
)
),
(
None
if border_subtitle is None
else (
border_subtitle,
*widget._get_subtitle_style_information(base_background),
)
),
content_size=widget.content_region.size, content_size=widget.content_region.size,
padding=styles.padding, padding=styles.padding,
crop=crop, crop=crop,
@@ -147,8 +165,8 @@ class StylesCache:
background: Color, background: Color,
render_content_line: RenderLineCallback, render_content_line: RenderLineCallback,
console: Console, console: Console,
border_title: Text | None, border_title: tuple[Text, Color, Color, Style] | None,
border_subtitle: Text | None, border_subtitle: tuple[Text, Color, Color, Style] | None,
content_size: Size | None = None, content_size: Size | None = None,
padding: Spacing | None = None, padding: Spacing | None = None,
crop: Region | None = None, crop: Region | None = None,
@@ -163,8 +181,8 @@ class StylesCache:
background: Background color of widget. background: Background color of widget.
render_content_line: Callback to render content line. render_content_line: Callback to render content line.
console: The console in use by the app. console: The console in use by the app.
border_title: The title for the widget border. border_title: Optional tuple of (title, color, background, style).
border_subtitle: The subtitle for the widget border. border_subtitle: Optional tuple of (subtitle, color, background, style).
content_size: Size of content or None to assume full size. content_size: Size of content or None to assume full size.
padding: Override padding from Styles, or None to use styles.padding. padding: Override padding from Styles, or None to use styles.padding.
crop: Region to crop to. crop: Region to crop to.
@@ -229,8 +247,8 @@ class StylesCache:
background: Color, background: Color,
render_content_line: Callable[[int], Strip], render_content_line: Callable[[int], Strip],
console: Console, console: Console,
border_title: Text | None, border_title: tuple[Text, Color, Color, Style] | None,
border_subtitle: Text | None, border_subtitle: tuple[Text, Color, Color, Style] | None,
) -> Strip: ) -> Strip:
"""Render a styled line. """Render a styled line.
@@ -244,8 +262,8 @@ class StylesCache:
background: Background color of widget. background: Background color of widget.
render_content_line: Callback to render a line of content. render_content_line: Callback to render a line of content.
console: The console in use by the app. console: The console in use by the app.
border_title: The title for the widget border. border_title: Optional tuple of (title, color, background, style).
border_subtitle: The subtitle for the widget border. border_subtitle: Optional tuple of (subtitle, color, background, style).
Returns: Returns:
A line of segments. A line of segments.
@@ -305,10 +323,26 @@ class StylesCache:
has_left = border_left != "" has_left = border_left != ""
has_right = border_right != "" has_right = border_right != ""
border_label = border_title if is_top else border_subtitle border_label = border_title if is_top else border_subtitle
if border_label is None:
render_label = None
else:
label, label_color, label_background, style = border_label
base_label_background = base_background + background
style += Style.from_color(
(
(base_label_background + label_color).rich_color
if label_color.a
else None
),
(base_label_background + label_background).rich_color
if label_background.a
else None,
)
render_label = (label, style)
# Try to save time with expensive call to `render_border_label`: # Try to save time with expensive call to `render_border_label`:
if border_label: if render_label:
label_segments = render_border_label( label_segments = render_border_label(
border_label, render_label,
is_top, is_top,
border_edge_type, border_edge_type,
width - 2, width - 2,

View File

@@ -640,6 +640,11 @@ class StylesBuilder:
process_link_hover_color = process_color process_link_hover_color = process_color
process_link_hover_background = process_color process_link_hover_background = process_color
process_border_title_color = process_color
process_border_title_background = process_color
process_border_subtitle_color = process_color
process_border_subtitle_background = process_color
def process_text_style(self, name: str, tokens: list[Token]) -> None: def process_text_style(self, name: str, tokens: list[Token]) -> None:
for token in tokens: for token in tokens:
value = token.value value = token.value
@@ -656,6 +661,9 @@ class StylesBuilder:
process_link_style = process_text_style process_link_style = process_text_style
process_link_hover_style = process_text_style process_link_hover_style = process_text_style
process_border_title_style = process_text_style
process_border_subtitle_style = process_text_style
def process_text_align(self, name: str, tokens: list[Token]) -> None: def process_text_align(self, name: str, tokens: list[Token]) -> None:
"""Process a text-align declaration""" """Process a text-align declaration"""
if not tokens: if not tokens:

View File

@@ -167,6 +167,16 @@ class RulesMap(TypedDict, total=False):
link_hover_background: Color link_hover_background: Color
link_hover_style: Style link_hover_style: Style
auto_border_title_color: bool
border_title_color: Color
border_title_background: Color
border_title_style: Style
auto_border_subtitle_color: bool
border_subtitle_color: Color
border_subtitle_background: Color
border_subtitle_style: Style
RULE_NAMES = list(RulesMap.__annotations__.keys()) RULE_NAMES = list(RulesMap.__annotations__.keys())
RULE_NAMES_SET = frozenset(RULE_NAMES) RULE_NAMES_SET = frozenset(RULE_NAMES)
@@ -321,6 +331,16 @@ class StylesBase(ABC):
link_hover_background = ColorProperty("transparent") link_hover_background = ColorProperty("transparent")
link_hover_style = StyleFlagsProperty() link_hover_style = StyleFlagsProperty()
auto_border_title_color = BooleanProperty(default=False)
border_title_color = ColorProperty(Color(255, 255, 255, 0))
border_title_background = ColorProperty(Color(0, 0, 0, 0))
border_title_style = StyleFlagsProperty()
auto_border_subtitle_color = BooleanProperty(default=False)
border_subtitle_color = ColorProperty(Color(255, 255, 255, 0))
border_subtitle_background = ColorProperty(Color(0, 0, 0, 0))
border_subtitle_style = StyleFlagsProperty()
def __textual_animation__( def __textual_animation__(
self, self,
attribute: str, attribute: str,
@@ -990,6 +1010,22 @@ class Styles(StylesBase):
if "link_hover_style" in rules: if "link_hover_style" in rules:
append_declaration("link-hover-style", str(self.link_hover_style)) append_declaration("link-hover-style", str(self.link_hover_style))
if "border_title_color" in rules:
append_declaration("title-color", self.border_title_color.css)
if "border_title_background" in rules:
append_declaration("title-background", self.border_title_background.css)
if "border_title_style" in rules:
append_declaration("title-text-style", str(self.border_title_style))
if "border_subtitle_color" in rules:
append_declaration("subtitle-color", self.border_subtitle_color.css)
if "border_subtitle_background" in rules:
append_declaration(
"subtitle-background", self.border_subtitle_background.css
)
if "border_subtitle_text_style" in rules:
append_declaration("subtitle-text-style", str(self.border_subtitle_style))
lines.sort() lines.sort()
return lines return lines

View File

@@ -25,6 +25,7 @@ EdgeType = Literal[
"hkey", "hkey",
"vkey", "vkey",
"tall", "tall",
"panel",
"wide", "wide",
] ]
Visibility = Literal["visible", "hidden", "initial", "inherit"] Visibility = Literal["visible", "hidden", "initial", "inherit"]

View File

@@ -765,6 +765,52 @@ class DOMNode(MessagePump):
) )
return style return style
def _get_title_style_information(
self, background: Color
) -> tuple[Color, Color, Style]:
"""Get a Rich Style object for for titles.
Args:
background: The background color.
Returns:
A Rich style.
"""
styles = self.styles
if styles.auto_border_title_color:
color = background.get_contrast_text(styles.border_title_color.a)
else:
color = styles.border_title_color
return (
color,
styles.border_title_background,
styles.border_title_style,
)
def _get_subtitle_style_information(
self, background: Color
) -> tuple[Color, Color, Style]:
"""Get a Rich Style object for for titles.
Args:
background: The background color.
Returns:
A Rich style.
"""
styles = self.styles
if styles.auto_border_subtitle_color:
color = background.get_contrast_text(styles.border_subtitle_color.a)
else:
color = styles.border_subtitle_color
return (
color,
styles.border_subtitle_background,
styles.border_subtitle_style,
)
@property @property
def background_colors(self) -> tuple[Color, Color]: def background_colors(self) -> tuple[Color, Color]:
"""The background color and the color of the parent's background. """The background color and the color of the parent's background.

File diff suppressed because one or more lines are too long

View File

@@ -105,7 +105,7 @@ def test_render_border_label_empty_label_skipped(
assert [] == list( assert [] == list(
render_border_label( render_border_label(
Text(""), (Text(""), Style()),
True, True,
"round", "round",
width, width,
@@ -142,7 +142,7 @@ def test_render_border_label_skipped_if_narrow(
assert [] == list( assert [] == list(
render_border_label( render_border_label(
Text.from_markup(label), (Text.from_markup(label), Style()),
True, True,
"round", "round",
width, width,
@@ -180,11 +180,10 @@ def test_render_border_label_wide_plain(label: str):
True, True,
True, True,
) )
left, original_text, right = render_border_label(Text.from_markup(label), *args) segments = render_border_label((Text.from_markup(label), Style()), *args)
(segment,) = segments
assert left == _BLANK_SEGMENT assert segment == Segment(f" {label} ", None)
assert right == _BLANK_SEGMENT
assert original_text == Segment(label, _EMPTY_STYLE)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -200,7 +199,7 @@ def test_render_border_empty_text_with_markup(label: str):
"""Test label rendering if there is no text but some markup.""" """Test label rendering if there is no text but some markup."""
assert [] == list( assert [] == list(
render_border_label( render_border_label(
Text.from_markup(label), (Text.from_markup(label), Style()),
True, True,
"round", "round",
999, 999,
@@ -222,7 +221,7 @@ def test_render_border_label():
# Implicit test on the number of segments returned: # Implicit test on the number of segments returned:
blank1, what, is_up, with_you, blank2 = render_border_label( blank1, what, is_up, with_you, blank2 = render_border_label(
Text.from_markup(label), (Text.from_markup(label), Style()),
True, True,
"round", "round",
9999, 9999,
@@ -251,7 +250,7 @@ def test_render_border_label():
assert with_you == expected_with_you assert with_you == expected_with_you
blank1, what, blank2 = render_border_label( blank1, what, blank2 = render_border_label(
Text.from_markup(label), (Text.from_markup(label), Style()),
True, True,
"round", "round",
5 + 4, # 5 where "What…" fits + 2 for the blank spaces + 2 for the corners. 5 + 4, # 5 where "What…" fits + 2 for the blank spaces + 2 for the corners.

View File

@@ -72,8 +72,8 @@ def test_border():
Color.parse("green"), Color.parse("green"),
content.__getitem__, content.__getitem__,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
) )
@@ -106,8 +106,8 @@ def test_padding():
Color.parse("green"), Color.parse("green"),
content.__getitem__, content.__getitem__,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
) )
@@ -141,8 +141,8 @@ def test_padding_border():
Color.parse("green"), Color.parse("green"),
content.__getitem__, content.__getitem__,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
) )
@@ -177,8 +177,8 @@ def test_outline():
Color.parse("green"), Color.parse("green"),
content.__getitem__, content.__getitem__,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
) )
@@ -208,8 +208,8 @@ def test_crop():
Color.parse("green"), Color.parse("green"),
content.__getitem__, content.__getitem__,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
crop=Region(2, 2, 3, 3), crop=Region(2, 2, 3, 3),
) )
@@ -247,8 +247,8 @@ def test_dirty_cache() -> None:
Color.parse("green"), Color.parse("green"),
get_content_line, get_content_line,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
) )
assert rendered_lines == [0, 1, 2] assert rendered_lines == [0, 1, 2]
@@ -275,8 +275,8 @@ def test_dirty_cache() -> None:
Color.parse("green"), Color.parse("green"),
get_content_line, get_content_line,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
) )
assert rendered_lines == [] assert rendered_lines == []
@@ -294,8 +294,8 @@ def test_dirty_cache() -> None:
Color.parse("green"), Color.parse("green"),
get_content_line, get_content_line,
Console(), Console(),
"", None,
"", None,
content_size=Size(3, 3), content_size=Size(3, 3),
) )
assert rendered_lines == [0, 1] assert rendered_lines == [0, 1]