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 P: Program,
148 W: Widget<Message, Theme, Renderer>,
149{
150 fn size(&self) -> Size<Length> {
151 self.element.size()
152 }
153
154 fn tag(&self) -> tree::Tag {
155 tree::Tag::of::<State<P>>()
156 }
157
158 fn state(&self) -> tree::State {
159 tree::State::new(State {
160 animation: (self.init)(),
161 instant: Instant::now(),
162 key: self.key,
163 should_reset: false,
164 size: None,
165 })
166 }
167
168 fn diff(&mut self, tree: &mut Tree) {
169 let State::<P> {
170 animation,
171 key: old_key,
172 should_reset,
173 instant,
174 ..
175 } = tree.state.downcast_mut();
176
177 if *old_key != self.key {
178 *old_key = self.key;
179 *animation = (self.init)();
180 *should_reset = false;
181 }
182
183 if let Some(next_element) = self.next_element.take() {
184 self.element = Some(next_element);
185 } else {
186 self.element = Some((self.view)(animation, *instant));
187 }
188
189 tree.diff_children(std::slice::from_mut(&mut self.element));
190 }
191
192 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
193 self.last_limits = *limits;
194
195 self.element
196 .layout(&mut tree.children[0], renderer, &limits.loose());
197
198 tree.size = tree.children[0].size;
199 }
200
201 fn update(
202 &mut self,
203 tree: &mut Tree,
204 event: &Event,
205 layout: Layout,
206 cursor: mouse::Cursor,
207 renderer: &Renderer,
208 shell: &mut Shell<'_, Message>,
209 viewport: &Rectangle,
210 ) {
211 if let core::Event::Window(core::window::Event::RedrawRequested(redraw)) = event {
212 let State::<P> {
213 animation,
214 instant,
215 should_reset,
216 size,
217 ..
218 } = tree.state.downcast_mut();
219
220 if instant == redraw {
221 shell.request_redraw();
222 } else {
223 let was_animating = animation.is_animating(*instant);
224
225 if *should_reset {
226 *animation = (self.init)();
227 *should_reset = false;
228 }
229
230 *instant = *redraw;
231 animation.go(self.value, *instant);
232
233 let is_animating = animation.is_animating(*instant);
234 let just_finished = was_animating && !is_animating;
235
236 if is_animating || just_finished {
237 let size = *size;
238
239 let mut new = (self.view)(animation, *instant);
240 tree.diff_children(&mut [&mut new]);
241
242 let new_size = new.size();
243
244 if size != Some(new_size) {
245 self.next_element = Some(new);
246 shell.invalidate_layout_with(shell::Diff::Perform);
247
248 let state = tree.state.downcast_mut::<State<P>>();
249 state.size = Some(new_size);
250 } else {
251 self.element = Some(new);
252 self.element
253 .layout(&mut tree.children[0], renderer, &self.last_limits);
254 }
255
256 shell.request_redraw();
257 }
258
259 if just_finished && let Some(on_finish) = &self.on_finish {
260 shell.publish(on_finish());
261 }
262 }
263 }
264
265 self.element.update(
266 &mut tree.children[0],
267 event,
268 layout,
269 cursor,
270 renderer,
271 shell,
272 viewport,
273 );
274 }
275
276 fn draw(
277 &self,
278 tree: &Tree,
279 renderer: &mut Renderer,
280 theme: &Theme,
281 style: &renderer::Style,
282 layout: Layout,
283 cursor: mouse::Cursor,
284 viewport: &Rectangle,
285 ) {
286 self.element.draw(
287 &tree.children[0],
288 renderer,
289 theme,
290 style,
291 layout,
292 cursor,
293 viewport,
294 );
295 }
296
297 fn mouse_interaction(
298 &self,
299 tree: &Tree,
300 layout: Layout,
301 cursor: mouse::Cursor,
302 viewport: &Rectangle,
303 renderer: &Renderer,
304 ) -> mouse::Interaction {
305 self.element
306 .mouse_interaction(&tree.children[0], layout, cursor, viewport, renderer)
307 }
308
309 fn operate(
310 &mut self,
311 tree: &mut Tree,
312 layout: Layout,
313 viewport: &Rectangle,
314 renderer: &Renderer,
315 operation: &mut dyn widget::Operation,
316 ) {
317 let mut should_reset = ShouldReset(false);
318 operation.custom(self.id.as_ref(), layout.bounds(), &mut should_reset);
319
320 if should_reset.0 {
321 tree.state.downcast_mut::<State<P>>().should_reset = true;
322 }
323
324 self.element
325 .operate(&mut tree.children[0], layout, viewport, renderer, operation);
326 }
327
328 fn overlay<'a>(
329 &'a mut self,
330 tree: &'a mut Tree,
331 layout: Layout,
332 renderer: &Renderer,
333 viewport: &Rectangle,
334 translation: Vector,
335 window: Size,
336 ) -> Vec<overlay::Element<'a, Message, Theme, Renderer>> {
337 self.element.overlay(
338 &mut tree.children[0],
339 layout,
340 renderer,
341 viewport,
342 translation,
343 window,
344 )
345 }
346}
347
348struct State<P: Program> {
349 animation: P,
350 instant: Instant,
351 key: Key,
352 should_reset: bool,
353 size: Option<Size<Length>>,
354}
355
356#[derive(Default, Clone, Copy, Hash, PartialEq, Eq)]
357struct Key(u64);
358
359impl Key {
360 fn new(data: impl Hash) -> Self {
361 let mut hasher = DefaultHasher::new();
362 data.hash(&mut hasher);
363 Self(hasher.finish())
364 }
365}
366
367struct ShouldReset(bool);
368
369pub fn reset<Message>(id: impl Into<widget::Id>) -> iced_runtime::Task<Message>
371where
372 Message: iced_runtime::futures::MaybeSend + 'static,
373{
374 let id = id.into();
375 iced_runtime::task::widget(reset_raw(id)).discard()
376}
377
378pub fn reset_raw(id: impl Into<widget::Id>) -> impl Operation {
380 struct Reset(widget::Id);
381
382 impl Operation for Reset {
383 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<()>)) {
384 operate(self);
385 }
386
387 fn custom(
388 &mut self,
389 id: Option<&widget::Id>,
390 _bounds: Rectangle,
391 state: &mut dyn std::any::Any,
392 ) {
393 if id == Some(&self.0)
394 && let Some(ShouldReset(should_reset)) = state.downcast_mut()
395 {
396 *should_reset = true;
397 }
398 }
399 }
400
401 Reset(id.into())
402}