#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
#[derive(Debug, Clone, Default)]
pub struct BoxModel {
pub border_box: Rect,
pub padding_box: Rect,
pub content_box: Rect,
pub children_box: Rect,
}
#[derive(Debug, Clone)]
pub struct InlineBox {
pub box_model: BoxModel,
pub line_spans: Vec<LineSpan>,
}
#[derive(Debug, Clone)]
pub struct LineSpan {
pub x_range: std::ops::Range<f32>,
pub line_pos: (f32, f32),
pub line_index: usize,
}
impl LineSpan {
pub fn width(&self) -> f32 {
self.x_range.end - self.x_range.start
}
}
#[derive(Debug, Clone, Default)]
pub enum LayoutBox {
#[default]
None,
BlockBox(BoxModel),
InlineBox(InlineBox),
}
#[derive(Debug, Clone, Copy)]
struct InlineBoxEdges {
left_border: f32,
right_border: f32,
top_border: f32,
bottom_border: f32,
left_padding: f32,
right_padding: f32,
top_padding: f32,
bottom_padding: f32,
}
impl InlineBoxEdges {
fn new(base: &BoxModel) -> Self {
Self {
left_border: base.padding_box.x - base.border_box.x,
right_border: base.border_box.right() - base.padding_box.right(),
top_border: base.padding_box.y - base.border_box.y,
bottom_border: base.border_box.bottom() - base.padding_box.bottom(),
left_padding: base.content_box.x - base.padding_box.x,
right_padding: base.padding_box.right() - base.content_box.right(),
top_padding: base.content_box.y - base.padding_box.y,
bottom_padding: base.padding_box.bottom() - base.content_box.bottom(),
}
}
}
#[derive(Debug)]
pub struct LayoutBoxIter<'a> {
inner: LayoutBoxIterInner<'a>,
}
#[derive(Debug)]
enum LayoutBoxIterInner<'a> {
Empty,
Block(Option<&'a BoxModel>),
Inline {
base: &'a BoxModel,
spans: std::slice::Iter<'a, LineSpan>,
len: usize,
edges: InlineBoxEdges,
},
}
#[derive(Debug)]
pub struct LayoutBoxIntoIter {
inner: LayoutBoxIntoIterInner,
}
#[derive(Debug)]
enum LayoutBoxIntoIterInner {
Empty,
Block(Option<BoxModel>),
Inline {
base: BoxModel,
spans: std::vec::IntoIter<LineSpan>,
len: usize,
edges: InlineBoxEdges,
},
}
impl Rect {
pub fn right(&self) -> f32 {
self.x + self.width
}
pub fn bottom(&self) -> f32 {
self.y + self.height
}
pub fn size(&self) -> (f32, f32) {
(self.width, self.height)
}
fn shift(&mut self, dx: f32, dy: f32) {
self.x += dx;
self.y += dy;
}
}
impl BoxModel {
pub(crate) fn shift(&mut self, dx: f32, dy: f32) {
self.border_box.shift(dx, dy);
self.padding_box.shift(dx, dy);
self.content_box.shift(dx, dy);
self.children_box.shift(dx, dy);
}
pub fn width(&self) -> f32 {
self.border_box.width
}
pub fn height(&self) -> f32 {
self.border_box.height
}
}
impl LayoutBox {
pub(crate) fn shift(&mut self, dx: f32, dy: f32) {
match self {
LayoutBox::None => {}
LayoutBox::BlockBox(b) => b.shift(dx, dy),
LayoutBox::InlineBox(inline) => {
inline.box_model.shift(dx, dy);
}
}
}
pub fn width_box(&self) -> f32 {
match self {
LayoutBox::None => 0.0,
LayoutBox::BlockBox(b) => b.width(),
LayoutBox::InlineBox(l) => l
.line_spans
.iter()
.map(|s| s.width())
.filter(|v| !v.is_nan())
.max_by(f32::total_cmp)
.unwrap_or_else(|| l.box_model.width()),
}
}
pub fn width(&self) -> f32 {
match self {
LayoutBox::None => 0.0,
LayoutBox::BlockBox(b) => b.width(),
LayoutBox::InlineBox(l) => l.box_model.width(),
}
}
pub fn height_box(&self) -> f32 {
match self {
LayoutBox::None => 0.0,
LayoutBox::BlockBox(b) => b.height(),
LayoutBox::InlineBox(l) => inline_box_height(l),
}
}
pub fn height(&self) -> f32 {
match self {
LayoutBox::None => 0.0,
LayoutBox::BlockBox(b) => b.height(),
LayoutBox::InlineBox(l) => l.box_model.height(),
}
}
pub fn is_empty(&self) -> bool {
match self {
LayoutBox::None => true,
LayoutBox::BlockBox(_) | LayoutBox::InlineBox(_) => false,
}
}
pub fn len(&self) -> usize {
match self {
LayoutBox::None => 0,
LayoutBox::BlockBox(_) => 1,
LayoutBox::InlineBox(v) => {
if v.line_spans.is_empty() {
1
} else {
v.line_spans.len()
}
}
}
}
pub fn iter(&self) -> LayoutBoxIter<'_> {
self.into_iter()
}
}
fn inline_box_height(inline: &InlineBox) -> f32 {
if let (Some(first), Some(last)) = (inline.line_spans.first(), inline.line_spans.last()) {
last.line_pos.1 - first.line_pos.1 + inline.box_model.height()
} else {
inline.box_model.height()
}
}
fn line_box(base: &BoxModel, span: &LineSpan, len: usize, edges: InlineBoxEdges) -> BoxModel {
let mut b = base.clone();
let new_content_width = span.width();
let keep_left = span.line_index == 0;
let keep_right = span.line_index == len - 1;
let left_padding = if keep_left { edges.left_padding } else { 0.0 };
let right_padding = if keep_right { edges.right_padding } else { 0.0 };
let left_border = if keep_left { edges.left_border } else { 0.0 };
let right_border = if keep_right { edges.right_border } else { 0.0 };
b.border_box.x = span.line_pos.0;
b.border_box.y = span.line_pos.1;
b.content_box.width = new_content_width;
b.content_box.x = span.line_pos.0 + left_border + left_padding;
b.content_box.y = span.line_pos.1 + edges.top_border + edges.top_padding;
b.padding_box.x = span.line_pos.0 + left_border;
b.padding_box.y = span.line_pos.1 + edges.top_border;
b.padding_box.width = new_content_width + left_padding + right_padding;
b.padding_box.height = b.content_box.height + edges.top_padding + edges.bottom_padding;
b.border_box.width = b.padding_box.width + left_border + right_border;
b.border_box.height = b.padding_box.height + edges.top_border + edges.bottom_border;
b.children_box = b.content_box;
b
}
impl<'a> IntoIterator for &'a LayoutBox {
type Item = BoxModel;
type IntoIter = LayoutBoxIter<'a>;
fn into_iter(self) -> Self::IntoIter {
LayoutBoxIter {
inner: match self {
LayoutBox::None => LayoutBoxIterInner::Empty,
LayoutBox::BlockBox(b) => LayoutBoxIterInner::Block(Some(b)),
LayoutBox::InlineBox(inline) => {
if inline.line_spans.is_empty() {
LayoutBoxIterInner::Block(Some(&inline.box_model))
} else {
LayoutBoxIterInner::Inline {
base: &inline.box_model,
spans: inline.line_spans.iter(),
len: inline.line_spans.len(),
edges: InlineBoxEdges::new(&inline.box_model),
}
}
}
},
}
}
}
impl Iterator for LayoutBoxIter<'_> {
type Item = BoxModel;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
LayoutBoxIterInner::Empty => None,
LayoutBoxIterInner::Block(b) => b.take().cloned(),
LayoutBoxIterInner::Inline {
base,
spans,
len,
edges,
} => spans.next().map(|span| line_box(base, span, *len, *edges)),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl DoubleEndedIterator for LayoutBoxIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
match &mut self.inner {
LayoutBoxIterInner::Empty => None,
LayoutBoxIterInner::Block(b) => b.take().cloned(),
LayoutBoxIterInner::Inline {
base,
spans,
len,
edges,
} => spans
.next_back()
.map(|span| line_box(base, span, *len, *edges)),
}
}
}
impl ExactSizeIterator for LayoutBoxIter<'_> {
fn len(&self) -> usize {
match &self.inner {
LayoutBoxIterInner::Empty => 0,
LayoutBoxIterInner::Block(b) => usize::from(b.is_some()),
LayoutBoxIterInner::Inline { spans, .. } => spans.len(),
}
}
}
impl std::iter::FusedIterator for LayoutBoxIter<'_> {}
impl IntoIterator for LayoutBox {
type Item = BoxModel;
type IntoIter = LayoutBoxIntoIter;
fn into_iter(self) -> Self::IntoIter {
LayoutBoxIntoIter {
inner: match self {
LayoutBox::None => LayoutBoxIntoIterInner::Empty,
LayoutBox::BlockBox(b) => LayoutBoxIntoIterInner::Block(Some(b)),
LayoutBox::InlineBox(inline) => {
if inline.line_spans.is_empty() {
LayoutBoxIntoIterInner::Block(Some(inline.box_model))
} else {
let edges = InlineBoxEdges::new(&inline.box_model);
LayoutBoxIntoIterInner::Inline {
base: inline.box_model,
len: inline.line_spans.len(),
spans: inline.line_spans.into_iter(),
edges,
}
}
}
},
}
}
}
impl Iterator for LayoutBoxIntoIter {
type Item = BoxModel;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
LayoutBoxIntoIterInner::Empty => None,
LayoutBoxIntoIterInner::Block(b) => b.take(),
LayoutBoxIntoIterInner::Inline {
base,
spans,
len,
edges,
} => spans.next().map(|span| line_box(base, &span, *len, *edges)),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl DoubleEndedIterator for LayoutBoxIntoIter {
fn next_back(&mut self) -> Option<Self::Item> {
match &mut self.inner {
LayoutBoxIntoIterInner::Empty => None,
LayoutBoxIntoIterInner::Block(b) => b.take(),
LayoutBoxIntoIterInner::Inline {
base,
spans,
len,
edges,
} => spans
.next_back()
.map(|span| line_box(base, &span, *len, *edges)),
}
}
}
impl ExactSizeIterator for LayoutBoxIntoIter {
fn len(&self) -> usize {
match &self.inner {
LayoutBoxIntoIterInner::Empty => 0,
LayoutBoxIntoIterInner::Block(b) => usize::from(b.is_some()),
LayoutBoxIntoIterInner::Inline { spans, .. } => spans.len(),
}
}
}
impl std::iter::FusedIterator for LayoutBoxIntoIter {}