floem/
context.rs

1use floem_reactive::Scope;
2use floem_renderer::Renderer as FloemRenderer;
3use floem_renderer::gpu_resources::{GpuResourceError, GpuResources};
4use peniko::kurbo::{Affine, Point, Rect, RoundedRect, Shape, Size, Vec2};
5use std::{
6    ops::{Deref, DerefMut},
7    rc::Rc,
8    sync::Arc,
9};
10use winit::window::Window;
11
12#[cfg(not(target_arch = "wasm32"))]
13use std::time::{Duration, Instant};
14#[cfg(target_arch = "wasm32")]
15use web_time::{Duration, Instant};
16
17#[cfg(feature = "crossbeam")]
18use crossbeam::channel::Receiver;
19#[cfg(not(feature = "crossbeam"))]
20use std::sync::mpsc::Receiver;
21
22use taffy::prelude::NodeId;
23
24use crate::animate::{AnimStateKind, RepeatMode};
25use crate::easing::{Easing, Linear};
26use crate::menu::Menu;
27use crate::renderer::Renderer;
28use crate::style::{Disabled, DisplayProp, Focusable, Hidden, PointerEvents, PointerEventsProp};
29use crate::view_state::IsHiddenState;
30use crate::{
31    action::{exec_after, show_context_menu},
32    app_state::AppState,
33    event::{Event, EventListener, EventPropagation},
34    id::ViewId,
35    inspector::CaptureState,
36    style::{Style, StyleProp, ZIndex},
37    view::{View, paint_bg, paint_border, paint_outline},
38    view_state::ChangeFlags,
39};
40
41pub type EventCallback = dyn FnMut(&Event) -> EventPropagation;
42pub type ResizeCallback = dyn Fn(Rect);
43pub type MenuCallback = dyn Fn() -> Menu;
44
45pub(crate) struct ResizeListener {
46    pub(crate) rect: Rect,
47    pub(crate) callback: Box<ResizeCallback>,
48}
49
50/// The listener when the view is got moved to a different position in the window
51pub(crate) struct MoveListener {
52    pub(crate) window_origin: Point,
53    pub(crate) callback: Box<dyn Fn(Point)>,
54}
55
56pub struct DragState {
57    pub(crate) id: ViewId,
58    pub(crate) offset: Vec2,
59    pub(crate) released_at: Option<Instant>,
60    pub(crate) release_location: Option<Point>,
61}
62
63pub(crate) enum FrameUpdate {
64    Style(ViewId),
65    Layout(ViewId),
66    Paint(ViewId),
67}
68
69#[derive(Debug, PartialEq, Eq)]
70pub(crate) enum PointerEventConsumed {
71    Yes,
72    No,
73}
74
75/// A bundle of helper methods to be used by `View::event` handlers
76pub struct EventCx<'a> {
77    pub(crate) app_state: &'a mut AppState,
78}
79
80impl EventCx<'_> {
81    pub fn app_state_mut(&mut self) -> &mut AppState {
82        self.app_state
83    }
84
85    pub fn app_state(&self) -> &AppState {
86        self.app_state
87    }
88
89    pub fn update_active(&mut self, id: ViewId) {
90        self.app_state.update_active(id);
91    }
92
93    pub fn is_active(&self, id: ViewId) -> bool {
94        self.app_state.is_active(&id)
95    }
96
97    #[allow(unused)]
98    pub(crate) fn update_focus(&mut self, id: ViewId, keyboard_navigation: bool) {
99        self.app_state.update_focus(id, keyboard_navigation);
100    }
101
102    /// Internal method used by Floem. This can be called from parent `View`s to propagate an event to the child `View`.
103    pub(crate) fn unconditional_view_event(
104        &mut self,
105        view_id: ViewId,
106        event: Event,
107        directed: bool,
108    ) -> (EventPropagation, PointerEventConsumed) {
109        if view_id.is_hidden() {
110            // we don't process events for hidden view
111            return (EventPropagation::Continue, PointerEventConsumed::No);
112        }
113        if view_id.is_disabled() && !event.allow_disabled() {
114            // if the view is disabled and the event is not processed
115            // for disabled views
116            return (EventPropagation::Continue, PointerEventConsumed::No);
117        }
118
119        // TODO! Handle file hover
120
121        // offset the event positions if the event has positions
122        // e.g. pointer events, so that the position is relative
123        // to the view, taking into account of the layout location
124        // of the view and the viewport of the view if it's in a scroll.
125        let event = self.offset_event(view_id, event);
126
127        let view = view_id.view();
128        let view_state = view_id.state();
129
130        let disable_default = if let Some(listener) = event.listener() {
131            view_state
132                .borrow()
133                .disable_default_events
134                .contains(&listener)
135        } else {
136            false
137        };
138
139        let is_pointer_none = event.is_pointer()
140            && view_state.borrow().computed_style.get(PointerEventsProp)
141                == Some(PointerEvents::None);
142
143        if !disable_default
144            && !is_pointer_none
145            && view
146                .borrow_mut()
147                .event_before_children(self, &event)
148                .is_processed()
149        {
150            if let Event::PointerDown(event) = &event {
151                if view_state.borrow().computed_style.get(Focusable) {
152                    let rect = view_id.get_size().unwrap_or_default().to_rect();
153                    let now_focused = rect.contains(event.pos);
154                    if now_focused {
155                        self.app_state.update_focus(view_id, false);
156                    }
157                }
158            }
159            if let Event::PointerMove(_event) = &event {
160                let view_state = view_state.borrow();
161                let style = view_state.combined_style.builtin();
162                if let Some(cursor) = style.cursor() {
163                    if self.app_state.cursor.is_none() {
164                        self.app_state.cursor = Some(cursor);
165                    }
166                }
167            }
168            return (EventPropagation::Stop, PointerEventConsumed::Yes);
169        }
170
171        let mut view_pointer_event_consumed = PointerEventConsumed::No;
172
173        if !directed {
174            let children = view_id.children();
175            for child in children.into_iter().rev() {
176                if !self.should_send(child, &event) {
177                    continue;
178                }
179                let (event_propagation, pointer_event_consumed) =
180                    self.unconditional_view_event(child, event.clone(), false);
181                if event_propagation.is_processed() {
182                    return (EventPropagation::Stop, PointerEventConsumed::Yes);
183                }
184                if event.is_pointer() && pointer_event_consumed == PointerEventConsumed::Yes {
185                    // if a child's pointer event was consumed because pointer-events: auto
186                    // we don't pass the pointer event the next child
187                    // also, we mark pointer_event_consumed to be yes
188                    // so that it will be bublled up the parent
189                    view_pointer_event_consumed = PointerEventConsumed::Yes;
190                    break;
191                }
192            }
193        }
194
195        if !disable_default
196            && !is_pointer_none
197            && view
198                .borrow_mut()
199                .event_after_children(self, &event)
200                .is_processed()
201        {
202            return (EventPropagation::Stop, PointerEventConsumed::Yes);
203        }
204
205        if is_pointer_none {
206            // if pointer-events: none, we don't handle the pointer event
207            return (EventPropagation::Continue, view_pointer_event_consumed);
208        }
209
210        // CLARIFY: should this be disabled when disable_default?
211        if !disable_default {
212            let popout_menu = || {
213                let bottom_left = {
214                    let layout = view_state.borrow().layout_rect;
215                    Point::new(layout.x0, layout.y1)
216                };
217
218                let popout_menu = view_state.borrow().popout_menu.clone();
219                show_context_menu(popout_menu?(), Some(bottom_left));
220                Some((EventPropagation::Stop, PointerEventConsumed::Yes))
221            };
222
223            match &event {
224                Event::PointerDown(pointer_event) => {
225                    self.app_state.clicking.insert(view_id);
226                    if pointer_event.button.is_primary() {
227                        let rect = view_id.get_size().unwrap_or_default().to_rect();
228                        let on_view = rect.contains(pointer_event.pos);
229
230                        if on_view {
231                            if view_state.borrow().computed_style.get(Focusable) {
232                                // if the view can be focused, we update the focus
233                                self.app_state.update_focus(view_id, false);
234                            }
235                            if pointer_event.count == 2
236                                && view_state
237                                    .borrow()
238                                    .event_listeners
239                                    .contains_key(&EventListener::DoubleClick)
240                            {
241                                view_state.borrow_mut().last_pointer_down =
242                                    Some(pointer_event.clone());
243                            }
244                            if view_state
245                                .borrow()
246                                .event_listeners
247                                .contains_key(&EventListener::Click)
248                            {
249                                view_state.borrow_mut().last_pointer_down =
250                                    Some(pointer_event.clone());
251                            }
252
253                            #[cfg(target_os = "macos")]
254                            if let Some((ep, pec)) = popout_menu() {
255                                return (ep, pec);
256                            };
257
258                            if view_id.can_drag() && self.app_state.drag_start.is_none() {
259                                self.app_state.drag_start = Some((view_id, pointer_event.pos));
260                            }
261                        }
262                    } else if pointer_event.button.is_secondary() {
263                        let rect = view_id.get_size().unwrap_or_default().to_rect();
264                        let on_view = rect.contains(pointer_event.pos);
265
266                        if on_view {
267                            if view_state.borrow().computed_style.get(Focusable) {
268                                // if the view can be focused, we update the focus
269                                self.app_state.update_focus(view_id, false);
270                            }
271                            if view_state
272                                .borrow()
273                                .event_listeners
274                                .contains_key(&EventListener::SecondaryClick)
275                            {
276                                view_state.borrow_mut().last_pointer_down =
277                                    Some(pointer_event.clone());
278                            }
279                        }
280                    }
281                }
282                Event::PointerMove(pointer_event) => {
283                    let rect = view_id.get_size().unwrap_or_default().to_rect();
284                    if rect.contains(pointer_event.pos) {
285                        if self.app_state.is_dragging() {
286                            self.app_state.dragging_over.insert(view_id);
287                            view_id.apply_event(&EventListener::DragOver, &event);
288                        } else {
289                            self.app_state.hovered.insert(view_id);
290                            let view_state = view_state.borrow();
291                            let style = view_state.combined_style.builtin();
292                            if let Some(cursor) = style.cursor() {
293                                if self.app_state.cursor.is_none() {
294                                    self.app_state.cursor = Some(cursor);
295                                }
296                            }
297                        }
298                    }
299                    if view_id.can_drag() {
300                        if let Some((_, drag_start)) = self
301                            .app_state
302                            .drag_start
303                            .as_ref()
304                            .filter(|(drag_id, _)| drag_id == &view_id)
305                        {
306                            let offset = pointer_event.pos - *drag_start;
307                            if let Some(dragging) = self
308                                .app_state
309                                .dragging
310                                .as_mut()
311                                .filter(|d| d.id == view_id && d.released_at.is_none())
312                            {
313                                // update the mouse position if the view is dragging and not released
314                                dragging.offset = drag_start.to_vec2();
315                                self.app_state.request_paint(view_id);
316                            } else if offset.x.abs() + offset.y.abs() > 1.0 {
317                                // start dragging when moved 1 px
318                                self.app_state.active = None;
319                                self.app_state.dragging = Some(DragState {
320                                    id: view_id,
321                                    offset: drag_start.to_vec2(),
322                                    released_at: None,
323                                    release_location: None,
324                                });
325                                self.update_active(view_id);
326                                self.app_state.request_paint(view_id);
327                                view_id.apply_event(&EventListener::DragStart, &event);
328                            }
329                        }
330                    }
331                    if view_id
332                        .apply_event(&EventListener::PointerMove, &event)
333                        .is_some_and(|prop| prop.is_processed())
334                    {
335                        return (EventPropagation::Stop, PointerEventConsumed::Yes);
336                    }
337                }
338                Event::PointerUp(pointer_event) => {
339                    if pointer_event.button.is_primary() {
340                        let rect = view_id.get_size().unwrap_or_default().to_rect();
341                        let on_view = rect.contains(pointer_event.pos);
342
343                        #[cfg(not(target_os = "macos"))]
344                        if on_view {
345                            if let Some((ep, pec)) = popout_menu() {
346                                return (ep, pec);
347                            };
348                        }
349
350                        // if id_path.is_none() {
351                        if !directed {
352                            if on_view {
353                                if let Some(dragging) = self.app_state.dragging.as_mut() {
354                                    let dragging_id = dragging.id;
355                                    if view_id
356                                        .apply_event(&EventListener::Drop, &event)
357                                        .is_some_and(|prop| prop.is_processed())
358                                    {
359                                        // if the drop is processed, we set dragging to none so that the animation
360                                        // for the dragged view back to its original position isn't played.
361                                        self.app_state.dragging = None;
362                                        self.app_state.request_paint(view_id);
363                                        dragging_id.apply_event(&EventListener::DragEnd, &event);
364                                    }
365                                }
366                            }
367                        } else if let Some(dragging) =
368                            self.app_state.dragging.as_mut().filter(|d| d.id == view_id)
369                        {
370                            let dragging_id = dragging.id;
371                            dragging.released_at = Some(Instant::now());
372                            dragging.release_location = Some(pointer_event.pos);
373                            self.app_state.request_paint(view_id);
374                            dragging_id.apply_event(&EventListener::DragEnd, &event);
375                        }
376
377                        let last_pointer_down = view_state.borrow_mut().last_pointer_down.take();
378
379                        let event_listeners = view_state.borrow().event_listeners.clone();
380                        if let Some(handlers) = event_listeners.get(&EventListener::DoubleClick) {
381                            view_state.borrow_mut();
382                            if on_view
383                                && self.app_state.is_clicking(&view_id)
384                                && last_pointer_down
385                                    .as_ref()
386                                    .map(|e| e.count == 2)
387                                    .unwrap_or(false)
388                                && handlers.iter().fold(false, |handled, handler| {
389                                    handled | (handler.borrow_mut())(&event).is_processed()
390                                })
391                            {
392                                return (EventPropagation::Stop, PointerEventConsumed::Yes);
393                            }
394                        }
395
396                        if let Some(handlers) = event_listeners.get(&EventListener::Click) {
397                            if on_view
398                                && self.app_state.is_clicking(&view_id)
399                                && last_pointer_down.is_some()
400                                && handlers.iter().fold(false, |handled, handler| {
401                                    handled | (handler.borrow_mut())(&event).is_processed()
402                                })
403                            {
404                                return (EventPropagation::Stop, PointerEventConsumed::Yes);
405                            }
406                        }
407
408                        if view_id
409                            .apply_event(&EventListener::PointerUp, &event)
410                            .is_some_and(|prop| prop.is_processed())
411                        {
412                            return (EventPropagation::Stop, PointerEventConsumed::Yes);
413                        }
414                    } else if pointer_event.button.is_secondary() {
415                        let rect = view_id.get_size().unwrap_or_default().to_rect();
416                        let on_view = rect.contains(pointer_event.pos);
417
418                        let last_pointer_down = view_state.borrow_mut().last_pointer_down.take();
419                        let event_listeners = view_state.borrow().event_listeners.clone();
420                        if let Some(handlers) = event_listeners.get(&EventListener::SecondaryClick)
421                        {
422                            if on_view
423                                && last_pointer_down.is_some()
424                                && handlers.iter().fold(false, |handled, handler| {
425                                    handled | (handler.borrow_mut())(&event).is_processed()
426                                })
427                            {
428                                return (EventPropagation::Stop, PointerEventConsumed::Yes);
429                            }
430                        }
431
432                        let viewport_event_position = {
433                            let layout = view_state.borrow().layout_rect;
434                            Point::new(
435                                layout.x0 + pointer_event.pos.x,
436                                layout.y0 + pointer_event.pos.y,
437                            )
438                        };
439                        let context_menu = view_state.borrow().context_menu.clone();
440                        if let Some(menu) = context_menu {
441                            show_context_menu(menu(), Some(viewport_event_position));
442                            return (EventPropagation::Stop, PointerEventConsumed::Yes);
443                        }
444                    }
445                }
446                Event::KeyDown(_) => {
447                    if self.app_state.is_focused(&view_id) && event.is_keyboard_trigger() {
448                        view_id.apply_event(&EventListener::Click, &event);
449                    }
450                }
451                Event::WindowResized(_) => {
452                    if view_state.borrow().has_style_selectors.has_responsive() {
453                        view_id.request_style();
454                    }
455                }
456                _ => (),
457            }
458        }
459
460        if !disable_default {
461            if let Some(listener) = event.listener() {
462                let event_listeners = view_state.borrow().event_listeners.clone();
463                if let Some(handlers) = event_listeners.get(&listener).cloned() {
464                    let should_run = if let Some(pos) = event.point() {
465                        let rect = view_id.get_size().unwrap_or_default().to_rect();
466                        rect.contains(pos)
467                    } else {
468                        true
469                    };
470                    if should_run
471                        && handlers.iter().fold(false, |handled, handler| {
472                            handled | (handler.borrow_mut())(&event).is_processed()
473                        })
474                    {
475                        return (EventPropagation::Stop, PointerEventConsumed::Yes);
476                    }
477                }
478            }
479        }
480
481        (EventPropagation::Continue, PointerEventConsumed::Yes)
482    }
483
484    /// translate a window-positioned event to the local coordinate system of a view
485    pub(crate) fn offset_event(&self, id: ViewId, event: Event) -> Event {
486        let state = id.state();
487        let viewport = state.borrow().viewport;
488        let transform = state.borrow().transform;
489
490        if let Some(layout) = id.get_layout() {
491            event.transform(
492                Affine::translate((
493                    layout.location.x as f64 - viewport.map(|rect| rect.x0).unwrap_or(0.0),
494                    layout.location.y as f64 - viewport.map(|rect| rect.y0).unwrap_or(0.0),
495                )) * transform,
496            )
497        } else {
498            event
499        }
500    }
501
502    /// Used to determine if you should send an event to another view. This is basically a check for pointer events to see if the pointer is inside a child view and to make sure the current view isn't hidden or disabled.
503    /// Usually this is used if you want to propagate an event to a child view
504    pub fn should_send(&mut self, id: ViewId, event: &Event) -> bool {
505        if id.is_hidden() || (id.is_disabled() && !event.allow_disabled()) {
506            return false;
507        }
508
509        let Some(point) = event.point() else {
510            return true;
511        };
512
513        let layout_rect = id.layout_rect();
514        let Some(layout) = id.get_layout() else {
515            return false;
516        };
517
518        // Check if point is within current view's bounds
519        let current_rect = layout_rect.with_origin(Point::new(
520            layout.location.x as f64,
521            layout.location.y as f64,
522        ));
523
524        if !current_rect.contains(point) {
525            return false;
526        }
527
528        true
529    }
530}
531
532#[derive(Default)]
533pub struct InteractionState {
534    pub(crate) is_hovered: bool,
535    pub(crate) is_selected: bool,
536    pub(crate) is_disabled: bool,
537    pub(crate) is_focused: bool,
538    pub(crate) is_clicking: bool,
539    pub(crate) is_dark_mode: bool,
540    pub(crate) is_file_hover: bool,
541    pub(crate) using_keyboard_navigation: bool,
542}
543
544pub struct StyleCx<'a> {
545    pub(crate) app_state: &'a mut AppState,
546    pub(crate) current_view: ViewId,
547    /// current is used as context for carrying inherited properties between views
548    pub(crate) current: Rc<Style>,
549    pub(crate) direct: Style,
550    saved: Vec<Rc<Style>>,
551    pub(crate) now: Instant,
552    saved_disabled: Vec<bool>,
553    saved_selected: Vec<bool>,
554    saved_hidden: Vec<bool>,
555    disabled: bool,
556    hidden: bool,
557    selected: bool,
558}
559
560impl<'a> StyleCx<'a> {
561    pub(crate) fn new(app_state: &'a mut AppState, root: ViewId) -> Self {
562        Self {
563            app_state,
564            current_view: root,
565            current: Default::default(),
566            direct: Default::default(),
567            saved: Default::default(),
568            now: Instant::now(),
569            saved_disabled: Default::default(),
570            saved_selected: Default::default(),
571            saved_hidden: Default::default(),
572            disabled: false,
573            hidden: false,
574            selected: false,
575        }
576    }
577
578    /// Marks the current context as selected.
579    pub fn selected(&mut self) {
580        self.selected = true;
581    }
582
583    pub fn hidden(&mut self) {
584        self.hidden = true;
585    }
586
587    fn get_interact_state(&self, id: &ViewId) -> InteractionState {
588        InteractionState {
589            is_selected: self.selected || id.is_selected(),
590            is_hovered: self.app_state.is_hovered(id),
591            is_disabled: id.is_disabled() || self.disabled,
592            is_focused: self.app_state.is_focused(id),
593            is_clicking: self.app_state.is_clicking(id),
594            is_dark_mode: self.app_state.is_dark_mode(),
595            is_file_hover: self.app_state.is_file_hover(id),
596            using_keyboard_navigation: self.app_state.keyboard_navigation,
597        }
598    }
599
600    /// Internal method used by Floem to compute the styles for the view.
601    pub fn style_view(&mut self, view_id: ViewId) {
602        self.save();
603        let view = view_id.view();
604        let view_state = view_id.state();
605        {
606            let mut view_state = view_state.borrow_mut();
607            if !view_state.requested_changes.contains(ChangeFlags::STYLE) {
608                self.restore();
609                return;
610            }
611            view_state.requested_changes.remove(ChangeFlags::STYLE);
612        }
613
614        let view_style = view.borrow().view_style();
615        let view_class = view.borrow().view_class();
616        {
617            let mut view_state = view_state.borrow_mut();
618
619            // Propagate style requests to children if needed.
620            if view_state.request_style_recursive {
621                view_state.request_style_recursive = false;
622                let children = view_id.children();
623                for child in children {
624                    let view_state = child.state();
625                    let mut state = view_state.borrow_mut();
626                    state.request_style_recursive = true;
627                    state.requested_changes.insert(ChangeFlags::STYLE);
628                }
629            }
630        }
631
632        let view_interact_state = self.get_interact_state(&view_id);
633        self.disabled = view_interact_state.is_disabled;
634        let mut new_frame = view_id.state().borrow_mut().compute_combined(
635            view_style,
636            view_interact_state,
637            self.app_state.screen_size_bp,
638            view_class,
639            &self.current,
640            self.hidden,
641        );
642
643        self.direct = view_state.borrow().combined_style.clone();
644        Style::apply_only_inherited(&mut self.current, &self.direct);
645        let mut computed_style = (*self.current).clone();
646        computed_style.apply_mut(self.direct.clone());
647        CaptureState::capture_style(view_id, self, computed_style.clone());
648        if computed_style.get(Focusable)
649            && !computed_style.get(Disabled)
650            && !computed_style.get(Hidden)
651            && computed_style.get(DisplayProp) != taffy::Display::None
652        {
653            self.app_state.focusable.insert(view_id);
654        } else {
655            self.app_state.focusable.remove(&view_id);
656        }
657        view_state.borrow_mut().computed_style = computed_style;
658        self.hidden |= view_id.is_hidden();
659
660        // This is used by the `request_transition` and `style` methods below.
661        self.current_view = view_id;
662
663        {
664            let mut view_state = view_state.borrow_mut();
665            // Extract the relevant layout properties so the content rect can be calculated
666            // when painting.
667            view_state.layout_props.read_explicit(
668                &self.direct,
669                &self.current,
670                &self.now,
671                &mut new_frame,
672            );
673
674            view_state.view_style_props.read_explicit(
675                &self.direct,
676                &self.current,
677                &self.now,
678                &mut new_frame,
679            );
680            if new_frame && !self.hidden {
681                self.app_state.schedule_style(view_id);
682            }
683        }
684        // If there's any changes to the Taffy style, request layout.
685        let layout_style = view_state.borrow().layout_props.to_style();
686        let taffy_style = self.direct.clone().apply(layout_style).to_taffy_style();
687        if taffy_style != view_state.borrow().taffy_style {
688            view_state.borrow_mut().taffy_style = taffy_style;
689            view_id.request_layout();
690        }
691
692        view.borrow_mut().style_pass(self);
693
694        let mut is_hidden_state = view_state.borrow().is_hidden_state;
695        let computed_display = view_state.borrow().combined_style.get(DisplayProp);
696        is_hidden_state.transition(
697            computed_display,
698            || {
699                let count = animations_on_remove(view_id, Scope::current());
700                view_state.borrow_mut().num_waiting_animations = count;
701                count > 0
702            },
703            || {
704                animations_on_create(view_id);
705            },
706            || {
707                stop_reset_remove_animations(view_id);
708            },
709            || view_state.borrow().num_waiting_animations,
710        );
711
712        view_state.borrow_mut().is_hidden_state = is_hidden_state;
713        let modified = view_state
714            .borrow()
715            .combined_style
716            .clone()
717            .apply_opt(is_hidden_state.get_display(), Style::display);
718
719        view_state.borrow_mut().combined_style = modified;
720
721        let mut transform = Affine::IDENTITY;
722
723        let transform_x = match view_state.borrow().layout_props.translate_x() {
724            crate::unit::PxPct::Px(px) => px,
725            crate::unit::PxPct::Pct(pct) => pct / 100.,
726        };
727        let transform_y = match view_state.borrow().layout_props.translate_y() {
728            crate::unit::PxPct::Px(px) => px,
729            crate::unit::PxPct::Pct(pct) => pct / 100.,
730        };
731        transform *= Affine::translate(Vec2 {
732            x: transform_x,
733            y: transform_y,
734        });
735
736        let scale_x = view_state.borrow().layout_props.scale_x().0 / 100.;
737        let scale_y = view_state.borrow().layout_props.scale_y().0 / 100.;
738        let size = view_id.layout_rect();
739        let center_x = size.width() / 2.;
740        let center_y = size.height() / 2.;
741        transform *= Affine::translate(Vec2 {
742            x: center_x,
743            y: center_y,
744        });
745        transform *= Affine::scale_non_uniform(scale_x, scale_y);
746        let rotation = view_state.borrow().layout_props.rotation().0;
747        transform *= Affine::rotate(rotation);
748        transform *= Affine::translate(Vec2 {
749            x: -center_x,
750            y: -center_y,
751        });
752
753        view_state.borrow_mut().transform = transform;
754
755        self.restore();
756    }
757
758    pub fn now(&self) -> Instant {
759        self.now
760    }
761
762    pub fn save(&mut self) {
763        self.saved.push(self.current.clone());
764        self.saved_disabled.push(self.disabled);
765        self.saved_selected.push(self.selected);
766        self.saved_hidden.push(self.hidden);
767    }
768
769    pub fn restore(&mut self) {
770        self.current = self.saved.pop().unwrap_or_default();
771        self.disabled = self.saved_disabled.pop().unwrap_or_default();
772        self.selected = self.saved_selected.pop().unwrap_or_default();
773        self.hidden = self.saved_hidden.pop().unwrap_or_default();
774    }
775
776    pub fn get_prop<P: StyleProp>(&self, _prop: P) -> Option<P::Type> {
777        self.direct
778            .get_prop::<P>()
779            .or_else(|| self.current.get_prop::<P>())
780    }
781
782    pub fn style(&self) -> Style {
783        (*self.current).clone().apply(self.direct.clone())
784    }
785
786    pub fn direct_style(&self) -> &Style {
787        &self.direct
788    }
789
790    pub fn indirect_style(&self) -> &Style {
791        &self.current
792    }
793
794    pub fn request_transition(&mut self) {
795        let id = self.current_view;
796        self.app_state_mut().schedule_style(id);
797    }
798
799    pub fn app_state_mut(&mut self) -> &mut AppState {
800        self.app_state
801    }
802
803    pub fn app_state(&self) -> &AppState {
804        self.app_state
805    }
806}
807
808pub struct ComputeLayoutCx<'a> {
809    pub(crate) app_state: &'a mut AppState,
810    pub(crate) viewport: Rect,
811    pub(crate) window_origin: Point,
812    pub(crate) saved_viewports: Vec<Rect>,
813    pub(crate) saved_window_origins: Vec<Point>,
814}
815
816impl<'a> ComputeLayoutCx<'a> {
817    pub(crate) fn new(app_state: &'a mut AppState, viewport: Rect) -> Self {
818        Self {
819            app_state,
820            viewport,
821            window_origin: Point::ZERO,
822            saved_viewports: Vec::new(),
823            saved_window_origins: Vec::new(),
824        }
825    }
826
827    pub fn window_origin(&self) -> Point {
828        self.window_origin
829    }
830
831    pub fn app_state_mut(&mut self) -> &mut AppState {
832        self.app_state
833    }
834
835    pub fn app_state(&self) -> &AppState {
836        self.app_state
837    }
838
839    pub fn save(&mut self) {
840        self.saved_viewports.push(self.viewport);
841        self.saved_window_origins.push(self.window_origin);
842    }
843
844    pub fn restore(&mut self) {
845        self.viewport = self.saved_viewports.pop().unwrap_or_default();
846        self.window_origin = self.saved_window_origins.pop().unwrap_or_default();
847    }
848
849    pub fn current_viewport(&self) -> Rect {
850        self.viewport
851    }
852
853    /// Internal method used by Floem. This method derives its calculations based on the [Taffy Node](taffy::tree::NodeId) returned by the `View::layout` method.
854    ///
855    /// It's responsible for:
856    /// - calculating and setting the view's origin (local coordinates and window coordinates)
857    /// - calculating and setting the view's viewport
858    /// - invoking any attached `context::ResizeListener`s
859    ///
860    /// Returns the bounding rect that encompasses this view and its children
861    pub fn compute_view_layout(&mut self, id: ViewId) -> Option<Rect> {
862        let view_state = id.state();
863
864        if view_state.borrow().is_hidden_state == IsHiddenState::Hidden {
865            view_state.borrow_mut().layout_rect = Rect::ZERO;
866            return None;
867        }
868
869        self.save();
870
871        let layout = id.get_layout().unwrap_or_default();
872        let origin = Point::new(layout.location.x as f64, layout.location.y as f64);
873        let this_viewport = view_state.borrow().viewport;
874        let this_viewport_origin = this_viewport.unwrap_or_default().origin().to_vec2();
875        let size = Size::new(layout.size.width as f64, layout.size.height as f64);
876        let parent_viewport = self.viewport.with_origin(
877            Point::new(
878                self.viewport.x0 - layout.location.x as f64,
879                self.viewport.y0 - layout.location.y as f64,
880            ) + this_viewport_origin,
881        );
882        self.viewport = parent_viewport.intersect(size.to_rect());
883        if let Some(this_viewport) = this_viewport {
884            self.viewport = self.viewport.intersect(this_viewport);
885        }
886
887        let window_origin = origin + self.window_origin.to_vec2() - this_viewport_origin;
888        self.window_origin = window_origin;
889        {
890            view_state.borrow_mut().window_origin = window_origin;
891        }
892
893        let resize_listener = view_state.borrow().resize_listener.clone();
894        if let Some(resize) = resize_listener.as_ref() {
895            let mut resize = resize.borrow_mut();
896            let new_rect = size.to_rect().with_origin(origin);
897            if new_rect != resize.rect {
898                resize.rect = new_rect;
899                (*resize.callback)(new_rect);
900            }
901        }
902
903        let move_listener = view_state.borrow().move_listener.clone();
904        if let Some(listener) = move_listener {
905            let mut listener = listener.borrow_mut();
906            if window_origin != listener.window_origin {
907                listener.window_origin = window_origin;
908                (*listener.callback)(window_origin);
909            }
910        }
911
912        let view = id.view();
913        let child_layout_rect = view.borrow_mut().compute_layout(self);
914
915        let layout_rect = size.to_rect().with_origin(self.window_origin);
916        let layout_rect = if let Some(child_layout_rect) = child_layout_rect {
917            layout_rect.union(child_layout_rect)
918        } else {
919            layout_rect
920        };
921
922        view_state.borrow_mut().layout_rect = layout_rect;
923
924        self.restore();
925
926        Some(layout_rect)
927    }
928}
929
930/// Holds current layout state for given position in the tree.
931/// You'll use this in the `View::layout` implementation to call `layout_node` on children and to access any font
932pub struct LayoutCx<'a> {
933    pub(crate) app_state: &'a mut AppState,
934}
935
936impl<'a> LayoutCx<'a> {
937    pub(crate) fn new(app_state: &'a mut AppState) -> Self {
938        Self { app_state }
939    }
940
941    pub fn app_state_mut(&mut self) -> &mut AppState {
942        self.app_state
943    }
944
945    pub fn app_state(&self) -> &AppState {
946        self.app_state
947    }
948
949    /// Responsible for invoking the recalculation of style and thus the layout and
950    /// creating or updating the layout of child nodes within the closure.
951    ///
952    /// You should ensure that all children are laid out within the closure and/or whatever
953    /// other work you need to do to ensure that the layout for the returned nodes is correct.
954    pub fn layout_node(
955        &mut self,
956        id: ViewId,
957        has_children: bool,
958        mut children: impl FnMut(&mut LayoutCx) -> Vec<NodeId>,
959    ) -> NodeId {
960        let view_state = id.state();
961        let node = view_state.borrow().node;
962        if !view_state
963            .borrow()
964            .requested_changes
965            .contains(ChangeFlags::LAYOUT)
966        {
967            return node;
968        }
969        view_state
970            .borrow_mut()
971            .requested_changes
972            .remove(ChangeFlags::LAYOUT);
973        let layout_style = view_state.borrow().layout_props.to_style();
974        let animate_out_display = view_state.borrow().is_hidden_state.get_display();
975        let style = view_state
976            .borrow()
977            .combined_style
978            .clone()
979            .apply(layout_style)
980            .apply_opt(animate_out_display, Style::display)
981            .to_taffy_style();
982        let _ = id.taffy().borrow_mut().set_style(node, style);
983
984        if has_children {
985            let nodes = children(self);
986            let _ = id.taffy().borrow_mut().set_children(node, &nodes);
987        }
988
989        node
990    }
991
992    /// Internal method used by Floem to invoke the user-defined `View::layout` method.
993    pub fn layout_view(&mut self, view: &mut dyn View) -> NodeId {
994        view.layout(self)
995    }
996}
997
998std::thread_local! {
999    /// Holds the ID of a View being painted very briefly if it is being rendered as
1000    /// a moving drag image.  Since that is a relatively unusual thing to need, it
1001    /// makes more sense to use a thread local for it and avoid cluttering the fields
1002    /// and memory footprint of PaintCx or PaintState or ViewId with a field for it.
1003    /// This is ephemerally set before paint calls that are painting the view in a
1004    /// location other than its natural one for purposes of drag and drop.
1005    static CURRENT_DRAG_PAINTING_ID : std::cell::Cell<Option<ViewId>> = const { std::cell::Cell::new(None) };
1006}
1007
1008pub struct PaintCx<'a> {
1009    pub(crate) app_state: &'a mut AppState,
1010    pub(crate) paint_state: &'a mut PaintState,
1011    pub(crate) transform: Affine,
1012    pub(crate) clip: Option<RoundedRect>,
1013    pub(crate) z_index: Option<i32>,
1014    pub(crate) saved_transforms: Vec<Affine>,
1015    pub(crate) saved_clips: Vec<Option<RoundedRect>>,
1016    pub(crate) saved_z_indexes: Vec<Option<i32>>,
1017    pub gpu_resources: Option<GpuResources>,
1018    pub window: Arc<dyn Window>,
1019    #[cfg(feature = "vello")]
1020    pub layer_count: usize,
1021    #[cfg(feature = "vello")]
1022    pub saved_layer_counts: Vec<usize>,
1023}
1024
1025impl PaintCx<'_> {
1026    pub fn save(&mut self) {
1027        self.saved_transforms.push(self.transform);
1028        self.saved_clips.push(self.clip);
1029        self.saved_z_indexes.push(self.z_index);
1030        #[cfg(feature = "vello")]
1031        self.saved_layer_counts.push(self.layer_count);
1032    }
1033
1034    pub fn restore(&mut self) {
1035        #[cfg(feature = "vello")]
1036        {
1037            let saved_count = self.saved_layer_counts.pop().unwrap_or_default();
1038            while self.layer_count > saved_count {
1039                self.pop_layer();
1040                self.layer_count -= 1;
1041            }
1042        }
1043
1044        self.transform = self.saved_transforms.pop().unwrap_or_default();
1045        self.clip = self.saved_clips.pop().unwrap_or_default();
1046        self.z_index = self.saved_z_indexes.pop().unwrap_or_default();
1047        self.paint_state
1048            .renderer_mut()
1049            .set_transform(self.transform);
1050        if let Some(z_index) = self.z_index {
1051            self.paint_state.renderer_mut().set_z_index(z_index);
1052        } else {
1053            self.paint_state.renderer_mut().set_z_index(0);
1054        }
1055
1056        #[cfg(not(feature = "vello"))]
1057        {
1058            if let Some(rect) = self.clip {
1059                self.paint_state.renderer_mut().clip(&rect);
1060            } else {
1061                self.paint_state.renderer_mut().clear_clip();
1062            }
1063        }
1064    }
1065
1066    /// Allows a `View` to determine if it is being called in order to
1067    /// paint a *draggable* image of itself during a drag (likely
1068    /// `draggable()` was called on the `View` or `ViewId`) as opposed
1069    /// to a normal paint in order to alter the way it renders itself.
1070    pub fn is_drag_paint(&self, id: ViewId) -> bool {
1071        // This could be an associated function, but it is likely
1072        // a Good Thing to restrict access to cases when the caller actually
1073        // has a PaintCx, and that doesn't make it a breaking change to
1074        // use instance methods in the future.
1075        if let Some(dragging) = CURRENT_DRAG_PAINTING_ID.get() {
1076            return dragging == id;
1077        }
1078        false
1079    }
1080
1081    /// paint the children of this view
1082    pub fn paint_children(&mut self, id: ViewId) {
1083        let children = id.children();
1084        for child in children {
1085            self.paint_view(child);
1086        }
1087    }
1088
1089    /// The entry point for painting a view. You shouldn't need to implement this yourself. Instead, implement [`View::paint`].
1090    /// It handles the internal work before and after painting [`View::paint`] implementations.
1091    /// It is responsible for
1092    /// - managing hidden status
1093    /// - clipping
1094    /// - painting computed styles like background color, border, font-styles, and z-index and handling painting requirements of drag and drop
1095    pub fn paint_view(&mut self, id: ViewId) {
1096        if id.is_hidden() {
1097            return;
1098        }
1099        let view = id.view();
1100        let view_state = id.state();
1101
1102        self.save();
1103        let size = self.transform(id);
1104        let is_empty = self
1105            .clip
1106            .map(|rect| rect.rect().intersect(size.to_rect()).is_zero_area())
1107            .unwrap_or(false);
1108        if !is_empty {
1109            let style = view_state.borrow().combined_style.clone();
1110            let view_style_props = view_state.borrow().view_style_props.clone();
1111            let layout_props = view_state.borrow().layout_props.clone();
1112
1113            if let Some(z_index) = style.get(ZIndex) {
1114                self.set_z_index(z_index);
1115            }
1116
1117            paint_bg(self, &view_style_props, size);
1118
1119            view.borrow_mut().paint(self);
1120            paint_border(self, &layout_props, &view_style_props, size);
1121            paint_outline(self, &view_style_props, size)
1122        }
1123        let mut drag_set_to_none = false;
1124
1125        if let Some(dragging) = self.app_state.dragging.as_ref() {
1126            if dragging.id == id {
1127                let transform = if let Some((released_at, release_location)) =
1128                    dragging.released_at.zip(dragging.release_location)
1129                {
1130                    let easing = Linear;
1131                    const ANIMATION_DURATION_MS: f64 = 300.0;
1132                    let elapsed = released_at.elapsed().as_millis() as f64;
1133                    let progress = elapsed / ANIMATION_DURATION_MS;
1134
1135                    if !(easing.finished(progress)) {
1136                        let offset_scale = 1.0 - easing.eval(progress);
1137                        let release_offset = release_location.to_vec2() - dragging.offset;
1138
1139                        // Schedule next animation frame
1140                        exec_after(Duration::from_millis(8), move |_| {
1141                            id.request_paint();
1142                        });
1143
1144                        Some(self.transform * Affine::translate(release_offset * offset_scale))
1145                    } else {
1146                        drag_set_to_none = true;
1147                        None
1148                    }
1149                } else {
1150                    // Handle active dragging
1151                    let translation =
1152                        self.app_state.last_cursor_location.to_vec2() - dragging.offset;
1153                    Some(self.transform.with_translation(translation))
1154                };
1155
1156                if let Some(transform) = transform {
1157                    self.save();
1158                    self.transform = transform;
1159                    self.paint_state
1160                        .renderer_mut()
1161                        .set_transform(self.transform);
1162                    self.set_z_index(1000);
1163                    self.clear_clip();
1164
1165                    // Apply styles
1166                    let style = view_state.borrow().combined_style.clone();
1167                    let mut view_style_props = view_state.borrow().view_style_props.clone();
1168
1169                    if let Some(dragging_style) = view_state.borrow().dragging_style.clone() {
1170                        let style = style.apply(dragging_style);
1171                        let mut _new_frame = false;
1172                        view_style_props.read_explicit(
1173                            &style,
1174                            &style,
1175                            &Instant::now(),
1176                            &mut _new_frame,
1177                        );
1178                    }
1179
1180                    // Paint with drag styling
1181                    let layout_props = view_state.borrow().layout_props.clone();
1182
1183                    // Important: If any method early exit points are added in this
1184                    // code block, they MUST call CURRENT_DRAG_PAINTING_ID.take() before
1185                    // returning.
1186
1187                    CURRENT_DRAG_PAINTING_ID.set(Some(id));
1188
1189                    paint_bg(self, &view_style_props, size);
1190                    view.borrow_mut().paint(self);
1191                    paint_border(self, &layout_props, &view_style_props, size);
1192                    paint_outline(self, &view_style_props, size);
1193
1194                    self.restore();
1195
1196                    CURRENT_DRAG_PAINTING_ID.take();
1197                }
1198            }
1199        }
1200
1201        if drag_set_to_none {
1202            self.app_state.dragging = None;
1203        }
1204        self.restore();
1205    }
1206
1207    /// Clip the drawing area to the given shape.
1208    pub fn clip(&mut self, shape: &impl Shape) {
1209        #[cfg(feature = "vello")]
1210        {
1211            use peniko::Mix;
1212
1213            self.push_layer(Mix::Normal, 1.0, Affine::IDENTITY, shape);
1214            self.layer_count += 1;
1215            self.clip = Some(shape.bounding_box().to_rounded_rect(0.0));
1216        }
1217
1218        #[cfg(not(feature = "vello"))]
1219        {
1220            let rect = if let Some(rect) = shape.as_rect() {
1221                rect.to_rounded_rect(0.0)
1222            } else if let Some(rect) = shape.as_rounded_rect() {
1223                rect
1224            } else {
1225                let rect = shape.bounding_box();
1226                rect.to_rounded_rect(0.0)
1227            };
1228
1229            let rect = if let Some(existing) = self.clip {
1230                let rect = existing.rect().intersect(rect.rect());
1231                self.paint_state.renderer_mut().clip(&rect);
1232                rect.to_rounded_rect(0.0)
1233            } else {
1234                self.paint_state.renderer_mut().clip(&shape);
1235                rect
1236            };
1237
1238            self.clip = Some(rect);
1239        }
1240    }
1241
1242    /// Remove clipping so the entire window can be rendered to.
1243    pub fn clear_clip(&mut self) {
1244        self.clip = None;
1245        self.paint_state.renderer_mut().clear_clip();
1246    }
1247
1248    pub fn offset(&mut self, offset: (f64, f64)) {
1249        let mut new = self.transform.as_coeffs();
1250        new[4] += offset.0;
1251        new[5] += offset.1;
1252        self.transform = Affine::new(new);
1253        self.paint_state
1254            .renderer_mut()
1255            .set_transform(self.transform);
1256        if let Some(rect) = self.clip.as_mut() {
1257            let raidus = rect.radii();
1258            *rect = rect
1259                .rect()
1260                .with_origin(rect.origin() - Vec2::new(offset.0, offset.1))
1261                .to_rounded_rect(raidus);
1262        }
1263    }
1264
1265    pub fn transform(&mut self, id: ViewId) -> Size {
1266        if let Some(layout) = id.get_layout() {
1267            let offset = layout.location;
1268            self.transform *= Affine::translate(Vec2 {
1269                x: offset.x as f64,
1270                y: offset.y as f64,
1271            });
1272            self.transform *= id.state().borrow().transform;
1273
1274            self.paint_state
1275                .renderer_mut()
1276                .set_transform(self.transform);
1277
1278            if let Some(rect) = self.clip.as_mut() {
1279                let raidus = rect.radii();
1280                *rect = rect
1281                    .rect()
1282                    .with_origin(rect.origin() - Vec2::new(offset.x as f64, offset.y as f64))
1283                    .to_rounded_rect(raidus);
1284            }
1285
1286            Size::new(layout.size.width as f64, layout.size.height as f64)
1287        } else {
1288            Size::ZERO
1289        }
1290    }
1291
1292    pub(crate) fn set_z_index(&mut self, z_index: i32) {
1293        self.z_index = Some(z_index);
1294        self.paint_state.renderer_mut().set_z_index(z_index);
1295    }
1296
1297    pub fn is_focused(&self, id: ViewId) -> bool {
1298        self.app_state.is_focused(&id)
1299    }
1300}
1301
1302// TODO: should this be private?
1303pub enum PaintState {
1304    /// The renderer is not yet initialized. This state is used to wait for the GPU resources to be acquired.
1305    PendingGpuResources {
1306        window: Arc<dyn Window>,
1307        rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1308        font_embolden: f32,
1309        /// This field holds an instance of `Renderer::Uninitialized` until the GPU resources are acquired,
1310        /// which will be returned in `PaintState::renderer` and `PaintState::renderer_mut`.
1311        /// All calls to renderer methods will be no-ops until the renderer is initialized.
1312        ///
1313        /// Previously, `PaintState::renderer` and `PaintState::renderer_mut` would panic if called when the renderer was uninitialized.
1314        /// However, this turned out to be hard to handle properly and led to panics, especially since the rest of the application code can't control when the renderer is initialized.
1315        renderer: crate::renderer::Renderer,
1316    },
1317    /// The renderer is initialized and ready to paint.
1318    Initialized { renderer: crate::renderer::Renderer },
1319}
1320
1321impl PaintState {
1322    pub fn new_pending(
1323        window: Arc<dyn Window>,
1324        rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1325        scale: f64,
1326        size: Size,
1327        font_embolden: f32,
1328    ) -> Self {
1329        Self::PendingGpuResources {
1330            window,
1331            rx,
1332            font_embolden,
1333            renderer: Renderer::Uninitialized { scale, size },
1334        }
1335    }
1336
1337    pub fn new(
1338        window: Arc<dyn Window>,
1339        surface: wgpu::Surface<'static>,
1340        gpu_resources: GpuResources,
1341        scale: f64,
1342        size: Size,
1343        font_embolden: f32,
1344    ) -> Self {
1345        let renderer = crate::renderer::Renderer::new(
1346            window.clone(),
1347            gpu_resources,
1348            surface,
1349            scale,
1350            size,
1351            font_embolden,
1352        );
1353        Self::Initialized { renderer }
1354    }
1355
1356    pub(crate) fn renderer(&self) -> &crate::renderer::Renderer {
1357        match self {
1358            PaintState::PendingGpuResources { renderer, .. } => renderer,
1359            PaintState::Initialized { renderer } => renderer,
1360        }
1361    }
1362
1363    pub(crate) fn renderer_mut(&mut self) -> &mut crate::renderer::Renderer {
1364        match self {
1365            PaintState::PendingGpuResources { renderer, .. } => renderer,
1366            PaintState::Initialized { renderer } => renderer,
1367        }
1368    }
1369
1370    pub(crate) fn resize(&mut self, scale: f64, size: Size) {
1371        self.renderer_mut().resize(scale, size);
1372    }
1373
1374    pub(crate) fn set_scale(&mut self, scale: f64) {
1375        self.renderer_mut().set_scale(scale);
1376    }
1377}
1378
1379pub struct UpdateCx<'a> {
1380    pub(crate) app_state: &'a mut AppState,
1381}
1382
1383impl UpdateCx<'_> {
1384    pub fn app_state_mut(&mut self) -> &mut AppState {
1385        self.app_state
1386    }
1387
1388    pub fn app_state(&self) -> &AppState {
1389        self.app_state
1390    }
1391}
1392
1393impl Deref for PaintCx<'_> {
1394    type Target = crate::renderer::Renderer;
1395
1396    fn deref(&self) -> &Self::Target {
1397        self.paint_state.renderer()
1398    }
1399}
1400
1401impl DerefMut for PaintCx<'_> {
1402    fn deref_mut(&mut self) -> &mut Self::Target {
1403        self.paint_state.renderer_mut()
1404    }
1405}
1406
1407fn animations_on_remove(id: ViewId, scope: Scope) -> u16 {
1408    let mut wait_for = 0;
1409    let state = id.state();
1410    let mut state = state.borrow_mut();
1411    state.num_waiting_animations = 0;
1412    let animations = &mut state.animations.stack;
1413    let mut request_style = false;
1414    for anim in animations {
1415        if anim.run_on_remove && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1416            anim.reverse_mut();
1417            request_style = true;
1418            wait_for += 1;
1419            let trigger = anim.on_visual_complete;
1420            scope.create_updater(
1421                move || trigger.track(),
1422                move |_| {
1423                    id.transition_anim_complete();
1424                },
1425            );
1426        }
1427    }
1428    drop(state);
1429    if request_style {
1430        id.request_style();
1431    }
1432
1433    id.children()
1434        .into_iter()
1435        .fold(wait_for, |acc, id| acc + animations_on_remove(id, scope))
1436}
1437fn stop_reset_remove_animations(id: ViewId) {
1438    let state = id.state();
1439    let mut state = state.borrow_mut();
1440    let animations = &mut state.animations.stack;
1441    let mut request_style = false;
1442    for anim in animations {
1443        if anim.run_on_remove
1444            && anim.state_kind() == AnimStateKind::PassInProgress
1445            && !matches!(anim.repeat_mode, RepeatMode::LoopForever)
1446        {
1447            anim.start_mut();
1448            request_style = true;
1449        }
1450    }
1451    drop(state);
1452    if request_style {
1453        id.request_style();
1454    }
1455
1456    id.children()
1457        .into_iter()
1458        .for_each(stop_reset_remove_animations)
1459}
1460
1461fn animations_on_create(id: ViewId) {
1462    let state = id.state();
1463    let mut state = state.borrow_mut();
1464    state.num_waiting_animations = 0;
1465    let animations = &mut state.animations.stack;
1466    let mut request_style = false;
1467    for anim in animations {
1468        if anim.run_on_create && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1469            anim.start_mut();
1470            request_style = true;
1471        }
1472    }
1473    drop(state);
1474    if request_style {
1475        id.request_style();
1476    }
1477
1478    id.children().into_iter().for_each(animations_on_create);
1479}