1use crate::{
2 app::{AppContext, MutableAppContext, WindowInvalidation},
3 elements::Element,
4 font_cache::FontCache,
5 geometry::rect::RectF,
6 json::{self, ToJson},
7 platform::{CursorStyle, Event},
8 scene::CursorRegion,
9 text_layout::TextLayoutCache,
10 Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, Entity,
11 FontSystem, ModelHandle, MouseRegion, MouseRegionId, ReadModel, ReadView, RenderContext,
12 RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle,
13 WeakViewHandle,
14};
15use pathfinder_geometry::vector::{vec2f, Vector2F};
16use serde_json::json;
17use std::{
18 collections::{HashMap, HashSet},
19 marker::PhantomData,
20 ops::{Deref, DerefMut},
21 sync::Arc,
22};
23
24pub struct Presenter {
25 window_id: usize,
26 pub(crate) rendered_views: HashMap<usize, ElementBox>,
27 parents: HashMap<usize, usize>,
28 cursor_regions: Vec<CursorRegion>,
29 mouse_regions: Vec<MouseRegion>,
30 font_cache: Arc<FontCache>,
31 text_layout_cache: TextLayoutCache,
32 asset_cache: Arc<AssetCache>,
33 last_mouse_moved_event: Option<Event>,
34 hovered_region_ids: HashSet<MouseRegionId>,
35 clicked_region: Option<MouseRegion>,
36 right_clicked_region: Option<MouseRegion>,
37 prev_drag_position: Option<Vector2F>,
38 titlebar_height: f32,
39}
40
41impl Presenter {
42 pub fn new(
43 window_id: usize,
44 titlebar_height: f32,
45 font_cache: Arc<FontCache>,
46 text_layout_cache: TextLayoutCache,
47 asset_cache: Arc<AssetCache>,
48 cx: &mut MutableAppContext,
49 ) -> Self {
50 Self {
51 window_id,
52 rendered_views: cx.render_views(window_id, titlebar_height),
53 parents: HashMap::new(),
54 cursor_regions: Default::default(),
55 mouse_regions: Default::default(),
56 font_cache,
57 text_layout_cache,
58 asset_cache,
59 last_mouse_moved_event: None,
60 hovered_region_ids: Default::default(),
61 clicked_region: None,
62 right_clicked_region: None,
63 prev_drag_position: None,
64 titlebar_height,
65 }
66 }
67
68 pub fn dispatch_path(&self, app: &AppContext) -> Vec<usize> {
69 let mut path = Vec::new();
70 if let Some(view_id) = app.focused_view_id(self.window_id) {
71 self.compute_dispatch_path_from(view_id, &mut path)
72 }
73 path
74 }
75
76 pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec<usize>) {
77 path.push(view_id);
78 while let Some(parent_id) = self.parents.get(&view_id).copied() {
79 path.push(parent_id);
80 view_id = parent_id;
81 }
82 path.reverse();
83 }
84
85 pub fn invalidate(
86 &mut self,
87 invalidation: &mut WindowInvalidation,
88 cx: &mut MutableAppContext,
89 ) {
90 cx.start_frame();
91 for view_id in &invalidation.removed {
92 invalidation.updated.remove(&view_id);
93 self.rendered_views.remove(&view_id);
94 self.parents.remove(&view_id);
95 }
96 for view_id in &invalidation.updated {
97 self.rendered_views.insert(
98 *view_id,
99 cx.render_view(RenderParams {
100 window_id: self.window_id,
101 view_id: *view_id,
102 titlebar_height: self.titlebar_height,
103 hovered_region_ids: self.hovered_region_ids.clone(),
104 clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id),
105 right_clicked_region_id: self
106 .right_clicked_region
107 .as_ref()
108 .map(MouseRegion::id),
109 refreshing: false,
110 })
111 .unwrap(),
112 );
113 }
114 }
115
116 pub fn refresh(&mut self, invalidation: &mut WindowInvalidation, cx: &mut MutableAppContext) {
117 self.invalidate(invalidation, cx);
118 for (view_id, view) in &mut self.rendered_views {
119 if !invalidation.updated.contains(view_id) {
120 *view = cx
121 .render_view(RenderParams {
122 window_id: self.window_id,
123 view_id: *view_id,
124 titlebar_height: self.titlebar_height,
125 hovered_region_ids: self.hovered_region_ids.clone(),
126 clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id),
127 right_clicked_region_id: self
128 .right_clicked_region
129 .as_ref()
130 .map(MouseRegion::id),
131 refreshing: true,
132 })
133 .unwrap();
134 }
135 }
136 }
137
138 pub fn build_scene(
139 &mut self,
140 window_size: Vector2F,
141 scale_factor: f32,
142 refreshing: bool,
143 cx: &mut MutableAppContext,
144 ) -> Scene {
145 let mut scene = Scene::new(scale_factor);
146
147 if let Some(root_view_id) = cx.root_view_id(self.window_id) {
148 self.layout(window_size, refreshing, cx);
149 let mut paint_cx = self.build_paint_context(&mut scene, cx);
150 paint_cx.paint(
151 root_view_id,
152 Vector2F::zero(),
153 RectF::new(Vector2F::zero(), window_size),
154 );
155 self.text_layout_cache.finish_frame();
156 self.cursor_regions = scene.cursor_regions();
157 self.mouse_regions = scene.mouse_regions();
158
159 if cx.window_is_active(self.window_id) {
160 if let Some(event) = self.last_mouse_moved_event.clone() {
161 self.dispatch_event(event, cx)
162 }
163 }
164 } else {
165 log::error!("could not find root_view_id for window {}", self.window_id);
166 }
167
168 scene
169 }
170
171 fn layout(&mut self, size: Vector2F, refreshing: bool, cx: &mut MutableAppContext) {
172 if let Some(root_view_id) = cx.root_view_id(self.window_id) {
173 self.build_layout_context(refreshing, cx)
174 .layout(root_view_id, SizeConstraint::strict(size));
175 }
176 }
177
178 pub fn build_layout_context<'a>(
179 &'a mut self,
180 refreshing: bool,
181 cx: &'a mut MutableAppContext,
182 ) -> LayoutContext<'a> {
183 LayoutContext {
184 rendered_views: &mut self.rendered_views,
185 parents: &mut self.parents,
186 font_cache: &self.font_cache,
187 font_system: cx.platform().fonts(),
188 text_layout_cache: &self.text_layout_cache,
189 asset_cache: &self.asset_cache,
190 view_stack: Vec::new(),
191 refreshing,
192 hovered_region_ids: self.hovered_region_ids.clone(),
193 clicked_region_id: self.clicked_region.as_ref().map(MouseRegion::id),
194 right_clicked_region_id: self.right_clicked_region.as_ref().map(MouseRegion::id),
195 titlebar_height: self.titlebar_height,
196 app: cx,
197 }
198 }
199
200 pub fn build_paint_context<'a>(
201 &'a mut self,
202 scene: &'a mut Scene,
203 cx: &'a mut MutableAppContext,
204 ) -> PaintContext {
205 PaintContext {
206 scene,
207 font_cache: &self.font_cache,
208 text_layout_cache: &self.text_layout_cache,
209 rendered_views: &mut self.rendered_views,
210 view_stack: Vec::new(),
211 app: cx,
212 }
213 }
214
215 pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) {
216 if let Some(root_view_id) = cx.root_view_id(self.window_id) {
217 let mut invalidated_views = Vec::new();
218 let mut hovered_regions = Vec::new();
219 let mut unhovered_regions = Vec::new();
220 let mut mouse_down_region = None;
221 let mut clicked_region = None;
222 let mut right_mouse_down_region = None;
223 let mut right_clicked_region = None;
224 let mut dragged_region = None;
225
226 match event {
227 Event::LeftMouseDown { position, .. } => {
228 for region in self.mouse_regions.iter().rev() {
229 if region.bounds.contains_point(position) {
230 invalidated_views.push(region.view_id);
231 mouse_down_region = Some((region.clone(), position));
232 self.clicked_region = Some(region.clone());
233 self.prev_drag_position = Some(position);
234 break;
235 }
236 }
237 }
238 Event::LeftMouseUp {
239 position,
240 click_count,
241 ..
242 } => {
243 self.prev_drag_position.take();
244 if let Some(region) = self.clicked_region.take() {
245 invalidated_views.push(region.view_id);
246 if region.bounds.contains_point(position) {
247 clicked_region = Some((region, position, click_count));
248 }
249 }
250 }
251 Event::RightMouseDown { position, .. } => {
252 for region in self.mouse_regions.iter().rev() {
253 if region.bounds.contains_point(position) {
254 invalidated_views.push(region.view_id);
255 right_mouse_down_region = Some((region.clone(), position));
256 self.right_clicked_region = Some(region.clone());
257 break;
258 }
259 }
260 }
261 Event::RightMouseUp {
262 position,
263 click_count,
264 ..
265 } => {
266 if let Some(region) = self.right_clicked_region.take() {
267 invalidated_views.push(region.view_id);
268 if region.bounds.contains_point(position) {
269 right_clicked_region = Some((region, position, click_count));
270 }
271 }
272 }
273 Event::MouseMoved {
274 position,
275 left_mouse_down,
276 } => {
277 self.last_mouse_moved_event = Some(event.clone());
278
279 if !left_mouse_down {
280 let mut style_to_assign = CursorStyle::Arrow;
281 for region in self.cursor_regions.iter().rev() {
282 if region.bounds.contains_point(position) {
283 style_to_assign = region.style;
284 break;
285 }
286 }
287 cx.platform().set_cursor_style(style_to_assign);
288
289 for region in self.mouse_regions.iter().rev() {
290 let region_id = region.id();
291 if region.bounds.contains_point(position) {
292 if !self.hovered_region_ids.contains(®ion_id) {
293 invalidated_views.push(region.view_id);
294 hovered_regions.push(region.clone());
295 self.hovered_region_ids.insert(region_id);
296 }
297 } else {
298 if self.hovered_region_ids.contains(®ion_id) {
299 invalidated_views.push(region.view_id);
300 unhovered_regions.push(region.clone());
301 self.hovered_region_ids.remove(®ion_id);
302 }
303 }
304 }
305 }
306 }
307 Event::LeftMouseDragged { position } => {
308 if let Some((clicked_region, prev_drag_position)) = self
309 .clicked_region
310 .as_ref()
311 .zip(self.prev_drag_position.as_mut())
312 {
313 dragged_region =
314 Some((clicked_region.clone(), position - *prev_drag_position));
315 *prev_drag_position = position;
316 }
317
318 self.last_mouse_moved_event = Some(Event::MouseMoved {
319 position,
320 left_mouse_down: true,
321 });
322 }
323 _ => {}
324 }
325
326 let mut event_cx = self.build_event_context(cx);
327 let mut handled = false;
328 for unhovered_region in unhovered_regions {
329 if let Some(hover_callback) = unhovered_region.hover {
330 handled = true;
331 event_cx.with_current_view(unhovered_region.view_id, |event_cx| {
332 hover_callback(false, event_cx);
333 })
334 }
335 }
336
337 for hovered_region in hovered_regions {
338 if let Some(hover_callback) = hovered_region.hover {
339 handled = true;
340 event_cx.with_current_view(hovered_region.view_id, |event_cx| {
341 hover_callback(true, event_cx);
342 })
343 }
344 }
345
346 if let Some((mouse_down_region, position)) = mouse_down_region {
347 if let Some(mouse_down_callback) = mouse_down_region.mouse_down {
348 handled = true;
349 event_cx.with_current_view(mouse_down_region.view_id, |event_cx| {
350 mouse_down_callback(position, event_cx);
351 })
352 }
353 }
354
355 if let Some((clicked_region, position, click_count)) = clicked_region {
356 if let Some(click_callback) = clicked_region.click {
357 handled = true;
358 event_cx.with_current_view(clicked_region.view_id, |event_cx| {
359 click_callback(position, click_count, event_cx);
360 })
361 }
362 }
363
364 if let Some((right_mouse_down_region, position)) = right_mouse_down_region {
365 if let Some(right_mouse_down_callback) = right_mouse_down_region.right_mouse_down {
366 handled = true;
367 event_cx.with_current_view(right_mouse_down_region.view_id, |event_cx| {
368 right_mouse_down_callback(position, event_cx);
369 })
370 }
371 }
372
373 if let Some((right_clicked_region, position, click_count)) = right_clicked_region {
374 if let Some(right_click_callback) = right_clicked_region.right_click {
375 handled = true;
376 event_cx.with_current_view(right_clicked_region.view_id, |event_cx| {
377 right_click_callback(position, click_count, event_cx);
378 })
379 }
380 }
381
382 if let Some((dragged_region, delta)) = dragged_region {
383 if let Some(drag_callback) = dragged_region.drag {
384 handled = true;
385 event_cx.with_current_view(dragged_region.view_id, |event_cx| {
386 drag_callback(delta, event_cx);
387 })
388 }
389 }
390
391 if !handled {
392 event_cx.dispatch_event(root_view_id, &event);
393 }
394
395 invalidated_views.extend(event_cx.invalidated_views);
396 let dispatch_directives = event_cx.dispatched_actions;
397
398 for view_id in invalidated_views {
399 cx.notify_view(self.window_id, view_id);
400 }
401
402 let mut dispatch_path = Vec::new();
403 for directive in dispatch_directives {
404 dispatch_path.clear();
405 if let Some(view_id) = directive.dispatcher_view_id {
406 self.compute_dispatch_path_from(view_id, &mut dispatch_path);
407 }
408 cx.dispatch_action_any(self.window_id, &dispatch_path, directive.action.as_ref());
409 }
410 }
411 }
412
413 pub fn build_event_context<'a>(
414 &'a mut self,
415 cx: &'a mut MutableAppContext,
416 ) -> EventContext<'a> {
417 EventContext {
418 rendered_views: &mut self.rendered_views,
419 dispatched_actions: Default::default(),
420 font_cache: &self.font_cache,
421 text_layout_cache: &self.text_layout_cache,
422 view_stack: Default::default(),
423 invalidated_views: Default::default(),
424 notify_count: 0,
425 app: cx,
426 }
427 }
428
429 pub fn debug_elements(&self, cx: &AppContext) -> Option<json::Value> {
430 let view = cx.root_view(self.window_id)?;
431 Some(json!({
432 "root_view": view.debug_json(cx),
433 "root_element": self.rendered_views.get(&view.id())
434 .map(|root_element| {
435 root_element.debug(&DebugContext {
436 rendered_views: &self.rendered_views,
437 font_cache: &self.font_cache,
438 app: cx,
439 })
440 })
441 }))
442 }
443}
444
445pub struct DispatchDirective {
446 pub dispatcher_view_id: Option<usize>,
447 pub action: Box<dyn Action>,
448}
449
450pub struct LayoutContext<'a> {
451 rendered_views: &'a mut HashMap<usize, ElementBox>,
452 parents: &'a mut HashMap<usize, usize>,
453 view_stack: Vec<usize>,
454 pub font_cache: &'a Arc<FontCache>,
455 pub font_system: Arc<dyn FontSystem>,
456 pub text_layout_cache: &'a TextLayoutCache,
457 pub asset_cache: &'a AssetCache,
458 pub app: &'a mut MutableAppContext,
459 pub refreshing: bool,
460 titlebar_height: f32,
461 hovered_region_ids: HashSet<MouseRegionId>,
462 clicked_region_id: Option<MouseRegionId>,
463 right_clicked_region_id: Option<MouseRegionId>,
464}
465
466impl<'a> LayoutContext<'a> {
467 fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
468 if let Some(parent_id) = self.view_stack.last() {
469 self.parents.insert(view_id, *parent_id);
470 }
471 self.view_stack.push(view_id);
472 let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
473 let size = rendered_view.layout(constraint, self);
474 self.rendered_views.insert(view_id, rendered_view);
475 self.view_stack.pop();
476 size
477 }
478
479 pub fn render<F, V, T>(&mut self, handle: &ViewHandle<V>, f: F) -> T
480 where
481 F: FnOnce(&mut V, &mut RenderContext<V>) -> T,
482 V: View,
483 {
484 handle.update(self.app, |view, cx| {
485 let mut render_cx = RenderContext {
486 app: cx,
487 window_id: handle.window_id(),
488 view_id: handle.id(),
489 view_type: PhantomData,
490 titlebar_height: self.titlebar_height,
491 hovered_region_ids: self.hovered_region_ids.clone(),
492 clicked_region_id: self.clicked_region_id,
493 right_clicked_region_id: self.right_clicked_region_id,
494 refreshing: self.refreshing,
495 };
496 f(view, &mut render_cx)
497 })
498 }
499}
500
501impl<'a> Deref for LayoutContext<'a> {
502 type Target = MutableAppContext;
503
504 fn deref(&self) -> &Self::Target {
505 self.app
506 }
507}
508
509impl<'a> DerefMut for LayoutContext<'a> {
510 fn deref_mut(&mut self) -> &mut Self::Target {
511 self.app
512 }
513}
514
515impl<'a> ReadView for LayoutContext<'a> {
516 fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
517 self.app.read_view(handle)
518 }
519}
520
521impl<'a> ReadModel for LayoutContext<'a> {
522 fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
523 self.app.read_model(handle)
524 }
525}
526
527impl<'a> UpgradeModelHandle for LayoutContext<'a> {
528 fn upgrade_model_handle<T: Entity>(
529 &self,
530 handle: &WeakModelHandle<T>,
531 ) -> Option<ModelHandle<T>> {
532 self.app.upgrade_model_handle(handle)
533 }
534
535 fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
536 self.app.model_handle_is_upgradable(handle)
537 }
538
539 fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
540 self.app.upgrade_any_model_handle(handle)
541 }
542}
543
544impl<'a> UpgradeViewHandle for LayoutContext<'a> {
545 fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
546 self.app.upgrade_view_handle(handle)
547 }
548
549 fn upgrade_any_view_handle(&self, handle: &crate::AnyWeakViewHandle) -> Option<AnyViewHandle> {
550 self.app.upgrade_any_view_handle(handle)
551 }
552}
553
554pub struct PaintContext<'a> {
555 rendered_views: &'a mut HashMap<usize, ElementBox>,
556 view_stack: Vec<usize>,
557 pub scene: &'a mut Scene,
558 pub font_cache: &'a FontCache,
559 pub text_layout_cache: &'a TextLayoutCache,
560 pub app: &'a AppContext,
561}
562
563impl<'a> PaintContext<'a> {
564 fn paint(&mut self, view_id: usize, origin: Vector2F, visible_bounds: RectF) {
565 if let Some(mut tree) = self.rendered_views.remove(&view_id) {
566 self.view_stack.push(view_id);
567 tree.paint(origin, visible_bounds, self);
568 self.rendered_views.insert(view_id, tree);
569 self.view_stack.pop();
570 }
571 }
572
573 pub fn current_view_id(&self) -> usize {
574 *self.view_stack.last().unwrap()
575 }
576}
577
578impl<'a> Deref for PaintContext<'a> {
579 type Target = AppContext;
580
581 fn deref(&self) -> &Self::Target {
582 self.app
583 }
584}
585
586pub struct EventContext<'a> {
587 rendered_views: &'a mut HashMap<usize, ElementBox>,
588 dispatched_actions: Vec<DispatchDirective>,
589 pub font_cache: &'a FontCache,
590 pub text_layout_cache: &'a TextLayoutCache,
591 pub app: &'a mut MutableAppContext,
592 pub notify_count: usize,
593 view_stack: Vec<usize>,
594 invalidated_views: HashSet<usize>,
595}
596
597impl<'a> EventContext<'a> {
598 fn dispatch_event(&mut self, view_id: usize, event: &Event) -> bool {
599 if let Some(mut element) = self.rendered_views.remove(&view_id) {
600 let result =
601 self.with_current_view(view_id, |this| element.dispatch_event(event, this));
602 self.rendered_views.insert(view_id, element);
603 result
604 } else {
605 false
606 }
607 }
608
609 fn with_current_view<F, T>(&mut self, view_id: usize, f: F) -> T
610 where
611 F: FnOnce(&mut Self) -> T,
612 {
613 self.view_stack.push(view_id);
614 let result = f(self);
615 self.view_stack.pop();
616 result
617 }
618
619 pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
620 self.dispatched_actions.push(DispatchDirective {
621 dispatcher_view_id: self.view_stack.last().copied(),
622 action,
623 });
624 }
625
626 pub fn dispatch_action<A: Action>(&mut self, action: A) {
627 self.dispatch_any_action(Box::new(action));
628 }
629
630 pub fn notify(&mut self) {
631 self.notify_count += 1;
632 if let Some(view_id) = self.view_stack.last() {
633 self.invalidated_views.insert(*view_id);
634 }
635 }
636
637 pub fn notify_count(&self) -> usize {
638 self.notify_count
639 }
640}
641
642impl<'a> Deref for EventContext<'a> {
643 type Target = MutableAppContext;
644
645 fn deref(&self) -> &Self::Target {
646 self.app
647 }
648}
649
650impl<'a> DerefMut for EventContext<'a> {
651 fn deref_mut(&mut self) -> &mut Self::Target {
652 self.app
653 }
654}
655
656pub struct DebugContext<'a> {
657 rendered_views: &'a HashMap<usize, ElementBox>,
658 pub font_cache: &'a FontCache,
659 pub app: &'a AppContext,
660}
661
662#[derive(Clone, Copy, Debug, Eq, PartialEq)]
663pub enum Axis {
664 Horizontal,
665 Vertical,
666}
667
668impl Axis {
669 pub fn invert(self) -> Self {
670 match self {
671 Self::Horizontal => Self::Vertical,
672 Self::Vertical => Self::Horizontal,
673 }
674 }
675}
676
677impl ToJson for Axis {
678 fn to_json(&self) -> serde_json::Value {
679 match self {
680 Axis::Horizontal => json!("horizontal"),
681 Axis::Vertical => json!("vertical"),
682 }
683 }
684}
685
686pub trait Vector2FExt {
687 fn along(self, axis: Axis) -> f32;
688}
689
690impl Vector2FExt for Vector2F {
691 fn along(self, axis: Axis) -> f32 {
692 match axis {
693 Axis::Horizontal => self.x(),
694 Axis::Vertical => self.y(),
695 }
696 }
697}
698
699#[derive(Copy, Clone, Debug)]
700pub struct SizeConstraint {
701 pub min: Vector2F,
702 pub max: Vector2F,
703}
704
705impl SizeConstraint {
706 pub fn new(min: Vector2F, max: Vector2F) -> Self {
707 Self { min, max }
708 }
709
710 pub fn strict(size: Vector2F) -> Self {
711 Self {
712 min: size,
713 max: size,
714 }
715 }
716
717 pub fn strict_along(axis: Axis, max: f32) -> Self {
718 match axis {
719 Axis::Horizontal => Self {
720 min: vec2f(max, 0.0),
721 max: vec2f(max, f32::INFINITY),
722 },
723 Axis::Vertical => Self {
724 min: vec2f(0.0, max),
725 max: vec2f(f32::INFINITY, max),
726 },
727 }
728 }
729
730 pub fn max_along(&self, axis: Axis) -> f32 {
731 match axis {
732 Axis::Horizontal => self.max.x(),
733 Axis::Vertical => self.max.y(),
734 }
735 }
736
737 pub fn min_along(&self, axis: Axis) -> f32 {
738 match axis {
739 Axis::Horizontal => self.min.x(),
740 Axis::Vertical => self.min.y(),
741 }
742 }
743
744 pub fn constrain(&self, size: Vector2F) -> Vector2F {
745 vec2f(
746 size.x().min(self.max.x()).max(self.min.x()),
747 size.y().min(self.max.y()).max(self.min.y()),
748 )
749 }
750}
751
752impl ToJson for SizeConstraint {
753 fn to_json(&self) -> serde_json::Value {
754 json!({
755 "min": self.min.to_json(),
756 "max": self.max.to_json(),
757 })
758 }
759}
760
761pub struct ChildView {
762 view: AnyViewHandle,
763}
764
765impl ChildView {
766 pub fn new(view: impl Into<AnyViewHandle>) -> Self {
767 Self { view: view.into() }
768 }
769}
770
771impl Element for ChildView {
772 type LayoutState = ();
773 type PaintState = ();
774
775 fn layout(
776 &mut self,
777 constraint: SizeConstraint,
778 cx: &mut LayoutContext,
779 ) -> (Vector2F, Self::LayoutState) {
780 let size = cx.layout(self.view.id(), constraint);
781 (size, ())
782 }
783
784 fn paint(
785 &mut self,
786 bounds: RectF,
787 visible_bounds: RectF,
788 _: &mut Self::LayoutState,
789 cx: &mut PaintContext,
790 ) -> Self::PaintState {
791 cx.paint(self.view.id(), bounds.origin(), visible_bounds);
792 }
793
794 fn dispatch_event(
795 &mut self,
796 event: &Event,
797 _: RectF,
798 _: RectF,
799 _: &mut Self::LayoutState,
800 _: &mut Self::PaintState,
801 cx: &mut EventContext,
802 ) -> bool {
803 cx.dispatch_event(self.view.id(), event)
804 }
805
806 fn debug(
807 &self,
808 bounds: RectF,
809 _: &Self::LayoutState,
810 _: &Self::PaintState,
811 cx: &DebugContext,
812 ) -> serde_json::Value {
813 json!({
814 "type": "ChildView",
815 "view_id": self.view.id(),
816 "bounds": bounds.to_json(),
817 "view": self.view.debug_json(cx.app),
818 "child": if let Some(view) = cx.rendered_views.get(&self.view.id()) {
819 view.debug(cx)
820 } else {
821 json!(null)
822 }
823 })
824 }
825}