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