use std::time::Instant;
use iced_core::{widget::Tree, Element, Rectangle, Widget};
use crate::{Animate, Animated, Event};
pub struct Animation<'a, T: Animate, Message, Theme, Renderer> {
animated_value: &'a Animated<T>,
content: Element<'a, Message, Theme, Renderer>,
on_update: Option<Box<dyn Fn(Event<T>) -> Message>>,
is_disabled: bool,
}
impl<'a, T, Message, Theme, Renderer> Animation<'a, T, Message, Theme, Renderer>
where
T: 'static + Animate,
Message: 'a,
{
pub fn new(
animated_value: &'a Animated<T>,
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self {
Self {
animated_value,
content: content.into(),
on_update: None,
is_disabled: false,
}
}
pub fn on_update<F>(mut self, build_message: F) -> Self
where
F: Fn(Event<T>) -> Message + 'static,
{
self.on_update = Some(Box::new(build_message));
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.is_disabled = disabled;
self
}
}
impl<'a, T, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Animation<'a, T, Message, Theme, Renderer>
where
T: 'static + Animate,
Message: 'a,
Renderer: 'a + iced_core::Renderer,
{
fn size(&self) -> iced_core::Size<iced_core::Length> {
self.content.as_widget().size()
}
fn size_hint(&self) -> iced_core::Size<iced_core::Length> {
self.content.as_widget().size_hint()
}
fn children(&self) -> Vec<iced_core::widget::Tree> {
vec![Tree::new(&self.content)]
}
fn diff(&self, tree: &mut iced_core::widget::Tree) {
tree.diff_children(std::slice::from_ref(&self.content));
}
fn mouse_interaction(
&self,
tree: &iced_core::widget::Tree,
layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor,
viewport: &iced_core::Rectangle,
renderer: &Renderer,
) -> iced_core::mouse::Interaction {
self.content.as_widget().mouse_interaction(
&tree.children[0],
layout,
cursor,
viewport,
renderer,
)
}
fn operate(
&mut self,
state: &mut iced_core::widget::Tree,
layout: iced_core::Layout<'_>,
renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<()>,
) {
self.content
.as_widget_mut()
.operate(&mut state.children[0], layout, renderer, operation);
}
fn state(&self) -> iced_core::widget::tree::State {
iced_core::widget::tree::State::None
}
fn overlay<'b>(
&'b mut self,
tree: &'b mut iced_core::widget::Tree,
layout: iced_core::Layout<'b>,
renderer: &Renderer,
viewport: &Rectangle,
translation: iced_core::Vector,
) -> Option<iced_core::overlay::Element<'b, Message, Theme, Renderer>> {
self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout,
renderer,
viewport,
translation,
)
}
fn layout(
&mut self,
tree: &mut iced_core::widget::Tree,
renderer: &Renderer,
limits: &iced_core::layout::Limits,
) -> iced_core::layout::Node {
self.content
.as_widget_mut()
.layout(&mut tree.children[0], renderer, limits)
}
fn draw(
&self,
tree: &iced_core::widget::Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &iced_core::renderer::Style,
layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor,
viewport: &iced_core::Rectangle,
) {
self.content.as_widget().draw(
&tree.children[0],
renderer,
theme,
style,
layout,
cursor,
viewport,
)
}
fn update(
&mut self,
tree: &mut iced_core::widget::Tree,
event: &iced_core::Event,
layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn iced_core::Clipboard,
shell: &mut iced_core::Shell<'_, Message>,
viewport: &iced_core::Rectangle,
) {
self.content.as_widget_mut().update(
&mut tree.children[0],
event,
layout,
cursor,
renderer,
clipboard,
shell,
viewport,
);
if !self.animated_value.is_animating() {
return;
}
if let Some(on_update) = &self.on_update {
let event: Event<T> = if self.is_disabled {
Event::Settle
} else {
let now = Instant::now();
Event::Tick(now)
};
shell.publish(on_update(event));
}
}
}
impl<'a, T, Message, Theme, Renderer> From<Animation<'a, T, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
T: 'static + Animate,
Message: 'a,
Theme: 'a,
Renderer: iced_core::Renderer + 'a,
{
fn from(animation: Animation<'a, T, Message, Theme, Renderer>) -> Self {
Self::new(animation)
}
}
pub fn animation<'a, T, Message, Theme, Renderer>(
value: &'a Animated<T>,
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Animation<'a, T, Message, Theme, Renderer>
where
T: 'static + Animate,
Message: 'a,
{
Animation::new(value, content)
}