diff --git a/examples/basic.css b/examples/basic.css index ed125641c..bbe4b534b 100644 --- a/examples/basic.css +++ b/examples/basic.css @@ -5,15 +5,12 @@ $primary: #20639b; App > Screen { layout: dock; docks: side=left/1; - text: on $primary; -} - -Widget:hover { - outline: solid green; + background: $primary; } #sidebar { - text: #09312e on #3caea3; + color: #09312e; + background: #3caea3; dock: side; width: 30; offset-x: -100%; @@ -27,17 +24,21 @@ Widget:hover { } #header { - text: white on #173f5f; + color: white; + background: #173f5f; height: 3; - border: hkey; + border: hkey white; } #content { - text: white on $primary; + color: white; + background: $primary; border-bottom: hkey #0f2b41; } #footer { - text: #3a3009 on #f6d55c; + color: #3a3009; + background: #f6d55c; + height: 3; } diff --git a/sandbox/uber.css b/sandbox/uber.css index cdba3a470..80f234300 100644 --- a/sandbox/uber.css +++ b/sandbox/uber.css @@ -1,7 +1,7 @@ #uber1 { layout: vertical; - - text-background: dark_green; + + background: dark_green; overflow: hidden auto; border: heavy white; } @@ -9,5 +9,5 @@ .list-item { height: 8; min-width: 80; - text-background: dark_blue; + background: dark_blue; } diff --git a/src/textual/_animator.py b/src/textual/_animator.py index d6b2b00da..62aba4894 100644 --- a/src/textual/_animator.py +++ b/src/textual/_animator.py @@ -186,7 +186,7 @@ class Animator: easing_function = EASING[easing] if isinstance(easing, str) else easing - animation: Animation + animation: Animation | None = None if hasattr(obj, "__textual_animation__"): animation = getattr(obj, "__textual_animation__")( attribute, @@ -196,7 +196,7 @@ class Animator: speed=speed, easing=easing_function, ) - else: + if animation is None: start_value = getattr(obj, attribute) if start_value == value: diff --git a/src/textual/app.py b/src/textual/app.py index 541f6a683..6499c45fd 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -720,4 +720,4 @@ class App(DOMNode): self.screen.query(selector).toggle_class(class_name) async def handle_styles_updated(self, message: messages.StylesUpdated) -> None: - self.stylesheet.update(self) + self.stylesheet.update(self, animate=True) diff --git a/src/textual/color.py b/src/textual/color.py index e469b167c..0d0f2096a 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -4,11 +4,14 @@ from functools import lru_cache import re from typing import NamedTuple -from .geometry import clamp import rich.repr from rich.color import Color as RichColor from rich.style import Style +from . import log +from .geometry import clamp + + ANSI_COLOR_NAMES = { "black": 0, "red": 1, @@ -534,16 +537,29 @@ class Color(NamedTuple): @classmethod def from_rich_color(cls, rich_color: RichColor) -> Color: + """Create color from Rich's color class.""" r, g, b = rich_color.get_truecolor() return cls(r, g, b) @property def rich_color(self) -> RichColor: + """This color encoded in Rich's Color class.""" r, g, b, _a = self return RichColor.from_rgb(r, g, b) + @property + def is_transparent(self) -> bool: + """Check if the color is transparent.""" + return self.a == 0 + @property def hex(self) -> str: + """The color in CSS hex form, with 6 digits for RGB, and 8 digits for RGBA. + + Returns: + str: A CSS hex-style color, e.g. "#46b3de" or "#3342457f" + + """ r, g, b, a = self return ( f"#{r:02X}{g:02X}{b:02X}" @@ -553,6 +569,12 @@ class Color(NamedTuple): @property def css(self) -> str: + """The color in CSS rgb or rgba form. + + Returns: + str: A CSS color, e.g. "rgb(10,20,30)" or "(rgb(50,70,80,0.5)" + + """ r, g, b, a = self return f"rgb({r},{g},{b})" if a == 1 else f"rgba({r},{g},{b},{a})" @@ -563,6 +585,25 @@ class Color(NamedTuple): yield b yield "a", a + def blend(self, destination: Color, factor: float) -> Color: + """Generate a new color between two colors. + + Args: + destination (Color): Another color. + factor (float): A blend factor, 0 -> 1 + + Returns: + Color: A new color. + """ + r1, g1, b1, a1 = self + r2, g2, b2, a2 = destination + return Color( + int(r1 + (r2 - r1) * factor), + int(g1 + (g2 - g1) * factor), + int(b1 + (b2 - b1) * factor), + a1 + (a2 - a1) * factor, + ) + @classmethod @lru_cache(maxsize=1024 * 4) def parse(cls, color_text: str) -> Color: @@ -587,7 +628,10 @@ class Color(NamedTuple): if rgb_hex is not None: color = cls( - int(rgb_hex[0:2], 16), int(rgb_hex[2:4], 16), int(rgb_hex[4:6], 16), 1 + int(rgb_hex[0:2], 16), + int(rgb_hex[2:4], 16), + int(rgb_hex[4:6], 16), + 1.0, ) elif rgba_hex is not None: color = cls( diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py index d2a43c83a..3622f2a21 100644 --- a/src/textual/css/_style_properties.py +++ b/src/textual/css/_style_properties.py @@ -15,7 +15,7 @@ import rich.repr from rich.style import Style from .. import log -from ..color import Color +from ..color import Color, ColorPair from ._error_tools import friendly_list from .constants import NULL_SPACING from .errors import StyleTypeError, StyleValueError @@ -316,46 +316,9 @@ class StyleProperty: Returns: A ``Style`` object. """ - has_rule = obj.has_rule - - style = Style.from_color( - obj.text_color.rich_color if has_rule("text_color") else None, - obj.text_background.rich_color if has_rule("text_background") else None, - ) - if has_rule("text_style"): - style += obj.text_style - + style = ColorPair(obj.color, obj.background).style return style - def __set__(self, obj: StylesBase, style: Style | str | None): - """Set the Style - - Args: - obj (Styles): The ``Styles`` object. - style (Style | str, optional): You can supply the ``Style`` directly, or a - string (e.g. ``"blue on #f0f0f0"``). - - Raises: - StyleSyntaxError: When the supplied style string has invalid syntax. - """ - obj.refresh() - - if style is None: - clear_rule = obj.clear_rule - clear_rule("text_color") - clear_rule("text_background") - clear_rule("text_style") - else: - if isinstance(style, str): - style = Style.parse(style) - - if style.color is not None: - obj.text_color = style.color - if style.bgcolor is not None: - obj.text_background = style.bgcolor - if style.without_color: - obj.text_style = str(style.without_color) - class SpacingProperty: """Descriptor for getting and setting spacing properties (e.g. padding and margin).""" @@ -660,9 +623,7 @@ class NameListProperty: ) -> tuple[str, ...]: return obj.get_rule(self.name, ()) - def __set__( - self, obj: StylesBase, names: str | tuple[str] | None = None - ) -> str | tuple[str] | None: + def __set__(self, obj: StylesBase, names: str | tuple[str] | None = None): if names is None: if obj.clear_rule(self.name): @@ -687,7 +648,7 @@ class ColorProperty: self.name = name def __get__(self, obj: StylesBase, objtype: type[Styles] | None = None) -> Color: - """Get the ``Color``, or ``Color.default()`` if no color is set. + """Get a ``Color``. Args: obj (Styles): The ``Styles`` object. @@ -696,7 +657,7 @@ class ColorProperty: Returns: Color: The Color """ - return obj.get_rule(self.name) or self._default_color + return obj.get_rule(self.name, self._default_color) def __set__(self, obj: StylesBase, color: Color | str | None): """Set the Color diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index bf153c3a1..66b75a2b2 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -459,36 +459,34 @@ class StylesBuilder: f"invalid value for layout (received {value!r}, expected {friendly_list(LAYOUT_MAP.keys())})", ) - def process_text(self, name: str, tokens: list[Token], important: bool) -> None: - style_definition = _join_tokens(tokens, joiner=" ") + # def process_text(self, name: str, tokens: list[Token], important: bool) -> None: + # style_definition = _join_tokens(tokens, joiner=" ") - # If every token in the value is a referenced by the same variable, - # we can display the variable name before the style definition. - # TODO: Factor this out to apply it to other properties too. - unique_references = {t.referenced_by for t in tokens if t.referenced_by} - if tokens and tokens[0].referenced_by and len(unique_references) == 1: - variable_prefix = f"${tokens[0].referenced_by.name}=" - else: - variable_prefix = "" + # # If every token in the value is a referenced by the same variable, + # # we can display the variable name before the style definition. + # # TODO: Factor this out to apply it to other properties too. + # unique_references = {t.referenced_by for t in tokens if t.referenced_by} + # if tokens and tokens[0].referenced_by and len(unique_references) == 1: + # variable_prefix = f"${tokens[0].referenced_by.name}=" + # else: + # variable_prefix = "" - try: - style = Style.parse(style_definition) - self.styles.text = style - except Exception as error: - message = f"property 'text' has invalid value {variable_prefix}{style_definition!r}; {error}" - self.error(name, tokens[0], message) - if important: - self.styles.important.update( - {"text_style", "text_background", "text_color"} - ) + # try: + # style = Style.parse(style_definition) + # self.styles.text = style + # except Exception as error: + # message = f"property 'text' has invalid value {variable_prefix}{style_definition!r}; {error}" + # self.error(name, tokens[0], message) + # if important: + # self.styles.important.update( + # {"text_style", "text_background", "text_color"} + # ) - def process_text_color( - self, name: str, tokens: list[Token], important: bool - ) -> None: + def process_color(self, name: str, tokens: list[Token], important: bool) -> None: for token in tokens: if token.name in ("color", "token"): try: - self.styles._rules["text_color"] = Color.parse(token.value) + self.styles._rules["color"] = Color.parse(token.value) except Exception as error: self.error( name, token, f"failed to parse color {token.value!r}; {error}" @@ -498,13 +496,13 @@ class StylesBuilder: name, token, f"unexpected token {token.value!r} in declaration" ) - def process_text_background( + def process_background( self, name: str, tokens: list[Token], important: bool ) -> None: for token in tokens: if token.name in ("color", "token"): try: - self.styles._rules["text_background"] = Color.parse(token.value) + self.styles._rules["background"] = Color.parse(token.value) except Exception as error: self.error( name, token, f"failed to parse color {token.value!r}; {error}" diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index e9b812a55..0c07489b3 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -61,16 +61,16 @@ class RulesMap(TypedDict, total=False): Any key may be absent, indiciating that rule has not been set. - Does not define composite rules, that is a rule that is made of a combination of other rules. For instance, - the text style is made up of text_color, text_background, and text_style. + Does not define composite rules, that is a rule that is made of a combination of other rules. + """ display: Display visibility: Visibility layout: "Layout" - text_color: Color - text_background: Color + color: Color + background: Color text_style: Style opacity: float @@ -132,6 +132,8 @@ class StylesBase(ABC): "min_height", "max_width", "max_height", + "color", + "background", } display = StringEnumProperty(VALID_DISPLAY, "block") @@ -139,8 +141,8 @@ class StylesBase(ABC): layout = LayoutProperty() text = StyleProperty() - text_color = ColorProperty(Color(255, 255, 255)) - text_background = ColorProperty(Color(255, 255, 255)) + color = ColorProperty(Color(255, 255, 255)) + background = ColorProperty(Color(255, 255, 255)) text_style = StyleFlagsProperty() opacity = FractionalProperty() @@ -257,14 +259,6 @@ class StylesBase(ABC): layout (bool, optional): Also require a layout. Defaults to False. """ - @abstractmethod - def check_refresh(self) -> tuple[bool, bool]: - """Check if the Styles must be refreshed. - - Returns: - tuple[bool, bool]: (repaint required, layout_required) - """ - @abstractmethod def reset(self) -> None: """Reset the rules to initial state.""" @@ -402,9 +396,6 @@ class Styles(StylesBase): _rules: RulesMap = field(default_factory=dict) - _layout_required: bool = False - _repaint_required: bool = False - important: set[str] = field(default_factory=set) def copy(self) -> Styles: @@ -449,18 +440,8 @@ class Styles(StylesBase): return self._rules.get(rule, default) def refresh(self, *, layout: bool = False) -> None: - self._repaint_required = True - self._layout_required = self._layout_required or layout - - def check_refresh(self) -> tuple[bool, bool]: - """Check if the Styles must be refreshed. - - Returns: - tuple[bool, bool]: (repaint required, layout_required) - """ - result = (self._repaint_required, self._layout_required) - self._repaint_required = self._layout_required = False - return result + if self.node is not None: + self.node.refresh(layout=layout) def reset(self) -> None: """Reset the rules to initial state.""" @@ -637,19 +618,12 @@ class Styles(StylesBase): assert self.layout is not None append_declaration("layout", self.layout.name) - if ( - has_rule("text_color") - and has_rule("text_background") - and has_rule("text_style") - ): - append_declaration("text", str(self.text)) - else: - if has_rule("text_color"): - append_declaration("text-color", self.text_color.hex) - if has_rule("text_background"): - append_declaration("text-background", self.text_background.hex) - if has_rule("text_style"): - append_declaration("text-style", str(get_rule("text_style"))) + if has_rule("color"): + append_declaration("color", self.color.hex) + if has_rule("background"): + append_declaration("background", self.background.hex) + if has_rule("text_style"): + append_declaration("text-style", str(get_rule("text_style"))) if has_rule("overflow-x"): append_declaration("overflow-x", self.overflow_x) @@ -725,17 +699,6 @@ class RenderStyles(StylesBase): def merge_rules(self, rules: RulesMap) -> None: self._inline_styles.merge_rules(rules) - def check_refresh(self) -> tuple[bool, bool]: - """Check if the Styles must be refreshed. - - Returns: - tuple[bool, bool]: (repaint required, layout_required) - """ - base_repaint, base_layout = self._base_styles.check_refresh() - inline_repaint, inline_layout = self._inline_styles.check_refresh() - result = (base_repaint or inline_repaint, base_layout or inline_layout) - return result - def reset(self) -> None: """Reset the rules to initial state.""" self._inline_styles.reset() diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index d3dc9d0fd..7920849e8 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -248,12 +248,6 @@ class Stylesheet: for key in modified_rule_keys: setattr(base_styles, key, rules.get(key)) - # The styles object may have requested a refresh / layout - # It's the style properties that set these flags - repaint, layout = styles.check_refresh() - if repaint: - node.refresh(layout=layout) - def update(self, root: DOMNode, animate: bool = False) -> None: """Update a node and its children.""" apply = self.apply diff --git a/src/textual/dom.py b/src/textual/dom.py index 81a8e3065..d6d4de60a 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -314,8 +314,7 @@ class DOMNode(MessagePump): for node in self.walk_children(): node._css_styles.reset() if isinstance(node, Widget): - # node.clear_render_cache() - node._repaint_required = True + node.set_dirty() node._layout_required = True def on_style_change(self) -> None: diff --git a/src/textual/geometry.py b/src/textual/geometry.py index 3c0114029..3d5db30ad 100644 --- a/src/textual/geometry.py +++ b/src/textual/geometry.py @@ -495,11 +495,11 @@ class Region(NamedTuple): cut_x ↓ ┌────────┐┌───┐ │ ││ │ - │ ││ │ + │ 0 ││ 1 │ │ ││ │ cut_y → └────────┘└───┘ ┌────────┐┌───┐ - │ ││ │ + │ 2 ││ 3 │ └────────┘└───┘ Args: @@ -531,7 +531,7 @@ class Region(NamedTuple): cut ↓ ┌────────┐┌───┐ - │ ││ │ + │ 0 ││ 1 │ │ ││ │ └────────┘└───┘ @@ -556,10 +556,11 @@ class Region(NamedTuple): """Split a region in to two, from a given x offset. ┌─────────┐ - │ │ + │ 0 │ │ │ cut → └─────────┘ ┌─────────┐ + │ 1 │ └─────────┘ Args: diff --git a/src/textual/screen.py b/src/textual/screen.py index 20cd625c4..b69122fab 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -134,6 +134,7 @@ class Screen(Widget): widget = message.widget assert isinstance(widget, Widget) self._dirty_widgets.append(widget) + self.check_idle() async def handle_layout(self, message: messages.Layout) -> None: message.stop() @@ -204,10 +205,8 @@ class Screen(Widget): widget, _region = self.get_widget_at(event.x, event.y) except errors.NoWidget: return - self.log("forward", widget, event) scroll_widget = widget if scroll_widget is not None: await scroll_widget.forward_event(event) else: - self.log("view.forwarded", event) await self.post_message(event) diff --git a/src/textual/widget.py b/src/textual/widget.py index b032ec918..3bc161c2e 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -84,7 +84,6 @@ class Widget(DOMNode): self._layout_required = False self._animate: BoundAnimator | None = None self._reactive_watches: dict[str, Callable] = {} - self._mouse_over: bool = False self.highlight_style: Style | None = None self._vertical_scrollbar: ScrollBar | None = None @@ -356,7 +355,7 @@ class Widget(DOMNode): def get_pseudo_classes(self) -> Iterable[str]: """Pseudo classes for a widget""" - if self._mouse_over: + if self.mouse_over: yield "hover" if self.has_focus: yield "focus" @@ -472,6 +471,7 @@ class Widget(DOMNode): def on_style_change(self) -> None: self.set_dirty() + self.check_idle() def size_updated( self, size: Size, virtual_size: Size, container_size: Size @@ -575,15 +575,11 @@ class Widget(DOMNode): Args: event (events.Idle): Idle event. """ - # Check if the styles have changed - repaint, layout = self.styles.check_refresh() - if self._dirty_regions: - repaint = True - if layout or self.check_layout(): + if self.check_layout(): self._reset_check_layout() self.screen.post_message_no_wait(messages.Layout(self)) - elif repaint: + elif self._dirty_regions: self.emit_no_wait(messages.Update(self, self)) async def focus(self) -> None: @@ -622,6 +618,12 @@ class Widget(DOMNode): async def on_key(self, event: events.Key) -> None: await self.dispatch_key(event) + def on_leave(self) -> None: + self.mouse_over = False + + def on_enter(self) -> None: + self.mouse_over = True + def on_mouse_scroll_down(self) -> None: self.scroll_down(animate=True) diff --git a/tests/css/test_parse.py b/tests/css/test_parse.py index 10b54b502..34f79034c 100644 --- a/tests/css/test_parse.py +++ b/tests/css/test_parse.py @@ -1,6 +1,7 @@ import pytest -from rich.color import Color, ColorType + +from textual.color import Color from textual.css.errors import UnresolvedVariableError from textual.css.parse import substitute_references from textual.css.scalar import Scalar, Unit @@ -16,39 +17,196 @@ class TestVariableReferenceSubstitution: css = "$x: 1; #some-widget{border: $x;}" variables = substitute_references(tokenize(css, "")) assert list(variables) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 6), referenced_by=None), - Token(name='selector_start_id', value='#some-widget', path='', code=css, location=(0, 7), - referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(0, 19), referenced_by=None), - Token(name='declaration_name', value='border:', path='', code=css, location=(0, 20), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 27), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 4), - referenced_by=ReferencedBy(name='x', location=(0, 28), length=2)), - Token(name='declaration_end', value=';', path='', code=css, location=(0, 30), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(0, 31), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), + Token( + name="selector_start_id", + value="#some-widget", + path="", + code=css, + location=(0, 7), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(0, 19), + referenced_by=None, + ), + Token( + name="declaration_name", + value="border:", + path="", + code=css, + location=(0, 20), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 27), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 4), + referenced_by=ReferencedBy(name="x", location=(0, 28), length=2), + ), + Token( + name="declaration_end", + value=";", + path="", + code=css, + location=(0, 30), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(0, 31), + referenced_by=None, + ), ] def test_simple_reference_no_whitespace(self): css = "$x:1; #some-widget{border: $x;}" variables = substitute_references(tokenize(css, "")) assert list(variables) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='selector_start_id', value='#some-widget', path='', code=css, location=(0, 6), - referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(0, 18), referenced_by=None), - Token(name='declaration_name', value='border:', path='', code=css, location=(0, 19), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 26), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 3), - referenced_by=ReferencedBy(name='x', location=(0, 27), length=2)), - Token(name='declaration_end', value=';', path='', code=css, location=(0, 29), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(0, 30), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="selector_start_id", + value="#some-widget", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(0, 18), + referenced_by=None, + ), + Token( + name="declaration_name", + value="border:", + path="", + code=css, + location=(0, 19), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 26), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 3), + referenced_by=ReferencedBy(name="x", location=(0, 27), length=2), + ), + Token( + name="declaration_end", + value=";", + path="", + code=css, + location=(0, 29), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(0, 30), + referenced_by=None, + ), ] def test_undefined_variable(self): @@ -59,117 +217,642 @@ class TestVariableReferenceSubstitution: def test_transitive_reference(self): css = "$x: 1\n$y: $x\n.thing { border: $y }" assert list(substitute_references(tokenize(css, ""))) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='variable_value_end', value='\n', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='variable_name', value='$y:', path='', code=css, location=(1, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 3), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 4), - referenced_by=ReferencedBy(name='x', location=(1, 4), length=2)), - Token(name='variable_value_end', value='\n', path='', code=css, location=(1, 6), referenced_by=None), - Token(name='selector_start_class', value='.thing', path='', code=css, location=(2, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 6), referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(2, 7), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 8), referenced_by=None), - Token(name='declaration_name', value='border:', path='', code=css, location=(2, 9), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 16), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 4), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 19), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(2, 20), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="variable_value_end", + value="\n", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="variable_name", + value="$y:", + path="", + code=css, + location=(1, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 3), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 4), + referenced_by=ReferencedBy(name="x", location=(1, 4), length=2), + ), + Token( + name="variable_value_end", + value="\n", + path="", + code=css, + location=(1, 6), + referenced_by=None, + ), + Token( + name="selector_start_class", + value=".thing", + path="", + code=css, + location=(2, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 6), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(2, 7), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 8), + referenced_by=None, + ), + Token( + name="declaration_name", + value="border:", + path="", + code=css, + location=(2, 9), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 16), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 4), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 19), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(2, 20), + referenced_by=None, + ), ] def test_multi_value_variable(self): css = "$x: 2 4\n$y: 6 $x 2\n.thing { border: $y }" assert list(substitute_references(tokenize(css, ""))) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='number', value='2', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 5), referenced_by=None), - Token(name='number', value='4', path='', code=css, location=(0, 6), referenced_by=None), - Token(name='variable_value_end', value='\n', path='', code=css, location=(0, 7), referenced_by=None), - Token(name='variable_name', value='$y:', path='', code=css, location=(1, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 3), referenced_by=None), - Token(name='number', value='6', path='', code=css, location=(1, 4), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 5), referenced_by=None), - Token(name='number', value='2', path='', code=css, location=(0, 4), - referenced_by=ReferencedBy(name='x', location=(1, 6), length=2)), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 5), - referenced_by=ReferencedBy(name='x', location=(1, 6), length=2)), - Token(name='number', value='4', path='', code=css, location=(0, 6), - referenced_by=ReferencedBy(name='x', location=(1, 6), length=2)), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 8), referenced_by=None), - Token(name='number', value='2', path='', code=css, location=(1, 9), referenced_by=None), - Token(name='variable_value_end', value='\n', path='', code=css, location=(1, 10), referenced_by=None), - Token(name='selector_start_class', value='.thing', path='', code=css, location=(2, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 6), referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(2, 7), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 8), referenced_by=None), - Token(name='declaration_name', value='border:', path='', code=css, location=(2, 9), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 16), referenced_by=None), - Token(name='number', value='6', path='', code=css, location=(1, 4), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 5), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='number', value='2', path='', code=css, location=(0, 4), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 5), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='number', value='4', path='', code=css, location=(0, 6), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 8), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='number', value='2', path='', code=css, location=(1, 9), - referenced_by=ReferencedBy(name='y', location=(2, 17), length=2)), - Token(name='whitespace', value=' ', path='', code=css, location=(2, 19), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(2, 20), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="number", + value="2", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 5), + referenced_by=None, + ), + Token( + name="number", + value="4", + path="", + code=css, + location=(0, 6), + referenced_by=None, + ), + Token( + name="variable_value_end", + value="\n", + path="", + code=css, + location=(0, 7), + referenced_by=None, + ), + Token( + name="variable_name", + value="$y:", + path="", + code=css, + location=(1, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 3), + referenced_by=None, + ), + Token( + name="number", + value="6", + path="", + code=css, + location=(1, 4), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 5), + referenced_by=None, + ), + Token( + name="number", + value="2", + path="", + code=css, + location=(0, 4), + referenced_by=ReferencedBy(name="x", location=(1, 6), length=2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 5), + referenced_by=ReferencedBy(name="x", location=(1, 6), length=2), + ), + Token( + name="number", + value="4", + path="", + code=css, + location=(0, 6), + referenced_by=ReferencedBy(name="x", location=(1, 6), length=2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 8), + referenced_by=None, + ), + Token( + name="number", + value="2", + path="", + code=css, + location=(1, 9), + referenced_by=None, + ), + Token( + name="variable_value_end", + value="\n", + path="", + code=css, + location=(1, 10), + referenced_by=None, + ), + Token( + name="selector_start_class", + value=".thing", + path="", + code=css, + location=(2, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 6), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(2, 7), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 8), + referenced_by=None, + ), + Token( + name="declaration_name", + value="border:", + path="", + code=css, + location=(2, 9), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 16), + referenced_by=None, + ), + Token( + name="number", + value="6", + path="", + code=css, + location=(1, 4), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 5), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="number", + value="2", + path="", + code=css, + location=(0, 4), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 5), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="number", + value="4", + path="", + code=css, + location=(0, 6), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 8), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="number", + value="2", + path="", + code=css, + location=(1, 9), + referenced_by=ReferencedBy(name="y", location=(2, 17), length=2), + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(2, 19), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(2, 20), + referenced_by=None, + ), ] def test_variable_used_inside_property_value(self): css = "$x: red\n.thing { border: on $x; }" assert list(substitute_references(tokenize(css, ""))) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='token', value='red', path='', code=css, location=(0, 4), referenced_by=None), - Token(name='variable_value_end', value='\n', path='', code=css, location=(0, 7), referenced_by=None), - Token(name='selector_start_class', value='.thing', path='', code=css, location=(1, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 6), referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(1, 7), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 8), referenced_by=None), - Token(name='declaration_name', value='border:', path='', code=css, location=(1, 9), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 16), referenced_by=None), - Token(name='token', value='on', path='', code=css, location=(1, 17), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 19), referenced_by=None), - Token(name='token', value='red', path='', code=css, location=(0, 4), - referenced_by=ReferencedBy(name='x', location=(1, 20), length=2)), - Token(name='declaration_end', value=';', path='', code=css, location=(1, 22), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(1, 23), referenced_by=None), - Token(name='declaration_set_end', value='}', path='', code=css, location=(1, 24), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="token", + value="red", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), + Token( + name="variable_value_end", + value="\n", + path="", + code=css, + location=(0, 7), + referenced_by=None, + ), + Token( + name="selector_start_class", + value=".thing", + path="", + code=css, + location=(1, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 6), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(1, 7), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 8), + referenced_by=None, + ), + Token( + name="declaration_name", + value="border:", + path="", + code=css, + location=(1, 9), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 16), + referenced_by=None, + ), + Token( + name="token", + value="on", + path="", + code=css, + location=(1, 17), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 19), + referenced_by=None, + ), + Token( + name="token", + value="red", + path="", + code=css, + location=(0, 4), + referenced_by=ReferencedBy(name="x", location=(1, 20), length=2), + ), + Token( + name="declaration_end", + value=";", + path="", + code=css, + location=(1, 22), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(1, 23), + referenced_by=None, + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(1, 24), + referenced_by=None, + ), ] def test_variable_definition_eof(self): css = "$x: 1" assert list(substitute_references(tokenize(css, ""))) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='number', value='1', path='', code=css, location=(0, 4), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="number", + value="1", + path="", + code=css, + location=(0, 4), + referenced_by=None, + ), ] def test_variable_reference_whitespace_trimming(self): css = "$x: 123;.thing{border: $x}" assert list(substitute_references(tokenize(css, ""))) == [ - Token(name='variable_name', value='$x:', path='', code=css, location=(0, 0), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 3), referenced_by=None), - Token(name='number', value='123', path='', code=css, location=(0, 7), referenced_by=None), - Token(name='variable_value_end', value=';', path='', code=css, location=(0, 10), referenced_by=None), - Token(name='selector_start_class', value='.thing', path='', code=css, location=(0, 11), referenced_by=None), - Token(name='declaration_set_start', value='{', path='', code=css, location=(0, 17), referenced_by=None), - Token(name='declaration_name', value='border:', path='', code=css, location=(0, 18), referenced_by=None), - Token(name='whitespace', value=' ', path='', code=css, location=(0, 25), referenced_by=None), - Token(name='number', value='123', path='', code=css, location=(0, 7), - referenced_by=ReferencedBy(name='x', location=(0, 26), length=2)), - Token(name='declaration_set_end', value='}', path='', code=css, location=(0, 28), referenced_by=None) + Token( + name="variable_name", + value="$x:", + path="", + code=css, + location=(0, 0), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 3), + referenced_by=None, + ), + Token( + name="number", + value="123", + path="", + code=css, + location=(0, 7), + referenced_by=None, + ), + Token( + name="variable_value_end", + value=";", + path="", + code=css, + location=(0, 10), + referenced_by=None, + ), + Token( + name="selector_start_class", + value=".thing", + path="", + code=css, + location=(0, 11), + referenced_by=None, + ), + Token( + name="declaration_set_start", + value="{", + path="", + code=css, + location=(0, 17), + referenced_by=None, + ), + Token( + name="declaration_name", + value="border:", + path="", + code=css, + location=(0, 18), + referenced_by=None, + ), + Token( + name="whitespace", + value=" ", + path="", + code=css, + location=(0, 25), + referenced_by=None, + ), + Token( + name="number", + value="123", + path="", + code=css, + location=(0, 7), + referenced_by=ReferencedBy(name="x", location=(0, 26), length=2), + ), + Token( + name="declaration_set_end", + value="}", + path="", + code=css, + location=(0, 28), + referenced_by=None, + ), ] @@ -196,33 +879,51 @@ class TestParseLayout: class TestParseText: def test_foreground(self): css = """#some-widget { - text: green; + color: green; } """ stylesheet = Stylesheet() stylesheet.parse(css) styles = stylesheet.rules[0].styles - assert styles.text_color == Color.parse("green") + assert styles.color == Color.parse("green") def test_background(self): css = """#some-widget { - text: on red; + background: red; } """ stylesheet = Stylesheet() stylesheet.parse(css) styles = stylesheet.rules[0].styles - assert styles.text_background == Color("red", type=ColorType.STANDARD, number=1) + assert styles.background == Color.parse("red") class TestParseOffset: - @pytest.mark.parametrize("offset_x, parsed_x, offset_y, parsed_y", [ - ["-5.5%", Scalar(-5.5, Unit.PERCENT, Unit.WIDTH), "-30%", Scalar(-30, Unit.PERCENT, Unit.HEIGHT)], - ["5%", Scalar(5, Unit.PERCENT, Unit.WIDTH), "40%", Scalar(40, Unit.PERCENT, Unit.HEIGHT)], - ["10", Scalar(10, Unit.CELLS, Unit.WIDTH), "40", Scalar(40, Unit.CELLS, Unit.HEIGHT)], - ]) + @pytest.mark.parametrize( + "offset_x, parsed_x, offset_y, parsed_y", + [ + [ + "-5.5%", + Scalar(-5.5, Unit.PERCENT, Unit.WIDTH), + "-30%", + Scalar(-30, Unit.PERCENT, Unit.HEIGHT), + ], + [ + "5%", + Scalar(5, Unit.PERCENT, Unit.WIDTH), + "40%", + Scalar(40, Unit.PERCENT, Unit.HEIGHT), + ], + [ + "10", + Scalar(10, Unit.CELLS, Unit.WIDTH), + "40", + Scalar(40, Unit.CELLS, Unit.HEIGHT), + ], + ], + ) def test_composite_rule(self, offset_x, parsed_x, offset_y, parsed_y): css = f"""#some-widget {{ offset: {offset_x} {offset_y}; @@ -238,11 +939,29 @@ class TestParseOffset: assert styles.offset.x == parsed_x assert styles.offset.y == parsed_y - @pytest.mark.parametrize("offset_x, parsed_x, offset_y, parsed_y", [ - ["-5.5%", Scalar(-5.5, Unit.PERCENT, Unit.WIDTH), "-30%", Scalar(-30, Unit.PERCENT, Unit.HEIGHT)], - ["5%", Scalar(5, Unit.PERCENT, Unit.WIDTH), "40%", Scalar(40, Unit.PERCENT, Unit.HEIGHT)], - ["-10", Scalar(-10, Unit.CELLS, Unit.WIDTH), "40", Scalar(40, Unit.CELLS, Unit.HEIGHT)], - ]) + @pytest.mark.parametrize( + "offset_x, parsed_x, offset_y, parsed_y", + [ + [ + "-5.5%", + Scalar(-5.5, Unit.PERCENT, Unit.WIDTH), + "-30%", + Scalar(-30, Unit.PERCENT, Unit.HEIGHT), + ], + [ + "5%", + Scalar(5, Unit.PERCENT, Unit.WIDTH), + "40%", + Scalar(40, Unit.PERCENT, Unit.HEIGHT), + ], + [ + "-10", + Scalar(-10, Unit.CELLS, Unit.WIDTH), + "40", + Scalar(40, Unit.CELLS, Unit.HEIGHT), + ], + ], + ) def test_separate_rules(self, offset_x, parsed_x, offset_y, parsed_y): css = f"""#some-widget {{ offset-x: {offset_x}; @@ -262,14 +981,15 @@ class TestParseOffset: class TestParseTransition: @pytest.mark.parametrize( - "duration, parsed_duration", [ + "duration, parsed_duration", + [ ["5.57s", 5.57], ["0.5s", 0.5], ["1200ms", 1.2], ["0.5ms", 0.0005], - ["20", 20.], + ["20", 20.0], ["0.1", 0.1], - ] + ], ) def test_various_duration_formats(self, duration, parsed_duration): easing = "in_out_cubic" @@ -286,7 +1006,9 @@ class TestParseTransition: assert len(stylesheet.rules) == 1 assert stylesheet.rules[0].errors == [] assert styles.transitions == { - "offset": Transition(duration=parsed_duration, easing=easing, delay=parsed_duration) + "offset": Transition( + duration=parsed_duration, easing=easing, delay=parsed_duration + ) } def test_no_delay_specified(self): @@ -317,14 +1039,17 @@ class TestParseTransition: class TestParseOpacity: - @pytest.mark.parametrize("css_value, styles_value", [ - ["-0.2", 0.0], - ["0.4", 0.4], - ["1.3", 1.0], - ["-20%", 0.0], - ["25%", 0.25], - ["128%", 1.0], - ]) + @pytest.mark.parametrize( + "css_value, styles_value", + [ + ["-0.2", 0.0], + ["0.4", 0.4], + ["1.3", 1.0], + ["-20%", 0.0], + ["25%", 0.25], + ["128%", 1.0], + ], + ) def test_opacity_to_styles(self, css_value, styles_value): css = f"#some-widget {{ opacity: {css_value} }}" stylesheet = Stylesheet() diff --git a/tests/css/test_styles.py b/tests/css/test_styles.py index bb4417894..58af80860 100644 --- a/tests/css/test_styles.py +++ b/tests/css/test_styles.py @@ -1,7 +1,8 @@ import pytest -from rich.color import Color + from rich.style import Style +from textual.color import Color from textual.css.errors import StyleTypeError from textual.css.styles import Styles, RenderStyles from textual.dom import DOMNode @@ -93,20 +94,20 @@ def test_render_styles_text(): assert styles_view.text == Style() # Base is bold blue - base.text_color = "blue" + base.color = "blue" base.text_style = "bold" assert styles_view.text == Style.parse("bold blue") # Base is bold blue, inline is red - inline.text_color = "red" + inline.color = "red" assert styles_view.text == Style.parse("bold red") # Base is bold yellow, inline is red - base.text_color = "yellow" + base.color = "yellow" assert styles_view.text == Style.parse("bold red") # Base is bold blue - inline.text_color = None + inline.color = None assert styles_view.text == Style.parse("bold yellow") @@ -125,25 +126,28 @@ def test_render_styles_border(): assert styles_view.border_left == ("rounded", Color.parse("green")) assert styles_view.border == ( ("heavy", Color.parse("red")), - ("", Color.default()), - ("", Color.default()), + ("", Color(0, 255, 0)), + ("", Color(0, 255, 0)), ("rounded", Color.parse("green")), ) def test_get_opacity_default(): styles = RenderStyles(DOMNode(), Styles(), Styles()) - assert styles.opacity == 1. + assert styles.opacity == 1.0 -@pytest.mark.parametrize("set_value, expected", [ - [0.2, 0.2], - [-0.4, 0.0], - [5.8, 1.0], - ["25%", 0.25], - ["-10%", 0.0], - ["120%", 1.0], -]) +@pytest.mark.parametrize( + "set_value, expected", + [ + [0.2, 0.2], + [-0.4, 0.0], + [5.8, 1.0], + ["25%", 0.25], + ["-10%", 0.0], + ["120%", 1.0], + ], +) def test_opacity_set_then_get(set_value, expected): styles = RenderStyles(DOMNode(), Styles(), Styles()) styles.opacity = set_value