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_2d_children(
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(
399 axis,
400 constraint.max,
401 cx.rem_pixels(),
402 &mut layout,
403 &mut layout.padding,
404 view,
405 cx,
406 )
407 } else {
408 todo!()
409 };
410
411 (size, layout)
412 }
413
414 fn paint(
415 &mut self,
416 scene: &mut SceneBuilder,
417 bounds: RectF,
418 visible_bounds: RectF,
419 layout: &mut NodeLayout,
420 view: &mut V,
421 cx: &mut PaintContext<V>,
422 ) -> Self::PaintState {
423 let rem_pixels = cx.rem_pixels();
424 // let margin: Edges<f32> = todo!(); // &self.style.margin.to_pixels(rem_size);
425 //
426
427 let size = bounds.size();
428
429 let margined_bounds = RectF::from_points(
430 bounds.origin() + vec2f(layout.margins.left, layout.margins.top),
431 bounds.lower_right() - vec2f(layout.margins.right, layout.margins.bottom),
432 );
433
434 // Paint drop shadow
435 for shadow in &self.style.shadows {
436 scene.push_shadow(scene::Shadow {
437 bounds: margin_bounds + shadow.offset,
438 corner_radius: self.style.corner_radius,
439 sigma: shadow.blur,
440 color: shadow.color,
441 });
442 }
443
444 // // Paint cursor style
445 // if let Some(hit_bounds) = content_bounds.intersection(visible_bounds) {
446 // if let Some(style) = self.style.cursor {
447 // scene.push_cursor_region(CursorRegion {
448 // bounds: hit_bounds,
449 // style,
450 // });
451 // }
452 // }
453
454 // Render the background and/or the border (if it not an overlay border).
455 let Fill::Color(fill_color) = self.style.fill;
456 let is_fill_visible = !fill_color.is_fully_transparent();
457 if is_fill_visible || self.style.borders.is_visible() {
458 scene.push_quad(Quad {
459 bounds: margined_bounds,
460 background: is_fill_visible.then_some(fill_color),
461 border: scene::Border {
462 width: self.style.borders.width,
463 color: self.style.borders.color,
464 overlay: false,
465 top: self.style.borders.top,
466 right: self.style.borders.right,
467 bottom: self.style.borders.bottom,
468 left: self.style.borders.left,
469 },
470 corner_radius: self.style.corner_radius,
471 });
472 }
473
474 if !self.children.is_empty() {
475 let padded_bounds = RectF::from_points(
476 margined_bounds.origin() + vec2f(layout.padding.left, layout.padding.top),
477 margined_bounds.lower_right() - vec2f(layout.padding.right, layout.padding.bottom),
478 );
479
480 // Account for padding first.
481 let padding: Edges<f32> = todo!(); // &self.style.padding.to_pixels(rem_size);
482 let padded_bounds = RectF::from_points(
483 margined_bounds.origin() + vec2f(padding.left, padding.top),
484 margined_bounds.lower_right() - vec2f(padding.right, padding.top),
485 );
486
487 match self.style.axis {
488 Axis3d::X => self.paint_2d_children(
489 scene,
490 Axis2d::X,
491 padded_bounds,
492 visible_bounds,
493 layout,
494 view,
495 cx,
496 ),
497 Axis3d::Y => self.paint_2d_children(
498 scene,
499 Axis2d::Y,
500 padded_bounds,
501 visible_bounds,
502 layout,
503 view,
504 cx,
505 ),
506 Axis3d::Z => todo!(),
507 }
508 }
509 }
510
511 fn rect_for_text_range(
512 &self,
513 range_utf16: Range<usize>,
514 _: RectF,
515 _: RectF,
516 _: &Self::LayoutState,
517 _: &Self::PaintState,
518 view: &V,
519 cx: &ViewContext<V>,
520 ) -> Option<RectF> {
521 self.children
522 .iter()
523 .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
524 }
525
526 fn debug(
527 &self,
528 bounds: RectF,
529 _: &Self::LayoutState,
530 _: &Self::PaintState,
531 view: &V,
532 cx: &ViewContext<V>,
533 ) -> Value {
534 json!({
535 "type": "Node",
536 "bounds": bounds.to_json(),
537 // TODO!
538 // "children": self.content.iter().map(|child| child.debug(view, cx)).collect::<Vec<Value>>()
539 })
540 }
541
542 fn metadata(&self) -> Option<&dyn Any> {
543 Some(&self.style)
544 }
545}
546
547pub struct TopBottom {
548 top: Length,
549 bottom: Length,
550}
551
552impl<T: Into<Length>> From<(T, T)> for TopBottom {
553 fn from((top, bottom): (T, T)) -> Self {
554 Self {
555 top: top.into(),
556 bottom: bottom.into(),
557 }
558 }
559}
560
561impl<T: Copy + Into<Length>> From<T> for TopBottom {
562 fn from(both: T) -> Self {
563 Self {
564 top: both.into(),
565 bottom: both.into(),
566 }
567 }
568}
569
570pub struct LeftRight {
571 left: Length,
572 right: Length,
573}
574
575impl From<(Length, Length)> for LeftRight {
576 fn from((left, right): (Length, Length)) -> Self {
577 Self { left, right }
578 }
579}
580
581impl From<Length> for LeftRight {
582 fn from(both: Length) -> Self {
583 Self {
584 left: both,
585 right: both,
586 }
587 }
588}
589
590fn align_child(
591 child_origin: &mut Vector2F,
592 parent_size: Vector2F,
593 child_size: Vector2F,
594 alignment: Vector2F,
595 horizontal: bool,
596 vertical: bool,
597) {
598 let parent_center = parent_size / 2.;
599 let parent_target = parent_center + parent_center * alignment;
600 let child_center = child_size / 2.;
601 let child_target = child_center + child_center * alignment;
602
603 if horizontal {
604 child_origin.set_x(child_origin.x() + parent_target.x() - child_target.x())
605 }
606 if vertical {
607 child_origin.set_y(child_origin.y() + parent_target.y() - child_target.y());
608 }
609}
610
611struct Interactive<Style> {
612 default: Style,
613 hovered: Style,
614 active: Style,
615 disabled: Style,
616}
617
618#[derive(Clone, Default)]
619pub struct NodeStyle {
620 axis: Axis3d,
621 wrap: bool,
622 align: Align,
623 overflow_x: Overflow,
624 overflow_y: Overflow,
625 gap_x: Gap,
626 gap_y: Gap,
627
628 size: Size<Length>,
629 margins: Edges<Length>,
630 padding: Edges<Length>,
631 text: OptionalTextStyle,
632 opacity: f32,
633 fill: Fill,
634 borders: Border,
635 corner_radius: f32,
636 shadows: Vec<Shadow>,
637}
638
639impl NodeStyle {
640 fn flex(&self, axis: Axis2d) -> Option<f32> {
641 let mut sum = None;
642 match axis {
643 Axis2d::X => {
644 sum = optional_add(sum, self.margins.left.flex());
645 sum = optional_add(sum, self.padding.left.flex());
646 sum = optional_add(sum, self.size.width.flex());
647 sum = optional_add(sum, self.padding.right.flex());
648 sum = optional_add(sum, self.margins.right.flex());
649 }
650 Axis2d::Y => {
651 sum = optional_add(sum, self.margins.top.flex());
652 sum = optional_add(sum, self.padding.top.flex());
653 sum = optional_add(sum, self.size.height.flex());
654 sum = optional_add(sum, self.padding.bottom.flex());
655 sum = optional_add(sum, self.margins.bottom.flex());
656 }
657 }
658 sum
659 }
660}
661
662#[optional_struct]
663struct TextStyle {
664 size: Rems,
665 font_family: Arc<str>,
666 weight: FontWeight,
667 style: FontStyle,
668}
669
670#[derive(Add, Default, Clone)]
671struct Size<T> {
672 width: T,
673 height: T,
674}
675
676impl<T> Size<T> {
677 fn get(&self, axis: Axis2d) -> T {
678 match axis {
679 Axis2d::X => self.width,
680 Axis2d::Y => self.height,
681 }
682 }
683}
684
685impl<T: Add<Output = T>> Size<Option<T>> {
686 fn add_assign_optional(&mut self, rhs: Size<Option<T>>) {
687 self.width = optional_add(self.width, rhs.width);
688 self.height = optional_add(self.height, rhs.height);
689 }
690}
691
692impl Size<Length> {
693 pub fn fixed(&self) -> Size<Rems> {
694 Size {
695 width: self.width.fixed().unwrap_or_default(),
696 height: self.height.fixed().unwrap_or_default(),
697 }
698 }
699
700 pub fn flex(&self) -> Vector2F {
701 vec2f(
702 self.width.flex().unwrap_or(0.),
703 self.height.flex().unwrap_or(0.),
704 )
705 }
706}
707
708impl Size<Rems> {
709 pub fn to_pixels(&self, rem_size: f32) -> Vector2F {
710 vec2f(
711 self.width.to_pixels(rem_size),
712 self.height.to_pixels(rem_size),
713 )
714 }
715}
716
717// Sides?
718#[derive(Clone, Default)]
719struct Edges<T> {
720 top: T,
721 bottom: T,
722 left: T,
723 right: T,
724}
725
726impl<T> Edges<T> {
727 fn start(&self, axis: Axis2d) -> &T {
728 match axis {
729 Axis2d::X => &self.left,
730 Axis2d::Y => &self.top,
731 }
732 }
733
734 fn start_mut(&mut self, axis: Axis2d) -> &mut T {
735 match axis {
736 Axis2d::X => &mut self.left,
737 Axis2d::Y => &mut self.top,
738 }
739 }
740
741 fn end(&self, axis: Axis2d) -> &T {
742 match axis {
743 Axis2d::X => &self.right,
744 Axis2d::Y => &self.bottom,
745 }
746 }
747
748 fn end_mut(&mut self, axis: Axis2d) -> &mut T {
749 match axis {
750 Axis2d::X => &mut self.right,
751 Axis2d::Y => &mut self.bottom,
752 }
753 }
754}
755
756impl Edges<f32> {
757 fn size(&self) -> Vector2F {
758 vec2f(self.left + self.right, self.top + self.bottom)
759 }
760}
761
762impl Edges<Length> {
763 fn fixed_pixels(&self, rem_pixels: f32) -> Edges<f32> {
764 Edges {
765 top: self.top.fixed_pixels(rem_pixels),
766 bottom: self.bottom.fixed_pixels(rem_pixels),
767 left: self.left.fixed_pixels(rem_pixels),
768 right: self.right.fixed_pixels(rem_pixels),
769 }
770 }
771
772 fn flex_pixels(
773 &self,
774 rem_pixels: f32,
775 remaining_flex: &mut f32,
776 remaining_length: &mut f32,
777 ) -> Edges<f32> {
778 Edges {
779 top: self
780 .top
781 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
782 bottom: self
783 .bottom
784 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
785 left: self
786 .left
787 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
788 right: self
789 .right
790 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
791 }
792 }
793
794 // pub fn fixed(&self) -> Size<Rems> {
795 // let mut size = Size::default();
796 // size.width += self.left.fixed().unwrap_or_default();
797 // size.width += self.right.fixed().unwrap_or_default();
798 // size
799 // }
800
801 pub fn flex(&self) -> Vector2F {
802 vec2f(
803 self.left.flex().unwrap_or(0.) + self.right.flex().unwrap_or(0.),
804 self.top.flex().unwrap_or(0.) + self.bottom.flex().unwrap_or(0.),
805 )
806 }
807}
808
809impl Edges<Rems> {
810 pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
811 Edges {
812 top: self.top.to_pixels(rem_size),
813 bottom: self.bottom.to_pixels(rem_size),
814 left: self.left.to_pixels(rem_size),
815 right: self.right.to_pixels(rem_size),
816 }
817 }
818}
819
820#[derive(Clone, Default)]
821struct CornerRadii {
822 top_left: f32,
823 top_right: f32,
824 bottom_right: f32,
825 bottom_left: f32,
826}
827
828#[derive(Clone)]
829pub enum Fill {
830 Color(Color),
831}
832
833impl From<Color> for Fill {
834 fn from(value: Color) -> Self {
835 Fill::Color(value)
836 }
837}
838
839impl Default for Fill {
840 fn default() -> Self {
841 Fill::Color(Color::default())
842 }
843}
844
845#[derive(Clone, Default)]
846struct Border {
847 color: Color,
848 width: f32,
849 top: bool,
850 bottom: bool,
851 left: bool,
852 right: bool,
853}
854
855impl Border {
856 fn is_visible(&self) -> bool {
857 self.width > 0.
858 && !self.color.is_fully_transparent()
859 && (self.top || self.bottom || self.left || self.right)
860 }
861
862 fn size(&self) -> Vector2F {
863 let width =
864 if self.left { self.width } else { 0. } + if self.right { self.width } else { 0. };
865 let height =
866 if self.top { self.width } else { 0. } + if self.bottom { self.width } else { 0. };
867
868 vec2f(width, height)
869 }
870}
871
872pub mod length {
873 use derive_more::{Add, AddAssign, Into};
874
875 #[derive(Add, AddAssign, Into, Clone, Copy, Default, Debug, PartialEq)]
876 pub struct Rems(f32);
877
878 pub fn rems(rems: f32) -> Rems {
879 Rems(rems)
880 }
881
882 impl Rems {
883 pub fn to_pixels(&self, rem_pixels: f32) -> f32 {
884 self.0 * rem_pixels
885 }
886 }
887
888 #[derive(Clone, Copy, Default, Debug)]
889 pub enum Length {
890 #[default]
891 Hug,
892 Fixed(Rems),
893 Auto {
894 flex: f32,
895 min: Rems,
896 max: Rems,
897 },
898 }
899
900 impl From<Rems> for Length {
901 fn from(value: Rems) -> Self {
902 Length::Fixed(value)
903 }
904 }
905
906 pub fn auto() -> Length {
907 flex(1.)
908 }
909
910 pub fn flex(flex: f32) -> Length {
911 Length::Auto {
912 flex,
913 min: Default::default(),
914 max: rems(f32::INFINITY),
915 }
916 }
917
918 pub fn constrained(flex: f32, min: Option<Rems>, max: Option<Rems>) -> Length {
919 Length::Auto {
920 flex,
921 min: min.unwrap_or(Default::default()),
922 max: max.unwrap_or(rems(f32::INFINITY)),
923 }
924 }
925
926 impl Length {
927 pub fn flex_pixels(
928 &self,
929 rem_pixels: f32,
930 remaining_flex: &mut f32,
931 remaining_length: &mut f32,
932 ) -> f32 {
933 match self {
934 Length::Auto { flex, min, max } => {
935 let flex_length = *remaining_length / *remaining_flex;
936 let length = (flex * flex_length)
937 .clamp(min.to_pixels(rem_pixels), max.to_pixels(rem_pixels));
938 *remaining_flex -= flex;
939 *remaining_length -= length;
940 length
941 }
942 _ => 0.,
943 }
944 }
945
946 pub fn fixed_pixels(&self, rem: f32) -> f32 {
947 match self {
948 Length::Fixed(rems) => rems.to_pixels(rem),
949 _ => 0.,
950 }
951 }
952
953 pub fn flex(&self) -> Option<f32> {
954 match self {
955 Length::Auto { flex, .. } => Some(*flex),
956 _ => None,
957 }
958 }
959
960 pub fn fixed(&self) -> Option<Rems> {
961 match self {
962 Length::Fixed(rems) => Some(*rems),
963 _ => None,
964 }
965 }
966 }
967}
968
969#[derive(Clone)]
970struct Align(Vector2F);
971
972impl Default for Align {
973 fn default() -> Self {
974 Self(vec2f(-1., -1.))
975 }
976}
977
978#[derive(Clone, Copy, Default)]
979enum Axis3d {
980 X,
981 #[default]
982 Y,
983 Z,
984}
985
986impl Axis3d {
987 fn to_2d(self) -> Option<Axis2d> {
988 match self {
989 Axis3d::X => Some(Axis2d::X),
990 Axis3d::Y => Some(Axis2d::Y),
991 Axis3d::Z => None,
992 }
993 }
994}
995
996#[derive(Clone, Copy, Default)]
997pub enum Axis2d {
998 X,
999 #[default]
1000 Y,
1001}
1002
1003impl Axis2d {
1004 fn rotate(self) -> Self {
1005 match self {
1006 Axis2d::X => Axis2d::Y,
1007 Axis2d::Y => Axis2d::X,
1008 }
1009 }
1010}
1011
1012#[derive(Clone, Copy, Default)]
1013enum Overflow {
1014 #[default]
1015 Visible,
1016 Hidden,
1017 Auto,
1018}
1019
1020#[derive(Clone, Copy)]
1021enum Gap {
1022 Fixed(f32),
1023 Around,
1024 Between,
1025 Even,
1026}
1027
1028impl Default for Gap {
1029 fn default() -> Self {
1030 Gap::Fixed(0.)
1031 }
1032}
1033
1034#[derive(Clone, Copy, Default)]
1035struct Shadow {
1036 offset: Vector2F,
1037 blur: f32,
1038 color: Color,
1039}
1040
1041#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
1042enum FontStyle {
1043 #[default]
1044 Normal,
1045 Italic,
1046 Oblique,
1047}
1048
1049#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
1050enum FontWeight {
1051 Thin,
1052 ExtraLight,
1053 Light,
1054 #[default]
1055 Normal,
1056 Medium,
1057 Semibold,
1058 Bold,
1059 ExtraBold,
1060 Black,
1061}
1062
1063#[derive(Default)]
1064pub struct Text {
1065 text: Cow<'static, str>,
1066 highlights: Option<Box<[(Range<usize>, HighlightStyle)]>>,
1067 custom_runs: Option<(
1068 Box<[Range<usize>]>,
1069 Box<dyn FnMut(usize, RectF, &mut SceneBuilder, &mut AppContext)>,
1070 )>,
1071}
1072
1073pub fn text<V: View>(text: impl Into<Cow<'static, str>>) -> Node<V> {
1074 row().child(Text {
1075 text: text.into(),
1076 ..Default::default()
1077 })
1078}
1079
1080#[derive(Default)]
1081struct NodeLayout {
1082 content_size: Vector2F,
1083 margins: Edges<f32>,
1084 padding: Edges<f32>,
1085}
1086
1087impl<V: View> Element<V> for Text {
1088 type LayoutState = TextLayout;
1089 type PaintState = ();
1090
1091 fn layout(
1092 &mut self,
1093 constraint: SizeConstraint,
1094 _: &mut V,
1095 cx: &mut LayoutContext<V>,
1096 ) -> (Vector2F, Self::LayoutState) {
1097 // Convert the string and highlight ranges into an iterator of highlighted chunks.
1098 let mut offset = 0;
1099 let mut highlight_ranges = self
1100 .highlights
1101 .as_ref()
1102 .map_or(Default::default(), AsRef::as_ref)
1103 .iter()
1104 .peekable();
1105 let chunks = std::iter::from_fn(|| {
1106 let result;
1107 if let Some((range, highlight_style)) = highlight_ranges.peek() {
1108 if offset < range.start {
1109 result = Some((&self.text[offset..range.start], None));
1110 offset = range.start;
1111 } else if range.end <= self.text.len() {
1112 result = Some((&self.text[range.clone()], Some(*highlight_style)));
1113 highlight_ranges.next();
1114 offset = range.end;
1115 } else {
1116 warn!(
1117 "Highlight out of text range. Text len: {}, Highlight range: {}..{}",
1118 self.text.len(),
1119 range.start,
1120 range.end
1121 );
1122 result = None;
1123 }
1124 } else if offset < self.text.len() {
1125 result = Some((&self.text[offset..], None));
1126 offset = self.text.len();
1127 } else {
1128 result = None;
1129 }
1130 result
1131 });
1132
1133 let style = cx.text_style();
1134
1135 // Perform shaping on these highlighted chunks
1136 let shaped_lines = layout_highlighted_chunks(
1137 chunks,
1138 &style,
1139 cx.text_layout_cache(),
1140 &cx.font_cache,
1141 usize::MAX,
1142 self.text.matches('\n').count() + 1,
1143 );
1144
1145 // If line wrapping is enabled, wrap each of the shaped lines.
1146 let font_id = style.font_id;
1147 let mut line_count = 0;
1148 let mut max_line_width = 0_f32;
1149 let mut wrap_boundaries = Vec::new();
1150 let mut wrapper = cx.font_cache.line_wrapper(font_id, style.font_size);
1151 for (line, shaped_line) in self.text.split('\n').zip(&shaped_lines) {
1152 if style.soft_wrap {
1153 let boundaries = wrapper
1154 .wrap_shaped_line(line, shaped_line, constraint.max.x())
1155 .collect::<Vec<_>>();
1156 line_count += boundaries.len() + 1;
1157 wrap_boundaries.push(boundaries);
1158 } else {
1159 line_count += 1;
1160 }
1161 max_line_width = max_line_width.max(shaped_line.width());
1162 }
1163
1164 let line_height = cx.font_cache.line_height(style.font_size);
1165 let size = vec2f(
1166 max_line_width
1167 .ceil()
1168 .max(constraint.min.x())
1169 .min(constraint.max.x()),
1170 (line_height * line_count as f32).ceil(),
1171 );
1172 (
1173 size,
1174 TextLayout {
1175 shaped_lines,
1176 wrap_boundaries,
1177 line_height,
1178 },
1179 )
1180 }
1181
1182 fn paint(
1183 &mut self,
1184 scene: &mut SceneBuilder,
1185 bounds: RectF,
1186 visible_bounds: RectF,
1187 layout: &mut Self::LayoutState,
1188 _: &mut V,
1189 cx: &mut PaintContext<V>,
1190 ) -> Self::PaintState {
1191 let mut origin = bounds.origin();
1192 let empty = Vec::new();
1193 let mut callback = |_, _, _: &mut SceneBuilder, _: &mut AppContext| {};
1194
1195 let mouse_runs;
1196 let custom_run_callback;
1197 if let Some((runs, build_region)) = &mut self.custom_runs {
1198 mouse_runs = runs.iter();
1199 custom_run_callback = build_region.as_mut();
1200 } else {
1201 mouse_runs = [].iter();
1202 custom_run_callback = &mut callback;
1203 }
1204 let mut custom_runs = mouse_runs.enumerate().peekable();
1205
1206 let mut offset = 0;
1207 for (ix, line) in layout.shaped_lines.iter().enumerate() {
1208 let wrap_boundaries = layout.wrap_boundaries.get(ix).unwrap_or(&empty);
1209 let boundaries = RectF::new(
1210 origin,
1211 vec2f(
1212 bounds.width(),
1213 (wrap_boundaries.len() + 1) as f32 * layout.line_height,
1214 ),
1215 );
1216
1217 let style = cx.text_style();
1218 if boundaries.intersects(visible_bounds) {
1219 if style.soft_wrap {
1220 line.paint_wrapped(
1221 scene,
1222 origin,
1223 visible_bounds,
1224 layout.line_height,
1225 wrap_boundaries,
1226 cx,
1227 );
1228 } else {
1229 line.paint(scene, origin, visible_bounds, layout.line_height, cx);
1230 }
1231 }
1232
1233 // Paint any custom runs that intersect this line.
1234 let end_offset = offset + line.len();
1235 if let Some((custom_run_ix, custom_run_range)) = custom_runs.peek().cloned() {
1236 if custom_run_range.start < end_offset {
1237 let mut current_custom_run = None;
1238 if custom_run_range.start <= offset {
1239 current_custom_run = Some((custom_run_ix, custom_run_range.end, origin));
1240 }
1241
1242 let mut glyph_origin = origin;
1243 let mut prev_position = 0.;
1244 let mut wrap_boundaries = wrap_boundaries.iter().copied().peekable();
1245 for (run_ix, glyph_ix, glyph) in
1246 line.runs().iter().enumerate().flat_map(|(run_ix, run)| {
1247 run.glyphs()
1248 .iter()
1249 .enumerate()
1250 .map(move |(ix, glyph)| (run_ix, ix, glyph))
1251 })
1252 {
1253 glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
1254 prev_position = glyph.position.x();
1255
1256 // If we've reached a soft wrap position, move down one line. If there
1257 // is a custom run in-progress, paint it.
1258 if wrap_boundaries
1259 .peek()
1260 .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
1261 {
1262 if let Some((run_ix, _, run_origin)) = &mut current_custom_run {
1263 let bounds = RectF::from_points(
1264 *run_origin,
1265 glyph_origin + vec2f(0., layout.line_height),
1266 );
1267 custom_run_callback(*run_ix, bounds, scene, cx);
1268 *run_origin =
1269 vec2f(origin.x(), glyph_origin.y() + layout.line_height);
1270 }
1271 wrap_boundaries.next();
1272 glyph_origin = vec2f(origin.x(), glyph_origin.y() + layout.line_height);
1273 }
1274
1275 // If we've reached the end of the current custom run, paint it.
1276 if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
1277 if offset + glyph.index == run_end_offset {
1278 current_custom_run.take();
1279 let bounds = RectF::from_points(
1280 run_origin,
1281 glyph_origin + vec2f(0., layout.line_height),
1282 );
1283 custom_run_callback(run_ix, bounds, scene, cx);
1284 custom_runs.next();
1285 }
1286
1287 if let Some((_, run_range)) = custom_runs.peek() {
1288 if run_range.start >= end_offset {
1289 break;
1290 }
1291 if run_range.start == offset + glyph.index {
1292 current_custom_run =
1293 Some((run_ix, run_range.end, glyph_origin));
1294 }
1295 }
1296 }
1297
1298 // If we've reached the start of a new custom run, start tracking it.
1299 if let Some((run_ix, run_range)) = custom_runs.peek() {
1300 if offset + glyph.index == run_range.start {
1301 current_custom_run = Some((*run_ix, run_range.end, glyph_origin));
1302 }
1303 }
1304 }
1305
1306 // If a custom run extends beyond the end of the line, paint it.
1307 if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
1308 let line_end = glyph_origin + vec2f(line.width() - prev_position, 0.);
1309 let bounds = RectF::from_points(
1310 run_origin,
1311 line_end + vec2f(0., layout.line_height),
1312 );
1313 custom_run_callback(run_ix, bounds, scene, cx);
1314 if end_offset == run_end_offset {
1315 custom_runs.next();
1316 }
1317 }
1318 }
1319 }
1320
1321 offset = end_offset + 1;
1322 origin.set_y(boundaries.max_y());
1323 }
1324 }
1325
1326 fn rect_for_text_range(
1327 &self,
1328 _: Range<usize>,
1329 _: RectF,
1330 _: RectF,
1331 _: &Self::LayoutState,
1332 _: &Self::PaintState,
1333 _: &V,
1334 _: &ViewContext<V>,
1335 ) -> Option<RectF> {
1336 None
1337 }
1338
1339 fn debug(
1340 &self,
1341 bounds: RectF,
1342 _: &Self::LayoutState,
1343 _: &Self::PaintState,
1344 _: &V,
1345 _: &ViewContext<V>,
1346 ) -> Value {
1347 json!({
1348 "type": "Text",
1349 "bounds": bounds.to_json(),
1350 "text": &self.text,
1351 })
1352 }
1353}
1354
1355pub struct TextLayout {
1356 shaped_lines: Vec<Line>,
1357 wrap_boundaries: Vec<Vec<ShapedBoundary>>,
1358 line_height: f32,
1359}
1360
1361fn optional_add<T>(a: Option<T>, b: Option<T>) -> Option<T::Output>
1362where
1363 T: Add<Output = T>,
1364{
1365 match (a, b) {
1366 (Some(a), Some(b)) => Some(a + b),
1367 (Some(a), None) => Some(a),
1368 (None, Some(b)) => Some(b),
1369 (None, None) => None,
1370 }
1371}