ai-station/.venv/lib/python3.12/site-packages/textual/css/scalar_animation.py

99 lines
3.1 KiB
Python

from __future__ import annotations
from typing import TYPE_CHECKING
from textual._animator import Animation, EasingFunction
from textual._types import AnimationLevel, CallbackType
from textual.css.scalar import Scalar, ScalarOffset
if TYPE_CHECKING:
from textual.css.styles import StylesBase
from textual.widget import Widget
class ScalarAnimation(Animation):
def __init__(
self,
widget: Widget,
styles: StylesBase,
start_time: float,
attribute: str,
value: ScalarOffset | Scalar,
duration: float | None,
speed: float | None,
easing: EasingFunction,
on_complete: CallbackType | None = None,
level: AnimationLevel = "full",
):
assert (
speed is not None or duration is not None
), "One of speed or duration required"
self.widget = widget
self.styles = styles
self.start_time = start_time
self.attribute = attribute
self.final_value = value
self.easing = easing
self.on_complete = on_complete
self.level = level
size = widget.outer_size
viewport = widget.app.size
self.start = getattr(styles, attribute).resolve(size, viewport)
self.destination = value.resolve(size, viewport)
if speed is not None:
distance = self.start.get_distance_to(self.destination)
self.duration = distance / speed
else:
assert duration is not None, "Duration expected to be non-None"
self.duration = duration
def __call__(
self, time: float, app_animation_level: AnimationLevel = "full"
) -> bool:
factor = min(1.0, (time - self.start_time) / self.duration)
eased_factor = self.easing(factor)
if (
eased_factor >= 1
or app_animation_level == "none"
or app_animation_level == "basic"
and self.level == "full"
):
setattr(self.styles, self.attribute, self.final_value)
return True
if hasattr(self.start, "blend"):
value = self.start.blend(self.destination, eased_factor)
else:
value = self.start + (self.destination - self.start) * eased_factor
current = self.styles.get_rule(self.attribute)
if current != value:
setattr(self.styles, self.attribute, value)
return False
async def stop(self, complete: bool = True) -> None:
"""Stop the animation.
Args:
complete: Flag to say if the animation should be taken to completion.
Note:
[`on_complete`][Animation.on_complete] will be called regardless
of the value provided for `complete`.
"""
if complete:
setattr(self.styles, self.attribute, self.final_value)
await self.invoke_callback()
def __eq__(self, other: object) -> bool:
if isinstance(other, ScalarAnimation):
return (
self.final_value == other.final_value
and self.duration == other.duration
)
return False