From 9d592e976e5d94c202d45aec050d8b394c893aa7 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Feb 2022 10:08:47 +0000 Subject: [PATCH] add render background --- src/textual/app.py | 8 ++- src/textual/css/_style_properties.py | 92 +++++++++++++----------- src/textual/css/styles.py | 10 +++ src/textual/layouts/dock.py | 13 ++-- src/textual/message.py | 1 - src/textual/renderables/_blend_colors.py | 23 ++++++ src/textual/widget.py | 11 --- 7 files changed, 97 insertions(+), 61 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index f292b1460..23adfdf80 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -33,6 +33,7 @@ from .geometry import Offset, Region, Size from .layouts.dock import Dock from .message_pump import MessagePump from .reactive import Reactive +from .renderables.gradient import VerticalGradient from .view import View from .widget import Widget @@ -152,9 +153,6 @@ class App(DOMNode): def __rich_repr__(self) -> rich.repr.Result: yield "title", self.title - def __rich__(self) -> RenderableType: - return self.view - @property def animator(self) -> Animator: return self._animator @@ -287,6 +285,10 @@ class App(DOMNode): except EmptyQueryError: raise KeyError(selector) + def render_background(self) -> RenderableType: + gradient = VerticalGradient("red", "blue") + return gradient + def update_styles(self) -> None: """Request update of styles. diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py index c8b17ba0c..4dc10ff37 100644 --- a/src/textual/css/_style_properties.py +++ b/src/textual/css/_style_properties.py @@ -31,7 +31,7 @@ from ..geometry import Spacing, SpacingDimensions, clamp if TYPE_CHECKING: from ..layout import Layout - from .styles import Styles + from .styles import Styles, StylesBase from .styles import DockGroup from .._box import BoxType @@ -55,7 +55,7 @@ class ScalarProperty: self.name = name def __get__( - self, obj: Styles, objtype: type[Styles] | None = None + self, obj: StylesBase, objtype: type[Styles] | None = None ) -> Scalar | None: """Get the scalar property @@ -69,7 +69,7 @@ class ScalarProperty: value = obj.get_rule(self.name) return value - def __set__(self, obj: Styles, value: float | Scalar | str | None) -> None: + def __set__(self, obj: StylesBase, value: float | Scalar | str | None) -> None: """Set the scalar property Args: @@ -116,14 +116,14 @@ class BoxProperty: DEFAULT = ("", Color.default()) - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name _type, edge = name.split("_") self._type = _type self.edge = edge def __get__( - self, obj: Styles, objtype: type[Styles] | None = None + self, obj: StylesBase, objtype: type[StylesBase] | None = None ) -> tuple[BoxType, Color]: """Get the box property @@ -206,7 +206,7 @@ class Edges(NamedTuple): class BorderProperty: """Descriptor for getting and setting full borders and outlines.""" - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name self._properties = ( f"{name}_top", @@ -215,7 +215,9 @@ class BorderProperty: f"{name}_left", ) - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> Edges: + def __get__( + self, obj: StylesBase, objtype: type[StylesBase] | None = None + ) -> Edges: """Get the border Args: @@ -237,7 +239,7 @@ class BorderProperty: def __set__( self, - obj: Styles, + obj: StylesBase, border: BorderDefinition | None, ) -> None: """Set the border @@ -298,7 +300,9 @@ class StyleProperty: DEFAULT_STYLE = Style() - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> Style: + def __get__( + self, obj: StylesBase, objtype: type[StylesBase] | None = None + ) -> Style: """Get the Style Args: @@ -319,7 +323,7 @@ class StyleProperty: return style - def __set__(self, obj: Styles, style: Style | str | None): + def __set__(self, obj: StylesBase, style: Style | str | None): """Set the Style Args: @@ -352,10 +356,12 @@ class StyleProperty: class SpacingProperty: """Descriptor for getting and setting spacing properties (e.g. padding and margin).""" - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> Spacing: + def __get__( + self, obj: StylesBase, objtype: type[StylesBase] | None = None + ) -> Spacing: """Get the Spacing Args: @@ -367,7 +373,7 @@ class SpacingProperty: """ return obj.get_rule(self.name, NULL_SPACING) - def __set__(self, obj: Styles, spacing: SpacingDimensions | None): + def __set__(self, obj: StylesBase, spacing: SpacingDimensions | None): """Set the Spacing Args: @@ -394,7 +400,7 @@ class DocksProperty: """ def __get__( - self, obj: Styles, objtype: type[Styles] | None = None + self, obj: StylesBase, objtype: type[StylesBase] | None = None ) -> tuple[DockGroup, ...]: """Get the Docks property @@ -407,7 +413,7 @@ class DocksProperty: """ return obj.get_rule("docks", ()) - def __set__(self, obj: Styles, docks: Iterable[DockGroup] | None): + def __set__(self, obj: StylesBase, docks: Iterable[DockGroup] | None): """Set the Docks property Args: @@ -429,7 +435,7 @@ class DockProperty: the docks themselves, and where they are located on screen. """ - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> str: + def __get__(self, obj: StylesBase, objtype: type[StylesBase] | None = None) -> str: """Get the Dock property Args: @@ -455,11 +461,11 @@ class DockProperty: class LayoutProperty: """Descriptor for getting and setting layout.""" - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name def __get__( - self, obj: Styles, objtype: type[Styles] | None = None + self, obj: StylesBase, objtype: type[StylesBase] | None = None ) -> Layout | None: """ Args: @@ -470,7 +476,7 @@ class LayoutProperty: """ return obj.get_rule(self.name) - def __set__(self, obj: Styles, layout: str | Layout | None): + def __set__(self, obj: StylesBase, layout: str | Layout | None): """ Args: obj (Styles): The Styles object. @@ -497,10 +503,12 @@ class OffsetProperty: will be adjusted by before it is rendered. """ - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> ScalarOffset: + def __get__( + self, obj: StylesBase, objtype: type[StylesBase] | None = None + ) -> ScalarOffset: """Get the offset Args: @@ -514,7 +522,7 @@ class OffsetProperty: return obj.get_rule(self.name, ScalarOffset.null()) def __set__( - self, obj: Styles, offset: tuple[int | str, int | str] | ScalarOffset | None + self, obj: StylesBase, offset: tuple[int | str, int | str] | ScalarOffset | None ): """Set the offset @@ -562,10 +570,10 @@ class StringEnumProperty: self._valid_values = valid_values self._default = default - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> str: + def __get__(self, obj: StylesBase, objtype: type[StylesBase] | None = None) -> str: """Get the string property, or the default value if it's not set Args: @@ -577,7 +585,7 @@ class StringEnumProperty: """ return obj.get_rule(self.name, self._default) - def __set__(self, obj: Styles, value: str | None = None): + def __set__(self, obj: StylesBase, value: str | None = None): """Set the string property and ensure it is in the set of allowed values. Args: @@ -603,10 +611,10 @@ class StringEnumProperty: class NameProperty: """Descriptor for getting and setting name properties.""" - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name - def __get__(self, obj: Styles, objtype: type[Styles] | None) -> str: + def __get__(self, obj: StylesBase, objtype: type[StylesBase] | None) -> str: """Get the name property Args: @@ -618,7 +626,7 @@ class NameProperty: """ return obj.get_rule(self.name, "") - def __set__(self, obj: Styles, name: str | None): + def __set__(self, obj: StylesBase, name: str | None): """Set the name property Args: @@ -640,16 +648,16 @@ class NameProperty: class NameListProperty: - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name def __get__( - self, obj: Styles, objtype: type[Styles] | None = None + self, obj: StylesBase, objtype: type[StylesBase] | None = None ) -> tuple[str, ...]: return obj.get_rule(self.name, ()) def __set__( - self, obj: Styles, names: str | tuple[str] | None = None + self, obj: StylesBase, names: str | tuple[str] | None = None ) -> str | tuple[str] | None: if names is None: @@ -668,10 +676,12 @@ class NameListProperty: class ColorProperty: """Descriptor for getting and setting color properties.""" - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> Color: + def __get__( + self, obj: StylesBaseStylesBase, objtype: type[Styles] | None = None + ) -> Color: """Get the ``Color``, or ``Color.default()`` if no color is set. Args: @@ -683,7 +693,7 @@ class ColorProperty: """ return obj.get_rule(self.name) or Color.default() - def __set__(self, obj: Styles, color: Color | str | None): + def __set__(self, obj: StylesBase, color: Color | str | None): """Set the Color Args: @@ -727,7 +737,9 @@ class StyleFlagsProperty: def __set_name__(self, owner: Styles, name: str) -> None: self.name = name - def __get__(self, obj: Styles, objtype: type[Styles] | None = None) -> Style: + def __get__( + self, obj: StylesBase, objtype: type[StylesBase] | None = None + ) -> Style: """Get the ``Style`` Args: @@ -739,7 +751,7 @@ class StyleFlagsProperty: """ return obj.get_rule(self.name, Style.null()) - def __set__(self, obj: Styles, style_flags: Style | str | None): + def __set__(self, obj: StylesBase, style_flags: Style | str | None): """Set the style using a style flag string Args: @@ -774,7 +786,7 @@ class TransitionsProperty: """Descriptor for getting transitions properties""" def __get__( - self, obj: Styles, objtype: type[Styles] | None = None + self, obj: StylesBase, objtype: type[StylesBase] | None = None ) -> dict[str, Transition]: """Get a mapping of properties to the transitions applied to them. @@ -804,10 +816,10 @@ class FractionalProperty: def __init__(self, default: float = 1.0): self.default = default - def __set_name__(self, owner: Styles, name: str) -> None: + def __set_name__(self, owner: StylesBase, name: str) -> None: self.name = name - def __get__(self, obj: Styles, type: type[Styles]) -> float: + def __get__(self, obj: StylesBase, type: type[StylesBase]) -> float: """Get the property value as a float between 0 and 1 Args: @@ -819,7 +831,7 @@ class FractionalProperty: """ return cast(float, obj.get_rule(self.name, self.default)) - def __set__(self, obj: Styles, value: float | str | None) -> None: + def __set__(self, obj: StylesBase, value: float | str | None) -> None: """Set the property value, clamping it between 0 and 1. Args: diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 383efea35..c097eb7da 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -162,6 +162,16 @@ class StylesBase(ABC): return NotImplemented return self.get_rules() == styles.get_rules() + @property + def gutter(self) -> Spacing: + """Get space around widget. + + Returns: + Spacing: Space around widget. + """ + + return self.margin + @abstractmethod def has_rule(self, rule: str) -> bool: """Check if a rule is set on this Styles object. diff --git a/src/textual/layouts/dock.py b/src/textual/layouts/dock.py index f82d07ef7..d70d24a74 100644 --- a/src/textual/layouts/dock.py +++ b/src/textual/layouts/dock.py @@ -75,18 +75,19 @@ class DockLayout(Layout): def make_dock_options(widget, edge: Edge) -> DockOptions: styles = widget.styles + has_rule = styles.has_rule return ( DockOptions( - styles.width.cells if styles.has_rule("width") else None, - styles.width.fraction if styles.has_rule("width") else 1, - styles.min_width.cells if styles.has_rule("min_width") else 1, + styles.width.cells if has_rule("width") else None, + styles.width.fraction if has_rule("width") else 1, + styles.min_width.cells if has_rule("min_width") else 1, ) if edge in ("left", "right") else DockOptions( - styles.height.cells if styles.has_rule("height") else None, - styles.height.fraction if styles.has_rule("height") else 1, - styles.min_height.cells if styles.has_rule("min_height") else 1, + styles.height.cells if has_rule("height") else None, + styles.height.fraction if has_rule("height") else 1, + styles.min_height.cells if has_rule("min_height") else 1, ) ) diff --git a/src/textual/message.py b/src/textual/message.py index e55e5052c..1114c2b4f 100644 --- a/src/textual/message.py +++ b/src/textual/message.py @@ -1,6 +1,5 @@ from __future__ import annotations -from asyncio import Event from time import monotonic from typing import ClassVar diff --git a/src/textual/renderables/_blend_colors.py b/src/textual/renderables/_blend_colors.py index a65fb0f8c..73bec5afd 100644 --- a/src/textual/renderables/_blend_colors.py +++ b/src/textual/renderables/_blend_colors.py @@ -21,3 +21,26 @@ def blend_colors(color1: Color, color2: Color, ratio: float) -> Color: return Color.from_rgb( red=r1 + dr * ratio, green=g1 + dg * ratio, blue=b1 + db * ratio ) + + +def blend_colors_rgb( + color1: tuple[int, int, int], color2: tuple[int, int, int], ratio: float +) -> Color: + """Blend two colors given as a tuple of 3 values for red, green, and blue. + + Args: + color1 (tuple[int, int, int]): The first color. + color2 (tuple[int, int, int]): The second color. + ratio (float): The ratio of color1 to color2. + + Returns: + Color: A Color representing the blending of the two supplied colors. + """ + r1, g1, b1 = color1 + r2, g2, b2 = color2 + dr = r2 - r1 + dg = g2 - g1 + db = b2 - b1 + return Color.from_rgb( + red=r1 + dr * ratio, green=g1 + dg * ratio, blue=b1 + db * ratio + ) diff --git a/src/textual/widget.py b/src/textual/widget.py index cc442ec78..d98d5af3f 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -204,17 +204,6 @@ class Widget(DOMNode): assert self._animate is not None return self._animate - @property - def gutter(self) -> Spacing: - """Get additional space reserved by margin / padding / border. - - Returns: - Spacing: [description] - """ - styles = self.styles - gutter = styles.margin + styles.padding + styles.border.spacing - return gutter - def on_style_change(self) -> None: self.clear_render_cache()