floem/
context.rs

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