1use floem_reactive::Scope;
2use floem_renderer::Renderer as FloemRenderer;
3use floem_renderer::gpu_resources::{GpuResourceError, GpuResources};
4use peniko::kurbo::{Affine, Point, Rect, RoundedRect, Shape, Size, Vec2};
5use smallvec::SmallVec;
6use std::{
7 ops::{Deref, DerefMut},
8 rc::Rc,
9 sync::Arc,
10};
11use ui_events::keyboard::{Key, KeyState, KeyboardEvent, Modifiers, NamedKey};
12use ui_events::pointer::{PointerButton, PointerButtonEvent, PointerEvent, PointerUpdate};
13use winit::window::Window;
14
15#[cfg(not(target_arch = "wasm32"))]
16use std::time::{Duration, Instant};
17#[cfg(target_arch = "wasm32")]
18use web_time::{Duration, Instant};
19
20#[cfg(feature = "crossbeam")]
21use crossbeam::channel::Receiver;
22#[cfg(not(feature = "crossbeam"))]
23use std::sync::mpsc::Receiver;
24
25use taffy::prelude::NodeId;
26use taffy::style::Position;
27
28use crate::animate::{AnimStateKind, RepeatMode};
29use crate::dropped_file::FileDragEvent;
30use crate::easing::{Easing, Linear};
31use crate::menu::Menu;
32use crate::renderer::Renderer;
33use crate::responsive::ScreenSizeBp;
34use crate::style::{
35 Focusable, PointerEvents, PointerEventsProp, PositionProp, StyleClassRef, ZIndex,
36 resolve_nested_maps,
37};
38use crate::view_state::{IsHiddenState, StackingInfo};
39use crate::{
40 action::{exec_after, show_context_menu},
41 event::{Event, EventListener, EventPropagation},
42 id::ViewId,
43 inspector::CaptureState,
44 nav::view_arrow_navigation,
45 style::{Style, StyleProp, StyleSelector},
46 view::{View, paint_bg, paint_border, paint_outline, view_tab_navigation},
47 view_state::ChangeFlags,
48 window_state::WindowState,
49};
50
51pub type EventCallback = dyn FnMut(&Event) -> EventPropagation;
52pub type ResizeCallback = dyn Fn(Rect);
53pub type MenuCallback = dyn Fn() -> Menu;
54
55#[derive(Default)]
56pub(crate) struct ResizeListeners {
57 pub(crate) rect: Rect,
58 pub(crate) callbacks: Vec<Rc<ResizeCallback>>,
59}
60
61#[derive(Default)]
63pub(crate) struct MoveListeners {
64 pub(crate) window_origin: Point,
65 pub(crate) callbacks: Vec<Rc<dyn Fn(Point)>>,
66}
67
68pub(crate) type CleanupListeners = Vec<Rc<dyn Fn()>>;
69
70pub struct DragState {
71 pub(crate) id: ViewId,
72 pub(crate) offset: Vec2,
73 pub(crate) released_at: Option<Instant>,
74 pub(crate) release_location: Option<Point>,
75}
76
77pub(crate) enum FrameUpdate {
78 Style(ViewId),
79 Layout(ViewId),
80 Paint(ViewId),
81}
82
83#[derive(Debug, PartialEq, Eq)]
84pub(crate) enum PointerEventConsumed {
85 Yes,
86 No,
87}
88
89pub(crate) fn children_in_paint_order(parent_id: ViewId) -> Vec<ViewId> {
92 let children = parent_id.children();
93
94 if children.len() <= 1 {
95 return children;
96 }
97
98 let needs_sort = children
101 .iter()
102 .any(|&id| id.state().borrow().stacking_info.effective_z_index != 0);
103
104 if !needs_sort {
105 return children;
106 }
107
108 let mut sortable: SmallVec<[(ViewId, i32, usize); 8]> = children
111 .iter()
112 .enumerate()
113 .map(|(dom_order, &child_id)| {
114 let z_index = child_id.state().borrow().stacking_info.effective_z_index;
115 (child_id, z_index, dom_order)
116 })
117 .collect();
118
119 sortable.sort_by(|a, b| a.1.cmp(&b.1));
121
122 sortable.into_iter().map(|(id, _, _)| id).collect()
123}
124
125pub struct EventCx<'a> {
127 pub window_state: &'a mut WindowState,
128}
129
130impl EventCx<'_> {
131 pub fn update_active(&mut self, id: ViewId) {
132 self.window_state.update_active(id);
133 }
134
135 pub fn is_active(&self, id: ViewId) -> bool {
136 self.window_state.is_active(&id)
137 }
138
139 #[allow(unused)]
140 pub(crate) fn update_focus(&mut self, id: ViewId, keyboard_navigation: bool) {
141 self.window_state.update_focus(id, keyboard_navigation);
142 }
143
144 pub(crate) fn unconditional_view_event(
146 &mut self,
147 view_id: ViewId,
148 event: Event,
149 directed: bool,
150 ) -> (EventPropagation, PointerEventConsumed) {
151 if view_id.is_hidden() {
152 return (EventPropagation::Continue, PointerEventConsumed::No);
154 }
155 if view_id.is_disabled() && !event.allow_disabled() {
156 return (EventPropagation::Continue, PointerEventConsumed::No);
159 }
160
161 let event = self.offset_event(view_id, event);
168
169 let view = view_id.view();
170 let view_state = view_id.state();
171
172 let disable_default = if let Some(listener) = event.listener() {
173 view_state
174 .borrow()
175 .disable_default_events
176 .contains(&listener)
177 } else {
178 false
179 };
180
181 let is_pointer_none = event.is_pointer()
182 && view_state.borrow().computed_style.get(PointerEventsProp)
183 == Some(PointerEvents::None);
184
185 if !disable_default
186 && !is_pointer_none
187 && view
188 .borrow_mut()
189 .event_before_children(self, &event)
190 .is_processed()
191 {
192 if let Event::Pointer(PointerEvent::Down(PointerButtonEvent { state, .. })) = &event {
193 if view_state.borrow().computed_style.get(Focusable) {
194 let rect = view_id.get_size().unwrap_or_default().to_rect();
195 let point = state.logical_point();
196 let now_focused = rect.contains(point);
197 if now_focused {
198 self.window_state.update_focus(view_id, false);
199 }
200 }
201 }
202 if let Event::Pointer(PointerEvent::Move(_)) = &event {
203 let view_state = view_state.borrow();
204 let style = view_state.combined_style.builtin();
205 if let Some(cursor) = style.cursor() {
206 if self.window_state.cursor.is_none() {
207 self.window_state.cursor = Some(cursor);
208 }
209 }
210 }
211 return (EventPropagation::Stop, PointerEventConsumed::Yes);
212 }
213
214 let mut view_pointer_event_consumed = PointerEventConsumed::No;
215
216 if !directed {
217 let children = children_in_paint_order(view_id);
218 for child in children.into_iter().rev() {
219 if !self.should_send(child, &event) {
220 continue;
221 }
222 let (event_propagation, pointer_event_consumed) =
223 self.unconditional_view_event(child, event.clone(), false);
224 if event_propagation.is_processed() {
225 return (EventPropagation::Stop, PointerEventConsumed::Yes);
226 }
227 if event.is_pointer() && pointer_event_consumed == PointerEventConsumed::Yes {
228 view_pointer_event_consumed = PointerEventConsumed::Yes;
233 break;
234 }
235 }
236 }
237
238 if !disable_default
239 && !is_pointer_none
240 && view
241 .borrow_mut()
242 .event_after_children(self, &event)
243 .is_processed()
244 {
245 return (EventPropagation::Stop, PointerEventConsumed::Yes);
246 }
247
248 if is_pointer_none {
249 return (EventPropagation::Continue, view_pointer_event_consumed);
251 }
252
253 if !disable_default {
255 let popout_menu = || {
256 let bottom_left = {
257 let layout = view_state.borrow().layout_rect;
258 Point::new(layout.x0, layout.y1)
259 };
260
261 let popout_menu = view_state.borrow().popout_menu.clone();
262 show_context_menu(popout_menu?(), Some(bottom_left));
263 Some((EventPropagation::Stop, PointerEventConsumed::Yes))
264 };
265
266 match &event {
267 Event::Pointer(PointerEvent::Down(PointerButtonEvent {
268 pointer,
269 state,
270 button,
271 ..
272 })) => {
273 self.window_state.clicking.insert(view_id);
274 let point = state.logical_point();
275 if pointer.is_primary_pointer()
276 && button.is_none_or(|b| b == PointerButton::Primary)
277 {
278 let rect = view_id.get_size().unwrap_or_default().to_rect();
279 let on_view = rect.contains(point);
280
281 if on_view {
282 if view_state.borrow().computed_style.get(Focusable) {
283 self.window_state.update_focus(view_id, false);
285 }
286 if state.count == 2
287 && view_state
288 .borrow()
289 .event_listeners
290 .contains_key(&EventListener::DoubleClick)
291 {
292 view_state.borrow_mut().last_pointer_down = Some(state.clone());
293 }
294 if view_state
295 .borrow()
296 .event_listeners
297 .contains_key(&EventListener::Click)
298 {
299 view_state.borrow_mut().last_pointer_down = Some(state.clone());
300 }
301
302 #[cfg(target_os = "macos")]
303 if let Some((ep, pec)) = popout_menu() {
304 return (ep, pec);
305 };
306
307 let bottom_left = {
308 let layout = view_state.borrow().layout_rect;
309 Point::new(layout.x0, layout.y1)
310 };
311 let popout_menu = view_state.borrow().popout_menu.clone();
312 if let Some(menu) = popout_menu {
313 show_context_menu(menu(), Some(bottom_left));
314 return (EventPropagation::Stop, PointerEventConsumed::Yes);
315 }
316 if view_id.can_drag() && self.window_state.drag_start.is_none() {
317 self.window_state.drag_start = Some((view_id, point));
318 }
319 }
320 } else if button.is_some_and(|b| b == PointerButton::Secondary) {
321 let rect = view_id.get_size().unwrap_or_default().to_rect();
322 let on_view = rect.contains(point);
323
324 if on_view {
325 if view_state.borrow().computed_style.get(Focusable) {
326 self.window_state.update_focus(view_id, false);
328 }
329 if view_state
330 .borrow()
331 .event_listeners
332 .contains_key(&EventListener::SecondaryClick)
333 {
334 view_state.borrow_mut().last_pointer_down = Some(state.clone());
335 }
336 }
337 }
338 }
339 Event::Pointer(PointerEvent::Move(PointerUpdate { current, .. })) => {
340 let rect = view_id.get_size().unwrap_or_default().to_rect();
341 if rect.contains(current.logical_point()) {
342 if self.window_state.is_dragging() {
343 self.window_state.dragging_over.insert(view_id);
344 view_id.apply_event(&EventListener::DragOver, &event);
345 } else {
346 self.window_state.hovered.insert(view_id);
347 let view_state = view_state.borrow();
348 let style = view_state.combined_style.builtin();
349 if let Some(cursor) = style.cursor() {
350 if self.window_state.cursor.is_none() {
351 self.window_state.cursor = Some(cursor);
352 }
353 }
354 }
355 }
356 if view_id.can_drag() {
357 if let Some((_, drag_start)) = self
358 .window_state
359 .drag_start
360 .as_ref()
361 .filter(|(drag_id, _)| drag_id == &view_id)
362 {
363 let offset = current.logical_point() - *drag_start;
364 if let Some(dragging) = self
365 .window_state
366 .dragging
367 .as_mut()
368 .filter(|d| d.id == view_id && d.released_at.is_none())
369 {
370 dragging.offset = drag_start.to_vec2();
372 self.window_state.request_paint(view_id);
373 } else if offset.x.abs() + offset.y.abs() > 1.0 {
374 self.window_state.active = None;
376 self.window_state.dragging = Some(DragState {
377 id: view_id,
378 offset: drag_start.to_vec2(),
379 released_at: None,
380 release_location: None,
381 });
382 self.update_active(view_id);
383 self.window_state.request_paint(view_id);
384 view_id.apply_event(&EventListener::DragStart, &event);
385 }
386 }
387 }
388 if view_id
389 .apply_event(&EventListener::PointerMove, &event)
390 .is_some_and(|prop| prop.is_processed())
391 {
392 return (EventPropagation::Stop, PointerEventConsumed::Yes);
393 }
394 }
395 Event::Pointer(PointerEvent::Up(PointerButtonEvent {
396 button,
397 pointer,
398 state,
399 })) => {
400 if pointer.is_primary_pointer()
401 && button.is_none_or(|b| b == PointerButton::Primary)
402 {
403 let rect = view_id.get_size().unwrap_or_default().to_rect();
404 let on_view = rect.contains(state.logical_point());
405
406 #[cfg(not(target_os = "macos"))]
407 if on_view {
408 if let Some((ep, pec)) = popout_menu() {
409 return (ep, pec);
410 };
411 }
412
413 if !directed {
414 if on_view {
415 if let Some(dragging) = self.window_state.dragging.as_mut() {
416 let dragging_id = dragging.id;
417 if view_id
418 .apply_event(&EventListener::Drop, &event)
419 .is_some_and(|prop| prop.is_processed())
420 {
421 self.window_state.dragging = None;
424 self.window_state.request_paint(view_id);
425 dragging_id.apply_event(&EventListener::DragEnd, &event);
426 }
427 }
428 }
429 } else if let Some(dragging) = self
430 .window_state
431 .dragging
432 .as_mut()
433 .filter(|d| d.id == view_id)
434 {
435 let dragging_id = dragging.id;
436 dragging.released_at = Some(Instant::now());
437 dragging.release_location = Some(state.logical_point());
438 self.window_state.request_paint(view_id);
439 dragging_id.apply_event(&EventListener::DragEnd, &event);
440 }
441
442 let last_pointer_down = view_state.borrow_mut().last_pointer_down.take();
443
444 let event_listeners = view_state.borrow().event_listeners.clone();
445 if let Some(handlers) = event_listeners.get(&EventListener::DoubleClick) {
446 view_state.borrow_mut();
447 if on_view
448 && self.window_state.is_clicking(&view_id)
449 && last_pointer_down
450 .as_ref()
451 .map(|s| s.count == 2)
452 .unwrap_or(false)
453 && handlers.iter().fold(false, |handled, handler| {
454 handled | (handler.borrow_mut())(&event).is_processed()
455 })
456 {
457 return (EventPropagation::Stop, PointerEventConsumed::Yes);
458 }
459 }
460
461 if let Some(handlers) = event_listeners.get(&EventListener::Click) {
462 if on_view
463 && self.window_state.is_clicking(&view_id)
464 && last_pointer_down.is_some()
465 && handlers.iter().fold(false, |handled, handler| {
466 handled | (handler.borrow_mut())(&event).is_processed()
467 })
468 {
469 return (EventPropagation::Stop, PointerEventConsumed::Yes);
470 }
471 }
472
473 if view_id
474 .apply_event(&EventListener::PointerUp, &event)
475 .is_some_and(|prop| prop.is_processed())
476 {
477 return (EventPropagation::Stop, PointerEventConsumed::Yes);
478 }
479 } else if button.is_some_and(|b| b == PointerButton::Secondary) {
480 let rect = view_id.get_size().unwrap_or_default().to_rect();
481 let on_view = rect.contains(state.logical_point());
482
483 let last_pointer_down = view_state.borrow_mut().last_pointer_down.take();
484 let event_listeners = view_state.borrow().event_listeners.clone();
485 if let Some(handlers) = event_listeners.get(&EventListener::SecondaryClick)
486 {
487 if on_view
488 && last_pointer_down.is_some()
489 && handlers.iter().fold(false, |handled, handler| {
490 handled | (handler.borrow_mut())(&event).is_processed()
491 })
492 {
493 return (EventPropagation::Stop, PointerEventConsumed::Yes);
494 }
495 }
496
497 let viewport_event_position = {
498 let layout = view_state.borrow().layout_rect;
499 Point::new(
500 layout.x0 + state.logical_point().x,
501 layout.y0 + state.logical_point().y,
502 )
503 };
504 let context_menu = view_state.borrow().context_menu.clone();
505 if let Some(menu) = context_menu {
506 show_context_menu(menu(), Some(viewport_event_position));
507 return (EventPropagation::Stop, PointerEventConsumed::Yes);
508 }
509 }
510 }
511 Event::Key(KeyboardEvent {
512 state: KeyState::Down,
513 ..
514 }) => {
515 if self.window_state.is_focused(&view_id) && event.is_keyboard_trigger() {
516 view_id.apply_event(&EventListener::Click, &event);
517 }
518 }
519 Event::WindowResized(_) => {
520 if view_state.borrow().has_style_selectors.has_responsive() {
521 view_id.request_style();
522 }
523 }
524 Event::FileDrag(e @ FileDragEvent::DragMoved { .. }) => {
525 if let Some(point) = e.logical_point() {
526 let rect = view_id.get_size().unwrap_or_default().to_rect();
527 let on_view = rect.contains(point);
528 if on_view {
529 self.window_state.file_hovered.insert(view_id);
530 view_id.request_style();
531 }
532 }
533 }
534 _ => (),
535 }
536 }
537
538 if !disable_default {
539 if let Some(listener) = event.listener() {
540 let event_listeners = view_state.borrow().event_listeners.clone();
541 if let Some(handlers) = event_listeners.get(&listener).cloned() {
542 let should_run = if let Some(pos) = event.point() {
543 let rect = view_id.get_size().unwrap_or_default().to_rect();
544 rect.contains(pos)
545 } else {
546 true
547 };
548 if should_run
549 && handlers.iter().fold(false, |handled, handler| {
550 handled | (handler.borrow_mut())(&event).is_processed()
551 })
552 {
553 return (EventPropagation::Stop, PointerEventConsumed::Yes);
554 }
555 }
556 }
557 }
558
559 (EventPropagation::Continue, PointerEventConsumed::Yes)
560 }
561
562 pub(crate) fn offset_event(&self, id: ViewId, event: Event) -> Event {
564 let state = id.state();
565 let viewport = state.borrow().viewport;
566 let transform = state.borrow().transform;
567
568 if let Some(layout) = id.get_layout() {
569 event.transform(
570 Affine::translate((
571 layout.location.x as f64 - viewport.map(|rect| rect.x0).unwrap_or(0.0),
572 layout.location.y as f64 - viewport.map(|rect| rect.y0).unwrap_or(0.0),
573 )) * transform,
574 )
575 } else {
576 event
577 }
578 }
579
580 pub fn should_send(&mut self, id: ViewId, event: &Event) -> bool {
583 if id.is_hidden() || (id.is_disabled() && !event.allow_disabled()) {
584 return false;
585 }
586
587 let Some(point) = event.point() else {
588 return true;
589 };
590
591 let layout_rect = id.layout_rect();
592 let Some(layout) = id.get_layout() else {
593 return false;
594 };
595
596 let current_rect = layout_rect.with_origin(Point::new(
598 layout.location.x as f64,
599 layout.location.y as f64,
600 ));
601 let transform = id.state().borrow().transform;
603 let current_rect = transform.transform_rect_bbox(current_rect);
604
605 if !current_rect.contains(point) {
606 return false;
607 }
608
609 true
610 }
611
612 pub(crate) fn dispatch_event(
629 &mut self,
630 root_id: ViewId,
631 main_view_id: ViewId,
632 event: Event,
633 ) -> EventPropagation {
634 let event = event.transform(Affine::scale(self.window_state.scale));
636
637 let is_pointer_move = if let Event::Pointer(PointerEvent::Move(pu)) = &event {
639 let pos = pu.current.logical_point();
640 self.window_state.last_cursor_location = pos;
641 Some(pu.pointer)
642 } else {
643 None
644 };
645
646 let (was_hovered, was_dragging_over) = if is_pointer_move.is_some() {
648 self.window_state.cursor = None;
649 let was_hovered = std::mem::take(&mut self.window_state.hovered);
650 let was_dragging_over = std::mem::take(&mut self.window_state.dragging_over);
651 (Some(was_hovered), Some(was_dragging_over))
652 } else {
653 (None, None)
654 };
655
656 let was_file_hovered = if matches!(event, Event::FileDrag(FileDragEvent::DragMoved { .. }))
658 || is_pointer_move.is_some()
659 {
660 if !self.window_state.file_hovered.is_empty() {
661 Some(std::mem::take(&mut self.window_state.file_hovered))
662 } else {
663 None
664 }
665 } else {
666 None
667 };
668
669 let is_pointer_down = matches!(&event, Event::Pointer(PointerEvent::Down { .. }));
671 let was_focused = if is_pointer_down {
672 self.window_state.clicking.clear();
673 self.window_state.focus.take()
674 } else {
675 self.window_state.focus
676 };
677
678 if event.needs_focus() {
680 let mut processed = false;
682
683 if let Some(id) = self.window_state.focus {
684 processed |= self
685 .unconditional_view_event(id, event.clone(), true)
686 .0
687 .is_processed();
688 }
689
690 if !processed {
691 if let Some(listener) = event.listener() {
692 processed |= main_view_id
693 .apply_event(&listener, &event)
694 .is_some_and(|prop| prop.is_processed());
695 }
696 }
697
698 if !processed {
699 if let Event::Key(KeyboardEvent {
701 key,
702 modifiers,
703 state: KeyState::Down,
704 ..
705 }) = &event
706 {
707 if *key == Key::Named(NamedKey::Tab)
708 && (modifiers.is_empty() || *modifiers == Modifiers::SHIFT)
709 {
710 let backwards = modifiers.contains(Modifiers::SHIFT);
711 view_tab_navigation(root_id, self.window_state, backwards);
712 } else if *modifiers == Modifiers::ALT {
713 if let Key::Named(
714 name @ (NamedKey::ArrowUp
715 | NamedKey::ArrowDown
716 | NamedKey::ArrowLeft
717 | NamedKey::ArrowRight),
718 ) = key
719 {
720 view_arrow_navigation(*name, self.window_state, root_id);
721 }
722 }
723 }
724
725 let keyboard_trigger_end = self.window_state.keyboard_navigation
727 && event.is_keyboard_trigger()
728 && matches!(
729 event,
730 Event::Key(KeyboardEvent {
731 state: KeyState::Up,
732 ..
733 })
734 );
735 if keyboard_trigger_end {
736 if let Some(id) = self.window_state.active {
737 if self
738 .window_state
739 .has_style_for_sel(id, StyleSelector::Active)
740 {
741 id.request_style_recursive();
742 }
743 self.window_state.active = None;
744 }
745 }
746 }
747 } else if self.window_state.active.is_some() && event.is_pointer() {
748 if self.window_state.is_dragging() {
750 self.unconditional_view_event(root_id, event.clone(), false);
751 }
752
753 let id = self.window_state.active.unwrap();
754
755 {
756 let window_origin = id.state().borrow().window_origin;
757 let layout = id.get_layout().unwrap_or_default();
758 let viewport = id.state().borrow().viewport.unwrap_or_default();
759 let transform = Affine::translate((
760 window_origin.x - layout.location.x as f64 + viewport.x0,
761 window_origin.y - layout.location.y as f64 + viewport.y0,
762 ));
763 self.unconditional_view_event(id, event.clone().transform(transform), true);
764 }
765
766 if let Event::Pointer(PointerEvent::Up { .. }) = &event {
767 if self
768 .window_state
769 .has_style_for_sel(id, StyleSelector::Active)
770 {
771 id.request_style_recursive();
772 }
773 self.window_state.active = None;
774 }
775 } else {
776 self.unconditional_view_event(root_id, event.clone(), false);
778 }
779
780 if let Event::Pointer(PointerEvent::Up { .. }) = &event {
782 self.window_state.drag_start = None;
783 }
784
785 if let Some(info) = is_pointer_move {
787 let hovered = &self.window_state.hovered.clone();
788 for id in was_hovered.unwrap().symmetric_difference(hovered) {
789 let view_state = id.state();
790 if view_state.borrow().has_active_animation()
791 || view_state
792 .borrow()
793 .has_style_selectors
794 .has(StyleSelector::Hover)
795 || view_state
796 .borrow()
797 .has_style_selectors
798 .has(StyleSelector::Active)
799 {
800 id.request_style();
801 }
802 if hovered.contains(id) {
803 id.apply_event(&EventListener::PointerEnter, &event);
804 } else {
805 self.unconditional_view_event(
806 *id,
807 Event::Pointer(PointerEvent::Leave(info)),
808 true,
809 );
810 }
811 }
812
813 let dragging_over = &self.window_state.dragging_over.clone();
815 for id in was_dragging_over
816 .unwrap()
817 .symmetric_difference(dragging_over)
818 {
819 if dragging_over.contains(id) {
820 id.apply_event(&EventListener::DragEnter, &event);
821 } else {
822 id.apply_event(&EventListener::DragLeave, &event);
823 }
824 }
825 }
826
827 if let Some(was_file_hovered) = was_file_hovered {
829 for id in was_file_hovered.symmetric_difference(&self.window_state.file_hovered) {
830 id.request_style();
831 }
832 }
833
834 if was_focused != self.window_state.focus {
836 self.window_state
837 .focus_changed(was_focused, self.window_state.focus);
838 }
839
840 if is_pointer_down {
842 for id in self.window_state.clicking.clone() {
843 if self
844 .window_state
845 .has_style_for_sel(id, StyleSelector::Active)
846 {
847 id.request_style_recursive();
848 }
849 }
850 }
851
852 if matches!(&event, Event::Pointer(PointerEvent::Up { .. })) {
854 for id in self.window_state.clicking.clone() {
855 if self
856 .window_state
857 .has_style_for_sel(id, StyleSelector::Active)
858 {
859 id.request_style_recursive();
860 }
861 }
862 self.window_state.clicking.clear();
863 }
864
865 EventPropagation::Continue
866 }
867}
868
869#[derive(Default, Debug, Clone, Copy)]
876pub struct InteractionState {
877 pub is_hovered: bool,
879 pub is_selected: bool,
881 pub is_disabled: bool,
883 pub is_hidden: bool,
885 pub is_focused: bool,
887 pub is_clicking: bool,
889 pub is_dark_mode: bool,
891 pub is_file_hover: bool,
893 pub using_keyboard_navigation: bool,
895}
896
897#[derive(Debug, Default, Clone, Copy)]
898pub(crate) struct InheritedInteractionCx {
899 pub(crate) disabled: bool,
900 pub(crate) hidden: bool,
901 pub(crate) selected: bool,
902}
903
904pub struct StyleCx<'a> {
905 pub window_state: &'a mut WindowState,
906 pub(crate) current_view: ViewId,
907 pub(crate) current: Style,
909 pub(crate) direct: Style,
910 pub(crate) now: Instant,
911 pub disabled: bool,
912 pub hidden: bool,
913 pub selected: bool,
914}
915impl StyleCx<'_> {
916 fn interaction_cx(&self) -> InheritedInteractionCx {
917 InheritedInteractionCx {
918 disabled: self.disabled,
919 hidden: self.hidden,
920 selected: self.selected,
921 }
922 }
923}
924
925impl<'a> StyleCx<'a> {
926 pub(crate) fn new(
927 window_state: &'a mut WindowState,
928 now: Instant,
929 view_id: ViewId,
930 default_style: impl FnOnce() -> Style,
931 ) -> Self {
932 let style_parent = view_id
934 .state()
935 .borrow()
936 .style_cx_parent
937 .or_else(|| view_id.parent());
938
939 let parent_cx = if let Some(p) = style_parent {
940 if let Some(style_cx) = p.state().borrow().style_cx.clone() {
942 style_cx
943 } else if p.parent().is_some() {
944 let mut parent_style_cx = StyleCx::new(window_state, now, p, default_style);
946 parent_style_cx.style_view();
947 p.state()
948 .borrow()
949 .style_cx
950 .clone()
951 .expect("cached in style_target")
952 } else {
953 let default = default_style();
955 p.state().borrow_mut().style_cx = Some(default.clone());
956 default
957 }
958 } else {
959 default_style()
960 };
961
962 let parent_style_interaction_cx = style_parent
964 .map(|p| {
965 let parent_state = p.state();
966 let parent_state = parent_state.borrow();
967 parent_state.style_interaction_cx
968 })
969 .unwrap_or_default();
970
971 Self {
972 window_state,
973 current_view: view_id,
974 current: parent_cx,
975 direct: Default::default(),
976 now,
977 disabled: parent_style_interaction_cx.disabled,
978 hidden: parent_style_interaction_cx.hidden,
979 selected: parent_style_interaction_cx.selected,
980 }
981 }
982
983 pub fn get_interact_state(&self, id: &ViewId) -> InteractionState {
984 let view_state = id.state();
985 let view_state = view_state.borrow();
986 let icx = view_state.parent_set_style_interaction;
987 InteractionState {
988 is_selected: self.selected | icx.selected,
989 is_disabled: self.disabled | icx.disabled,
990 is_hidden: self.hidden | icx.hidden,
991 is_hovered: self.window_state.is_hovered(id),
992 is_focused: self.window_state.is_focused(id),
993 is_clicking: self.window_state.is_clicking(id),
994 is_dark_mode: self.window_state.is_dark_mode(),
995 is_file_hover: self.window_state.is_file_hover(id),
996 using_keyboard_navigation: self.window_state.keyboard_navigation,
997 }
998 }
999
1000 pub fn style_view(&mut self) {
1001 let view_id = self.current_view;
1002
1003 let view = view_id.view();
1004 let view_state = view_id.state();
1005
1006 {
1007 let mut view_state = view_state.borrow_mut();
1008 if self.window_state.view_style_dirty.contains(&view_id) {
1009 self.window_state.view_style_dirty.remove(&view_id);
1010 if let Some(view_style) = view.borrow().view_style() {
1011 let offset = view_state.view_style_offset;
1012 view_state.style.set(offset, view_style);
1013 }
1014 }
1015
1016 if view_state.request_style_recursive {
1018 view_state.request_style_recursive = false;
1019 let children = view_id.children();
1020 for child in children {
1021 let view_state = child.state();
1022 let mut state = view_state.borrow_mut();
1023 state.request_style_recursive = true;
1024 self.window_state.style_dirty.insert(child);
1025 }
1026 }
1027 }
1028
1029 let view_interact_state = self.get_interact_state(&view_id);
1030 let view_class = view.borrow().view_class();
1031
1032 let (mut new_frame, classes_applied) = self.compute_combined(
1033 view_interact_state,
1034 self.window_state.screen_size_bp,
1035 view_class,
1036 &self.current.clone(),
1037 );
1038
1039 if classes_applied {
1040 let children = view_id.children();
1041 for child in children {
1042 let view_state = child.state();
1043 let mut state = view_state.borrow_mut();
1044 state.request_style_recursive = true;
1045 self.window_state.style_dirty.insert(child);
1046 }
1047 }
1048
1049 self.direct = view_state.borrow().combined_style.clone();
1050 self.current.apply_mut(self.direct.inherited());
1051 let computed_style = self.current.clone().apply(self.direct.clone());
1052 CaptureState::capture_style(view_id, self, computed_style.clone());
1053 view_state.borrow_mut().computed_style = computed_style.clone();
1054 let builtin = computed_style.builtin();
1055 self.hidden |= builtin.set_hidden() | (builtin.display() == taffy::Display::None);
1056 self.disabled |= builtin.set_disabled();
1057 self.selected |= builtin.set_selected();
1058 view_state.borrow_mut().style_cx = Some(self.current.clone());
1059 view_state.borrow_mut().style_interaction_cx = self.interaction_cx();
1060
1061 if builtin.focusable()
1062 && !builtin.set_disabled()
1063 && !builtin.set_hidden()
1064 && builtin.display() != taffy::Display::None
1065 {
1066 self.window_state.focusable.insert(view_id);
1067 } else {
1068 self.window_state.focusable.remove(&view_id);
1069 }
1070
1071 {
1072 let mut view_state = view_state.borrow_mut();
1073 view_state.layout_props.read_explicit(
1076 &self.direct,
1077 &self.current,
1078 &self.now,
1079 &mut new_frame,
1080 );
1081
1082 view_state.view_style_props.read_explicit(
1083 &self.direct,
1084 &self.current,
1085 &self.now,
1086 &mut new_frame,
1087 );
1088
1089 if view_state.view_transform_props.read_explicit(
1090 &self.direct,
1091 &self.current,
1092 &self.now,
1093 &mut new_frame,
1094 ) || new_frame
1095 {
1096 self.window_state.schedule_layout(view_id);
1097 }
1098 }
1099
1100 if new_frame {
1101 self.window_state.schedule_style(view_id);
1102 }
1103
1104 let layout_style = view_state.borrow().layout_props.to_style();
1106 let taffy_style = self.direct.clone().apply(layout_style).to_taffy_style();
1107 if taffy_style != view_state.borrow().taffy_style {
1108 view_state.borrow_mut().taffy_style = taffy_style;
1109 self.window_state.schedule_layout(view_id);
1110 }
1111
1112 view.borrow_mut().style_pass(self);
1113
1114 let mut is_hidden_state = view_state.borrow().is_hidden_state;
1115 let computed_display = view_state.borrow().combined_style.builtin().display();
1116 is_hidden_state.transition(
1117 computed_display,
1118 || {
1119 let count = animations_on_remove(view_id, Scope::current());
1120 view_state.borrow_mut().num_waiting_animations = count;
1121 count > 0
1122 },
1123 || {
1124 animations_on_create(view_id);
1125 },
1126 || {
1127 stop_reset_remove_animations(view_id);
1128 },
1129 || view_state.borrow().num_waiting_animations,
1130 );
1131
1132 view_state.borrow_mut().is_hidden_state = is_hidden_state;
1133 let modified = view_state
1134 .borrow()
1135 .combined_style
1136 .clone()
1137 .apply_opt(is_hidden_state.get_display(), Style::display);
1138
1139 view_state.borrow_mut().combined_style = modified;
1140
1141 let size = view_id.layout_rect().size();
1142 let transform = view_state.borrow().view_transform_props.affine(size);
1143 view_state.borrow_mut().transform = transform;
1144
1145 let z_index = view_state.borrow().combined_style.get(ZIndex);
1151 let position = view_state.borrow().combined_style.get(PositionProp);
1152 let has_transform = transform != Affine::IDENTITY;
1153
1154 let creates_context = z_index.is_some() || position == Position::Absolute || has_transform; view_state.borrow_mut().stacking_info = StackingInfo {
1157 creates_context,
1158 effective_z_index: z_index.unwrap_or(0),
1159 };
1160 }
1161
1162 fn compute_combined(
1167 &mut self,
1168 interact_state: InteractionState,
1169 screen_size_bp: ScreenSizeBp,
1170 view_class: Option<StyleClassRef>,
1171 context: &Style,
1172 ) -> (bool, bool) {
1173 let mut new_frame = false;
1174
1175 let mut combined_style = Style::new();
1177
1178 let mut classes: SmallVec<[_; 4]> = SmallVec::new();
1179
1180 if let Some(view_class) = view_class {
1182 classes.insert(0, view_class);
1183 }
1184
1185 let state = self.current_view.state();
1186 let mut state = state.borrow_mut();
1187
1188 for class in &state.classes {
1189 classes.push(*class);
1190 }
1191
1192 let mut new_context = context.clone();
1193 let mut new_classes = false;
1194
1195 let self_style = state.style();
1199
1200 let mut selectors = self_style.selectors();
1201
1202 for class in &classes {
1204 if let Some(class_style) = context.get_nested_map(class.key) {
1205 selectors = selectors.union(class_style.selectors());
1206 }
1207 }
1208
1209 state.has_style_selectors = selectors;
1210
1211 let (resolved_style, classes_applied) = resolve_nested_maps(
1212 combined_style,
1213 &interact_state,
1214 screen_size_bp,
1215 &classes,
1216 &mut new_context,
1217 );
1218 combined_style = resolved_style;
1219 new_classes |= classes_applied;
1220
1221 let self_style = state.style();
1222
1223 combined_style.apply_mut(self_style.clone());
1224
1225 let (resolved_style, classes_applied) = resolve_nested_maps(
1226 combined_style,
1227 &interact_state,
1228 screen_size_bp,
1229 &classes,
1230 &mut new_context,
1231 );
1232 combined_style = resolved_style;
1233 new_classes |= classes_applied;
1234
1235 for animation in state
1237 .animations
1238 .stack
1239 .iter_mut()
1240 .filter(|anim| anim.can_advance() || anim.should_apply_folded())
1241 {
1242 if animation.can_advance() {
1243 new_frame = true;
1244 animation.animate_into(&mut combined_style);
1245 animation.advance();
1246 } else {
1247 animation.apply_folded(&mut combined_style)
1248 }
1249 debug_assert!(!animation.is_idle());
1250 }
1251
1252 if interact_state.is_hidden {
1254 combined_style = combined_style.hide();
1255 }
1256
1257 state.combined_style = combined_style;
1258 (new_frame, new_classes)
1259 }
1260
1261 pub fn now(&self) -> Instant {
1262 self.now
1263 }
1264
1265 pub fn get_prop<P: StyleProp>(&self, _prop: P) -> Option<P::Type> {
1266 self.direct
1267 .get_prop::<P>()
1268 .or_else(|| self.current.get_prop::<P>())
1269 }
1270
1271 pub fn style(&self) -> Style {
1272 self.current.clone().apply(self.direct.clone())
1273 }
1274
1275 pub fn direct_style(&self) -> &Style {
1276 &self.direct
1277 }
1278
1279 pub fn indirect_style(&self) -> &Style {
1280 &self.current
1281 }
1282
1283 pub fn request_transition(&mut self) {
1284 let id = self.current_view;
1285 self.window_state.schedule_style(id);
1286 }
1287}
1288
1289pub struct ComputeLayoutCx<'a> {
1290 pub window_state: &'a mut WindowState,
1291 pub(crate) viewport: Rect,
1292 pub(crate) window_origin: Point,
1293 pub(crate) saved_viewports: Vec<Rect>,
1294 pub(crate) saved_window_origins: Vec<Point>,
1295}
1296
1297impl<'a> ComputeLayoutCx<'a> {
1298 pub(crate) fn new(window_state: &'a mut WindowState, viewport: Rect) -> Self {
1299 Self {
1300 window_state,
1301 viewport,
1302 window_origin: Point::ZERO,
1303 saved_viewports: Vec::new(),
1304 saved_window_origins: Vec::new(),
1305 }
1306 }
1307
1308 pub fn window_origin(&self) -> Point {
1309 self.window_origin
1310 }
1311
1312 pub fn save(&mut self) {
1313 self.saved_viewports.push(self.viewport);
1314 self.saved_window_origins.push(self.window_origin);
1315 }
1316
1317 pub fn restore(&mut self) {
1318 self.viewport = self.saved_viewports.pop().unwrap_or_default();
1319 self.window_origin = self.saved_window_origins.pop().unwrap_or_default();
1320 }
1321
1322 pub fn current_viewport(&self) -> Rect {
1323 self.viewport
1324 }
1325
1326 pub fn compute_view_layout(&mut self, id: ViewId) -> Option<Rect> {
1335 let view_state = id.state();
1336
1337 if view_state.borrow().is_hidden_state == IsHiddenState::Hidden {
1338 view_state.borrow_mut().layout_rect = Rect::ZERO;
1339 return None;
1340 }
1341
1342 self.save();
1343
1344 let layout = id.get_layout().unwrap_or_default();
1345 let origin = Point::new(layout.location.x as f64, layout.location.y as f64);
1346 let this_viewport = view_state.borrow().viewport;
1347 let this_viewport_origin = this_viewport.unwrap_or_default().origin().to_vec2();
1348 let size = Size::new(layout.size.width as f64, layout.size.height as f64);
1349 let parent_viewport = self.viewport.with_origin(
1350 Point::new(
1351 self.viewport.x0 - layout.location.x as f64,
1352 self.viewport.y0 - layout.location.y as f64,
1353 ) + this_viewport_origin,
1354 );
1355 self.viewport = parent_viewport.intersect(size.to_rect());
1356 if let Some(this_viewport) = this_viewport {
1357 self.viewport = self.viewport.intersect(this_viewport);
1358 }
1359
1360 let window_origin = origin + self.window_origin.to_vec2() - this_viewport_origin;
1361 self.window_origin = window_origin;
1362 {
1363 view_state.borrow_mut().window_origin = window_origin;
1364 }
1365
1366 {
1367 let view_state = view_state.borrow();
1368 let mut resize_listeners = view_state.resize_listeners.borrow_mut();
1369
1370 let new_rect = size.to_rect().with_origin(origin);
1371 if new_rect != resize_listeners.rect {
1372 resize_listeners.rect = new_rect;
1373
1374 let callbacks = resize_listeners.callbacks.clone();
1375
1376 std::mem::drop(resize_listeners);
1378 std::mem::drop(view_state);
1379
1380 for callback in callbacks {
1381 (*callback)(new_rect);
1382 }
1383 }
1384 }
1385
1386 {
1387 let view_state = view_state.borrow();
1388 let mut move_listeners = view_state.move_listeners.borrow_mut();
1389
1390 if window_origin != move_listeners.window_origin {
1391 move_listeners.window_origin = window_origin;
1392
1393 let callbacks = move_listeners.callbacks.clone();
1394
1395 std::mem::drop(move_listeners);
1397 std::mem::drop(view_state);
1398
1399 for callback in callbacks {
1400 (*callback)(window_origin);
1401 }
1402 }
1403 }
1404
1405 let view = id.view();
1406 let child_layout_rect = view.borrow_mut().compute_layout(self);
1407
1408 let layout_rect = size.to_rect().with_origin(self.window_origin);
1409 let layout_rect = if let Some(child_layout_rect) = child_layout_rect {
1410 layout_rect.union(child_layout_rect)
1411 } else {
1412 layout_rect
1413 };
1414
1415 let transform = view_state.borrow().transform;
1416 let layout_rect = transform.transform_rect_bbox(layout_rect);
1417
1418 view_state.borrow_mut().layout_rect = layout_rect;
1419
1420 self.restore();
1421
1422 Some(layout_rect)
1423 }
1424}
1425
1426pub struct LayoutCx<'a> {
1429 pub window_state: &'a mut WindowState,
1430}
1431
1432impl<'a> LayoutCx<'a> {
1433 pub(crate) fn new(window_state: &'a mut WindowState) -> Self {
1434 Self { window_state }
1435 }
1436
1437 pub fn layout_node(
1443 &mut self,
1444 id: ViewId,
1445 has_children: bool,
1446 mut children: impl FnMut(&mut LayoutCx) -> Vec<NodeId>,
1447 ) -> NodeId {
1448 let view_state = id.state();
1449 let node = view_state.borrow().node;
1450 if !view_state
1451 .borrow()
1452 .requested_changes
1453 .contains(ChangeFlags::LAYOUT)
1454 {
1455 return node;
1456 }
1457 view_state
1458 .borrow_mut()
1459 .requested_changes
1460 .remove(ChangeFlags::LAYOUT);
1461 let layout_style = view_state.borrow().layout_props.to_style();
1462 let animate_out_display = view_state.borrow().is_hidden_state.get_display();
1463 let style = view_state
1464 .borrow()
1465 .combined_style
1466 .clone()
1467 .apply(layout_style)
1468 .apply_opt(animate_out_display, Style::display)
1469 .to_taffy_style();
1470 let _ = id.taffy().borrow_mut().set_style(node, style);
1471
1472 if has_children {
1473 let nodes = children(self);
1474 let _ = id.taffy().borrow_mut().set_children(node, &nodes);
1475 }
1476
1477 node
1478 }
1479
1480 pub fn layout_view(&mut self, view: &mut dyn View) -> NodeId {
1482 view.layout(self)
1483 }
1484}
1485
1486std::thread_local! {
1487 static CURRENT_DRAG_PAINTING_ID : std::cell::Cell<Option<ViewId>> = const { std::cell::Cell::new(None) };
1494}
1495
1496pub(crate) struct PendingDragPaint {
1499 pub id: ViewId,
1500 pub base_transform: Affine,
1501}
1502
1503pub struct PaintCx<'a> {
1504 pub window_state: &'a mut WindowState,
1505 pub(crate) paint_state: &'a mut PaintState,
1506 pub(crate) transform: Affine,
1507 pub(crate) clip: Option<RoundedRect>,
1508 pub(crate) saved_transforms: Vec<Affine>,
1509 pub(crate) saved_clips: Vec<Option<RoundedRect>>,
1510 pub(crate) pending_drag_paint: Option<PendingDragPaint>,
1512 pub gpu_resources: Option<GpuResources>,
1513 pub window: Arc<dyn Window>,
1514 #[cfg(feature = "vello")]
1515 pub layer_count: usize,
1516 #[cfg(feature = "vello")]
1517 pub saved_layer_counts: Vec<usize>,
1518}
1519
1520impl PaintCx<'_> {
1521 pub fn save(&mut self) {
1522 self.saved_transforms.push(self.transform);
1523 self.saved_clips.push(self.clip);
1524 #[cfg(feature = "vello")]
1525 self.saved_layer_counts.push(self.layer_count);
1526 }
1527
1528 pub fn restore(&mut self) {
1529 #[cfg(feature = "vello")]
1530 {
1531 let saved_count = self.saved_layer_counts.pop().unwrap_or_default();
1532 while self.layer_count > saved_count {
1533 self.pop_layer();
1534 self.layer_count -= 1;
1535 }
1536 }
1537
1538 self.transform = self.saved_transforms.pop().unwrap_or_default();
1539 self.clip = self.saved_clips.pop().unwrap_or_default();
1540 self.paint_state
1541 .renderer_mut()
1542 .set_transform(self.transform);
1543
1544 #[cfg(not(feature = "vello"))]
1545 {
1546 if let Some(rect) = self.clip {
1547 self.paint_state.renderer_mut().clip(&rect);
1548 } else {
1549 self.paint_state.renderer_mut().clear_clip();
1550 }
1551 }
1552 }
1553
1554 pub fn is_drag_paint(&self, id: ViewId) -> bool {
1559 if let Some(dragging) = CURRENT_DRAG_PAINTING_ID.get() {
1564 return dragging == id;
1565 }
1566 false
1567 }
1568
1569 pub fn paint_children(&mut self, id: ViewId) {
1571 let children = children_in_paint_order(id);
1572 for child in children {
1573 self.paint_view(child);
1574 }
1575 }
1576
1577 pub fn paint_view(&mut self, id: ViewId) {
1584 if id.is_hidden() {
1585 return;
1586 }
1587 let view = id.view();
1588 let view_state = id.state();
1589
1590 self.save();
1591 let size = self.transform(id);
1592 let is_empty = self
1593 .clip
1594 .map(|rect| rect.rect().intersect(size.to_rect()).is_zero_area())
1595 .unwrap_or(false);
1596 if !is_empty {
1597 let view_style_props = view_state.borrow().view_style_props.clone();
1598 let layout_props = view_state.borrow().layout_props.clone();
1599
1600 paint_bg(self, &view_style_props, size);
1601
1602 view.borrow_mut().paint(self);
1603 paint_border(self, &layout_props, &view_style_props, size);
1604 paint_outline(self, &view_style_props, size)
1605 }
1606 if let Some(dragging) = self.window_state.dragging.as_ref() {
1608 if dragging.id == id {
1609 self.pending_drag_paint = Some(PendingDragPaint {
1611 id,
1612 base_transform: self.transform,
1613 });
1614 }
1615 }
1616
1617 self.restore();
1618 }
1619
1620 pub fn paint_pending_drag(&mut self) {
1623 let Some(pending) = self.pending_drag_paint.take() else {
1624 return;
1625 };
1626
1627 let id = pending.id;
1628 let base_transform = pending.base_transform;
1629
1630 let Some(dragging) = self.window_state.dragging.as_ref() else {
1631 return;
1632 };
1633
1634 if dragging.id != id {
1635 return;
1636 }
1637
1638 let mut drag_set_to_none = false;
1639
1640 let transform = if let Some((released_at, release_location)) =
1641 dragging.released_at.zip(dragging.release_location)
1642 {
1643 let easing = Linear;
1644 const ANIMATION_DURATION_MS: f64 = 300.0;
1645 let elapsed = released_at.elapsed().as_millis() as f64;
1646 let progress = elapsed / ANIMATION_DURATION_MS;
1647
1648 if !(easing.finished(progress)) {
1649 let offset_scale = 1.0 - easing.eval(progress);
1650 let release_offset = release_location.to_vec2() - dragging.offset;
1651
1652 exec_after(Duration::from_millis(8), move |_| {
1654 id.request_paint();
1655 });
1656
1657 Some(base_transform * Affine::translate(release_offset * offset_scale))
1658 } else {
1659 drag_set_to_none = true;
1660 None
1661 }
1662 } else {
1663 let translation = self.window_state.last_cursor_location.to_vec2() - dragging.offset;
1665 Some(base_transform.with_translation(translation))
1666 };
1667
1668 if let Some(transform) = transform {
1669 let view = id.view();
1670 let view_state = id.state();
1671
1672 self.save();
1673 self.transform = transform;
1674 self.paint_state
1675 .renderer_mut()
1676 .set_transform(self.transform);
1677 self.clear_clip();
1678
1679 let size = if let Some(layout) = id.get_layout() {
1681 Size::new(layout.size.width as f64, layout.size.height as f64)
1682 } else {
1683 Size::ZERO
1684 };
1685
1686 let style = view_state.borrow().combined_style.clone();
1688 let mut view_style_props = view_state.borrow().view_style_props.clone();
1689
1690 if let Some(dragging_style) = view_state.borrow().dragging_style.clone() {
1691 let style = style.apply(dragging_style);
1692 let mut _new_frame = false;
1693 view_style_props.read_explicit(&style, &style, &Instant::now(), &mut _new_frame);
1694 }
1695
1696 let layout_props = view_state.borrow().layout_props.clone();
1698
1699 CURRENT_DRAG_PAINTING_ID.set(Some(id));
1704
1705 paint_bg(self, &view_style_props, size);
1706 view.borrow_mut().paint(self);
1707 paint_border(self, &layout_props, &view_style_props, size);
1708 paint_outline(self, &view_style_props, size);
1709
1710 self.restore();
1711
1712 CURRENT_DRAG_PAINTING_ID.take();
1713 }
1714
1715 if drag_set_to_none {
1716 self.window_state.dragging = None;
1717 }
1718 }
1719
1720 pub fn clip(&mut self, shape: &impl Shape) {
1722 #[cfg(feature = "vello")]
1723 {
1724 use peniko::Mix;
1725
1726 self.push_layer(Mix::Normal, 1.0, Affine::IDENTITY, shape);
1727 self.layer_count += 1;
1728 self.clip = Some(shape.bounding_box().to_rounded_rect(0.0));
1729 }
1730
1731 #[cfg(not(feature = "vello"))]
1732 {
1733 let rect = if let Some(rect) = shape.as_rect() {
1734 rect.to_rounded_rect(0.0)
1735 } else if let Some(rect) = shape.as_rounded_rect() {
1736 rect
1737 } else {
1738 let rect = shape.bounding_box();
1739 rect.to_rounded_rect(0.0)
1740 };
1741
1742 let rect = if let Some(existing) = self.clip {
1743 let rect = existing.rect().intersect(rect.rect());
1744 self.paint_state.renderer_mut().clip(&rect);
1745 rect.to_rounded_rect(0.0)
1746 } else {
1747 self.paint_state.renderer_mut().clip(&shape);
1748 rect
1749 };
1750
1751 self.clip = Some(rect);
1752 }
1753 }
1754
1755 pub fn clear_clip(&mut self) {
1757 self.clip = None;
1758 self.paint_state.renderer_mut().clear_clip();
1759 }
1760
1761 pub fn offset(&mut self, offset: (f64, f64)) {
1762 let mut new = self.transform.as_coeffs();
1763 new[4] += offset.0;
1764 new[5] += offset.1;
1765 self.transform = Affine::new(new);
1766 self.paint_state
1767 .renderer_mut()
1768 .set_transform(self.transform);
1769 if let Some(rect) = self.clip.as_mut() {
1770 let raidus = rect.radii();
1771 *rect = rect
1772 .rect()
1773 .with_origin(rect.origin() - Vec2::new(offset.0, offset.1))
1774 .to_rounded_rect(raidus);
1775 }
1776 }
1777
1778 pub fn transform(&mut self, id: ViewId) -> Size {
1779 if let Some(layout) = id.get_layout() {
1780 let offset = layout.location;
1781 self.transform *= Affine::translate(Vec2 {
1782 x: offset.x as f64,
1783 y: offset.y as f64,
1784 });
1785 self.transform *= id.state().borrow().transform;
1786
1787 self.paint_state
1788 .renderer_mut()
1789 .set_transform(self.transform);
1790
1791 if let Some(rect) = self.clip.as_mut() {
1792 let raidus = rect.radii();
1793 *rect = rect
1794 .rect()
1795 .with_origin(rect.origin() - Vec2::new(offset.x as f64, offset.y as f64))
1796 .to_rounded_rect(raidus);
1797 }
1798
1799 Size::new(layout.size.width as f64, layout.size.height as f64)
1800 } else {
1801 Size::ZERO
1802 }
1803 }
1804
1805 pub fn is_focused(&self, id: ViewId) -> bool {
1806 self.window_state.is_focused(&id)
1807 }
1808}
1809
1810pub enum PaintState {
1812 PendingGpuResources {
1814 window: Arc<dyn Window>,
1815 rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1816 font_embolden: f32,
1817 renderer: crate::renderer::Renderer,
1824 },
1825 Initialized { renderer: crate::renderer::Renderer },
1827}
1828
1829impl PaintState {
1830 pub fn new_pending(
1831 window: Arc<dyn Window>,
1832 rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1833 scale: f64,
1834 size: Size,
1835 font_embolden: f32,
1836 ) -> Self {
1837 Self::PendingGpuResources {
1838 window,
1839 rx,
1840 font_embolden,
1841 renderer: Renderer::Uninitialized { scale, size },
1842 }
1843 }
1844
1845 pub fn new(
1846 window: Arc<dyn Window>,
1847 surface: wgpu::Surface<'static>,
1848 gpu_resources: GpuResources,
1849 scale: f64,
1850 size: Size,
1851 font_embolden: f32,
1852 ) -> Self {
1853 let renderer = crate::renderer::Renderer::new(
1854 window.clone(),
1855 gpu_resources,
1856 surface,
1857 scale,
1858 size,
1859 font_embolden,
1860 );
1861 Self::Initialized { renderer }
1862 }
1863
1864 pub(crate) fn renderer(&self) -> &crate::renderer::Renderer {
1865 match self {
1866 PaintState::PendingGpuResources { renderer, .. } => renderer,
1867 PaintState::Initialized { renderer } => renderer,
1868 }
1869 }
1870
1871 pub(crate) fn renderer_mut(&mut self) -> &mut crate::renderer::Renderer {
1872 match self {
1873 PaintState::PendingGpuResources { renderer, .. } => renderer,
1874 PaintState::Initialized { renderer } => renderer,
1875 }
1876 }
1877
1878 pub(crate) fn resize(&mut self, scale: f64, size: Size) {
1879 self.renderer_mut().resize(scale, size);
1880 }
1881
1882 pub(crate) fn set_scale(&mut self, scale: f64) {
1883 self.renderer_mut().set_scale(scale);
1884 }
1885}
1886
1887pub struct UpdateCx<'a> {
1888 pub window_state: &'a mut WindowState,
1889}
1890
1891impl Deref for PaintCx<'_> {
1892 type Target = crate::renderer::Renderer;
1893
1894 fn deref(&self) -> &Self::Target {
1895 self.paint_state.renderer()
1896 }
1897}
1898
1899impl DerefMut for PaintCx<'_> {
1900 fn deref_mut(&mut self) -> &mut Self::Target {
1901 self.paint_state.renderer_mut()
1902 }
1903}
1904
1905fn animations_on_remove(id: ViewId, scope: Scope) -> u16 {
1906 let mut wait_for = 0;
1907 let state = id.state();
1908 let mut state = state.borrow_mut();
1909 state.num_waiting_animations = 0;
1910 let animations = &mut state.animations.stack;
1911 let mut request_style = false;
1912 for anim in animations {
1913 if anim.run_on_remove && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1914 anim.reverse_mut();
1915 request_style = true;
1916 wait_for += 1;
1917 let trigger = anim.on_visual_complete;
1918 scope.create_updater(
1919 move || trigger.track(),
1920 move |_| {
1921 id.transition_anim_complete();
1922 },
1923 );
1924 }
1925 }
1926 drop(state);
1927 if request_style {
1928 id.request_style();
1929 }
1930
1931 id.children()
1932 .into_iter()
1933 .fold(wait_for, |acc, id| acc + animations_on_remove(id, scope))
1934}
1935fn stop_reset_remove_animations(id: ViewId) {
1936 let state = id.state();
1937 let mut state = state.borrow_mut();
1938 let animations = &mut state.animations.stack;
1939 let mut request_style = false;
1940 for anim in animations {
1941 if anim.run_on_remove
1942 && anim.state_kind() == AnimStateKind::PassInProgress
1943 && !matches!(anim.repeat_mode, RepeatMode::LoopForever)
1944 {
1945 anim.start_mut();
1946 request_style = true;
1947 }
1948 }
1949 drop(state);
1950 if request_style {
1951 id.request_style();
1952 }
1953
1954 id.children()
1955 .into_iter()
1956 .for_each(stop_reset_remove_animations)
1957}
1958
1959fn animations_on_create(id: ViewId) {
1960 let state = id.state();
1961 let mut state = state.borrow_mut();
1962 state.num_waiting_animations = 0;
1963 let animations = &mut state.animations.stack;
1964 let mut request_style = false;
1965 for anim in animations {
1966 if anim.run_on_create && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1967 anim.start_mut();
1968 request_style = true;
1969 }
1970 }
1971 drop(state);
1972 if request_style {
1973 id.request_style();
1974 }
1975
1976 id.children().into_iter().for_each(animations_on_create);
1977}
1978
1979#[cfg(test)]
1980mod tests {
1981 use super::*;
1982
1983 fn create_view_with_z_index(z_index: Option<i32>) -> ViewId {
1985 let id = ViewId::new();
1986 let state = id.state();
1988 state.borrow_mut().stacking_info = StackingInfo {
1989 creates_context: z_index.is_some(),
1990 effective_z_index: z_index.unwrap_or(0),
1991 };
1992 id
1993 }
1994
1995 fn setup_parent_with_children(children: Vec<ViewId>) -> ViewId {
1997 let parent = ViewId::new();
1998 parent.set_children_ids(children);
1999 parent
2000 }
2001
2002 fn get_z_indices(children: &[ViewId]) -> Vec<i32> {
2004 children
2005 .iter()
2006 .map(|id| id.state().borrow().stacking_info.effective_z_index)
2007 .collect()
2008 }
2009
2010 #[test]
2011 fn test_no_children() {
2012 let parent = ViewId::new();
2013 let result = children_in_paint_order(parent);
2014 assert!(result.is_empty());
2015 }
2016
2017 #[test]
2018 fn test_single_child() {
2019 let child = create_view_with_z_index(Some(5));
2020 let parent = setup_parent_with_children(vec![child]);
2021
2022 let result = children_in_paint_order(parent);
2023 assert_eq!(result.len(), 1);
2024 assert_eq!(result[0], child);
2025 }
2026
2027 #[test]
2028 fn test_children_no_z_index_preserves_dom_order() {
2029 let child1 = create_view_with_z_index(None);
2031 let child2 = create_view_with_z_index(None);
2032 let child3 = create_view_with_z_index(None);
2033 let parent = setup_parent_with_children(vec![child1, child2, child3]);
2034
2035 let result = children_in_paint_order(parent);
2036 assert_eq!(result, vec![child1, child2, child3]);
2037 }
2038
2039 #[test]
2040 fn test_basic_z_index_sorting() {
2041 let child_z10 = create_view_with_z_index(Some(10));
2043 let child_z1 = create_view_with_z_index(Some(1));
2044 let child_z5 = create_view_with_z_index(Some(5));
2045 let parent = setup_parent_with_children(vec![child_z10, child_z1, child_z5]);
2047
2048 let result = children_in_paint_order(parent);
2049 assert_eq!(get_z_indices(&result), vec![1, 5, 10]);
2051 assert_eq!(result, vec![child_z1, child_z5, child_z10]);
2052 }
2053
2054 #[test]
2055 fn test_negative_z_index() {
2056 let child_pos = create_view_with_z_index(Some(1));
2058 let child_neg = create_view_with_z_index(Some(-1));
2059 let child_zero = create_view_with_z_index(Some(0));
2060 let parent = setup_parent_with_children(vec![child_pos, child_neg, child_zero]);
2062
2063 let result = children_in_paint_order(parent);
2064 assert_eq!(get_z_indices(&result), vec![-1, 0, 1]);
2066 }
2067
2068 #[test]
2069 fn test_equal_z_index_preserves_dom_order() {
2070 let child1 = create_view_with_z_index(Some(5));
2072 let child2 = create_view_with_z_index(Some(5));
2073 let child3 = create_view_with_z_index(Some(5));
2074 let parent = setup_parent_with_children(vec![child1, child2, child3]);
2075
2076 let result = children_in_paint_order(parent);
2077 assert_eq!(result, vec![child1, child2, child3]);
2079 }
2080
2081 #[test]
2082 fn test_mixed_z_index_and_default() {
2083 let child_default = create_view_with_z_index(None); let child_z5 = create_view_with_z_index(Some(5));
2086 let child_z_neg = create_view_with_z_index(Some(-1));
2087 let parent = setup_parent_with_children(vec![child_default, child_z5, child_z_neg]);
2089
2090 let result = children_in_paint_order(parent);
2091 assert_eq!(get_z_indices(&result), vec![-1, 0, 5]);
2093 }
2094
2095 #[test]
2096 fn test_optimization_skips_sort_when_all_zero() {
2097 let child1 = create_view_with_z_index(Some(0));
2100 let child2 = create_view_with_z_index(Some(0));
2101 let child3 = create_view_with_z_index(Some(0));
2102 let parent = setup_parent_with_children(vec![child1, child2, child3]);
2103
2104 let result = children_in_paint_order(parent);
2105 assert_eq!(result, vec![child1, child2, child3]);
2107 }
2108
2109 #[test]
2110 fn test_large_z_index_values() {
2111 let child_max = create_view_with_z_index(Some(i32::MAX));
2113 let child_min = create_view_with_z_index(Some(i32::MIN));
2114 let child_zero = create_view_with_z_index(Some(0));
2115 let parent = setup_parent_with_children(vec![child_max, child_min, child_zero]);
2116
2117 let result = children_in_paint_order(parent);
2118 assert_eq!(get_z_indices(&result), vec![i32::MIN, 0, i32::MAX]);
2119 }
2120
2121 #[test]
2122 fn test_event_dispatch_order_is_reverse_of_paint() {
2123 let child_z1 = create_view_with_z_index(Some(1));
2125 let child_z10 = create_view_with_z_index(Some(10));
2126 let child_z5 = create_view_with_z_index(Some(5));
2127 let parent = setup_parent_with_children(vec![child_z1, child_z10, child_z5]);
2128
2129 let paint_order = children_in_paint_order(parent);
2130 assert_eq!(get_z_indices(&paint_order), vec![1, 5, 10]);
2132
2133 let event_order: Vec<_> = paint_order.into_iter().rev().collect();
2135 assert_eq!(get_z_indices(&event_order), vec![10, 5, 1]);
2136 }
2137
2138 #[test]
2139 fn test_many_children_sorting() {
2140 let children: Vec<_> = (0..10)
2142 .map(|i| create_view_with_z_index(Some(9 - i))) .collect();
2144 let parent = setup_parent_with_children(children.clone());
2145
2146 let result = children_in_paint_order(parent);
2147 let z_indices = get_z_indices(&result);
2149 assert_eq!(z_indices, (0..10).collect::<Vec<_>>());
2150 }
2151}