1use super::layout_highlighted_chunks;
2use crate::{
3 color::Color,
4 fonts::HighlightStyle,
5 geometry::{
6 rect::RectF,
7 vector::{vec2f, Vector2F},
8 },
9 json::{json, ToJson},
10 scene,
11 serde_json::Value,
12 text_layout::{Line, ShapedBoundary},
13 AnyElement, AppContext, Element, LayoutContext, PaintContext, Quad, SceneBuilder,
14 SizeConstraint, Vector2FExt, View, ViewContext,
15};
16use derive_more::Add;
17use length::{Length, Rems};
18use log::warn;
19use optional_struct::*;
20use std::{
21 any::Any,
22 borrow::Cow,
23 f32,
24 ops::{Add, Range},
25 sync::Arc,
26};
27
28pub struct Node<V: View> {
29 style: NodeStyle,
30 children: Vec<AnyElement<V>>,
31}
32
33pub fn node<V: View>(child: impl Element<V>) -> Node<V> {
34 Node::default().child(child)
35}
36
37pub fn column<V: View>() -> Node<V> {
38 Node::default()
39}
40
41pub fn row<V: View>() -> Node<V> {
42 Node {
43 style: NodeStyle {
44 axis: Axis3d::X,
45 ..Default::default()
46 },
47 children: Default::default(),
48 }
49}
50
51pub fn stack<V: View>() -> Node<V> {
52 Node {
53 style: NodeStyle {
54 axis: Axis3d::Z,
55 ..Default::default()
56 },
57 children: Default::default(),
58 }
59}
60
61impl<V: View> Default for Node<V> {
62 fn default() -> Self {
63 Self {
64 style: Default::default(),
65 children: Default::default(),
66 }
67 }
68}
69
70impl<V: View> Node<V> {
71 pub fn child(mut self, child: impl Element<V>) -> Self {
72 self.children.push(child.into_any());
73 self
74 }
75
76 pub fn children<I, E>(mut self, children: I) -> Self
77 where
78 I: IntoIterator<Item = E>,
79 E: Element<V>,
80 {
81 self.children
82 .extend(children.into_iter().map(|child| child.into_any()));
83 self
84 }
85
86 pub fn width(mut self, width: impl Into<Length>) -> Self {
87 self.style.size.width = width.into();
88 self
89 }
90
91 pub fn height(mut self, height: impl Into<Length>) -> Self {
92 self.style.size.height = height.into();
93 self
94 }
95
96 pub fn fill(mut self, fill: impl Into<Fill>) -> Self {
97 self.style.fill = fill.into();
98 self
99 }
100
101 pub fn text_size(mut self, text_size: Rems) -> Self {
102 self.style.text.size = Some(text_size);
103 self
104 }
105
106 pub fn margins(
107 mut self,
108 top_bottom: impl Into<TopBottom>,
109 left_right: impl Into<LeftRight>,
110 ) -> Self {
111 let top_bottom = top_bottom.into();
112 let left_right = left_right.into();
113 self.style.margins = Edges {
114 top: top_bottom.top,
115 bottom: top_bottom.bottom,
116 left: left_right.left,
117 right: left_right.right,
118 };
119 self
120 }
121
122 pub fn margin_top(mut self, top: Length) -> Self {
123 self.style.margins.top = top;
124 self
125 }
126
127 pub fn margin_bottom(mut self, bottom: Length) -> Self {
128 self.style.margins.bottom = bottom;
129 self
130 }
131
132 pub fn margin_left(mut self, left: impl Into<Length>) -> Self {
133 self.style.margins.left = left.into();
134 self
135 }
136
137 pub fn margin_right(mut self, right: impl Into<Length>) -> Self {
138 self.style.margins.right = right.into();
139 self
140 }
141
142 fn layout_xy(
143 &mut self,
144 axis: Axis2d,
145 max_size: Vector2F,
146 rem_pixels: f32,
147 layout: &mut NodeLayout,
148 view: &mut V,
149 cx: &mut LayoutContext<V>,
150 ) -> Vector2F {
151 layout.margins = self.style.margins.fixed_pixels(rem_pixels);
152 layout.padding = self.style.padding.fixed_pixels(rem_pixels);
153
154 let padded_max =
155 max_size - layout.margins.size() - self.style.borders.width - layout.padding.size();
156 let mut remaining_length = padded_max.get(axis);
157
158 // Pass 1: Total up flex units and layout inflexible children.
159 //
160 // Consume the remaining length as we layout inflexible children, so that any
161 // remaining length can be distributed among flexible children in the next pass.
162 let mut remaining_flex: f32 = 0.;
163 let mut cross_axis_max: f32 = 0.;
164 let cross_axis = axis.rotate();
165
166 // Fixed children are unconstrained along the primary axis, and constrained to
167 // the padded max size along the cross axis.
168 let mut child_constraint =
169 SizeConstraint::loose(Vector2F::infinity().set(cross_axis, padded_max.get(cross_axis)));
170
171 for child in &mut self.children {
172 if let Some(child_flex) = child
173 .metadata::<NodeStyle>()
174 .and_then(|style| style.flex(axis))
175 {
176 remaining_flex += child_flex;
177 } else {
178 let child_size = child.layout(child_constraint, view, cx);
179 cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
180 remaining_length -= child_size.get(axis);
181 }
182 }
183
184 // Pass 2: Allocate the remaining space among flexible lengths along the primary axis.
185 if remaining_flex > 0. {
186 // Add flex pixels from margin and padding.
187 *layout.margins.start_mut(axis) += self.style.margins.start(axis).flex_pixels(
188 rem_pixels,
189 &mut remaining_flex,
190 &mut remaining_length,
191 );
192 *layout.padding.start_mut(axis) += self.style.padding.start(axis).flex_pixels(
193 rem_pixels,
194 &mut remaining_flex,
195 &mut remaining_length,
196 );
197
198 // Lay out the flexible children
199 let mut child_max = padded_max;
200 for child in &mut self.children {
201 if let Some(child_flex) = child
202 .metadata::<NodeStyle>()
203 .and_then(|style| style.flex(axis))
204 {
205 child_max.set(axis, child_flex / remaining_flex * remaining_length);
206 let child_size = child.layout(SizeConstraint::loose(child_max), view, cx);
207
208 remaining_flex -= child_flex;
209 remaining_length -= child_size.get(axis);
210 cross_axis_max = child_size.get(cross_axis).max(cross_axis_max);
211 }
212 }
213
214 // Add flex pixels from margin and padding.
215 *layout.margins.end_mut(axis) += self.style.margins.end(axis).flex_pixels(
216 rem_pixels,
217 &mut remaining_flex,
218 &mut remaining_length,
219 );
220 *layout.padding.end_mut(axis) += self.style.padding.end(axis).flex_pixels(
221 rem_pixels,
222 &mut remaining_flex,
223 &mut remaining_length,
224 );
225 }
226
227 let mut size = max_size;
228
229 match self.style.size.get(axis) {
230 Length::Hug => {
231 size.set(axis, max_size.get(axis) - remaining_length);
232 }
233 Length::Fixed(_) => {}
234 Length::Auto { flex, min, max } => todo!(),
235 };
236
237 let width = match self.style.size.width {
238 Length::Hug => match axis {
239 Axis2d::X => max_size.get(axis) - remaining_length,
240 Axis2d::Y => {
241 cross_axis_max
242 + layout.padding.size().get(cross_axis)
243 + self.style.borders.size().get(cross_axis)
244 + layout.margins.size().get(cross_axis)
245 }
246 },
247 Length::Fixed(width) => width.to_pixels(rem_pixels),
248 Length::Auto { flex, min, max } => max_size
249 .x()
250 .clamp(min.to_pixels(rem_pixels), max.to_pixels(rem_pixels)),
251 };
252
253 let height = match self.style.size.height {
254 Length::Hug => match axis {
255 Axis2d::Y => max_size.get(axis) - remaining_length,
256 Axis2d::X => {
257 cross_axis_max
258 + layout.padding.size().get(cross_axis)
259 + self.style.borders.size().get(cross_axis)
260 + layout.margins.size().get(cross_axis)
261 }
262 },
263 Length::Fixed(height) => height.to_pixels(rem_pixels),
264 Length::Auto { flex, min, max } => max_size
265 .y()
266 .clamp(min.to_pixels(rem_pixels), max.to_pixels(rem_pixels)),
267 };
268
269 let length = max_size.get(axis) - remaining_length;
270 match axis {
271 Axis2d::X => vec2f(length, cross_axis_max),
272 Axis2d::Y => vec2f(cross_axis_max, length),
273 }
274 }
275
276 fn paint_children_xy(
277 &mut self,
278 scene: &mut SceneBuilder,
279 axis: Axis2d,
280 bounds: RectF,
281 visible_bounds: RectF,
282 layout: &mut NodeLayout,
283 view: &mut V,
284 cx: &mut ViewContext<V>,
285 ) {
286 let parent_size = bounds.size();
287 let mut child_origin = bounds.origin();
288
289 // Align all children together along the primary axis
290 let mut align_horizontally = false;
291 let mut align_vertically = false;
292 match axis {
293 Axis2d::X => align_horizontally = true,
294 Axis2d::Y => align_vertically = true,
295 }
296 align_child(
297 &mut child_origin,
298 parent_size,
299 layout.content_size,
300 self.style.align.0,
301 align_horizontally,
302 align_vertically,
303 );
304
305 for child in &mut self.children {
306 // Align each child along the cross axis
307 align_horizontally = !align_horizontally;
308 align_vertically = !align_vertically;
309 align_child(
310 &mut child_origin,
311 parent_size,
312 child.size(),
313 self.style.align.0,
314 align_horizontally,
315 align_vertically,
316 );
317
318 child.paint(scene, child_origin, visible_bounds, view, cx);
319
320 // Advance along the primary axis by the size of this child
321 match axis {
322 Axis2d::X => child_origin.set_x(child_origin.x() + child.size().x()),
323 Axis2d::Y => child_origin.set_y(child_origin.y() + child.size().y()),
324 }
325 }
326 }
327
328 // fn layout_stacked_children(
329 // &mut self,
330 // constraint: SizeConstraint,
331 // view: &mut V,
332 // cx: &mut LayoutContext<V>,
333 // ) -> Vector2F {
334 // let mut size = Vector2F::zero();
335
336 // for child in &mut self.children {
337 // let child_size = child.layout(constraint, view, cx);
338 // size.set_x(size.x().max(child_size.x()));
339 // size.set_y(size.y().max(child_size.y()));
340 // }
341
342 // size
343 // }
344
345 // fn inset_size(&self, rem_size: f32) -> Vector2F {
346 // todo!()
347 // // self.padding_size(rem_size) + self.border_size() + self.margin_size(rem_size)
348 // }
349
350 //
351 // fn margin_fixed_size(&self, rem_size: f32) -> Vector2F {
352 // self.style.margins.fixed().to_pixels(rem_size)
353 // }
354
355 // fn padding_size(&self, rem_size: f32) -> Vector2F {
356 // // We need to account for auto padding
357 // todo!()
358 // // vec2f(
359 // // (self.style.padding.left + self.style.padding.right).to_pixels(rem_size),
360 // // (self.style.padding.top + self.style.padding.bottom).to_pixels(rem_size),
361 // // )
362 // }
363
364 // fn border_size(&self) -> Vector2F {
365 // let mut x = 0.0;
366 // if self.style.borders.left {
367 // x += self.style.borders.width;
368 // }
369 // if self.style.borders.right {
370 // x += self.style.borders.width;
371 // }
372
373 // let mut y = 0.0;
374 // if self.style.borders.top {
375 // y += self.style.borders.width;
376 // }
377 // if self.style.borders.bottom {
378 // y += self.style.borders.width;
379 // }
380
381 // vec2f(x, y)
382 // }
383}
384
385impl<V: View> Element<V> for Node<V> {
386 type LayoutState = NodeLayout;
387 type PaintState = ();
388
389 fn layout(
390 &mut self,
391 constraint: SizeConstraint,
392 view: &mut V,
393 cx: &mut LayoutContext<V>,
394 ) -> (Vector2F, Self::LayoutState) {
395 let mut layout = NodeLayout::default();
396
397 let size = if let Some(axis) = self.style.axis.to_2d() {
398 self.layout_xy(axis, constraint.max, cx.rem_pixels(), &mut layout, view, cx)
399 } else {
400 todo!()
401 };
402
403 (size, layout)
404 }
405
406 fn paint(
407 &mut self,
408 scene: &mut SceneBuilder,
409 bounds: RectF,
410 visible_bounds: RectF,
411 layout: &mut NodeLayout,
412 view: &mut V,
413 cx: &mut PaintContext<V>,
414 ) -> Self::PaintState {
415 let rem_pixels = cx.rem_pixels();
416 let margined_bounds = RectF::from_points(
417 bounds.origin() + vec2f(layout.margins.left, layout.margins.top),
418 bounds.lower_right() - vec2f(layout.margins.right, layout.margins.bottom),
419 );
420
421 // Paint drop shadow
422 for shadow in &self.style.shadows {
423 scene.push_shadow(scene::Shadow {
424 bounds: margined_bounds + shadow.offset,
425 corner_radius: self.style.corner_radius,
426 sigma: shadow.blur,
427 color: shadow.color,
428 });
429 }
430
431 // // Paint cursor style
432 // if let Some(hit_bounds) = content_bounds.intersection(visible_bounds) {
433 // if let Some(style) = self.style.cursor {
434 // scene.push_cursor_region(CursorRegion {
435 // bounds: hit_bounds,
436 // style,
437 // });
438 // }
439 // }
440
441 // Render the background and/or the border.
442 let Fill::Color(fill_color) = self.style.fill;
443 let is_fill_visible = !fill_color.is_fully_transparent();
444 if is_fill_visible || self.style.borders.is_visible() {
445 scene.push_quad(Quad {
446 bounds: margined_bounds,
447 background: is_fill_visible.then_some(fill_color),
448 border: scene::Border {
449 width: self.style.borders.width,
450 color: self.style.borders.color,
451 overlay: false,
452 top: self.style.borders.top,
453 right: self.style.borders.right,
454 bottom: self.style.borders.bottom,
455 left: self.style.borders.left,
456 },
457 corner_radius: self.style.corner_radius,
458 });
459 }
460
461 if !self.children.is_empty() {
462 // Account for padding first.
463 let borders = &self.style.borders;
464 let padded_bounds = RectF::from_points(
465 margined_bounds.origin()
466 + vec2f(
467 borders.left_width() + layout.padding.left,
468 borders.top_width() + layout.padding.top,
469 ),
470 margined_bounds.lower_right()
471 - vec2f(
472 layout.padding.right + borders.right_width(),
473 layout.padding.bottom + borders.bottom_width(),
474 ),
475 );
476
477 if let Some(axis) = self.style.axis.to_2d() {
478 self.paint_children_xy(scene, axis, padded_bounds, visible_bounds, layout, view, cx)
479 } else {
480 todo!();
481 }
482 }
483 }
484
485 fn rect_for_text_range(
486 &self,
487 range_utf16: Range<usize>,
488 _: RectF,
489 _: RectF,
490 _: &Self::LayoutState,
491 _: &Self::PaintState,
492 view: &V,
493 cx: &ViewContext<V>,
494 ) -> Option<RectF> {
495 self.children
496 .iter()
497 .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
498 }
499
500 fn debug(
501 &self,
502 bounds: RectF,
503 _: &Self::LayoutState,
504 _: &Self::PaintState,
505 view: &V,
506 cx: &ViewContext<V>,
507 ) -> Value {
508 json!({
509 "type": "Node",
510 "bounds": bounds.to_json(),
511 // TODO!
512 // "children": self.content.iter().map(|child| child.debug(view, cx)).collect::<Vec<Value>>()
513 })
514 }
515
516 fn metadata(&self) -> Option<&dyn Any> {
517 Some(&self.style)
518 }
519}
520
521pub struct TopBottom {
522 top: Length,
523 bottom: Length,
524}
525
526impl<T: Into<Length>> From<(T, T)> for TopBottom {
527 fn from((top, bottom): (T, T)) -> Self {
528 Self {
529 top: top.into(),
530 bottom: bottom.into(),
531 }
532 }
533}
534
535impl<T: Copy + Into<Length>> From<T> for TopBottom {
536 fn from(both: T) -> Self {
537 Self {
538 top: both.into(),
539 bottom: both.into(),
540 }
541 }
542}
543
544pub struct LeftRight {
545 left: Length,
546 right: Length,
547}
548
549impl From<(Length, Length)> for LeftRight {
550 fn from((left, right): (Length, Length)) -> Self {
551 Self { left, right }
552 }
553}
554
555impl From<Length> for LeftRight {
556 fn from(both: Length) -> Self {
557 Self {
558 left: both,
559 right: both,
560 }
561 }
562}
563
564fn align_child(
565 child_origin: &mut Vector2F,
566 parent_size: Vector2F,
567 child_size: Vector2F,
568 alignment: Vector2F,
569 horizontal: bool,
570 vertical: bool,
571) {
572 let parent_center = parent_size / 2.;
573 let parent_target = parent_center + parent_center * alignment;
574 let child_center = child_size / 2.;
575 let child_target = child_center + child_center * alignment;
576
577 if horizontal {
578 child_origin.set_x(child_origin.x() + parent_target.x() - child_target.x())
579 }
580 if vertical {
581 child_origin.set_y(child_origin.y() + parent_target.y() - child_target.y());
582 }
583}
584
585struct Interactive<Style> {
586 default: Style,
587 hovered: Style,
588 active: Style,
589 disabled: Style,
590}
591
592#[derive(Clone, Default)]
593pub struct NodeStyle {
594 axis: Axis3d,
595 wrap: bool,
596 align: Align,
597 overflow_x: Overflow,
598 overflow_y: Overflow,
599 gap_x: Gap,
600 gap_y: Gap,
601
602 size: Size<Length>,
603 margins: Edges<Length>,
604 padding: Edges<Length>,
605 text: OptionalTextStyle,
606 opacity: f32,
607 fill: Fill,
608 borders: Borders,
609 corner_radius: f32,
610 shadows: Vec<Shadow>,
611}
612
613impl NodeStyle {
614 fn flex(&self, axis: Axis2d) -> Option<f32> {
615 let mut sum = None;
616 match axis {
617 Axis2d::X => {
618 sum = optional_add(sum, self.margins.left.flex());
619 sum = optional_add(sum, self.padding.left.flex());
620 sum = optional_add(sum, self.size.width.flex());
621 sum = optional_add(sum, self.padding.right.flex());
622 sum = optional_add(sum, self.margins.right.flex());
623 }
624 Axis2d::Y => {
625 sum = optional_add(sum, self.margins.top.flex());
626 sum = optional_add(sum, self.padding.top.flex());
627 sum = optional_add(sum, self.size.height.flex());
628 sum = optional_add(sum, self.padding.bottom.flex());
629 sum = optional_add(sum, self.margins.bottom.flex());
630 }
631 }
632 sum
633 }
634}
635
636#[optional_struct]
637struct TextStyle {
638 size: Rems,
639 font_family: Arc<str>,
640 weight: FontWeight,
641 style: FontStyle,
642}
643
644#[derive(Add, Default, Clone)]
645struct Size<T> {
646 width: T,
647 height: T,
648}
649
650impl<T: Copy> Size<T> {
651 fn get(&self, axis: Axis2d) -> T {
652 match axis {
653 Axis2d::X => self.width,
654 Axis2d::Y => self.height,
655 }
656 }
657}
658
659impl<T: Copy + Add<Output = T>> Size<Option<T>> {
660 fn add_assign_optional(&mut self, rhs: Size<Option<T>>) {
661 self.width = optional_add(self.width, rhs.width);
662 self.height = optional_add(self.height, rhs.height);
663 }
664}
665
666impl Size<Length> {
667 pub fn fixed(&self) -> Size<Rems> {
668 Size {
669 width: self.width.fixed().unwrap_or_default(),
670 height: self.height.fixed().unwrap_or_default(),
671 }
672 }
673
674 pub fn flex(&self) -> Vector2F {
675 vec2f(
676 self.width.flex().unwrap_or(0.),
677 self.height.flex().unwrap_or(0.),
678 )
679 }
680}
681
682impl Size<Rems> {
683 pub fn to_pixels(&self, rem_size: f32) -> Vector2F {
684 vec2f(
685 self.width.to_pixels(rem_size),
686 self.height.to_pixels(rem_size),
687 )
688 }
689}
690
691// Sides?
692#[derive(Clone, Default)]
693struct Edges<T> {
694 top: T,
695 bottom: T,
696 left: T,
697 right: T,
698}
699
700impl<T> Edges<T> {
701 fn start(&self, axis: Axis2d) -> &T {
702 match axis {
703 Axis2d::X => &self.left,
704 Axis2d::Y => &self.top,
705 }
706 }
707
708 fn start_mut(&mut self, axis: Axis2d) -> &mut T {
709 match axis {
710 Axis2d::X => &mut self.left,
711 Axis2d::Y => &mut self.top,
712 }
713 }
714
715 fn end(&self, axis: Axis2d) -> &T {
716 match axis {
717 Axis2d::X => &self.right,
718 Axis2d::Y => &self.bottom,
719 }
720 }
721
722 fn end_mut(&mut self, axis: Axis2d) -> &mut T {
723 match axis {
724 Axis2d::X => &mut self.right,
725 Axis2d::Y => &mut self.bottom,
726 }
727 }
728}
729
730impl Edges<f32> {
731 fn size(&self) -> Vector2F {
732 vec2f(self.left + self.right, self.top + self.bottom)
733 }
734}
735
736impl Edges<Length> {
737 fn fixed_pixels(&self, rem_pixels: f32) -> Edges<f32> {
738 Edges {
739 top: self.top.fixed_pixels(rem_pixels),
740 bottom: self.bottom.fixed_pixels(rem_pixels),
741 left: self.left.fixed_pixels(rem_pixels),
742 right: self.right.fixed_pixels(rem_pixels),
743 }
744 }
745
746 fn flex_pixels(
747 &self,
748 rem_pixels: f32,
749 remaining_flex: &mut f32,
750 remaining_length: &mut f32,
751 ) -> Edges<f32> {
752 Edges {
753 top: self
754 .top
755 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
756 bottom: self
757 .bottom
758 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
759 left: self
760 .left
761 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
762 right: self
763 .right
764 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
765 }
766 }
767
768 // pub fn fixed(&self) -> Size<Rems> {
769 // let mut size = Size::default();
770 // size.width += self.left.fixed().unwrap_or_default();
771 // size.width += self.right.fixed().unwrap_or_default();
772 // size
773 // }
774
775 pub fn flex(&self) -> Vector2F {
776 vec2f(
777 self.left.flex().unwrap_or(0.) + self.right.flex().unwrap_or(0.),
778 self.top.flex().unwrap_or(0.) + self.bottom.flex().unwrap_or(0.),
779 )
780 }
781}
782
783impl Edges<Rems> {
784 pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
785 Edges {
786 top: self.top.to_pixels(rem_size),
787 bottom: self.bottom.to_pixels(rem_size),
788 left: self.left.to_pixels(rem_size),
789 right: self.right.to_pixels(rem_size),
790 }
791 }
792}
793
794#[derive(Clone, Default)]
795struct CornerRadii {
796 top_left: f32,
797 top_right: f32,
798 bottom_right: f32,
799 bottom_left: f32,
800}
801
802#[derive(Clone)]
803pub enum Fill {
804 Color(Color),
805}
806
807impl From<Color> for Fill {
808 fn from(value: Color) -> Self {
809 Fill::Color(value)
810 }
811}
812
813impl Default for Fill {
814 fn default() -> Self {
815 Fill::Color(Color::default())
816 }
817}
818
819#[derive(Clone, Default)]
820struct Borders {
821 color: Color,
822 width: f32,
823 top: bool,
824 bottom: bool,
825 left: bool,
826 right: bool,
827}
828
829impl Borders {
830 fn is_visible(&self) -> bool {
831 self.width > 0.
832 && !self.color.is_fully_transparent()
833 && (self.top || self.bottom || self.left || self.right)
834 }
835
836 fn top_width(&self) -> f32 {
837 if self.top {
838 self.width
839 } else {
840 0.
841 }
842 }
843
844 fn bottom_width(&self) -> f32 {
845 if self.bottom {
846 self.width
847 } else {
848 0.
849 }
850 }
851
852 fn left_width(&self) -> f32 {
853 if self.left {
854 self.width
855 } else {
856 0.
857 }
858 }
859
860 fn right_width(&self) -> f32 {
861 if self.right {
862 self.width
863 } else {
864 0.
865 }
866 }
867
868 fn size(&self) -> Vector2F {
869 let width =
870 if self.left { self.width } else { 0. } + if self.right { self.width } else { 0. };
871 let height =
872 if self.top { self.width } else { 0. } + if self.bottom { self.width } else { 0. };
873
874 vec2f(width, height)
875 }
876}
877
878pub mod length {
879 use derive_more::{Add, AddAssign, Into};
880
881 #[derive(Add, AddAssign, Into, Clone, Copy, Default, Debug, PartialEq)]
882 pub struct Rems(f32);
883
884 pub fn rems(rems: f32) -> Rems {
885 Rems(rems)
886 }
887
888 impl Rems {
889 pub fn to_pixels(&self, rem_pixels: f32) -> f32 {
890 self.0 * rem_pixels
891 }
892 }
893
894 #[derive(Clone, Copy, Default, Debug)]
895 pub enum Length {
896 #[default]
897 Hug,
898 Fixed(Rems),
899 Auto {
900 flex: f32,
901 min: Rems,
902 max: Rems,
903 },
904 }
905
906 impl From<Rems> for Length {
907 fn from(value: Rems) -> Self {
908 Length::Fixed(value)
909 }
910 }
911
912 pub fn auto() -> Length {
913 flex(1.)
914 }
915
916 pub fn flex(flex: f32) -> Length {
917 Length::Auto {
918 flex,
919 min: Default::default(),
920 max: rems(f32::INFINITY),
921 }
922 }
923
924 pub fn constrained(flex: f32, min: Option<Rems>, max: Option<Rems>) -> Length {
925 Length::Auto {
926 flex,
927 min: min.unwrap_or(Default::default()),
928 max: max.unwrap_or(rems(f32::INFINITY)),
929 }
930 }
931
932 impl Length {
933 pub fn flex_pixels(
934 &self,
935 rem_pixels: f32,
936 remaining_flex: &mut f32,
937 remaining_length: &mut f32,
938 ) -> f32 {
939 match self {
940 Length::Auto { flex, min, max } => {
941 let flex_length = *remaining_length / *remaining_flex;
942 let length = (flex * flex_length)
943 .clamp(min.to_pixels(rem_pixels), max.to_pixels(rem_pixels));
944 *remaining_flex -= flex;
945 *remaining_length -= length;
946 length
947 }
948 _ => 0.,
949 }
950 }
951
952 pub fn fixed_pixels(&self, rem: f32) -> f32 {
953 match self {
954 Length::Fixed(rems) => rems.to_pixels(rem),
955 _ => 0.,
956 }
957 }
958
959 pub fn flex(&self) -> Option<f32> {
960 match self {
961 Length::Auto { flex, .. } => Some(*flex),
962 _ => None,
963 }
964 }
965
966 pub fn fixed(&self) -> Option<Rems> {
967 match self {
968 Length::Fixed(rems) => Some(*rems),
969 _ => None,
970 }
971 }
972 }
973}
974
975#[derive(Clone)]
976struct Align(Vector2F);
977
978impl Default for Align {
979 fn default() -> Self {
980 Self(vec2f(-1., -1.))
981 }
982}
983
984#[derive(Clone, Copy, Default)]
985enum Axis3d {
986 X,
987 #[default]
988 Y,
989 Z,
990}
991
992impl Axis3d {
993 fn to_2d(self) -> Option<Axis2d> {
994 match self {
995 Axis3d::X => Some(Axis2d::X),
996 Axis3d::Y => Some(Axis2d::Y),
997 Axis3d::Z => None,
998 }
999 }
1000}
1001
1002#[derive(Clone, Copy, Default)]
1003pub enum Axis2d {
1004 X,
1005 #[default]
1006 Y,
1007}
1008
1009impl Axis2d {
1010 fn rotate(self) -> Self {
1011 match self {
1012 Axis2d::X => Axis2d::Y,
1013 Axis2d::Y => Axis2d::X,
1014 }
1015 }
1016}
1017
1018#[derive(Clone, Copy, Default)]
1019enum Overflow {
1020 #[default]
1021 Visible,
1022 Hidden,
1023 Auto,
1024}
1025
1026#[derive(Clone, Copy)]
1027enum Gap {
1028 Fixed(f32),
1029 Around,
1030 Between,
1031 Even,
1032}
1033
1034impl Default for Gap {
1035 fn default() -> Self {
1036 Gap::Fixed(0.)
1037 }
1038}
1039
1040#[derive(Clone, Copy, Default)]
1041struct Shadow {
1042 offset: Vector2F,
1043 blur: f32,
1044 color: Color,
1045}
1046
1047#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
1048enum FontStyle {
1049 #[default]
1050 Normal,
1051 Italic,
1052 Oblique,
1053}
1054
1055#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
1056enum FontWeight {
1057 Thin,
1058 ExtraLight,
1059 Light,
1060 #[default]
1061 Normal,
1062 Medium,
1063 Semibold,
1064 Bold,
1065 ExtraBold,
1066 Black,
1067}
1068
1069#[derive(Default)]
1070pub struct Text {
1071 text: Cow<'static, str>,
1072 highlights: Option<Box<[(Range<usize>, HighlightStyle)]>>,
1073 custom_runs: Option<(
1074 Box<[Range<usize>]>,
1075 Box<dyn FnMut(usize, RectF, &mut SceneBuilder, &mut AppContext)>,
1076 )>,
1077}
1078
1079pub fn text<V: View>(text: impl Into<Cow<'static, str>>) -> Node<V> {
1080 row().child(Text {
1081 text: text.into(),
1082 ..Default::default()
1083 })
1084}
1085
1086#[derive(Default)]
1087pub struct NodeLayout {
1088 content_size: Vector2F,
1089 margins: Edges<f32>,
1090 padding: Edges<f32>,
1091}
1092
1093impl<V: View> Element<V> for Text {
1094 type LayoutState = TextLayout;
1095 type PaintState = ();
1096
1097 fn layout(
1098 &mut self,
1099 constraint: SizeConstraint,
1100 _: &mut V,
1101 cx: &mut LayoutContext<V>,
1102 ) -> (Vector2F, Self::LayoutState) {
1103 // Convert the string and highlight ranges into an iterator of highlighted chunks.
1104 let mut offset = 0;
1105 let mut highlight_ranges = self
1106 .highlights
1107 .as_ref()
1108 .map_or(Default::default(), AsRef::as_ref)
1109 .iter()
1110 .peekable();
1111 let chunks = std::iter::from_fn(|| {
1112 let result;
1113 if let Some((range, highlight_style)) = highlight_ranges.peek() {
1114 if offset < range.start {
1115 result = Some((&self.text[offset..range.start], None));
1116 offset = range.start;
1117 } else if range.end <= self.text.len() {
1118 result = Some((&self.text[range.clone()], Some(*highlight_style)));
1119 highlight_ranges.next();
1120 offset = range.end;
1121 } else {
1122 warn!(
1123 "Highlight out of text range. Text len: {}, Highlight range: {}..{}",
1124 self.text.len(),
1125 range.start,
1126 range.end
1127 );
1128 result = None;
1129 }
1130 } else if offset < self.text.len() {
1131 result = Some((&self.text[offset..], None));
1132 offset = self.text.len();
1133 } else {
1134 result = None;
1135 }
1136 result
1137 });
1138
1139 let style = cx.text_style();
1140
1141 // Perform shaping on these highlighted chunks
1142 let shaped_lines = layout_highlighted_chunks(
1143 chunks,
1144 &style,
1145 cx.text_layout_cache(),
1146 &cx.font_cache,
1147 usize::MAX,
1148 self.text.matches('\n').count() + 1,
1149 );
1150
1151 // If line wrapping is enabled, wrap each of the shaped lines.
1152 let font_id = style.font_id;
1153 let mut line_count = 0;
1154 let mut max_line_width = 0_f32;
1155 let mut wrap_boundaries = Vec::new();
1156 let mut wrapper = cx.font_cache.line_wrapper(font_id, style.font_size);
1157 for (line, shaped_line) in self.text.split('\n').zip(&shaped_lines) {
1158 if style.soft_wrap {
1159 let boundaries = wrapper
1160 .wrap_shaped_line(line, shaped_line, constraint.max.x())
1161 .collect::<Vec<_>>();
1162 line_count += boundaries.len() + 1;
1163 wrap_boundaries.push(boundaries);
1164 } else {
1165 line_count += 1;
1166 }
1167 max_line_width = max_line_width.max(shaped_line.width());
1168 }
1169
1170 let line_height = cx.font_cache.line_height(style.font_size);
1171 let size = vec2f(
1172 max_line_width
1173 .ceil()
1174 .max(constraint.min.x())
1175 .min(constraint.max.x()),
1176 (line_height * line_count as f32).ceil(),
1177 );
1178 (
1179 size,
1180 TextLayout {
1181 shaped_lines,
1182 wrap_boundaries,
1183 line_height,
1184 },
1185 )
1186 }
1187
1188 fn paint(
1189 &mut self,
1190 scene: &mut SceneBuilder,
1191 bounds: RectF,
1192 visible_bounds: RectF,
1193 layout: &mut Self::LayoutState,
1194 _: &mut V,
1195 cx: &mut PaintContext<V>,
1196 ) -> Self::PaintState {
1197 let mut origin = bounds.origin();
1198 let empty = Vec::new();
1199 let mut callback = |_, _, _: &mut SceneBuilder, _: &mut AppContext| {};
1200
1201 let mouse_runs;
1202 let custom_run_callback;
1203 if let Some((runs, build_region)) = &mut self.custom_runs {
1204 mouse_runs = runs.iter();
1205 custom_run_callback = build_region.as_mut();
1206 } else {
1207 mouse_runs = [].iter();
1208 custom_run_callback = &mut callback;
1209 }
1210 let mut custom_runs = mouse_runs.enumerate().peekable();
1211
1212 let mut offset = 0;
1213 for (ix, line) in layout.shaped_lines.iter().enumerate() {
1214 let wrap_boundaries = layout.wrap_boundaries.get(ix).unwrap_or(&empty);
1215 let boundaries = RectF::new(
1216 origin,
1217 vec2f(
1218 bounds.width(),
1219 (wrap_boundaries.len() + 1) as f32 * layout.line_height,
1220 ),
1221 );
1222
1223 let style = cx.text_style();
1224 if boundaries.intersects(visible_bounds) {
1225 if style.soft_wrap {
1226 line.paint_wrapped(
1227 scene,
1228 origin,
1229 visible_bounds,
1230 layout.line_height,
1231 wrap_boundaries,
1232 cx,
1233 );
1234 } else {
1235 line.paint(scene, origin, visible_bounds, layout.line_height, cx);
1236 }
1237 }
1238
1239 // Paint any custom runs that intersect this line.
1240 let end_offset = offset + line.len();
1241 if let Some((custom_run_ix, custom_run_range)) = custom_runs.peek().cloned() {
1242 if custom_run_range.start < end_offset {
1243 let mut current_custom_run = None;
1244 if custom_run_range.start <= offset {
1245 current_custom_run = Some((custom_run_ix, custom_run_range.end, origin));
1246 }
1247
1248 let mut glyph_origin = origin;
1249 let mut prev_position = 0.;
1250 let mut wrap_boundaries = wrap_boundaries.iter().copied().peekable();
1251 for (run_ix, glyph_ix, glyph) in
1252 line.runs().iter().enumerate().flat_map(|(run_ix, run)| {
1253 run.glyphs()
1254 .iter()
1255 .enumerate()
1256 .map(move |(ix, glyph)| (run_ix, ix, glyph))
1257 })
1258 {
1259 glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
1260 prev_position = glyph.position.x();
1261
1262 // If we've reached a soft wrap position, move down one line. If there
1263 // is a custom run in-progress, paint it.
1264 if wrap_boundaries
1265 .peek()
1266 .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
1267 {
1268 if let Some((run_ix, _, run_origin)) = &mut current_custom_run {
1269 let bounds = RectF::from_points(
1270 *run_origin,
1271 glyph_origin + vec2f(0., layout.line_height),
1272 );
1273 custom_run_callback(*run_ix, bounds, scene, cx);
1274 *run_origin =
1275 vec2f(origin.x(), glyph_origin.y() + layout.line_height);
1276 }
1277 wrap_boundaries.next();
1278 glyph_origin = vec2f(origin.x(), glyph_origin.y() + layout.line_height);
1279 }
1280
1281 // If we've reached the end of the current custom run, paint it.
1282 if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
1283 if offset + glyph.index == run_end_offset {
1284 current_custom_run.take();
1285 let bounds = RectF::from_points(
1286 run_origin,
1287 glyph_origin + vec2f(0., layout.line_height),
1288 );
1289 custom_run_callback(run_ix, bounds, scene, cx);
1290 custom_runs.next();
1291 }
1292
1293 if let Some((_, run_range)) = custom_runs.peek() {
1294 if run_range.start >= end_offset {
1295 break;
1296 }
1297 if run_range.start == offset + glyph.index {
1298 current_custom_run =
1299 Some((run_ix, run_range.end, glyph_origin));
1300 }
1301 }
1302 }
1303
1304 // If we've reached the start of a new custom run, start tracking it.
1305 if let Some((run_ix, run_range)) = custom_runs.peek() {
1306 if offset + glyph.index == run_range.start {
1307 current_custom_run = Some((*run_ix, run_range.end, glyph_origin));
1308 }
1309 }
1310 }
1311
1312 // If a custom run extends beyond the end of the line, paint it.
1313 if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
1314 let line_end = glyph_origin + vec2f(line.width() - prev_position, 0.);
1315 let bounds = RectF::from_points(
1316 run_origin,
1317 line_end + vec2f(0., layout.line_height),
1318 );
1319 custom_run_callback(run_ix, bounds, scene, cx);
1320 if end_offset == run_end_offset {
1321 custom_runs.next();
1322 }
1323 }
1324 }
1325 }
1326
1327 offset = end_offset + 1;
1328 origin.set_y(boundaries.max_y());
1329 }
1330 }
1331
1332 fn rect_for_text_range(
1333 &self,
1334 _: Range<usize>,
1335 _: RectF,
1336 _: RectF,
1337 _: &Self::LayoutState,
1338 _: &Self::PaintState,
1339 _: &V,
1340 _: &ViewContext<V>,
1341 ) -> Option<RectF> {
1342 None
1343 }
1344
1345 fn debug(
1346 &self,
1347 bounds: RectF,
1348 _: &Self::LayoutState,
1349 _: &Self::PaintState,
1350 _: &V,
1351 _: &ViewContext<V>,
1352 ) -> Value {
1353 json!({
1354 "type": "Text",
1355 "bounds": bounds.to_json(),
1356 "text": &self.text,
1357 })
1358 }
1359}
1360
1361pub struct TextLayout {
1362 shaped_lines: Vec<Line>,
1363 wrap_boundaries: Vec<Vec<ShapedBoundary>>,
1364 line_height: f32,
1365}
1366
1367fn optional_add<T>(a: Option<T>, b: Option<T>) -> Option<T::Output>
1368where
1369 T: Add<Output = T>,
1370{
1371 match (a, b) {
1372 (Some(a), Some(b)) => Some(a + b),
1373 (Some(a), None) => Some(a),
1374 (None, Some(b)) => Some(b),
1375 (None, None) => None,
1376 }
1377}