diff --git a/src/textual/renderables/opacity.py b/src/textual/renderables/opacity.py index 7c9879aa6..9e149725c 100644 --- a/src/textual/renderables/opacity.py +++ b/src/textual/renderables/opacity.py @@ -1,8 +1,17 @@ +from time import sleep + from rich.console import ConsoleOptions, Console, RenderResult, RenderableType +from rich.live import Live +from rich.panel import Panel +from rich.segment import Segment +from rich.style import Style +from rich.text import Text + +from textual.renderables.utilities import blend_colors class Opacity: - """Return a renderable with the foreground color blended into the background color. + """Wrap a renderable to blend foreground color into the background color. Args: renderable (RenderableType): The RenderableType to manipulate. @@ -16,4 +25,56 @@ class Opacity: def __rich_console__( self, console: Console, options: ConsoleOptions ) -> RenderResult: - pass + lines = console.render_lines(self.renderable, options) + opacity = self.value + for line in lines: + for segment in line: + style = segment.style + if not style: + yield segment + continue + fg, bg = style.color, style.bgcolor + if fg and bg: + style = Style.from_color( + color=blend_colors(bg, fg, ratio=opacity), + bgcolor=bg, + ) + yield Segment( + text=segment.text, + style=style, + control=segment.control, + ) + else: + yield segment + yield "" + + +if __name__ == "__main__": + console = Console() + + panel = Panel.fit( + Text("Steak: £30", style="#fcffde on #03761e"), + title="Menu", + style="#ffffff on #000000", + ) + console.print(panel) + + opacity_panel = Opacity(panel, value=0.5) + console.print(opacity_panel) + + def frange(start, end, step): + current = start + while current < end: + yield current + current += step + + while current >= 0: + yield current + current -= step + + import itertools + + with Live(opacity_panel, refresh_per_second=60) as live: + for value in itertools.cycle(frange(0, 1, 0.05)): + opacity_panel.value = value + sleep(0.05) diff --git a/src/textual/renderables/sparkline.py b/src/textual/renderables/sparkline.py index a24630580..175952941 100644 --- a/src/textual/renderables/sparkline.py +++ b/src/textual/renderables/sparkline.py @@ -8,6 +8,8 @@ from rich.console import ConsoleOptions, Console, RenderResult from rich.segment import Segment from rich.style import Style +from textual.renderables.utilities import blend_colors + T = TypeVar("T", int, float) @@ -83,34 +85,12 @@ class Sparkline: partition_summary = summary_func(partition) height_ratio = (partition_summary - minimum) / extent bar_index = int(height_ratio * (len(self.BARS) - 1)) - bar_color = _blend_colors(min_color, max_color, height_ratio) + bar_color = blend_colors(min_color, max_color, height_ratio) bars_rendered += 1 bucket_index += step yield Segment(text=self.BARS[bar_index], style=Style.from_color(bar_color)) -def _blend_colors(color1: Color, color2: Color, ratio: float) -> Color: - """Given two RGB colors, return a color that sits some distance between - them in RGB color space. - - Args: - color1 (Color): The first color. - color2 (Color): 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.triplet - r2, g2, b2 = color2.triplet - 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 - ) - - if __name__ == "__main__": console = Console() diff --git a/src/textual/renderables/utilities.py b/src/textual/renderables/utilities.py new file mode 100644 index 000000000..a65fb0f8c --- /dev/null +++ b/src/textual/renderables/utilities.py @@ -0,0 +1,23 @@ +from rich.color import Color + + +def blend_colors(color1: Color, color2: Color, ratio: float) -> Color: + """Given two RGB colors, return a color that sits some distance between + them in RGB color space. + + Args: + color1 (Color): The first color. + color2 (Color): 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.triplet + r2, g2, b2 = color2.triplet + 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 + )