use crate::{animate::Animate, animated::Mode, Animated};
use iced::{
advanced::{
graphics::core::event,
layout,
widget::{tree, Tree},
Widget,
},
Element,
};
pub struct AnimationBuilder<'a, T, Message, Theme, Renderer>
where
T: 'static + Animate,
{
target: T,
builder: Box<dyn Fn(T) -> Element<'a, Message, Theme, Renderer> + 'a>,
mode: Mode,
animates_layout: bool,
is_disabled: bool,
cached_element: Element<'a, Message, Theme, Renderer>,
}
impl<'a, T, Message, Theme, Renderer> AnimationBuilder<'a, T, Message, Theme, Renderer>
where
T: 'static + Animate,
{
pub fn new(
target: T,
builder: impl Fn(T) -> Element<'a, Message, Theme, Renderer> + 'a,
) -> Self {
let element = (builder)(target.clone());
Self {
target,
builder: Box::new(builder),
cached_element: element,
mode: Mode::default(),
animates_layout: false,
is_disabled: false,
}
}
pub fn animation(mut self, mode: impl Into<Mode>) -> Self {
self.mode = mode.into();
self
}
pub fn animates_layout(mut self, animates_layout: bool) -> Self {
self.animates_layout = animates_layout;
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.is_disabled = disabled;
self
}
}
impl<'a, T, Message, Theme, Renderer> From<AnimationBuilder<'a, T, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
T: 'static + Animate,
Message: Clone + 'a,
Theme: 'a,
Renderer: iced::advanced::Renderer + 'a,
{
fn from(animation: AnimationBuilder<'a, T, Message, Theme, Renderer>) -> Self {
Self::new(animation)
}
}
struct State<T> {
animation: Animated<T>,
mode: Mode,
}
impl<T, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for AnimationBuilder<'_, T, Message, Theme, Renderer>
where
T: 'static + Animate,
Renderer: iced::advanced::Renderer,
{
fn size(&self) -> iced::Size<iced::Length> {
self.cached_element.as_widget().size()
}
fn state(&self) -> tree::State {
tree::State::new(State {
animation: Animated::new(self.target.clone(), self.mode),
mode: self.mode,
})
}
fn tag(&self) -> tree::Tag {
tree::Tag::of::<State<T>>()
}
fn diff(&self, tree: &mut Tree) {
let state = tree.state.downcast_mut::<State<T>>();
if state.animation.target() != &self.target {
if self.is_disabled {
state.animation.settle();
} else {
state.animation.set_target(self.target.clone());
}
}
if state.mode != self.mode {
state.mode = self.mode;
state.animation.apply(self.mode);
}
tree.diff_children(std::slice::from_ref(&self.cached_element));
}
fn layout(
&self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.cached_element
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
}
fn operate(
&self,
state: &mut Tree,
layout: layout::Layout<'_>,
renderer: &Renderer,
operation: &mut dyn iced::advanced::widget::Operation<()>,
) {
self.cached_element.as_widget().operate(
&mut state.children[0],
layout,
renderer,
operation,
);
}
fn overlay<'b>(
&'b mut self,
tree: &'b mut Tree,
layout: layout::Layout<'_>,
renderer: &Renderer,
translation: iced::Vector,
) -> Option<iced::advanced::overlay::Element<'b, Message, Theme, Renderer>> {
self.cached_element.as_widget_mut().overlay(
&mut tree.children[0],
layout,
renderer,
translation,
)
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: layout::Layout<'_>,
cursor: iced::advanced::mouse::Cursor,
viewport: &iced::Rectangle,
renderer: &Renderer,
) -> iced::advanced::mouse::Interaction {
self.cached_element.as_widget().mouse_interaction(
&tree.children[0],
layout,
cursor,
viewport,
renderer,
)
}
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.cached_element)]
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &iced::advanced::renderer::Style,
layout: iced::advanced::Layout<'_>,
cursor: iced::advanced::mouse::Cursor,
viewport: &iced::Rectangle,
) {
self.cached_element.as_widget().draw(
&tree.children[0],
renderer,
theme,
style,
layout,
cursor,
viewport,
)
}
fn on_event(
&mut self,
tree: &mut Tree,
event: iced::Event,
layout: iced::advanced::Layout<'_>,
cursor: iced::advanced::mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn iced::advanced::Clipboard,
shell: &mut iced::advanced::Shell<'_, Message>,
viewport: &iced::Rectangle,
) -> event::Status {
let status = self.cached_element.as_widget_mut().on_event(
&mut tree.children[0],
event.clone(),
layout,
cursor,
renderer,
clipboard,
shell,
viewport,
);
let iced::Event::Window(iced::window::Event::RedrawRequested(now)) = event else {
return status;
};
let state = tree.state.downcast_mut::<State<T>>();
if state.animation.is_animating() {
shell.request_redraw(iced::window::RedrawRequest::NextFrame);
if self.animates_layout {
shell.invalidate_layout();
}
state.animation.tick(now);
self.cached_element = (self.builder)(state.animation.value().clone());
}
status
}
}
pub fn animation_builder<'a, T, Message, Theme, Renderer>(
value: T,
builder: impl Fn(T) -> Element<'a, Message, Theme, Renderer> + 'a,
) -> AnimationBuilder<'a, T, Message, Theme, Renderer>
where
T: 'static + Animate,
{
AnimationBuilder::new(value, builder)
}