use crate::layout::{Border, BorderCollapse, LayoutRect, Padding};
pub fn compute_content_area(area: LayoutRect, padding: Padding, border: Border) -> LayoutRect {
compute_content_area_collapsed(area, padding, border, BorderCollapse::Separate, area.width)
}
pub fn compute_content_area_collapsed(
area: LayoutRect,
padding: Padding,
border: Border,
collapse: BorderCollapse,
containing_block_width: u16,
) -> LayoutRect {
let effective_border = if collapse == BorderCollapse::Collapse && !border.is_empty() {
Border::none()
} else {
border
};
let border_left = effective_border.left.cells();
let border_top = effective_border.top.cells();
let border_h = border_left + effective_border.right.cells();
let border_v = border_top + effective_border.bottom.cells();
let cb_w = containing_block_width;
let pad_l = padding.left.resolve(cb_w);
let pad_r = padding.right.resolve(cb_w);
let pad_t = padding.top.resolve(cb_w);
let pad_b = padding.bottom.resolve(cb_w);
let inset_x = pad_l + border_left;
let inset_y = pad_t + border_top;
let total_h = pad_l + pad_r + border_h;
let total_v = pad_t + pad_b + border_v;
LayoutRect::new(
area.x + inset_x as i32,
area.y + inset_y as i32,
area.width.saturating_sub(total_h),
area.height.saturating_sub(total_v),
)
}
pub fn compute_padding_box(outer: LayoutRect, border: Border) -> LayoutRect {
let bl = border.left.cells();
let bt = border.top.cells();
let bh = bl + border.right.cells();
let bv = bt + border.bottom.cells();
LayoutRect::new(
outer.x + bl as i32,
outer.y + bt as i32,
outer.width.saturating_sub(bh),
outer.height.saturating_sub(bv),
)
}
pub fn clamp_size(value: u16, min: Option<u16>, max: Option<u16>) -> u16 {
let mut result = value;
if let Some(max) = max {
result = result.min(max);
}
if let Some(min) = min {
result = result.max(min);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::layout::BorderStyle;
#[test]
fn content_area_none_padding_no_border() {
let area = LayoutRect::new(0, 0, 80, 24);
assert_eq!(
compute_content_area(area, Padding::default(), Border::none()),
area
);
}
#[test]
fn content_area_with_padding() {
let area = LayoutRect::new(0, 0, 80, 24);
let content = compute_content_area(area, Padding::symmetric(2, 1), Border::none());
assert_eq!(content, LayoutRect::new(2, 1, 76, 22));
}
#[test]
fn content_area_with_asymmetric_padding() {
let area = LayoutRect::new(0, 0, 80, 24);
let content = compute_content_area(area, Padding::new(1, 3, 2, 5), Border::none());
assert_eq!(content, LayoutRect::new(5, 1, 72, 21));
}
#[test]
fn content_area_with_single_border() {
let area = LayoutRect::new(0, 0, 80, 24);
let content = compute_content_area(area, Padding::default(), Border::single());
assert_eq!(content, LayoutRect::new(1, 1, 78, 22));
}
#[test]
fn content_area_with_top_only_border() {
let area = LayoutRect::new(0, 0, 80, 24);
let content = compute_content_area(area, Padding::default(), Border::top());
assert_eq!(content, LayoutRect::new(0, 1, 80, 23));
}
#[test]
fn clamp_within_range_unchanged() {
assert_eq!(clamp_size(10, Some(0), Some(20)), 10);
}
#[test]
fn clamp_below_min() {
assert_eq!(clamp_size(3, Some(5), Some(20)), 5);
}
#[test]
fn clamp_above_max() {
assert_eq!(clamp_size(30, Some(5), Some(20)), 20);
}
#[test]
fn clamp_min_wins_over_max() {
assert_eq!(clamp_size(7, Some(10), Some(5)), 10);
}
#[test]
fn clamp_no_constraints() {
assert_eq!(clamp_size(42, None, None), 42);
}
#[test]
fn padding_box_subtracts_border_on_all_sides() {
let outer = LayoutRect::new(0, 0, 10, 10);
let border = Border::single(); let pb = compute_padding_box(outer, border);
assert_eq!(pb, LayoutRect::new(1, 1, 8, 8));
}
#[test]
fn padding_box_independent_of_border_collapse() {
let outer = LayoutRect::new(5, 10, 20, 15);
let border = Border::single();
let pb = compute_padding_box(outer, border);
assert_eq!(pb, LayoutRect::new(6, 11, 18, 13));
}
#[test]
fn padding_box_saturates_when_border_exceeds_outer() {
let outer = LayoutRect::new(0, 0, 1, 1);
let border = Border::single();
let pb = compute_padding_box(outer, border);
assert_eq!(pb.x, 1);
assert_eq!(pb.y, 1);
assert_eq!(pb.width, 0);
assert_eq!(pb.height, 0);
}
#[test]
fn padding_box_with_no_border_equals_outer() {
let outer = LayoutRect::new(3, 7, 50, 40);
let pb = compute_padding_box(outer, Border::none());
assert_eq!(pb, outer);
}
#[test]
fn padding_box_per_side_border_only_top() {
let outer = LayoutRect::new(0, 0, 20, 10);
let mut border = Border::none();
border.top = BorderStyle::Solid;
let pb = compute_padding_box(outer, border);
assert_eq!(pb, LayoutRect::new(0, 1, 20, 9));
}
}