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