use std::collections::BTreeMap;
use crate::color::Color;
use crate::node::NodeId;
use crate::style::visual::BorderStyle;
use crate::units::Pt;
#[derive(Debug, Clone)]
pub struct TableLayoutData {
pub column_edges: Vec<Pt>,
pub row_edges: Vec<Pt>,
pub collapsed_borders: Vec<CollapsedBorder>,
pub header_row_indices: Vec<usize>,
pub header_height: Pt,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CollapsedBorder {
pub x1: Pt,
pub y1: Pt,
pub x2: Pt,
pub y2: Pt,
pub width: Pt,
pub color: Color,
pub style: BorderStyle,
}
#[derive(Debug, Clone)]
pub struct TextLines {
pub line_height: Pt,
pub line_count: u32,
}
#[derive(Debug, Clone)]
pub struct FootnoteLayout {
pub body_height: Pt,
pub marker: String,
}
#[derive(Debug, Clone)]
pub struct ComputedLayout {
boxes: Vec<ComputedBox>,
text_lines: BTreeMap<NodeId, TextLines>,
table_data: BTreeMap<NodeId, TableLayoutData>,
footnote_data: BTreeMap<NodeId, FootnoteLayout>,
}
impl ComputedLayout {
#[must_use]
pub fn from_vec(boxes: Vec<ComputedBox>) -> Self {
Self {
boxes,
text_lines: BTreeMap::new(),
table_data: BTreeMap::new(),
footnote_data: BTreeMap::new(),
}
}
#[must_use]
pub fn with_text_lines(
boxes: Vec<ComputedBox>,
text_lines: BTreeMap<NodeId, TextLines>,
) -> Self {
Self {
boxes,
text_lines,
table_data: BTreeMap::new(),
footnote_data: BTreeMap::new(),
}
}
#[must_use]
pub fn text_lines(&self, id: NodeId) -> Option<&TextLines> {
self.text_lines.get(&id)
}
#[must_use]
pub fn table_data(&self, id: NodeId) -> Option<&TableLayoutData> {
self.table_data.get(&id)
}
pub fn insert_table_data(&mut self, id: NodeId, data: TableLayoutData) {
self.table_data.insert(id, data);
}
#[must_use]
pub fn footnote_data(&self, id: NodeId) -> Option<&FootnoteLayout> {
self.footnote_data.get(&id)
}
pub fn insert_footnote_data(&mut self, id: NodeId, data: FootnoteLayout) {
self.footnote_data.insert(id, data);
}
pub fn set(&mut self, id: NodeId, cbox: ComputedBox) {
self.boxes[id.raw() as usize] = cbox;
}
pub fn boxes_mut(&mut self) -> &mut [ComputedBox] {
&mut self.boxes
}
#[must_use]
pub fn get(&self, id: NodeId) -> &ComputedBox {
&self.boxes[id.raw() as usize]
}
#[must_use]
pub fn len(&self) -> usize {
self.boxes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.boxes.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (NodeId, &ComputedBox)> {
self.boxes
.iter()
.enumerate()
.map(|(i, b)| (NodeId::from_raw(i as u32), b))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ComputedBox {
pub x: Pt,
pub y: Pt,
pub width: Pt,
pub height: Pt,
pub padding_top: Pt,
pub padding_right: Pt,
pub padding_bottom: Pt,
pub padding_left: Pt,
pub border_top: Pt,
pub border_right: Pt,
pub border_bottom: Pt,
pub border_left: Pt,
}
impl ComputedBox {
#[must_use]
pub fn content_x(&self) -> Pt {
self.x + self.border_left + self.padding_left
}
#[must_use]
pub fn content_y(&self) -> Pt {
self.y + self.border_top + self.padding_top
}
#[must_use]
pub fn content_width(&self) -> Pt {
self.width - self.border_left - self.border_right - self.padding_left - self.padding_right
}
#[must_use]
pub fn content_height(&self) -> Pt {
self.height - self.border_top - self.border_bottom - self.padding_top - self.padding_bottom
}
}
impl Default for ComputedBox {
fn default() -> Self {
Self {
x: Pt::ZERO,
y: Pt::ZERO,
width: Pt::ZERO,
height: Pt::ZERO,
padding_top: Pt::ZERO,
padding_right: Pt::ZERO,
padding_bottom: Pt::ZERO,
padding_left: Pt::ZERO,
border_top: Pt::ZERO,
border_right: Pt::ZERO,
border_bottom: Pt::ZERO,
border_left: Pt::ZERO,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn content_area_computation() {
let b = ComputedBox {
x: Pt::new(10.0),
y: Pt::new(20.0),
width: Pt::new(200.0),
height: Pt::new(100.0),
padding_top: Pt::new(5.0),
padding_right: Pt::new(10.0),
padding_bottom: Pt::new(5.0),
padding_left: Pt::new(10.0),
border_top: Pt::new(1.0),
border_right: Pt::new(1.0),
border_bottom: Pt::new(1.0),
border_left: Pt::new(1.0),
};
assert_eq!(b.content_x().get(), 21.0); assert_eq!(b.content_y().get(), 26.0); assert_eq!(b.content_width().get(), 178.0); assert_eq!(b.content_height().get(), 88.0); }
}