1use derive_more::Add;
2use gpui::elements::layout_highlighted_chunks;
3use gpui::{
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, PaintContext, Quad, SceneBuilder,
15 SizeConstraint, View, ViewContext,
16};
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 id: Option<Cow<'static, str>>,
32}
33
34pub fn node<V: View>(child: impl Element<V>) -> Node<V> {
35 Node::default().child(child)
36}
37
38pub fn column<V: View>() -> Node<V> {
39 Node::default()
40}
41
42pub fn row<V: View>() -> Node<V> {
43 Node {
44 style: NodeStyle {
45 axis: Axis3d::X,
46 ..Default::default()
47 },
48 ..Default::default()
49 }
50}
51
52pub fn stack<V: View>() -> Node<V> {
53 Node {
54 style: NodeStyle {
55 axis: Axis3d::Z,
56 ..Default::default()
57 },
58 ..Default::default()
59 }
60}
61
62impl<V: View> Default for Node<V> {
63 fn default() -> Self {
64 Self {
65 style: Default::default(),
66 children: Default::default(),
67 id: None,
68 }
69 }
70}
71
72impl<V: View> Element<V> for Node<V> {
73 type LayoutState = NodeLayout;
74 type PaintState = ();
75
76 fn layout(
77 &mut self,
78 constraint: SizeConstraint,
79 view: &mut V,
80 cx: &mut LayoutContext<V>,
81 ) -> (Vector2F, Self::LayoutState) {
82 let layout = if let Some(axis) = self.style.axis.to_2d() {
83 self.layout_xy(axis, constraint, cx.rem_pixels(), view, cx)
84 } else {
85 todo!()
86 };
87
88 (layout.size.max(constraint.min), layout)
89 }
90
91 fn paint(
92 &mut self,
93 scene: &mut SceneBuilder,
94 bounds: RectF,
95 visible_bounds: RectF,
96 layout: &mut NodeLayout,
97 view: &mut V,
98 cx: &mut PaintContext<V>,
99 ) -> Self::PaintState {
100 let margined_bounds = RectF::from_points(
101 bounds.origin() + vec2f(layout.margins.left, layout.margins.top),
102 bounds.lower_right() - vec2f(layout.margins.right, layout.margins.bottom),
103 );
104
105 // Paint drop shadow
106 for shadow in &self.style.shadows {
107 scene.push_shadow(scene::Shadow {
108 bounds: margined_bounds + shadow.offset,
109 corner_radius: self.style.corner_radius,
110 sigma: shadow.blur,
111 color: shadow.color,
112 });
113 }
114
115 // // Paint cursor style
116 // if let Some(hit_bounds) = content_bounds.intersection(visible_bounds) {
117 // if let Some(style) = self.style.cursor {
118 // scene.push_cursor_region(CursorRegion {
119 // bounds: hit_bounds,
120 // style,
121 // });
122 // }
123 // }
124
125 // Render the background and/or the border.
126 let Fill::Color(fill_color) = self.style.fill;
127 let is_fill_visible = !fill_color.is_fully_transparent();
128 if is_fill_visible || self.style.borders.is_visible() {
129 eprintln!(
130 "{}: paint background: {:?}",
131 self.id.as_deref().unwrap_or(""),
132 margined_bounds
133 );
134
135 scene.push_quad(Quad {
136 bounds: margined_bounds,
137 background: is_fill_visible.then_some(fill_color),
138 border: scene::Border {
139 width: self.style.borders.width,
140 color: self.style.borders.color,
141 overlay: false,
142 top: self.style.borders.top,
143 right: self.style.borders.right,
144 bottom: self.style.borders.bottom,
145 left: self.style.borders.left,
146 },
147 corner_radius: self.style.corner_radius,
148 });
149 }
150
151 if !self.children.is_empty() {
152 // Account for padding first.
153 let borders = &self.style.borders;
154 let padded_bounds = RectF::from_points(
155 margined_bounds.origin()
156 + vec2f(
157 borders.left_width() + layout.padding.left,
158 borders.top_width() + layout.padding.top,
159 ),
160 margined_bounds.lower_right()
161 - vec2f(
162 layout.padding.right + borders.right_width(),
163 layout.padding.bottom + borders.bottom_width(),
164 ),
165 );
166
167 if let Some(axis) = self.style.axis.to_2d() {
168 // let parent_size = padded_bounds.size();
169 let mut child_origin = padded_bounds.origin();
170
171 // Align all children together along the primary axis
172 // let mut align_horizontally = false;
173 // let mut align_vertically = false;
174 // match axis {
175 // Axis2d::X => align_horizontally = true,
176 // Axis2d::Y => align_vertically = true,
177 // }
178 // align_child(
179 // &mut child_origin,
180 // parent_size,
181 // layout.content_size,
182 // self.style.align.0,
183 // align_horizontally,
184 // align_vertically,
185 // );
186
187 for child in &mut self.children {
188 // Align each child along the cross axis
189 // align_horizontally = !align_horizontally;
190 // align_vertically = !align_vertically;
191 // align_child(
192 // &mut child_origin,
193 // parent_size,
194 // child.size(),
195 // self.style.align.0,
196 // align_horizontally,
197 // align_vertically,
198 // );
199 //
200 child.paint(scene, child_origin, visible_bounds, view, cx);
201
202 // Advance along the primary axis by the size of this child
203 child_origin.set(axis, child_origin.get(axis) + child.size().get(axis));
204 }
205 } else {
206 todo!();
207 }
208 }
209 }
210
211 fn rect_for_text_range(
212 &self,
213 range_utf16: Range<usize>,
214 _: RectF,
215 _: RectF,
216 _: &Self::LayoutState,
217 _: &Self::PaintState,
218 view: &V,
219 cx: &ViewContext<V>,
220 ) -> Option<RectF> {
221 self.children
222 .iter()
223 .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
224 }
225
226 fn debug(
227 &self,
228 bounds: RectF,
229 _: &Self::LayoutState,
230 _: &Self::PaintState,
231 view: &V,
232 cx: &ViewContext<V>,
233 ) -> Value {
234 json!({
235 "type": "Node",
236 "bounds": bounds.to_json(),
237 // TODO!
238 // "children": self.content.iter().map(|child| child.debug(view, cx)).collect::<Vec<Value>>()
239 })
240 }
241
242 fn metadata(&self) -> Option<&dyn Any> {
243 Some(&self.style)
244 }
245}
246
247impl<V: View> Node<V> {
248 pub fn id(mut self, id: impl Into<Cow<'static, str>>) -> Self {
249 self.id = Some(id.into());
250 self
251 }
252
253 pub fn child(mut self, child: impl Element<V>) -> Self {
254 self.children.push(child.into_any());
255 self
256 }
257
258 pub fn children<I, E>(mut self, children: I) -> Self
259 where
260 I: IntoIterator<Item = E>,
261 E: Element<V>,
262 {
263 self.children
264 .extend(children.into_iter().map(|child| child.into_any()));
265 self
266 }
267
268 pub fn width(mut self, width: impl Into<Length>) -> Self {
269 self.style.size.width = width.into();
270 self
271 }
272
273 pub fn height(mut self, height: impl Into<Length>) -> Self {
274 self.style.size.height = height.into();
275 self
276 }
277
278 pub fn fill(mut self, fill: impl Into<Fill>) -> Self {
279 self.style.fill = fill.into();
280 self
281 }
282
283 pub fn text_size(mut self, text_size: Rems) -> Self {
284 self.style.text.size = Some(text_size);
285 self
286 }
287
288 pub fn margins(
289 mut self,
290 top_bottom: impl Into<TopBottom>,
291 left_right: impl Into<LeftRight>,
292 ) -> Self {
293 let top_bottom = top_bottom.into();
294 let left_right = left_right.into();
295 self.style.margins = Edges {
296 top: top_bottom.top,
297 bottom: top_bottom.bottom,
298 left: left_right.left,
299 right: left_right.right,
300 };
301 self
302 }
303
304 pub fn margin_top(mut self, top: Length) -> Self {
305 self.style.margins.top = top;
306 self
307 }
308
309 pub fn margin_bottom(mut self, bottom: Length) -> Self {
310 self.style.margins.bottom = bottom;
311 self
312 }
313
314 pub fn margin_left(mut self, left: impl Into<Length>) -> Self {
315 self.style.margins.left = left.into();
316 self
317 }
318
319 pub fn margin_right(mut self, right: impl Into<Length>) -> Self {
320 self.style.margins.right = right.into();
321 self
322 }
323
324 fn id_as_string(&self) -> String {
325 self.id.as_deref().unwrap_or("<anonymous>").to_string()
326 }
327
328 fn log(&self, s: &str) {
329 eprintln!("{}: {}", self.id_as_string(), s);
330 }
331
332 fn layout_xy(
333 &mut self,
334 primary_axis: Axis2d,
335 constraint: SizeConstraint,
336 rem_pixels: f32,
337 view: &mut V,
338 cx: &mut LayoutContext<V>,
339 ) -> NodeLayout {
340 self.log(&format!("{:?}", constraint));
341
342 let cross_axis = primary_axis.rotate();
343 let total_flex = self.style.flex();
344 let mut layout = NodeLayout {
345 size: Default::default(),
346 padding: self.style.padding.fixed_pixels(rem_pixels),
347 margins: self.style.margins.fixed_pixels(rem_pixels),
348 borders: self.style.borders.edges(),
349 };
350 let fixed_padding_size = layout.padding.size();
351 let fixed_margin_size = layout.margins.size();
352 let borders_size = layout.borders.size();
353 let padded_constraint = constraint - fixed_margin_size - borders_size - fixed_padding_size;
354 let mut child_constraint = SizeConstraint::default();
355
356 dbg!(self.id_as_string());
357 for axis in [Axis2d::X, Axis2d::Y] {
358 dbg!(axis);
359 let length = self.style.size.get(axis);
360
361 match length {
362 Length::Fixed(fixed_length) => {
363 // If the length is fixed, we calculate flexible padding and margins
364 // before laying out the children.
365 let fixed_length = fixed_length.to_pixels(rem_pixels);
366 let mut remaining_flex = total_flex.get(axis);
367 let mut remaining_length = padded_constraint.max.get(axis) - fixed_length;
368
369 // Here we avoid the padding exceeding the fixed length by giving
370 // the padding calculation its own remaining_flex and remaining_length.
371 let mut padding_flex = self.style.padding.flex().get(axis);
372 let mut padding_length =
373 ((padding_flex / remaining_flex) * remaining_length).min(fixed_length);
374 layout.padding.compute_flex_edges(
375 &self.style.padding,
376 axis,
377 &mut padding_flex,
378 &mut padding_length,
379 rem_pixels,
380 );
381 remaining_flex -= padding_flex;
382 remaining_length -= padding_flex;
383 layout.margins.compute_flex_edges(
384 &self.style.margins,
385 axis,
386 &mut remaining_flex,
387 &mut remaining_length,
388 rem_pixels,
389 );
390
391 child_constraint.max.set(axis, remaining_length);
392 if axis == cross_axis {
393 child_constraint.min.set(axis, remaining_length);
394 }
395 }
396 Length::Auto { .. } => {
397 // If the length is flex, we calculate the content's share first.
398 // We then layout the children and determine the flexible padding
399 // and margins in a second phase.
400 let mut remaining_flex = total_flex.get(axis);
401 let mut remaining_length = dbg!(padded_constraint.max.get(axis));
402 let content_length =
403 length.flex_pixels(rem_pixels, &mut remaining_flex, &mut remaining_length);
404 dbg!(content_length);
405 child_constraint.max.set(axis, content_length);
406 if axis == cross_axis {
407 child_constraint.min.set(axis, content_length);
408 }
409 }
410 Length::Hug => {
411 // If hug, leave the child constraint in its default zero state.
412 // This will tell children to be as small as possible along this dimension,
413 // and we calculate the flexible padding and margins in a second phase.
414 }
415 }
416 }
417
418 let content_size = {
419 dbg!(self.id_as_string(), "lay out children");
420 // Layout fixed children using the child constraint determined above.
421 let mut remaining_child_length = dbg!(child_constraint.max).get(primary_axis);
422 let mut remaining_child_flex = 0.;
423 let mut total_child_length = 0.;
424 let mut cross_axis_max: f32 = 0.;
425 child_constraint.min.set(primary_axis, 0.);
426 child_constraint.max.set(primary_axis, 0.);
427
428 for child in &mut self.children {
429 // Don't lay out children that are flexible along the primary for this first pass,
430 // but total up their flex for use in the second pass.
431 if let Some(child_flex) = child
432 .metadata::<NodeStyle>()
433 .map(|style| style.flex().get(primary_axis))
434 {
435 if child_flex > 0. {
436 remaining_child_flex += child_flex;
437 continue;
438 }
439 }
440
441 // The child is fixed along the primary axis, so perform layout.
442 let child_size = child.layout(child_constraint, view, cx);
443 let child_length = child_size.get(primary_axis);
444 remaining_child_length -= child_length;
445 total_child_length += child_length;
446 cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
447 }
448
449 // Now divide the remaining length among the flexible children.
450 let id = self.id_as_string();
451 for child in &mut self.children {
452 if let Some(child_flex) = child
453 .metadata::<NodeStyle>()
454 .map(|style| style.flex().get(primary_axis))
455 {
456 if child_flex > 0. {
457 eprintln!("{}: child is flexible", id);
458
459 let max_child_length =
460 (child_flex / remaining_child_flex) * remaining_child_length;
461 child_constraint.max.set(primary_axis, max_child_length);
462
463 let child_size = child.layout(child_constraint, view, cx);
464 let child_length = child_size.get(primary_axis);
465 total_child_length += child_length;
466 remaining_child_length -= child_length;
467 remaining_child_flex -= child_flex;
468 cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
469 }
470 }
471 }
472
473 match primary_axis {
474 Axis2d::X => vec2f(total_child_length, cross_axis_max),
475 Axis2d::Y => vec2f(cross_axis_max, total_child_length),
476 }
477 };
478
479 // Now distribute remaining space to flexible padding and margins.
480 dbg!(self.id_as_string());
481 for axis in [Axis2d::X, Axis2d::Y] {
482 dbg!(axis);
483 let length = self.style.size.get(axis);
484
485 // Finish with flexible margins and padding now that children are laid out.
486 match length {
487 Length::Hug => {
488 // Now that we know the size of our children, we can distribute
489 // space to flexible padding and margins.
490 let mut remaining_flex = total_flex.get(axis);
491 let mut remaining_length =
492 padded_constraint.min.get(axis) - content_size.get(axis);
493 layout.padding.compute_flex_edges(
494 &self.style.padding,
495 axis,
496 &mut remaining_flex,
497 &mut remaining_length,
498 rem_pixels,
499 );
500 layout.margins.compute_flex_edges(
501 &self.style.margins,
502 axis,
503 &mut remaining_flex,
504 &mut remaining_length,
505 rem_pixels,
506 );
507 layout.size.set(
508 axis,
509 content_size.get(axis)
510 + layout.padding.size().get(axis)
511 + layout.borders.size().get(axis)
512 + layout.margins.size().get(axis),
513 );
514 }
515 Length::Fixed(fixed) => {
516 // For a fixed length, we've already computed margins and padding
517 // before laying out children. Padding and border are included in the
518 // fixed length, so we just add the margins to determine the size.
519 layout.size.set(
520 axis,
521 fixed.to_pixels(rem_pixels) + layout.margins.size().get(axis),
522 )
523 }
524 Length::Auto { .. } => {
525 let mut remaining_flex = total_flex.get(axis);
526 let mut remaining_length = padded_constraint.max.get(axis);
527
528 dbg!(remaining_flex, remaining_length);
529
530 let flex_length =
531 length.flex_pixels(rem_pixels, &mut remaining_flex, &mut remaining_length);
532
533 dbg!(flex_length, remaining_flex, remaining_length);
534
535 layout.padding.compute_flex_edges(
536 &self.style.padding,
537 axis,
538 &mut remaining_flex,
539 &mut remaining_length,
540 rem_pixels,
541 );
542
543 dbg!(remaining_flex, remaining_length);
544
545 layout.margins.compute_flex_edges(
546 &self.style.margins,
547 axis,
548 &mut remaining_flex,
549 &mut remaining_length,
550 rem_pixels,
551 );
552
553 dbg!(remaining_flex, remaining_length);
554
555 layout.size.set(
556 axis,
557 flex_length
558 + layout.padding.size().get(axis)
559 + layout.borders.size().get(axis)
560 + layout.margins.size().get(axis),
561 )
562 }
563 }
564 }
565
566 self.log(&format!("{:?}", layout));
567
568 layout
569 }
570}
571
572pub struct TopBottom {
573 top: Length,
574 bottom: Length,
575}
576
577impl<T: Into<Length>> From<(T, T)> for TopBottom {
578 fn from((top, bottom): (T, T)) -> Self {
579 Self {
580 top: top.into(),
581 bottom: bottom.into(),
582 }
583 }
584}
585
586impl<T: Copy + Into<Length>> From<T> for TopBottom {
587 fn from(both: T) -> Self {
588 Self {
589 top: both.into(),
590 bottom: both.into(),
591 }
592 }
593}
594
595pub struct LeftRight {
596 left: Length,
597 right: Length,
598}
599
600impl From<(Length, Length)> for LeftRight {
601 fn from((left, right): (Length, Length)) -> Self {
602 Self { left, right }
603 }
604}
605
606impl From<Length> for LeftRight {
607 fn from(both: Length) -> Self {
608 Self {
609 left: both,
610 right: both,
611 }
612 }
613}
614
615fn align_child(
616 child_origin: &mut Vector2F,
617 parent_size: Vector2F,
618 child_size: Vector2F,
619 alignment: Vector2F,
620 horizontal: bool,
621 vertical: bool,
622) {
623 let parent_center = parent_size / 2.;
624 let parent_target = parent_center + parent_center * alignment;
625 let child_center = child_size / 2.;
626 let child_target = child_center + child_center * alignment;
627
628 if horizontal {
629 child_origin.set_x(child_origin.x() + parent_target.x() - child_target.x())
630 }
631 if vertical {
632 child_origin.set_y(child_origin.y() + parent_target.y() - child_target.y());
633 }
634}
635
636struct Interactive<Style> {
637 default: Style,
638 hovered: Style,
639 active: Style,
640 disabled: Style,
641}
642
643#[derive(Clone, Default)]
644pub struct NodeStyle {
645 axis: Axis3d,
646 wrap: bool,
647 align: Align,
648 overflow_x: Overflow,
649 overflow_y: Overflow,
650 gap_x: Gap,
651 gap_y: Gap,
652
653 size: Size<Length>,
654 margins: Edges<Length>,
655 padding: Edges<Length>,
656 text: OptionalTextStyle,
657 opacity: f32,
658 fill: Fill,
659 borders: Borders,
660 corner_radius: f32,
661 shadows: Vec<Shadow>,
662}
663
664impl NodeStyle {
665 fn flex(&self) -> Vector2F {
666 self.size.flex() + self.padding.flex() + self.margins.flex()
667 }
668}
669
670#[optional_struct]
671struct TextStyle {
672 size: Rems,
673 font_family: Arc<str>,
674 weight: FontWeight,
675 style: FontStyle,
676}
677
678#[derive(Add, Default, Clone)]
679struct Size<T> {
680 width: T,
681 height: T,
682}
683
684impl<T: Copy> Size<T> {
685 fn get(&self, axis: Axis2d) -> T {
686 match axis {
687 Axis2d::X => self.width,
688 Axis2d::Y => self.height,
689 }
690 }
691}
692
693impl Size<Length> {
694 fn fixed_pixels(&self, rem_pixels: f32) -> Size<f32> {
695 Size {
696 width: self.width.fixed_pixels(rem_pixels),
697 height: self.height.fixed_pixels(rem_pixels),
698 }
699 }
700
701 pub fn fixed(&self) -> Size<Rems> {
702 Size {
703 width: self.width.fixed().unwrap_or_default(),
704 height: self.height.fixed().unwrap_or_default(),
705 }
706 }
707
708 pub fn flex(&self) -> Vector2F {
709 vec2f(
710 self.width.flex().unwrap_or(0.),
711 self.height.flex().unwrap_or(0.),
712 )
713 }
714}
715
716impl Size<Rems> {
717 pub fn to_pixels(&self, rem_size: f32) -> Vector2F {
718 vec2f(
719 self.width.to_pixels(rem_size),
720 self.height.to_pixels(rem_size),
721 )
722 }
723}
724
725#[derive(Clone, Default, Debug)]
726struct Edges<T> {
727 top: T,
728 bottom: T,
729 left: T,
730 right: T,
731}
732
733impl<T> Edges<T> {
734 fn start(&self, axis: Axis2d) -> &T {
735 match axis {
736 Axis2d::X => &self.left,
737 Axis2d::Y => &self.top,
738 }
739 }
740
741 fn start_mut(&mut self, axis: Axis2d) -> &mut T {
742 match axis {
743 Axis2d::X => &mut self.left,
744 Axis2d::Y => &mut self.top,
745 }
746 }
747
748 fn end(&self, axis: Axis2d) -> &T {
749 match axis {
750 Axis2d::X => &self.right,
751 Axis2d::Y => &self.bottom,
752 }
753 }
754
755 fn end_mut(&mut self, axis: Axis2d) -> &mut T {
756 match axis {
757 Axis2d::X => &mut self.right,
758 Axis2d::Y => &mut self.bottom,
759 }
760 }
761}
762
763impl Edges<f32> {
764 fn size(&self) -> Vector2F {
765 vec2f(self.left + self.right, self.top + self.bottom)
766 }
767
768 fn compute_flex_edges(
769 &mut self,
770 style_edges: &Edges<Length>,
771 axis: Axis2d,
772 remaining_flex: &mut f32,
773 remaining_length: &mut f32,
774 rem_pixels: f32,
775 ) {
776 *self.start_mut(axis) +=
777 style_edges
778 .start(axis)
779 .flex_pixels(rem_pixels, remaining_flex, remaining_length);
780 *self.end_mut(axis) +=
781 style_edges
782 .end(axis)
783 .flex_pixels(rem_pixels, remaining_flex, remaining_length);
784 }
785}
786
787impl Edges<Length> {
788 fn fixed_pixels(&self, rem_pixels: f32) -> Edges<f32> {
789 Edges {
790 top: self.top.fixed_pixels(rem_pixels),
791 bottom: self.bottom.fixed_pixels(rem_pixels),
792 left: self.left.fixed_pixels(rem_pixels),
793 right: self.right.fixed_pixels(rem_pixels),
794 }
795 }
796
797 fn flex_pixels(
798 &self,
799 rem_pixels: f32,
800 remaining_flex: &mut f32,
801 remaining_length: &mut f32,
802 ) -> Edges<f32> {
803 Edges {
804 top: self
805 .top
806 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
807 bottom: self
808 .bottom
809 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
810 left: self
811 .left
812 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
813 right: self
814 .right
815 .flex_pixels(rem_pixels, remaining_flex, remaining_length),
816 }
817 }
818
819 // pub fn fixed(&self) -> Size<Rems> {
820 // let mut size = Size::default();
821 // size.width += self.left.fixed().unwrap_or_default();
822 // size.width += self.right.fixed().unwrap_or_default();
823 // size
824 // }
825
826 pub fn flex(&self) -> Vector2F {
827 vec2f(
828 self.left.flex().unwrap_or(0.) + self.right.flex().unwrap_or(0.),
829 self.top.flex().unwrap_or(0.) + self.bottom.flex().unwrap_or(0.),
830 )
831 }
832}
833
834impl Edges<Rems> {
835 pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
836 Edges {
837 top: self.top.to_pixels(rem_size),
838 bottom: self.bottom.to_pixels(rem_size),
839 left: self.left.to_pixels(rem_size),
840 right: self.right.to_pixels(rem_size),
841 }
842 }
843}
844
845#[derive(Clone, Default)]
846struct CornerRadii {
847 top_left: f32,
848 top_right: f32,
849 bottom_right: f32,
850 bottom_left: f32,
851}
852
853#[derive(Clone)]
854pub enum Fill {
855 Color(Color),
856}
857
858impl From<Color> for Fill {
859 fn from(value: Color) -> Self {
860 Fill::Color(value)
861 }
862}
863
864impl Default for Fill {
865 fn default() -> Self {
866 Fill::Color(Color::default())
867 }
868}
869
870#[derive(Clone, Default)]
871struct Borders {
872 color: Color,
873 width: f32,
874 top: bool,
875 bottom: bool,
876 left: bool,
877 right: bool,
878}
879
880impl Borders {
881 fn is_visible(&self) -> bool {
882 self.width > 0.
883 && !self.color.is_fully_transparent()
884 && (self.top || self.bottom || self.left || self.right)
885 }
886
887 fn top_width(&self) -> f32 {
888 if self.top {
889 self.width
890 } else {
891 0.
892 }
893 }
894
895 fn bottom_width(&self) -> f32 {
896 if self.bottom {
897 self.width
898 } else {
899 0.
900 }
901 }
902
903 fn left_width(&self) -> f32 {
904 if self.left {
905 self.width
906 } else {
907 0.
908 }
909 }
910
911 fn right_width(&self) -> f32 {
912 if self.right {
913 self.width
914 } else {
915 0.
916 }
917 }
918
919 fn edges(&self) -> Edges<f32> {
920 let mut edges = Edges::default();
921 if self.width > 0. {
922 if self.top {
923 edges.top = self.width;
924 }
925 if self.bottom {
926 edges.bottom = self.width;
927 }
928 if self.left {
929 edges.left = self.width;
930 }
931 if self.right {
932 edges.right = self.width;
933 }
934 }
935 edges
936 }
937
938 fn size(&self) -> Vector2F {
939 let width =
940 if self.left { self.width } else { 0. } + if self.right { self.width } else { 0. };
941 let height =
942 if self.top { self.width } else { 0. } + if self.bottom { self.width } else { 0. };
943
944 vec2f(width, height)
945 }
946}
947
948pub mod length {
949 use derive_more::{Add, AddAssign, Into};
950
951 #[derive(Add, AddAssign, Into, Clone, Copy, Default, Debug, PartialEq)]
952 pub struct Rems(f32);
953
954 pub fn rems(rems: f32) -> Rems {
955 Rems(rems)
956 }
957
958 impl Rems {
959 pub fn to_pixels(&self, rem_pixels: f32) -> f32 {
960 self.0 * rem_pixels
961 }
962 }
963
964 #[derive(Clone, Copy, Default, Debug)]
965 pub enum Length {
966 #[default]
967 Hug,
968 Fixed(Rems),
969 Auto {
970 flex: f32,
971 min: Rems,
972 max: Rems,
973 },
974 }
975
976 impl From<Rems> for Length {
977 fn from(value: Rems) -> Self {
978 Length::Fixed(value)
979 }
980 }
981
982 pub fn auto() -> Length {
983 flex(1.)
984 }
985
986 pub fn flex(flex: f32) -> Length {
987 Length::Auto {
988 flex,
989 min: Default::default(),
990 max: rems(f32::INFINITY),
991 }
992 }
993
994 pub fn constrained(flex: f32, min: Option<Rems>, max: Option<Rems>) -> Length {
995 Length::Auto {
996 flex,
997 min: min.unwrap_or(Default::default()),
998 max: max.unwrap_or(rems(f32::INFINITY)),
999 }
1000 }
1001
1002 impl Length {
1003 pub fn flex_pixels(
1004 &self,
1005 rem_pixels: f32,
1006 remaining_flex: &mut f32,
1007 remaining_length: &mut f32,
1008 ) -> f32 {
1009 match self {
1010 Length::Auto { flex, min, max } => {
1011 let flex_length = *remaining_length / *remaining_flex;
1012 let length = (flex * flex_length)
1013 .clamp(min.to_pixels(rem_pixels), max.to_pixels(rem_pixels));
1014 *remaining_flex -= flex;
1015 *remaining_length -= length;
1016 length
1017 }
1018 _ => 0.,
1019 }
1020 }
1021
1022 pub fn fixed_pixels(&self, rem: f32) -> f32 {
1023 match self {
1024 Length::Fixed(rems) => rems.to_pixels(rem),
1025 _ => 0.,
1026 }
1027 }
1028
1029 pub fn flex(&self) -> Option<f32> {
1030 match self {
1031 Length::Auto { flex, .. } => Some(*flex),
1032 _ => None,
1033 }
1034 }
1035
1036 pub fn fixed(&self) -> Option<Rems> {
1037 match self {
1038 Length::Fixed(rems) => Some(*rems),
1039 _ => None,
1040 }
1041 }
1042 }
1043}
1044
1045#[derive(Clone)]
1046struct Align(Vector2F);
1047
1048impl Default for Align {
1049 fn default() -> Self {
1050 Self(vec2f(-1., -1.))
1051 }
1052}
1053
1054#[derive(Clone, Copy, Default)]
1055enum Axis3d {
1056 X,
1057 #[default]
1058 Y,
1059 Z,
1060}
1061
1062impl Axis3d {
1063 fn to_2d(self) -> Option<Axis2d> {
1064 match self {
1065 Axis3d::X => Some(Axis2d::X),
1066 Axis3d::Y => Some(Axis2d::Y),
1067 Axis3d::Z => None,
1068 }
1069 }
1070}
1071
1072#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
1073pub enum Axis2d {
1074 X,
1075 #[default]
1076 Y,
1077}
1078
1079impl Axis2d {
1080 fn rotate(self) -> Self {
1081 match self {
1082 Axis2d::X => Axis2d::Y,
1083 Axis2d::Y => Axis2d::X,
1084 }
1085 }
1086}
1087
1088#[derive(Clone, Copy, Default)]
1089enum Overflow {
1090 #[default]
1091 Visible,
1092 Hidden,
1093 Auto,
1094}
1095
1096#[derive(Clone, Copy)]
1097enum Gap {
1098 Fixed(f32),
1099 Around,
1100 Between,
1101 Even,
1102}
1103
1104impl Default for Gap {
1105 fn default() -> Self {
1106 Gap::Fixed(0.)
1107 }
1108}
1109
1110#[derive(Clone, Copy, Default)]
1111struct Shadow {
1112 offset: Vector2F,
1113 blur: f32,
1114 color: Color,
1115}
1116
1117#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
1118enum FontStyle {
1119 #[default]
1120 Normal,
1121 Italic,
1122 Oblique,
1123}
1124
1125#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
1126enum FontWeight {
1127 Thin,
1128 ExtraLight,
1129 Light,
1130 #[default]
1131 Normal,
1132 Medium,
1133 Semibold,
1134 Bold,
1135 ExtraBold,
1136 Black,
1137}
1138
1139#[derive(Default)]
1140pub struct Text {
1141 text: Cow<'static, str>,
1142 highlights: Option<Box<[(Range<usize>, HighlightStyle)]>>,
1143 custom_runs: Option<(
1144 Box<[Range<usize>]>,
1145 Box<dyn FnMut(usize, RectF, &mut SceneBuilder, &mut AppContext)>,
1146 )>,
1147}
1148
1149pub fn text<V: View>(text: impl Into<Cow<'static, str>>) -> Node<V> {
1150 row().child(Text {
1151 text: text.into(),
1152 ..Default::default()
1153 })
1154}
1155
1156#[derive(Default, Debug)]
1157pub struct NodeLayout {
1158 size: Vector2F,
1159 padding: Edges<f32>,
1160 borders: Edges<f32>,
1161 margins: Edges<f32>,
1162}
1163
1164impl<V: View> Element<V> for Text {
1165 type LayoutState = TextLayout;
1166 type PaintState = ();
1167
1168 fn layout(
1169 &mut self,
1170 constraint: SizeConstraint,
1171 _: &mut V,
1172 cx: &mut LayoutContext<V>,
1173 ) -> (Vector2F, Self::LayoutState) {
1174 // Convert the string and highlight ranges into an iterator of highlighted chunks.
1175 let mut offset = 0;
1176 let mut highlight_ranges = self
1177 .highlights
1178 .as_ref()
1179 .map_or(Default::default(), AsRef::as_ref)
1180 .iter()
1181 .peekable();
1182 let chunks = std::iter::from_fn(|| {
1183 let result;
1184 if let Some((range, highlight_style)) = highlight_ranges.peek() {
1185 if offset < range.start {
1186 result = Some((&self.text[offset..range.start], None));
1187 offset = range.start;
1188 } else if range.end <= self.text.len() {
1189 result = Some((&self.text[range.clone()], Some(*highlight_style)));
1190 highlight_ranges.next();
1191 offset = range.end;
1192 } else {
1193 warn!(
1194 "Highlight out of text range. Text len: {}, Highlight range: {}..{}",
1195 self.text.len(),
1196 range.start,
1197 range.end
1198 );
1199 result = None;
1200 }
1201 } else if offset < self.text.len() {
1202 result = Some((&self.text[offset..], None));
1203 offset = self.text.len();
1204 } else {
1205 result = None;
1206 }
1207 result
1208 });
1209
1210 let style = cx.text_style();
1211
1212 // Perform shaping on these highlighted chunks
1213 let shaped_lines = layout_highlighted_chunks(
1214 chunks,
1215 &style,
1216 cx.text_layout_cache(),
1217 &cx.font_cache,
1218 usize::MAX,
1219 self.text.matches('\n').count() + 1,
1220 );
1221
1222 // If line wrapping is enabled, wrap each of the shaped lines.
1223 let font_id = style.font_id;
1224 let mut line_count = 0;
1225 let mut max_line_width = 0_f32;
1226 let mut wrap_boundaries = Vec::new();
1227 let mut wrapper = cx.font_cache.line_wrapper(font_id, style.font_size);
1228 for (line, shaped_line) in self.text.split('\n').zip(&shaped_lines) {
1229 if style.soft_wrap {
1230 let boundaries = wrapper
1231 .wrap_shaped_line(line, shaped_line, constraint.max.x())
1232 .collect::<Vec<_>>();
1233 line_count += boundaries.len() + 1;
1234 wrap_boundaries.push(boundaries);
1235 } else {
1236 line_count += 1;
1237 }
1238 max_line_width = max_line_width.max(shaped_line.width());
1239 }
1240
1241 let line_height = cx.font_cache.line_height(style.font_size);
1242 let size = vec2f(
1243 max_line_width
1244 .ceil()
1245 .max(constraint.min.x())
1246 .min(constraint.max.x()),
1247 (line_height * line_count as f32).ceil(),
1248 );
1249 (
1250 size,
1251 TextLayout {
1252 shaped_lines,
1253 wrap_boundaries,
1254 line_height,
1255 },
1256 )
1257 }
1258
1259 fn paint(
1260 &mut self,
1261 scene: &mut SceneBuilder,
1262 bounds: RectF,
1263 visible_bounds: RectF,
1264 layout: &mut Self::LayoutState,
1265 _: &mut V,
1266 cx: &mut PaintContext<V>,
1267 ) -> Self::PaintState {
1268 dbg!(bounds);
1269
1270 let mut origin = bounds.origin();
1271 let empty = Vec::new();
1272 let mut callback = |_, _, _: &mut SceneBuilder, _: &mut AppContext| {};
1273
1274 let mouse_runs;
1275 let custom_run_callback;
1276 if let Some((runs, build_region)) = &mut self.custom_runs {
1277 mouse_runs = runs.iter();
1278 custom_run_callback = build_region.as_mut();
1279 } else {
1280 mouse_runs = [].iter();
1281 custom_run_callback = &mut callback;
1282 }
1283 let mut custom_runs = mouse_runs.enumerate().peekable();
1284
1285 let mut offset = 0;
1286 for (ix, line) in layout.shaped_lines.iter().enumerate() {
1287 let wrap_boundaries = layout.wrap_boundaries.get(ix).unwrap_or(&empty);
1288 let boundaries = RectF::new(
1289 origin,
1290 vec2f(
1291 bounds.width(),
1292 (wrap_boundaries.len() + 1) as f32 * layout.line_height,
1293 ),
1294 );
1295
1296 let style = cx.text_style();
1297 if boundaries.intersects(visible_bounds) {
1298 if style.soft_wrap {
1299 line.paint_wrapped(
1300 scene,
1301 origin,
1302 visible_bounds,
1303 layout.line_height,
1304 wrap_boundaries,
1305 cx,
1306 );
1307 } else {
1308 line.paint(scene, origin, visible_bounds, layout.line_height, cx);
1309 }
1310 }
1311
1312 // Paint any custom runs that intersect this line.
1313 let end_offset = offset + line.len();
1314 if let Some((custom_run_ix, custom_run_range)) = custom_runs.peek().cloned() {
1315 if custom_run_range.start < end_offset {
1316 let mut current_custom_run = None;
1317 if custom_run_range.start <= offset {
1318 current_custom_run = Some((custom_run_ix, custom_run_range.end, origin));
1319 }
1320
1321 let mut glyph_origin = origin;
1322 let mut prev_position = 0.;
1323 let mut wrap_boundaries = wrap_boundaries.iter().copied().peekable();
1324 for (run_ix, glyph_ix, glyph) in
1325 line.runs().iter().enumerate().flat_map(|(run_ix, run)| {
1326 run.glyphs()
1327 .iter()
1328 .enumerate()
1329 .map(move |(ix, glyph)| (run_ix, ix, glyph))
1330 })
1331 {
1332 glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
1333 prev_position = glyph.position.x();
1334
1335 // If we've reached a soft wrap position, move down one line. If there
1336 // is a custom run in-progress, paint it.
1337 if wrap_boundaries
1338 .peek()
1339 .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
1340 {
1341 if let Some((run_ix, _, run_origin)) = &mut current_custom_run {
1342 let bounds = RectF::from_points(
1343 *run_origin,
1344 glyph_origin + vec2f(0., layout.line_height),
1345 );
1346 custom_run_callback(*run_ix, bounds, scene, cx);
1347 *run_origin =
1348 vec2f(origin.x(), glyph_origin.y() + layout.line_height);
1349 }
1350 wrap_boundaries.next();
1351 glyph_origin = vec2f(origin.x(), glyph_origin.y() + layout.line_height);
1352 }
1353
1354 // If we've reached the end of the current custom run, paint it.
1355 if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
1356 if offset + glyph.index == run_end_offset {
1357 current_custom_run.take();
1358 let bounds = RectF::from_points(
1359 run_origin,
1360 glyph_origin + vec2f(0., layout.line_height),
1361 );
1362 custom_run_callback(run_ix, bounds, scene, cx);
1363 custom_runs.next();
1364 }
1365
1366 if let Some((_, run_range)) = custom_runs.peek() {
1367 if run_range.start >= end_offset {
1368 break;
1369 }
1370 if run_range.start == offset + glyph.index {
1371 current_custom_run =
1372 Some((run_ix, run_range.end, glyph_origin));
1373 }
1374 }
1375 }
1376
1377 // If we've reached the start of a new custom run, start tracking it.
1378 if let Some((run_ix, run_range)) = custom_runs.peek() {
1379 if offset + glyph.index == run_range.start {
1380 current_custom_run = Some((*run_ix, run_range.end, glyph_origin));
1381 }
1382 }
1383 }
1384
1385 // If a custom run extends beyond the end of the line, paint it.
1386 if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
1387 let line_end = glyph_origin + vec2f(line.width() - prev_position, 0.);
1388 let bounds = RectF::from_points(
1389 run_origin,
1390 line_end + vec2f(0., layout.line_height),
1391 );
1392 custom_run_callback(run_ix, bounds, scene, cx);
1393 if end_offset == run_end_offset {
1394 custom_runs.next();
1395 }
1396 }
1397 }
1398 }
1399
1400 offset = end_offset + 1;
1401 origin.set_y(boundaries.max_y());
1402 }
1403 }
1404
1405 fn rect_for_text_range(
1406 &self,
1407 _: Range<usize>,
1408 _: RectF,
1409 _: RectF,
1410 _: &Self::LayoutState,
1411 _: &Self::PaintState,
1412 _: &V,
1413 _: &ViewContext<V>,
1414 ) -> Option<RectF> {
1415 None
1416 }
1417
1418 fn debug(
1419 &self,
1420 bounds: RectF,
1421 _: &Self::LayoutState,
1422 _: &Self::PaintState,
1423 _: &V,
1424 _: &ViewContext<V>,
1425 ) -> Value {
1426 json!({
1427 "type": "Text",
1428 "bounds": bounds.to_json(),
1429 "text": &self.text,
1430 })
1431 }
1432}
1433
1434pub struct TextLayout {
1435 shaped_lines: Vec<Line>,
1436 wrap_boundaries: Vec<Vec<ShapedBoundary>>,
1437 line_height: f32,
1438}
1439
1440fn optional_add<T>(a: Option<T>, b: Option<T>) -> Option<T::Output>
1441where
1442 T: Add<Output = T>,
1443{
1444 match (a, b) {
1445 (Some(a), Some(b)) => Some(a + b),
1446 (Some(a), None) => Some(a),
1447 (None, Some(b)) => Some(b),
1448 (None, None) => None,
1449 }
1450}
1451
1452trait Vector2FExt {
1453 fn infinity() -> Self;
1454 fn get(self, axis: Axis2d) -> f32;
1455 fn set(&mut self, axis: Axis2d, value: f32);
1456 fn increment_x(&mut self, delta: f32) -> f32;
1457 fn increment_y(&mut self, delta: f32) -> f32;
1458}
1459
1460impl Vector2FExt for Vector2F {
1461 fn infinity() -> Self {
1462 vec2f(f32::INFINITY, f32::INFINITY)
1463 }
1464
1465 fn get(self, axis: Axis2d) -> f32 {
1466 match axis {
1467 Axis2d::X => self.x(),
1468 Axis2d::Y => self.y(),
1469 }
1470 }
1471
1472 fn set(&mut self, axis: Axis2d, value: f32) {
1473 match axis {
1474 Axis2d::X => self.set_x(value),
1475 Axis2d::Y => self.set_y(value),
1476 }
1477 }
1478
1479 fn increment_x(&mut self, delta: f32) -> f32 {
1480 self.set_x(self.x() + delta);
1481 self.x()
1482 }
1483
1484 fn increment_y(&mut self, delta: f32) -> f32 {
1485 self.set_y(self.y() + delta);
1486 self.y()
1487 }
1488}
1489
1490trait ElementExt<V: View> {
1491 fn margin_left(self, margin_left: impl Into<Length>) -> Node<V>
1492 where
1493 Self: Element<V> + Sized,
1494 {
1495 node(self).margin_left(margin_left)
1496 }
1497}
1498
1499impl<V, E> ElementExt<V> for E
1500where
1501 V: View,
1502 E: Element<V>,
1503{
1504 fn margin_left(self, margin_left: impl Into<Length>) -> Node<V>
1505 where
1506 Self: Sized,
1507 {
1508 node(self).margin_left(margin_left)
1509 }
1510}