use crate::InlineBox;
use crate::layout::alignment::align;
use crate::layout::alignment::unjustify;
use crate::layout::data::LayoutData;
use crate::style::Brush;
use core::cmp::Ordering;
use core::fmt;
use crate::IndentOptions;
use crate::layout::{
ContentWidths, Style, alignment::Alignment, alignment::AlignmentOptions, line::Line,
line_break::BreakLines,
};
#[derive(Clone)]
pub struct Layout<B: Brush> {
pub(crate) data: LayoutData<B>,
}
impl<B: Brush> fmt::Debug for Layout<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("Layout").field("data", &self.data).finish()
} else {
f.debug_struct("Layout")
.field("text_len", &self.data.text_len)
.field("width", &self.data.width)
.field("height", &self.data.height)
.field("lines", &self.data.lines.len())
.field("runs", &self.data.runs.len())
.field("styles", &self.data.styles.len())
.finish_non_exhaustive()
}
}
}
impl<B: Brush> Layout<B> {
pub fn new() -> Self {
Self::default()
}
pub fn scale(&self) -> f32 {
self.data.scale
}
pub fn styles(&self) -> &[Style<B>] {
&self.data.styles
}
pub fn layout_max_advance(&self) -> f32 {
self.data.layout_max_advance
}
pub fn width(&self) -> f32 {
self.data.width
}
pub fn full_width(&self) -> f32 {
self.data.full_width
}
pub fn calculate_content_widths(&self) -> ContentWidths {
self.data.calculate_content_widths()
}
pub fn height(&self) -> f32 {
self.data.height
}
pub fn len(&self) -> usize {
self.data.lines.len()
}
pub fn is_empty(&self) -> bool {
self.data.lines.is_empty()
}
pub fn get(&self, index: usize) -> Option<Line<'_, B>> {
Some(Line {
index: index as u32,
layout: self,
data: self.data.lines.get(index)?,
})
}
pub fn is_rtl(&self) -> bool {
self.data.base_level & 1 != 0
}
pub fn inline_boxes(&self) -> &[InlineBox] {
&self.data.inline_boxes
}
pub fn inline_boxes_mut(&mut self) -> &mut [InlineBox] {
&mut self.data.inline_boxes
}
pub fn lines(
&self,
) -> impl ExactSizeIterator<Item = Line<'_, B>> + DoubleEndedIterator + '_ + Clone {
self.data
.lines
.iter()
.enumerate()
.map(move |(index, data)| Line {
index: index as u32,
layout: self,
data,
})
}
pub fn set_text_indent(&mut self, amount: f32, options: IndentOptions) {
self.data.indent_amount = amount;
self.data.indent_options = options;
}
pub fn break_lines(&mut self) -> BreakLines<'_, B> {
unjustify(&mut self.data);
BreakLines::new(self)
}
pub fn break_all_lines(&mut self, max_advance: Option<f32>) {
self.break_lines()
.break_remaining(max_advance.unwrap_or(f32::MAX));
}
pub fn align(&mut self, alignment: Alignment, options: AlignmentOptions) {
unjustify(&mut self.data);
align(&mut self.data, alignment, options);
}
pub(crate) fn line_for_byte_index(&self, index: usize) -> Option<(usize, Line<'_, B>)> {
let line_index = self
.data
.lines
.binary_search_by(|line| {
if index < line.text_range.start {
Ordering::Greater
} else if index >= line.text_range.end {
Ordering::Less
} else {
Ordering::Equal
}
})
.ok()?;
Some((line_index, self.get(line_index)?))
}
pub(crate) fn line_for_offset(&self, offset: f32) -> Option<(usize, Line<'_, B>)> {
if offset < 0.0 {
return Some((0, self.get(0)?));
}
let maybe_line_index = self.data.lines.binary_search_by(|line| {
if offset < line.metrics.block_min_coord {
Ordering::Greater
} else if offset >= line.metrics.block_max_coord {
Ordering::Less
} else {
Ordering::Equal
}
});
let line_index = match maybe_line_index {
Ok(index) => index,
Err(index) => index.saturating_sub(1),
};
Some((line_index, self.get(line_index)?))
}
}
impl<B: Brush> Default for Layout<B> {
fn default() -> Self {
Self {
data: LayoutData::default(),
}
}
}