use crate::layout_struct::info_types::TextConfig;
use cotis_defaults::element_configs::text_config::TextConfig as DefaultTextConfig;
use cotis_utils::math::Dimensions;
use std::ops::Range;
pub type TextMeasuringFun = Box<dyn Fn(&str, &DefaultTextConfig) -> Dimensions>;
fn monospace_text_dimensions(text: &str, style: &TextConfig) -> Dimensions {
let n = text.chars().count() as f32;
let width = if n <= 0.0 {
0.0
} else {
n * style.font_size + (n - 1.0).max(0.0) * style.letter_spacing
};
let height = if style.line_height > 0.0 {
style.line_height
} else {
style.font_size
};
Dimensions { width, height }
}
fn measure_text_fragment(
text: &str,
style: &TextConfig,
measurer: Option<&TextMeasuringFun>,
) -> Dimensions {
let line_h = if style.line_height > 0.0 {
style.line_height
} else {
style.font_size
};
if text.is_empty() {
return Dimensions {
width: 0.0,
height: line_h,
};
}
match measurer {
Some(m) => m(
text,
&DefaultTextConfig {
font_id: style.font_id as u16,
font_size: style.font_size,
letter_spacing: style.letter_spacing,
line_height: style.line_height,
wrap_mode: style.wrap_mode,
alignment: style.alignment,
},
),
None => monospace_text_dimensions(text, style),
}
}
fn newline_line_ranges(source: &str) -> Vec<Range<usize>> {
let mut ranges = Vec::new();
let mut start = 0usize;
for (i, ch) in source.char_indices() {
if ch == '\n' {
ranges.push(start..i);
start = i + ch.len_utf8();
}
}
ranges.push(start..source.len());
ranges
}
#[derive(Debug)]
enum LinePiece {
Word(Range<usize>),
SpaceRun(usize),
}
fn line_word_space_pieces(line: &str) -> Vec<LinePiece> {
let mut out = Vec::new();
let mut it = line.char_indices().peekable();
while let Some((start, c)) = it.next() {
if c.is_whitespace() {
let mut count = 1usize;
while let Some(&(_, c2)) = it.peek() {
if c2.is_whitespace() {
it.next();
count += 1;
} else {
break;
}
}
out.push(LinePiece::SpaceRun(count));
} else {
let mut end = start + c.len_utf8();
while let Some(&(_, c2)) = it.peek() {
if c2.is_whitespace() {
break;
}
let (j, c2) = it.next().unwrap();
end = j + c2.len_utf8();
}
out.push(LinePiece::Word(start..end));
}
}
out
}
#[derive(Clone, Debug)]
enum TextLineChildSpec {
Spacer,
Word(Range<usize>),
}
fn text_line_child_specs(pieces: &[LinePiece]) -> Vec<TextLineChildSpec> {
let n = pieces.len();
if n == 0 {
return Vec::new();
}
let mut out = Vec::new();
for (idx, p) in pieces.iter().enumerate() {
match p {
LinePiece::SpaceRun(k) => {
let k = *k;
if k == 0 {
continue;
}
if n == 1 {
for _ in 0..k + 1 {
out.push(TextLineChildSpec::Spacer);
}
} else if idx == 0 || idx == n - 1 {
for _ in 0..k {
out.push(TextLineChildSpec::Spacer);
}
} else {
for _ in 0..k.saturating_sub(1) {
out.push(TextLineChildSpec::Spacer);
}
}
}
LinePiece::Word(r) => out.push(TextLineChildSpec::Word(r.clone())),
}
}
out
}
pub(crate) mod text_leafs;