Make a declared length one that cannot carry a share
`declared_lens` filtered `leftover` out of both its sources and every consumer then re-dropped it, so the rule lived in two filters and a comment. A declaration is a `Len`: `LayoutLen::declared` states the rule once and both sources go through it, and `Declared` replaces the bare two-element array on `ActiveData` and in four signatures. The two sources stay one value deliberately. A rule decides the child's box; a hint only promises what it will report -- but `size_hint` is by contract an exact answer with no painter context, and `hints_agree` fails a widget that draws something else, so narrowing the box to a hint cannot change what is drawn. Every consumer asks about the length, never which said it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
55df32a33c
commit
2807a925af
5 files changed
+57
-34
No files matched your search
@@ -158,6 +158,13 @@ impl LayoutLen {
|
|||||||
self.leftover > Weight::ZERO && self.without_leftover() == Len::ZERO
|
self.leftover > Weight::ZERO && self.without_leftover() == Len::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This as a length of a box, where it is one. `leftover` is not: a
|
||||||
|
/// share of what is left over is a length only to whoever divides one,
|
||||||
|
/// so it passes up in the reported size instead and is resolved there.
|
||||||
|
pub fn declared(self) -> Option<Len> {
|
||||||
|
(self.leftover == Weight::ZERO).then(|| self.without_leftover())
|
||||||
|
}
|
||||||
|
|
||||||
/// What this takes whatever is left over: the reading of a length for
|
/// What this takes whatever is left over: the reading of a length for
|
||||||
/// anyone not dividing a box between siblings, where a share is a claim
|
/// anyone not dividing a box between siblings, where a share is a claim
|
||||||
/// on someone else's room rather than a length of its own.
|
/// on someone else's room rather than a length of its own.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
LayerId, LayoutHolds, LayoutLen, MaskIdx, MoveIdx, PlaceDesc, RegionAlign, RetainedPrimitive,
|
Declared, LayerId, LayoutHolds, MaskIdx, MoveIdx, PlaceDesc, RegionAlign, RetainedPrimitive,
|
||||||
Size, TextureHandle, UiRegion, UiVec2, WidgetId,
|
Size, TextureHandle, UiRegion, UiVec2, WidgetId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ pub struct ActiveData {
|
|||||||
/// The declared lengths whoever drew this widget resolved into its rel base.
|
/// The declared lengths whoever drew this widget resolved into its rel base.
|
||||||
/// A change to one moves a box this widget cannot fix by drawing again,
|
/// A change to one moves a box this widget cannot fix by drawing again,
|
||||||
/// and comparing them is what says so.
|
/// and comparing them is what says so.
|
||||||
pub declared: [Option<LayoutLen>; 2],
|
pub declared: Declared,
|
||||||
/// Its alignment when it was last drawn, which a change to the property
|
/// Its alignment when it was last drawn, which a change to the property
|
||||||
/// is found against.
|
/// is found against.
|
||||||
pub own_align: RegionAlign,
|
pub own_align: RegionAlign,
|
||||||
|
|||||||
+15
-19
@@ -1,7 +1,7 @@
|
|||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
use crate::layout_diagnostics::{self as diag, Counter};
|
use crate::layout_diagnostics::{self as diag, Counter};
|
||||||
use crate::{
|
use crate::{
|
||||||
Axis, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc, Px, PxVec2, RegionAlign, Rel,
|
Axis, Declared, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc, Px, PxVec2, RegionAlign, Rel,
|
||||||
RenderedText, RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData,
|
RenderedText, RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData,
|
||||||
TextureHandle, UiRegion, UiRenderState, UiRsc, UiVec2, Weight, WidgetId, Widgets,
|
TextureHandle, UiRegion, UiRenderState, UiRsc, UiVec2, Weight, WidgetId, Widgets,
|
||||||
render::{
|
render::{
|
||||||
@@ -292,7 +292,7 @@ impl<'a> Painter<'a> {
|
|||||||
/// it resolves into its rel base. Reading them depends on nothing -- the box
|
/// it resolves into its rel base. Reading them depends on nothing -- the box
|
||||||
/// that comes of them is kept on the child, and `redraw` compares it
|
/// that comes of them is kept on the child, and `redraw` compares it
|
||||||
/// there.
|
/// there.
|
||||||
fn declared_lens<W: ?Sized>(&self, id: &StrongWidget<W>) -> [Option<LayoutLen>; 2] {
|
fn declared_lens<W: ?Sized>(&self, id: &StrongWidget<W>) -> Declared {
|
||||||
self.rsc.widgets().declared_lens(id.id())
|
self.rsc.widgets().declared_lens(id.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,11 +599,11 @@ impl Painter<'_> {
|
|||||||
holds: LayoutHolds,
|
holds: LayoutHolds,
|
||||||
region: UiRegion,
|
region: UiRegion,
|
||||||
place: PlaceDesc,
|
place: PlaceDesc,
|
||||||
declared: [Option<LayoutLen>; 2],
|
declared: Declared,
|
||||||
) -> LayoutHolds {
|
) -> LayoutHolds {
|
||||||
let mut result = LayoutHolds::ANY;
|
let mut result = LayoutHolds::ANY;
|
||||||
for axis in AXES {
|
for axis in AXES {
|
||||||
let declared = declared[axis as usize];
|
let declared = declared[axis];
|
||||||
let holds = holds[axis];
|
let holds = holds[axis];
|
||||||
let result = &mut result[axis];
|
let result = &mut result[axis];
|
||||||
// Every read became pixels against the window, so a range on
|
// Every read became pixels against the window, so a range on
|
||||||
@@ -644,13 +644,11 @@ impl Painter<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widgets {
|
impl Widgets {
|
||||||
/// What a widget declares a length of its box to be. `leftover` is not
|
/// What a widget's box is where a rule or its own hint says so outright.
|
||||||
/// one: a share of what is left over is only a length to the widget
|
pub(crate) fn declared_lens(&self, id: WidgetId) -> Declared {
|
||||||
/// dividing one, so it passes up in the size instead.
|
|
||||||
pub(crate) fn declared_lens(&self, id: WidgetId) -> [Option<LayoutLen>; 2] {
|
|
||||||
let rules = self.size_rules(id);
|
let rules = self.size_rules(id);
|
||||||
let widget = self.get_dyn(id);
|
let widget = self.get_dyn(id);
|
||||||
AXES.map(|axis| {
|
Declared::per_axis(|axis| {
|
||||||
rules[axis].declared().or_else(|| {
|
rules[axis].declared().or_else(|| {
|
||||||
// A hint still narrows the box where no rule does, which is
|
// A hint still narrows the box where no rule does, which is
|
||||||
// how a widget with a natural pixel size -- an image, a gap
|
// how a widget with a natural pixel size -- an image, a gap
|
||||||
@@ -660,7 +658,7 @@ impl Widgets {
|
|||||||
// the box it was offered.
|
// the box it was offered.
|
||||||
widget
|
widget
|
||||||
.and_then(|widget| widget.size_hint(axis))
|
.and_then(|widget| widget.size_hint(axis))
|
||||||
.filter(|len| len.leftover == Weight::ZERO)
|
.and_then(LayoutLen::declared)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -674,7 +672,7 @@ impl LayoutLen {
|
|||||||
/// the rule already gave the region its length, and the rule's length is
|
/// the rule already gave the region its length, and the rule's length is
|
||||||
/// what the widget reports there. And an axis the parent decided from
|
/// what the widget reports there. And an axis the parent decided from
|
||||||
/// the answer is the answer already.
|
/// the answer is the answer already.
|
||||||
pub(crate) fn fills(self, declared: Option<LayoutLen>, decided: bool) -> bool {
|
pub(crate) fn fills(self, declared: Option<Len>, decided: bool) -> bool {
|
||||||
self.leftover != Weight::ZERO || declared.is_some() || decided
|
self.leftover != Weight::ZERO || declared.is_some() || decided
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -693,14 +691,13 @@ impl PlaceDesc {
|
|||||||
self,
|
self,
|
||||||
region: UiRegion,
|
region: UiRegion,
|
||||||
size: Size,
|
size: Size,
|
||||||
declared: [Option<LayoutLen>; 2],
|
declared: Declared,
|
||||||
align: RegionAlign,
|
align: RegionAlign,
|
||||||
) -> UiRegion {
|
) -> UiRegion {
|
||||||
let mut placed = region;
|
let mut placed = region;
|
||||||
for axis in AXES {
|
for axis in AXES {
|
||||||
let n = axis as usize;
|
|
||||||
let reported = size[axis];
|
let reported = size[axis];
|
||||||
if reported.fills(declared[n], self[axis].does_fill()) {
|
if reported.fills(declared[axis], self[axis].does_fill()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
placed[axis] = placed[axis].place(reported.without_leftover(), align[axis]);
|
placed[axis] = placed[axis].place(reported.without_leftover(), align[axis]);
|
||||||
@@ -722,22 +719,21 @@ impl PlaceDesc {
|
|||||||
self,
|
self,
|
||||||
own: UiRegion,
|
own: UiRegion,
|
||||||
parent_rel_base: UiVec2,
|
parent_rel_base: UiVec2,
|
||||||
declared: [Option<LayoutLen>; 2],
|
declared: Declared,
|
||||||
align: RegionAlign,
|
align: RegionAlign,
|
||||||
) -> (UiVec2, UiRegion) {
|
) -> (UiVec2, UiRegion) {
|
||||||
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 AXES {
|
||||||
let n = axis as usize;
|
|
||||||
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]);
|
||||||
let len = declared[n]
|
let len = declared[axis]
|
||||||
.map(|len| len.without_leftover().within_len(base))
|
.map(|len| len.within_len(base))
|
||||||
.unwrap_or(base);
|
.unwrap_or(base);
|
||||||
rel_base[axis] = len;
|
rel_base[axis] = len;
|
||||||
if declared[n].is_some() {
|
if declared[axis].is_some() {
|
||||||
region[axis] = given[axis].place(len, align[axis]);
|
region[axis] = given[axis].place(len, align[axis]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#[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, DrawLayers, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx, Moves,
|
ActiveData, Axis, Declared, DrawLayers, IdLike, LayoutHolds, LayoutLen, Len, MaskIdx, MoveIdx,
|
||||||
Painter, PixelRegion, PlaceDesc, PxVec2, Rel, Size, StrongWidget, UiRegion, UiRsc, UiSpan,
|
Moves, Painter, PixelRegion, PlaceDesc, PxVec2, Rel, Size, StrongWidget, UiRegion, UiRsc,
|
||||||
UiVec2, Weight, WidgetId, Widgets,
|
UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
||||||
util::{HashMap, Vec2},
|
util::{HashMap, Vec2},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -868,7 +868,7 @@ impl UiRenderState {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
size_deps: Vec::new(),
|
size_deps: Vec::new(),
|
||||||
move_idx: info.parent_move,
|
move_idx: info.parent_move,
|
||||||
declared: [None; 2],
|
declared: Declared::NONE,
|
||||||
own_align: rsc.widgets().alignment(id),
|
own_align: rsc.widgets().alignment(id),
|
||||||
parent_move: info.parent_move,
|
parent_move: info.parent_move,
|
||||||
mask: info.mask,
|
mask: info.mask,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::util::impl_axis_index;
|
use crate::util::impl_axis_index;
|
||||||
use crate::{LayoutLen, Weight};
|
use crate::{Axis, LayoutLen, Len};
|
||||||
|
|
||||||
/// What a widget's length on one axis is, as a rule its parent applies where
|
/// What a widget's length on one axis is, as a rule its parent applies where
|
||||||
/// it draws it rather than an answer the widget gives about itself.
|
/// it draws it rather than an answer the widget gives about itself.
|
||||||
@@ -20,14 +20,9 @@ pub enum SizeRule {
|
|||||||
|
|
||||||
impl SizeRule {
|
impl SizeRule {
|
||||||
/// The length this rule gives without the widget being drawn, if it can
|
/// The length this rule gives without the widget being drawn, if it can
|
||||||
/// give one. `leftover` is never among them: a share is a length only to
|
/// give one.
|
||||||
/// whoever divides one, so it passes up in the reported size instead and
|
pub fn declared(&self) -> Option<Len> {
|
||||||
/// is resolved there.
|
self.exact().and_then(LayoutLen::declared)
|
||||||
pub fn declared(&self) -> Option<LayoutLen> {
|
|
||||||
match self {
|
|
||||||
Self::Exact(len) if len.leftover == Weight::ZERO => Some(*len),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The length this rule gives outright, whatever the widget reports --
|
/// The length this rule gives outright, whatever the widget reports --
|
||||||
@@ -72,3 +67,28 @@ pub struct SizeRules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl_axis_index!(SizeRules => SizeRule);
|
impl_axis_index!(SizeRules => SizeRule);
|
||||||
|
|
||||||
|
/// What a widget's box is on each axis where something says so outright,
|
||||||
|
/// before it is drawn: a rule beside it, or a hint it gives about itself.
|
||||||
|
/// Whoever draws the widget resolves these against its rel base.
|
||||||
|
///
|
||||||
|
/// A [`Len`] rather than a [`LayoutLen`], because a share can never be one
|
||||||
|
/// -- see [`LayoutLen::declared`].
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Declared {
|
||||||
|
pub x: Option<Len>,
|
||||||
|
pub y: Option<Len>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Declared {
|
||||||
|
pub const NONE: Self = Self { x: None, y: None };
|
||||||
|
|
||||||
|
pub fn per_axis(f: impl Fn(Axis) -> Option<Len>) -> Self {
|
||||||
|
Self {
|
||||||
|
x: f(Axis::X),
|
||||||
|
y: f(Axis::Y),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_axis_index!(Declared => Option<Len>);
|
||||||
Reference in new issue
Block a user