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