Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136//! CSS 2.1 §9.4.3's relative-positioning offset for block-level, non-replaced boxes in//! normal flow -- the smallest untouched geometry rule the engine had left before this//! seam existed (`handoff.md`: "the engine ignores `position: relative` today"). No new//! box kind, no change to `crates/layout/src/boxtree.rs`, isolated beside//! `crate::absolute`'s seam the same way. One plain-data function a generated candidate//! implements://!//! - [`resolve`]-shaped ([`RelposResolver`]): given the box's own (already//! percentage-resolved) `top`/`right`/`bottom`/`left`, its containing block's//! padding-box size, and its `direction`, returns the horizontal and vertical shift CSS//! 2.1 9.4.3 gives the box. `crates/layout/src/fragment.rs` applies this shift on top of//! the box's ordinary in-flow placement -- never folded into that placement itself,//! since 9.4.3 is explicit that "this may cause it to overlap other boxes, but ... the//! offset does not affect the position of any other box": a relatively positioned box's//! siblings stay exactly where they would sit if it had never moved, and so do its own//! descendants relative to it -- only where the box (and everything inside it) ends up//! on the page changes. A relatively positioned box is still the containing block for//! its absolutely positioned descendants (CSS 2.1 10.1: `position` other than `static`//! qualifies), resolved by `fragment.rs` against this box's own un-shifted padding-box//! origin before this seam's shift is computed for the box itself (`layout_box`'s own//! body) -- the whole already-resolved subtree moves together with the box once the//! shift is applied to its fragment's offset one level further out.//!//! `crates/layout/src/fragment.rs` resolves every percentage in this seam's inputs against//! the containing block's own width/height before calling [`resolve`], the same pattern//! `crate::absolute`'s own module doc gives for the absolute-positioning seam: a candidate//! here is not asked to also resolve percentages, `crate::percentage`'s own job.//!//! Scope. In: `position: relative` on block-level, non-replaced boxes in normal flow;//! `top`/`bottom` and `left`/`right` in `px`, `%` (against the containing block's height//! and width respectively) and `auto`; the over-constrained rules -- horizontally, CSS 2.1//! 9.4.3 branches on `direction` the same way the absolute-positioning seam's width//! equation does (`crate::absolute::AbsoluteInputs::direction`): `left` wins when both//! `left` and `right` are given in `ltr`, `right` wins in `rtl`; vertically there is no//! such branch -- `top` always wins over `bottom` when both are given, regardless of//! `direction`. Out: `fixed`, `sticky`, inline boxes, text, transforms, scrolling.//!//! [`resolve_baseline`] is the seam's deliberately wrong starting candidate: every offset//! is ignored outright and the box is left exactly where normal-flow placement already put//! it -- the engine's actual behavior before this seam existed (`handoff.md`'s own//! description). Unlike every other seam in this codebase except `crate::absolute`, no//! hand-written `correct` implementation is written before a model has had a chance at//! this one (`roadmap.md` -> "Proof of concept", claim 3; this seam's own spec,//! `specs/relative-positioning.md`, titled "build the seam model-first").
use dom::computed::LengthPercentageOrAuto;
use crate::width::Direction;
/// Plain-data inputs to one relatively positioned box's shift. Every/// `LengthPercentageOrAuto` field here is already resolved past `Percentage` by/// `fragment.rs` (this module's own doc comment) -- a correct [`resolve`] never needs to/// see the containing block's size to interpret one of these fields, only to know it when/// deciding what an over-constrained `Auto` side falls back to.#[derive(Debug, Clone, Copy, PartialEq)]pub struct RelposInputs { /// This box's own containing block's padding-box width (CSS 2.1 9.4.3's percentage /// basis for `left`/`right`, mirroring `crate::absolute::AbsoluteInputs`'s own field). pub containing_block_width: f64, pub containing_block_height: f64, pub top: LengthPercentageOrAuto, pub right: LengthPercentageOrAuto, pub bottom: LengthPercentageOrAuto, pub left: LengthPercentageOrAuto, /// The box's own `direction`, the property CSS 2.1 9.4.3's horizontal over-constraint /// rule branches on (mirroring `crate::width::WidthInputs::direction` and /// `crate::absolute::AbsoluteInputs::direction`): in `Ltr`, `right` is the offset /// discarded when both `left` and `right` are given; in `Rtl`, `left` is. pub direction: Direction,}
/// The horizontal and vertical shift CSS 2.1 9.4.3 gives a relatively positioned box,/// applied by `fragment.rs` on top of the box's ordinary in-flow placement.#[derive(Debug, Clone, Copy, PartialEq)]pub struct RelposOutputs { pub shift_x: f64, pub shift_y: f64,}
/// One candidate's implementation of the seam: plain data in, plain data out. Every/// candidate module the generation loop compiles exports a function of this type named/// `resolve`.pub type RelposResolver = fn(&RelposInputs) -> RelposOutputs;
/// The seam's deliberately wrong starting candidate (this module's own doc comment):/// every offset (`top`/`right`/`bottom`/`left`) is ignored outright, leaving the box/// exactly where normal-flow placement already put it -- what `fragment.rs` did before/// this seam existed.pub fn resolve_baseline(_inputs: &RelposInputs) -> RelposOutputs { RelposOutputs { shift_x: 0.0, shift_y: 0.0 }}
#[cfg(test)]mod tests { use super::*;
fn inputs() -> RelposInputs { RelposInputs { containing_block_width: 800.0, containing_block_height: 600.0, top: LengthPercentageOrAuto::Length(50.0), right: LengthPercentageOrAuto::Auto, bottom: LengthPercentageOrAuto::Auto, left: LengthPercentageOrAuto::Length(30.0), direction: Direction::Ltr, } }
#[test] fn baseline_ignores_every_offset_and_returns_zero_shift() { let out = resolve_baseline(&inputs()); assert_eq!(out.shift_x, 0.0, "not the explicit left:30px"); assert_eq!(out.shift_y, 0.0, "not the explicit top:50px"); }
#[test] fn baseline_ignores_negative_and_percentage_offsets_too() { let mut i = inputs(); i.top = LengthPercentageOrAuto::Length(-20.0); i.left = LengthPercentageOrAuto::Percentage(0.5); let out = resolve_baseline(&i); assert_eq!(out.shift_x, 0.0); assert_eq!(out.shift_y, 0.0); }
#[test] fn baseline_ignores_over_constrained_offsets_in_either_direction() { let mut i = inputs(); i.left = LengthPercentageOrAuto::Length(20.0); i.right = LengthPercentageOrAuto::Length(30.0); i.direction = Direction::Rtl; let out = resolve_baseline(&i); assert_eq!(out.shift_x, 0.0, "even rtl's winning side (right) is ignored"); }}