[css] Add the management of a "blank" value for our borders

This commit is contained in:
Olivier Philippon
2022-05-17 15:15:32 +01:00
parent c2c2e79b2b
commit b135fa784b
5 changed files with 111 additions and 15 deletions

65
sandbox/borders.py Normal file
View File

@@ -0,0 +1,65 @@
from rich.console import RenderableType
from rich.text import Text
from textual.app import App, ComposeResult
from textual.css.types import EdgeType
from textual.widget import Widget
from textual.widgets import Placeholder
class VerticalContainer(Widget):
CSS = """
VerticalContainer {
layout: vertical;
overflow: hidden auto;
background: darkblue;
}
VerticalContainer Placeholder {
margin: 1 0;
height: 5;
align: center top;
}
"""
class Introduction(Widget):
CSS = """
Introduction {
background: indigo;
color: white;
height: 3;
padding: 1 0;
}
"""
def render(self, styles) -> RenderableType:
return Text("Here are the color edge types we support.", justify="center")
class MyTestApp(App):
def compose(self) -> ComposeResult:
placeholders = []
for border_edge_type in EdgeType.__args__:
border_placeholder = Placeholder(
id=f"placeholder_{border_edge_type}",
title=(border_edge_type or " ").upper(),
name=f"border: {border_edge_type} white",
)
border_placeholder.styles.border = (border_edge_type, "white")
placeholders.append(border_placeholder)
yield VerticalContainer(Introduction(), *placeholders, id="root")
def on_mount(self):
self.bind("q", "quit")
self.bind("t", "tree")
def action_tree(self):
self.log(self.tree)
app = MyTestApp()
if __name__ == "__main__":
app.run()

View File

@@ -5,9 +5,8 @@ from functools import lru_cache
from rich.console import Console, ConsoleOptions, RenderResult, RenderableType
import rich.repr
from rich.segment import Segment, SegmentLines
from rich.style import Style, StyleType
from rich.style import Style
from .color import Color
from .css.types import EdgeStyle, EdgeType
@@ -15,10 +14,14 @@ INNER = 1
OUTER = 2
BORDER_CHARS: dict[EdgeType, tuple[str, str, str]] = {
# TODO: in "browsers' CSS" `none` and `hidden` both set the border width to zero. Should we do the same?
# Each string of the tuple represents a sub-tuple itself:
# - 1st string represents `(top1, top2, top3)`
# - 2nd string represents (mid1, mid2, mid3)
# - 3rd string represents (bottom1, bottom2, bottom3)
"": (" ", " ", " "),
"none": (" ", " ", " "),
"hidden": (" ", " ", " "),
"blank": (" ", " ", " "),
"round": ("╭─╮", "│ │", "╰─╯"),
"solid": ("┌─┐", "│ │", "└─┘"),
"double": ("╔═╗", "║ ║", "╚═╝"),
@@ -40,6 +43,7 @@ BORDER_LOCATIONS: dict[
"": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"none": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"hidden": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"blank": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"round": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"solid": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
"double": ((0, 0, 0), (0, 0, 0), (0, 0, 0)),
@@ -53,6 +57,8 @@ BORDER_LOCATIONS: dict[
"wide": ((1, 1, 1), (0, 1, 0), (1, 1, 1)),
}
_INVISIBLE_EDGE_TYPES: tuple[EdgeType, ...] = ("none", "hidden")
@lru_cache(maxsize=1024)
def get_box(
@@ -135,7 +141,12 @@ class Border:
(bottom, bottom_color),
(left, left_color),
) = edge_styles
self._sides = (top or "none", right or "none", bottom or "none", left or "none")
self._sides: tuple[EdgeType, ...] = (
top or "none",
right or "none",
bottom or "none",
left or "none",
)
from_color = Style.from_color
self._styles = (
@@ -159,10 +170,10 @@ class Border:
width (int): Desired width.
"""
top, right, bottom, left = self._sides
has_left = left != "none"
has_right = right != "none"
has_top = top != "none"
has_bottom = bottom != "none"
has_left = left not in _INVISIBLE_EDGE_TYPES
has_right = right not in _INVISIBLE_EDGE_TYPES
has_top = top not in _INVISIBLE_EDGE_TYPES
has_bottom = bottom not in _INVISIBLE_EDGE_TYPES
if has_top:
lines.pop(0)
@@ -188,10 +199,10 @@ class Border:
outer_style = console.get_style(self.outer_style)
top_style, right_style, bottom_style, left_style = self._styles
has_left = left != "none"
has_right = right != "none"
has_top = top != "none"
has_bottom = bottom != "none"
has_left = left not in _INVISIBLE_EDGE_TYPES
has_right = right not in _INVISIBLE_EDGE_TYPES
has_top = top not in _INVISIBLE_EDGE_TYPES
has_bottom = bottom not in _INVISIBLE_EDGE_TYPES
width = options.max_width - has_left - has_right

View File

@@ -1,4 +1,6 @@
from __future__ import annotations
import sys
import typing
if sys.version_info >= (3, 8):
from typing import Final
@@ -7,12 +9,16 @@ else:
from ..geometry import Spacing
if typing.TYPE_CHECKING:
from .types import EdgeType
VALID_VISIBILITY: Final = {"visible", "hidden"}
VALID_DISPLAY: Final = {"block", "none"}
VALID_BORDER: Final = {
VALID_BORDER: Final[set[EdgeType]] = {
"none",
"hidden",
"round",
"blank",
"solid",
"double",
"dashed",

View File

@@ -16,6 +16,7 @@ EdgeType = Literal[
"",
"none",
"hidden",
"blank",
"round",
"solid",
"double",
@@ -35,6 +36,6 @@ AlignVertical = Literal["top", "middle", "bottom"]
ScrollbarGutter = Literal["auto", "stable"]
BoxSizing = Literal["border-box", "content-box"]
Overflow = Literal["scroll", "hidden", "auto"]
EdgeStyle = Tuple[str, Color]
EdgeStyle = Tuple[EdgeType, Color]
Specificity3 = Tuple[int, int, int]
Specificity4 = Tuple[int, int, int, int]

View File

@@ -19,6 +19,19 @@ class Placeholder(Widget, can_focus=True):
has_focus: Reactive[bool] = Reactive(False)
mouse_over: Reactive[bool] = Reactive(False)
def __init__(
# parent class constructor signature:
self,
*children: Widget,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
# ...and now for our own class specific params:
title: str | None = None,
) -> None:
super().__init__(*children, name=name, id=id, classes=classes)
self.title = title
def __rich_repr__(self) -> rich.repr.Result:
yield from super().__rich_repr__()
yield "has_focus", self.has_focus, False
@@ -32,7 +45,7 @@ class Placeholder(Widget, can_focus=True):
Pretty(self, no_wrap=True, overflow="ellipsis"),
vertical="middle",
),
title=self.__class__.__name__,
title=self.title or self.__class__.__name__,
border_style="green" if self.mouse_over else "blue",
box=box.HEAVY if self.has_focus else box.ROUNDED,
)