`along` said nothing about what it did. It is `Span::slot` now: the
stretch of the row between two distances from where the span starts
laying out, as a span of its own box, with the mirror for a negative
direction in one place. `far` is `row`, which is what the comment above
it already called it, and `shares` is `has_room` beside the
`any_leftover` it was folded into. `reached` now guards on the leftover
weight it divides by rather than on the numerator that happened to be
zero with it.
The pairs layout returns are named rather than positional: `Answer`
{size, holds} and `Drawn` {answer, drawing_holds} replace
`(Size, LayoutHolds)` and a three-tuple with two `LayoutHolds` in it,
which was the one shape the cold dump exists to catch. `try_reuse`
answers `bool` rather than `Option<()>`, and the four hand-written
copies of `move_idx != parent_move` are `ActiveData::is_region_node`.
`AXES` was declared in three modules; it is `Axis::BOTH`. `rel_min`,
`rel_max` and the unused `select_len` are gone -- `ZERO` and `FULL`
already said those. Three doc comments sat on `impl` blocks instead of
the single method inside them. `reposition` and `redepth` walked their
children by index, looking the parent up again per child; both take the
list and put it back. `Scroll`'s `fixed` and `fixed_len` are
`answer_px` and `answer_is_px`, which says which one is the length.
fmt, workspace clippy under `-D warnings` with and without
`layout-diagnostics`, and the workspace tests are clean. The cold dump
over 400 depth-5 trees is byte-identical to `6c84b6f`: 34,492 boxes,
no seed moved.
109 lines
3.8 KiB
Rust
109 lines
3.8 KiB
Rust
use crate::util::impl_axis_index;
|
|
use crate::{Axis, Holds, Len, Px, PxVec2, UiRegion, UiVec2};
|
|
|
|
/// What one evaluation of a widget depends on along one axis: 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 AxisHolds {
|
|
pub window: Holds,
|
|
pub rel_base: Option<Len>,
|
|
pub region: Holds,
|
|
pub region_len: Option<Len>,
|
|
}
|
|
|
|
impl AxisHolds {
|
|
pub const ANY: Self = Self {
|
|
window: Holds::ANY,
|
|
rel_base: None,
|
|
region: Holds::ANY,
|
|
region_len: None,
|
|
};
|
|
|
|
pub fn and(self, other: Self) -> Self {
|
|
// Two pins of the same length disagreeing would mean one drawing was
|
|
// a fraction of two different lengths at once.
|
|
debug_assert!(
|
|
self.region_len.is_none()
|
|
|| other.region_len.is_none()
|
|
|| self.region_len == other.region_len
|
|
);
|
|
debug_assert!(
|
|
self.rel_base.is_none() || other.rel_base.is_none() || self.rel_base == other.rel_base
|
|
);
|
|
Self {
|
|
window: self.window.and(other.window),
|
|
rel_base: self.rel_base.or(other.rel_base),
|
|
region: self.region.and(other.region),
|
|
region_len: self.region_len.or(other.region_len),
|
|
}
|
|
}
|
|
|
|
pub fn covers(self, other: Self) -> bool {
|
|
self.window.covers(other.window)
|
|
&& self.region.covers(other.region)
|
|
&& self
|
|
.region_len
|
|
.is_none_or(|len| other.region_len == Some(len))
|
|
&& self.rel_base.is_none_or(|len| other.rel_base == Some(len))
|
|
}
|
|
|
|
/// Whether a widget in a box `len` long, with that rel base, in that
|
|
/// window, is one this drawing holds for.
|
|
pub fn contains(self, window: Px, rel_base: Len, len: Len) -> bool {
|
|
self.window.contains(window)
|
|
&& self.rel_base.is_none_or(|pinned| pinned == rel_base)
|
|
&& self.region.contains(len.to_px(window))
|
|
&& self.region_len.is_none_or(|pinned| pinned == len)
|
|
}
|
|
}
|
|
|
|
/// [`AxisHolds`] on both axes. Every question asked of it is asked of one
|
|
/// axis at a time, since a widget that read one length holds for any length
|
|
/// of the other.
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct LayoutHolds {
|
|
pub x: AxisHolds,
|
|
pub y: AxisHolds,
|
|
}
|
|
|
|
impl LayoutHolds {
|
|
pub const ANY: Self = Self {
|
|
x: AxisHolds::ANY,
|
|
y: AxisHolds::ANY,
|
|
};
|
|
|
|
pub fn and(self, other: Self) -> Self {
|
|
Self {
|
|
x: self.x.and(other.x),
|
|
y: self.y.and(other.y),
|
|
}
|
|
}
|
|
|
|
pub fn covers(self, other: Self) -> bool {
|
|
self.x.covers(other.x) && self.y.covers(other.y)
|
|
}
|
|
|
|
pub fn contains(self, window: PxVec2, rel_base: UiVec2, region: UiRegion) -> bool {
|
|
Axis::BOTH
|
|
.into_iter()
|
|
.all(|axis| self[axis].contains(window[axis], rel_base[axis], region[axis].len()))
|
|
}
|
|
}
|
|
|
|
impl_axis_index!(LayoutHolds => AxisHolds);
|