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