node.rs

   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}