ai-station/.venv/lib/python3.12/site-packages/textual/widgets/_switch.py

205 lines
5.8 KiB
Python

from __future__ import annotations
from typing import TYPE_CHECKING, ClassVar
from rich.console import RenderableType
if TYPE_CHECKING:
from textual.app import RenderResult
from typing_extensions import Self
from textual.binding import Binding, BindingType
from textual.events import Click
from textual.geometry import Size
from textual.message import Message
from textual.reactive import reactive
from textual.scrollbar import ScrollBarRender
from textual.widget import Widget
class Switch(Widget, can_focus=True):
"""A switch widget that represents a boolean value.
Can be toggled by clicking on it or through its [bindings][textual.widgets.Switch.BINDINGS].
The switch widget also contains [component classes][textual.widgets.Switch.COMPONENT_CLASSES]
that enable more customization.
"""
BINDINGS: ClassVar[list[BindingType]] = [
Binding("enter,space", "toggle_switch", "Toggle", show=False),
]
"""
| Key(s) | Description |
| :- | :- |
| enter,space | Toggle the switch state. |
"""
COMPONENT_CLASSES: ClassVar[set[str]] = {
"switch--slider",
}
"""
| Class | Description |
| :- | :- |
| `switch--slider` | Targets the slider of the switch. |
"""
DEFAULT_CSS = """
Switch {
border: tall $border-blurred;
background: $surface;
height: auto;
width: auto;
padding: 0 2;
&.-on .switch--slider {
color: $success;
}
& .switch--slider {
color: $panel;
background: $panel-darken-2;
}
&:hover {
& > .switch--slider {
color: $panel-lighten-1
}
&.-on > .switch--slider {
color: $success-lighten-1;
}
}
&:focus {
border: tall $border;
background-tint: $foreground 5%;
}
&:light {
&.-on .switch--slider {
color: $success;
}
& .switch--slider {
color: $primary 15%;
background: $panel-darken-2;
}
&:hover {
& > .switch--slider {
color: $primary 25%;
}
&.-on > .switch--slider {
color: $success-lighten-1;
}
}
}
}
"""
value: reactive[bool] = reactive(False, init=False)
"""The value of the switch; `True` for on and `False` for off."""
_slider_position = reactive(0.0)
"""The position of the slider."""
class Changed(Message):
"""Posted when the status of the switch changes.
Can be handled using `on_switch_changed` in a subclass of `Switch`
or in a parent widget in the DOM.
Attributes:
value: The value that the switch was changed to.
switch: The `Switch` widget that was changed.
"""
def __init__(self, switch: Switch, value: bool) -> None:
super().__init__()
self.value: bool = value
self.switch: Switch = switch
@property
def control(self) -> Switch:
"""Alias for self.switch."""
return self.switch
def __init__(
self,
value: bool = False,
*,
animate: bool = True,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
tooltip: RenderableType | None = None,
):
"""Initialise the switch.
Args:
value: The initial value of the switch.
animate: True if the switch should animate when toggled.
name: The name of the switch.
id: The ID of the switch in the DOM.
classes: The CSS classes of the switch.
disabled: Whether the switch is disabled or not.
tooltip: Optional tooltip.
"""
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
if value:
self._slider_position = 1.0
self.set_reactive(Switch.value, value)
self._should_animate = animate
if tooltip is not None:
self.tooltip = tooltip
def watch_value(self, value: bool) -> None:
target_slider_position = 1.0 if value else 0.0
if self._should_animate:
self.animate(
"_slider_position",
target_slider_position,
duration=0.3,
level="basic",
)
else:
self._slider_position = target_slider_position
self.post_message(self.Changed(self, self.value))
def watch__slider_position(self, slider_position: float) -> None:
self.set_class(slider_position == 1, "-on")
def render(self) -> RenderResult:
style = self.get_component_rich_style("switch--slider")
return ScrollBarRender(
virtual_size=100,
window_size=50,
position=self._slider_position * 50,
style=style,
vertical=False,
)
def get_content_width(self, container: Size, viewport: Size) -> int:
return 4
def get_content_height(self, container: Size, viewport: Size, width: int) -> int:
return 1
async def _on_click(self, event: Click) -> None:
"""Toggle the state of the switch."""
event.stop()
self.toggle()
def action_toggle_switch(self) -> None:
"""Toggle the state of the switch."""
self.toggle()
def toggle(self) -> Self:
"""Toggle the switch value.
As a result of the value changing, a `Switch.Changed` message will
be posted.
Returns:
The `Switch` instance.
"""
self.value = not self.value
return self