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