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 ui_events::keyboard::{KeyState, KeyboardEvent};
11use ui_events::pointer::{PointerButton, PointerButtonEvent, PointerEvent, PointerUpdate};
12use winit::window::Window;
13
14#[cfg(not(target_arch = "wasm32"))]
15use std::time::{Duration, Instant};
16#[cfg(target_arch = "wasm32")]
17use web_time::{Duration, Instant};
18
19#[cfg(feature = "crossbeam")]
20use crossbeam::channel::Receiver;
21#[cfg(not(feature = "crossbeam"))]
22use std::sync::mpsc::Receiver;
23
24use taffy::prelude::NodeId;
25
26use crate::animate::{AnimStateKind, RepeatMode};
27use crate::dropped_file::FileDragEvent;
28use crate::easing::{Easing, Linear};
29use crate::menu::Menu;
30use crate::renderer::Renderer;
31use crate::style::{Disabled, DisplayProp, Focusable, Hidden, PointerEvents, PointerEventsProp};
32use crate::view_state::IsHiddenState;
33use crate::{
34 action::{exec_after, show_context_menu},
35 event::{Event, EventListener, EventPropagation},
36 id::ViewId,
37 inspector::CaptureState,
38 style::{Style, StyleProp, ZIndex},
39 view::{View, paint_bg, paint_border, paint_outline},
40 view_state::ChangeFlags,
41 window_state::WindowState,
42};
43
44pub type EventCallback = dyn FnMut(&Event) -> EventPropagation;
45pub type ResizeCallback = dyn Fn(Rect);
46pub type MenuCallback = dyn Fn() -> Menu;
47
48#[derive(Default)]
49pub(crate) struct ResizeListeners {
50 pub(crate) rect: Rect,
51 pub(crate) callbacks: Vec<Rc<ResizeCallback>>,
52}
53
54#[derive(Default)]
56pub(crate) struct MoveListeners {
57 pub(crate) window_origin: Point,
58 pub(crate) callbacks: Vec<Rc<dyn Fn(Point)>>,
59}
60
61pub(crate) type CleanupListeners = Vec<Rc<dyn Fn()>>;
62
63pub struct DragState {
64 pub(crate) id: ViewId,
65 pub(crate) offset: Vec2,
66 pub(crate) released_at: Option<Instant>,
67 pub(crate) release_location: Option<Point>,
68}
69
70pub(crate) enum FrameUpdate {
71 Style(ViewId),
72 Layout(ViewId),
73 Paint(ViewId),
74}
75
76#[derive(Debug, PartialEq, Eq)]
77pub(crate) enum PointerEventConsumed {
78 Yes,
79 No,
80}
81
82pub struct EventCx<'a> {
84 pub window_state: &'a mut WindowState,
85}
86
87impl EventCx<'_> {
88 pub fn update_active(&mut self, id: ViewId) {
89 self.window_state.update_active(id);
90 }
91
92 pub fn is_active(&self, id: ViewId) -> bool {
93 self.window_state.is_active(&id)
94 }
95
96 #[allow(unused)]
97 pub(crate) fn update_focus(&mut self, id: ViewId, keyboard_navigation: bool) {
98 self.window_state.update_focus(id, keyboard_navigation);
99 }
100
101 pub(crate) fn unconditional_view_event(
103 &mut self,
104 view_id: ViewId,
105 event: Event,
106 directed: bool,
107 ) -> (EventPropagation, PointerEventConsumed) {
108 if view_id.is_hidden() {
109 return (EventPropagation::Continue, PointerEventConsumed::No);
111 }
112 if view_id.is_disabled() && !event.allow_disabled() {
113 return (EventPropagation::Continue, PointerEventConsumed::No);
116 }
117
118 let event = self.offset_event(view_id, event);
125
126 let view = view_id.view();
127 let view_state = view_id.state();
128
129 let disable_default = if let Some(listener) = event.listener() {
130 view_state
131 .borrow()
132 .disable_default_events
133 .contains(&listener)
134 } else {
135 false
136 };
137
138 let is_pointer_none = event.is_pointer()
139 && view_state.borrow().computed_style.get(PointerEventsProp)
140 == Some(PointerEvents::None);
141
142 if !disable_default
143 && !is_pointer_none
144 && view
145 .borrow_mut()
146 .event_before_children(self, &event)
147 .is_processed()
148 {
149 if let Event::Pointer(PointerEvent::Down(PointerButtonEvent { state, .. })) = &event {
150 if view_state.borrow().computed_style.get(Focusable) {
151 let rect = view_id.get_size().unwrap_or_default().to_rect();
152 let point = state.logical_point();
153 let now_focused = rect.contains(point);
154 if now_focused {
155 self.window_state.update_focus(view_id, false);
156 }
157 }
158 }
159 if let Event::Pointer(PointerEvent::Move(_)) = &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.window_state.cursor.is_none() {
164 self.window_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::Pointer(PointerEvent::Down(PointerButtonEvent {
225 pointer,
226 state,
227 button,
228 ..
229 })) => {
230 self.window_state.clicking.insert(view_id);
231 let point = state.logical_point();
232 if pointer.is_primary_pointer()
233 && button.is_none_or(|b| b == PointerButton::Primary)
234 {
235 let rect = view_id.get_size().unwrap_or_default().to_rect();
236 let on_view = rect.contains(point);
237
238 if on_view {
239 if view_state.borrow().computed_style.get(Focusable) {
240 self.window_state.update_focus(view_id, false);
242 }
243 if state.count == 2
244 && view_state
245 .borrow()
246 .event_listeners
247 .contains_key(&EventListener::DoubleClick)
248 {
249 view_state.borrow_mut().last_pointer_down = Some(state.clone());
250 }
251 if view_state
252 .borrow()
253 .event_listeners
254 .contains_key(&EventListener::Click)
255 {
256 view_state.borrow_mut().last_pointer_down = Some(state.clone());
257 }
258
259 #[cfg(target_os = "macos")]
260 if let Some((ep, pec)) = popout_menu() {
261 return (ep, pec);
262 };
263
264 let bottom_left = {
265 let layout = view_state.borrow().layout_rect;
266 Point::new(layout.x0, layout.y1)
267 };
268 let popout_menu = view_state.borrow().popout_menu.clone();
269 if let Some(menu) = popout_menu {
270 show_context_menu(menu(), Some(bottom_left));
271 return (EventPropagation::Stop, PointerEventConsumed::Yes);
272 }
273 if view_id.can_drag() && self.window_state.drag_start.is_none() {
274 self.window_state.drag_start = Some((view_id, point));
275 }
276 }
277 } else if button.is_some_and(|b| b == PointerButton::Secondary) {
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 view_state
287 .borrow()
288 .event_listeners
289 .contains_key(&EventListener::SecondaryClick)
290 {
291 view_state.borrow_mut().last_pointer_down = Some(state.clone());
292 }
293 }
294 }
295 }
296 Event::Pointer(PointerEvent::Move(PointerUpdate { current, .. })) => {
297 let rect = view_id.get_size().unwrap_or_default().to_rect();
298 if rect.contains(current.logical_point()) {
299 if self.window_state.is_dragging() {
300 self.window_state.dragging_over.insert(view_id);
301 view_id.apply_event(&EventListener::DragOver, &event);
302 } else {
303 self.window_state.hovered.insert(view_id);
304 let view_state = view_state.borrow();
305 let style = view_state.combined_style.builtin();
306 if let Some(cursor) = style.cursor() {
307 if self.window_state.cursor.is_none() {
308 self.window_state.cursor = Some(cursor);
309 }
310 }
311 }
312 }
313 if view_id.can_drag() {
314 if let Some((_, drag_start)) = self
315 .window_state
316 .drag_start
317 .as_ref()
318 .filter(|(drag_id, _)| drag_id == &view_id)
319 {
320 let offset = current.logical_point() - *drag_start;
321 if let Some(dragging) = self
322 .window_state
323 .dragging
324 .as_mut()
325 .filter(|d| d.id == view_id && d.released_at.is_none())
326 {
327 dragging.offset = drag_start.to_vec2();
329 self.window_state.request_paint(view_id);
330 } else if offset.x.abs() + offset.y.abs() > 1.0 {
331 self.window_state.active = None;
333 self.window_state.dragging = Some(DragState {
334 id: view_id,
335 offset: drag_start.to_vec2(),
336 released_at: None,
337 release_location: None,
338 });
339 self.update_active(view_id);
340 self.window_state.request_paint(view_id);
341 view_id.apply_event(&EventListener::DragStart, &event);
342 }
343 }
344 }
345 if view_id
346 .apply_event(&EventListener::PointerMove, &event)
347 .is_some_and(|prop| prop.is_processed())
348 {
349 return (EventPropagation::Stop, PointerEventConsumed::Yes);
350 }
351 }
352 Event::Pointer(PointerEvent::Up(PointerButtonEvent {
353 button,
354 pointer,
355 state,
356 })) => {
357 if pointer.is_primary_pointer()
358 && button.is_none_or(|b| b == PointerButton::Primary)
359 {
360 let rect = view_id.get_size().unwrap_or_default().to_rect();
361 let on_view = rect.contains(state.logical_point());
362
363 #[cfg(not(target_os = "macos"))]
364 if on_view {
365 if let Some((ep, pec)) = popout_menu() {
366 return (ep, pec);
367 };
368 }
369
370 if !directed {
371 if on_view {
372 if let Some(dragging) = self.window_state.dragging.as_mut() {
373 let dragging_id = dragging.id;
374 if view_id
375 .apply_event(&EventListener::Drop, &event)
376 .is_some_and(|prop| prop.is_processed())
377 {
378 self.window_state.dragging = None;
381 self.window_state.request_paint(view_id);
382 dragging_id.apply_event(&EventListener::DragEnd, &event);
383 }
384 }
385 }
386 } else if let Some(dragging) = self
387 .window_state
388 .dragging
389 .as_mut()
390 .filter(|d| d.id == view_id)
391 {
392 let dragging_id = dragging.id;
393 dragging.released_at = Some(Instant::now());
394 dragging.release_location = Some(state.logical_point());
395 self.window_state.request_paint(view_id);
396 dragging_id.apply_event(&EventListener::DragEnd, &event);
397 }
398
399 let last_pointer_down = view_state.borrow_mut().last_pointer_down.take();
400
401 let event_listeners = view_state.borrow().event_listeners.clone();
402 if let Some(handlers) = event_listeners.get(&EventListener::DoubleClick) {
403 view_state.borrow_mut();
404 if on_view
405 && self.window_state.is_clicking(&view_id)
406 && last_pointer_down
407 .as_ref()
408 .map(|s| s.count == 2)
409 .unwrap_or(false)
410 && handlers.iter().fold(false, |handled, handler| {
411 handled | (handler.borrow_mut())(&event).is_processed()
412 })
413 {
414 return (EventPropagation::Stop, PointerEventConsumed::Yes);
415 }
416 }
417
418 if let Some(handlers) = event_listeners.get(&EventListener::Click) {
419 if on_view
420 && self.window_state.is_clicking(&view_id)
421 && last_pointer_down.is_some()
422 && handlers.iter().fold(false, |handled, handler| {
423 handled | (handler.borrow_mut())(&event).is_processed()
424 })
425 {
426 return (EventPropagation::Stop, PointerEventConsumed::Yes);
427 }
428 }
429
430 if view_id
431 .apply_event(&EventListener::PointerUp, &event)
432 .is_some_and(|prop| prop.is_processed())
433 {
434 return (EventPropagation::Stop, PointerEventConsumed::Yes);
435 }
436 } else if button.is_some_and(|b| b == PointerButton::Secondary) {
437 let rect = view_id.get_size().unwrap_or_default().to_rect();
438 let on_view = rect.contains(state.logical_point());
439
440 let last_pointer_down = view_state.borrow_mut().last_pointer_down.take();
441 let event_listeners = view_state.borrow().event_listeners.clone();
442 if let Some(handlers) = event_listeners.get(&EventListener::SecondaryClick)
443 {
444 if on_view
445 && last_pointer_down.is_some()
446 && handlers.iter().fold(false, |handled, handler| {
447 handled | (handler.borrow_mut())(&event).is_processed()
448 })
449 {
450 return (EventPropagation::Stop, PointerEventConsumed::Yes);
451 }
452 }
453
454 let viewport_event_position = {
455 let layout = view_state.borrow().layout_rect;
456 Point::new(
457 layout.x0 + state.logical_point().x,
458 layout.y0 + state.logical_point().y,
459 )
460 };
461 let context_menu = view_state.borrow().context_menu.clone();
462 if let Some(menu) = context_menu {
463 show_context_menu(menu(), Some(viewport_event_position));
464 return (EventPropagation::Stop, PointerEventConsumed::Yes);
465 }
466 }
467 }
468 Event::Key(KeyboardEvent {
469 state: KeyState::Down,
470 ..
471 }) => {
472 if self.window_state.is_focused(&view_id) && event.is_keyboard_trigger() {
473 view_id.apply_event(&EventListener::Click, &event);
474 }
475 }
476 Event::WindowResized(_) => {
477 if view_state.borrow().has_style_selectors.has_responsive() {
478 view_id.request_style();
479 }
480 }
481 Event::FileDrag(e @ FileDragEvent::DragMoved { .. }) => {
482 if let Some(point) = e.logical_point() {
483 let rect = view_id.get_size().unwrap_or_default().to_rect();
484 let on_view = rect.contains(point);
485 if on_view {
486 self.window_state.file_hovered.insert(view_id);
487 view_id.request_style();
488 }
489 }
490 }
491 _ => (),
492 }
493 }
494
495 if !disable_default {
496 if let Some(listener) = event.listener() {
497 let event_listeners = view_state.borrow().event_listeners.clone();
498 if let Some(handlers) = event_listeners.get(&listener).cloned() {
499 let should_run = if let Some(pos) = event.point() {
500 let rect = view_id.get_size().unwrap_or_default().to_rect();
501 rect.contains(pos)
502 } else {
503 true
504 };
505 if should_run
506 && handlers.iter().fold(false, |handled, handler| {
507 handled | (handler.borrow_mut())(&event).is_processed()
508 })
509 {
510 return (EventPropagation::Stop, PointerEventConsumed::Yes);
511 }
512 }
513 }
514 }
515
516 (EventPropagation::Continue, PointerEventConsumed::Yes)
517 }
518
519 pub(crate) fn offset_event(&self, id: ViewId, event: Event) -> Event {
521 let state = id.state();
522 let viewport = state.borrow().viewport;
523 let transform = state.borrow().transform;
524
525 if let Some(layout) = id.get_layout() {
526 event.transform(
527 Affine::translate((
528 layout.location.x as f64 - viewport.map(|rect| rect.x0).unwrap_or(0.0),
529 layout.location.y as f64 - viewport.map(|rect| rect.y0).unwrap_or(0.0),
530 )) * transform,
531 )
532 } else {
533 event
534 }
535 }
536
537 pub fn should_send(&mut self, id: ViewId, event: &Event) -> bool {
540 if id.is_hidden() || (id.is_disabled() && !event.allow_disabled()) {
541 return false;
542 }
543
544 let Some(point) = event.point() else {
545 return true;
546 };
547
548 let layout_rect = id.layout_rect();
549 let Some(layout) = id.get_layout() else {
550 return false;
551 };
552
553 let current_rect = layout_rect.with_origin(Point::new(
555 layout.location.x as f64,
556 layout.location.y as f64,
557 ));
558 let transform = id.state().borrow().transform;
560 let current_rect = transform.transform_rect_bbox(current_rect);
561
562 if !current_rect.contains(point) {
563 return false;
564 }
565
566 true
567 }
568}
569
570#[derive(Default)]
571pub struct InteractionState {
572 pub(crate) is_hovered: bool,
573 pub(crate) is_selected: bool,
574 pub(crate) is_disabled: bool,
575 pub(crate) is_focused: bool,
576 pub(crate) is_clicking: bool,
577 pub(crate) is_dark_mode: bool,
578 pub(crate) is_file_hover: bool,
579 pub(crate) using_keyboard_navigation: bool,
580}
581
582pub struct StyleCx<'a> {
583 pub window_state: &'a mut WindowState,
584 pub(crate) current_view: ViewId,
585 pub(crate) current: Rc<Style>,
587 pub(crate) direct: Style,
588 saved: Vec<Rc<Style>>,
589 pub(crate) now: Instant,
590 saved_disabled: Vec<bool>,
591 saved_selected: Vec<bool>,
592 saved_hidden: Vec<bool>,
593 disabled: bool,
594 hidden: bool,
595 selected: bool,
596}
597
598impl<'a> StyleCx<'a> {
599 pub(crate) fn new(window_state: &'a mut WindowState, root: ViewId) -> Self {
600 Self {
601 window_state,
602 current_view: root,
603 current: Default::default(),
604 direct: Default::default(),
605 saved: Default::default(),
606 now: Instant::now(),
607 saved_disabled: Default::default(),
608 saved_selected: Default::default(),
609 saved_hidden: Default::default(),
610 disabled: false,
611 hidden: false,
612 selected: false,
613 }
614 }
615
616 pub fn selected(&mut self) {
618 self.selected = true;
619 }
620
621 pub fn hidden(&mut self) {
622 self.hidden = true;
623 }
624
625 fn get_interact_state(&self, id: &ViewId) -> InteractionState {
626 InteractionState {
627 is_selected: self.selected || id.is_selected(),
628 is_hovered: self.window_state.is_hovered(id),
629 is_disabled: id.is_disabled() || self.disabled,
630 is_focused: self.window_state.is_focused(id),
631 is_clicking: self.window_state.is_clicking(id),
632 is_dark_mode: self.window_state.is_dark_mode(),
633 is_file_hover: self.window_state.is_file_hover(id),
634 using_keyboard_navigation: self.window_state.keyboard_navigation,
635 }
636 }
637
638 pub fn style_view(&mut self, view_id: ViewId) {
640 self.save();
641 let view = view_id.view();
642 let view_state = view_id.state();
643 {
644 let mut view_state = view_state.borrow_mut();
645 if !view_state.requested_changes.contains(ChangeFlags::STYLE)
646 && !view_state
647 .requested_changes
648 .contains(ChangeFlags::VIEW_STYLE)
649 {
650 self.restore();
651 return;
652 }
653 view_state.requested_changes.remove(ChangeFlags::STYLE);
654 }
655 let view_class = view.borrow().view_class();
656 {
657 let mut view_state = view_state.borrow_mut();
658 if view_state
659 .requested_changes
660 .contains(ChangeFlags::VIEW_STYLE)
661 {
662 view_state.requested_changes.remove(ChangeFlags::VIEW_STYLE);
663 if let Some(view_style) = view.borrow().view_style() {
664 let offset = view_state.view_style_offset;
665 view_state.style.set(offset, view_style);
666 }
667 }
668 if view_state.request_style_recursive {
670 view_state.request_style_recursive = false;
671 let children = view_id.children();
672 for child in children {
673 let view_state = child.state();
674 let mut state = view_state.borrow_mut();
675 state.request_style_recursive = true;
676 state.requested_changes.insert(ChangeFlags::STYLE);
677 }
678 }
679 }
680
681 let view_interact_state = self.get_interact_state(&view_id);
682 self.disabled = view_interact_state.is_disabled;
683 let (mut new_frame, classes_applied) = view_id.state().borrow_mut().compute_combined(
684 view_interact_state,
685 self.window_state.screen_size_bp,
686 view_class,
687 &self.current,
688 self.hidden,
689 );
690 if classes_applied {
691 let children = view_id.children();
692 for child in children {
693 let view_state = child.state();
694 let mut state = view_state.borrow_mut();
695 state.request_style_recursive = true;
696 state.requested_changes.insert(ChangeFlags::STYLE);
697 }
698 }
699
700 self.direct = view_state.borrow().combined_style.clone();
701 Style::apply_only_inherited(&mut self.current, &self.direct);
702 let mut computed_style = (*self.current).clone();
703 computed_style.apply_mut(self.direct.clone());
704 CaptureState::capture_style(view_id, self, computed_style.clone());
705 if computed_style.get(Focusable)
706 && !computed_style.get(Disabled)
707 && !computed_style.get(Hidden)
708 && computed_style.get(DisplayProp) != taffy::Display::None
709 {
710 self.window_state.focusable.insert(view_id);
711 } else {
712 self.window_state.focusable.remove(&view_id);
713 }
714 view_state.borrow_mut().computed_style = computed_style;
715 self.hidden |= view_id.is_hidden();
716
717 self.current_view = view_id;
719
720 {
721 let mut view_state = view_state.borrow_mut();
722 view_state.layout_props.read_explicit(
725 &self.direct,
726 &self.current,
727 &self.now,
728 &mut new_frame,
729 );
730 if new_frame {
731 self.window_state.schedule_layout(view_id);
733 }
734
735 view_state.view_style_props.read_explicit(
736 &self.direct,
737 &self.current,
738 &self.now,
739 &mut new_frame,
740 );
741 if new_frame && !self.hidden {
742 self.window_state.schedule_style(view_id);
743 }
744 }
745 let layout_style = view_state.borrow().layout_props.to_style();
747 let taffy_style = self.direct.clone().apply(layout_style).to_taffy_style();
748 if taffy_style != view_state.borrow().taffy_style {
749 view_state.borrow_mut().taffy_style = taffy_style;
750 self.window_state.schedule_layout(view_id);
751 }
752
753 view.borrow_mut().style_pass(self);
754
755 let mut is_hidden_state = view_state.borrow().is_hidden_state;
756 let computed_display = view_state.borrow().combined_style.get(DisplayProp);
757 is_hidden_state.transition(
758 computed_display,
759 || {
760 let count = animations_on_remove(view_id, Scope::current());
761 view_state.borrow_mut().num_waiting_animations = count;
762 count > 0
763 },
764 || {
765 animations_on_create(view_id);
766 },
767 || {
768 stop_reset_remove_animations(view_id);
769 },
770 || view_state.borrow().num_waiting_animations,
771 );
772
773 view_state.borrow_mut().is_hidden_state = is_hidden_state;
774 let modified = view_state
775 .borrow()
776 .combined_style
777 .clone()
778 .apply_opt(is_hidden_state.get_display(), Style::display);
779
780 view_state.borrow_mut().combined_style = modified;
781
782 let mut transform = Affine::IDENTITY;
783
784 let transform_x = match view_state.borrow().layout_props.translate_x() {
785 crate::unit::PxPct::Px(px) => px,
786 crate::unit::PxPct::Pct(pct) => pct / 100.,
787 };
788 let transform_y = match view_state.borrow().layout_props.translate_y() {
789 crate::unit::PxPct::Px(px) => px,
790 crate::unit::PxPct::Pct(pct) => pct / 100.,
791 };
792 transform *= Affine::translate(Vec2 {
793 x: transform_x,
794 y: transform_y,
795 });
796
797 let scale_x = view_state.borrow().layout_props.scale_x().0 / 100.;
798 let scale_y = view_state.borrow().layout_props.scale_y().0 / 100.;
799 let size = view_id.layout_rect();
800 let center_x = size.width() / 2.;
801 let center_y = size.height() / 2.;
802 transform *= Affine::translate(Vec2 {
803 x: center_x,
804 y: center_y,
805 });
806 transform *= Affine::scale_non_uniform(scale_x, scale_y);
807 let rotation = view_state.borrow().layout_props.rotation().0;
808 transform *= Affine::rotate(rotation);
809 transform *= Affine::translate(Vec2 {
810 x: -center_x,
811 y: -center_y,
812 });
813
814 view_state.borrow_mut().transform = transform;
815
816 self.restore();
817 }
818
819 pub fn now(&self) -> Instant {
820 self.now
821 }
822
823 pub fn save(&mut self) {
824 self.saved.push(self.current.clone());
825 self.saved_disabled.push(self.disabled);
826 self.saved_selected.push(self.selected);
827 self.saved_hidden.push(self.hidden);
828 }
829
830 pub fn restore(&mut self) {
831 self.current = self.saved.pop().unwrap_or_default();
832 self.disabled = self.saved_disabled.pop().unwrap_or_default();
833 self.selected = self.saved_selected.pop().unwrap_or_default();
834 self.hidden = self.saved_hidden.pop().unwrap_or_default();
835 }
836
837 pub fn get_prop<P: StyleProp>(&self, _prop: P) -> Option<P::Type> {
838 self.direct
839 .get_prop::<P>()
840 .or_else(|| self.current.get_prop::<P>())
841 }
842
843 pub fn style(&self) -> Style {
844 (*self.current).clone().apply(self.direct.clone())
845 }
846
847 pub fn direct_style(&self) -> &Style {
848 &self.direct
849 }
850
851 pub fn indirect_style(&self) -> &Style {
852 &self.current
853 }
854
855 pub fn request_transition(&mut self) {
856 let id = self.current_view;
857 self.window_state.schedule_style(id);
858 }
859}
860
861pub struct ComputeLayoutCx<'a> {
862 pub window_state: &'a mut WindowState,
863 pub(crate) viewport: Rect,
864 pub(crate) window_origin: Point,
865 pub(crate) saved_viewports: Vec<Rect>,
866 pub(crate) saved_window_origins: Vec<Point>,
867}
868
869impl<'a> ComputeLayoutCx<'a> {
870 pub(crate) fn new(window_state: &'a mut WindowState, viewport: Rect) -> Self {
871 Self {
872 window_state,
873 viewport,
874 window_origin: Point::ZERO,
875 saved_viewports: Vec::new(),
876 saved_window_origins: Vec::new(),
877 }
878 }
879
880 pub fn window_origin(&self) -> Point {
881 self.window_origin
882 }
883
884 pub fn save(&mut self) {
885 self.saved_viewports.push(self.viewport);
886 self.saved_window_origins.push(self.window_origin);
887 }
888
889 pub fn restore(&mut self) {
890 self.viewport = self.saved_viewports.pop().unwrap_or_default();
891 self.window_origin = self.saved_window_origins.pop().unwrap_or_default();
892 }
893
894 pub fn current_viewport(&self) -> Rect {
895 self.viewport
896 }
897
898 pub fn compute_view_layout(&mut self, id: ViewId) -> Option<Rect> {
907 let view_state = id.state();
908
909 if view_state.borrow().is_hidden_state == IsHiddenState::Hidden {
910 view_state.borrow_mut().layout_rect = Rect::ZERO;
911 return None;
912 }
913
914 self.save();
915
916 let layout = id.get_layout().unwrap_or_default();
917 let origin = Point::new(layout.location.x as f64, layout.location.y as f64);
918 let this_viewport = view_state.borrow().viewport;
919 let this_viewport_origin = this_viewport.unwrap_or_default().origin().to_vec2();
920 let size = Size::new(layout.size.width as f64, layout.size.height as f64);
921 let parent_viewport = self.viewport.with_origin(
922 Point::new(
923 self.viewport.x0 - layout.location.x as f64,
924 self.viewport.y0 - layout.location.y as f64,
925 ) + this_viewport_origin,
926 );
927 self.viewport = parent_viewport.intersect(size.to_rect());
928 if let Some(this_viewport) = this_viewport {
929 self.viewport = self.viewport.intersect(this_viewport);
930 }
931
932 let window_origin = origin + self.window_origin.to_vec2() - this_viewport_origin;
933 self.window_origin = window_origin;
934 {
935 view_state.borrow_mut().window_origin = window_origin;
936 }
937
938 {
939 let view_state = view_state.borrow();
940 let mut resize_listeners = view_state.resize_listeners.borrow_mut();
941
942 let new_rect = size.to_rect().with_origin(origin);
943 if new_rect != resize_listeners.rect {
944 resize_listeners.rect = new_rect;
945
946 let callbacks = resize_listeners.callbacks.clone();
947
948 std::mem::drop(resize_listeners);
950 std::mem::drop(view_state);
951
952 for callback in callbacks {
953 (*callback)(new_rect);
954 }
955 }
956 }
957
958 {
959 let view_state = view_state.borrow();
960 let mut move_listeners = view_state.move_listeners.borrow_mut();
961
962 if window_origin != move_listeners.window_origin {
963 move_listeners.window_origin = window_origin;
964
965 let callbacks = move_listeners.callbacks.clone();
966
967 std::mem::drop(move_listeners);
969 std::mem::drop(view_state);
970
971 for callback in callbacks {
972 (*callback)(window_origin);
973 }
974 }
975 }
976
977 let view = id.view();
978 let child_layout_rect = view.borrow_mut().compute_layout(self);
979
980 let layout_rect = size.to_rect().with_origin(self.window_origin);
981 let layout_rect = if let Some(child_layout_rect) = child_layout_rect {
982 layout_rect.union(child_layout_rect)
983 } else {
984 layout_rect
985 };
986
987 let transform = view_state.borrow().transform;
988 let layout_rect = transform.transform_rect_bbox(layout_rect);
989
990 view_state.borrow_mut().layout_rect = layout_rect;
991
992 self.restore();
993
994 Some(layout_rect)
995 }
996}
997
998pub struct LayoutCx<'a> {
1001 pub window_state: &'a mut WindowState,
1002}
1003
1004impl<'a> LayoutCx<'a> {
1005 pub(crate) fn new(window_state: &'a mut WindowState) -> Self {
1006 Self { window_state }
1007 }
1008
1009 pub fn layout_node(
1015 &mut self,
1016 id: ViewId,
1017 has_children: bool,
1018 mut children: impl FnMut(&mut LayoutCx) -> Vec<NodeId>,
1019 ) -> NodeId {
1020 let view_state = id.state();
1021 let node = view_state.borrow().node;
1022 if !view_state
1023 .borrow()
1024 .requested_changes
1025 .contains(ChangeFlags::LAYOUT)
1026 {
1027 return node;
1028 }
1029 view_state
1030 .borrow_mut()
1031 .requested_changes
1032 .remove(ChangeFlags::LAYOUT);
1033 let layout_style = view_state.borrow().layout_props.to_style();
1034 let animate_out_display = view_state.borrow().is_hidden_state.get_display();
1035 let style = view_state
1036 .borrow()
1037 .combined_style
1038 .clone()
1039 .apply(layout_style)
1040 .apply_opt(animate_out_display, Style::display)
1041 .to_taffy_style();
1042 let _ = id.taffy().borrow_mut().set_style(node, style);
1043
1044 if has_children {
1045 let nodes = children(self);
1046 let _ = id.taffy().borrow_mut().set_children(node, &nodes);
1047 }
1048
1049 node
1050 }
1051
1052 pub fn layout_view(&mut self, view: &mut dyn View) -> NodeId {
1054 view.layout(self)
1055 }
1056}
1057
1058std::thread_local! {
1059 static CURRENT_DRAG_PAINTING_ID : std::cell::Cell<Option<ViewId>> = const { std::cell::Cell::new(None) };
1066}
1067
1068pub struct PaintCx<'a> {
1069 pub window_state: &'a mut WindowState,
1070 pub(crate) paint_state: &'a mut PaintState,
1071 pub(crate) transform: Affine,
1072 pub(crate) clip: Option<RoundedRect>,
1073 pub(crate) z_index: Option<i32>,
1074 pub(crate) saved_transforms: Vec<Affine>,
1075 pub(crate) saved_clips: Vec<Option<RoundedRect>>,
1076 pub(crate) saved_z_indexes: Vec<Option<i32>>,
1077 pub gpu_resources: Option<GpuResources>,
1078 pub window: Arc<dyn Window>,
1079 #[cfg(feature = "vello")]
1080 pub layer_count: usize,
1081 #[cfg(feature = "vello")]
1082 pub saved_layer_counts: Vec<usize>,
1083}
1084
1085impl PaintCx<'_> {
1086 pub fn save(&mut self) {
1087 self.saved_transforms.push(self.transform);
1088 self.saved_clips.push(self.clip);
1089 self.saved_z_indexes.push(self.z_index);
1090 #[cfg(feature = "vello")]
1091 self.saved_layer_counts.push(self.layer_count);
1092 }
1093
1094 pub fn restore(&mut self) {
1095 #[cfg(feature = "vello")]
1096 {
1097 let saved_count = self.saved_layer_counts.pop().unwrap_or_default();
1098 while self.layer_count > saved_count {
1099 self.pop_layer();
1100 self.layer_count -= 1;
1101 }
1102 }
1103
1104 self.transform = self.saved_transforms.pop().unwrap_or_default();
1105 self.clip = self.saved_clips.pop().unwrap_or_default();
1106 self.z_index = self.saved_z_indexes.pop().unwrap_or_default();
1107 self.paint_state
1108 .renderer_mut()
1109 .set_transform(self.transform);
1110 if let Some(z_index) = self.z_index {
1111 self.paint_state.renderer_mut().set_z_index(z_index);
1112 } else {
1113 self.paint_state.renderer_mut().set_z_index(0);
1114 }
1115
1116 #[cfg(not(feature = "vello"))]
1117 {
1118 if let Some(rect) = self.clip {
1119 self.paint_state.renderer_mut().clip(&rect);
1120 } else {
1121 self.paint_state.renderer_mut().clear_clip();
1122 }
1123 }
1124 }
1125
1126 pub fn is_drag_paint(&self, id: ViewId) -> bool {
1131 if let Some(dragging) = CURRENT_DRAG_PAINTING_ID.get() {
1136 return dragging == id;
1137 }
1138 false
1139 }
1140
1141 pub fn paint_children(&mut self, id: ViewId) {
1143 let children = id.children();
1144 for child in children {
1145 self.paint_view(child);
1146 }
1147 }
1148
1149 pub fn paint_view(&mut self, id: ViewId) {
1156 if id.is_hidden() {
1157 return;
1158 }
1159 let view = id.view();
1160 let view_state = id.state();
1161
1162 self.save();
1163 let size = self.transform(id);
1164 let is_empty = self
1165 .clip
1166 .map(|rect| rect.rect().intersect(size.to_rect()).is_zero_area())
1167 .unwrap_or(false);
1168 if !is_empty {
1169 let style = view_state.borrow().combined_style.clone();
1170 let view_style_props = view_state.borrow().view_style_props.clone();
1171 let layout_props = view_state.borrow().layout_props.clone();
1172
1173 if let Some(z_index) = style.get(ZIndex) {
1174 self.set_z_index(z_index);
1175 }
1176
1177 paint_bg(self, &view_style_props, size);
1178
1179 view.borrow_mut().paint(self);
1180 paint_border(self, &layout_props, &view_style_props, size);
1181 paint_outline(self, &view_style_props, size)
1182 }
1183 let mut drag_set_to_none = false;
1184
1185 if let Some(dragging) = self.window_state.dragging.as_ref() {
1186 if dragging.id == id {
1187 let transform = if let Some((released_at, release_location)) =
1188 dragging.released_at.zip(dragging.release_location)
1189 {
1190 let easing = Linear;
1191 const ANIMATION_DURATION_MS: f64 = 300.0;
1192 let elapsed = released_at.elapsed().as_millis() as f64;
1193 let progress = elapsed / ANIMATION_DURATION_MS;
1194
1195 if !(easing.finished(progress)) {
1196 let offset_scale = 1.0 - easing.eval(progress);
1197 let release_offset = release_location.to_vec2() - dragging.offset;
1198
1199 exec_after(Duration::from_millis(8), move |_| {
1201 id.request_paint();
1202 });
1203
1204 Some(self.transform * Affine::translate(release_offset * offset_scale))
1205 } else {
1206 drag_set_to_none = true;
1207 None
1208 }
1209 } else {
1210 let translation =
1212 self.window_state.last_cursor_location.to_vec2() - dragging.offset;
1213 Some(self.transform.with_translation(translation))
1214 };
1215
1216 if let Some(transform) = transform {
1217 self.save();
1218 self.transform = transform;
1219 self.paint_state
1220 .renderer_mut()
1221 .set_transform(self.transform);
1222 self.set_z_index(1000);
1223 self.clear_clip();
1224
1225 let style = view_state.borrow().combined_style.clone();
1227 let mut view_style_props = view_state.borrow().view_style_props.clone();
1228
1229 if let Some(dragging_style) = view_state.borrow().dragging_style.clone() {
1230 let style = style.apply(dragging_style);
1231 let mut _new_frame = false;
1232 view_style_props.read_explicit(
1233 &style,
1234 &style,
1235 &Instant::now(),
1236 &mut _new_frame,
1237 );
1238 }
1239
1240 let layout_props = view_state.borrow().layout_props.clone();
1242
1243 CURRENT_DRAG_PAINTING_ID.set(Some(id));
1248
1249 paint_bg(self, &view_style_props, size);
1250 view.borrow_mut().paint(self);
1251 paint_border(self, &layout_props, &view_style_props, size);
1252 paint_outline(self, &view_style_props, size);
1253
1254 self.restore();
1255
1256 CURRENT_DRAG_PAINTING_ID.take();
1257 }
1258 }
1259 }
1260
1261 if drag_set_to_none {
1262 self.window_state.dragging = None;
1263 }
1264 self.restore();
1265 }
1266
1267 pub fn clip(&mut self, shape: &impl Shape) {
1269 #[cfg(feature = "vello")]
1270 {
1271 use peniko::Mix;
1272
1273 self.push_layer(Mix::Normal, 1.0, Affine::IDENTITY, shape);
1274 self.layer_count += 1;
1275 self.clip = Some(shape.bounding_box().to_rounded_rect(0.0));
1276 }
1277
1278 #[cfg(not(feature = "vello"))]
1279 {
1280 let rect = if let Some(rect) = shape.as_rect() {
1281 rect.to_rounded_rect(0.0)
1282 } else if let Some(rect) = shape.as_rounded_rect() {
1283 rect
1284 } else {
1285 let rect = shape.bounding_box();
1286 rect.to_rounded_rect(0.0)
1287 };
1288
1289 let rect = if let Some(existing) = self.clip {
1290 let rect = existing.rect().intersect(rect.rect());
1291 self.paint_state.renderer_mut().clip(&rect);
1292 rect.to_rounded_rect(0.0)
1293 } else {
1294 self.paint_state.renderer_mut().clip(&shape);
1295 rect
1296 };
1297
1298 self.clip = Some(rect);
1299 }
1300 }
1301
1302 pub fn clear_clip(&mut self) {
1304 self.clip = None;
1305 self.paint_state.renderer_mut().clear_clip();
1306 }
1307
1308 pub fn offset(&mut self, offset: (f64, f64)) {
1309 let mut new = self.transform.as_coeffs();
1310 new[4] += offset.0;
1311 new[5] += offset.1;
1312 self.transform = Affine::new(new);
1313 self.paint_state
1314 .renderer_mut()
1315 .set_transform(self.transform);
1316 if let Some(rect) = self.clip.as_mut() {
1317 let raidus = rect.radii();
1318 *rect = rect
1319 .rect()
1320 .with_origin(rect.origin() - Vec2::new(offset.0, offset.1))
1321 .to_rounded_rect(raidus);
1322 }
1323 }
1324
1325 pub fn transform(&mut self, id: ViewId) -> Size {
1326 if let Some(layout) = id.get_layout() {
1327 let offset = layout.location;
1328 self.transform *= Affine::translate(Vec2 {
1329 x: offset.x as f64,
1330 y: offset.y as f64,
1331 });
1332 self.transform *= id.state().borrow().transform;
1333
1334 self.paint_state
1335 .renderer_mut()
1336 .set_transform(self.transform);
1337
1338 if let Some(rect) = self.clip.as_mut() {
1339 let raidus = rect.radii();
1340 *rect = rect
1341 .rect()
1342 .with_origin(rect.origin() - Vec2::new(offset.x as f64, offset.y as f64))
1343 .to_rounded_rect(raidus);
1344 }
1345
1346 Size::new(layout.size.width as f64, layout.size.height as f64)
1347 } else {
1348 Size::ZERO
1349 }
1350 }
1351
1352 pub(crate) fn set_z_index(&mut self, z_index: i32) {
1353 self.z_index = Some(z_index);
1354 self.paint_state.renderer_mut().set_z_index(z_index);
1355 }
1356
1357 pub fn is_focused(&self, id: ViewId) -> bool {
1358 self.window_state.is_focused(&id)
1359 }
1360}
1361
1362pub enum PaintState {
1364 PendingGpuResources {
1366 window: Arc<dyn Window>,
1367 rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1368 font_embolden: f32,
1369 renderer: crate::renderer::Renderer,
1376 },
1377 Initialized { renderer: crate::renderer::Renderer },
1379}
1380
1381impl PaintState {
1382 pub fn new_pending(
1383 window: Arc<dyn Window>,
1384 rx: Receiver<Result<(GpuResources, wgpu::Surface<'static>), GpuResourceError>>,
1385 scale: f64,
1386 size: Size,
1387 font_embolden: f32,
1388 ) -> Self {
1389 Self::PendingGpuResources {
1390 window,
1391 rx,
1392 font_embolden,
1393 renderer: Renderer::Uninitialized { scale, size },
1394 }
1395 }
1396
1397 pub fn new(
1398 window: Arc<dyn Window>,
1399 surface: wgpu::Surface<'static>,
1400 gpu_resources: GpuResources,
1401 scale: f64,
1402 size: Size,
1403 font_embolden: f32,
1404 ) -> Self {
1405 let renderer = crate::renderer::Renderer::new(
1406 window.clone(),
1407 gpu_resources,
1408 surface,
1409 scale,
1410 size,
1411 font_embolden,
1412 );
1413 Self::Initialized { renderer }
1414 }
1415
1416 pub(crate) fn renderer(&self) -> &crate::renderer::Renderer {
1417 match self {
1418 PaintState::PendingGpuResources { renderer, .. } => renderer,
1419 PaintState::Initialized { renderer } => renderer,
1420 }
1421 }
1422
1423 pub(crate) fn renderer_mut(&mut self) -> &mut crate::renderer::Renderer {
1424 match self {
1425 PaintState::PendingGpuResources { renderer, .. } => renderer,
1426 PaintState::Initialized { renderer } => renderer,
1427 }
1428 }
1429
1430 pub(crate) fn resize(&mut self, scale: f64, size: Size) {
1431 self.renderer_mut().resize(scale, size);
1432 }
1433
1434 pub(crate) fn set_scale(&mut self, scale: f64) {
1435 self.renderer_mut().set_scale(scale);
1436 }
1437}
1438
1439pub struct UpdateCx<'a> {
1440 pub window_state: &'a mut WindowState,
1441}
1442
1443impl Deref for PaintCx<'_> {
1444 type Target = crate::renderer::Renderer;
1445
1446 fn deref(&self) -> &Self::Target {
1447 self.paint_state.renderer()
1448 }
1449}
1450
1451impl DerefMut for PaintCx<'_> {
1452 fn deref_mut(&mut self) -> &mut Self::Target {
1453 self.paint_state.renderer_mut()
1454 }
1455}
1456
1457fn animations_on_remove(id: ViewId, scope: Scope) -> u16 {
1458 let mut wait_for = 0;
1459 let state = id.state();
1460 let mut state = state.borrow_mut();
1461 state.num_waiting_animations = 0;
1462 let animations = &mut state.animations.stack;
1463 let mut request_style = false;
1464 for anim in animations {
1465 if anim.run_on_remove && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1466 anim.reverse_mut();
1467 request_style = true;
1468 wait_for += 1;
1469 let trigger = anim.on_visual_complete;
1470 scope.create_updater(
1471 move || trigger.track(),
1472 move |_| {
1473 id.transition_anim_complete();
1474 },
1475 );
1476 }
1477 }
1478 drop(state);
1479 if request_style {
1480 id.request_style();
1481 }
1482
1483 id.children()
1484 .into_iter()
1485 .fold(wait_for, |acc, id| acc + animations_on_remove(id, scope))
1486}
1487fn stop_reset_remove_animations(id: ViewId) {
1488 let state = id.state();
1489 let mut state = state.borrow_mut();
1490 let animations = &mut state.animations.stack;
1491 let mut request_style = false;
1492 for anim in animations {
1493 if anim.run_on_remove
1494 && anim.state_kind() == AnimStateKind::PassInProgress
1495 && !matches!(anim.repeat_mode, RepeatMode::LoopForever)
1496 {
1497 anim.start_mut();
1498 request_style = true;
1499 }
1500 }
1501 drop(state);
1502 if request_style {
1503 id.request_style();
1504 }
1505
1506 id.children()
1507 .into_iter()
1508 .for_each(stop_reset_remove_animations)
1509}
1510
1511fn animations_on_create(id: ViewId) {
1512 let state = id.state();
1513 let mut state = state.borrow_mut();
1514 state.num_waiting_animations = 0;
1515 let animations = &mut state.animations.stack;
1516 let mut request_style = false;
1517 for anim in animations {
1518 if anim.run_on_create && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
1519 anim.start_mut();
1520 request_style = true;
1521 }
1522 }
1523 drop(state);
1524 if request_style {
1525 id.request_style();
1526 }
1527
1528 id.children().into_iter().for_each(animations_on_create);
1529}