`frame` named a length, not a rectangle, which was the one word in the layout vocabulary that lied about its own shape. It is `rel_base`: what a fraction a widget declares or reports is a fraction of. Three API changes with it, all for containers that do one simple thing: - `widget_within(id, region)` returns, taking a box in the widget's own coordinates and deriving the child's rel base from it. `Offset` and `Pad` are one call each again. `Offset` also stops reading `region_len`, which pinned its drawing to a box length it does not care about. - `place_at` takes the rel base, returns the answer, and asks the child where there is no answer to re-express. Which of the two happens is the painter's to work out, so `Span`'s second pass is one call and its `drawn_across` bookkeeping is gone. - `Part::All` is a `Part::WHOLE` constant rather than a variant, since it was exactly `Of(UiSpan::FULL)` and bought a separate arm in two matches. Measured at 0.07% of instructions retired against 0.04% run-to-run noise. Cold layout is byte-identical to `84dad21` over 400 depth-5 trees.
80 lines
3.3 KiB
Rust
80 lines
3.3 KiB
Rust
use crate::{Axis, Holds, Len, PxVec2, UiRegion, UiVec2};
|
|
|
|
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
|
|
|
/// What one evaluation of a widget depends on: the window lengths its reads
|
|
/// hold for, the pixel lengths of its own box, and the symbolic lengths of
|
|
/// that box and of its rel base where either one is what it was expressed in.
|
|
///
|
|
/// The symbolic lengths are pins rather than ranges: a container places its
|
|
/// children as lengths of its rel base measured from where its own box starts,
|
|
/// so what it draws turns on that box's length and on nothing about where it
|
|
/// is. A box pin reaches the parent only where the box it pinned is the
|
|
/// parent's own; anywhere else the parent chose that length itself, and a
|
|
/// widget pinned this way is checked when it is re-placed.
|
|
///
|
|
/// A rel base pin says the answer or the drawing is a fraction of the rel base,
|
|
/// which is a different length wherever the rel base is a different one -- at
|
|
/// the same window size, so no range of window pixels can say it. A length
|
|
/// of the rel base that is only pixels is not one: it is that many pixels
|
|
/// whatever the rel base turns out to be.
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct LayoutHolds {
|
|
pub window: [Holds; 2],
|
|
pub rel_base: [Option<Len>; 2],
|
|
pub region: [Holds; 2],
|
|
pub region_len: [Option<Len>; 2],
|
|
}
|
|
|
|
impl LayoutHolds {
|
|
pub const ANY: Self = Self {
|
|
window: [Holds::ANY; 2],
|
|
rel_base: [None; 2],
|
|
region: [Holds::ANY; 2],
|
|
region_len: [None; 2],
|
|
};
|
|
|
|
pub fn and(self, other: Self) -> Self {
|
|
let mut result = Self::ANY;
|
|
for n in 0..2 {
|
|
result.window[n] = self.window[n].and(other.window[n]);
|
|
result.region[n] = self.region[n].and(other.region[n]);
|
|
debug_assert!(
|
|
self.region_len[n].is_none()
|
|
|| other.region_len[n].is_none()
|
|
|| self.region_len[n] == other.region_len[n]
|
|
);
|
|
debug_assert!(
|
|
self.rel_base[n].is_none()
|
|
|| other.rel_base[n].is_none()
|
|
|| self.rel_base[n] == other.rel_base[n]
|
|
);
|
|
result.region_len[n] = self.region_len[n].or(other.region_len[n]);
|
|
result.rel_base[n] = self.rel_base[n].or(other.rel_base[n]);
|
|
}
|
|
result
|
|
}
|
|
|
|
pub fn covers(self, other: Self) -> bool {
|
|
(0..2).all(|n| {
|
|
self.window[n].lo <= other.window[n].lo
|
|
&& self.window[n].hi >= other.window[n].hi
|
|
&& self.region[n].lo <= other.region[n].lo
|
|
&& self.region[n].hi >= other.region[n].hi
|
|
&& self.region_len[n].is_none_or(|len| other.region_len[n] == Some(len))
|
|
&& self.rel_base[n].is_none_or(|len| other.rel_base[n] == Some(len))
|
|
})
|
|
}
|
|
|
|
pub fn contains(self, window: PxVec2, rel_base: UiVec2, region: UiRegion) -> bool {
|
|
AXES.into_iter().all(|axis| {
|
|
let n = axis as usize;
|
|
let len = region.axis(axis).len();
|
|
self.window[n].contains(window.axis(axis))
|
|
&& self.rel_base[n].is_none_or(|pinned| pinned == rel_base.axis(axis))
|
|
&& self.region[n].contains(len.to_px(window.axis(axis)))
|
|
&& self.region_len[n].is_none_or(|pinned| pinned == len)
|
|
})
|
|
}
|
|
}
|