///|
// Box-sizing and dimension conversion helpers for layout style normalization.
///|
/// Helper to resolve a dimension to pixels (for padding/border calculation)
fn resolve_dimension_to_px(dim : @types.Dimension) -> Double {
match dim {
@types.Length(v) => v
@types.Percent(_) => 0.0 // Percentages are resolved later
@types.Calc(px, _) => px // Percent part resolved later; keep px term
@types.MathFn(__mop, __mterms) => {
let px = @types.apply_math_op(__mop, __mterms.map(fn(__t) { __t.0 }))
px // Percent part resolved later; keep px term
}
@types.Auto => 0.0
@types.MinContent => 0.0 // Intrinsic sizing resolved during layout
@types.MaxContent => 0.0
@types.FitContent(_) => 0.0
}
}
///|
fn resolve_dimension_with_percent_basis(
dim : @types.Dimension,
percent_basis : Double,
) -> Double {
match dim {
@types.Length(v) => v
@types.Percent(p) =>
if percent_basis > 0.0 {
percent_basis * p
} else {
0.0
}
@types.Calc(px, pct) =>
if percent_basis > 0.0 {
percent_basis * pct + px
} else {
px
}
@types.MathFn(__mop, __mterms) =>
if percent_basis > 0.0 {
@types.apply_math_op(
__mop,
__mterms.map(fn(__t) { percent_basis * __t.1 + __t.0 }),
)
} else {
@types.apply_math_op(__mop, __mterms.map(fn(__t) { __t.0 }))
}
@types.Auto => 0.0
@types.MinContent => 0.0
@types.MaxContent => 0.0
@types.FitContent(_) => 0.0
}
}
///|
fn is_zero_dimension_value(dim : @types.Dimension) -> Bool {
match dim {
@types.Length(v) => v == 0.0
@types.Percent(v) => v == 0.0
_ => false
}
}
///|
fn has_zero_box_offsets(style : @style.Style) -> Bool {
is_zero_dimension_value(style.padding.left) &&
is_zero_dimension_value(style.padding.right) &&
is_zero_dimension_value(style.padding.top) &&
is_zero_dimension_value(style.padding.bottom) &&
is_zero_dimension_value(style.border.left) &&
is_zero_dimension_value(style.border.right) &&
is_zero_dimension_value(style.border.top) &&
is_zero_dimension_value(style.border.bottom)
}
///|
/// Adjust dimensions for box-sizing: content-box
/// When box-sizing is content-box (default), specified width/height are content dimensions.
/// The layout engine expects outer dimensions (border-box), so we adjust here.
fn adjust_for_box_sizing(style : @style.Style) -> @style.Style {
match style.box_sizing {
@types.BorderBox => {
let padding_h = resolve_dimension_to_px(style.padding.left) +
resolve_dimension_to_px(style.padding.right)
let padding_v = resolve_dimension_to_px(style.padding.top) +
resolve_dimension_to_px(style.padding.bottom)
let border_h = resolve_dimension_to_px(style.border.left) +
resolve_dimension_to_px(style.border.right)
let border_v = resolve_dimension_to_px(style.border.top) +
resolve_dimension_to_px(style.border.bottom)
let min_outer_width = padding_h + border_h
let min_outer_height = padding_v + border_v
let clamped_width = match style.width {
@types.Length(w) =>
@types.Dimension::Length(
if w < min_outer_width {
min_outer_width
} else {
w
},
)
other => other
}
let clamped_height = match style.height {
@types.Length(h) =>
@types.Dimension::Length(
if h < min_outer_height {
min_outer_height
} else {
h
},
)
other => other
}
let clamped_min_width = match style.min_width {
@types.Length(w) =>
@types.Dimension::Length(
if w < min_outer_width {
min_outer_width
} else {
w
},
)
other => other
}
let clamped_min_height = match style.min_height {
@types.Length(h) =>
@types.Dimension::Length(
if h < min_outer_height {
min_outer_height
} else {
h
},
)
other => other
}
let clamped_max_width = match style.max_width {
@types.Length(w) =>
@types.Dimension::Length(
if w < min_outer_width {
min_outer_width
} else {
w
},
)
other => other
}
let clamped_max_height = match style.max_height {
@types.Length(h) =>
@types.Dimension::Length(
if h < min_outer_height {
min_outer_height
} else {
h
},
)
other => other
}
{
..style,
width: clamped_width,
height: clamped_height,
min_width: clamped_min_width,
min_height: clamped_min_height,
max_width: clamped_max_width,
max_height: clamped_max_height,
}
}
@types.ContentBox => {
if has_zero_box_offsets(style) {
return { ..style, box_sizing: @types.BorderBox }
}
let percent_basis = match style.width {
@types.Length(w) =>
if w > 0.0 {
w +
resolve_dimension_to_px(style.padding.left) +
resolve_dimension_to_px(style.padding.right) +
resolve_dimension_to_px(style.border.left) +
resolve_dimension_to_px(style.border.right)
} else {
0.0
}
_ => 0.0
}
// Calculate padding and border sums
let padding_h = resolve_dimension_with_percent_basis(
style.padding.left,
percent_basis,
) +
resolve_dimension_with_percent_basis(style.padding.right, percent_basis)
let padding_v = resolve_dimension_with_percent_basis(
style.padding.top,
percent_basis,
) +
resolve_dimension_with_percent_basis(
style.padding.bottom,
percent_basis,
)
let border_h = resolve_dimension_with_percent_basis(
style.border.left,
percent_basis,
) +
resolve_dimension_with_percent_basis(style.border.right, percent_basis)
let border_v = resolve_dimension_with_percent_basis(
style.border.top,
percent_basis,
) +
resolve_dimension_with_percent_basis(style.border.bottom, percent_basis)
// Adjust width and height to include padding+border
let adjusted_width = match style.width {
@types.Length(w) => @types.Dimension::Length(w + padding_h + border_h)
other => other
}
let adjusted_height = match style.height {
@types.Length(h) => @types.Dimension::Length(h + padding_v + border_v)
other => other
}
// Adjust min/max constraints too
let adjusted_min_width = match style.min_width {
@types.Length(w) => @types.Dimension::Length(w + padding_h + border_h)
other => other
}
let adjusted_min_height = match style.min_height {
@types.Length(h) => @types.Dimension::Length(h + padding_v + border_v)
other => other
}
let adjusted_max_width = match style.max_width {
@types.Length(w) => @types.Dimension::Length(w + padding_h + border_h)
other => other
}
let adjusted_max_height = match style.max_height {
@types.Length(h) => @types.Dimension::Length(h + padding_v + border_v)
other => other
}
{
..style,
width: adjusted_width,
height: adjusted_height,
min_width: adjusted_min_width,
min_height: adjusted_min_height,
max_width: adjusted_max_width,
max_height: adjusted_max_height,
// Mark as adjusted (now using border-box semantics internally)
box_sizing: @types.BorderBox,
}
}
}
}