diff --git a/src/textual/renderables/bar.py b/src/textual/renderables/bar.py index 2fa31550fc..558ea96e61 100644 --- a/src/textual/renderables/bar.py +++ b/src/textual/renderables/bar.py @@ -18,6 +18,10 @@ class Bar: gradient: Optional gradient object. """ + HALF_BAR_LEFT: str = "╺" + BAR: str = "━" + HALF_BAR_RIGHT: str = "╸" + def __init__( self, highlight_range: tuple[float, float] = (0, 0), @@ -40,10 +44,6 @@ 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 = "━" - width = self.width or options.max_width start, end = self.highlight_range @@ -53,7 +53,7 @@ def __rich_console__( output_bar = Text("", end="") if start == end == 0 or end < 0 or start > end: - output_bar.append(Text(bar * width, style=background_style, end="")) + output_bar.append(Text(self.BAR * width, style=background_style, end="")) yield output_bar return @@ -67,10 +67,10 @@ def __rich_console__( # Initial non-highlighted portion of bar output_bar.append( - Text(bar * (int(start - 0.5)), style=background_style, end="") + Text(self.BAR * (int(start - 0.5)), style=background_style, end="") ) if not half_start and start > 0: - output_bar.append(Text(half_bar_right, style=background_style, end="")) + output_bar.append(Text(self.HALF_BAR_RIGHT, style=background_style, end="")) highlight_bar = Text("", end="") # The highlighted portion @@ -78,13 +78,19 @@ def __rich_console__( if half_start: highlight_bar.append( Text( - half_bar_left + bar * (bar_width - 1), style=highlight_style, end="" + self.HALF_BAR_LEFT + self.BAR * (bar_width - 1), + style=highlight_style, + end="", ) ) else: - highlight_bar.append(Text(bar * bar_width, style=highlight_style, end="")) + highlight_bar.append( + Text(self.BAR * bar_width, style=highlight_style, end="") + ) if half_end: - highlight_bar.append(Text(half_bar_right, style=highlight_style, end="")) + highlight_bar.append( + Text(self.HALF_BAR_RIGHT, style=highlight_style, end="") + ) if self.gradient is not None: _apply_gradient(highlight_bar, self.gradient, width) @@ -92,9 +98,9 @@ def __rich_console__( # The non-highlighted tail if not half_end and end - width != 0: - output_bar.append(Text(half_bar_left, style=background_style, end="")) + output_bar.append(Text(self.HALF_BAR_LEFT, style=background_style, end="")) output_bar.append( - Text(bar * (int(width) - int(end) - 1), style=background_style, end="") + Text(self.BAR * (int(width) - int(end) - 1), style=background_style, end="") ) # Fire actions when certain ranges are clicked (e.g. for tabs) diff --git a/src/textual/widgets/_progress_bar.py b/src/textual/widgets/_progress_bar.py index a1c0260b8e..4da6ebab4e 100644 --- a/src/textual/widgets/_progress_bar.py +++ b/src/textual/widgets/_progress_bar.py @@ -72,11 +72,13 @@ def __init__( disabled: bool = False, clock: Clock | None = None, gradient: Gradient | None = None, + bar_renderable: BarRenderable = BarRenderable, ): """Create a bar for a [`ProgressBar`][textual.widgets.ProgressBar].""" self._clock = (clock or Clock()).clone() super().__init__(name=name, id=id, classes=classes, disabled=disabled) self.set_reactive(Bar.gradient, gradient) + self.bar_renderable = bar_renderable def _validate_percentage(self, percentage: float | None) -> float | None: """Avoid updating the bar, if the percentage increase is too small to render.""" @@ -104,7 +106,7 @@ def render(self) -> RenderResult: if self.percentage < 1 else self.get_component_rich_style("bar--complete") ) - return BarRenderable( + return self.bar_renderable( highlight_range=(0, self.size.width * self.percentage), highlight_style=Style.from_color(bar_style.color), background_style=Style.from_color(bar_style.bgcolor), @@ -133,7 +135,7 @@ def render_indeterminate(self) -> RenderResult: end = start + highlighted_bar_width bar_style = self.get_component_rich_style("bar--indeterminate") - return BarRenderable( + return self.bar_renderable( highlight_range=(max(0, start), min(end, width)), highlight_style=Style.from_color(bar_style.color), background_style=Style.from_color(bar_style.bgcolor), @@ -239,6 +241,7 @@ def __init__( disabled: bool = False, clock: Clock | None = None, gradient: Gradient | None = None, + bar_renderable: BarRenderable = BarRenderable, ): """Create a Progress Bar widget. @@ -265,6 +268,7 @@ def key_space(self): disabled: Whether the widget is disabled or not. clock: An optional clock object (leave as default unless testing). gradient: An optional Gradient object (will replace CSS styles in the bar). + bar_renderable: A custom Bar object that is rendered as the bar. """ self._clock = clock or Clock() self._eta = ETA() @@ -274,6 +278,7 @@ def key_space(self): self.show_percentage = show_percentage self.show_eta = show_eta self.set_reactive(ProgressBar.gradient, gradient) + self.bar_renderable = bar_renderable def on_mount(self) -> None: self.update() @@ -283,7 +288,7 @@ def on_mount(self) -> None: def compose(self) -> ComposeResult: if self.show_bar: yield ( - Bar(id="bar", clock=self._clock) + Bar(id="bar", clock=self._clock, bar_renderable=self.bar_renderable) .data_bind(ProgressBar.percentage) .data_bind(ProgressBar.gradient) )