From aaf0e088603fff8f6ce6297beff503332404b1b7 Mon Sep 17 00:00:00 2001 From: Peter Talaber Date: Sun, 23 Jul 2023 13:49:31 +0200 Subject: [PATCH] Add new color scheme and thickness to progress bar - Rainbow scheme will update bar color based on percentage starting from .bar--bar.color up to .bar--complete.color in counterclockwise direction. - Bar thickness can have 3 styles: thin, normal or thick. --- .../widgets/progress_bar_styled_rainbow.css | 22 + .../widgets/progress_bar_styled_rainbow.py | 38 + .../widgets/progress_bar_styled_rainbow_.py | 52 ++ .../widgets/progress_bar_styled_thickness.py | 38 + .../widgets/progress_bar_styled_thickness_.py | 53 ++ docs/widgets/progress_bar.md | 71 +- src/textual/color.py | 26 +- src/textual/renderables/bar.py | 28 +- src/textual/widgets/_progress_bar.py | 87 +- .../__snapshots__/test_snapshots.ambr | 803 ++++++++++++++++++ tests/snapshot_tests/test_snapshots.py | 17 + 11 files changed, 1204 insertions(+), 31 deletions(-) create mode 100644 docs/examples/widgets/progress_bar_styled_rainbow.css create mode 100644 docs/examples/widgets/progress_bar_styled_rainbow.py create mode 100644 docs/examples/widgets/progress_bar_styled_rainbow_.py create mode 100644 docs/examples/widgets/progress_bar_styled_thickness.py create mode 100644 docs/examples/widgets/progress_bar_styled_thickness_.py diff --git a/docs/examples/widgets/progress_bar_styled_rainbow.css b/docs/examples/widgets/progress_bar_styled_rainbow.css new file mode 100644 index 0000000000..63a9439173 --- /dev/null +++ b/docs/examples/widgets/progress_bar_styled_rainbow.css @@ -0,0 +1,22 @@ +Bar > .bar--indeterminate { + color: $primary; + background: $secondary; +} + +Bar > .bar--bar { + color: blue; + background: $primary 30%; +} + +Bar > .bar--complete { + color: red; +} + +PercentageStatus { + text-style: reverse; + color: $secondary; +} + +ETAStatus { + text-style: underline; +} diff --git a/docs/examples/widgets/progress_bar_styled_rainbow.py b/docs/examples/widgets/progress_bar_styled_rainbow.py new file mode 100644 index 0000000000..dbf2345c7d --- /dev/null +++ b/docs/examples/widgets/progress_bar_styled_rainbow.py @@ -0,0 +1,38 @@ +from textual.app import App, ComposeResult +from textual.containers import Center, Middle +from textual.timer import Timer +from textual.widgets import Footer, ProgressBar + + +class StyledExtProgressBar(App[None]): + BINDINGS = [ + ("s", "start", "Start"), + ] + CSS_PATH = "progress_bar_styled_rainbow.css" + + progress_timer: Timer + """Timer to simulate progress happening.""" + + def compose(self) -> ComposeResult: + with Center(): + with Middle(): + yield ProgressBar(color_scheme="rainbow") + yield Footer() + + def on_mount(self) -> None: + """Set up a timer to simulate progess happening.""" + self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True) + + def make_progress(self) -> None: + """Called automatically to advance the progress bar.""" + self.query_one(ProgressBar).advance(1) + + def action_start(self) -> None: + """Start the progress tracking.""" + self.query_one(ProgressBar).update(total=100) + self.progress_timer.resume() + + +if __name__ == "__main__": + app = StyledExtProgressBar() + app.run() diff --git a/docs/examples/widgets/progress_bar_styled_rainbow_.py b/docs/examples/widgets/progress_bar_styled_rainbow_.py new file mode 100644 index 0000000000..21b33e4dd6 --- /dev/null +++ b/docs/examples/widgets/progress_bar_styled_rainbow_.py @@ -0,0 +1,52 @@ +from textual.app import App, ComposeResult +from textual.containers import Center, Middle +from textual.timer import Timer +from textual.widgets import Footer, ProgressBar + + +class StyledExtProgressBar(App[None]): + BINDINGS = [ + ("s", "start", "Start"), + ] + CSS_PATH = "progress_bar_styled_rainbow.css" + + progress_timer: Timer + """Timer to simulate progress happening.""" + + def compose(self) -> ComposeResult: + with Center(): + with Middle(): + yield ProgressBar(color_scheme="rainbow") + yield Footer() + + def on_mount(self) -> None: + """Set up a timer to simulate progess happening.""" + self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True) + + def make_progress(self) -> None: + """Called automatically to advance the progress bar.""" + self.query_one(ProgressBar).advance(1) + + def action_start(self) -> None: + """Start the progress tracking.""" + self.query_one(ProgressBar).update(total=100) + self.progress_timer.resume() + + def key_1(self) -> None: + self._action_common_keypress(10) + + def key_5(self) -> None: + self._action_common_keypress(50) + + def key_9(self) -> None: + self._action_common_keypress(90) + + def _action_common_keypress(self, progress: int) -> None: + # Freeze time for the indeterminate progress bar. + self.query_one(ProgressBar).query_one("#eta")._get_elapsed_time = lambda: 0 + self.query_one(ProgressBar).update(total=100, progress=progress) + self.progress_timer.pause() + +if __name__ == "__main__": + app = StyledExtProgressBar() + app.run() diff --git a/docs/examples/widgets/progress_bar_styled_thickness.py b/docs/examples/widgets/progress_bar_styled_thickness.py new file mode 100644 index 0000000000..df85be9fe7 --- /dev/null +++ b/docs/examples/widgets/progress_bar_styled_thickness.py @@ -0,0 +1,38 @@ +from textual.app import App, ComposeResult +from textual.containers import Center, Middle +from textual.timer import Timer +from textual.widgets import Footer, ProgressBar + + +class StyledExtProgressBar(App[None]): + BINDINGS = [ + ("s", "start", "Start"), + ] + CSS_PATH = "progress_bar_styled.css" + + progress_timer: Timer + """Timer to simulate progress happening.""" + + def compose(self) -> ComposeResult: + with Center(): + with Middle(): + yield ProgressBar(thickness=2) + yield Footer() + + def on_mount(self) -> None: + """Set up a timer to simulate progess happening.""" + self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True) + + def make_progress(self) -> None: + """Called automatically to advance the progress bar.""" + self.query_one(ProgressBar).advance(1) + + def action_start(self) -> None: + """Start the progress tracking.""" + self.query_one(ProgressBar).update(total=100) + self.progress_timer.resume() + + +if __name__ == "__main__": + app = StyledExtProgressBar() + app.run() diff --git a/docs/examples/widgets/progress_bar_styled_thickness_.py b/docs/examples/widgets/progress_bar_styled_thickness_.py new file mode 100644 index 0000000000..250aba695d --- /dev/null +++ b/docs/examples/widgets/progress_bar_styled_thickness_.py @@ -0,0 +1,53 @@ +from textual.app import App, ComposeResult +from textual.containers import Center, Middle +from textual.timer import Timer +from textual.widgets import Footer, ProgressBar + + +class StyledExtProgressBar(App[None]): + BINDINGS = [ + ("s", "start", "Start"), + ] + CSS_PATH = "progress_bar_styled.css" + + progress_timer: Timer + """Timer to simulate progress happening.""" + + def compose(self) -> ComposeResult: + with Center(): + with Middle(): + yield ProgressBar() + yield Footer() + + def on_mount(self) -> None: + """Set up a timer to simulate progess happening.""" + self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True) + + def make_progress(self) -> None: + """Called automatically to advance the progress bar.""" + self.query_one(ProgressBar).advance(1) + + def action_start(self) -> None: + """Start the progress tracking.""" + self.query_one(ProgressBar).update(total=100) + self.progress_timer.resume() + + def key_0(self) -> None: + self._action_common_keypress(0) + + def key_1(self) -> None: + self._action_common_keypress(1) + + def key_2(self) -> None: + self._action_common_keypress(2) + + def _action_common_keypress(self, thickness: int) -> None: + # Freeze time for the indeterminate progress bar. + self.query_one(ProgressBar).query_one("#eta")._get_elapsed_time = lambda: 0 + self.query_one(ProgressBar).query_one("#bar").thickness = thickness + self.query_one(ProgressBar).update(total=100, progress=50) + self.progress_timer.pause() + +if __name__ == "__main__": + app = StyledExtProgressBar() + app.run() diff --git a/docs/widgets/progress_bar.md b/docs/widgets/progress_bar.md index ab927aa763..a1b42d09b9 100644 --- a/docs/widgets/progress_bar.md +++ b/docs/widgets/progress_bar.md @@ -24,7 +24,7 @@ It shows the progress bar in: === "39% done" - ```{.textual path="docs/examples/widgets/progress_bar_isolated_.py" press="t"} + ```{.textual path="docs/examples/widgets/progress_bar_styled_.py" press="t"} ``` === "Completed" @@ -104,16 +104,79 @@ Refer to the [section below](#styling-the-progress-bar) for more information. --8<-- "docs/examples/widgets/progress_bar_styled.tcss" ``` +### Rainbow Color Schemes + +The rainbow color scheme gradually changes the color of the bar as it progresses. +The initial color is defined by the component class `bar--bar` and the final color by the component class `bar--complete`. +(The gradient is computed in the HSL color space in the counterclockwise direction.) + +=== "Rainbow at 10%" + + ```{.textual path="docs/examples/widgets/progress_bar_styled_rainbow_.py" press="1"} + ``` + +=== "Rainbow at 50%" + + ```{.textual path="docs/examples/widgets/progress_bar_styled_rainbow_.py" press="5"} + ``` + +=== "Rainbow at 90%" + + ```{.textual path="docs/examples/widgets/progress_bar_styled_rainbow_.py" press="9"} + ``` +=== "progress_bar_styled_rainbow.py" + + ```python + --8<-- "docs/examples/widgets/progress_bar_styled_rainbow.py" + ``` + +=== "progress_bar_styled.css" + + ```sass + --8<-- "docs/examples/widgets/progress_bar_styled_rainbow.css" + ``` + +### Progress Bar Thickness Styling + +This shows a progress bar with custom bar thickness: thin, normal or thick. + +=== "Thin" + + ```{.textual path="docs/examples/widgets/progress_bar_styled_thickness_.py" press="0"} + ``` + +=== "Normal" + + ```{.textual path="docs/examples/widgets/progress_bar_styled_thickness_.py" press="1"} + ``` + +=== "Thick" + + ```{.textual path="docs/examples/widgets/progress_bar_styled_thickness_.py" press="2"} + ``` +=== "progress_bar_styled_thickness.py" + + ```python + --8<-- "docs/examples/widgets/progress_bar_styled_thickness.py" + ``` + +=== "progress_bar_styled.css" + + ```sass + --8<-- "docs/examples/widgets/progress_bar_styled.css" + ``` + ## Styling the Progress Bar The progress bar is composed of three sub-widgets that can be styled independently: | Widget name | ID | Description | | ------------------ | ------------- | ---------------------------------------------------------------- | -| `Bar` | `#bar` | The bar that visually represents the progress made. | +| `Bar` | `#bar` | Bar that visually represents the progress made. | | `PercentageStatus` | `#percentage` | [Label](./label.md) that shows the percentage of completion. | | `ETAStatus` | `#eta` | [Label](./label.md) that shows the estimated time to completion. | + ### Bar Component Classes ::: textual.widgets._progress_bar.Bar.COMPONENT_CLASSES @@ -125,9 +188,9 @@ The progress bar is composed of three sub-widgets that can be styled independent | Name | Type | Default | Description | | ------------ | ------- | ------- | ------------------------------------------------------------------------------------------------------- | -| `percentage` | `float | None` | The read-only percentage of progress that has been made. This is `None` if the `total` hasn't been set. | +| `percentage` | `float` | `None` | The read-only percentage of progress that has been made. This is `None` if the `total` hasn't been set. | | `progress` | `float` | `0` | The number of steps of progress already made. | -| `total` | `float | None` | The total number of steps that we are keeping track of. | +| `total` | `float` | `None` | The total number of steps that we are keeping track of. | ## Messages diff --git a/src/textual/color.py b/src/textual/color.py index acfba0a121..1339e81600 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -393,9 +393,9 @@ def hsl_blend( This method calculates a new color on a gradient using the HSL color space. The position on the gradient is given by `factor`, which is a float between -1 and 1, where 0 is the original color, and 1 or -1 is the `destination` color. - A negative `factor` affects the direction of the hue angle, a positive number is in the clockwise direction, a negative number is in the counter-clockwise direction. - For lightess and saturation, only the absolute value of `factor` is used. - A value of `gradient` between the two extremes produces a color somewhere between the two end points. + The sign of `factor` affects the direction of the hue angle, a positive number is in the clockwise direction, a negative number is in the counter-clockwise direction. + For lightness and saturation, only the absolute value of `factor` is used. + A value of `factor` between the two extremes produces a color somewhere between the two end points. Args: destination: Another color. @@ -405,10 +405,10 @@ def hsl_blend( Returns: A new color. """ - abs_factor = factor if factor >= 0 else -1.0 * factor + abs_factor = abs(factor) if factor == 0: return self - elif factor >= 1 or factor <= -1: + elif abs_factor >= 1: return destination hsl_1 = self.hsl @@ -421,19 +421,11 @@ def hsl_blend( else: new_alpha = alpha - # When the factor is > 0, hue is clockwise, otherwise it is counter-clockwise. - if factor > 0: - if hsl_1.h <= hsl_2.h: - new_h = hsl_1.h + (hsl_2.h - hsl_1.h) * abs_factor - else: - new_h = hsl_1.h + (hsl_2.h + 1.0 - hsl_1.h) * abs_factor - new_h = new_h - 1.0 if new_h >= 1.0 else new_h + sign = 1 if factor > 0 else -1 + if (sign * hsl_1.h) <= (sign * hsl_2.h): + new_h = hsl_1.h + (hsl_2.h - hsl_1.h) * abs_factor else: - if hsl_1.h >= hsl_2.h: - new_h = hsl_1.h + (hsl_2.h - hsl_1.h) * abs_factor - else: - new_h = (hsl_1.h + (hsl_2.h - 1.0 - hsl_1.h) * abs_factor) - new_h = new_h + 1.0 if new_h < 0.0 else new_h + new_h = (hsl_1.h + (hsl_2.h + sign - hsl_1.h) * abs_factor) % 1 new_s = hsl_1.s + (hsl_2.s - hsl_1.s) * abs_factor new_l = hsl_1.l + (hsl_2.l - hsl_1.l) * abs_factor diff --git a/src/textual/renderables/bar.py b/src/textual/renderables/bar.py index 1c9b6a1134..0dcc0ac3d5 100644 --- a/src/textual/renderables/bar.py +++ b/src/textual/renderables/bar.py @@ -1,10 +1,21 @@ from __future__ import annotations +from typing_extensions import Literal + from rich.console import Console, ConsoleOptions, RenderResult from rich.style import StyleType from rich.text import Text +BarThickness = Literal[0, 1, 2] +"""The values of the valid bar thicknesses. + +These are the thicknesses that can be used with a [`Bar`][textual.widgets.Bar]. +""" +_VALID_BAR_THICKNESSES = {0, 1, 2} +_DEFAULT_BAR_THICKNESS = 1 + + class Bar: """Thin horizontal bar with a portion highlighted. @@ -22,12 +33,14 @@ def __init__( background_style: StyleType = "grey37", clickable_ranges: dict[str, tuple[int, int]] | None = None, width: int | None = None, + thickness: BarThickness = _DEFAULT_BAR_THICKNESS, ) -> None: self.highlight_range = highlight_range self.highlight_style = highlight_style self.background_style = background_style self.clickable_ranges = clickable_ranges or {} self.width = width + self.thickness = thickness def __rich_console__( self, console: Console, options: ConsoleOptions @@ -35,9 +48,7 @@ def __rich_console__( highlight_style = console.get_style(self.highlight_style) background_style = console.get_style(self.background_style) - half_bar_right = "╸" - half_bar_left = "╺" - bar = "━" + bar, half_bar_right, half_bar_left = self._bar_characters width = self.width or options.max_width start, end = self.highlight_range @@ -95,6 +106,15 @@ def __rich_console__( yield output_bar + @property + def _bar_characters(self) -> tuple(str, str, str): + bar_code = [ + ("─", "╴", "╶"), + ("━", "╸", "╺"), + ("█", "▌", "▐"), + ] + return bar_code[self.thickness] + if __name__ == "__main__": import random @@ -130,7 +150,7 @@ def frange(start, end, step): from rich.live import Live - bar = Bar(highlight_range=(0, 4.5), width=80) + bar = Bar(highlight_range=(0, 4.5), width=80, thickness=1) with Live(bar, refresh_per_second=60) as live: while True: bar.highlight_range = ( diff --git a/src/textual/widgets/_progress_bar.py b/src/textual/widgets/_progress_bar.py index ec8c1b22cb..578aaf28ee 100644 --- a/src/textual/widgets/_progress_bar.py +++ b/src/textual/widgets/_progress_bar.py @@ -5,15 +5,23 @@ from math import ceil from time import monotonic from typing import Callable, Optional +from typing_extensions import Literal from rich.style import Style from .._types import UnusedParameter from ..app import ComposeResult, RenderResult +from ..color import Color from ..containers import Horizontal +from ..css._error_tools import friendly_list from ..geometry import clamp from ..reactive import reactive from ..renderables.bar import Bar as BarRenderable +from ..renderables.bar import ( + BarThickness, + _DEFAULT_BAR_THICKNESS, + _VALID_BAR_THICKNESSES, +) from ..timer import Timer from ..widget import Widget from ..widgets import Label @@ -22,6 +30,23 @@ """Sentinel for method signatures.""" +BarColorScheme = Literal["default", "rainbow"] +"""The names of the valid bar color schemes. + +These are the color schemes that can be used with a [`Bar`][textual.widgets.Bar]. +""" +_VALID_BAR_COLOR_SCHEMES = {"default", "rainbow"} +_DEFAULT_BAR_COLOR_SCHEME = "default" + + +class InvalidBarColorScheme(Exception): + """Exception raised if an invalid bar color scheme is used.""" + + +class InvalidBarThickness(Exception): + """Exception raised if an invalid bar thickness is used.""" + + class Bar(Widget, can_focus=False): """The bar portion of the progress bar.""" @@ -62,9 +87,15 @@ class Bar(Widget, can_focus=False): """The percentage of progress that has been completed.""" _start_time: float | None """The time when the widget started tracking progress.""" + color_scheme = reactive(_DEFAULT_BAR_COLOR_SCHEME) + """The color scheme of the bar.""" + thickness = reactive(_DEFAULT_BAR_THICKNESS) + """The thickness of the bar.""" def __init__( self, + color_scheme: BarColorScheme | None = None, + thickness: BarThickness | None = None, name: str | None = None, id: str | None = None, classes: str | None = None, @@ -74,6 +105,26 @@ def __init__( super().__init__(name=name, id=id, classes=classes, disabled=disabled) self._start_time = None self._percentage = None + self.color_scheme = self.validate_color_scheme(color_scheme) + self.thickness = self.validate_thickness(thickness) + + def validate_color_scheme(self, color_scheme: BarColorScheme) -> BarColorScheme: + if color_scheme is None: + color_scheme = _DEFAULT_BAR_COLOR_SCHEME + if color_scheme not in _VALID_BAR_COLOR_SCHEMES: + raise InvalidBarColorScheme( + f"Valid bar color schemes are {friendly_list(_VALID_BAR_COLOR_SCHEMES)}" + ) + return color_scheme + + def validate_thickness(self, thickness: BarThickness) -> BarThickness: + if thickness is None: + thickness = _DEFAULT_BAR_THICKNESS + if thickness not in _VALID_BAR_THICKNESSES: + raise InvalidBarThickness( + f"Valid thicknesses are {friendly_list(_VALID_BAR_THICKNESSES)}" + ) + return thickness def watch__percentage(self, percentage: float | None) -> None: """Manage the timer that enables the indeterminate bar animation.""" @@ -87,15 +138,13 @@ def render(self) -> RenderResult: if self._percentage is None: return self.render_indeterminate() else: - bar_style = ( - self.get_component_rich_style("bar--bar") - if self._percentage < 1 - else self.get_component_rich_style("bar--complete") - ) + bar_style = self._get_bar_style() + return BarRenderable( highlight_range=(0, self.size.width * self._percentage), highlight_style=Style.from_color(bar_style.color), background_style=Style.from_color(bar_style.bgcolor), + thickness=self.thickness, ) def render_indeterminate(self) -> RenderResult: @@ -119,8 +168,26 @@ def render_indeterminate(self) -> RenderResult: highlight_range=(max(0, start), min(end, width)), highlight_style=Style.from_color(bar_style.color), background_style=Style.from_color(bar_style.bgcolor), + thickness=self.thickness, ) + def _get_bar_style(self): + if self.color_scheme == "default": + return ( + self.get_component_rich_style("bar--bar") + if self._percentage < 1 + else self.get_component_rich_style("bar--complete") + ) + elif self.color_scheme == "rainbow": + from_color = self.get_component_rich_style("bar--bar").color + target_color = self.get_component_rich_style("bar--complete").color + bar_color = ( + Color.from_rich_color(from_color) + .hsl_blend(Color.from_rich_color(target_color), -1.0 * self._percentage) + .rich_color + ) + return Style.from_color(bar_color) + def _get_elapsed_time(self) -> float: """Get time for the indeterminate progress animation. @@ -299,6 +366,8 @@ class ProgressBar(Widget, can_focus=False): def __init__( self, total: float | None = None, + color_scheme: BarColorScheme | None = None, + thickness: BarThickness | None = None, *, show_bar: bool = True, show_percentage: bool = True, @@ -324,6 +393,8 @@ def key_space(self): Args: total: The total number of steps in the progress if known. + color_scheme: Progress bar color scheme. If not set default scheme will be used. + thickness: Progress bar thickness. If not set default thickness will be used. show_bar: Whether to show the bar portion of the progress bar. show_percentage: Whether to show the percentage status of the bar. show_eta: Whether to show the ETA countdown of the progress bar. @@ -338,6 +409,8 @@ def key_space(self): self.show_eta = show_eta self.total = total + self._color_scheme = color_scheme + self._thickness = thickness def compose(self) -> ComposeResult: # We create a closure so that we can determine what are the sub-widgets @@ -354,7 +427,9 @@ def updater(percentage: float | None) -> None: with Horizontal(): if self.show_bar: - bar = Bar(id="bar") + bar = Bar( + id="bar", color_scheme=self._color_scheme, thickness=self._thickness + ) self.watch(self, "percentage", update_percentage(bar)) yield bar if self.show_percentage: diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 98c66c4729..a1624a3cb2 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -24739,6 +24739,326 @@ ''' # --- +# name: test_progress_bar_in_rainbow_style_high + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + StyledExtProgressBar + + + + + + + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╺━━90%--:--:-- + + + + + + + + + + + +  S  Start  + + + + + ''' +# --- +# name: test_progress_bar_in_rainbow_style_low + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + StyledExtProgressBar + + + + + + + + + + + + + + + + + + + + + ━━━╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━10%--:--:-- + + + + + + + + + + + +  S  Start  + + + + + ''' +# --- # name: test_progress_bar_indeterminate ''' @@ -25059,6 +25379,489 @@ ''' # --- +# name: test_progress_bar_thickness_default + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + StyledExtProgressBar + + + + + + + + + + + + + + + + + + + + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━50%--:--:-- + + + + + + + + + + + +  S  Start  + + + + + ''' +# --- +# name: test_progress_bar_thickness_thick + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + StyledExtProgressBar + + + + + + + + + + + + + + + + + + + + + ███████████████████████████████50%--:--:-- + + + + + + + + + + + +  S  Start  + + + + + ''' +# --- +# name: test_progress_bar_thickness_thin + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + StyledExtProgressBar + + + + + + + + + + + + + + + + + + + + + ───────────────────────────────50%--:--:-- + + + + + + + + + + + +  S  Start  + + + + + ''' +# --- # name: test_quickly_change_tabs ''' diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 7797a8c2bd..f079a2a020 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -315,6 +315,23 @@ def test_progress_bar_completed(snap_compare): def test_progress_bar_completed_styled(snap_compare): assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_.py", press=["u"]) +def test_progress_bar_in_rainbow_style_low(snap_compare): + assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_rainbow_.py", press=["1"]) + +def test_progress_bar_in_rainbow_style_high(snap_compare): + assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_rainbow_.py", press=["5"]) + +def test_progress_bar_in_rainbow_style_high(snap_compare): + assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_rainbow_.py", press=["9"]) + +def test_progress_bar_thickness_thin(snap_compare): + assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_thickness_.py", press=["0"]) + +def test_progress_bar_thickness_default(snap_compare): + assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_thickness_.py", press=["1"]) + +def test_progress_bar_thickness_thick(snap_compare): + assert snap_compare(WIDGET_EXAMPLES_DIR / "progress_bar_styled_thickness_.py", press=["2"]) def test_rule_horizontal_rules(snap_compare): assert snap_compare(WIDGET_EXAMPLES_DIR / "horizontal_rules.py")