Name the values layout carries, and say what a span's slot is
`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.
This commit is contained in:
1 parent
6c84b6f2cb
commit
3da1c71870
9 files changed
+167
-150
No files matched your search
@@ -9,6 +9,9 @@ pub enum Axis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Axis {
|
impl Axis {
|
||||||
|
/// Both of them, for the layout code that asks the same question of each.
|
||||||
|
pub const BOTH: [Self; 2] = [Self::X, Self::Y];
|
||||||
|
|
||||||
/// A per-axis pair with `aligned` on this axis and `ortho` on the other,
|
/// A per-axis pair with `aligned` on this axis and `ortho` on the other,
|
||||||
/// which is what `from_axis` does for a vector.
|
/// which is what `from_axis` does for a vector.
|
||||||
pub fn pair<T>(self, aligned: T, ortho: T) -> [T; 2] {
|
pub fn pair<T>(self, aligned: T, ortho: T) -> [T; 2] {
|
||||||
|
|||||||
@@ -163,14 +163,6 @@ impl Len {
|
|||||||
Self::from_parts(Rel::ZERO, Px::from_f32(px))
|
Self::from_parts(Rel::ZERO, Px::from_f32(px))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn rel_min() -> Self {
|
|
||||||
Self::ZERO
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn rel_max() -> Self {
|
|
||||||
Self::FULL
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn max(&self, other: Self) -> Self {
|
pub const fn max(&self, other: Self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rel: self.rel.max(other.rel),
|
rel: self.rel.max(other.rel),
|
||||||
@@ -213,10 +205,6 @@ impl Len {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_len(&self, len: Len) -> Self {
|
|
||||||
len.within_len(*self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn flip(&mut self) {
|
pub const fn flip(&mut self) {
|
||||||
self.rel = Rel::ONE.sub(self.rel);
|
self.rel = Rel::ONE.sub(self.rel);
|
||||||
self.px = self.px.neg();
|
self.px = self.px.neg();
|
||||||
|
|||||||
+16
-2
@@ -27,7 +27,7 @@ pub struct ActiveData {
|
|||||||
pub region: UiRegion,
|
pub region: UiRegion,
|
||||||
/// The measured answer and its dependencies. A hint-only dependency or
|
/// The measured answer and its dependencies. A hint-only dependency or
|
||||||
/// a widget first encountered during placement has no measurement yet.
|
/// a widget first encountered during placement has no measurement yet.
|
||||||
pub answer: Option<(Size, LayoutHolds)>,
|
pub answer: Option<Answer>,
|
||||||
/// Asked more than once in its parent's last draw -- measured in one box
|
/// Asked more than once in its parent's last draw -- measured in one box
|
||||||
/// and then asked in the one the parent decided. The parent's layout
|
/// and then asked in the one the parent decided. The parent's layout
|
||||||
/// rests on the first answer and its drawing on the last, so only the
|
/// rests on the first answer and its drawing on the last, so only the
|
||||||
@@ -82,6 +82,20 @@ impl ActiveData {
|
|||||||
/// all. Not `size`, which is what its last drawing reported: a drawing
|
/// all. Not `size`, which is what its last drawing reported: a drawing
|
||||||
/// re-expressed in the box that answer chose is not a second answer.
|
/// re-expressed in the box that answer chose is not a second answer.
|
||||||
pub fn measured(&self) -> Option<Size> {
|
pub fn measured(&self) -> Option<Size> {
|
||||||
self.answer.map(|(size, _)| size)
|
self.answer.map(|answer| answer.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether it owns a region node rather than sharing the one it was drawn
|
||||||
|
/// under, which is what its two move indices being different says.
|
||||||
|
pub fn is_region_node(&self) -> bool {
|
||||||
|
self.move_idx != self.parent_move
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What a widget answered when it was asked: the size it reported, and the
|
||||||
|
/// boxes and windows that answer holds for.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Answer {
|
||||||
|
pub size: Size,
|
||||||
|
pub holds: LayoutHolds,
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
use crate::util::impl_axis_index;
|
use crate::util::impl_axis_index;
|
||||||
use crate::{Axis, Holds, Len, Px, PxVec2, UiRegion, UiVec2};
|
use crate::{Axis, Holds, Len, Px, PxVec2, UiRegion, UiVec2};
|
||||||
|
|
||||||
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
|
||||||
|
|
||||||
/// What one evaluation of a widget depends on along one axis: the window
|
/// 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
|
/// 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
|
/// symbolic lengths of that box and of its rel base where either one is what
|
||||||
@@ -101,7 +99,8 @@ impl LayoutHolds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(self, window: PxVec2, rel_base: UiVec2, region: UiRegion) -> bool {
|
pub fn contains(self, window: PxVec2, rel_base: UiVec2, region: UiRegion) -> bool {
|
||||||
AXES.into_iter()
|
Axis::BOTH
|
||||||
|
.into_iter()
|
||||||
.all(|axis| self[axis].contains(window[axis], rel_base[axis], region[axis].len()))
|
.all(|axis| self[axis].contains(window[axis], rel_base[axis], region[axis].len()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-25
@@ -10,7 +10,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
ui::render_state::{DrawInfo, Placing},
|
ui::render_state::{DrawInfo, Placing},
|
||||||
};
|
};
|
||||||
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
|
||||||
|
|
||||||
/// makes your surfaces look pretty
|
/// makes your surfaces look pretty
|
||||||
pub struct Painter<'a> {
|
pub struct Painter<'a> {
|
||||||
@@ -155,8 +154,8 @@ impl<'a> Painter<'a> {
|
|||||||
/// where that is this widget's own narrowed the way the region is. An
|
/// where that is this widget's own narrowed the way the region is. An
|
||||||
/// axis the region leaves whole is not read at all, so a wrapper that
|
/// axis the region leaves whole is not read at all, so a wrapper that
|
||||||
/// only moves its child does not pin its drawing to a rel base.
|
/// only moves its child does not pin its drawing to a rel base.
|
||||||
fn state_rel_base(&mut self, mut place: PlaceDesc) -> PlaceDesc {
|
fn resolve_rel_base(&mut self, mut place: PlaceDesc) -> PlaceDesc {
|
||||||
for axis in AXES {
|
for axis in Axis::BOTH {
|
||||||
if let Some(span) = place[axis].narrows_rel_base() {
|
if let Some(span) = place[axis].narrows_rel_base() {
|
||||||
let len = span.len();
|
let len = span.len();
|
||||||
let stated = (len != Len::FULL).then(|| len.within_len(self.rel_base(axis)));
|
let stated = (len != Len::FULL).then(|| len.within_len(self.rel_base(axis)));
|
||||||
@@ -181,7 +180,7 @@ impl<'a> Painter<'a> {
|
|||||||
id: &'s StrongWidget<W>,
|
id: &'s StrongWidget<W>,
|
||||||
place: impl Into<PlaceDesc>,
|
place: impl Into<PlaceDesc>,
|
||||||
) -> DrawResult<'s, 'a, W> {
|
) -> DrawResult<'s, 'a, W> {
|
||||||
let place = self.state_rel_base(place.into());
|
let place = self.resolve_rel_base(place.into());
|
||||||
let region_node = self.rsc.widgets().is_region_node(id.id());
|
let region_node = self.rsc.widgets().is_region_node(id.id());
|
||||||
let declared = self.declared_lens(id);
|
let declared = self.declared_lens(id);
|
||||||
let align = self.rsc.widgets().alignment(id.id());
|
let align = self.rsc.widgets().alignment(id.id());
|
||||||
@@ -198,7 +197,7 @@ impl<'a> Painter<'a> {
|
|||||||
self.children.push(id.id());
|
self.children.push(id.id());
|
||||||
}
|
}
|
||||||
let px = rel_base.to_px(self.window);
|
let px = rel_base.to_px(self.window);
|
||||||
let (size, answer_holds, holds) = self.state.draw_inner(
|
let drawn = self.state.draw_inner(
|
||||||
id.id(),
|
id.id(),
|
||||||
DrawInfo {
|
DrawInfo {
|
||||||
layer: self.layer,
|
layer: self.layer,
|
||||||
@@ -217,8 +216,8 @@ impl<'a> Painter<'a> {
|
|||||||
None,
|
None,
|
||||||
self.rsc,
|
self.rsc,
|
||||||
);
|
);
|
||||||
let holds = self.in_parent(holds, region, place, declared);
|
let holds = self.in_parent(drawn.drawing_holds, region, place, declared);
|
||||||
let answer_holds = self.in_parent(answer_holds, region, place, declared);
|
let answer_holds = self.in_parent(drawn.answer.holds, region, place, declared);
|
||||||
match self.under.iter_mut().find(|(child, _)| *child == id.id()) {
|
match self.under.iter_mut().find(|(child, _)| *child == id.id()) {
|
||||||
Some((_, kept)) => *kept = holds,
|
Some((_, kept)) => *kept = holds,
|
||||||
None => self.under.push((id.id(), holds)),
|
None => self.under.push((id.id(), holds)),
|
||||||
@@ -226,7 +225,7 @@ impl<'a> Painter<'a> {
|
|||||||
DrawResult {
|
DrawResult {
|
||||||
child: id,
|
child: id,
|
||||||
painter: self,
|
painter: self,
|
||||||
size,
|
size: drawn.answer.size,
|
||||||
answer_holds,
|
answer_holds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,8 +254,8 @@ impl<'a> Painter<'a> {
|
|||||||
id: &'s StrongWidget<W>,
|
id: &'s StrongWidget<W>,
|
||||||
place: impl Into<PlaceDesc>,
|
place: impl Into<PlaceDesc>,
|
||||||
) -> DrawResult<'s, 'a, W> {
|
) -> DrawResult<'s, 'a, W> {
|
||||||
let place = self.state_rel_base(place.into());
|
let place = self.resolve_rel_base(place.into());
|
||||||
let states_rel_base = AXES
|
let states_rel_base = Axis::BOTH
|
||||||
.iter()
|
.iter()
|
||||||
.any(|&axis| place[axis].stated_rel_base().is_some());
|
.any(|&axis| place[axis].stated_rel_base().is_some());
|
||||||
if states_rel_base || !self.children.contains(&id.id()) {
|
if states_rel_base || !self.children.contains(&id.id()) {
|
||||||
@@ -579,10 +578,10 @@ impl PrimitiveLike for &TextureHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves what a child depends on into this widget's own terms: this
|
|
||||||
/// method's `impl` block is where a `Painter`'s own boxes are, so it takes
|
|
||||||
/// only what the child was asked with.
|
|
||||||
impl Painter<'_> {
|
impl Painter<'_> {
|
||||||
|
/// Moves what a child depends on into this widget's own terms, taking
|
||||||
|
/// only what the child was asked with.
|
||||||
|
///
|
||||||
/// Window ranges are already about the one unit and combine directly.
|
/// Window ranges are already about the one unit and combine directly.
|
||||||
/// A rel base pin becomes this widget's own rel base wherever a length of it
|
/// A rel base pin becomes this widget's own rel base wherever a length of it
|
||||||
/// is what reached the child; where only pixels did, no length of this
|
/// is what reached the child; where only pixels did, no length of this
|
||||||
@@ -602,7 +601,7 @@ impl Painter<'_> {
|
|||||||
declared: Declared,
|
declared: Declared,
|
||||||
) -> LayoutHolds {
|
) -> LayoutHolds {
|
||||||
let mut result = LayoutHolds::ANY;
|
let mut result = LayoutHolds::ANY;
|
||||||
for axis in AXES {
|
for axis in Axis::BOTH {
|
||||||
let declared = declared[axis];
|
let declared = declared[axis];
|
||||||
let holds = holds[axis];
|
let holds = holds[axis];
|
||||||
let result = &mut result[axis];
|
let result = &mut result[axis];
|
||||||
@@ -677,16 +676,16 @@ impl LayoutLen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where a widget's drawing goes inside the part its parent gave it: what
|
|
||||||
/// it reported, on the side of the part its alignment says, and the whole
|
|
||||||
/// part wherever the answer fills it.
|
|
||||||
///
|
|
||||||
/// The length it reported is a length of its rel base, and the part is one too,
|
|
||||||
/// so this takes one from the other rather than composing it into the part.
|
|
||||||
/// That is what makes a fraction the same fraction wherever the part it is
|
|
||||||
/// placed in sits and however long it is -- the fraction is resolved once,
|
|
||||||
/// here, against the rel base it was reported of.
|
|
||||||
impl PlaceDesc {
|
impl PlaceDesc {
|
||||||
|
/// Where a widget's drawing goes inside the part its parent gave it: what
|
||||||
|
/// it reported, on the side of the part its alignment says, and the whole
|
||||||
|
/// part wherever the answer fills it.
|
||||||
|
///
|
||||||
|
/// The length it reported is a length of its rel base, and the part is one
|
||||||
|
/// too, so this takes one from the other rather than composing it into the
|
||||||
|
/// part. That is what makes a fraction the same fraction wherever the part
|
||||||
|
/// it is placed in sits and however long it is -- the fraction is resolved
|
||||||
|
/// once, here, against the rel base it was reported of.
|
||||||
pub(crate) fn placement(
|
pub(crate) fn placement(
|
||||||
self,
|
self,
|
||||||
region: UiRegion,
|
region: UiRegion,
|
||||||
@@ -695,7 +694,7 @@ impl PlaceDesc {
|
|||||||
align: RegionAlign,
|
align: RegionAlign,
|
||||||
) -> UiRegion {
|
) -> UiRegion {
|
||||||
let mut placed = region;
|
let mut placed = region;
|
||||||
for axis in AXES {
|
for axis in Axis::BOTH {
|
||||||
let reported = size[axis];
|
let reported = size[axis];
|
||||||
if reported.fills(declared[axis], self[axis].does_fill()) {
|
if reported.fills(declared[axis], self[axis].does_fill()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -725,7 +724,7 @@ impl PlaceDesc {
|
|||||||
let given = self.of(own, align);
|
let given = self.of(own, align);
|
||||||
let mut rel_base = parent_rel_base;
|
let mut rel_base = parent_rel_base;
|
||||||
let mut region = given;
|
let mut region = given;
|
||||||
for axis in AXES {
|
for axis in Axis::BOTH {
|
||||||
let base = self[axis]
|
let base = self[axis]
|
||||||
.stated_rel_base()
|
.stated_rel_base()
|
||||||
.unwrap_or_else(|| parent_rel_base[axis]);
|
.unwrap_or_else(|| parent_rel_base[axis]);
|
||||||
|
|||||||
+82
-78
@@ -1,14 +1,12 @@
|
|||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
|
use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
|
||||||
use crate::{
|
use crate::{
|
||||||
ActiveData, Axis, Declared, DrawLayers, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx,
|
ActiveData, Answer, Axis, Declared, DrawLayers, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx,
|
||||||
Moves, Painter, PixelRegion, PlaceDesc, PxVec2, Rel, Size, StrongWidget, UiRegion, UiRsc,
|
MoveIdx, Moves, Painter, PixelRegion, PlaceDesc, PxVec2, Rel, Size, StrongWidget, UiRegion,
|
||||||
UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
||||||
util::{HashMap, Vec2},
|
util::{HashMap, Vec2},
|
||||||
};
|
};
|
||||||
|
|
||||||
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
|
||||||
|
|
||||||
/// Where a widget is drawn: what its parent decides about the draw besides
|
/// Where a widget is drawn: what its parent decides about the draw besides
|
||||||
/// the boxes themselves.
|
/// the boxes themselves.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -36,6 +34,14 @@ pub(super) struct DrawInfo {
|
|||||||
pub px: PxVec2,
|
pub px: PxVec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What one draw of a widget came to: the answer it gave, and the boxes and
|
||||||
|
/// windows the drawing that gave it holds for. The two are separate ranges --
|
||||||
|
/// a drawing can be invalid where its answer still stands.
|
||||||
|
pub(super) struct Drawn {
|
||||||
|
pub answer: Answer,
|
||||||
|
pub drawing_holds: LayoutHolds,
|
||||||
|
}
|
||||||
|
|
||||||
/// What a widget's children are placed in: its own box, the coordinates its
|
/// What a widget's children are placed in: its own box, the coordinates its
|
||||||
/// drawing is in, and what else one ask of a child is decided from.
|
/// drawing is in, and what else one ask of a child is decided from.
|
||||||
pub(super) struct Placing {
|
pub(super) struct Placing {
|
||||||
@@ -113,7 +119,7 @@ impl UiRenderState {
|
|||||||
// it will ask either again.
|
// it will ask either again.
|
||||||
let answer = active
|
let answer = active
|
||||||
.answer
|
.answer
|
||||||
.is_some_and(|(_, holds)| holds.contains(size, active.rel_base, active.region));
|
.is_some_and(|answer| answer.holds.contains(size, active.rel_base, active.region));
|
||||||
answer && active.holds.contains(size, active.rel_base, active.region)
|
answer && active.holds.contains(size, active.rel_base, active.region)
|
||||||
});
|
});
|
||||||
if !stands {
|
if !stands {
|
||||||
@@ -207,7 +213,7 @@ impl UiRenderState {
|
|||||||
info: DrawInfo,
|
info: DrawInfo,
|
||||||
mut old: Option<ActiveData>,
|
mut old: Option<ActiveData>,
|
||||||
rsc: &mut dyn UiRsc,
|
rsc: &mut dyn UiRsc,
|
||||||
) -> (Size, LayoutHolds, LayoutHolds) {
|
) -> Drawn {
|
||||||
let old_parent = old
|
let old_parent = old
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.or_else(|| self.active.get(&id))
|
.or_else(|| self.active.get(&id))
|
||||||
@@ -233,9 +239,9 @@ impl UiRenderState {
|
|||||||
.then(|| self.retained_answer(id, region, info))
|
.then(|| self.retained_answer(id, region, info))
|
||||||
.flatten()
|
.flatten()
|
||||||
.and_then(|answer| {
|
.and_then(|answer| {
|
||||||
let placed = info.placed.placement(region, answer.0, declared, align);
|
let placed = info.placed.placement(region, answer.size, declared, align);
|
||||||
self.try_reuse(id, region, placed, info, rsc)
|
self.try_reuse(id, region, placed, info, rsc)
|
||||||
.map(|()| answer)
|
.then_some(answer)
|
||||||
});
|
});
|
||||||
let answer = reused.unwrap_or_else(|| {
|
let answer = reused.unwrap_or_else(|| {
|
||||||
if old.is_none() {
|
if old.is_none() {
|
||||||
@@ -245,7 +251,7 @@ impl UiRenderState {
|
|||||||
// Where the drawing goes: the part its parent gave it, with the
|
// Where the drawing goes: the part its parent gave it, with the
|
||||||
// answer placed inside that part on any axis the parent left
|
// answer placed inside that part on any axis the parent left
|
||||||
// open.
|
// open.
|
||||||
let placed = info.placed.placement(region, answer.0, declared, align);
|
let placed = info.placed.placement(region, answer.size, declared, align);
|
||||||
if placed != region {
|
if placed != region {
|
||||||
self.relocate(id, placed, info, rsc);
|
self.relocate(id, placed, info, rsc);
|
||||||
}
|
}
|
||||||
@@ -273,7 +279,10 @@ impl UiRenderState {
|
|||||||
{
|
{
|
||||||
old_parent.children.retain(|child| *child != id);
|
old_parent.children.retain(|child| *child != id);
|
||||||
}
|
}
|
||||||
(answer.0, answer.1, drawing_holds)
|
Drawn {
|
||||||
|
answer,
|
||||||
|
drawing_holds,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls a widget's `draw` and keeps what it drew in `region`.
|
/// Calls a widget's `draw` and keeps what it drew in `region`.
|
||||||
@@ -284,7 +293,7 @@ impl UiRenderState {
|
|||||||
info: DrawInfo,
|
info: DrawInfo,
|
||||||
old: Option<ActiveData>,
|
old: Option<ActiveData>,
|
||||||
rsc: &mut dyn UiRsc,
|
rsc: &mut dyn UiRsc,
|
||||||
) -> (Size, LayoutHolds) {
|
) -> Answer {
|
||||||
let rel_base = info.rel_base;
|
let rel_base = info.rel_base;
|
||||||
let (move_idx, region, retired_move) = match info.region_node {
|
let (move_idx, region, retired_move) = match info.region_node {
|
||||||
// A node entry is only a translation. Its local box keeps the
|
// A node entry is only a translation. Its local box keeps the
|
||||||
@@ -395,9 +404,11 @@ impl UiRenderState {
|
|||||||
// was offered reports the height it needs.
|
// was offered reports the height it needs.
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
mask == info.mask
|
mask == info.mask
|
||||||
|| AXES
|
|| Axis::BOTH.into_iter().all(|axis| size.within_box(
|
||||||
.into_iter()
|
region,
|
||||||
.all(|axis| size.within_box(region, self.output_size, axis)),
|
self.output_size,
|
||||||
|
axis
|
||||||
|
)),
|
||||||
"'{}' ({id:?}) clips to {px:?} and reports {size}",
|
"'{}' ({id:?}) clips to {px:?} and reports {size}",
|
||||||
rsc.widgets().label(id),
|
rsc.widgets().label(id),
|
||||||
);
|
);
|
||||||
@@ -417,7 +428,7 @@ impl UiRenderState {
|
|||||||
// that many pixels of this window -- the same pin a widget that read
|
// that many pixels of this window -- the same pin a widget that read
|
||||||
// its rel base took for its drawing.
|
// its rel base took for its drawing.
|
||||||
let mut own_holds = own;
|
let mut own_holds = own;
|
||||||
for axis in AXES {
|
for axis in Axis::BOTH {
|
||||||
let fraction = rules[axis].exact().is_some_and(|len| len.rel != Rel::ZERO);
|
let fraction = rules[axis].exact().is_some_and(|len| len.rel != Rel::ZERO);
|
||||||
if fraction {
|
if fraction {
|
||||||
own_holds[axis].rel_base = Some(info.rel_base[axis]);
|
own_holds[axis].rel_base = Some(info.rel_base[axis]);
|
||||||
@@ -489,7 +500,10 @@ impl UiRenderState {
|
|||||||
};
|
};
|
||||||
rsc.on_draw(&active);
|
rsc.on_draw(&active);
|
||||||
self.active.insert(id, active);
|
self.active.insert(id, active);
|
||||||
(size, answer_holds)
|
Answer {
|
||||||
|
size,
|
||||||
|
holds: answer_holds,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keeps a region node's entry across redraws because descendants retain
|
/// Keeps a region node's entry across redraws because descendants retain
|
||||||
@@ -516,23 +530,17 @@ impl UiRenderState {
|
|||||||
/// drawing ended up. Alignment is exactly that case: the first box is the
|
/// drawing ended up. Alignment is exactly that case: the first box is the
|
||||||
/// question and the smaller placed box holds the drawing. Whether the
|
/// question and the smaller placed box holds the drawing. Whether the
|
||||||
/// answer is stale at all is its caller's question, asked once there.
|
/// answer is stale at all is its caller's question, asked once there.
|
||||||
fn retained_answer(
|
fn retained_answer(&self, id: WidgetId, region: UiRegion, info: DrawInfo) -> Option<Answer> {
|
||||||
&self,
|
|
||||||
id: WidgetId,
|
|
||||||
region: UiRegion,
|
|
||||||
info: DrawInfo,
|
|
||||||
) -> Option<(Size, LayoutHolds)> {
|
|
||||||
let active = self.active.get(&id)?;
|
let active = self.active.get(&id)?;
|
||||||
let has_region_node = active.move_idx != active.parent_move;
|
|
||||||
if !active.drawn
|
if !active.drawn
|
||||||
|| has_region_node != info.region_node
|
|| active.is_region_node() != info.region_node
|
||||||
|| active.parent_move != info.parent_move
|
|| active.parent_move != info.parent_move
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let answer = active.answer?;
|
let answer = active.answer?;
|
||||||
answer
|
answer
|
||||||
.1
|
.holds
|
||||||
.contains(self.output_size, info.rel_base, region)
|
.contains(self.output_size, info.rel_base, region)
|
||||||
.then_some(answer)
|
.then_some(answer)
|
||||||
}
|
}
|
||||||
@@ -546,7 +554,7 @@ impl UiRenderState {
|
|||||||
placed: UiRegion,
|
placed: UiRegion,
|
||||||
info: DrawInfo,
|
info: DrawInfo,
|
||||||
rsc: &mut dyn UiRsc,
|
rsc: &mut dyn UiRsc,
|
||||||
) -> Option<()> {
|
) -> bool {
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
diag::bump(Counter::ReuseAttempts);
|
diag::bump(Counter::ReuseAttempts);
|
||||||
if rsc.widgets().needs_redraw.contains(&id) {
|
if rsc.widgets().needs_redraw.contains(&id) {
|
||||||
@@ -555,19 +563,20 @@ impl UiRenderState {
|
|||||||
diag::bump(Counter::ReuseDirty);
|
diag::bump(Counter::ReuseDirty);
|
||||||
diag::reuse(id, ReuseOutcome::Dirty);
|
diag::reuse(id, ReuseOutcome::Dirty);
|
||||||
}
|
}
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
let active = self.active.get(&id)?;
|
let Some(active) = self.active.get(&id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
if !active.drawn {
|
if !active.drawn {
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
diag::reuse(id, ReuseOutcome::Undrawn);
|
diag::reuse(id, ReuseOutcome::Undrawn);
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
let has_region_node = active.move_idx != active.parent_move;
|
if active.is_region_node() != info.region_node {
|
||||||
if has_region_node != info.region_node {
|
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
diag::bump(Counter::ReuseWrongNode);
|
diag::bump(Counter::ReuseWrongNode);
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
// Drawn on another layer: the drawing sits in that layer's list and
|
// Drawn on another layer: the drawing sits in that layer's list and
|
||||||
// paints at its moment, which no amount of geometry says. A container
|
// paints at its moment, which no amount of geometry says. A container
|
||||||
@@ -580,10 +589,10 @@ impl UiRenderState {
|
|||||||
diag::bump(Counter::ReuseWrongLayer);
|
diag::bump(Counter::ReuseWrongLayer);
|
||||||
diag::reuse(id, ReuseOutcome::WrongLayer);
|
diag::reuse(id, ReuseOutcome::WrongLayer);
|
||||||
}
|
}
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
if active.parent_mask != info.mask {
|
if active.parent_mask != info.mask {
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
// Drawn somewhere else in the tree: its box is in coordinates it no
|
// Drawn somewhere else in the tree: its box is in coordinates it no
|
||||||
// longer sits in, and its slot names the wrong parent.
|
// longer sits in, and its slot names the wrong parent.
|
||||||
@@ -593,7 +602,7 @@ impl UiRenderState {
|
|||||||
diag::bump(Counter::ReuseWrongParent);
|
diag::bump(Counter::ReuseWrongParent);
|
||||||
diag::reuse(id, ReuseOutcome::WrongParent);
|
diag::reuse(id, ReuseOutcome::WrongParent);
|
||||||
}
|
}
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
// In pixels, because the box is a fraction of the window and that
|
// In pixels, because the box is a fraction of the window and that
|
||||||
// may be what changed -- an unchanged fraction of a window half the
|
// may be what changed -- an unchanged fraction of a window half the
|
||||||
@@ -607,7 +616,7 @@ impl UiRenderState {
|
|||||||
// Which of the three said no, so a rel base that redraws more
|
// Which of the three said no, so a rel base that redraws more
|
||||||
// than it should says where to look. They overlap: a drawing
|
// than it should says where to look. They overlap: a drawing
|
||||||
// can be outside two of them at once.
|
// can be outside two of them at once.
|
||||||
for axis in AXES {
|
for axis in Axis::BOTH {
|
||||||
let holds = active.holds[axis];
|
let holds = active.holds[axis];
|
||||||
let len = region[axis].len();
|
let len = region[axis].len();
|
||||||
let window = self.output_size[axis];
|
let window = self.output_size[axis];
|
||||||
@@ -628,10 +637,10 @@ impl UiRenderState {
|
|||||||
diag::bump(Counter::ReuseOutside);
|
diag::bump(Counter::ReuseOutside);
|
||||||
diag::reuse(id, ReuseOutcome::Outside);
|
diag::reuse(id, ReuseOutcome::Outside);
|
||||||
}
|
}
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
self.relocate(id, placed, info, rsc);
|
self.relocate(id, placed, info, rsc);
|
||||||
Some(())
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Puts a retained drawing where its parent now has it, without drawing:
|
/// Puts a retained drawing where its parent now has it, without drawing:
|
||||||
@@ -644,14 +653,14 @@ impl UiRenderState {
|
|||||||
"'{}' ({id:?}) placed while marked to draw",
|
"'{}' ({id:?}) placed while marked to draw",
|
||||||
rsc.widgets().label(id)
|
rsc.widgets().label(id)
|
||||||
);
|
);
|
||||||
let has_region_node = active.move_idx != active.parent_move;
|
let is_region_node = active.is_region_node();
|
||||||
let local = match has_region_node {
|
let local = match is_region_node {
|
||||||
true => placed.at_origin(),
|
true => placed.at_origin(),
|
||||||
false => placed,
|
false => placed,
|
||||||
};
|
};
|
||||||
let moved = active.placement != local;
|
let moved = active.placement != local;
|
||||||
let slot = active.move_idx;
|
let slot = active.move_idx;
|
||||||
if has_region_node {
|
if is_region_node {
|
||||||
self.moves.set(slot, placed.as_translation());
|
self.moves.set(slot, placed.as_translation());
|
||||||
}
|
}
|
||||||
if moved {
|
if moved {
|
||||||
@@ -663,23 +672,13 @@ impl UiRenderState {
|
|||||||
active.placed = info.placed;
|
active.placed = info.placed;
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
{
|
{
|
||||||
match (moved, has_region_node) {
|
let (counter, outcome) = match (moved, is_region_node) {
|
||||||
(true, true) => diag::bump(Counter::ReuseMoved),
|
(true, true) => (Counter::ReuseMoved, ReuseOutcome::Moved),
|
||||||
(true, false) => diag::bump(Counter::ReuseRemapped),
|
(true, false) => (Counter::ReuseRemapped, ReuseOutcome::Remapped),
|
||||||
(false, _) => diag::bump(Counter::ReuseExact),
|
(false, _) => (Counter::ReuseExact, ReuseOutcome::Exact),
|
||||||
}
|
};
|
||||||
diag::reuse(
|
diag::bump(counter);
|
||||||
id,
|
diag::reuse(id, outcome);
|
||||||
if moved {
|
|
||||||
if has_region_node {
|
|
||||||
ReuseOutcome::Moved
|
|
||||||
} else {
|
|
||||||
ReuseOutcome::Remapped
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ReuseOutcome::Exact
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +711,7 @@ impl UiRenderState {
|
|||||||
parent: Some(at.id),
|
parent: Some(at.id),
|
||||||
depth: at.depth + 1,
|
depth: at.depth + 1,
|
||||||
parent_move: at.move_idx,
|
parent_move: at.move_idx,
|
||||||
region_node: active.move_idx != active.parent_move,
|
region_node: active.is_region_node(),
|
||||||
mask: at.mask,
|
mask: at.mask,
|
||||||
rel_base,
|
rel_base,
|
||||||
region,
|
region,
|
||||||
@@ -755,16 +754,18 @@ impl UiRenderState {
|
|||||||
move_idx: active.move_idx,
|
move_idx: active.move_idx,
|
||||||
mask: active.mask,
|
mask: active.mask,
|
||||||
};
|
};
|
||||||
let children = active.children.len();
|
// Taken out and put back so that placing a child can borrow the state
|
||||||
for index in 0..children {
|
// it needs; nothing on that path reads this widget's own child list.
|
||||||
let child = self.active[&id].children[index];
|
let children = std::mem::take(&mut self.active.get_mut(&id).unwrap().children);
|
||||||
|
for &child in &children {
|
||||||
self.place_child(child, &at, rsc);
|
self.place_child(child, &at, rsc);
|
||||||
}
|
}
|
||||||
|
self.active.get_mut(&id).unwrap().children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reused subtree keeps its shape, so every widget in it moves by the
|
/// A reused subtree keeps its shape, so each widget in it keeps its depth
|
||||||
/// same amount -- and where the top of it did not move, none of it did,
|
/// under the top -- and where the top's own depth did not change, none of
|
||||||
/// which is what makes this free in the ordinary case.
|
/// them did, which is what makes this free in the ordinary case.
|
||||||
fn redepth(&mut self, id: WidgetId, depth: usize) {
|
fn redepth(&mut self, id: WidgetId, depth: usize) {
|
||||||
let Some(active) = self.active.get_mut(&id) else {
|
let Some(active) = self.active.get_mut(&id) else {
|
||||||
return;
|
return;
|
||||||
@@ -773,18 +774,21 @@ impl UiRenderState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
active.depth = depth;
|
active.depth = depth;
|
||||||
let children = active.children.len();
|
// Taken out and put back so the walk can borrow the state it needs;
|
||||||
for index in 0..children {
|
// it only ever goes further down, so it reads no list but its own.
|
||||||
let child = self.active[&id].children[index];
|
let children = std::mem::take(&mut active.children);
|
||||||
|
for &child in &children {
|
||||||
self.redepth(child, depth + 1);
|
self.redepth(child, depth + 1);
|
||||||
}
|
}
|
||||||
|
self.active.get_mut(&id).unwrap().children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hints_agree(id: WidgetId, size: Size, rsc: &dyn UiRsc) -> bool {
|
fn hints_agree(id: WidgetId, size: Size, rsc: &dyn UiRsc) -> bool {
|
||||||
let Some(widget) = rsc.widgets().get_dyn(id) else {
|
let Some(widget) = rsc.widgets().get_dyn(id) else {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
AXES.into_iter()
|
Axis::BOTH
|
||||||
|
.into_iter()
|
||||||
.all(|axis| widget.size_hint(axis).is_none_or(|hint| hint == size[axis]))
|
.all(|axis| widget.size_hint(axis).is_none_or(|hint| hint == size[axis]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1118,13 +1122,13 @@ impl UiRenderState {
|
|||||||
diag::bump(Counter::LocalRedraws);
|
diag::bump(Counter::LocalRedraws);
|
||||||
|
|
||||||
let old = self.remove(id, false, rsc);
|
let old = self.remove(id, false, rsc);
|
||||||
let answer = self.draw_inner(id, info, old, rsc);
|
let drawn = self.draw_inner(id, info, old, rsc);
|
||||||
let active = self.active.get_mut(&id).unwrap();
|
let active = self.active.get_mut(&id).unwrap();
|
||||||
// A wider contract does not invalidate the guarantee the parent kept.
|
// A wider contract does not invalidate the guarantee the parent kept.
|
||||||
// Retain that guarantee so widening and narrowing back do not churn it.
|
// Retain that guarantee so widening and narrowing back do not churn it.
|
||||||
if let Some((size, holds)) = was_answer
|
if let Some(was) = was_answer
|
||||||
&& answer.0 == size
|
&& drawn.answer.size == was.size
|
||||||
&& answer.1.covers(holds)
|
&& drawn.answer.holds.covers(was.holds)
|
||||||
{
|
{
|
||||||
active.answer = was_answer;
|
active.answer = was_answer;
|
||||||
}
|
}
|
||||||
@@ -1169,11 +1173,11 @@ impl UiRenderState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether what a widget reports along `axis` is inside the box it drew in.
|
|
||||||
/// Both are lengths of the window, so the comparison is in its pixels. A
|
|
||||||
/// share is a length only to whoever divides one, so it is not a claim about
|
|
||||||
/// this box and cannot exceed it.
|
|
||||||
impl Size {
|
impl Size {
|
||||||
|
/// Whether what a widget reports along `axis` is inside the box it drew
|
||||||
|
/// in. Both are lengths of the window, so the comparison is in its
|
||||||
|
/// pixels. A share is a length only to whoever divides one, so it is not
|
||||||
|
/// a claim about this box and cannot exceed it.
|
||||||
fn within_box(self, region: UiRegion, window: PxVec2, axis: Axis) -> bool {
|
fn within_box(self, region: UiRegion, window: PxVec2, axis: Axis) -> bool {
|
||||||
let len = self[axis];
|
let len = self[axis];
|
||||||
let window = window[axis];
|
let window = window[axis];
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ impl Widget for Scroll {
|
|||||||
let answer_len = painter
|
let answer_len = painter
|
||||||
.widget_at(&self.inner, PlaceDesc::WHOLE.fills())
|
.widget_at(&self.inner, PlaceDesc::WHOLE.fills())
|
||||||
.len(self.axis);
|
.len(self.axis);
|
||||||
let fixed = painter.to_px(answer_len.without_leftover(), self.axis);
|
let answer_px = painter.to_px(answer_len.without_leftover(), self.axis);
|
||||||
self.container_len = container_len;
|
self.container_len = container_len;
|
||||||
self.content_len = fixed.max(container_len);
|
self.content_len = answer_px.max(container_len);
|
||||||
|
|
||||||
if self.snap_end {
|
if self.snap_end {
|
||||||
self.amt = self.content_len - self.container_len;
|
self.amt = self.content_len - self.container_len;
|
||||||
@@ -31,10 +31,10 @@ impl Widget for Scroll {
|
|||||||
// the drawing holds for that length alone. One scrolled part way sits
|
// the drawing holds for that length alone. One scrolled part way sits
|
||||||
// where it is until the box shrinks past what is left of it. Kept to
|
// where it is until the box shrinks past what is left of it. Kept to
|
||||||
// the end, it moves with every length.
|
// the end, it moves with every length.
|
||||||
let fixed_len = answer_len.is_px();
|
let answer_is_px = answer_len.is_px();
|
||||||
if fixed_len && self.content_len <= self.container_len && align == AxisAlign::NEG {
|
if answer_is_px && self.content_len <= self.container_len && align == AxisAlign::NEG {
|
||||||
painter.holds(self.axis, fixed..=Px::MAX);
|
painter.holds(self.axis, answer_px..=Px::MAX);
|
||||||
} else if fixed_len && !self.snap_end {
|
} else if answer_is_px && !self.snap_end {
|
||||||
let left = self.content_len - self.amt;
|
let left = self.content_len - self.amt;
|
||||||
painter.holds(self.axis, Px::MIN..=left);
|
painter.holds(self.axis, Px::MIN..=left);
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-23
@@ -10,21 +10,18 @@ pub struct Span {
|
|||||||
impl Widget for Span {
|
impl Widget for Span {
|
||||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||||
let axis = self.dir.axis;
|
let axis = self.dir.axis;
|
||||||
// The row: this span's own box, as a length of the rel base its children
|
// The row this span lays its children out along, as a length of the
|
||||||
// are laid out against. Its start is nothing's business -- a slot is
|
// rel base they are laid out against. Where it starts is nothing's
|
||||||
// a length from it -- so what this reads is the length alone.
|
// business -- a slot is a length from there -- so what this reads is
|
||||||
let far = painter.region_len(axis);
|
// the length alone.
|
||||||
let along = |from: Len, to: Len| match self.dir.sign {
|
let row = painter.region_len(axis);
|
||||||
Sign::Pos => UiSpan::new(from, to),
|
|
||||||
Sign::Neg => UiSpan::new(far - to, far - from),
|
|
||||||
};
|
|
||||||
// A length for every child before their final slots are chosen: from
|
// A length for every child before their final slots are chosen: from
|
||||||
// a hint where one says, and from drawing otherwise. The rel base passes
|
// a hint where one says, and from drawing otherwise. The rel base passes
|
||||||
// through unchanged, so `rel(0.5)` is half the area this span was
|
// through unchanged, so `rel(0.5)` is half the area this span was
|
||||||
// given whatever else is in it and wherever this child sits among
|
// given whatever else is in it and wherever this child sits among
|
||||||
// them; what a drawn child is asked in is the room left from the
|
// them; what a drawn child is asked in is the room left from the
|
||||||
// cursor, because a text has to wrap at the width actually there.
|
// cursor, because a text has to wrap at the width actually there.
|
||||||
let mut cursor = Len::rel_min();
|
let mut cursor = Len::ZERO;
|
||||||
let mut lens = Vec::with_capacity(self.children.len());
|
let mut lens = Vec::with_capacity(self.children.len());
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
let len = match painter.size_hint(child, axis) {
|
let len = match painter.size_hint(child, axis) {
|
||||||
@@ -33,7 +30,7 @@ impl Widget for Span {
|
|||||||
// Across itself the child sits where its own alignment
|
// Across itself the child sits where its own alignment
|
||||||
// says, in the whole of the row: a span is what contains
|
// says, in the whole of the row: a span is what contains
|
||||||
// its children there, and nothing divides that axis.
|
// its children there, and nothing divides that axis.
|
||||||
let room = along(cursor, far).shifted_desc().on_axis(axis);
|
let room = self.slot(row, cursor, row).shifted_desc().on_axis(axis);
|
||||||
painter.widget_at(child, room).len(axis)
|
painter.widget_at(child, room).len(axis)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -55,7 +52,7 @@ impl Widget for Span {
|
|||||||
|
|
||||||
// What is left for the shares to divide: the row less everything
|
// What is left for the shares to divide: the row less everything
|
||||||
// fixed, as a length of the rel base rather than a number of pixels.
|
// fixed, as a length of the rel base rather than a number of pixels.
|
||||||
let room = far - total.without_leftover();
|
let room = row - total.without_leftover();
|
||||||
// Whether anything is left over is a question in pixels: `rel(0.5)`
|
// Whether anything is left over is a question in pixels: `rel(0.5)`
|
||||||
// beside 300 px is full at 600 and overfull at 400. Asked of `room`
|
// beside 300 px is full at 600 and overfull at 400. Asked of `room`
|
||||||
// itself, and answered back through the same expression, so the
|
// itself, and answered back through the same expression, so the
|
||||||
@@ -65,10 +62,10 @@ impl Widget for Span {
|
|||||||
// sign of `room.rel`, which `through` already reads. What the
|
// sign of `room.rel`, which `through` already reads. What the
|
||||||
// generated oracle checks is the consequence, since which children
|
// generated oracle checks is the consequence, since which children
|
||||||
// exist at all turns on this.
|
// exist at all turns on this.
|
||||||
let mut shares = false;
|
let any_leftover = total.leftover > Weight::ZERO;
|
||||||
if total.leftover > Weight::ZERO {
|
let has_room = any_leftover && painter.to_px(room, axis) > Px::ZERO;
|
||||||
shares = painter.to_px(room, axis) > Px::ZERO;
|
if any_leftover {
|
||||||
let holds = match shares {
|
let holds = match has_room {
|
||||||
true => Holds::from(Px::STEP..=Px::MAX),
|
true => Holds::from(Px::STEP..=Px::MAX),
|
||||||
false => Holds::from(Px::MIN..=Px::ZERO),
|
false => Holds::from(Px::MIN..=Px::ZERO),
|
||||||
};
|
};
|
||||||
@@ -87,24 +84,26 @@ impl Widget for Span {
|
|||||||
// than stepped from the last child: the share of the room is
|
// than stepped from the last child: the share of the room is
|
||||||
// rounded, and taking each end from the one before it would carry
|
// rounded, and taking each end from the one before it would carry
|
||||||
// every rounding along the row.
|
// every rounding along the row.
|
||||||
let mut fixed = Len::rel_min();
|
let mut fixed = Len::ZERO;
|
||||||
let mut taken = Weight::ZERO;
|
let mut taken = Weight::ZERO;
|
||||||
let mut ortho = LayoutLen::ZERO;
|
let mut ortho = LayoutLen::ZERO;
|
||||||
let reached = |fixed: Len, taken: Weight| match taken == Weight::ZERO {
|
// Nothing divides the room where no child asked for any of it, and a
|
||||||
true => fixed,
|
// ratio of a whole of nothing has no answer.
|
||||||
false => fixed + room.scale(Rel::ratio(taken, total.leftover)),
|
let reached = |fixed: Len, taken: Weight| match any_leftover {
|
||||||
|
false => fixed,
|
||||||
|
true => fixed + room.scale(Rel::ratio(taken, total.leftover)),
|
||||||
};
|
};
|
||||||
for (child, &len) in self.children.iter().zip(&lens) {
|
for (child, &len) in self.children.iter().zip(&lens) {
|
||||||
// A child asking for nothing but a part of what is left over,
|
// A child asking for nothing but a part of what is left over,
|
||||||
// when nothing is, is not drawn at all. One that also asked for
|
// when nothing is, is not drawn at all. One that also asked for
|
||||||
// pixels or a fraction keeps those and overflows.
|
// pixels or a fraction keeps those and overflows.
|
||||||
if len.is_only_leftover() && !shares {
|
if len.is_only_leftover() && !has_room {
|
||||||
painter.undraw(child);
|
painter.undraw(child);
|
||||||
fixed.px += self.gap;
|
fixed.px += self.gap;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let from = reached(fixed, taken);
|
let from = reached(fixed, taken);
|
||||||
if len.leftover > Weight::ZERO && shares {
|
if len.leftover > Weight::ZERO && has_room {
|
||||||
taken += len.leftover;
|
taken += len.leftover;
|
||||||
}
|
}
|
||||||
fixed += len.without_leftover();
|
fixed += len.without_leftover();
|
||||||
@@ -116,9 +115,9 @@ impl Widget for Span {
|
|||||||
// it, since a text wraps at the width it is actually given. A
|
// it, since a text wraps at the width it is actually given. A
|
||||||
// fixed child's slot is its own answer, so a drawing made in the
|
// fixed child's slot is its own answer, so a drawing made in the
|
||||||
// room is put there as it is, and one not made yet is made here.
|
// room is put there as it is, and one not made yet is made here.
|
||||||
let slot = along(from, to);
|
let slot = self.slot(row, from, to);
|
||||||
let mut place = slot.shifted_desc().fills().on_axis(axis);
|
let mut place = slot.shifted_desc().fills().on_axis(axis);
|
||||||
if len.leftover > Weight::ZERO && shares {
|
if len.leftover > Weight::ZERO && has_room {
|
||||||
place = place.rel_base(axis, slot.len());
|
place = place.rel_base(axis, slot.len());
|
||||||
}
|
}
|
||||||
let used = painter.place_at(child, place).len(!axis);
|
let used = painter.place_at(child, place).len(!axis);
|
||||||
@@ -152,6 +151,17 @@ impl Widget for Span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
|
/// The stretch of the row between two distances from where this span
|
||||||
|
/// starts laying children out, as a span of its own box. A negative
|
||||||
|
/// direction lays out from the far end, so the same two distances mirror
|
||||||
|
/// in a row `row` long.
|
||||||
|
fn slot(&self, row: Len, from: Len, to: Len) -> UiSpan {
|
||||||
|
match self.dir.sign {
|
||||||
|
Sign::Pos => from.to(to),
|
||||||
|
Sign::Neg => (row - to).to(row - from),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn empty(dir: Dir) -> Self {
|
pub fn empty(dir: Dir) -> Self {
|
||||||
Self {
|
Self {
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ fn moving_an_ordinary_subtree_remaps_its_mask() {
|
|||||||
let active = &h.render.active[&masked.id()];
|
let active = &h.render.active[&masked.id()];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
h.rsc.ui().masks[active.mask.idx()].region,
|
h.rsc.ui().masks[active.mask.idx()].region,
|
||||||
UiRegion::new(UiSpan::new(Len::px(150.0), Len::rel_max()), UiSpan::FULL,)
|
UiRegion::new(UiSpan::new(Len::px(150.0), Len::FULL), UiSpan::FULL,)
|
||||||
);
|
);
|
||||||
assert_corners!(h, inner, (150, 0), (400, 200));
|
assert_corners!(h, inner, (150, 0), (400, 200));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user