1mod align;
2mod canvas;
3mod constrained_box;
4mod container;
5mod empty;
6mod expanded;
7mod flex;
8mod hook;
9mod image;
10mod keystroke_label;
11mod label;
12mod list;
13mod mouse_event_handler;
14mod overlay;
15mod resizable;
16mod stack;
17mod svg;
18mod text;
19mod tooltip;
20mod uniform_list;
21
22use self::expanded::Expanded;
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::presenter::ChildView;
29use crate::{
30 geometry::{
31 rect::RectF,
32 vector::{vec2f, Vector2F},
33 },
34 json,
35 presenter::MeasurementContext,
36 Action, DebugContext, Event, EventContext, LayoutContext, PaintContext, RenderContext,
37 SizeConstraint, View,
38};
39use core::panic;
40use json::ToJson;
41use std::{
42 any::Any,
43 borrow::Cow,
44 cell::RefCell,
45 mem,
46 ops::{Deref, DerefMut, Range},
47 rc::Rc,
48};
49
50trait AnyElement {
51 fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F;
52 fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext);
53 fn dispatch_event(&mut self, event: &Event, cx: &mut EventContext) -> bool;
54 fn rect_for_text_range(
55 &self,
56 range_utf16: Range<usize>,
57 cx: &MeasurementContext,
58 ) -> Option<RectF>;
59 fn debug(&self, cx: &DebugContext) -> serde_json::Value;
60
61 fn size(&self) -> Vector2F;
62 fn metadata(&self) -> Option<&dyn Any>;
63}
64
65pub trait Element {
66 type LayoutState;
67 type PaintState;
68
69 fn layout(
70 &mut self,
71 constraint: SizeConstraint,
72 cx: &mut LayoutContext,
73 ) -> (Vector2F, Self::LayoutState);
74
75 fn paint(
76 &mut self,
77 bounds: RectF,
78 visible_bounds: RectF,
79 layout: &mut Self::LayoutState,
80 cx: &mut PaintContext,
81 ) -> Self::PaintState;
82
83 fn dispatch_event(
84 &mut self,
85 event: &Event,
86 bounds: RectF,
87 visible_bounds: RectF,
88 layout: &mut Self::LayoutState,
89 paint: &mut Self::PaintState,
90 cx: &mut EventContext,
91 ) -> bool;
92
93 fn rect_for_text_range(
94 &self,
95 range_utf16: Range<usize>,
96 bounds: RectF,
97 visible_bounds: RectF,
98 layout: &Self::LayoutState,
99 paint: &Self::PaintState,
100 cx: &MeasurementContext,
101 ) -> Option<RectF>;
102
103 fn metadata(&self) -> Option<&dyn Any> {
104 None
105 }
106
107 fn debug(
108 &self,
109 bounds: RectF,
110 layout: &Self::LayoutState,
111 paint: &Self::PaintState,
112 cx: &DebugContext,
113 ) -> serde_json::Value;
114
115 fn boxed(self) -> ElementBox
116 where
117 Self: 'static + Sized,
118 {
119 ElementBox(ElementRc {
120 name: None,
121 element: Rc::new(RefCell::new(Lifecycle::Init { element: self })),
122 })
123 }
124
125 fn named(self, name: impl Into<Cow<'static, str>>) -> ElementBox
126 where
127 Self: 'static + Sized,
128 {
129 ElementBox(ElementRc {
130 name: Some(name.into()),
131 element: Rc::new(RefCell::new(Lifecycle::Init { element: self })),
132 })
133 }
134
135 fn constrained(self) -> ConstrainedBox
136 where
137 Self: 'static + Sized,
138 {
139 ConstrainedBox::new(self.boxed())
140 }
141
142 fn aligned(self) -> Align
143 where
144 Self: 'static + Sized,
145 {
146 Align::new(self.boxed())
147 }
148
149 fn contained(self) -> Container
150 where
151 Self: 'static + Sized,
152 {
153 Container::new(self.boxed())
154 }
155
156 fn expanded(self) -> Expanded
157 where
158 Self: 'static + Sized,
159 {
160 Expanded::new(self.boxed())
161 }
162
163 fn flex(self, flex: f32, expanded: bool) -> FlexItem
164 where
165 Self: 'static + Sized,
166 {
167 FlexItem::new(self.boxed()).flex(flex, expanded)
168 }
169
170 fn flex_float(self) -> FlexItem
171 where
172 Self: 'static + Sized,
173 {
174 FlexItem::new(self.boxed()).float()
175 }
176
177 fn with_tooltip<Tag: 'static, T: View>(
178 self,
179 id: usize,
180 text: String,
181 action: Option<Box<dyn Action>>,
182 style: TooltipStyle,
183 cx: &mut RenderContext<T>,
184 ) -> Tooltip
185 where
186 Self: 'static + Sized,
187 {
188 Tooltip::new::<Tag, T>(id, text, action, style, self.boxed(), cx)
189 }
190
191 fn with_resize_handle<Tag: 'static, T: View>(
192 self,
193 element_id: usize,
194 side: Side,
195 handle_size: f32,
196 initial_size: f32,
197 cx: &mut RenderContext<T>,
198 ) -> Resizable
199 where
200 Self: 'static + Sized,
201 {
202 Resizable::new::<Tag, T>(
203 self.boxed(),
204 element_id,
205 side,
206 handle_size,
207 initial_size,
208 cx,
209 )
210 }
211}
212
213pub enum Lifecycle<T: Element> {
214 Empty,
215 Init {
216 element: T,
217 },
218 PostLayout {
219 element: T,
220 constraint: SizeConstraint,
221 size: Vector2F,
222 layout: T::LayoutState,
223 },
224 PostPaint {
225 element: T,
226 constraint: SizeConstraint,
227 bounds: RectF,
228 visible_bounds: RectF,
229 layout: T::LayoutState,
230 paint: T::PaintState,
231 },
232}
233pub struct ElementBox(ElementRc);
234
235#[derive(Clone)]
236pub struct ElementRc {
237 name: Option<Cow<'static, str>>,
238 element: Rc<RefCell<dyn AnyElement>>,
239}
240
241impl<T: Element> AnyElement for Lifecycle<T> {
242 fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F {
243 let result;
244 *self = match mem::take(self) {
245 Lifecycle::Empty => unreachable!(),
246 Lifecycle::Init { mut element }
247 | Lifecycle::PostLayout { mut element, .. }
248 | Lifecycle::PostPaint { mut element, .. } => {
249 let (size, layout) = element.layout(constraint, cx);
250 debug_assert!(size.x().is_finite());
251 debug_assert!(size.y().is_finite());
252
253 result = size;
254 Lifecycle::PostLayout {
255 element,
256 constraint,
257 size,
258 layout,
259 }
260 }
261 };
262 result
263 }
264
265 fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
266 *self = match mem::take(self) {
267 Lifecycle::PostLayout {
268 mut element,
269 constraint,
270 size,
271 mut layout,
272 } => {
273 let bounds = RectF::new(origin, size);
274 let visible_bounds = visible_bounds
275 .intersection(bounds)
276 .unwrap_or_else(|| RectF::new(bounds.origin(), Vector2F::default()));
277 let paint = element.paint(bounds, visible_bounds, &mut layout, cx);
278 Lifecycle::PostPaint {
279 element,
280 constraint,
281 bounds,
282 visible_bounds,
283 layout,
284 paint,
285 }
286 }
287 Lifecycle::PostPaint {
288 mut element,
289 constraint,
290 bounds,
291 mut layout,
292 ..
293 } => {
294 let bounds = RectF::new(origin, bounds.size());
295 let visible_bounds = visible_bounds
296 .intersection(bounds)
297 .unwrap_or_else(|| RectF::new(bounds.origin(), Vector2F::default()));
298 let paint = element.paint(bounds, visible_bounds, &mut layout, cx);
299 Lifecycle::PostPaint {
300 element,
301 constraint,
302 bounds,
303 visible_bounds,
304 layout,
305 paint,
306 }
307 }
308 _ => panic!("invalid element lifecycle state"),
309 }
310 }
311
312 fn dispatch_event(&mut self, event: &Event, cx: &mut EventContext) -> bool {
313 if let Lifecycle::PostPaint {
314 element,
315 bounds,
316 visible_bounds,
317 layout,
318 paint,
319 ..
320 } = self
321 {
322 element.dispatch_event(event, *bounds, *visible_bounds, layout, paint, cx)
323 } else {
324 panic!("invalid element lifecycle state");
325 }
326 }
327
328 fn rect_for_text_range(
329 &self,
330 range_utf16: Range<usize>,
331 cx: &MeasurementContext,
332 ) -> Option<RectF> {
333 if let Lifecycle::PostPaint {
334 element,
335 bounds,
336 visible_bounds,
337 layout,
338 paint,
339 ..
340 } = self
341 {
342 element.rect_for_text_range(range_utf16, *bounds, *visible_bounds, layout, paint, cx)
343 } else {
344 None
345 }
346 }
347
348 fn size(&self) -> Vector2F {
349 match self {
350 Lifecycle::Empty | Lifecycle::Init { .. } => panic!("invalid element lifecycle state"),
351 Lifecycle::PostLayout { size, .. } => *size,
352 Lifecycle::PostPaint { bounds, .. } => bounds.size(),
353 }
354 }
355
356 fn metadata(&self) -> Option<&dyn Any> {
357 match self {
358 Lifecycle::Empty => unreachable!(),
359 Lifecycle::Init { element }
360 | Lifecycle::PostLayout { element, .. }
361 | Lifecycle::PostPaint { element, .. } => element.metadata(),
362 }
363 }
364
365 fn debug(&self, cx: &DebugContext) -> serde_json::Value {
366 match self {
367 Lifecycle::PostPaint {
368 element,
369 constraint,
370 bounds,
371 visible_bounds,
372 layout,
373 paint,
374 } => {
375 let mut value = element.debug(*bounds, layout, paint, cx);
376 if let json::Value::Object(map) = &mut value {
377 let mut new_map: crate::json::Map<String, serde_json::Value> =
378 Default::default();
379 if let Some(typ) = map.remove("type") {
380 new_map.insert("type".into(), typ);
381 }
382 new_map.insert("constraint".into(), constraint.to_json());
383 new_map.insert("bounds".into(), bounds.to_json());
384 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
385 new_map.append(map);
386 json::Value::Object(new_map)
387 } else {
388 value
389 }
390 }
391 _ => panic!("invalid element lifecycle state"),
392 }
393 }
394}
395
396impl<T: Element> Default for Lifecycle<T> {
397 fn default() -> Self {
398 Self::Empty
399 }
400}
401
402impl ElementBox {
403 pub fn name(&self) -> Option<&str> {
404 self.0.name.as_deref()
405 }
406
407 pub fn metadata<T: 'static>(&self) -> Option<&T> {
408 let element = unsafe { &*self.0.element.as_ptr() };
409 element.metadata().and_then(|m| m.downcast_ref())
410 }
411}
412
413impl From<ElementBox> for ElementRc {
414 fn from(val: ElementBox) -> Self {
415 val.0
416 }
417}
418
419impl Deref for ElementBox {
420 type Target = ElementRc;
421
422 fn deref(&self) -> &Self::Target {
423 &self.0
424 }
425}
426
427impl DerefMut for ElementBox {
428 fn deref_mut(&mut self) -> &mut Self::Target {
429 &mut self.0
430 }
431}
432
433impl ElementRc {
434 pub fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F {
435 self.element.borrow_mut().layout(constraint, cx)
436 }
437
438 pub fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
439 self.element.borrow_mut().paint(origin, visible_bounds, cx);
440 }
441
442 pub fn dispatch_event(&mut self, event: &Event, cx: &mut EventContext) -> bool {
443 self.element.borrow_mut().dispatch_event(event, cx)
444 }
445
446 pub fn rect_for_text_range(
447 &self,
448 range_utf16: Range<usize>,
449 cx: &MeasurementContext,
450 ) -> Option<RectF> {
451 self.element.borrow().rect_for_text_range(range_utf16, cx)
452 }
453
454 pub fn size(&self) -> Vector2F {
455 self.element.borrow().size()
456 }
457
458 pub fn debug(&self, cx: &DebugContext) -> json::Value {
459 let mut value = self.element.borrow().debug(cx);
460
461 if let Some(name) = &self.name {
462 if let json::Value::Object(map) = &mut value {
463 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
464 new_map.insert("name".into(), json::Value::String(name.to_string()));
465 new_map.append(map);
466 return json::Value::Object(new_map);
467 }
468 }
469
470 value
471 }
472
473 pub fn with_metadata<T, F, R>(&self, f: F) -> R
474 where
475 T: 'static,
476 F: FnOnce(Option<&T>) -> R,
477 {
478 let element = self.element.borrow();
479 f(element.metadata().and_then(|m| m.downcast_ref()))
480 }
481}
482
483pub trait ParentElement<'a>: Extend<ElementBox> + Sized {
484 fn add_children(&mut self, children: impl IntoIterator<Item = ElementBox>) {
485 self.extend(children);
486 }
487
488 fn add_child(&mut self, child: ElementBox) {
489 self.add_children(Some(child));
490 }
491
492 fn with_children(mut self, children: impl IntoIterator<Item = ElementBox>) -> Self {
493 self.add_children(children);
494 self
495 }
496
497 fn with_child(self, child: ElementBox) -> Self {
498 self.with_children(Some(child))
499 }
500}
501
502impl<'a, T> ParentElement<'a> for T where T: Extend<ElementBox> {}
503
504fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
505 if max_size.x().is_infinite() && max_size.y().is_infinite() {
506 size
507 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
508 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
509 } else {
510 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
511 }
512}