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