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