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