mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Add on_complete callback to Animations
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from rich.console import RenderableType
|
||||
from rich.panel import Panel
|
||||
|
||||
from textual import events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.layout import Horizontal, Vertical
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
@@ -18,37 +16,22 @@ class Box(Widget, can_focus=True):
|
||||
super().__init__(*children, id=id, classes=classes)
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return Panel("Box")
|
||||
return "Box"
|
||||
|
||||
|
||||
class JustABox(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Horizontal(
|
||||
Vertical(
|
||||
Box(id="box1", classes="box"),
|
||||
Box(id="box2", classes="box"),
|
||||
# Box(id="box3", classes="box"),
|
||||
# Box(id="box4", classes="box"),
|
||||
# Box(id="box5", classes="box"),
|
||||
# Box(id="box6", classes="box"),
|
||||
# Box(id="box7", classes="box"),
|
||||
# Box(id="box8", classes="box"),
|
||||
# Box(id="box9", classes="box"),
|
||||
# Box(id="box10", classes="box"),
|
||||
id="left_pane",
|
||||
),
|
||||
Box(id="middle_pane"),
|
||||
Vertical(
|
||||
Box(id="boxa", classes="box"),
|
||||
Box(id="boxb", classes="box"),
|
||||
Box(id="boxc", classes="box"),
|
||||
id="right_pane",
|
||||
),
|
||||
id="horizontal",
|
||||
)
|
||||
self.box = Box()
|
||||
yield self.box
|
||||
|
||||
def key_p(self):
|
||||
print(self.query("#horizontal").first().styles.layout)
|
||||
def key_a(self):
|
||||
self.animator.animate(
|
||||
self.box.styles,
|
||||
"opacity",
|
||||
value=0.0,
|
||||
duration=2.0,
|
||||
on_complete=self.box.remove,
|
||||
)
|
||||
|
||||
async def on_key(self, event: events.Key) -> None:
|
||||
await self.dispatch_key(event)
|
||||
|
||||
@@ -30,8 +30,20 @@ class Animatable(Protocol):
|
||||
|
||||
|
||||
class Animation(ABC):
|
||||
|
||||
on_complete: Callable[[], None] | None = None
|
||||
"""Callback to run after animation completes"""
|
||||
|
||||
@abstractmethod
|
||||
def __call__(self, time: float) -> bool: # pragma: no cover
|
||||
"""Call the animation, return a boolean indicating whether animation is in-progress or complete.
|
||||
|
||||
Args:
|
||||
time (float): The current timestamp
|
||||
|
||||
Returns:
|
||||
bool: True if the animation has finished, otherwise False.
|
||||
"""
|
||||
raise NotImplementedError("")
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
@@ -48,6 +60,7 @@ class SimpleAnimation(Animation):
|
||||
end_value: float | Animatable
|
||||
final_value: object
|
||||
easing: EasingFunction
|
||||
on_complete: Callable[[], None] | None = None
|
||||
|
||||
def __call__(self, time: float) -> bool:
|
||||
|
||||
@@ -109,6 +122,7 @@ class BoundAnimator:
|
||||
duration: float | None = None,
|
||||
speed: float | None = None,
|
||||
easing: EasingFunction | str = DEFAULT_EASING,
|
||||
on_complete: Callable[[], None] | None = None,
|
||||
) -> None:
|
||||
easing_function = EASING[easing] if isinstance(easing, str) else easing
|
||||
return self._animator.animate(
|
||||
@@ -119,6 +133,7 @@ class BoundAnimator:
|
||||
duration=duration,
|
||||
speed=speed,
|
||||
easing=easing_function,
|
||||
on_complete=on_complete,
|
||||
)
|
||||
|
||||
|
||||
@@ -163,6 +178,7 @@ class Animator:
|
||||
duration: float | None = None,
|
||||
speed: float | None = None,
|
||||
easing: EasingFunction | str = DEFAULT_EASING,
|
||||
on_complete: Callable[[], None] | None = None,
|
||||
) -> None:
|
||||
"""Animate an attribute to a new value.
|
||||
|
||||
@@ -201,6 +217,7 @@ class Animator:
|
||||
duration=duration,
|
||||
speed=speed,
|
||||
easing=easing_function,
|
||||
on_complete=on_complete,
|
||||
)
|
||||
if animation is None:
|
||||
start_value = getattr(obj, attribute)
|
||||
@@ -223,6 +240,7 @@ class Animator:
|
||||
end_value=value,
|
||||
final_value=final_value,
|
||||
easing=easing_function,
|
||||
on_complete=on_complete,
|
||||
)
|
||||
assert animation is not None, "animation expected to be non-None"
|
||||
|
||||
@@ -241,7 +259,11 @@ class Animator:
|
||||
animation_keys = list(self._animations.keys())
|
||||
for animation_key in animation_keys:
|
||||
animation = self._animations[animation_key]
|
||||
if animation(animation_time):
|
||||
animation_complete = animation(animation_time)
|
||||
if animation_complete:
|
||||
completion_callback = animation.on_complete
|
||||
if completion_callback is not None:
|
||||
completion_callback()
|
||||
del self._animations[animation_key]
|
||||
|
||||
def _get_time(self) -> float:
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Callable
|
||||
|
||||
from .. import events, log
|
||||
from ..geometry import Offset
|
||||
from .._animator import Animation
|
||||
from .scalar import ScalarOffset
|
||||
@@ -25,6 +24,7 @@ class ScalarAnimation(Animation):
|
||||
duration: float | None,
|
||||
speed: float | None,
|
||||
easing: EasingFunction,
|
||||
on_complete: Callable[[], None] | None = None,
|
||||
):
|
||||
assert (
|
||||
speed is not None or duration is not None
|
||||
@@ -35,6 +35,7 @@ class ScalarAnimation(Animation):
|
||||
self.attribute = attribute
|
||||
self.final_value = value
|
||||
self.easing = easing
|
||||
self.on_complete = on_complete
|
||||
|
||||
size = widget.outer_size
|
||||
viewport = widget.app.size
|
||||
@@ -55,7 +56,6 @@ class ScalarAnimation(Animation):
|
||||
eased_factor = self.easing(factor)
|
||||
|
||||
if eased_factor >= 1:
|
||||
offset = self.final_value
|
||||
setattr(self.styles, self.attribute, self.final_value)
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user