1use std::hash::{DefaultHasher, Hash, Hasher};
3
4use crate::core::animation::{Animation, Float};
5use crate::core::layout::{self, Layout};
6use crate::core::mouse;
7use crate::core::overlay;
8use crate::core::renderer;
9use crate::core::shell;
10use crate::core::time::Instant;
11use crate::core::widget::{self, Operation, Tree, tree};
12use crate::core::{self, Element, Event, Length, Point, Rectangle, Shell, Size, Vector, Widget};
13use crate::space;
14
15pub trait Program: 'static {
17 type Value: Copy + 'static;
19
20 fn go(&mut self, value: Self::Value, now: Instant);
23
24 fn is_animating(&self, now: Instant) -> bool;
26}
27
28impl<I> Program for Animation<I>
29where
30 I: Float + Clone + Copy + PartialEq + 'static,
31{
32 type Value = I;
33
34 fn go(&mut self, value: Self::Value, now: Instant) {
35 self.go_mut(value, now);
36 }
37
38 fn is_animating(&self, now: Instant) -> bool {
39 self.is_animating(now)
40 }
41}
42
43pub struct Transition<'a, Message, Theme, Renderer, P>
45where
46 P: Program,
47{
48 init: Box<dyn Fn() -> P + 'a>,
49 view: Box<dyn Fn(&P, Instant) -> Element<'a, Message, Theme, Renderer> + 'a>,
50 on_finish: Option<Box<dyn Fn() -> Message + 'a>>,
51 element: Element<'a, Message, Theme, Renderer>,
52 next_element: Option<Element<'a, Message, Theme, Renderer>>,
53 last_limits: layout::Limits,
54 new_layout: Option<layout::Node>,
55 key: Key,
56 id: Option<widget::Id>,
57 value: P::Value,
58}
59
60impl<'a, Message, Theme, Renderer, P> Transition<'a, Message, Theme, Renderer, P>
61where
62 Renderer: core::Renderer,
63 P: Program,
64{
65 pub fn new<E>(
72 init: impl Fn() -> P + 'a,
73 value: P::Value,
74 view: impl Fn(&P, Instant) -> E + 'a,
75 ) -> Self
76 where
77 E: Into<Element<'a, Message, Theme, Renderer>>,
78 {
79 Self {
80 init: Box::new(init),
81 view: Box::new(move |program, at| view(program, at).into()),
82 on_finish: None,
83 element: Element::new(space()),
84 next_element: None,
85 new_layout: None,
86 last_limits: layout::Limits::new(Size::ZERO, Size::ZERO),
87 key: Key::default(),
88 id: None,
89 value,
90 }
91 }
92
93 pub fn id(mut self, id: impl Into<widget::Id>) -> Self {
97 self.id = Some(id.into());
98 self
99 }
100
101 pub fn key(mut self, key: impl Hash) -> Self {
105 self.key = Key::new(key);
106 self
107 }
108
109 pub fn on_finish(self, on_finish: Message) -> Self
113 where
114 Message: Clone + 'a,
115 {
116 self.on_finish_maybe(on_finish)
117 }
118
119 pub fn on_finish_maybe(mut self, on_finish: impl Into<Option<Message>>) -> Self
123 where
124 Message: Clone + 'a,
125 {
126 self.on_finish = on_finish
127 .into()
128 .map(|on_finish| Box::new(move || on_finish.clone()) as _);
129
130 self
131 }
132
133 pub fn on_finish_with(mut self, on_finish: impl Fn() -> Message + 'a) -> Self {
144 self.on_finish = Some(Box::new(on_finish));
145 self
146 }
147}
148
149impl<Message, Theme, Renderer, P> Widget<Message, Theme, Renderer>
150 for Transition<'_, Message, Theme, Renderer, P>
151where
152 Renderer: core::Renderer,
153 P: Program,
154{
155 fn size(&self) -> Size<Length> {
156 self.element.as_widget().size()
157 }
158
159 fn tag(&self) -> tree::Tag {
160 tree::Tag::of::<State<P>>()
161 }
162
163 fn state(&self) -> tree::State {
164 tree::State::new(State {
165 animation: (self.init)(),
166 instant: Instant::now(),
167 key: self.key,
168 should_reset: false,
169 size: None,
170 })
171 }
172
173 fn diff(&mut self, tree: &mut Tree) {
174 let State::<P> {
175 animation,
176 key: old_key,
177 should_reset,
178 instant,
179 ..
180 } = tree.state.downcast_mut();
181
182 if *old_key != self.key {
183 *old_key = self.key;
184 *animation = (self.init)();
185 *should_reset = false;
186 }
187
188 if let Some(next_element) = self.next_element.take() {
189 self.element = next_element;
190 } else {
191 self.element = (self.view)(animation, *instant);
192 }
193
194 tree.diff_children(std::slice::from_mut(&mut self.element));
195 }
196
197 fn layout(
198 &mut self,
199 tree: &mut Tree,
200 renderer: &Renderer,
201 limits: &layout::Limits,
202 ) -> layout::Node {
203 self.last_limits = *limits;
204 self.new_layout = None;
205
206 self.element
207 .as_widget_mut()
208 .layout(&mut tree.children[0], renderer, &limits.loose())
209 }
210
211 fn update(
212 &mut self,
213 tree: &mut Tree,
214 event: &Event,
215 layout: Layout<'_>,
216 cursor: mouse::Cursor,
217 renderer: &Renderer,
218 shell: &mut Shell<'_, Message>,
219 viewport: &Rectangle,
220 ) {
221 if let core::Event::Window(core::window::Event::RedrawRequested(redraw)) = event {
222 let State::<P> {
223 animation,
224 instant,
225 should_reset,
226 size,
227 ..
228 } = tree.state.downcast_mut();
229
230 if instant == redraw {
231 shell.request_redraw();
232 } else {
233 let was_animating = animation.is_animating(*instant);
234
235 if *should_reset {
236 *animation = (self.init)();
237 *should_reset = false;
238 }
239
240 *instant = *redraw;
241 animation.go(self.value, *instant);
242
243 let is_animating = animation.is_animating(*instant);
244 let just_finished = was_animating && !is_animating;
245
246 if is_animating || just_finished {
247 let size = *size;
248
249 let mut new = (self.view)(animation, *instant);
250 tree.diff_children(&mut [new.as_widget_mut()]);
251
252 let new_size = new.as_widget().size();
253
254 if size != Some(new_size) {
255 self.next_element = Some(new);
256 shell.invalidate_layout_with(shell::Diff::Perform);
257
258 let state = tree.state.downcast_mut::<State<P>>();
259 state.size = Some(new_size);
260 } else {
261 self.element = new;
262 self.new_layout = Some(self.element.as_widget_mut().layout(
263 &mut tree.children[0],
264 renderer,
265 &self.last_limits,
266 ));
267 }
268
269 shell.request_redraw();
270 }
271
272 if just_finished && let Some(on_finish) = &self.on_finish {
273 shell.publish(on_finish());
274 }
275 }
276 }
277
278 self.element.as_widget_mut().update(
279 &mut tree.children[0],
280 event,
281 self::layout(layout, &self.new_layout),
282 cursor,
283 renderer,
284 shell,
285 viewport,
286 );
287 }
288
289 fn draw(
290 &self,
291 tree: &Tree,
292 renderer: &mut Renderer,
293 theme: &Theme,
294 style: &renderer::Style,
295 layout: Layout<'_>,
296 cursor: mouse::Cursor,
297 viewport: &Rectangle,
298 ) {
299 self.element.as_widget().draw(
300 &tree.children[0],
301 renderer,
302 theme,
303 style,
304 self::layout(layout, &self.new_layout),
305 cursor,
306 viewport,
307 );
308 }
309
310 fn mouse_interaction(
311 &self,
312 tree: &Tree,
313 layout: Layout<'_>,
314 cursor: mouse::Cursor,
315 viewport: &Rectangle,
316 renderer: &Renderer,
317 ) -> mouse::Interaction {
318 self.element.as_widget().mouse_interaction(
319 &tree.children[0],
320 self::layout(layout, &self.new_layout),
321 cursor,
322 viewport,
323 renderer,
324 )
325 }
326
327 fn operate(
328 &mut self,
329 tree: &mut Tree,
330 layout: Layout<'_>,
331 renderer: &Renderer,
332 operation: &mut dyn widget::Operation,
333 ) {
334 let layout = self::layout(layout, &self.new_layout);
335
336 let mut should_reset = ShouldReset(false);
337 operation.custom(self.id.as_ref(), layout.bounds(), &mut should_reset);
338
339 if should_reset.0 {
340 tree.state.downcast_mut::<State<P>>().should_reset = true;
341 }
342
343 self.element
344 .as_widget_mut()
345 .operate(&mut tree.children[0], layout, renderer, operation);
346 }
347
348 fn overlay<'a>(
349 &'a mut self,
350 tree: &'a mut Tree,
351 layout: Layout<'a>,
352 renderer: &Renderer,
353 viewport: &Rectangle,
354 translation: Vector,
355 ) -> Option<overlay::Element<'a, Message, Theme, Renderer>> {
356 self.element.as_widget_mut().overlay(
357 &mut tree.children[0],
358 self::layout(layout, &self.new_layout),
359 renderer,
360 viewport,
361 translation,
362 )
363 }
364}
365
366impl<'a, Message, Theme, Renderer, P> From<Transition<'a, Message, Theme, Renderer, P>>
367 for Element<'a, Message, Theme, Renderer>
368where
369 Message: 'a,
370 Theme: 'a,
371 Renderer: core::Renderer + 'a,
372 P: Program,
373{
374 fn from(transition: Transition<'a, Message, Theme, Renderer, P>) -> Self {
375 Self::new(transition)
376 }
377}
378
379struct State<P: Program> {
380 animation: P,
381 instant: Instant,
382 key: Key,
383 should_reset: bool,
384 size: Option<Size<Length>>,
385}
386
387#[derive(Default, Clone, Copy, Hash, PartialEq, Eq)]
388struct Key(u64);
389
390impl Key {
391 fn new(data: impl Hash) -> Self {
392 let mut hasher = DefaultHasher::new();
393 data.hash(&mut hasher);
394 Self(hasher.finish())
395 }
396}
397
398struct ShouldReset(bool);
399
400pub fn reset<Message>(id: impl Into<widget::Id>) -> iced_runtime::Task<Message>
402where
403 Message: iced_runtime::futures::MaybeSend + 'static,
404{
405 let id = id.into();
406 iced_runtime::task::widget(reset_raw(id)).discard()
407}
408
409pub fn reset_raw(id: impl Into<widget::Id>) -> impl Operation {
411 struct Reset(widget::Id);
412
413 impl Operation for Reset {
414 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<()>)) {
415 operate(self);
416 }
417
418 fn custom(
419 &mut self,
420 id: Option<&widget::Id>,
421 _bounds: Rectangle,
422 state: &mut dyn std::any::Any,
423 ) {
424 if id == Some(&self.0)
425 && let Some(ShouldReset(should_reset)) = state.downcast_mut()
426 {
427 *should_reset = true;
428 }
429 }
430 }
431
432 Reset(id.into())
433}
434
435fn layout<'a>(current: Layout<'a>, animated: &'a Option<layout::Node>) -> Layout<'a> {
436 animated
437 .as_ref()
438 .map(|new_layout| Layout::with_offset(current.position() - Point::ORIGIN, new_layout))
439 .unwrap_or(current)
440}