mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Swap to overriding render_line
More experimenting with overriding OptionList, and rather than trying to swap out and around the prompt under the hood, I got to thinking that it made more sense to perhaps override render_line. So far so good...
This commit is contained in:
@@ -4,11 +4,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import ClassVar, Generic, TypeVar
|
from typing import ClassVar, Generic, TypeVar
|
||||||
|
|
||||||
from rich.console import RenderableType
|
from rich.segment import Segment
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
from rich.text import Text, TextType
|
from rich.text import Text, TextType
|
||||||
|
|
||||||
from ..binding import Binding
|
from ..binding import Binding
|
||||||
|
from ..strip import Strip
|
||||||
from ._option_list import Option, OptionList
|
from ._option_list import Option, OptionList
|
||||||
from ._toggle_button import ToggleButton
|
from ._toggle_button import ToggleButton
|
||||||
|
|
||||||
@@ -36,7 +37,6 @@ class Selection(Generic[SelectionType], Option):
|
|||||||
id: The optional ID for the selection.
|
id: The optional ID for the selection.
|
||||||
disabled: The initial enabled/disabled state. Enabled by default.
|
disabled: The initial enabled/disabled state. Enabled by default.
|
||||||
"""
|
"""
|
||||||
self._prompt = prompt
|
|
||||||
self._parent = parent
|
self._parent = parent
|
||||||
super().__init__(prompt, id, disabled)
|
super().__init__(prompt, id, disabled)
|
||||||
self._value: SelectionType = value
|
self._value: SelectionType = value
|
||||||
@@ -46,10 +46,6 @@ class Selection(Generic[SelectionType], Option):
|
|||||||
"""The value for this selection."""
|
"""The value for this selection."""
|
||||||
return self._value
|
return self._value
|
||||||
|
|
||||||
@property
|
|
||||||
def prompt(self) -> RenderableType:
|
|
||||||
return self._parent._make_label(self)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selected(self) -> bool:
|
def selected(self) -> bool:
|
||||||
return self._value in self._parent._selected
|
return self._value in self._parent._selected
|
||||||
@@ -58,34 +54,51 @@ class Selection(Generic[SelectionType], Option):
|
|||||||
class SelectionList(Generic[SelectionType], OptionList):
|
class SelectionList(Generic[SelectionType], OptionList):
|
||||||
"""A vertical option list that allows making multiple selections."""
|
"""A vertical option list that allows making multiple selections."""
|
||||||
|
|
||||||
BINDINGS = [Binding("space, enter", "toggle"), Binding("x", "redraw")]
|
BINDINGS = [Binding("space, enter", "toggle")]
|
||||||
|
|
||||||
COMPONENT_CLASSES: ClassVar[set[str]] = {
|
COMPONENT_CLASSES: ClassVar[set[str]] = {
|
||||||
"selection-list--button",
|
"selection-list--button",
|
||||||
"selection-list--button-selected",
|
"selection-list--button-selected",
|
||||||
|
"selection-list--button-highlighted",
|
||||||
|
"selection-list--button-selected-highlighted",
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_CSS = """
|
DEFAULT_CSS = """
|
||||||
/* Base button colours (including in dark mode). */
|
|
||||||
|
|
||||||
SelectionList > .selection-list--button {
|
SelectionList > .selection-list--button {
|
||||||
color: $background;
|
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
background: $foreground 15%;
|
background: $foreground 15%;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionList:focus > .selection-list--button {
|
SelectionList:focus > .selection-list--button {
|
||||||
background: $foreground 25%;
|
background: $foreground 25%;
|
||||||
background: red;
|
}
|
||||||
color: red;
|
|
||||||
|
SelectionList > .selection-list--button-highlighted {
|
||||||
|
text-style: bold;
|
||||||
|
background: $foreground 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionList:focus > .selection-list--button-highlighted {
|
||||||
|
text-style: bold;
|
||||||
|
background: $foreground 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionList > .selection-list--button-selected {
|
SelectionList > .selection-list--button-selected {
|
||||||
color: $success;
|
background: $foreground 15%;
|
||||||
text-style: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionList:focus > .selection-list--button-selected {
|
SelectionList:focus > .selection-list--button-selected {
|
||||||
|
color: $success;
|
||||||
|
background: $foreground 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionList > .selection-list--button-selected-highlighted {
|
||||||
|
color: $success;
|
||||||
|
background: $foreground 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionList:focus > .selection-list--button-selected-highlighted {
|
||||||
|
color: $success;
|
||||||
background: $foreground 25%;
|
background: $foreground 25%;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@@ -148,8 +161,6 @@ class SelectionList(Generic[SelectionType], OptionList):
|
|||||||
(ToggleButton.BUTTON_LEFT, side_style),
|
(ToggleButton.BUTTON_LEFT, side_style),
|
||||||
(ToggleButton.BUTTON_INNER, button_style),
|
(ToggleButton.BUTTON_INNER, button_style),
|
||||||
(ToggleButton.BUTTON_RIGHT, side_style),
|
(ToggleButton.BUTTON_RIGHT, side_style),
|
||||||
" ",
|
|
||||||
selection._prompt,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _make_selection(
|
def _make_selection(
|
||||||
@@ -186,3 +197,66 @@ class SelectionList(Generic[SelectionType], OptionList):
|
|||||||
self._selected[option._value] = None
|
self._selected[option._value] = None
|
||||||
self._refresh_content_tracking(force=True)
|
self._refresh_content_tracking(force=True)
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
def render_line(self, y: int) -> Strip:
|
||||||
|
"""Render a line in the display.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
y: The line to render.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A `Strip` that is the line to render.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# First off, get the underlying prompt from OptionList.
|
||||||
|
prompt = super().render_line(y)
|
||||||
|
|
||||||
|
# If it looks like the prompt itself is actually an empty line...
|
||||||
|
if not prompt:
|
||||||
|
# ...get out with that. We don't need to do any more here.
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
# We know the prompt we're going to display, what we're going to do
|
||||||
|
# is place a CheckBox-a-like button next to it. So to start with
|
||||||
|
# let's pull out the actual Selection we're looking at right now.
|
||||||
|
_, scroll_y = self.scroll_offset
|
||||||
|
selection_index = scroll_y + y
|
||||||
|
selection = self.get_option_at_index(selection_index)
|
||||||
|
assert isinstance(selection, Selection)
|
||||||
|
|
||||||
|
component_style = "selection-list--button"
|
||||||
|
if selection.selected:
|
||||||
|
component_style += "-selected"
|
||||||
|
if self.highlighted == selection_index:
|
||||||
|
component_style += "-highlighted"
|
||||||
|
|
||||||
|
# Get the underlying style used for the prompt.
|
||||||
|
underlying_style = next(iter(prompt)).style
|
||||||
|
assert underlying_style is not None
|
||||||
|
|
||||||
|
# Get the style for the button.
|
||||||
|
button_style = self.get_component_rich_style(component_style)
|
||||||
|
|
||||||
|
# If the button is off, we're going to do a bit of a switcharound to
|
||||||
|
# make it look like it's a "cutout".
|
||||||
|
if not selection.selected:
|
||||||
|
button_style += Style.from_color(
|
||||||
|
self.background_colors[1].rich_color, button_style.bgcolor
|
||||||
|
)
|
||||||
|
|
||||||
|
# Building the style for the side characters. Note that this is
|
||||||
|
# sensitive to the type of character used, so pay attention to
|
||||||
|
# BUTTON_LEFT and BUTTON_RIGHT.
|
||||||
|
side_style = Style.from_color(button_style.bgcolor, underlying_style.bgcolor)
|
||||||
|
|
||||||
|
# At this point we should have everything we need to place a
|
||||||
|
# "button" before the option.
|
||||||
|
return Strip(
|
||||||
|
[
|
||||||
|
Segment(ToggleButton.BUTTON_LEFT, style=side_style),
|
||||||
|
Segment(ToggleButton.BUTTON_INNER, style=button_style),
|
||||||
|
Segment(ToggleButton.BUTTON_RIGHT, style=side_style),
|
||||||
|
Segment(" ", style=underlying_style),
|
||||||
|
*prompt,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user