Add on_complete callback to Animations

This commit is contained in:
Darren Burns
2022-08-16 13:50:11 +01:00
parent e94ea25faf
commit 50e2baba54
3 changed files with 37 additions and 32 deletions

View File

@@ -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)

View File

@@ -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:

View File

@@ -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