1mod align;
2mod canvas;
3mod clipped;
4mod constrained_box;
5mod container;
6mod empty;
7mod expanded;
8mod flex;
9mod hook;
10mod image;
11mod keystroke_label;
12mod label;
13mod list;
14mod mouse_event_handler;
15mod overlay;
16mod resizable;
17mod stack;
18mod svg;
19mod text;
20mod tooltip;
21mod uniform_list;
22
23pub use self::{
24 align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*,
25 keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, resizable::*,
26 stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
27};
28pub use crate::window::ChildView;
29
30use self::{clipped::Clipped, expanded::Expanded};
31use crate::{
32 geometry::{
33 rect::RectF,
34 vector::{vec2f, Vector2F},
35 },
36 json, Action, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle, WindowContext,
37};
38use anyhow::{anyhow, Result};
39use core::panic;
40use json::ToJson;
41use std::{any::Any, borrow::Cow, marker::PhantomData, mem, ops::Range};
42use util::ResultExt;
43
44trait AnyDrawable<V: View> {
45 fn layout(
46 &mut self,
47 constraint: SizeConstraint,
48 view: &mut V,
49 cx: &mut ViewContext<V>,
50 ) -> Vector2F;
51
52 fn paint(
53 &mut self,
54 scene: &mut SceneBuilder,
55 origin: Vector2F,
56 visible_bounds: RectF,
57 view: &mut V,
58 cx: &mut ViewContext<V>,
59 );
60
61 fn rect_for_text_range(
62 &self,
63 range_utf16: Range<usize>,
64 view: &V,
65 cx: &ViewContext<V>,
66 ) -> Option<RectF>;
67
68 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
69
70 fn size(&self) -> Vector2F;
71
72 fn metadata(&self) -> Option<&dyn Any>;
73}
74
75pub trait Drawable<V: View> {
76 type LayoutState;
77 type PaintState;
78
79 fn layout(
80 &mut self,
81 constraint: SizeConstraint,
82 view: &mut V,
83 cx: &mut ViewContext<V>,
84 ) -> (Vector2F, Self::LayoutState);
85
86 fn paint(
87 &mut self,
88 scene: &mut SceneBuilder,
89 bounds: RectF,
90 visible_bounds: RectF,
91 layout: &mut Self::LayoutState,
92 view: &mut V,
93 cx: &mut ViewContext<V>,
94 ) -> Self::PaintState;
95
96 fn rect_for_text_range(
97 &self,
98 range_utf16: Range<usize>,
99 bounds: RectF,
100 visible_bounds: RectF,
101 layout: &Self::LayoutState,
102 paint: &Self::PaintState,
103 view: &V,
104 cx: &ViewContext<V>,
105 ) -> Option<RectF>;
106
107 fn metadata(&self) -> Option<&dyn Any> {
108 None
109 }
110
111 fn debug(
112 &self,
113 bounds: RectF,
114 layout: &Self::LayoutState,
115 paint: &Self::PaintState,
116 view: &V,
117 cx: &ViewContext<V>,
118 ) -> serde_json::Value;
119
120 fn boxed(self) -> Element<V>
121 where
122 Self: 'static + Sized,
123 {
124 Element {
125 element: Box::new(Lifecycle::Init { element: self }),
126 view_type: PhantomData,
127 name: None,
128 }
129 }
130
131 fn named(self, name: impl Into<Cow<'static, str>>) -> Element<V>
132 where
133 Self: 'static + Sized,
134 {
135 Element {
136 element: Box::new(Lifecycle::Init { element: self }),
137 view_type: PhantomData,
138 name: Some(name.into()),
139 }
140 }
141
142 fn constrained(self) -> ConstrainedBox<V>
143 where
144 Self: 'static + Sized,
145 {
146 ConstrainedBox::new(self.boxed())
147 }
148
149 fn aligned(self) -> Align<V>
150 where
151 Self: 'static + Sized,
152 {
153 Align::new(self.boxed())
154 }
155
156 fn clipped(self) -> Clipped<V>
157 where
158 Self: 'static + Sized,
159 {
160 Clipped::new(self.boxed())
161 }
162
163 fn contained(self) -> Container<V>
164 where
165 Self: 'static + Sized,
166 {
167 Container::new(self.boxed())
168 }
169
170 fn expanded(self) -> Expanded<V>
171 where
172 Self: 'static + Sized,
173 {
174 Expanded::new(self.boxed())
175 }
176
177 fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
178 where
179 Self: 'static + Sized,
180 {
181 FlexItem::new(self.boxed()).flex(flex, expanded)
182 }
183
184 fn flex_float(self) -> FlexItem<V>
185 where
186 Self: 'static + Sized,
187 {
188 FlexItem::new(self.boxed()).float()
189 }
190
191 fn with_tooltip<Tag: 'static>(
192 self,
193 id: usize,
194 text: String,
195 action: Option<Box<dyn Action>>,
196 style: TooltipStyle,
197 cx: &mut ViewContext<V>,
198 ) -> Tooltip<V>
199 where
200 Self: 'static + Sized,
201 {
202 Tooltip::new::<Tag, V>(id, text, action, style, self.boxed(), cx)
203 }
204
205 fn with_resize_handle<Tag: 'static>(
206 self,
207 element_id: usize,
208 side: Side,
209 handle_size: f32,
210 initial_size: f32,
211 cx: &mut ViewContext<V>,
212 ) -> Resizable<V>
213 where
214 Self: 'static + Sized,
215 {
216 Resizable::new::<Tag, V>(
217 self.boxed(),
218 element_id,
219 side,
220 handle_size,
221 initial_size,
222 cx,
223 )
224 }
225}
226
227pub enum Lifecycle<V: View, E: Drawable<V>> {
228 Empty,
229 Init {
230 element: E,
231 },
232 PostLayout {
233 element: E,
234 constraint: SizeConstraint,
235 size: Vector2F,
236 layout: E::LayoutState,
237 },
238 PostPaint {
239 element: E,
240 constraint: SizeConstraint,
241 bounds: RectF,
242 visible_bounds: RectF,
243 layout: E::LayoutState,
244 paint: E::PaintState,
245 },
246}
247
248impl<V: View, E: Drawable<V>> AnyDrawable<V> for Lifecycle<V, E> {
249 fn layout(
250 &mut self,
251 constraint: SizeConstraint,
252 view: &mut V,
253 cx: &mut ViewContext<V>,
254 ) -> Vector2F {
255 let result;
256 *self = match mem::take(self) {
257 Lifecycle::Empty => unreachable!(),
258 Lifecycle::Init { mut element }
259 | Lifecycle::PostLayout { mut element, .. }
260 | Lifecycle::PostPaint { mut element, .. } => {
261 let (size, layout) = element.layout(constraint, view, cx);
262 debug_assert!(size.x().is_finite());
263 debug_assert!(size.y().is_finite());
264
265 result = size;
266 Lifecycle::PostLayout {
267 element,
268 constraint,
269 size,
270 layout,
271 }
272 }
273 };
274 result
275 }
276
277 fn paint(
278 &mut self,
279 scene: &mut SceneBuilder,
280 origin: Vector2F,
281 visible_bounds: RectF,
282 view: &mut V,
283 cx: &mut ViewContext<V>,
284 ) {
285 *self = match mem::take(self) {
286 Lifecycle::PostLayout {
287 mut element,
288 constraint,
289 size,
290 mut layout,
291 } => {
292 let bounds = RectF::new(origin, size);
293 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
294 Lifecycle::PostPaint {
295 element,
296 constraint,
297 bounds,
298 visible_bounds,
299 layout,
300 paint,
301 }
302 }
303 Lifecycle::PostPaint {
304 mut element,
305 constraint,
306 bounds,
307 mut layout,
308 ..
309 } => {
310 let bounds = RectF::new(origin, bounds.size());
311 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
312 Lifecycle::PostPaint {
313 element,
314 constraint,
315 bounds,
316 visible_bounds,
317 layout,
318 paint,
319 }
320 }
321 Lifecycle::Empty => panic!("invalid element lifecycle state"),
322 Lifecycle::Init { .. } => {
323 panic!("invalid element lifecycle state, paint called before layout")
324 }
325 }
326 }
327
328 fn rect_for_text_range(
329 &self,
330 range_utf16: Range<usize>,
331 view: &V,
332 cx: &ViewContext<V>,
333 ) -> Option<RectF> {
334 if let Lifecycle::PostPaint {
335 element,
336 bounds,
337 visible_bounds,
338 layout,
339 paint,
340 ..
341 } = self
342 {
343 element.rect_for_text_range(
344 range_utf16,
345 *bounds,
346 *visible_bounds,
347 layout,
348 paint,
349 view,
350 cx,
351 )
352 } else {
353 None
354 }
355 }
356
357 fn size(&self) -> Vector2F {
358 match self {
359 Lifecycle::Empty | Lifecycle::Init { .. } => panic!("invalid element lifecycle state"),
360 Lifecycle::PostLayout { size, .. } => *size,
361 Lifecycle::PostPaint { bounds, .. } => bounds.size(),
362 }
363 }
364
365 fn metadata(&self) -> Option<&dyn Any> {
366 match self {
367 Lifecycle::Empty => unreachable!(),
368 Lifecycle::Init { element }
369 | Lifecycle::PostLayout { element, .. }
370 | Lifecycle::PostPaint { element, .. } => element.metadata(),
371 }
372 }
373
374 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
375 match self {
376 Lifecycle::PostPaint {
377 element,
378 constraint,
379 bounds,
380 visible_bounds,
381 layout,
382 paint,
383 } => {
384 let mut value = element.debug(*bounds, layout, paint, view, cx);
385 if let json::Value::Object(map) = &mut value {
386 let mut new_map: crate::json::Map<String, serde_json::Value> =
387 Default::default();
388 if let Some(typ) = map.remove("type") {
389 new_map.insert("type".into(), typ);
390 }
391 new_map.insert("constraint".into(), constraint.to_json());
392 new_map.insert("bounds".into(), bounds.to_json());
393 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
394 new_map.append(map);
395 json::Value::Object(new_map)
396 } else {
397 value
398 }
399 }
400
401 _ => panic!("invalid element lifecycle state"),
402 }
403 }
404}
405
406impl<V: View, E: Drawable<V>> Default for Lifecycle<V, E> {
407 fn default() -> Self {
408 Self::Empty
409 }
410}
411
412pub struct Element<V: View> {
413 element: Box<dyn AnyDrawable<V>>,
414 view_type: PhantomData<V>,
415 name: Option<Cow<'static, str>>,
416}
417
418impl<V: View> Element<V> {
419 pub fn name(&self) -> Option<&str> {
420 self.name.as_deref()
421 }
422
423 pub fn metadata<T: 'static>(&self) -> Option<&T> {
424 self.element
425 .metadata()
426 .and_then(|data| data.downcast_ref::<T>())
427 }
428
429 pub fn layout(
430 &mut self,
431 constraint: SizeConstraint,
432 view: &mut V,
433 cx: &mut ViewContext<V>,
434 ) -> Vector2F {
435 self.element.layout(constraint, view, cx)
436 }
437
438 pub fn paint(
439 &mut self,
440 scene: &mut SceneBuilder,
441 origin: Vector2F,
442 visible_bounds: RectF,
443 view: &mut V,
444 cx: &mut ViewContext<V>,
445 ) {
446 self.element.paint(scene, origin, visible_bounds, view, cx);
447 }
448
449 pub fn rect_for_text_range(
450 &self,
451 range_utf16: Range<usize>,
452 view: &V,
453 cx: &ViewContext<V>,
454 ) -> Option<RectF> {
455 self.element.rect_for_text_range(range_utf16, view, cx)
456 }
457
458 pub fn size(&self) -> Vector2F {
459 self.element.size()
460 }
461
462 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
463 let mut value = self.element.debug(view, cx);
464
465 if let Some(name) = &self.name {
466 if let json::Value::Object(map) = &mut value {
467 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
468 new_map.insert("name".into(), json::Value::String(name.to_string()));
469 new_map.append(map);
470 return json::Value::Object(new_map);
471 }
472 }
473
474 value
475 }
476
477 pub fn with_metadata<T, F, R>(&self, f: F) -> R
478 where
479 T: 'static,
480 F: FnOnce(Option<&T>) -> R,
481 {
482 f(self.element.metadata().and_then(|m| m.downcast_ref()))
483 }
484}
485
486pub struct RootElement<V: View> {
487 element: Element<V>,
488 view: WeakViewHandle<V>,
489}
490
491impl<V: View> RootElement<V> {
492 pub fn new(element: Element<V>, view: WeakViewHandle<V>) -> Self {
493 Self { element, view }
494 }
495}
496
497pub trait AnyRootElement {
498 fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F>;
499 fn paint(
500 &mut self,
501 scene: &mut SceneBuilder,
502 origin: Vector2F,
503 visible_bounds: RectF,
504 cx: &mut WindowContext,
505 ) -> Result<()>;
506 fn rect_for_text_range(
507 &self,
508 range_utf16: Range<usize>,
509 cx: &WindowContext,
510 ) -> Result<Option<RectF>>;
511 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
512 fn name(&self) -> Option<&str>;
513}
514
515impl<V: View> AnyRootElement for RootElement<V> {
516 fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F> {
517 let view = self
518 .view
519 .upgrade(cx)
520 .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
521 view.update(cx, |view, cx| Ok(self.element.layout(constraint, view, cx)))
522 }
523
524 fn paint(
525 &mut self,
526 scene: &mut SceneBuilder,
527 origin: Vector2F,
528 visible_bounds: RectF,
529 cx: &mut WindowContext,
530 ) -> Result<()> {
531 let view = self
532 .view
533 .upgrade(cx)
534 .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
535
536 view.update(cx, |view, cx| {
537 self.element.paint(scene, origin, visible_bounds, view, cx);
538 Ok(())
539 })
540 }
541
542 fn rect_for_text_range(
543 &self,
544 range_utf16: Range<usize>,
545 cx: &WindowContext,
546 ) -> Result<Option<RectF>> {
547 let view = self.view.upgrade(cx).ok_or_else(|| {
548 anyhow!("rect_for_text_range called on a root element for a dropped view")
549 })?;
550 let view = view.read(cx);
551 let view_context = ViewContext::immutable(cx, self.view.id());
552 Ok(self
553 .element
554 .rect_for_text_range(range_utf16, view, &view_context))
555 }
556
557 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
558 let view = self
559 .view
560 .upgrade(cx)
561 .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
562 let view = view.read(cx);
563 let view_context = ViewContext::immutable(cx, self.view.id());
564 Ok(self.element.debug(view, &view_context))
565 }
566
567 fn name(&self) -> Option<&str> {
568 self.element.name()
569 }
570}
571
572impl<V: View, R: View> Drawable<V> for RootElement<R> {
573 type LayoutState = ();
574 type PaintState = ();
575
576 fn layout(
577 &mut self,
578 constraint: SizeConstraint,
579 view: &mut V,
580 cx: &mut ViewContext<V>,
581 ) -> (Vector2F, Self::LayoutState) {
582 let size = AnyRootElement::layout(self, constraint, cx)
583 .log_err()
584 .unwrap_or_else(|| Vector2F::zero());
585 (size, ())
586 }
587
588 fn paint(
589 &mut self,
590 scene: &mut SceneBuilder,
591 bounds: RectF,
592 visible_bounds: RectF,
593 layout: &mut Self::LayoutState,
594 view: &mut V,
595 cx: &mut ViewContext<V>,
596 ) -> Self::PaintState {
597 todo!()
598 }
599
600 fn rect_for_text_range(
601 &self,
602 range_utf16: Range<usize>,
603 bounds: RectF,
604 visible_bounds: RectF,
605 layout: &Self::LayoutState,
606 paint: &Self::PaintState,
607 view: &V,
608 cx: &ViewContext<V>,
609 ) -> Option<RectF> {
610 todo!()
611 }
612
613 fn debug(
614 &self,
615 bounds: RectF,
616 layout: &Self::LayoutState,
617 paint: &Self::PaintState,
618 view: &V,
619 cx: &ViewContext<V>,
620 ) -> serde_json::Value {
621 todo!()
622 }
623}
624
625pub trait ParentElement<'a, V: View>: Extend<Element<V>> + Sized {
626 fn add_children(&mut self, children: impl IntoIterator<Item = Element<V>>) {
627 self.extend(children);
628 }
629
630 fn add_child(&mut self, child: Element<V>) {
631 self.add_children(Some(child));
632 }
633
634 fn with_children(mut self, children: impl IntoIterator<Item = Element<V>>) -> Self {
635 self.add_children(children);
636 self
637 }
638
639 fn with_child(self, child: Element<V>) -> Self {
640 self.with_children(Some(child))
641 }
642}
643
644impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<Element<V>> {}
645
646pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
647 if max_size.x().is_infinite() && max_size.y().is_infinite() {
648 size
649 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
650 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
651 } else {
652 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
653 }
654}