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