aetna-core 0.3.4

Aetna — backend-agnostic UI library core
Documentation
use crate::tree::TextWrap;

#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct MixedInlineLine {
    pub top: f32,
    pub width: f32,
    pub ascent: f32,
    pub descent: f32,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct MixedInlineMeasurement {
    pub width: f32,
    pub height: f32,
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct MixedInlineBreaker {
    wrap_width: Option<f32>,
    base_ascent: f32,
    base_descent: f32,
    line_height: f32,
    x: f32,
    line_top: f32,
    line_ascent: f32,
    line_descent: f32,
    max_width: f32,
    total_height: f32,
}

impl MixedInlineBreaker {
    pub(crate) fn new(
        wrap: TextWrap,
        wrap_width: Option<f32>,
        base_ascent: f32,
        base_descent: f32,
        line_height: f32,
    ) -> Self {
        Self {
            wrap_width: if matches!(wrap, TextWrap::Wrap) {
                wrap_width
            } else {
                None
            },
            base_ascent,
            base_descent,
            line_height,
            x: 0.0,
            line_top: 0.0,
            line_ascent: base_ascent,
            line_descent: base_descent,
            max_width: 0.0,
            total_height: 0.0,
        }
    }

    pub(crate) fn x(&self) -> f32 {
        self.x
    }

    pub(crate) fn line_is_empty(&self) -> bool {
        self.x == 0.0
    }

    pub(crate) fn skips_leading_space(&self, is_space: bool) -> bool {
        is_space && self.line_is_empty()
    }

    pub(crate) fn wraps_before(&self, is_space: bool, width: f32) -> bool {
        self.wrap_width
            .is_some_and(|limit| !is_space && self.x > 0.0 && self.x + width > limit)
    }

    pub(crate) fn skips_overflowing_space(&self, is_space: bool, width: f32) -> bool {
        self.wrap_width
            .is_some_and(|limit| is_space && self.x + width > limit)
    }

    pub(crate) fn push(&mut self, width: f32, ascent: f32, descent: f32) {
        self.x += width;
        self.line_ascent = self.line_ascent.max(ascent);
        self.line_descent = self.line_descent.max(descent);
    }

    pub(crate) fn finish_line(&mut self) -> MixedInlineLine {
        let line = MixedInlineLine {
            top: self.line_top,
            width: self.x,
            ascent: self.line_ascent,
            descent: self.line_descent,
        };
        self.max_width = self.max_width.max(self.x);
        self.total_height += self.line_advance();
        self.line_top = self.total_height;
        self.x = 0.0;
        self.line_ascent = self.base_ascent;
        self.line_descent = self.base_descent;
        line
    }

    pub(crate) fn finish(mut self) -> MixedInlineMeasurement {
        self.finish_line();
        MixedInlineMeasurement {
            width: self
                .wrap_width
                .map(|limit| self.max_width.min(limit))
                .unwrap_or(self.max_width),
            height: self.total_height,
        }
    }

    fn line_advance(&self) -> f32 {
        (self.line_ascent + self.line_descent).max(self.line_height)
    }
}