use crate::html::css_parse::{InsetSides, Position};
use crate::layout::{LayoutBox, Rect};
pub fn resolve_positioned_rects(boxes: &mut [LayoutBox], viewport: Rect) {
for b in boxes {
resolve_positioned_box(b, None, viewport);
}
}
pub fn pin_fixed_boxes(boxes: &mut [LayoutBox], viewport: Rect) {
for b in boxes {
if matches!(b.positioning.position, Position::Fixed) {
reposition(b, viewport);
}
pin_fixed_boxes(&mut b.children, viewport);
}
}
pub fn pin_sticky_boxes(boxes: &mut [LayoutBox], viewport: Rect) {
for b in boxes {
if matches!(b.positioning.position, Position::Sticky) {
stick(b, viewport);
}
pin_sticky_boxes(&mut b.children, viewport);
}
}
pub fn apply_relative_offset(b: &mut LayoutBox, inset: InsetSides) {
let dx = match inset.left {
Some(l) => l as i64,
None => match inset.right {
Some(r) => -(r as i64),
None => 0,
},
};
let dy = match inset.top {
Some(t) => t as i64,
None => match inset.bottom {
Some(bot) => -(bot as i64),
None => 0,
},
};
if dx != 0 || dy != 0 {
translate_subtree(b, dx, dy);
}
}
fn resolve_positioned_box(b: &mut LayoutBox, pos_ancestor: Option<Rect>, viewport: Rect) {
match b.positioning.position {
Position::Static => {
for child in &mut b.children {
resolve_positioned_box(child, pos_ancestor, viewport);
}
}
Position::Relative => {
let cb = padding_box(b);
for child in &mut b.children {
resolve_positioned_box(child, Some(cb), viewport);
}
}
Position::Absolute => {
let cb = pos_ancestor.unwrap_or(viewport);
reposition(b, cb);
let inner = padding_box(b);
for child in &mut b.children {
resolve_positioned_box(child, Some(inner), viewport);
}
}
Position::Fixed => {
reposition(b, viewport);
let inner = padding_box(b);
for child in &mut b.children {
resolve_positioned_box(child, Some(inner), viewport);
}
}
Position::Sticky => {
let cb = padding_box(b);
for child in &mut b.children {
resolve_positioned_box(child, Some(cb), viewport);
}
}
}
}
fn padding_box(b: &LayoutBox) -> Rect {
let border = &b.paint_style.box_edges.border;
Rect::new(
b.rect.x.saturating_add(border.left.thickness()),
b.rect.y.saturating_add(border.top.thickness()),
b.rect
.width
.saturating_sub(border.left.thickness())
.saturating_sub(border.right.thickness()),
b.rect
.height
.saturating_sub(border.top.thickness())
.saturating_sub(border.bottom.thickness()),
)
}
fn reposition(b: &mut LayoutBox, cb: Rect) {
let inset = b.positioning.inset;
let new_x = match inset.left {
Some(l) => (cb.x as i64 + l as i64).max(0) as usize,
None => match inset.right {
Some(r) => {
(cb.x as i64 + cb.width as i64 - b.rect.width as i64 - r as i64).max(0) as usize
}
None => cb.x,
},
};
let new_y = match inset.top {
Some(t) => (cb.y as i64 + t as i64).max(0) as usize,
None => match inset.bottom {
Some(boot) => (cb.y as i64 + cb.height as i64 - b.rect.height as i64 - boot as i64)
.max(0) as usize,
None => cb.y,
},
};
let dx = new_x as i64 - b.rect.x as i64;
let dy = new_y as i64 - b.rect.y as i64;
translate_subtree(b, dx, dy);
}
fn stick(b: &mut LayoutBox, viewport: Rect) {
if b.sticky_flow_y.is_none() {
b.sticky_flow_y = Some(b.rect.y);
}
let flow_y = b.sticky_flow_y.unwrap_or(b.rect.y);
let h = b.rect.height;
let top_inset = b.positioning.inset.top.unwrap_or(0);
let bottom_inset = b.positioning.inset.bottom.unwrap_or(0);
let min_y = (viewport.y as i64 + top_inset as i64).max(0) as usize;
let max_y = (viewport.y as i64 + viewport.height as i64 - bottom_inset as i64 - h as i64).max(0)
as usize;
let target_y = if max_y < min_y {
min_y
} else if flow_y < min_y {
min_y
} else if flow_y > max_y {
max_y
} else {
flow_y
};
let dy = target_y as i64 - b.rect.y as i64;
if dy != 0 {
translate_subtree(b, 0, dy);
}
}
pub(crate) fn translate_subtree(b: &mut LayoutBox, dx: i64, dy: i64) {
b.rect.x = shift(b.rect.x, dx);
b.rect.y = shift(b.rect.y, dy);
for child in &mut b.children {
translate_subtree(child, dx, dy);
}
}
fn shift(v: usize, d: i64) -> usize {
if d >= 0 {
v.saturating_add(d as usize)
} else {
v.saturating_sub(d.unsigned_abs() as usize)
}
}