1use floem_reactive::Scope;
2use floem_renderer::Renderer as FloemRenderer;
3use floem_renderer::gpu_resources::{GpuResourceError, GpuResources};
4use peniko::kurbo::{Affine, Point, Rect, RoundedRect, Shape, Size, Vec2};
5use std::{
6 ops::{Deref, DerefMut},
7 rc::Rc,
8 sync::Arc,
9};
10use winit::window::Window;
11
12#[cfg(not(target_arch = "wasm32"))]
13use std::time::{Duration, Instant};
14#[cfg(target_arch = "wasm32")]
15use web_time::{Duration, Instant};
16
17#[cfg(feature = "crossbeam")]
18use crossbeam::channel::Receiver;
19#[cfg(not(feature = "crossbeam"))]
20use std::sync::mpsc::Receiver;
21
22use taffy::prelude::NodeId;
23
24use crate::animate::{AnimStateKind, RepeatMode};
25use crate::easing::{Easing, Linear};
26use crate::menu::Menu;
27use crate::renderer::Renderer;
28use crate::style::{Disabled, DisplayProp, Focusable, Hidden, PointerEvents, PointerEventsProp};
29use crate::view_state::IsHiddenState;
30use crate::{
31 action::{exec_after, show_context_menu},
32 app_state::AppState,
33 event::{Event, EventListener, EventPropagation},
34 id::ViewId,
35 inspector::CaptureState,
36 style::{Style, StyleProp, ZIndex},
37 view::{View, paint_bg, paint_border, paint_outline},
38 view_state::ChangeFlags,
39};
40
41pub type EventCallback = dyn FnMut(&Event) -> EventPropagation;
42pub type ResizeCallback = dyn Fn(Rect);
43pub type MenuCallback = dyn Fn() -> Menu;
44
45pub(crate) struct ResizeListener {
46 pub(crate) rect: Rect,
47 pub(crate) callback: Box<ResizeCallback>,
48}
49
50pub(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
75pub 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 pub(crate) fn unconditional_view_event(
104 &mut self,
105 view_id: ViewId,
106 event: Event,
107 directed: bool,
108 ) -> (EventPropagation, PointerEventConsumed) {
109 if view_id.is_hidden() {
110 return (EventPropagation::Continue, PointerEventConsumed::No);
112 }
113 if view_id.is_disabled() && !event.allow_disabled() {
114 return (EventPropagation::Continue, PointerEventConsumed::No);
117 }
118
119 let event = self.offset_event(view_id, event);
126
127 let view = view_id.view();
128 let view_state = view_id.state();
129
130 let disable_default = if let Some(listener) = event.listener() {
131 view_state
132 .borrow()
133 .disable_default_events
134 .contains(&listener)
135 } else {
136 false
137 };
138
139 let is_pointer_none = event.is_pointer()
140 && view_state.borrow().computed_style.get(PointerEventsProp)
141 == Some(PointerEvents::None);
142
143 if !disable_default
144 && !is_pointer_none
145 && view
146 .borrow_mut()
147 .event_before_children(self, &event)
148 .is_processed()
149 {
150 if let Event::PointerDown(event) = &event {
151 if view_state.borrow().computed_style.get(Focusable) {
152 let rect = view_id.get_size().unwrap_or_default().to_rect();
153 let now_focused = rect.contains(event.pos);
154 if now_focused {
155 self.app_state.update_focus(view_id, false);
156 }
157 }
158 }
159 if let Event::PointerMove(_event) = &event {
160 let view_state = view_state.borrow();
161 let style = view_state.combined_style.builtin();
162 if let Some(cursor) = style.cursor() {
163 if self.app_state.cursor.is_none() {
164 self.app_state.cursor = Some(cursor);
165 }
166 }
167 }
168 return (EventPropagation::Stop, PointerEventConsumed::Yes);
169 }
170
171 let mut view_pointer_event_consumed = PointerEventConsumed::No;
172
173 if !directed {
174 let children = view_id.children();
175 for child in children.into_iter().rev() {
176 if !self.should_send(child, &event) {
177 continue;
178 }
179 let (event_propagation, pointer_event_consumed) =
180 self.unconditional_view_event(child, event.clone(), false);
181 if event_propagation.is_processed() {
182 return (EventPropagation::Stop, PointerEventConsumed::Yes);
183 }
184 if event.is_pointer() && pointer_event_consumed == PointerEventConsumed::Yes {
185 view_pointer_event_consumed = PointerEventConsumed::Yes;
190 break;
191 }
192 }
193 }
194
195 if !disable_default
196 && !is_pointer_none
197 && view
198 .borrow_mut()
199 .event_after_children(self, &event)
200 .is_processed()
201 {
202 return (EventPropagation::Stop, PointerEventConsumed::Yes);
203 }
204
205 if is_pointer_none {
206 return (EventPropagation::Continue, view_pointer_event_consumed);
208 }
209
210 if !disable_default {
212 let popout_menu = || {
213 let bottom_left = {
214 let layout = view_state.borrow().layout_rect;
215 Point::new(layout.x0, layout.y1)
216 };
217
218 let popout_menu = view_state.borrow().popout_menu.clone();
219 show_context_menu(popout_menu?(), Some(bottom_left));
220 Some((EventPropagation::Stop, PointerEventConsumed::Yes))
221 };
222
223 match &event {
224 Event::PointerDown(pointer_event) => {
225 self.app_state.clicking.insert(view_id);
226 if pointer_event.button.is_primary() {
227 let rect = view_id.get_size().unwrap_or_default().to_rect();
228 let on_view = rect.contains(pointer_event.pos);
229
230 if on_view {
231 if view_state.borrow().computed_style.get(Focusable) {
232 self.app_state.update_focus(view_id, false);
234 }
235 if pointer_event.count == 2
236 && view_state
237 .borrow()
238 .event_listeners
239 .contains_key(&EventListener::DoubleClick)
240 {
241 view_state.borrow_mut().last_pointer_down =
242 Some(pointer_event.clone());
243 }
244 if view_state
245 .borrow()
246 .event_listeners
247 .contains_key(&EventListener::Click)
248 {
249 view_state.borrow_mut().last_pointer_down =
250 Some(pointer_event.clone());
251 }
252
253 #[cfg(target_os = "macos")]
254 if let Some((ep, pec)) = popout_menu() {
255 return (ep, pec);
256 };
257
258 if view_id.can_drag() && self.app_state.drag_start.is_none() {
259 self.app_state.drag_start = Some((view_id, pointer_event.pos));
260 }
261 }
262 } else if pointer_event.button.is_secondary() {
263 let rect = view_id.get_size().unwrap_or_default().to_rect();
264 let on_view = rect.contains(pointer_event.pos);
265
266 if on_view {
267 if view_state.borrow().computed_style.get(Focusable) {
268 self.app_state.update_focus(view_id, false);
270 }
271 if view_state
272 .borrow()
273 .event_listeners
274 .contains_key(&EventListener::SecondaryClick)
275 {
276 view_state.borrow_mut().last_pointer_down =
277 Some(pointer_event.clone());
278 }
279 }
280 }
281 }
282 Event::PointerMove(pointer_event) => {
283 let rect = view_id.get_size().unwrap_or_default().to_rect();
284 if rect.contains(pointer_event.pos) {
285 if self.app_state.is_dragging() {
286 self.app_state.dragging_over.insert(view_id);
287 view_id.apply_event(&EventListener::DragOver, &event);
288 } else {
289 self.app_state.hovered.insert(view_id);
290 let view_state = view_state.borrow();
291 let style = view_state.combined_style.builtin();
292 if let Some(cursor) = style.cursor() {
293 if self.app_state.cursor.is_none() {
294 self.app_state.cursor = Some(cursor);
295 }
296 }
297 }
298 }
299 if view_id.can_drag() {
300 if let Some((_, drag_start)) = self
301 .app_state
302 .drag_start
303 .as_ref()
304 .filter(|(drag_id, _)| drag_id == &view_id)
305 {
306 let offset = pointer_event.pos - *drag_start;
307 if let Some(dragging) = self
308 .app_state
309 .dragging
310 .as_mut()
311 .filter(|d| d.id == view_id && d.released_at.is_none())
312 {
313 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 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 !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 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 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 pub fn should_send(&mut self, id: ViewId, event: &Event) -> bool {
505 if id.is_hidden() || (id.is_disabled() && !event.allow_disabled()) {
506 return false;
507 }
508
509 let Some(point) = event.point() else {
510 return true;
511 };
512
513 let layout_rect = id.layout_rect();
514 let Some(layout) = id.get_layout() else {
515 return false;
516 };
517
518 let current_rect = layout_rect.with_origin(Point::new(
520 layout.location.x as f64,
521 layout.location.y as f64,
522 ));
523
524 if !current_rect.contains(point) {
525 return false;
526 }
527
528 true
529 }
530}
531
532#[derive(Default)]
533pub struct InteractionState {
534 pub(crate) is_hovered: bool,
535 pub(crate) is_selected: bool,
536 pub(crate) is_disabled: bool,
537 pub(crate) is_focused: bool,
538 pub(crate) is_clicking: bool,
539 pub(crate) is_dark_mode: bool,
540 pub(crate) is_file_hover: bool,
541 pub(crate) using_keyboard_navigation: bool,
542}
543
544pub struct StyleCx<'a> {
545 pub(crate) app_state: &'a mut AppState,
546 pub(crate) current_view: ViewId,
547 pub(crate) current: Rc<Style>,
549 pub(crate) direct: Style,
550 saved: Vec<Rc<Style>>,
551 pub(crate) now: Instant,
552 saved_disabled: Vec<bool>,
553 saved_selected: Vec<bool>,
554 saved_hidden: Vec<bool>,
555 disabled: bool,
556 hidden: bool,
557 selected: bool,
558}
559
560impl<'a> StyleCx<'a> {
561 pub(crate) fn new(app_state: &'a mut AppState, root: ViewId) -> Self {
562 Self {
563 app_state,
564 current_view: root,
565 current: Default::default(),
566 direct: Default::default(),
567 saved: Default::default(),
568 now: Instant::now(),
569 saved_disabled: Default::default(),
570 saved_selected: Default::default(),
571 saved_hidden: Default::default(),
572 disabled: false,
573 hidden: false,
574 selected: false,
575 }
576 }
577
578 pub fn selected(&mut self) {
580 self.selected = true;
581 }
582
583 pub fn hidden(&mut self) {
584 self.hidden = true;
585 }
586
587 fn get_interact_state(&self, id: &ViewId) -> InteractionState {
588 InteractionState {
589 is_selected: self.selected || id.is_selected(),
590 is_hovered: self.app_state.is_hovered(id),
591 is_disabled: id.is_disabled() || self.disabled,
592 is_focused: self.app_state.is_focused(id),
593 is_clicking: self.app_state.is_clicking(id),
594 is_dark_mode: self.app_state.is_dark_mode(),
595 is_file_hover: self.app_state.is_file_hover(id),
596 using_keyboard_navigation: self.app_state.keyboard_navigation,
597 }
598 }
599
600 pub fn style_view(&mut self, view_id: ViewId) {
602 self.save();
603 let view = view_id.view();
604 let view_state = view_id.state();
605 {
606 let mut view_state = view_state.borrow_mut();
607 if !view_state.requested_changes.contains(ChangeFlags::STYLE) {
608 self.restore();
609 return;
610 }
611 view_state.requested_changes.remove(ChangeFlags::STYLE);
612 }
613
614 let view_style = view.borrow().view_style();
615 let view_class = view.borrow().view_class();
616 {
617 let mut view_state = view_state.borrow_mut();
618
619 if view_state.request_style_recursive {
621 view_state.request_style_recursive = false;
622 let children = view_id.children();
623 for child in children {
624 let view_state = child.state();
625 let mut state = view_state.borrow_mut();
626 state.request_style_recursive = true;
627 state.requested_changes.insert(ChangeFlags::STYLE);
628 }
629 }
630 }
631
632 let view_interact_state = self.get_interact_state(&view_id);
633 self.disabled = view_interact_state.is_disabled;
634 let mut new_frame = view_id.state().borrow_mut().compute_combined(
635 view_style,
636 view_interact_state,
637 self.app_state.screen_size_bp,
638 view_class,
639 &self.current,
640 self.hidden,
641 );
642
643 self.direct = view_state.borrow().combined_style.clone();
644 Style::apply_only_inherited(&mut self.current, &self.direct);
645 let mut computed_style = (*self.current).clone();
646 computed_style.apply_mut(self.direct.clone());
647 CaptureState::capture_style(view_id, self, computed_style.clone());
648 if computed_style.get(Focusable)
649 && !computed_style.get(Disabled)
650 && !computed_style.get(Hidden)
651 && computed_style.get(DisplayProp) != taffy::Display::None
652 {
653 self.app_state.focusable.insert(view_id);
654 } else {
655 self.app_state.focusable.remove(&view_id);
656 }
657 view_state.borrow_mut().computed_style = computed_style;
658 self.hidden |= view_id.is_hidden();
659
660 self.current_view = view_id;
662
663 {
664 let mut view_state = view_state.borrow_mut();
665 view_state.layout_props.read_explicit(
668 &self.direct,
669 &self.current,
670 &self.now,
671 &mut new_frame,
672 );
673
674 view_state.view_style_props.read_explicit(
675 &self.direct,
676 &self.current,
677 &self.now,
678 &mut new_frame,
679 );
680 if new_frame && !self.hidden {
681 self.app_state.schedule_style(view_id);
682 }
683 }
684 let layout_style = view_state.borrow().layout_props.to_style();
686 let taffy_style = self.direct.clone().apply(layout_style).to_taffy_style();
687 if taffy_style != view_state.borrow().taffy_style {
688 view_state.borrow_mut().taffy_style = taffy_style;
689 view_id.request_layout();
690 }
691
692 view.borrow_mut().style_pass(self);
693
694 let mut is_hidden_state = view_state.borrow().is_hidden_state;
695 let computed_display = view_state.borrow().combined_style.get(DisplayProp);
696 is_hidden_state.transition(
697 computed_display,
698 || {
699 let count = animations_on_remove(view_id, Scope::current());
700 view_state.borrow_mut().num_waiting_animations = count;
701 count > 0
702 },
703 || {
704 animations_on_create(view_id);
705 },
706 || {
707 stop_reset_remove_animations(view_id);
708 },
709 || view_state.borrow().num_waiting_animations,
710 );
711
712 view_state.borrow_mut().is_hidden_state = is_hidden_state;
713 let modified = view_state
714 .borrow()
715 .combined_style
716 .clone()
717 .apply_opt(is_hidden_state.get_display(), Style::display);
718
719 view_state.borrow_mut().combined_style = modified;
720
721 let mut transform = Affine::IDENTITY;
722
723 let transform_x = match view_state.borrow().layout_props.translate_x() {
724 crate::unit::PxPct::Px(px) => px,
725 crate::unit::PxPct::Pct(pct) => pct / 100.,
726 };
727 let transform_y = match view_state.borrow().layout_props.translate_y() {
728 crate::unit::PxPct::Px(px) => px,
729 crate::unit::PxPct::Pct(pct) => pct / 100.,
730 };
731 transform *= Affine::translate(Vec2 {
732 x: transform_x,
733 y: transform_y,
734 });
735
736 let scale_x = view_state.borrow().layout_props.scale_x().0 / 100.;
737 let scale_y = view_state.borrow().layout_props.scale_y().0 / 100.;
738 let size = view_id.layout_rect();
739 let center_x = size.width() / 2.;
740 let center_y = size.height() / 2.;
741 transform *= Affine::translate(Vec2 {
742 x: center_x,
743 y: center_y,
744 });
745 transform *= Affine::scale_non_uniform(scale_x, scale_y);
746 let rotation = view_state.borrow().layout_props.rotation().0;
747 transform *= Affine::rotate(rotation);
748 transform *= Affine::translate(Vec2 {
749 x: -center_x,
750 y: -center_y,
751 });
752
753 view_state.borrow_mut().transform = transform;
754
755 self.restore();
756 }
757
758 pub fn now(&self) -> Instant {
759 self.now
760 }
761
762 pub fn save(&mut self) {
763 self.saved.push(self.current.clone());
764 self.saved_disabled.push(self.disabled);
765 self.saved_selected.push(self.selected);
766 self.saved_hidden.push(self.hidden);
767 }
768
769 pub fn restore(&mut self) {
770 self.current = self.saved.pop().unwrap_or_default();
771 self.disabled = self.saved_disabled.pop().unwrap_or_default();
772 self.selected = self.saved_selected.pop().unwrap_or_default();
773 self.hidden = self.saved_hidden.pop().unwrap_or_default();
774 }
775
776 pub fn get_prop<P: StyleProp>(&self, _prop: P) -> Option<P::Type> {
777 self.direct
778 .get_prop::<P>()
779 .or_else(|| self.current.get_prop::<P>())
780 }
781
782 pub fn style(&self) -> Style {
783 (*self.current).clone().apply(self.direct.clone())
784 }
785
786 pub fn direct_style(&self) -> &Style {
787 &self.direct
788 }
789
790 pub fn indirect_style(&self) -> &Style {
791 &self.current
792 }
793
794 pub fn request_transition(&mut self) {
795 let id = self.current_view;
796 self.app_state_mut().schedule_style(id);
797 }
798
799 pub fn app_state_mut(&mut self) -> &mut AppState {
800 self.app_state
801 }
802
803 pub fn app_state(&self) -> &AppState {
804 self.app_state
805 }
806}
807
808pub struct ComputeLayoutCx<'a> {
809 pub(crate) app_state: &'a mut AppState,
810 pub(crate) viewport: Rect,
811 pub(crate) window_origin: Point,
812 pub(crate) saved_viewports: Vec<Rect>,
813 pub(crate) saved_window_origins: Vec<Point>,
814}
815
816impl<'a> ComputeLayoutCx<'a> {
817 pub(crate) fn new(app_state: &'a mut AppState, viewport: Rect) -> Self {
818 Self {
819 app_state,
820 viewport,
821 window_origin: Point::ZERO,
822 saved_viewports: Vec::new(),
823 saved_window_origins: Vec::new(),
824 }
825 }
826
827 pub fn window_origin(&self) -> Point {
828 self.window_origin
829 }
830
831 pub fn app_state_mut(&mut self) -> &mut AppState {
832 self.app_state
833 }
834
835 pub fn app_state(&self) -> &AppState {
836 self.app_state
837 }
838
839 pub fn save(&mut self) {
840 self.saved_viewports.push(self.viewport);
841 self.saved_window_origins.push(self.window_origin);
842 }
843
844 pub fn restore(&mut self) {
845 self.viewport = self.saved_viewports.pop().unwrap_or_default();
846 self.window_origin = self.saved_window_origins.pop().unwrap_or_default();
847 }
848
849 pub fn current_viewport(&self) -> Rect {
850 self.viewport
851 }
852
853 pub fn compute_view_layout(&mut self, id: ViewId) -> Option<Rect> {
862 let view_state = id.state();
863
864 if view_state.borrow().is_hidden_state == IsHiddenState::Hidden {
865 view_state.borrow_mut().layout_rect = Rect::ZERO;
866 return None;
867 }
868
869 self.save();
870
871 let layout = id.get_layout().unwrap_or_default();
872 let origin = Point::new(layout.location.x as f64, layout.location.y as f64);
873 let this_viewport = view_state.borrow().viewport;
874 let this_viewport_origin = this_viewport.unwrap_or_default().origin().to_vec2();
875 let size = Size::new(layout.size.width as f64, layout.size.height as f64);
876 let parent_viewport = self.viewport.with_origin(
877 Point::new(
878 self.viewport.x0 - layout.location.x as f64,
879 self.viewport.y0 - layout.location.y as f64,
880 ) + this_viewport_origin,
881 );
882 self.viewport = parent_viewport.intersect(size.to_rect());
883 if let Some(this_viewport) = this_viewport {
884 self.viewport = self.viewport.intersect(this_viewport);
885 }
886
887 let window_origin = origin + self.window_origin.to_vec2() - this_viewport_origin;
888 self.window_origin = window_origin;
889 {
890 view_state.borrow_mut().window_origin = window_origin;
891 }
892
893 let resize_listener = view_state.borrow().resize_listener.clone();
894 if let Some(resize) = resize_listener.as_ref() {
895 let mut resize = resize.borrow_mut();
896 let new_rect = size.to_rect().with_origin(origin);
897 if new_rect != resize.rect {
898 resize.rect = new_rect;
899 (*resize.callback)(new_rect);
900 }
901 }
902
903 let move_listener = view_state.borrow().move_listener.clone();
904 if let Some(listener) = move_listener {
905 let mut listener = listener.borrow_mut();
906 if window_origin != listener.window_origin {
907 listener.window_origin = window_origin;
908 (*listener.callback)(window_origin);
909 }
910 }
911
912 let view = id.view();
913 let child_layout_rect = view.borrow_mut().compute_layout(self);
914
915 let layout_rect = size.to_rect().with_origin(self.window_origin);
916 let layout_rect = if let Some(child_layout_rect) = child_layout_rect {
917 layout_rect.union(child_layout_rect)
918 } else {
919 layout_rect
920 };
921
922 view_state.borrow_mut().layout_rect = layout_rect;
923
924 self.restore();
925
926 Some(layout_rect)
927 }
928}
929
930pub struct LayoutCx<'a> {
933 pub(crate) app_state: &'a mut AppState,
934}
935
936impl<'a> LayoutCx<'a> {
937 pub(crate) fn new(app_state: &'a mut AppState) -> Self {
938 Self { app_state }
939 }
940
941 pub fn app_state_mut(&mut self) -> &mut AppState {
942 self.app_state
943 }
944
945 pub fn app_state(&self) -> &AppState {
946 self.app_state
947 }
948
949 pub fn layout_node(
955 &mut self,
956 id: ViewId,
957 has_children: bool,
958 mut children: impl FnMut(&mut LayoutCx) -> Vec<NodeId>,
959 ) -> NodeId {
960 let view_state = id.state();
961 let node = view_state.borrow().node;
962 if !view_state
963 .borrow()
964 .requested_changes
965 .contains(ChangeFlags::LAYOUT)
966 {
967 return node;
968 }
969 view_state
970 .borrow_mut()
971 .requested_changes
972 .remove(ChangeFlags::LAYOUT);
973 let layout_style = view_state.borrow().layout_props.to_style();
974 let animate_out_display = view_state.borrow().is_hidden_state.get_display();
975 let style = view_state
976 .borrow()
977 .combined_style
978 .clone()
979 .apply(layout_style)
980 .apply_opt(animate_out_display, Style::display)
981 .to_taffy_style();
982 let _ = id.taffy().borrow_mut().set_style(node, style);
983
984 if has_children {
985 let nodes = children(self);
986 let _ = id.taffy().borrow_mut().set_children(node, &nodes);
987 }
988
989 node
990 }
991
992 pub fn layout_view(&mut self, view: &mut dyn View) -> NodeId {
994 view.layout(self)
995 }
996}
997
998std::thread_local! {
999 static CURRENT_DRAG_PAINTING_ID : std::cell::Cell<Option<ViewId>> = const { std::cell::Cell::new(None) };
1006}
1007
1008pub struct PaintCx<'a> {
1009 pub(crate) app_state: &'a mut AppState,
1010 pub(crate) paint_state: &'a mut PaintState,
1011 pub(crate) transform: Affine,
1012 pub(crate) clip: Option<RoundedRect>,
1013 pub(crate) z_index: Option<i32>,
1014 pub(crate) saved_transforms: Vec<Affine>,
1015 pub(crate) saved_clips: Vec<Option<RoundedRect>>,
1016 pub(crate) saved_z_indexes: Vec<Option<i32>>,
1017 pub gpu_resources: Option<GpuResources>,
1018 pub window: Arc<dyn Window>,
1019 #[cfg(feature = "vello")]
1020 pub layer_count: usize,
1021 #[cfg(feature = "vello")]
1022 pub saved_layer_counts: Vec<usize>,
1023}
1024
1025impl PaintCx<'_> {
1026 pub fn save(&mut self) {
1027 self.saved_transforms.push(self.transform);
1028 self.saved_clips.push(self.clip);
1029 self.saved_z_indexes.push(self.z_index);
1030 #[cfg(feature = "vello")]
1031 self.saved_layer_counts.push(self.layer_count);
1032 }
1033
1034 pub fn restore(&mut self) {
1035 #[cfg(feature = "vello")]
1036 {
1037 let saved_count = self.saved_layer_counts.pop().unwrap_or_default();
1038 while self.layer_count > saved_count {
1039 self.pop_layer();
1040 self.layer_count -= 1;
1041 }
1042 }
1043
1044 self.transform = self.saved_transforms.pop().unwrap_or_default();
1045 self.clip = self.saved_clips.pop().unwrap_or_default();
1046 self.z_index = self.saved_z_indexes.pop().unwrap_or_default();
1047 self.paint_state
1048 .renderer_mut()
1049 .set_transform(self.transform);
1050 if let Some(z_index) = self.z_index {
1051 self.paint_state.renderer_mut().set_z_index(z_index);
1052 } else {
1053 self.paint_state.renderer_mut().set_z_index(0);
1054 }
1055
1056 #[cfg(not(feature = "vello"))]
1057 {
1058 if let Some(rect) = self.clip {
1059 self.paint_state.renderer_mut().clip(&rect);
1060 } else {
1061 self.paint_state.renderer_mut().clear_clip();
1062 }
1063 }
1064 }
1065
1066 pub fn is_drag_paint(&self, id: ViewId) -> bool {
1071 if let Some(dragging) = CURRENT_DRAG_PAINTING_ID.get() {
1076 return dragging == id;
1077 }
1078 false
1079 }
1080
1081 pub fn paint_children(&mut self, id: ViewId) {
1083 let children = id.children();
1084 for child in children {
1085 self.paint_view(child);
1086 }
1087 }
1088
1089 pub fn paint_view(&mut self, id: ViewId) {
1096 if id.is_hidden() {
1097 return;
1098 }
1099 let view = id.view();
1100 let view_state = id.state();
1101
1102 self.save();
1103 let size = self.transform(id);
1104 let is_empty = self
1105 .clip
1106 .map(|rect| rect.rect().intersect(size.to_rect()).is_zero_area())
1107 .unwrap_or(false);
1108 if !is_empty {
1109 let style = view_state.borrow().combined_style.clone();
1110 let view_style_props = view_state.borrow().view_style_props.clone();
1111 let layout_props = view_state.borrow().layout_props.clone();
1112
1113 if let Some(z_index) = style.get(ZIndex) {
1114 self.set_z_index(z_index);
1115 }
1116
1117 paint_bg(self, &view_style_props, size);
1118
1119 view.borrow_mut().paint(self);
1120 paint_border(self, &layout_props, &view_style_props, size);
1121 paint_outline(self, &view_style_props, size)
1122 }
1123 let mut drag_set_to_none = false;
1124
1125 if let Some(dragging) = self.app_state.dragging.as_ref() {
1126 if dragging.id == id {
1127 let transform = if let Some((released_at, release_location)) =
1128 dragging.released_at.zip(dragging.release_location)
1129 {
1130 let easing = Linear;
1131 const ANIMATION_DURATION_MS: f64 = 300.0;
1132 let elapsed = released_at.elapsed().as_millis() as f64;
1133 let progress = elapsed / ANIMATION_DURATION_MS;
1134
1135 if !(easing.finished(progress)) {
1136 let offset_scale = 1.0 - easing.eval(progress);
1137 let release_offset = release_location.to_vec2() - dragging.offset;
1138
1139 exec_after(Duration::from_millis(8), move |_| {
1141 id.request_paint();
1142 });
1143
1144 Some(self.transform * Affine::translate(release_offset * offset_scale))
1145 } else {
1146 drag_set_to_none = true;
1147 None
1148 }
1149 } else {
1150 let translation =
1152 self.app_state.last_cursor_location.to_vec2() - dragging.offset;
1153 Some(self.transform.with_translation(translation))
1154 };
1155
1156 if let Some(transform) = transform {
1157 self.save();
1158 self.transform = transform;
1159 self.paint_state
1160 .renderer_mut()
1161 .set_transform(self.transform);
1162 self.set_z_index(1000);
1163 self.clear_clip();
1164
1165 let style = view_state.borrow().combined_style.clone();
1167 let mut view_style_props = view_state.borrow().view_style_props.clone();
1168
1169 if let Some(dragging_style) = view_state.borrow().dragging_style.clone() {
1170 let style = style.apply(dragging_style);
1171 let mut _new_frame = false;
1172 view_style_props.read_explicit(
1173 &style,
1174 &style,
1175 &Instant::now(),
1176 &mut _new_frame,
1177 );
1178 }
1179
1180 let layout_props = view_state.borrow().layout_props.clone();
1182
1183 CURRENT_DRAG_PAINTING_ID.set(Some(id));
1188
1189 paint_bg(self, &view_style_props, size);
1190 view.borrow_mut().paint(self);
1191 paint_border(self, &layout_props, &view_style_props, size);
1192 paint_outline(self, &view_style_props, size);
1193
1194 self.restore();
1195
1196 CURRENT_DRAG_PAINTING_ID.take();
1197 }
1198 }
1199 }
1200
1201 if drag_set_to_none {
1202 self.app_state.dragging = None;
1203 }
1204 self.restore();
1205 }
1206
1207 pub fn clip(&mut self, shape: &impl Shape) {
1209 #[cfg(feature = "vello")]
1210 {
1211 use peniko::Mix;
1212
1213 self.push_layer(Mix::Normal, 1.0, Affine::IDENTITY, shape);
1214 self.layer_count += 1;
1215 self.clip = Some(shape.bounding_box().to_rounded_rect(0.0));
1216 }
1217
1218 #[cfg(not(feature = "vello"))]
1219 {
1220 let rect = if let Some(rect) = shape.as_rect() {
1221 rect.to_rounded_rect(0.0)
1222 } else if let Some(rect) = shape.as_rounded_rect() {
1223 rect
1224 } else {
1225 let rect = shape.bounding_box();
1226 rect.to_rounded_rect(0.0)
1227 };
1228
1229 let rect = if let Some(existing) = self.clip {
1230 let rect = existing.rect().intersect(rect.rect());
1231 self.paint_state.renderer_mut().clip(&rect);
1232 rect.to_rounded_rect(0.0)
1233 } else {
1234 self.paint_state.renderer_mut().clip(&shape);
1235 rect
1236 };
1237
1238 self.clip = Some(rect);
1239 }
1240 }
1241
1242 pub fn clear_clip(&mut self) {
1244 self.clip = None;
1245 self.paint_state.renderer_mut().clear_clip();
1246 }
1247
1248 pub fn offset(&mut self, offset: (f64, f64)) {
1249 let mut new = self.transform.as_coeffs();
1250 new[4] += offset.0;
1251 new[5] += offset.1;
1252 self.transform = Affine::new(new);
1253 self.paint_state
1254 .renderer_mut()
1255 .set_transform(self.transform);
1256 if let Some(rect) = self.clip.as_mut() {
1257 let raidus = rect.radii();
1258 *rect = rect
1259 .rect()
1260 .with_origin(rect.origin() - Vec2::new(offset.0, offset.1))
1261 .to_rounded_rect(raidus);
1262 }
1263 }
1264
1265 pub fn transform(&mut self, id: ViewId) -> Size {
1266 if let Some(layout) = id.get_layout() {
1267 let offset = layout.location;
1268 self.transform *= Affine::translate(Vec2 {
1269 x: offset.x as f64,
1270 y: offset.y as f64,
1271 });
1272 self.transform *= id.state().borrow().transform;
1273
1274 self.paint_state
1275 .renderer_mut()
1276 .set_transform(self.transform);
1277
1278 if let Some(rect) = self.clip.as_mut() {
1279 let raidus = rect.radii();
1280 *rect = rect
1281 .rect()
1282 .with_origin(rect.origin() - Vec2::new(offset.x as f64, offset.y as f64))
1283 .to_rounded_rect(raidus);
1284 }
1285
1286 Size::new(layout.size.width as f64, layout.size.height as f64)
1287 } else {
1288 Size::ZERO
1289 }
1290 }
1291
1292 pub(crate) fn set_z_index(&mut self, z_index: i32) {
1293 self.z_index = Some(z_index);
1294 self.paint_state.renderer_mut().set_z_index(z_index);
1295 }
1296
1297 pub fn is_focused(&self, id: ViewId) -> bool {
1298 self.app_state.is_focused(&id)
1299 }
1300}
1301
1302pub enum PaintState {
1304 PendingGpuResources {
1306 window: Arc<dyn Window>,
1307 rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1308 font_embolden: f32,
1309 renderer: crate::renderer::Renderer,
1316 },
1317 Initialized { renderer: crate::renderer::Renderer },
1319}
1320
1321impl PaintState {
1322 pub fn new_pending(
1323 window: Arc<dyn Window>,
1324 rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1325 scale: f64,
1326 size: Size,
1327 font_embolden: f32,
1328 ) -> Self {
1329 Self::PendingGpuResources {
1330 window,
1331 rx,
1332 font_embolden,
1333 renderer: Renderer::Uninitialized { scale, size },
1334 }
1335 }
1336
1337 pub fn new(
1338 window: Arc<dyn Window>,
1339 surface: wgpu::Surface<'static>,
1340 gpu_resources: GpuResources,
1341 scale: f64,
1342 size: Size,
1343 font_embolden: f32,
1344 ) -> Self {
1345 let renderer = crate::renderer::Renderer::new(
1346 window.clone(),
1347 gpu_resources,
1348 surface,
1349 scale,
1350 size,
1351 font_embolden,
1352 );
1353 Self::Initialized { renderer }
1354 }
1355
1356 pub(crate) fn renderer(&self) -> &crate::renderer::Renderer {
1357 match self {
1358 PaintState::PendingGpuResources { renderer, .. } => renderer,
1359 PaintState::Initialized { renderer } => renderer,
1360 }
1361 }
1362
1363 pub(crate) fn renderer_mut(&mut self) -> &mut crate::renderer::Renderer {
1364 match self {
1365 PaintState::PendingGpuResources { renderer, .. } => renderer,
1366 PaintState::Initialized { renderer } => renderer,
1367 }
1368 }
1369
1370 pub(crate) fn resize(&mut self, scale: f64, size: Size) {
1371 self.renderer_mut().resize(scale, size);
1372 }
1373
1374 pub(crate) fn set_scale(&mut self, scale: f64) {
1375 self.renderer_mut().set_scale(scale);
1376 }
1377}
1378
1379pub struct UpdateCx<'a> {
1380 pub(crate) app_state: &'a mut AppState,
1381}
1382
1383impl UpdateCx<'_> {
1384 pub fn app_state_mut(&mut self) -> &mut AppState {
1385 self.app_state
1386 }
1387
1388 pub fn app_state(&self) -> &AppState {
1389 self.app_state
1390 }
1391}
1392
1393impl Deref for PaintCx<'_> {
1394 type Target = crate::renderer::Renderer;
1395
1396 fn deref(&self) -> &Self::Target {
1397 self.paint_state.renderer()
1398 }
1399}
1400
1401impl DerefMut for PaintCx<'_> {
1402 fn deref_mut(&mut self) -> &mut Self::Target {
1403 self.paint_state.renderer_mut()
1404 }
1405}
1406
1407fn animations_on_remove(id: ViewId, scope: Scope) -> u16 {
1408 let mut wait_for = 0;
1409 let state = id.state();
1410 let mut state = state.borrow_mut();
1411 state.num_waiting_animations = 0;
1412 let animations = &mut state.animations.stack;
1413 let mut request_style = false;
1414 for anim in animations {
1415 if anim.run_on_remove && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1416 anim.reverse_mut();
1417 request_style = true;
1418 wait_for += 1;
1419 let trigger = anim.on_visual_complete;
1420 scope.create_updater(
1421 move || trigger.track(),
1422 move |_| {
1423 id.transition_anim_complete();
1424 },
1425 );
1426 }
1427 }
1428 drop(state);
1429 if request_style {
1430 id.request_style();
1431 }
1432
1433 id.children()
1434 .into_iter()
1435 .fold(wait_for, |acc, id| acc + animations_on_remove(id, scope))
1436}
1437fn stop_reset_remove_animations(id: ViewId) {
1438 let state = id.state();
1439 let mut state = state.borrow_mut();
1440 let animations = &mut state.animations.stack;
1441 let mut request_style = false;
1442 for anim in animations {
1443 if anim.run_on_remove
1444 && anim.state_kind() == AnimStateKind::PassInProgress
1445 && !matches!(anim.repeat_mode, RepeatMode::LoopForever)
1446 {
1447 anim.start_mut();
1448 request_style = true;
1449 }
1450 }
1451 drop(state);
1452 if request_style {
1453 id.request_style();
1454 }
1455
1456 id.children()
1457 .into_iter()
1458 .for_each(stop_reset_remove_animations)
1459}
1460
1461fn animations_on_create(id: ViewId) {
1462 let state = id.state();
1463 let mut state = state.borrow_mut();
1464 state.num_waiting_animations = 0;
1465 let animations = &mut state.animations.stack;
1466 let mut request_style = false;
1467 for anim in animations {
1468 if anim.run_on_create && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1469 anim.start_mut();
1470 request_style = true;
1471 }
1472 }
1473 drop(state);
1474 if request_style {
1475 id.request_style();
1476 }
1477
1478 id.children().into_iter().for_each(animations_on_create);
1479}