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