From 13d8d71d4e1cc4452cb5ba2894e8285a0e0b58b8 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 14 Apr 2023 18:18:26 +0100 Subject: [PATCH 1/8] border styles --- src/textual/_border.py | 38 +++++++++++++----- src/textual/_styles_cache.py | 36 ++++++++++++----- src/textual/css/_styles_builder.py | 8 ++++ src/textual/css/styles.py | 32 +++++++++++++++ src/textual/css/types.py | 1 + src/textual/dom.py | 62 ++++++++++++++++++++++++++++++ 6 files changed, 157 insertions(+), 20 deletions(-) diff --git a/src/textual/_border.py b/src/textual/_border.py index 3b3bf26704..8741b75b0c 100644 --- a/src/textual/_border.py +++ b/src/textual/_border.py @@ -102,6 +102,11 @@ ("▊", " ", "▎"), ("▊", "▁", "▎"), ), + "panel": ( + ("▊", "█", "▎"), + ("▊", " ", "▎"), + ("▊", "▁", "▎"), + ), "wide": ( ("▁", "▁", "▁"), ("▎", " ", "▋"), @@ -195,6 +200,11 @@ (2, 0, 1), (2, 0, 1), ), + "panel": ( + (2, 0, 1), + (2, 0, 1), + (2, 0, 1), + ), "wide": ( (1, 1, 1), (0, 1, 3), @@ -283,7 +293,7 @@ def get_box( def render_border_label( - label: Text, + label: tuple[Text, Style], is_title: bool, name: EdgeType, width: int, @@ -300,7 +310,7 @@ def render_border_label( account the inner, outer, and border-specific, styles. 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`). name: Name of the box type. width: The width, in cells, of the space available for the whole edge. @@ -323,12 +333,17 @@ def render_border_label( # How many cells do we need to reserve for surrounding blanks and corners? corners_needed = has_left_corner + has_right_corner 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 - text_label = label.copy() + text_label, label_style = label + text_label = text_label.copy() text_label.truncate(width - cells_reserved, overflow="ellipsis") - segments = text_label.render(console) + text_label.pad_left(1) + text_label.pad_right(1) + text_label.stylize_before(label_style) label_style_location = BORDER_LABEL_LOCATIONS[name][0 if is_title else 1] @@ -347,15 +362,18 @@ def render_border_label( else: assert False + text_label.stylize_before(base_style) + segments = text_label.render(console) + styled_segments = [ Segment(segment.text, base_style + segment.style) for segment in segments ] - blank = Segment(" ", base_style) - if has_left_corner: - yield blank + # blank = Segment(" ", base_style) + # if has_left_corner: + # yield blank yield from styled_segments - if has_right_corner: - yield blank + # if has_right_corner: + # yield blank def render_row( diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 8092f0c457..3d481d7191 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -107,6 +107,14 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: Returns: Rendered lines. """ + + border_title = widget._border_title + if border_title is not None: + border_title.stylize_before(widget.title_rich_style) + border_subtitle = widget._border_subtitle + if border_subtitle is not None: + border_subtitle.stylize_before(widget.subtitle_rich_style) + base_background, background = widget.background_colors styles = widget.styles strips = self.render( @@ -116,8 +124,16 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: background, widget.render_line, widget.app.console, - widget._border_title, - widget._border_subtitle, + ( + border_title + if border_title is None + else (border_title, widget.title_rich_style) + ), + ( + border_subtitle + if border_subtitle is None + else (border_subtitle, widget.subtitle_rich_style) + ), content_size=widget.content_region.size, padding=styles.padding, crop=crop, @@ -147,8 +163,8 @@ def render( background: Color, render_content_line: RenderLineCallback, console: Console, - border_title: Text | None, - border_subtitle: Text | None, + border_title: tuple[Text, Style] | None, + border_subtitle: tuple[Text, Style] | None, content_size: Size | None = None, padding: Spacing | None = None, crop: Region | None = None, @@ -163,8 +179,8 @@ def render( background: Background color of widget. render_content_line: Callback to render content line. console: The console in use by the app. - border_title: The title for the widget border. - border_subtitle: The subtitle for the widget border. + border_title: The title and style for the widget border. + border_subtitle: The subtitle and style for the widget border. content_size: Size of content or None to assume full size. padding: Override padding from Styles, or None to use styles.padding. crop: Region to crop to. @@ -229,8 +245,8 @@ def render_line( background: Color, render_content_line: Callable[[int], Strip], console: Console, - border_title: Text | None, - border_subtitle: Text | None, + border_title: tuple[Text, Style] | None, + border_subtitle: tuple[Text, Style] | None, ) -> Strip: """Render a styled line. @@ -244,8 +260,8 @@ def render_line( background: Background color of widget. render_content_line: Callback to render a line of content. console: The console in use by the app. - border_title: The title for the widget border. - border_subtitle: The subtitle for the widget border. + border_title: Optional title and style for the widget border. + border_subtitle: Optional subtitle and style for the widget border. Returns: A line of segments. diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index eb85462345..4e6ddcc1d1 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -640,6 +640,11 @@ def process_color(self, name: str, tokens: list[Token]) -> None: process_link_hover_color = 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: for token in tokens: value = token.value @@ -656,6 +661,9 @@ def process_text_style(self, name: str, tokens: list[Token]) -> None: process_link_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: """Process a text-align declaration""" if not tokens: diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index e751033ae5..772dc4c196 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -167,6 +167,14 @@ class RulesMap(TypedDict, total=False): link_hover_background: Color link_hover_style: Style + border_title_color: Color + border_title_background: Color + border_title_style: Style + + border_subtitle_color: Color + border_subtitle_background: Color + border_subtitle_style: Style + RULE_NAMES = list(RulesMap.__annotations__.keys()) RULE_NAMES_SET = frozenset(RULE_NAMES) @@ -321,6 +329,14 @@ class StylesBase(ABC): link_hover_background = ColorProperty("transparent") link_hover_style = StyleFlagsProperty() + border_title_color = ColorProperty(Color(255, 255, 255)) + border_title_background = ColorProperty(Color(0, 0, 0)) + border_title_style = StyleFlagsProperty() + + border_subtitle_color = ColorProperty(Color(255, 255, 255)) + border_subtitle_background = ColorProperty(Color(0, 0, 0)) + border_subtitle_style = StyleFlagsProperty() + def __textual_animation__( self, attribute: str, @@ -990,6 +1006,22 @@ def append_declaration(name: str, value: str) -> None: if "link_hover_style" in rules: 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() return lines diff --git a/src/textual/css/types.py b/src/textual/css/types.py index 48f8433b3d..395b549e05 100644 --- a/src/textual/css/types.py +++ b/src/textual/css/types.py @@ -25,6 +25,7 @@ "hkey", "vkey", "tall", + "panel", "wide", ] Visibility = Literal["visible", "hidden", "initial", "inherit"] diff --git a/src/textual/dom.py b/src/textual/dom.py index 1e7282748d..380c787d11 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -765,6 +765,68 @@ def rich_style(self) -> Style: ) return style + @property + def title_rich_style(self) -> Style: + """Get a Rich Style object for for titles. + + Returns: + A Rich style. + + """ + background = Color(0, 0, 0, 0) + color = Color(255, 255, 255, 0) + style = Style() + for node in reversed(self.ancestors_with_self): + styles = node.styles + if styles.has_rule("background"): + background += styles.background + style += styles.text_style + + styles = self.styles + has_rule = self.styles.has_rule + if has_rule("border_title_background"): + background += styles.border_title_background + if has_rule("border_title_color"): + color += styles.border_title_color + style += Style.from_color( + (background + color).rich_color if (color.a) else None, + background.rich_color if background.a else None, + ) + if has_rule("border_title_style"): + style += self.styles.border_title_style + return style + + @property + def subtitle_rich_style(self) -> Style: + """Get a Rich Style object for for titles. + + Returns: + A Rich style. + + """ + background = Color(0, 0, 0, 0) + color = Color(255, 255, 255, 0) + style = Style() + for node in reversed(self.ancestors_with_self): + styles = node.styles + if styles.has_rule("background"): + background += styles.background + style += styles.text_style + + styles = self.styles + has_rule = styles.has_rule + if has_rule("border_subtitle_background"): + background += styles.border_subtitle_background + if has_rule("border_subtitle_color"): + color += styles.border_subtitle_color + style += Style.from_color( + (background + color).rich_color if (color.a) else None, + background.rich_color if background.a else None, + ) + if has_rule("border_subtitle_style"): + style += self.styles.border_subtitle_style + return style + @property def background_colors(self) -> tuple[Color, Color]: """The background color and the color of the parent's background. From 2e8b364afcd29d2ba44bee9fa068752be23edc30 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 14 Apr 2023 21:34:39 +0100 Subject: [PATCH 2/8] docs for border styles --- CHANGELOG.md | 3 +++ docs/snippets/see_also_border.md | 9 +++++++ docs/styles/border.md | 30 +++++------------------ docs/styles/border_subtitle_align.md | 4 +++ docs/styles/border_subtitle_background.md | 14 +++++++++++ docs/styles/border_subtitle_color.md | 13 ++++++++++ docs/styles/border_subtitle_style.md | 15 ++++++++++++ docs/styles/border_title_align.md | 4 +++ docs/styles/border_title_background.md | 14 +++++++++++ docs/styles/border_title_color.md | 13 ++++++++++ docs/styles/border_title_style.md | 15 ++++++++++++ docs/styles/outline.md | 2 +- docs/styles/text_style.md | 2 +- mkdocs-nav.yml | 10 ++++++-- src/textual/css/styles.py | 4 +++ src/textual/dom.py | 20 ++++++++++++--- 16 files changed, 140 insertions(+), 32 deletions(-) create mode 100644 docs/snippets/see_also_border.md create mode 100644 docs/styles/border_subtitle_background.md create mode 100644 docs/styles/border_subtitle_color.md create mode 100644 docs/styles/border_subtitle_style.md create mode 100644 docs/styles/border_title_background.md create mode 100644 docs/styles/border_title_color.md create mode 100644 docs/styles/border_title_style.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 84fa8fff9b..fa600a9bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 - `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 "panel" border style +- Added `border-title-color`, `border-title-background`, `border-title-style` rules +- Added `border-subtitle-color`, `border-subtitle-background`, `border-subtitle-style` rules ### Fixed diff --git a/docs/snippets/see_also_border.md b/docs/snippets/see_also_border.md new file mode 100644 index 0000000000..83b09dbe3a --- /dev/null +++ b/docs/snippets/see_also_border.md @@ -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. diff --git a/docs/styles/border.md b/docs/styles/border.md index 267c163476..6463c871d8 100644 --- a/docs/styles/border.md +++ b/docs/styles/border.md @@ -2,9 +2,11 @@ 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 - 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 @@ -17,31 +19,10 @@ border-bottom: [<border>] [<border>] [<color> [<percentage>]]; --8<-- "docs/snippets/syntax_block_end.md" -The `border` style accepts an optional [``](../../css_types/border) that sets the visual style of the widget border, an optional [``](../../css_types/color) to set the color of the border, and an optional [``](../../css_types/percentage) to specify the color transparency. - -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. - -### 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. +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. -### Defaults +In Python, the border is set with a tuple of [border style](./border.md) and a color. -If `` is specified but `` is not, it defaults to `"solid"`. -If `` is specified but ``is not, it defaults to green (RGB color `"#00FF00"`). -If `` is not specified it defaults to `100%`. ## 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. - [`outline`](./outline.md) to add an outline around the content of a widget. +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_align.md b/docs/styles/border_subtitle_align.md index d46c9f1e2d..148918ca66 100644 --- a/docs/styles/border_subtitle_align.md +++ b/docs/styles/border_subtitle_align.md @@ -60,3 +60,7 @@ widget.styles.border_subtitle_align = "left" widget.styles.border_subtitle_align = "center" widget.styles.border_subtitle_align = "right" ``` + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_background.md b/docs/styles/border_subtitle_background.md new file mode 100644 index 0000000000..e4482a2881 --- /dev/null +++ b/docs/styles/border_subtitle_background.md @@ -0,0 +1,14 @@ +# 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: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_color.md b/docs/styles/border_subtitle_color.md new file mode 100644 index 0000000000..b7cda2e504 --- /dev/null +++ b/docs/styles/border_subtitle_color.md @@ -0,0 +1,13 @@ +# 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: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_subtitle_style.md b/docs/styles/border_subtitle_style.md new file mode 100644 index 0000000000..575c379ba9 --- /dev/null +++ b/docs/styles/border_subtitle_style.md @@ -0,0 +1,15 @@ +# 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: <text-style>; +--8<-- "docs/snippets/syntax_block_end.md" + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_align.md b/docs/styles/border_title_align.md index e410a771c0..5f754fe392 100644 --- a/docs/styles/border_title_align.md +++ b/docs/styles/border_title_align.md @@ -60,3 +60,7 @@ widget.styles.border_title_align = "left" widget.styles.border_title_align = "center" widget.styles.border_title_align = "right" ``` + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_background.md b/docs/styles/border_title_background.md new file mode 100644 index 0000000000..36dcd52c38 --- /dev/null +++ b/docs/styles/border_title_background.md @@ -0,0 +1,14 @@ +# 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: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_color.md b/docs/styles/border_title_color.md new file mode 100644 index 0000000000..b40a09c053 --- /dev/null +++ b/docs/styles/border_title_color.md @@ -0,0 +1,13 @@ +# 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: (<color> | auto) [<percentage>]; +--8<-- "docs/snippets/syntax_block_end.md" + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/border_title_style.md b/docs/styles/border_title_style.md new file mode 100644 index 0000000000..ba18cab982 --- /dev/null +++ b/docs/styles/border_title_style.md @@ -0,0 +1,15 @@ +# 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: <text-style>; +--8<-- "docs/snippets/syntax_block_end.md" + + +## See also + +--8<-- "docs/snippets/see_also_border.md" diff --git a/docs/styles/outline.md b/docs/styles/outline.md index 5e5155f891..650619b05f 100644 --- a/docs/styles/outline.md +++ b/docs/styles/outline.md @@ -4,7 +4,7 @@ The `outline` style enables the drawing of a box around the content of a widget, !!! 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 diff --git a/docs/styles/text_style.md b/docs/styles/text_style.md index a0ee02f90c..8e9cb4775f 100644 --- a/docs/styles/text_style.md +++ b/docs/styles/text_style.md @@ -1,6 +1,6 @@ # 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 diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index c299345827..7b77d6f43a 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -64,17 +64,23 @@ nav: - "events/screen_suspend.md" - "events/show.md" - Styles: - - "styles/index.md" - "styles/align.md" - "styles/background.md" - - "styles/border.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_background.md" + - "styles/border_title_color.md" + - "styles/border_title_style.md" + - "styles/border.md" - "styles/box_sizing.md" - "styles/color.md" - "styles/content_align.md" - "styles/display.md" - "styles/dock.md" + - "styles/index.md" - Grid: - "styles/grid/index.md" - "styles/grid/column_span.md" diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 772dc4c196..c470405566 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -167,10 +167,12 @@ class RulesMap(TypedDict, total=False): link_hover_background: Color 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: Color border_subtitle_color: Color border_subtitle_background: Color border_subtitle_style: Style @@ -329,10 +331,12 @@ class StylesBase(ABC): link_hover_background = ColorProperty("transparent") link_hover_style = StyleFlagsProperty() + auto_border_title_color = BooleanProperty(default=False) border_title_color = ColorProperty(Color(255, 255, 255)) border_title_background = ColorProperty(Color(0, 0, 0)) border_title_style = StyleFlagsProperty() + auto_border_subtitle_color = BooleanProperty(default=False) border_subtitle_color = ColorProperty(Color(255, 255, 255)) border_subtitle_background = ColorProperty(Color(0, 0, 0)) border_subtitle_style = StyleFlagsProperty() diff --git a/src/textual/dom.py b/src/textual/dom.py index 380c787d11..023498b8b2 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -787,9 +787,15 @@ def title_rich_style(self) -> Style: if has_rule("border_title_background"): background += styles.border_title_background if has_rule("border_title_color"): - color += styles.border_title_color + if ( + styles.has_rule("auto_border_title_color") + and styles.auto_border_title_color + ): + color = background.get_contrast_text(color.a) + else: + color = styles.border_title_color style += Style.from_color( - (background + color).rich_color if (color.a) else None, + (background + color).rich_color if color.a else None, background.rich_color if background.a else None, ) if has_rule("border_title_style"): @@ -818,9 +824,15 @@ def subtitle_rich_style(self) -> Style: if has_rule("border_subtitle_background"): background += styles.border_subtitle_background if has_rule("border_subtitle_color"): - color += styles.border_subtitle_color + if ( + styles.has_rule("auto_border_subtitle_color") + and styles.auto_border_subtitle_color + ): + color = background.get_contrast_text(color.a) + else: + color = styles.border_title_color style += Style.from_color( - (background + color).rich_color if (color.a) else None, + (background + color).rich_color if color.a else None, background.rich_color if background.a else None, ) if has_rule("border_subtitle_style"): From 1660426f7851ff0542529328be8e921e3d8f32b8 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 14 Apr 2023 22:27:03 +0100 Subject: [PATCH 3/8] fix tests --- src/textual/_border.py | 12 ++++-------- tests/test_border.py | 17 ++++++++--------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/textual/_border.py b/src/textual/_border.py index 8741b75b0c..a2a54c3306 100644 --- a/src/textual/_border.py +++ b/src/textual/_border.py @@ -338,11 +338,12 @@ def render_border_label( if not text_label.cell_len or width <= cells_reserved: return - text_label, label_style = label text_label = text_label.copy() text_label.truncate(width - cells_reserved, overflow="ellipsis") - text_label.pad_left(1) - text_label.pad_right(1) + 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] @@ -368,12 +369,7 @@ def render_border_label( styled_segments = [ Segment(segment.text, base_style + segment.style) for segment in segments ] - # blank = Segment(" ", base_style) - # if has_left_corner: - # yield blank yield from styled_segments - # if has_right_corner: - # yield blank def render_row( diff --git a/tests/test_border.py b/tests/test_border.py index fe02d5e28c..d3a1643a17 100644 --- a/tests/test_border.py +++ b/tests/test_border.py @@ -105,7 +105,7 @@ def test_render_border_label_empty_label_skipped( assert [] == list( render_border_label( - Text(""), + (Text(""), Style()), True, "round", width, @@ -142,7 +142,7 @@ def test_render_border_label_skipped_if_narrow( assert [] == list( render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", width, @@ -180,11 +180,10 @@ def test_render_border_label_wide_plain(label: str): 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 right == _BLANK_SEGMENT - assert original_text == Segment(label, _EMPTY_STYLE) + assert segment == Segment(f" {label} ", _EMPTY_STYLE) @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.""" assert [] == list( render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", 999, @@ -222,7 +221,7 @@ def test_render_border_label(): # Implicit test on the number of segments returned: blank1, what, is_up, with_you, blank2 = render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", 9999, @@ -251,7 +250,7 @@ def test_render_border_label(): assert with_you == expected_with_you blank1, what, blank2 = render_border_label( - Text.from_markup(label), + (Text.from_markup(label), Style()), True, "round", 5 + 4, # 5 where "What…" fits + 2 for the blank spaces + 2 for the corners. From c61d0d4425a0ca7f923cea52ded03ae4df6d5ed4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 15 Apr 2023 10:09:38 +0100 Subject: [PATCH 4/8] tests --- src/textual/_border.py | 21 +- src/textual/_styles_cache.py | 44 +- src/textual/css/styles.py | 8 +- src/textual/dom.py | 64 +- .../__snapshots__/test_snapshots.ambr | 2322 ++++++++--------- tests/test_border.py | 2 +- tests/test_styles_cache.py | 32 +- 7 files changed, 1232 insertions(+), 1261 deletions(-) diff --git a/src/textual/_border.py b/src/textual/_border.py index a2a54c3306..5cbb61dee7 100644 --- a/src/textual/_border.py +++ b/src/textual/_border.py @@ -109,7 +109,7 @@ ), "wide": ( ("▁", "▁", "▁"), - ("▎", " ", "▋"), + ("▎", " ", "▊"), ("▔", "▔", "▔"), ), } @@ -212,6 +212,11 @@ ), } +# 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 # rendering a border label. # The values are a pair with (title location, subtitle location). @@ -347,6 +352,7 @@ def render_border_label( text_label.stylize_before(label_style) 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 outer = outer_style + style @@ -363,13 +369,14 @@ def render_border_label( else: assert False - text_label.stylize_before(base_style) - segments = text_label.render(console) + if (flip_top and is_title) or (flip_bottom and not is_title): + base_style = base_style.without_color + Style.from_color( + base_style.bgcolor, base_style.color + ) - styled_segments = [ - Segment(segment.text, base_style + segment.style) for segment in segments - ] - yield from styled_segments + text_label.stylize_before(base_style + label_style) + segments = text_label.render(console) + yield from segments def render_row( diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 3d481d7191..e512b9708a 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -109,11 +109,7 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: """ border_title = widget._border_title - if border_title is not None: - border_title.stylize_before(widget.title_rich_style) border_subtitle = widget._border_subtitle - if border_subtitle is not None: - border_subtitle.stylize_before(widget.subtitle_rich_style) base_background, background = widget.background_colors styles = widget.styles @@ -125,14 +121,14 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: widget.render_line, widget.app.console, ( - border_title + None if border_title is None - else (border_title, widget.title_rich_style) + else (border_title, *widget._title_style_information) ), ( - border_subtitle + None if border_subtitle is None - else (border_subtitle, widget.subtitle_rich_style) + else (border_subtitle, *widget._subtitle_style_information) ), content_size=widget.content_region.size, padding=styles.padding, @@ -163,8 +159,8 @@ def render( background: Color, render_content_line: RenderLineCallback, console: Console, - border_title: tuple[Text, Style] | None, - border_subtitle: tuple[Text, Style] | None, + border_title: tuple[Text, Color, Color, Style] | None, + border_subtitle: tuple[Text, Color, Color, Style] | None, content_size: Size | None = None, padding: Spacing | None = None, crop: Region | None = None, @@ -179,8 +175,8 @@ def render( background: Background color of widget. render_content_line: Callback to render content line. console: The console in use by the app. - border_title: The title and style for the widget border. - border_subtitle: The subtitle and style for the widget border. + border_title: Optional tuple of (title, color, background, style). + border_subtitle: Optional tuple of (subtitle, color, background, style). content_size: Size of content or None to assume full size. padding: Override padding from Styles, or None to use styles.padding. crop: Region to crop to. @@ -245,8 +241,8 @@ def render_line( background: Color, render_content_line: Callable[[int], Strip], console: Console, - border_title: tuple[Text, Style] | None, - border_subtitle: tuple[Text, Style] | None, + border_title: tuple[Text, Color, Color, Style] | None, + border_subtitle: tuple[Text, Color, Color, Style] | None, ) -> Strip: """Render a styled line. @@ -260,8 +256,8 @@ def render_line( background: Background color of widget. render_content_line: Callback to render a line of content. console: The console in use by the app. - border_title: Optional title and style for the widget border. - border_subtitle: Optional subtitle and style for the widget border. + border_title: Optional tuple of (title, color, background, style). + border_subtitle: Optional tuple of (subtitle, color, background, style). Returns: A line of segments. @@ -321,10 +317,22 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: has_left = border_left != "" has_right = border_right != "" 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.rich_color if label_background.a else None, + ) + render_label = (label, style) # Try to save time with expensive call to `render_border_label`: - if border_label: + if render_label: label_segments = render_border_label( - border_label, + render_label, is_top, border_edge_type, width - 2, diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index c470405566..2097c4c6d7 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -332,13 +332,13 @@ class StylesBase(ABC): link_hover_style = StyleFlagsProperty() auto_border_title_color = BooleanProperty(default=False) - border_title_color = ColorProperty(Color(255, 255, 255)) - border_title_background = ColorProperty(Color(0, 0, 0)) + 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)) - border_subtitle_background = ColorProperty(Color(0, 0, 0)) + 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__( diff --git a/src/textual/dom.py b/src/textual/dom.py index 023498b8b2..d9e1e81178 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -766,78 +766,34 @@ def rich_style(self) -> Style: return style @property - def title_rich_style(self) -> Style: + def _title_style_information(self) -> tuple[Color, Color, Style]: """Get a Rich Style object for for titles. Returns: A Rich style. """ - background = Color(0, 0, 0, 0) - color = Color(255, 255, 255, 0) - style = Style() - for node in reversed(self.ancestors_with_self): - styles = node.styles - if styles.has_rule("background"): - background += styles.background - style += styles.text_style - styles = self.styles - has_rule = self.styles.has_rule - if has_rule("border_title_background"): - background += styles.border_title_background - if has_rule("border_title_color"): - if ( - styles.has_rule("auto_border_title_color") - and styles.auto_border_title_color - ): - color = background.get_contrast_text(color.a) - else: - color = styles.border_title_color - style += Style.from_color( - (background + color).rich_color if color.a else None, - background.rich_color if background.a else None, + return ( + styles.border_title_color, + styles.border_title_background, + styles.border_title_style, ) - if has_rule("border_title_style"): - style += self.styles.border_title_style - return style @property - def subtitle_rich_style(self) -> Style: + def _subtitle_style_information(self) -> tuple[Color, Color, Style]: """Get a Rich Style object for for titles. Returns: A Rich style. """ - background = Color(0, 0, 0, 0) - color = Color(255, 255, 255, 0) - style = Style() - for node in reversed(self.ancestors_with_self): - styles = node.styles - if styles.has_rule("background"): - background += styles.background - style += styles.text_style - styles = self.styles - has_rule = styles.has_rule - if has_rule("border_subtitle_background"): - background += styles.border_subtitle_background - if has_rule("border_subtitle_color"): - if ( - styles.has_rule("auto_border_subtitle_color") - and styles.auto_border_subtitle_color - ): - color = background.get_contrast_text(color.a) - else: - color = styles.border_title_color - style += Style.from_color( - (background + color).rich_color if color.a else None, - background.rich_color if background.a else None, + return ( + styles.border_subtitle_color, + styles.border_subtitle_background, + styles.border_subtitle_style, ) - if has_rule("border_subtitle_style"): - style += self.styles.border_subtitle_style - return style @property def background_colors(self) -> tuple[Color, Color]: diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index b591ed28a3..dc245599c6 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -893,140 +893,141 @@ font-weight: 700; } - .terminal-3953159668-matrix { + .terminal-4039043553-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3953159668-title { + .terminal-4039043553-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3953159668-r1 { fill: #05080f } - .terminal-3953159668-r2 { fill: #e1e1e1 } - .terminal-3953159668-r3 { fill: #c5c8c6 } - .terminal-3953159668-r4 { fill: #1e2226;font-weight: bold } - .terminal-3953159668-r5 { fill: #35393d } - .terminal-3953159668-r6 { fill: #454a50 } - .terminal-3953159668-r7 { fill: #fea62b } - .terminal-3953159668-r8 { fill: #e2e3e3;font-weight: bold } - .terminal-3953159668-r9 { fill: #000000 } - .terminal-3953159668-r10 { fill: #e2e3e3 } + .terminal-4039043553-r1 { fill: #05080f } + .terminal-4039043553-r2 { fill: #e1e1e1 } + .terminal-4039043553-r3 { fill: #c5c8c6 } + .terminal-4039043553-r4 { fill: #1e2226;font-weight: bold } + .terminal-4039043553-r5 { fill: #35393d } + .terminal-4039043553-r6 { fill: #454a50 } + .terminal-4039043553-r7 { fill: #fea62b } + .terminal-4039043553-r8 { fill: #e2e3e3;font-weight: bold } + .terminal-4039043553-r9 { fill: #000000 } + .terminal-4039043553-r10 { fill: #e2e3e3 } + .terminal-4039043553-r11 { fill: #14191f } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderApp + BorderApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ascii - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔+-------------------ascii--------------------+ - none|| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|I must not fear.| - hidden|Fear is the mind-killer.| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|Fear is the little-death that brings | - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|total obliteration.| - blank|I will face my fear.| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|I will permit it to pass over me and | - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|through me.| - round|And when it has gone past, I will turn| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|the inner eye to see its path.| - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|Where the fear has gone there will be | - solid|nothing. Only I will remain.| - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|| - double+----------------------------------------------+ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - dashed - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ascii + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔+------------------- ascii --------------------+ + none|| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|I must not fear.| + hidden|Fear is the mind-killer.| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|Fear is the little-death that brings | + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|total obliteration.| + blank|I will face my fear.| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅|I will permit it to pass over me and | + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|through me.| + round|And when it has gone past, I will turn| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|the inner eye to see its path.| + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|Where the fear has gone there will be | + solid|nothing. Only I will remain.| + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁|| + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔|| + double+----------------------------------------------+ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + dashed + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ @@ -1901,246 +1902,246 @@ font-weight: 700; } - .terminal-1921368926-matrix { + .terminal-797557575-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1921368926-title { + .terminal-797557575-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1921368926-r1 { fill: #c5c8c6 } - .terminal-1921368926-r2 { fill: #e1e1e1 } - .terminal-1921368926-r3 { fill: #454a50 } - .terminal-1921368926-r4 { fill: #e2e3e3;font-weight: bold } - .terminal-1921368926-r5 { fill: #24292f;font-weight: bold } - .terminal-1921368926-r6 { fill: #000000 } - .terminal-1921368926-r7 { fill: #004578 } - .terminal-1921368926-r8 { fill: #121212 } - .terminal-1921368926-r9 { fill: #e2e3e3 } - .terminal-1921368926-r10 { fill: #0053aa } - .terminal-1921368926-r11 { fill: #dde8f3;font-weight: bold } - .terminal-1921368926-r12 { fill: #ffff00;font-weight: bold } - .terminal-1921368926-r13 { fill: #24292f } + .terminal-797557575-r1 { fill: #c5c8c6 } + .terminal-797557575-r2 { fill: #e1e1e1 } + .terminal-797557575-r3 { fill: #454a50 } + .terminal-797557575-r4 { fill: #e2e3e3;font-weight: bold } + .terminal-797557575-r5 { fill: #24292f;font-weight: bold } + .terminal-797557575-r6 { fill: #000000 } + .terminal-797557575-r7 { fill: #004578 } + .terminal-797557575-r8 { fill: #121212 } + .terminal-797557575-r9 { fill: #e2e3e3 } + .terminal-797557575-r10 { fill: #0053aa } + .terminal-797557575-r11 { fill: #dde8f3;font-weight: bold } + .terminal-797557575-r12 { fill: #ffff00;font-weight: bold } + .terminal-797557575-r13 { fill: #24292f } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ContentSwitcherApp + ContentSwitcherApp - + - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - DataTableMarkdown - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ───────────────────────────────────────── - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Three Flavours Cornetto - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - The Three Flavours Cornetto  - trilogy is an anthology series  - of Britishcomedic genre films  - directed by Edgar Wright. - - Shaun of the Dead - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - UK  - Release  - FlavourDateDirector -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  - Strawbe…2004-04…Edgar  - Wright - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - Hot Fuzz - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - UK  - Release  - FlavourDateDirector -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  - Classico2007-02…Edgar  - Wright - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - The World's End - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - UK  - Release  - FlavourDateDirector - ───────────────────────────────────────── + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + DataTableMarkdown + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ───────────────────────────────────────── + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Three Flavours Cornetto + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + The Three Flavours Cornetto  + trilogy is an anthology series  + of Britishcomedic genre films  + directed by Edgar Wright. + + Shaun of the Dead + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + UK  + Release  + FlavourDateDirector +  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  + Strawbe…2004-04…Edgar  + Wright + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + Hot Fuzz + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + UK  + Release  + FlavourDateDirector +  ━━━━━━━━━━━━━━━━━━━━━━━━━━━  + Classico2007-02…Edgar  + Wright + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + The World's End + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + UK  + Release  + FlavourDateDirector + ───────────────────────────────────────── @@ -3119,133 +3120,133 @@ font-weight: 700; } - .terminal-1917293938-matrix { + .terminal-1717278065-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1917293938-title { + .terminal-1717278065-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1917293938-r1 { fill: #c5c8c6 } - .terminal-1917293938-r2 { fill: #0178d4 } - .terminal-1917293938-r3 { fill: #e1e1e1 } - .terminal-1917293938-r4 { fill: #1e1e1e } + .terminal-1717278065-r1 { fill: #c5c8c6 } + .terminal-1717278065-r2 { fill: #0178d4 } + .terminal-1717278065-r3 { fill: #e1e1e1 } + .terminal-1717278065-r4 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - AllBordersApp + AllBordersApp - + - - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - |ascii|blankdashed - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - - ══════════════════━━━━━━━━━━━━━━━━━━ - doubleheavyhidden/none - ══════════════════━━━━━━━━━━━━━━━━━━ - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - hkeyinnerouter - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - - - ────────────────────────────────────▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - roundsolidtall - ────────────────────────────────────▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - thickvkeywide - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + |ascii|blankdashed + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + + ══════════════════━━━━━━━━━━━━━━━━━━ + doubleheavyhidden/none + ══════════════════━━━━━━━━━━━━━━━━━━ + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + hkeyinnerouter + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + + + ────────────────────────────────────▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + roundsolidtall + ────────────────────────────────────▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + thickvkeywide + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ @@ -3276,141 +3277,141 @@ font-weight: 700; } - .terminal-3714773288-matrix { + .terminal-1997861159-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3714773288-title { + .terminal-1997861159-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3714773288-r1 { fill: #e1e1e1 } - .terminal-3714773288-r2 { fill: #c5c8c6 } - .terminal-3714773288-r3 { fill: #fea62b } - .terminal-3714773288-r4 { fill: #fea62b;font-weight: bold } - .terminal-3714773288-r5 { fill: #fea62b;font-weight: bold;font-style: italic; } - .terminal-3714773288-r6 { fill: #cc555a;font-weight: bold } - .terminal-3714773288-r7 { fill: #1e1e1e } - .terminal-3714773288-r8 { fill: #1e1e1e;text-decoration: underline; } - .terminal-3714773288-r9 { fill: #fea62b;text-decoration: underline; } - .terminal-3714773288-r10 { fill: #4b4e55;text-decoration: underline; } - .terminal-3714773288-r11 { fill: #4ebf71 } - .terminal-3714773288-r12 { fill: #b93c5b } + .terminal-1997861159-r1 { fill: #e1e1e1 } + .terminal-1997861159-r2 { fill: #c5c8c6 } + .terminal-1997861159-r3 { fill: #fea62b } + .terminal-1997861159-r4 { fill: #fea62b;font-weight: bold } + .terminal-1997861159-r5 { fill: #fea62b;font-weight: bold;font-style: italic; } + .terminal-1997861159-r6 { fill: #cc555a;font-weight: bold } + .terminal-1997861159-r7 { fill: #1e1e1e } + .terminal-1997861159-r8 { fill: #1e1e1e;text-decoration: underline; } + .terminal-1997861159-r9 { fill: #fea62b;text-decoration: underline; } + .terminal-1997861159-r10 { fill: #4b4e55;text-decoration: underline; } + .terminal-1997861159-r11 { fill: #4ebf71 } + .terminal-1997861159-r12 { fill: #b93c5b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderSubTitleAlignAll + BorderSubTitleAlignAll - + - - - - Border titleLef…▁▁▁▁Left▁▁▁▁ - This is the story ofa Pythondeveloper that - Border subtitleCen…▔▔▔▔@@@▔▔▔▔▔ - - - - - - +--------------+Title───────────────── - |had to fill up|nine labelsand ended up redoing it - +-Left-------+──────────────Subtitle - - - - - Title, but really looo… - Title, but r…Title, but reall… - because the first tryhad some labelsthat were too long. - Subtitle, bu…Subtitle, but re… - Subtitle, but really l… - + + + + Border titleLef…▁▁▁▁Left▁▁▁▁ + This is the story ofa Pythondeveloper that + Border subtitleCen…▔▔▔▔@@@▔▔▔▔▔ + + + + + + +--------------+Title───────────────── + |had to fill up|nine labelsand ended up redoing it + +-Left-------+──────────────Subtitle + + + + + Title, but really looo… + Title, but r…Title, but reall… + because the first tryhad some labelsthat were too long. + Subtitle, bu…Subtitle, but re… + Subtitle, but really l… + @@ -3441,134 +3442,134 @@ font-weight: 700; } - .terminal-1044533580-matrix { + .terminal-1601354540-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1044533580-title { + .terminal-1601354540-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1044533580-r1 { fill: #e1e1e1 } - .terminal-1044533580-r2 { fill: #c5c8c6 } - .terminal-1044533580-r3 { fill: #fea62b } - .terminal-1044533580-r4 { fill: #ffffff } - .terminal-1044533580-r5 { fill: #1e1e1e } + .terminal-1601354540-r1 { fill: #e1e1e1 } + .terminal-1601354540-r2 { fill: #c5c8c6 } + .terminal-1601354540-r3 { fill: #fea62b } + .terminal-1601354540-r4 { fill: #ffffff } + .terminal-1601354540-r5 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderSubtitleAlignApp + BorderSubtitleAlignApp - - - - - ──────────────────────────────────────────────────────────────────────────── - - My subtitle is on the left. - - < Left─────────────────────────────────────────────────────────────────── - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - My subtitle is centered - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍Centered!╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - My subtitle is on the right - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Right > - - - - - + + + + + ──────────────────────────────────────────────────────────────────────────── + + My subtitle is on the left. + +  < Left ─────────────────────────────────────────────────────────────────── + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + My subtitle is centered + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + My subtitle is on the right + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Right >  + + + + + @@ -3599,134 +3600,134 @@ font-weight: 700; } - .terminal-304237721-matrix { + .terminal-2047325817-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-304237721-title { + .terminal-2047325817-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-304237721-r1 { fill: #e1e1e1 } - .terminal-304237721-r2 { fill: #c5c8c6 } - .terminal-304237721-r3 { fill: #fea62b } - .terminal-304237721-r4 { fill: #ffffff } - .terminal-304237721-r5 { fill: #1e1e1e } + .terminal-2047325817-r1 { fill: #e1e1e1 } + .terminal-2047325817-r2 { fill: #c5c8c6 } + .terminal-2047325817-r3 { fill: #fea62b } + .terminal-2047325817-r4 { fill: #ffffff } + .terminal-2047325817-r5 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BorderTitleAlignApp + BorderTitleAlignApp - - - - - < Left─────────────────────────────────────────────────────────────────── - - My title is on the left. - - ──────────────────────────────────────────────────────────────────────────── - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍Centered!╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - My title is centered - - ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔Right > - - My title is on the right - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - - - + + + + +  < Left ─────────────────────────────────────────────────────────────────── + + My title is on the left. + + ──────────────────────────────────────────────────────────────────────────── + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ Centered! ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + My title is centered + + ╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Right >  + + My title is on the right + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + @@ -3757,132 +3758,132 @@ font-weight: 700; } - .terminal-1232593861-matrix { + .terminal-3266307003-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1232593861-title { + .terminal-3266307003-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1232593861-r1 { fill: #000000 } - .terminal-1232593861-r2 { fill: #c5c8c6 } - .terminal-1232593861-r3 { fill: #ccccff } + .terminal-3266307003-r1 { fill: #000000 } + .terminal-3266307003-r2 { fill: #c5c8c6 } + .terminal-3266307003-r3 { fill: #ccccff } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BoxSizingApp + BoxSizingApp - + - - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - I'm using border-box! - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - I'm using content-box! - - - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - + + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + I'm using border-box! + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + I'm using content-box! + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + @@ -7529,133 +7530,133 @@ font-weight: 700; } - .terminal-211150573-matrix { + .terminal-3234129636-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-211150573-title { + .terminal-3234129636-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-211150573-r1 { fill: #000000 } - .terminal-211150573-r2 { fill: #c5c8c6 } - .terminal-211150573-r3 { fill: #0000ff } - .terminal-211150573-r4 { fill: #ccccff } + .terminal-3234129636-r1 { fill: #000000 } + .terminal-3234129636-r2 { fill: #c5c8c6 } + .terminal-3234129636-r3 { fill: #0000ff } + .terminal-3234129636-r4 { fill: #ccccff } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarginApp + MarginApp - + - - - - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - 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. - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - + + + + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + 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. + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + + + + @@ -8806,133 +8807,133 @@ font-weight: 700; } - .terminal-3260169885-matrix { + .terminal-3556982422-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3260169885-title { + .terminal-3556982422-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3260169885-r1 { fill: #000000 } - .terminal-3260169885-r2 { fill: #c5c8c6 } - .terminal-3260169885-r3 { fill: #008000 } - .terminal-3260169885-r4 { fill: #cce5cc } + .terminal-3556982422-r1 { fill: #000000 } + .terminal-3556982422-r2 { fill: #c5c8c6 } + .terminal-3556982422-r3 { fill: #008000 } + .terminal-3556982422-r4 { fill: #cce5cc } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OutlineApp + OutlineApp - + - - - - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ear is the mind-killer. - ear is the little-death that brings total obliteration. -  will face my fear. -  will permit it to pass over me and through me. - nd when it has gone past, I will turn the inner eye to see its - ath. - here the fear has gone there will be nothing. Only I will  - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - - - + + + + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ear is the mind-killer. + ear is the little-death that brings total obliteration. +  will face my fear. +  will permit it to pass over me and through me. + nd when it has gone past, I will turn the inner eye to see its + ath. + here the fear has gone there will be nothing. Only I will  + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + + + + + + @@ -8963,133 +8964,133 @@ font-weight: 700; } - .terminal-3219552913-matrix { + .terminal-3019471504-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3219552913-title { + .terminal-3019471504-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3219552913-r1 { fill: #c5c8c6 } - .terminal-3219552913-r2 { fill: #0178d4 } - .terminal-3219552913-r3 { fill: #e1e1e1 } - .terminal-3219552913-r4 { fill: #1e1e1e } + .terminal-3019471504-r1 { fill: #c5c8c6 } + .terminal-3019471504-r2 { fill: #0178d4 } + .terminal-3019471504-r3 { fill: #e1e1e1 } + .terminal-3019471504-r4 { fill: #1e1e1e } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - AllOutlinesApp + AllOutlinesApp - + - - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - |ascii|blankdashed - +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ - - - ══════════════════━━━━━━━━━━━━━━━━━━ - doubleheavyhidden/none - ══════════════════━━━━━━━━━━━━━━━━━━ - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ - hkeyinnernone - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - - - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀──────────────────────────────────── - outerroundsolid - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄──────────────────────────────────── - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - tallvkeywide - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + |ascii|blankdashed + +------------------+╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍ + + + ══════════════════━━━━━━━━━━━━━━━━━━ + doubleheavyhidden/none + ══════════════════━━━━━━━━━━━━━━━━━━ + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ + hkeyinnernone + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + + + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀──────────────────────────────────── + outerroundsolid + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄──────────────────────────────────── + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + tallvkeywide + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ @@ -9277,136 +9278,136 @@ font-weight: 700; } - .terminal-3616080938-matrix { + .terminal-2990670852-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3616080938-title { + .terminal-2990670852-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3616080938-r1 { fill: #c5c8c6 } - .terminal-3616080938-r2 { fill: #000000 } - .terminal-3616080938-r3 { fill: #008000 } - .terminal-3616080938-r4 { fill: #e5f0e5 } - .terminal-3616080938-r5 { fill: #036a03 } - .terminal-3616080938-r6 { fill: #14191f } + .terminal-2990670852-r1 { fill: #c5c8c6 } + .terminal-2990670852-r2 { fill: #000000 } + .terminal-2990670852-r3 { fill: #008000 } + .terminal-2990670852-r4 { fill: #e5f0e5 } + .terminal-2990670852-r5 { fill: #036a03 } + .terminal-2990670852-r6 { fill: #14191f } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OverflowApp + OverflowApp - + - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - I must not fear.I must not fear. - Fear is the mind-killer.Fear is the mind-killer. - Fear is the little-death that Fear is the little-death that  - brings total obliteration.brings total obliteration. - I will face my fear.I will face my fear. - I will permit it to pass over meI will permit it to pass over me  - and through me.and through me. - And when it has gone past, I And when it has gone past, I will  - will turn the inner eye to see turn the inner eye to see its  - its path.▁▁path. - Where the fear has gone there Where the fear has gone there will - will be nothing. Only I will be nothing. Only I will remain. - remain.▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I must not fear. - I must not fear.Fear is the mind-killer. - Fear is the mind-killer.Fear is the little-death that  - Fear is the little-death that brings total obliteration. - brings total obliteration.I will face my fear. - I will face my fear.I will permit it to pass over me  - I will permit it to pass over meand through me. + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + I must not fear.I must not fear. + Fear is the mind-killer.Fear is the mind-killer. + Fear is the little-death that Fear is the little-death that  + brings total obliteration.brings total obliteration. + I will face my fear.I will face my fear. + I will permit it to pass over meI will permit it to pass over me  + and through me.and through me. + And when it has gone past, I And when it has gone past, I will  + will turn the inner eye to see turn the inner eye to see its  + its path.▁▁path. + Where the fear has gone there Where the fear has gone there will + will be nothing. Only I will be nothing. Only I will remain. + remain.▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I must not fear. + I must not fear.Fear is the mind-killer. + Fear is the mind-killer.Fear is the little-death that  + Fear is the little-death that brings total obliteration. + brings total obliteration.I will face my fear. + I will face my fear.I will permit it to pass over me  + I will permit it to pass over meand through me. @@ -13410,169 +13411,169 @@ font-weight: 700; } - .terminal-832370059-matrix { + .terminal-1975973248-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-832370059-title { + .terminal-1975973248-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-832370059-r1 { fill: #c5c8c6 } - .terminal-832370059-r2 { fill: #e3e3e3 } - .terminal-832370059-r3 { fill: #e1e1e1 } - .terminal-832370059-r4 { fill: #23568b } - .terminal-832370059-r5 { fill: #e2e2e2 } - .terminal-832370059-r6 { fill: #004578 } - .terminal-832370059-r7 { fill: #14191f } - .terminal-832370059-r8 { fill: #262626 } - .terminal-832370059-r9 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; } - .terminal-832370059-r10 { fill: #e2e2e2;font-weight: bold } - .terminal-832370059-r11 { fill: #7ae998 } - .terminal-832370059-r12 { fill: #4ebf71;font-weight: bold } - .terminal-832370059-r13 { fill: #008139 } - .terminal-832370059-r14 { fill: #dde8f3;font-weight: bold } - .terminal-832370059-r15 { fill: #ddedf9 } + .terminal-1975973248-r1 { fill: #c5c8c6 } + .terminal-1975973248-r2 { fill: #e3e3e3 } + .terminal-1975973248-r3 { fill: #e1e1e1 } + .terminal-1975973248-r4 { fill: #23568b } + .terminal-1975973248-r5 { fill: #e2e2e2 } + .terminal-1975973248-r6 { fill: #004578 } + .terminal-1975973248-r7 { fill: #14191f } + .terminal-1975973248-r8 { fill: #262626 } + .terminal-1975973248-r9 { fill: #e2e2e2;font-weight: bold;text-decoration: underline; } + .terminal-1975973248-r10 { fill: #e2e2e2;font-weight: bold } + .terminal-1975973248-r11 { fill: #7ae998 } + .terminal-1975973248-r12 { fill: #4ebf71;font-weight: bold } + .terminal-1975973248-r13 { fill: #008139 } + .terminal-1975973248-r14 { fill: #dde8f3;font-weight: bold } + .terminal-1975973248-r15 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - Textual Demo + Textual Demo - + - - Textual Demo - ▅▅ - - TOP - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▃ - - Widgets - Textual Demo - - Welcome! Textual is a framework for creating sophisticated - Rich contentapplications with the terminal. - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Start - CSS▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - - - -                           Widgets                            -  CTRL+C  Quit  CTRL+B  Sidebar  CTRL+T  Toggle Dark mode  CTRL+S  Screenshot  F1  Notes  + + Textual Demo + ▅▅ + + TOP + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▃ + + Widgets + Textual Demo + + Welcome! Textual is a framework for creating sophisticated + Rich contentapplications with the terminal. + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Start + CSS▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + + + + + + + + + + +                           Widgets                            +  CTRL+C  Quit  CTRL+B  Sidebar  CTRL+T  Toggle Dark mode  CTRL+S  Screenshot  F1  Notes  @@ -14107,148 +14108,148 @@ font-weight: 700; } - .terminal-1506359380-matrix { + .terminal-391329861-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1506359380-title { + .terminal-391329861-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1506359380-r1 { fill: #454a50 } - .terminal-1506359380-r2 { fill: #e1e1e1 } - .terminal-1506359380-r3 { fill: #c5c8c6 } - .terminal-1506359380-r4 { fill: #e2e3e3;font-weight: bold } - .terminal-1506359380-r5 { fill: #262626 } - .terminal-1506359380-r6 { fill: #000000 } - .terminal-1506359380-r7 { fill: #e2e2e2 } - .terminal-1506359380-r8 { fill: #e3e3e3 } - .terminal-1506359380-r9 { fill: #14191f } - .terminal-1506359380-r10 { fill: #b93c5b } - .terminal-1506359380-r11 { fill: #121212 } - .terminal-1506359380-r12 { fill: #1e1e1e } - .terminal-1506359380-r13 { fill: #e2e3e3 } - .terminal-1506359380-r14 { fill: #fea62b } - .terminal-1506359380-r15 { fill: #211505;font-weight: bold } - .terminal-1506359380-r16 { fill: #211505 } - .terminal-1506359380-r17 { fill: #dde8f3;font-weight: bold } - .terminal-1506359380-r18 { fill: #ddedf9 } + .terminal-391329861-r1 { fill: #454a50 } + .terminal-391329861-r2 { fill: #e1e1e1 } + .terminal-391329861-r3 { fill: #c5c8c6 } + .terminal-391329861-r4 { fill: #e2e3e3;font-weight: bold } + .terminal-391329861-r5 { fill: #262626 } + .terminal-391329861-r6 { fill: #000000 } + .terminal-391329861-r7 { fill: #e2e2e2 } + .terminal-391329861-r8 { fill: #e3e3e3 } + .terminal-391329861-r9 { fill: #14191f } + .terminal-391329861-r10 { fill: #b93c5b } + .terminal-391329861-r11 { fill: #121212 } + .terminal-391329861-r12 { fill: #1e1e1e } + .terminal-391329861-r13 { fill: #e2e3e3 } + .terminal-391329861-r14 { fill: #fea62b } + .terminal-391329861-r15 { fill: #211505;font-weight: bold } + .terminal-391329861-r16 { fill: #211505 } + .terminal-391329861-r17 { fill: #dde8f3;font-weight: bold } + .terminal-391329861-r18 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - EasingApp + EasingApp - + - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - round▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Animation Duration:1.0 - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - out_sine - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - out_quint - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Welcome to Textual! - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - out_quartI must not fear. - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Fear is the  - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔mind-killer. - out_quadFear is the  - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁little-death that  - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔brings total  - out_expoobliteration.▆▆ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I will face my fear. - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔I will permit it to  - out_elasticpass over me and  - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁through me. - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔And when it has gone  - out_cubic - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ CTRL+P  Focus: Duration Input  CTRL+B  Toggle Dark  + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + round▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Animation Duration:1.0 + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + out_sine + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + out_quint + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Welcome to Textual! + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + out_quartI must not fear. + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁Fear is the  + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔mind-killer. + out_quadFear is the  + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁little-death that  + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔brings total  + out_expoobliteration.▆▆ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁I will face my fear. + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔I will permit it to  + out_elasticpass over me and  + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁through me. + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔And when it has gone  + out_cubic + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ CTRL+P  Focus: Duration Input  CTRL+B  Toggle Dark  @@ -17287,140 +17288,140 @@ font-weight: 700; } - .terminal-4078770422-matrix { + .terminal-1636374768-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4078770422-title { + .terminal-1636374768-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4078770422-r1 { fill: #e1e1e1 } - .terminal-4078770422-r2 { fill: #121212 } - .terminal-4078770422-r3 { fill: #c5c8c6 } - .terminal-4078770422-r4 { fill: #0053aa } - .terminal-4078770422-r5 { fill: #dde8f3;font-weight: bold } - .terminal-4078770422-r6 { fill: #939393;font-weight: bold } - .terminal-4078770422-r7 { fill: #24292f } - .terminal-4078770422-r8 { fill: #e2e3e3;font-weight: bold } - .terminal-4078770422-r9 { fill: #4ebf71;font-weight: bold } - .terminal-4078770422-r10 { fill: #e1e1e1;font-style: italic; } - .terminal-4078770422-r11 { fill: #e1e1e1;font-weight: bold } + .terminal-1636374768-r1 { fill: #e1e1e1 } + .terminal-1636374768-r2 { fill: #121212 } + .terminal-1636374768-r3 { fill: #c5c8c6 } + .terminal-1636374768-r4 { fill: #0053aa } + .terminal-1636374768-r5 { fill: #dde8f3;font-weight: bold } + .terminal-1636374768-r6 { fill: #939393;font-weight: bold } + .terminal-1636374768-r7 { fill: #24292f } + .terminal-1636374768-r8 { fill: #e2e3e3;font-weight: bold } + .terminal-1636374768-r9 { fill: #4ebf71;font-weight: bold } + .terminal-1636374768-r10 { fill: #e1e1e1;font-style: italic; } + .terminal-1636374768-r11 { fill: #e1e1e1;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarkdownExampleApp + MarkdownExampleApp - + - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Markdown Document - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - This is an example of Textual's Markdown widget. - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Features - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Markdown syntax and extensions are supported. - - ● Typography emphasisstronginline code etc. - ● Headers - ● Lists (bullet and ordered) - ● Syntax highlighted code blocks - ● Tables! - - - - + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Markdown Document + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + This is an example of Textual's Markdown widget. + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Features + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Markdown syntax and extensions are supported. + + ● Typography emphasisstronginline code etc. + ● Headers + ● Lists (bullet and ordered) + ● Syntax highlighted code blocks + ● Tables! + + + + @@ -17451,145 +17452,145 @@ font-weight: 700; } - .terminal-971113808-matrix { + .terminal-873465137-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-971113808-title { + .terminal-873465137-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-971113808-r1 { fill: #c5c8c6 } - .terminal-971113808-r2 { fill: #24292f } - .terminal-971113808-r3 { fill: #121212 } - .terminal-971113808-r4 { fill: #e1e1e1 } - .terminal-971113808-r5 { fill: #e2e3e3 } - .terminal-971113808-r6 { fill: #96989b } - .terminal-971113808-r7 { fill: #0053aa } - .terminal-971113808-r8 { fill: #008139 } - .terminal-971113808-r9 { fill: #dde8f3;font-weight: bold } - .terminal-971113808-r10 { fill: #939393;font-weight: bold } - .terminal-971113808-r11 { fill: #14191f } - .terminal-971113808-r12 { fill: #e2e3e3;font-weight: bold } - .terminal-971113808-r13 { fill: #4ebf71;font-weight: bold } - .terminal-971113808-r14 { fill: #e1e1e1;font-style: italic; } - .terminal-971113808-r15 { fill: #e1e1e1;font-weight: bold } + .terminal-873465137-r1 { fill: #c5c8c6 } + .terminal-873465137-r2 { fill: #24292f } + .terminal-873465137-r3 { fill: #121212 } + .terminal-873465137-r4 { fill: #e1e1e1 } + .terminal-873465137-r5 { fill: #e2e3e3 } + .terminal-873465137-r6 { fill: #96989b } + .terminal-873465137-r7 { fill: #0053aa } + .terminal-873465137-r8 { fill: #008139 } + .terminal-873465137-r9 { fill: #dde8f3;font-weight: bold } + .terminal-873465137-r10 { fill: #939393;font-weight: bold } + .terminal-873465137-r11 { fill: #14191f } + .terminal-873465137-r12 { fill: #e2e3e3;font-weight: bold } + .terminal-873465137-r13 { fill: #4ebf71;font-weight: bold } + .terminal-873465137-r14 { fill: #e1e1e1;font-style: italic; } + .terminal-873465137-r15 { fill: #e1e1e1;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MarkdownExampleApp + MarkdownExampleApp - + - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - ▼  Markdown Viewer - ├──  FeaturesMarkdown Viewer - ├──  Tables - └──  Code Blocks▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - This is an example of Textual's MarkdownViewer - widget. - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅ - - Features - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Markdown syntax and extensions are supported. - - ● Typography emphasisstronginline code - etc. - ● Headers - ● Lists (bullet and ordered) - ● Syntax highlighted code blocks - ● Tables! - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▼  Markdown Viewer + ├──  FeaturesMarkdown Viewer + ├──  Tables + └──  Code Blocks▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + This is an example of Textual's MarkdownViewer + widget. + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▅ + + Features + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Markdown syntax and extensions are supported. + + ● Typography emphasisstronginline code + etc. + ● Headers + ● Lists (bullet and ordered) + ● Syntax highlighted code blocks + ● Tables! + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + @@ -20181,135 +20182,134 @@ font-weight: 700; } - .terminal-1405474793-matrix { + .terminal-2499454553-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1405474793-title { + .terminal-2499454553-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1405474793-r1 { fill: #004578 } - .terminal-1405474793-r2 { fill: #c5c8c6 } - .terminal-1405474793-r3 { fill: #e1e1e1 } - .terminal-1405474793-r4 { fill: #23568b } - .terminal-1405474793-r5 { fill: #14191f } + .terminal-2499454553-r1 { fill: #004578 } + .terminal-2499454553-r2 { fill: #c5c8c6 } + .terminal-2499454553-r3 { fill: #e1e1e1 } + .terminal-2499454553-r4 { fill: #23568b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MyApp + MyApp - - - - ────────────────────────────────────────────────────────────────────────────── - SPAM - SPAM - SPAM▂▂ - SPAM - SPAM - SPAM - ────────────────────────────────────────────────────────────────────────── - SPAM - SPAM▆▆ - SPAM - SPAM - SPAM - SPAM▁▁ - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - SPAM - ────────────────────────────────────────────────────────────────────────────── + + + + ────────────────────────────────────────────────────────────────────────────── + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM + SPAM▆▆ + SPAM + ────────────────────────────────────────────────────────────────────────────── @@ -20495,134 +20495,134 @@ font-weight: 700; } - .terminal-2452225303-matrix { + .terminal-1647606097-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2452225303-title { + .terminal-1647606097-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2452225303-r1 { fill: #c5c8c6 } - .terminal-2452225303-r2 { fill: #e3e3e3 } - .terminal-2452225303-r3 { fill: #ff0000 } - .terminal-2452225303-r4 { fill: #dde2e8 } - .terminal-2452225303-r5 { fill: #ddedf9 } + .terminal-1647606097-r1 { fill: #c5c8c6 } + .terminal-1647606097-r2 { fill: #e3e3e3 } + .terminal-1647606097-r3 { fill: #ff0000 } + .terminal-1647606097-r4 { fill: #dde2e8 } + .terminal-1647606097-r5 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ScrollViewTester + ScrollViewTester - - - - ScrollViewTester - 1────────────────────────────────────────────────────────────────────────── - Welcome to line 980 - Welcome to line 981 - Welcome to line 982 - Welcome to line 983 - Welcome to line 984 - Welcome to line 985 - Welcome to line 986 - Welcome to line 987 - Welcome to line 988 - Welcome to line 989 - Welcome to line 990 - Welcome to line 991 - Welcome to line 992 - Welcome to line 993 - Welcome to line 994 - Welcome to line 995 - Welcome to line 996 - Welcome to line 997 - Welcome to line 998 - Welcome to line 999 - ────────────────────────────────────────────────────────────────────────────── + + + + ScrollViewTester +  1 ────────────────────────────────────────────────────────────────────────── + Welcome to line 980 + Welcome to line 981 + Welcome to line 982 + Welcome to line 983 + Welcome to line 984 + Welcome to line 985 + Welcome to line 986 + Welcome to line 987 + Welcome to line 988 + Welcome to line 989 + Welcome to line 990 + Welcome to line 991 + Welcome to line 992 + Welcome to line 993 + Welcome to line 994 + Welcome to line 995 + Welcome to line 996 + Welcome to line 997 + Welcome to line 998 + Welcome to line 999 + ────────────────────────────────────────────────────────────────────────────── @@ -20813,140 +20813,140 @@ font-weight: 700; } - .terminal-4003130388-matrix { + .terminal-1328081937-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4003130388-title { + .terminal-1328081937-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4003130388-r1 { fill: #c5c8c6 } - .terminal-4003130388-r2 { fill: #737373 } - .terminal-4003130388-r3 { fill: #e1e1e1;font-weight: bold } - .terminal-4003130388-r4 { fill: #323232 } - .terminal-4003130388-r5 { fill: #0178d4 } - .terminal-4003130388-r6 { fill: #121212 } - .terminal-4003130388-r7 { fill: #0053aa } - .terminal-4003130388-r8 { fill: #dde8f3;font-weight: bold } - .terminal-4003130388-r9 { fill: #e1e1e1 } - .terminal-4003130388-r10 { fill: #ddedf9 } + .terminal-1328081937-r1 { fill: #c5c8c6 } + .terminal-1328081937-r2 { fill: #737373 } + .terminal-1328081937-r3 { fill: #e1e1e1;font-weight: bold } + .terminal-1328081937-r4 { fill: #323232 } + .terminal-1328081937-r5 { fill: #0178d4 } + .terminal-1328081937-r6 { fill: #121212 } + .terminal-1328081937-r7 { fill: #0053aa } + .terminal-1328081937-r8 { fill: #dde8f3;font-weight: bold } + .terminal-1328081937-r9 { fill: #e1e1e1 } + .terminal-1328081937-r10 { fill: #ddedf9 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TabbedApp + TabbedApp - + - - - LetoJessicaPaul - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - Lady Jessica - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Bene Gesserit and concubine of Leto, and mother of Paul and Alia. - - - - PaulAlia - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - First child - - - - - - -  L  Leto  J  Jessica  P  Paul  + + + LetoJessicaPaul + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + Lady Jessica + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Bene Gesserit and concubine of Leto, and mother of Paul and Alia. + + + + PaulAlia + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + First child + + + + + + +  L  Leto  J  Jessica  P  Paul  diff --git a/tests/test_border.py b/tests/test_border.py index d3a1643a17..5d2518244c 100644 --- a/tests/test_border.py +++ b/tests/test_border.py @@ -183,7 +183,7 @@ def test_render_border_label_wide_plain(label: str): segments = render_border_label((Text.from_markup(label), Style()), *args) (segment,) = segments - assert segment == Segment(f" {label} ", _EMPTY_STYLE) + assert segment == Segment(f" {label} ", None) @pytest.mark.parametrize( diff --git a/tests/test_styles_cache.py b/tests/test_styles_cache.py index e1cb37c65b..6567de4eae 100644 --- a/tests/test_styles_cache.py +++ b/tests/test_styles_cache.py @@ -72,8 +72,8 @@ def test_border(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -106,8 +106,8 @@ def test_padding(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -141,8 +141,8 @@ def test_padding_border(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -177,8 +177,8 @@ def test_outline(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) @@ -208,8 +208,8 @@ def test_crop(): Color.parse("green"), content.__getitem__, Console(), - "", - "", + None, + None, content_size=Size(3, 3), crop=Region(2, 2, 3, 3), ) @@ -247,8 +247,8 @@ def get_content_line(y: int) -> Strip: Color.parse("green"), get_content_line, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) assert rendered_lines == [0, 1, 2] @@ -275,8 +275,8 @@ def get_content_line(y: int) -> Strip: Color.parse("green"), get_content_line, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) assert rendered_lines == [] @@ -294,8 +294,8 @@ def get_content_line(y: int) -> Strip: Color.parse("green"), get_content_line, Console(), - "", - "", + None, + None, content_size=Size(3, 3), ) assert rendered_lines == [0, 1] From 666373afc82647a9d66ae917022bf05dc8f7a832 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 15 Apr 2023 14:37:44 +0100 Subject: [PATCH 5/8] tests and docs --- docs/examples/styles/border_title_colors.css | 16 ++ docs/examples/styles/border_title_colors.py | 19 +++ docs/snippets/border_title_color.md | 18 ++ docs/styles/align.md | 7 +- docs/styles/border_subtitle_background.md | 20 +++ docs/styles/border_subtitle_color.md | 20 +++ docs/styles/border_subtitle_style.md | 20 +++ docs/styles/border_title_background.md | 19 +++ docs/styles/border_title_color.md | 18 ++ docs/styles/border_title_style.md | 20 +++ src/textual/_styles_cache.py | 10 +- .../__snapshots__/test_snapshots.ambr | 158 ++++++++++++++++++ 12 files changed, 336 insertions(+), 9 deletions(-) create mode 100644 docs/examples/styles/border_title_colors.css create mode 100644 docs/examples/styles/border_title_colors.py create mode 100644 docs/snippets/border_title_color.md diff --git a/docs/examples/styles/border_title_colors.css b/docs/examples/styles/border_title_colors.css new file mode 100644 index 0000000000..0ab8909441 --- /dev/null +++ b/docs/examples/styles/border_title_colors.css @@ -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; +} diff --git a/docs/examples/styles/border_title_colors.py b/docs/examples/styles/border_title_colors.py new file mode 100644 index 0000000000..1af74ccc6d --- /dev/null +++ b/docs/examples/styles/border_title_colors.py @@ -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() diff --git a/docs/snippets/border_title_color.md b/docs/snippets/border_title_color.md new file mode 100644 index 0000000000..c2a69052c2 --- /dev/null +++ b/docs/snippets/border_title_color.md @@ -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" + ``` diff --git a/docs/styles/align.md b/docs/styles/align.md index 76835389cf..c32d004aac 100644 --- a/docs/styles/align.md +++ b/docs/styles/align.md @@ -13,12 +13,7 @@ align-vertical: <vertical>; The `align` style takes a [``](../../css_types/horizontal) followed by a [``](../../css_types/vertical). -You can specify the alignment of children on both the horizontal and vertical axes at the same time, -or on each of the axis separately. -To specify alignment on a single axis, use the respective style and type: - - - `align-horizontal` takes a [``](../../css_types/horizontal) and does alignment along the horizontal axis; and - - `align-vertical` takes a [``](../../css_types/vertical) and does alignment along the vertical axis. +You can also set the alignment for each axis individually with `align-horizontal` and `align-vertical`. ## Examples diff --git a/docs/styles/border_subtitle_background.md b/docs/styles/border_subtitle_background.md index e4482a2881..8d60b3e150 100644 --- a/docs/styles/border_subtitle_background.md +++ b/docs/styles/border_subtitle_background.md @@ -9,6 +9,26 @@ border-subtitle-background: (<color> | --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" diff --git a/docs/styles/border_subtitle_color.md b/docs/styles/border_subtitle_color.md index b7cda2e504..3880111595 100644 --- a/docs/styles/border_subtitle_color.md +++ b/docs/styles/border_subtitle_color.md @@ -8,6 +8,26 @@ The `border-subtitle-color` style sets the color of the [border_subtitle][textua border-subtitle-color: (<color> | auto) [<percentage>]; --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" diff --git a/docs/styles/border_subtitle_style.md b/docs/styles/border_subtitle_style.md index 575c379ba9..3b826568f8 100644 --- a/docs/styles/border_subtitle_style.md +++ b/docs/styles/border_subtitle_style.md @@ -10,6 +10,26 @@ border-subtitle-style: <text-style><color> | au --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" diff --git a/docs/styles/border_title_color.md b/docs/styles/border_title_color.md index b40a09c053..2272de9c6f 100644 --- a/docs/styles/border_title_color.md +++ b/docs/styles/border_title_color.md @@ -8,6 +8,24 @@ The `border-title-color` style sets the color of the [border_title][textual.widg border-title-color: (<color> | auto) [<percentage>]; --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" diff --git a/docs/styles/border_title_style.md b/docs/styles/border_title_style.md index ba18cab982..09e8319cca 100644 --- a/docs/styles/border_title_style.md +++ b/docs/styles/border_title_style.md @@ -10,6 +10,26 @@ border-title-style: <text-style>; --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" diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index e512b9708a..7add09c691 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -323,10 +323,14 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: 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 + ( + (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, - base_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`: diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index dc245599c6..5cd124486e 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -3735,6 +3735,164 @@ ''' # --- +# name: test_css_property[border_title_colors.py] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BorderTitleApp + + + + + + + + + + + + + + + +  Textual Rocks ━━━━━━━━━━━━━ + + + + + Hello, World! + + + + + ━━━━━━━━━━━━━ Textual Rocks  + + + + + + + + + + + + ''' +# --- # name: test_css_property[box_sizing.py] ''' From 39e4252ce7fda8094d4a8989270f776198dc4b61 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 15 Apr 2023 14:38:57 +0100 Subject: [PATCH 6/8] changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa600a9bb5..3b7a424da7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +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 - `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 "panel" border style -- Added `border-title-color`, `border-title-background`, `border-title-style` rules -- Added `border-subtitle-color`, `border-subtitle-background`, `border-subtitle-style` rules +- 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 From bdd66552e1dc8e7dfb50bcb9aeb96e7d65b14300 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 15 Apr 2023 14:53:57 +0100 Subject: [PATCH 7/8] implement auto --- src/textual/_styles_cache.py | 10 ++++++++-- src/textual/css/styles.py | 2 +- src/textual/dom.py | 22 ++++++++++++++++------ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 7add09c691..a623c53f40 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -123,12 +123,18 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: ( None if border_title is None - else (border_title, *widget._title_style_information) + else ( + border_title, + *widget._get_title_style_information(base_background), + ) ), ( None if border_subtitle is None - else (border_subtitle, *widget._subtitle_style_information) + else ( + border_subtitle, + *widget._get_subtitle_style_information(base_background), + ) ), content_size=widget.content_region.size, padding=styles.padding, diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 2097c4c6d7..2dcbb181ea 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -172,7 +172,7 @@ class RulesMap(TypedDict, total=False): border_title_background: Color border_title_style: Style - auto_border_subtitle_color: Color + auto_border_subtitle_color: bool border_subtitle_color: Color border_subtitle_background: Color border_subtitle_style: Style diff --git a/src/textual/dom.py b/src/textual/dom.py index d9e1e81178..c8fcdd6781 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -765,8 +765,9 @@ def rich_style(self) -> Style: ) return style - @property - def _title_style_information(self) -> tuple[Color, Color, Style]: + def _get_title_style_information( + self, background: Color + ) -> tuple[Color, Color, Style]: """Get a Rich Style object for for titles. Returns: @@ -774,14 +775,19 @@ def _title_style_information(self) -> tuple[Color, Color, 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 ( - styles.border_title_color, + color, styles.border_title_background, styles.border_title_style, ) - @property - def _subtitle_style_information(self) -> tuple[Color, Color, Style]: + def _get_subtitle_style_information( + self, background: Color + ) -> tuple[Color, Color, Style]: """Get a Rich Style object for for titles. Returns: @@ -789,8 +795,12 @@ def _subtitle_style_information(self) -> tuple[Color, Color, Style]: """ styles = self.styles + if styles.auto_border_title_color: + color = background.get_contrast_text(styles.border_subtitle_color.a) + else: + color = styles.border_subtitle_color return ( - styles.border_subtitle_color, + color, styles.border_subtitle_background, styles.border_subtitle_style, ) From 0ec2b22c143f5a2484feacfc01af5fb47073b018 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 16 Apr 2023 09:12:11 +0100 Subject: [PATCH 8/8] style information fix --- src/textual/dom.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/textual/dom.py b/src/textual/dom.py index c8fcdd6781..430ebeb433 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -770,6 +770,9 @@ def _get_title_style_information( ) -> tuple[Color, Color, Style]: """Get a Rich Style object for for titles. + Args: + background: The background color. + Returns: A Rich style. @@ -790,12 +793,15 @@ def _get_subtitle_style_information( ) -> 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: + if styles.auto_border_subtitle_color: color = background.get_contrast_text(styles.border_subtitle_color.a) else: color = styles.border_subtitle_color