Lay out in a frame that passes through and a box placed in it
A widget is asked in two boxes rather than one. Its frame is what a fraction it declares or reports is a fraction of, and it passes through a span, a stack and a scroll unchanged, so `rel(0.5)` is half the same area however many containers sit between: a frame is narrowed only by what is decided above the widget -- a declared length, the root. Its extent is where the drawing goes, given as a `Place` per axis: a part of the parent's own box, measured in frame lengths from where that box starts, which the child either fills or has its answer placed inside. What that buys is that nothing under a container depends on where the container sits. A container reads `extent_len` for the length it divides and nothing about the start, so moving it re-places its children by re-adding that start and draws nobody again; and a fraction is resolved once, against the frame, rather than once per box it is composed through -- a stack sized by a child that reports `rel(0.5)` no longer takes half of half. `Place` replaces `DrawRegion`, `ExtentPlacement`, `widget_within`, `measure_len`, `region()`, `placement()` and `box_of`. Primitives and masks are written in the widget's own box's coordinates alone, so the drawing has one reference rather than two. The placement pin goes with them: reading the extent's length pins that length symbolically, and pins compose only where a child's box is its parent's own. Placing an answer waits for the end of the parent's draw or for the next ask of that child in it, so a span child is one drawing and one move rather than two moves. `Pad` is transparent: its padding goes around what it pads and its child keeps the outer frame, which is where `Outset` was going anyway. A fraction under a pad is now a fraction of the frame rather than of the inset box. Checked: fmt, clippy with -D warnings, 109 suite tests and 20 core tests in debug, the 11 generated cases, and the shrinker at 400 trees of depth 5 over all fifteen cases -- which still finds seed 108 under `reorder`, where a wrapping text measured in one box and drawn in another settles differently warm than cold. `redraw` therefore keeps the baseline's deferral for a box that is not as long as the one the widget was measured in; the plan's step 6 is not done, and the next commit message or the handoff says why.
This commit is contained in:
1 parent
34cafb6edc
commit
1956be3f3d
19 files changed
+989
-943
No files matched your search
+39
-40
@@ -1,63 +1,62 @@
|
||||
use crate::{Axis, Holds, PxVec2, UiRegion};
|
||||
use crate::{Axis, Holds, Len, PxVec2, UiRegion};
|
||||
|
||||
/// Dependencies of one evaluation, before the frame and extent are composed.
|
||||
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
||||
|
||||
/// What one evaluation of a widget depends on: the pixel lengths of its
|
||||
/// frame and of its own box that its drawing and its answer hold for, and
|
||||
/// the symbolic length of its own box where it read one.
|
||||
///
|
||||
/// The symbolic length is a pin rather than a range: a container places its
|
||||
/// children as lengths of its frame measured from where its own box starts,
|
||||
/// so what it draws turns on that box's length and on nothing about where it
|
||||
/// is. It does not compose into the parent -- a widget pinned this way is
|
||||
/// checked when it is re-placed.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct LayoutHolds {
|
||||
pub frame: [Holds; 2],
|
||||
pub extent: [Holds; 2],
|
||||
pub placement: Option<UiRegion>,
|
||||
pub extent_len: [Option<Len>; 2],
|
||||
}
|
||||
|
||||
impl LayoutHolds {
|
||||
pub const ANY: Self = Self {
|
||||
frame: [Holds::ANY; 2],
|
||||
extent: [Holds::ANY; 2],
|
||||
placement: None,
|
||||
extent_len: [None; 2],
|
||||
};
|
||||
|
||||
pub fn and(self, other: Self) -> Self {
|
||||
debug_assert!(
|
||||
self.placement.is_none()
|
||||
|| other.placement.is_none()
|
||||
|| self.placement == other.placement
|
||||
);
|
||||
Self {
|
||||
frame: [
|
||||
self.frame[0].and(other.frame[0]),
|
||||
self.frame[1].and(other.frame[1]),
|
||||
],
|
||||
extent: [
|
||||
self.extent[0].and(other.extent[0]),
|
||||
self.extent[1].and(other.extent[1]),
|
||||
],
|
||||
placement: self.placement.or(other.placement),
|
||||
let mut result = Self::ANY;
|
||||
for n in 0..2 {
|
||||
result.frame[n] = self.frame[n].and(other.frame[n]);
|
||||
result.extent[n] = self.extent[n].and(other.extent[n]);
|
||||
debug_assert!(
|
||||
self.extent_len[n].is_none()
|
||||
|| other.extent_len[n].is_none()
|
||||
|| self.extent_len[n] == other.extent_len[n]
|
||||
);
|
||||
result.extent_len[n] = self.extent_len[n].or(other.extent_len[n]);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn covers(self, other: Self) -> bool {
|
||||
self.placement
|
||||
.is_none_or(|placement| other.placement == Some(placement))
|
||||
&& [0, 1].into_iter().all(|n| {
|
||||
self.frame[n].lo <= other.frame[n].lo
|
||||
&& self.frame[n].hi >= other.frame[n].hi
|
||||
&& self.extent[n].lo <= other.extent[n].lo
|
||||
&& self.extent[n].hi >= other.extent[n].hi
|
||||
})
|
||||
(0..2).all(|n| {
|
||||
self.frame[n].lo <= other.frame[n].lo
|
||||
&& self.frame[n].hi >= other.frame[n].hi
|
||||
&& self.extent[n].lo <= other.extent[n].lo
|
||||
&& self.extent[n].hi >= other.extent[n].hi
|
||||
&& self.extent_len[n].is_none_or(|len| other.extent_len[n] == Some(len))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn contains(self, px: PxVec2, placement: UiRegion) -> bool {
|
||||
self.placement.is_none_or(|old| old == placement)
|
||||
&& [Axis::X, Axis::Y].into_iter().all(|axis| {
|
||||
self.frame[axis as usize].contains(px.axis(axis))
|
||||
&& self.extent[axis as usize]
|
||||
.contains(placement.axis(axis).len().to_px(px.axis(axis)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn in_frame(self, placement: UiRegion) -> [Holds; 2] {
|
||||
[Axis::X, Axis::Y].map(|axis| {
|
||||
self.frame[axis as usize]
|
||||
.and(self.extent[axis as usize].through(placement.axis(axis).len()))
|
||||
pub fn contains(self, px: PxVec2, extent: UiRegion) -> bool {
|
||||
AXES.into_iter().all(|axis| {
|
||||
let n = axis as usize;
|
||||
let len = extent.axis(axis).len();
|
||||
self.frame[n].contains(px.axis(axis))
|
||||
&& self.extent[n].contains(len.to_px(px.axis(axis)))
|
||||
&& self.extent_len[n].is_none_or(|pinned| pinned == len)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user