Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
950960cccd | ||
|
|
1c80051d57 |
No files matched your search
+19
-41
@@ -139,30 +139,20 @@ impl<'a> Painter<'a> {
|
||||
id: &'s StrongWidget<W>,
|
||||
region: UiRegion,
|
||||
) -> DrawResult<'s, 'a, W> {
|
||||
self.widget_at(id, region, region.size(), [false; 2])
|
||||
self.widget_at(id, region, [false; 2])
|
||||
}
|
||||
|
||||
/// Draws a widget in `region`, saying what the answer means.
|
||||
///
|
||||
/// `reports_of` is what a fraction the child reports is a fraction of, as
|
||||
/// lengths of this widget's own box. It is the box the child was given
|
||||
/// wherever that box is the child's whole area -- a pad's inset, a stack
|
||||
/// child, a scroll's content -- and a span passes its own extent along
|
||||
/// the row instead: it offers each child the room left from its cursor,
|
||||
/// because a text has to wrap at the width actually there, while
|
||||
/// `rel(0.5)` still means half the span wherever the child sits in it.
|
||||
///
|
||||
/// A `decided` axis is one where this box was chosen from the widget's
|
||||
/// own answer. On those the answer is not placed inside the box again: it
|
||||
/// already is the box, and a fraction taken of it a second time would
|
||||
/// shrink it twice. A container uses that where it hands back exactly
|
||||
/// what a child asked for -- a span placing a child at the length it
|
||||
/// reported, a scroll giving its content the content's own length.
|
||||
/// Draws a widget in a box this widget chose from the widget's own
|
||||
/// answer along the `decided` axes. On those the answer is not placed
|
||||
/// inside the box again: it already is the box, and a fraction the
|
||||
/// widget reported, taken of this box a second time, would shrink it
|
||||
/// twice. A container uses this where it hands back exactly what a child
|
||||
/// asked for -- a span placing a child at the length it reported, a
|
||||
/// scroll giving its content the content's own length.
|
||||
pub fn widget_at<'s, W: ?Sized>(
|
||||
&'s mut self,
|
||||
id: &'s StrongWidget<W>,
|
||||
region: UiRegion,
|
||||
reports_of: UiVec2,
|
||||
decided: [bool; 2],
|
||||
) -> DrawResult<'s, 'a, W> {
|
||||
let region_node = self.rsc.widgets().is_region_node(id.id());
|
||||
@@ -232,10 +222,18 @@ impl<'a> Painter<'a> {
|
||||
for (axis, under) in AXES.into_iter().zip(self.under.iter_mut()) {
|
||||
*under = under.and(holds[axis as usize].through(local.axis(axis).len()));
|
||||
}
|
||||
// The answer as it was given. A fraction in it is a fraction of this
|
||||
// widget's box, which is the same thing a rule beside the child
|
||||
// means and the same thing for every box this widget hands out: a
|
||||
// span offers each child the room left from its cursor, because a
|
||||
// text has to wrap at the width actually there, and `rel(0.5)` is
|
||||
// still half the span. Padding is outside what it pads for the same
|
||||
// reason -- inset the fraction and a child's `rel` would mean the
|
||||
// inner box while its `px` meant the outer one.
|
||||
DrawResult {
|
||||
child: id,
|
||||
painter: self,
|
||||
size: in_parent_frame(size, reports_of, declared),
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,14 +267,12 @@ impl<'a> Painter<'a> {
|
||||
|
||||
/// A child's length in the box it is about to be offered, if it can be
|
||||
/// had without drawing it: from its hint, or from a drawing it already
|
||||
/// has that holds for that box. `reports_of` is what a fraction in the
|
||||
/// answer is a fraction of, as it is for [`Self::widget_at`].
|
||||
/// has that holds for that box.
|
||||
pub fn known_len<W: ?Sized>(
|
||||
&mut self,
|
||||
child: &StrongWidget<W>,
|
||||
axis: Axis,
|
||||
region: UiRegion,
|
||||
reports_of: UiVec2,
|
||||
) -> Option<LayoutLen> {
|
||||
let declared = self.declared_lens(child);
|
||||
let align = self.rsc.widgets().alignment(child.id());
|
||||
@@ -302,7 +298,7 @@ impl<'a> Painter<'a> {
|
||||
for (axis, under) in AXES.into_iter().zip(self.under.iter_mut()) {
|
||||
*under = under.and(holds[axis as usize].through(local.axis(axis).len()));
|
||||
}
|
||||
Some(in_parent_frame(size, reports_of, declared).axis(axis))
|
||||
Some(size.axis(axis))
|
||||
}
|
||||
|
||||
/// Whether this is the first box a child is asked about in during a draw
|
||||
@@ -521,24 +517,6 @@ impl PrimitiveLike for &TextureHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/// A child's answer as lengths of the parent's own box. A widget reports a
|
||||
/// fraction, and `reports_of` is the length that fraction is of: the box the
|
||||
/// child was given wherever that is the child's whole area, and the parent's
|
||||
/// own extent wherever the box is a positional remainder, as a span's is
|
||||
/// after an earlier child. Pixels come through untouched either way, being
|
||||
/// that many pixels wherever they end up. A declared axis is already the
|
||||
/// parent's: it resolved the rule in its own box, and the rule is what the
|
||||
/// report says.
|
||||
fn in_parent_frame(size: Size, reports_of: UiVec2, declared: [Option<LayoutLen>; 2]) -> Size {
|
||||
let mut size = size;
|
||||
for (axis, declared) in AXES.into_iter().zip(declared) {
|
||||
if declared.is_none() {
|
||||
*size.axis_mut(axis) = size.axis(axis).within_len(reports_of.axis(axis));
|
||||
}
|
||||
}
|
||||
size
|
||||
}
|
||||
|
||||
/// What a widget declares a length of its box to be. `leftover` is not one: a
|
||||
/// share of what is left over is only a length to the widget dividing one,
|
||||
/// so it passes up in the size instead.
|
||||
|
||||
@@ -9,10 +9,10 @@ impl Widget for Pad {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
// The inner's own alignment, not the near edge. This reports the
|
||||
// inner's size plus the padding, so where the box is that answer the
|
||||
// inset box is exactly the inner and alignment has no room to move
|
||||
// it; where the box is bigger -- a share of a row, a rule over this
|
||||
// widget -- the slack is the inner's to sit in, and forcing the near
|
||||
// edge pinned it to a corner it had not asked for.
|
||||
// inner is exactly what it asked for and alignment has no room to
|
||||
// move it; where the box is bigger -- a share of a row, a rule over
|
||||
// this widget -- the slack is the inner's to sit in, and forcing the
|
||||
// near edge pinned it to a corner it had not asked for.
|
||||
let inner = painter
|
||||
.widget_within(&self.inner, self.padding.region())
|
||||
.size();
|
||||
@@ -29,6 +29,45 @@ impl Widget for Pad {
|
||||
}
|
||||
}
|
||||
|
||||
/// Room taken off the inside rather than added round the outside: the child
|
||||
/// draws in what is left once both edges are gone, and this widget is
|
||||
/// exactly as long as the box it was given.
|
||||
///
|
||||
/// So `rel(1.0)` under an [`Inset`] is the room inside it, where the same
|
||||
/// rule under a [`Pad`] is the pad's whole box and overflows it by the
|
||||
/// padding. Both are wanted; which one a layout means is which widget it
|
||||
/// reaches for.
|
||||
pub struct Inset {
|
||||
pub padding: Padding,
|
||||
pub inner: StrongWidget,
|
||||
}
|
||||
|
||||
impl Widget for Inset {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
let region = self.padding.inset_region();
|
||||
let inner = painter.widget_within(&self.inner, region).size();
|
||||
// What a fraction the child reported is a fraction of is this
|
||||
// widget's to say, and it says the room inside: the child asked for
|
||||
// a part of the box it drew in, and that box is shorter than this
|
||||
// one by both edges. Then the edges go back on, so this widget is
|
||||
// its child and the room taken off around it.
|
||||
let (x, y) = (
|
||||
inner.x.within_len(region.x.len()),
|
||||
inner.y.within_len(region.y.len()),
|
||||
);
|
||||
Size {
|
||||
x: LayoutLen {
|
||||
px: x.px + self.padding.left + self.padding.right,
|
||||
..x
|
||||
},
|
||||
y: LayoutLen {
|
||||
px: y.px + self.padding.top + self.padding.bottom,
|
||||
..y
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Padding {
|
||||
pub left: Px,
|
||||
pub right: Px,
|
||||
@@ -53,7 +92,24 @@ impl Padding {
|
||||
bottom: amt,
|
||||
}
|
||||
}
|
||||
/// The box a [`Pad`] gives its child: as long as the pad's own, moved in
|
||||
/// by the near edge. Padding is outside what it pads, so a fraction the
|
||||
/// child asks for is a fraction of the same length whether a rule beside
|
||||
/// it states one or it reports one, and its pixels are the same pixels.
|
||||
/// Shrinking the box instead would make `rel` mean the inner box while
|
||||
/// `px` meant the outer one. [`Inset`] is the widget that shrinks.
|
||||
pub fn region(&self) -> UiRegion {
|
||||
let mut region = UiRegion::FULL;
|
||||
region.x.start.px += self.left;
|
||||
region.y.start.px += self.top;
|
||||
region.x.end.px += self.left;
|
||||
region.y.end.px += self.top;
|
||||
region
|
||||
}
|
||||
|
||||
/// The box an [`Inset`] gives its child: shorter than its own by both
|
||||
/// edges, so what the child fills is the room left inside.
|
||||
pub fn inset_region(&self) -> UiRegion {
|
||||
let mut region = UiRegion::FULL;
|
||||
region.x.start.px += self.left;
|
||||
region.y.start.px += self.top;
|
||||
|
||||
@@ -14,8 +14,7 @@ impl Widget for Scroll {
|
||||
let container_len = painter.px_len(self.axis);
|
||||
// Draw in the whole container only when its scrolling-axis length is
|
||||
// not already known, then draw it at the scrolled offset.
|
||||
let whole = UiRegion::FULL;
|
||||
let answer_len = match painter.known_len(&self.inner, self.axis, whole, whole.size()) {
|
||||
let answer_len = match painter.known_len(&self.inner, self.axis, UiRegion::FULL) {
|
||||
Some(len) => len,
|
||||
None => painter.widget(&self.inner).size().axis(self.axis),
|
||||
};
|
||||
@@ -64,7 +63,7 @@ impl Widget for Scroll {
|
||||
region = region.offset(offset);
|
||||
region.axis_mut(self.axis).end = region.axis(self.axis).start.offset(self.content_len);
|
||||
}
|
||||
painter.widget_at(&self.inner, region, region.size(), [true; 2]);
|
||||
painter.widget_at(&self.inner, region, [true; 2]);
|
||||
// What it occupies is its box, on both axes: it clips its content to
|
||||
// that box, so it can neither take less of one nor honestly ask for
|
||||
// more. The content's length is what it scrolls through, not what it
|
||||
|
||||
@@ -21,14 +21,12 @@ impl Widget for Span {
|
||||
}
|
||||
let region = UiRegion::from_axis(axis, span, UiSpan::FULL);
|
||||
// Offered the room left from the cursor, because a text has to
|
||||
// wrap at the width actually there, but reporting a fraction of
|
||||
// the whole row: `rel(0.5)` is half the span whatever else is in
|
||||
// it and wherever this child sits among them.
|
||||
let len = match painter.known_len(child, axis, region, UiVec2::FULL_SIZE) {
|
||||
// wrap at the width actually there, while what it reports is a
|
||||
// fraction of the whole row: `rel(0.5)` is half the span
|
||||
// whatever else is in it and wherever this child sits.
|
||||
let len = match painter.known_len(child, axis, region) {
|
||||
Some(len) => len,
|
||||
None => painter
|
||||
.widget_at(child, region, UiVec2::FULL_SIZE, [false; 2])
|
||||
.len(axis),
|
||||
None => painter.widget_at(child, region, [false; 2]).len(axis),
|
||||
};
|
||||
cursor.px += len.px + self.gap;
|
||||
cursor.rel += len.rel;
|
||||
@@ -128,12 +126,7 @@ impl Widget for Span {
|
||||
// Along the row this box is the child's own answer, so the answer
|
||||
// is not placed in it again; across it the child sits where its
|
||||
// alignment says.
|
||||
let placed = painter.widget_at(
|
||||
child,
|
||||
region,
|
||||
UiVec2::FULL_SIZE,
|
||||
[axis == Axis::X, axis == Axis::Y],
|
||||
);
|
||||
let placed = painter.widget_at(child, region, [axis == Axis::X, axis == Axis::Y]);
|
||||
if shrinks {
|
||||
let used = placed.len(!axis);
|
||||
// Choosing between a fixed and a relative length from the
|
||||
|
||||
@@ -35,7 +35,7 @@ impl Widget for Stack {
|
||||
// child is handed a box that owes nothing to its own answer, and
|
||||
// where it sits in one bigger than itself is its own business.
|
||||
match sizing == Some(i) {
|
||||
true => painter.widget_at(child, region, region.size(), [true; 2]),
|
||||
true => painter.widget_at(child, region, [true; 2]),
|
||||
false => painter.widget_within(child, region),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,6 +12,16 @@ widget_trait! {
|
||||
}
|
||||
}
|
||||
|
||||
fn inset(self, padding: impl Into<Padding>) -> impl WidgetFn<Rsc, Inset> {
|
||||
// Room taken off the inside, where `pad` adds it round the outside:
|
||||
// this is as long as the box it is given and the child fills what is
|
||||
// left of it.
|
||||
|state| Inset {
|
||||
padding: padding.into(),
|
||||
inner: self.add_strong(state),
|
||||
}
|
||||
}
|
||||
|
||||
fn align(self, align: impl Into<Align>) -> impl WidgetIdFn<Rsc, WL::Widget> {
|
||||
// An axis left out keeps whatever it had, which is centered unless
|
||||
// something else set it.
|
||||
|
||||
+25
-8
@@ -82,11 +82,13 @@ fn a_text_in_a_span_wraps_at_the_room_left_rather_than_the_whole_row() {
|
||||
assert!(crowded > whole_row, "{crowded} against {whole_row}");
|
||||
}
|
||||
|
||||
/// The same reading through a pad: its inset is the whole box less the
|
||||
/// padding, so half of the inset plus the padding is half the box plus one
|
||||
/// padding, not two.
|
||||
/// Padding is outside what it pads, so a fraction under one is a fraction of
|
||||
/// the box the padding is measured from: half of a 400 px row is 200, and
|
||||
/// the pad is that plus both edges. Inset it instead and `rel` would mean the
|
||||
/// inner box while `px` meant the outer one, which is the one thing a length
|
||||
/// may not do.
|
||||
#[test]
|
||||
fn a_pad_reports_a_fraction_of_its_inset_as_a_fraction_of_its_box() {
|
||||
fn a_pad_is_outside_the_fraction_its_child_asked_for() {
|
||||
let mut h = Harness::new((400, 100));
|
||||
let inner = rect(Color::GREEN).width(rel(0.5)).add(&mut h.rsc);
|
||||
let padded = (inner,).span(Dir::RIGHT).pad(10).add(&mut h.rsc);
|
||||
@@ -95,8 +97,23 @@ fn a_pad_reports_a_fraction_of_its_inset_as_a_fraction_of_its_box() {
|
||||
// placed inside it by its own alignment, which is not what is under test.
|
||||
h.set_root((padded, tail).span(Dir::RIGHT).width(rel(1.0)));
|
||||
|
||||
assert_corners!(h, padded, (0, 0), (210, 100));
|
||||
assert_corners!(h, tail, (210, 0), (310, 100));
|
||||
assert_corners!(h, padded, (0, 0), (220, 100));
|
||||
assert_corners!(h, tail, (220, 0), (320, 100));
|
||||
}
|
||||
|
||||
/// The other half of the pair: an inset takes its room off the inside, so it
|
||||
/// is exactly as long as the box it was given and the fraction its child
|
||||
/// asked for is a fraction of what is left inside. Half of the 380 left in a
|
||||
/// 400 px row is 190, and the inset is the whole 400.
|
||||
#[test]
|
||||
fn an_inset_is_inside_the_fraction_its_child_asked_for() {
|
||||
let mut h = Harness::new((400, 100));
|
||||
let inner = rect(Color::GREEN).width(rel(0.5)).add(&mut h.rsc);
|
||||
let inset = (inner,).span(Dir::RIGHT).inset(10).add(&mut h.rsc);
|
||||
h.set_root((inset,).span(Dir::RIGHT).width(rel(1.0)));
|
||||
|
||||
assert_corners!(h, inset, (0, 0), (200, 100));
|
||||
assert_corners!(h, inner, (10, 0), (200, 100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -237,7 +254,7 @@ fn a_moved_subtree_takes_its_children_with_it() {
|
||||
let mut h = Harness::new((400, 400));
|
||||
let first = rect(Color::RED).height(40).add(&mut h.rsc);
|
||||
let inner = rect(Color::BLUE).add(&mut h.rsc);
|
||||
let row = inner.pad(10).height(40).region_node().add(&mut h.rsc);
|
||||
let row = inner.inset(10).height(40).region_node().add(&mut h.rsc);
|
||||
// 80 of fixed rows in a 400 window, so the span takes 80 and sits in the
|
||||
// middle of what it was given.
|
||||
h.set_root((first, row).span(Dir::DOWN));
|
||||
@@ -280,7 +297,7 @@ fn a_box_with_a_fixed_length_can_be_stretched_on_its_other_axis() {
|
||||
// impossible to take out of: recovering a fraction of a box needs a
|
||||
// relative extent, and it has none on that axis.
|
||||
let inner = rect(Color::BLUE).add(&mut h.rsc);
|
||||
let row = inner.pad(10).height(40).add(&mut h.rsc);
|
||||
let row = inner.inset(10).height(40).add(&mut h.rsc);
|
||||
let filler = rect(Color::GREEN).add(&mut h.rsc);
|
||||
// This column is an item in a row, so it takes the width left for it
|
||||
// rather than asking for a full row-width in addition to the bar.
|
||||
|
||||
@@ -462,7 +462,7 @@ fn a_change_two_levels_under_its_reader_still_reaches_it() {
|
||||
let (leaf, _) = counted(&mut h, Size::px((100, 100).into()), true);
|
||||
let padded = leaf.pad(10).add(&mut h.rsc);
|
||||
let below = rect(Color::RED).add(&mut h.rsc);
|
||||
h.set_root((padded, below).span(Dir::DOWN).pad(12));
|
||||
h.set_root((padded, below).span(Dir::DOWN).inset(12));
|
||||
assert_corners!(h, below, (12, 132), (388, 388));
|
||||
|
||||
h.rsc[leaf].size = Size::px((100, 200).into());
|
||||
|
||||
Reference in new issue
Block a user