Compare commits
2
Commits
c2b8bf83de
...
2dba90bd0f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dba90bd0f | ||
|
|
b295c8b97a |
No files matched your search
+86
-6
@@ -187,10 +187,20 @@ 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.resolve_rel_base(place.into());
|
let mut 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 align = self.rsc.widgets().alignment(id.id());
|
let align = self.rsc.widgets().alignment(id.id());
|
||||||
|
// A share fills what the pixels and fraction beside it leave of the
|
||||||
|
// box and overflows where they are longer, which is the rule a span
|
||||||
|
// follows with one child. Only the overflow is a box of the child's
|
||||||
|
// own: a share that fits is the box it was given, which is what this
|
||||||
|
// place already says.
|
||||||
|
for axis in Axis::BOTH {
|
||||||
|
if let Some(len) = self.share_past_the_offer(id.id(), place, align, axis) {
|
||||||
|
place[axis] = len.as_desc().fills();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let declared = self.declared_lens(id);
|
||||||
let (rel_base, region) =
|
let (rel_base, region) =
|
||||||
place.rel_base_and_region(self.region, self.rel_base, declared, align);
|
place.rel_base_and_region(self.region, self.rel_base, declared, align);
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
@@ -299,6 +309,45 @@ impl<'a> Painter<'a> {
|
|||||||
self.rsc.widgets().declared_lens(id.id())
|
self.rsc.widgets().declared_lens(id.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The box a child's own share asks for where that is longer than the box
|
||||||
|
/// `place` gives it, and nothing where the share fits.
|
||||||
|
///
|
||||||
|
/// A share is a length only to whoever divides one, and nothing divides a
|
||||||
|
/// box handed to one child: what is left of it after the pixels and the
|
||||||
|
/// fraction beside the share is what the share takes, so the length comes
|
||||||
|
/// to the whole box until those are longer than it and to them once they
|
||||||
|
/// are. Only that second case is a box this widget did not give, and the
|
||||||
|
/// crossing between them is a question in pixels, so this widget's drawing
|
||||||
|
/// holds for the windows on one side of it. Narrowed rather than stated,
|
||||||
|
/// because this widget may have read its own box as well, and a range it
|
||||||
|
/// pinned for that still holds.
|
||||||
|
fn share_past_the_offer(
|
||||||
|
&mut self,
|
||||||
|
id: WidgetId,
|
||||||
|
place: PlaceDesc,
|
||||||
|
align: RegionAlign,
|
||||||
|
axis: Axis,
|
||||||
|
) -> Option<Len> {
|
||||||
|
// A place that is the child's placement outright is a box its parent
|
||||||
|
// decided, and a parent that divides one has already given the share
|
||||||
|
// whatever it was owed. Only an offer -- a box with the answer still
|
||||||
|
// to be placed inside it -- is a box a share reads.
|
||||||
|
if place[axis].fills {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// A share with nothing beside it is the box whatever the box is, so
|
||||||
|
// there is no comparison to make and no range to keep for one.
|
||||||
|
let stated = self.rsc.widgets().exact_len(id, axis)?;
|
||||||
|
if stated.leftover == Weight::ZERO || stated.is_only_leftover() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let fixed = stated
|
||||||
|
.without_leftover()
|
||||||
|
.within_len(place.base(axis, self.rel_base));
|
||||||
|
let offer = place.of(self.region, align)[axis].len();
|
||||||
|
self.longer_than(fixed, offer, axis).then_some(fixed)
|
||||||
|
}
|
||||||
|
|
||||||
/// What a child says its length is without being drawn, if it can say,
|
/// What a child says its length is without being drawn, if it can say,
|
||||||
/// as the length its draw would report: a fraction in it is resolved
|
/// as the length its draw would report: a fraction in it is resolved
|
||||||
/// against this widget's rel base, which is the rel base a child asked with
|
/// against this widget's rel base, which is the rel base a child asked with
|
||||||
@@ -475,6 +524,40 @@ impl<'a> Painter<'a> {
|
|||||||
len.to_px(window)
|
len.to_px(window)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `len` is longer than `than`, kept as the windows that comparison
|
||||||
|
/// comes out the same way on: a drawing that took one of two lengths holds
|
||||||
|
/// where the same one is the longer, and nowhere else.
|
||||||
|
///
|
||||||
|
/// Which is longer is a question in pixels -- `rel(0.5)` is longer than 300
|
||||||
|
/// px at a box of 600 and shorter at 400 -- and it is asked of the
|
||||||
|
/// difference and answered back through that same difference, so the
|
||||||
|
/// boundary is the drawing's own rather than a second way of finding it.
|
||||||
|
/// Narrowed rather than stated, because whatever else this widget read
|
||||||
|
/// about the window is a reason its drawing holds where it does too.
|
||||||
|
///
|
||||||
|
/// This is the one operation a length that is the longer of two needs: the
|
||||||
|
/// room a container has left for the shares it divides, and a share that
|
||||||
|
/// overflows the box it was given because the pixels beside it are longer
|
||||||
|
/// than the box.
|
||||||
|
pub fn longer_than(&mut self, len: Len, than: Len, axis: Axis) -> bool {
|
||||||
|
let over = len - than;
|
||||||
|
let window = self.window[axis];
|
||||||
|
let longer = over.to_px(window) > Px::ZERO;
|
||||||
|
let side = match longer {
|
||||||
|
true => Px::STEP..=Px::MAX,
|
||||||
|
false => Px::MIN..=Px::ZERO,
|
||||||
|
};
|
||||||
|
let holds = Holds::from(side).through(over);
|
||||||
|
debug_assert!(
|
||||||
|
holds.contains(window),
|
||||||
|
"'{}' ({:?}) compared two lengths and kept a range without this window",
|
||||||
|
self.label(),
|
||||||
|
self.id
|
||||||
|
);
|
||||||
|
self.own[axis].window = self.own[axis].window.and(holds);
|
||||||
|
longer
|
||||||
|
}
|
||||||
|
|
||||||
/// The windows this drawing holds for, stated rather than taken: a
|
/// The windows this drawing holds for, stated rather than taken: a
|
||||||
/// container that branched on a length in pixels says which side of the
|
/// container that branched on a length in pixels says which side of the
|
||||||
/// boundary it was on, which is wider than the one window reading that
|
/// boundary it was on, which is wider than the one window reading that
|
||||||
@@ -725,10 +808,7 @@ impl PlaceDesc {
|
|||||||
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 Axis::BOTH {
|
for axis in Axis::BOTH {
|
||||||
let base = match self[axis].rel_base {
|
let base = self.base(axis, parent_rel_base);
|
||||||
RelBase::Len(len) => len,
|
|
||||||
RelBase::Inherit | RelBase::WithRegion => parent_rel_base[axis],
|
|
||||||
};
|
|
||||||
let len = declared[axis]
|
let len = declared[axis]
|
||||||
.map(|len| len.within_len(base))
|
.map(|len| len.within_len(base))
|
||||||
.unwrap_or(base);
|
.unwrap_or(base);
|
||||||
|
|||||||
+11
-1
@@ -1,5 +1,5 @@
|
|||||||
use crate::util::impl_axis_index;
|
use crate::util::impl_axis_index;
|
||||||
use crate::{Axis, AxisAlign, Len, PrimitiveHandle, RegionAlign, UiRegion, UiSpan};
|
use crate::{Axis, AxisAlign, Len, PrimitiveHandle, RegionAlign, UiRegion, UiSpan, UiVec2};
|
||||||
|
|
||||||
/// How a child's region along one axis comes from the region of the widget
|
/// How a child's region along one axis comes from the region of the widget
|
||||||
/// asking, and what its fractions are of.
|
/// asking, and what its fractions are of.
|
||||||
@@ -123,6 +123,16 @@ impl PlaceDesc {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What a child's fractions on one axis are of, as a length of the
|
||||||
|
/// window: a length this place names, or the rel base of the widget
|
||||||
|
/// giving it, which is `parent_rel_base`.
|
||||||
|
pub(super) fn base(&self, axis: Axis, parent_rel_base: UiVec2) -> Len {
|
||||||
|
match self[axis].rel_base {
|
||||||
|
RelBase::Len(len) => len,
|
||||||
|
RelBase::Inherit | RelBase::WithRegion => parent_rel_base[axis],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The box each axis names, in the coordinates `own` is in.
|
/// The box each axis names, in the coordinates `own` is in.
|
||||||
pub fn of(self, own: UiRegion, align: RegionAlign) -> UiRegion {
|
pub fn of(self, own: UiRegion, align: RegionAlign) -> UiRegion {
|
||||||
UiRegion::new(self.x.of(own.x, align.x), self.y.of(own.y, align.y))
|
UiRegion::new(self.x.of(own.x, align.x), self.y.of(own.y, align.y))
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 191 B |
+29
-3
@@ -190,6 +190,9 @@ pub enum Kind {
|
|||||||
color: usize,
|
color: usize,
|
||||||
alpha: u8,
|
alpha: u8,
|
||||||
},
|
},
|
||||||
|
/// The one leaf whose own length is a number of pixels it knows before it
|
||||||
|
/// is drawn, which is the hint a rule beside it has to win over.
|
||||||
|
Image,
|
||||||
/// Scrolling reads the pixel length of its box, which nothing else here
|
/// Scrolling reads the pixel length of its box, which nothing else here
|
||||||
/// does, and gives its child a box longer than its own.
|
/// does, and gives its child a box longer than its own.
|
||||||
Scroll {
|
Scroll {
|
||||||
@@ -443,9 +446,11 @@ impl Kind {
|
|||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
// The one leaf that reads the width it is given, then the one
|
// The one leaf that reads the width it is given, then the one
|
||||||
// that does not, then the one that measures nothing at all.
|
// that does not, then the one that measures nothing at all. A
|
||||||
|
// picture measures nothing either, but its length is its own, so
|
||||||
|
// it steps to the leaf that takes whatever it is given.
|
||||||
Kind::Wrapped => out.push(Kind::OneLine),
|
Kind::Wrapped => out.push(Kind::OneLine),
|
||||||
Kind::OneLine => out.push(Kind::Rect {
|
Kind::OneLine | Kind::Image => out.push(Kind::Rect {
|
||||||
color: 0,
|
color: 0,
|
||||||
alpha: 255,
|
alpha: 255,
|
||||||
}),
|
}),
|
||||||
@@ -627,9 +632,10 @@ struct Sow<'a> {
|
|||||||
|
|
||||||
impl Sow<'_> {
|
impl Sow<'_> {
|
||||||
fn leaf(&mut self) -> Plan {
|
fn leaf(&mut self) -> Plan {
|
||||||
Plan::bare(match self.rng.below(4) {
|
Plan::bare(match self.rng.below(5) {
|
||||||
0 => Kind::Wrapped,
|
0 => Kind::Wrapped,
|
||||||
1 => Kind::OneLine,
|
1 => Kind::OneLine,
|
||||||
|
2 => Kind::Image,
|
||||||
_ => {
|
_ => {
|
||||||
let color = self.rng.below(COLORS.len());
|
let color = self.rng.below(COLORS.len());
|
||||||
let alpha = (self.rng.below(5) * 63) as u8;
|
let alpha = (self.rng.below(5) * 63) as u8;
|
||||||
@@ -793,6 +799,7 @@ pub fn build<Rsc: UiRsc + 'static>(rsc: &mut Rsc, plan: &Plan) -> (StrongWidget,
|
|||||||
let mut build = Build {
|
let mut build = Build {
|
||||||
rsc,
|
rsc,
|
||||||
tree: Tree::default(),
|
tree: Tree::default(),
|
||||||
|
checkerboard: None,
|
||||||
};
|
};
|
||||||
let root = build.node(plan);
|
let root = build.node(plan);
|
||||||
(root, build.tree)
|
(root, build.tree)
|
||||||
@@ -801,6 +808,10 @@ pub fn build<Rsc: UiRsc + 'static>(rsc: &mut Rsc, plan: &Plan) -> (StrongWidget,
|
|||||||
struct Build<'a, Rsc> {
|
struct Build<'a, Rsc> {
|
||||||
rsc: &'a mut Rsc,
|
rsc: &'a mut Rsc,
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
|
/// The checkerboard, uploaded when the first image in this tree is built.
|
||||||
|
/// A handle is a reference to the texture, so every image after that one
|
||||||
|
/// clones this rather than uploading the same picture again.
|
||||||
|
checkerboard: Option<TextureHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Rsc: UiRsc + 'static> Build<'_, Rsc> {
|
impl<Rsc: UiRsc + 'static> Build<'_, Rsc> {
|
||||||
@@ -826,6 +837,20 @@ impl<Rsc: UiRsc + 'static> Build<'_, Rsc> {
|
|||||||
built
|
built
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The one picture the generated trees draw: a 64x64 checkerboard of purple
|
||||||
|
/// and black in 8 px cells. Committed rather than drawn here, so that one
|
||||||
|
/// seed is one tree whatever anything else does, and included rather than
|
||||||
|
/// opened, so that growing a tree does not depend on a working directory.
|
||||||
|
fn checkerboard(&mut self) -> TextureHandle {
|
||||||
|
if self.checkerboard.is_none() {
|
||||||
|
let image = include_bytes!("assets/checkerboard.png")
|
||||||
|
.get_image()
|
||||||
|
.expect("the checkerboard is committed beside this file");
|
||||||
|
self.checkerboard = Some(self.rsc.ui_mut().textures.add(image));
|
||||||
|
}
|
||||||
|
self.checkerboard.clone().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn kind(&mut self, kind: &Kind) -> StrongWidget {
|
fn kind(&mut self, kind: &Kind) -> StrongWidget {
|
||||||
let id: StrongWidget = match kind {
|
let id: StrongWidget = match kind {
|
||||||
Kind::Wrapped => wtext(WORDS).size(16).wrap(true).add_strong(self.rsc),
|
Kind::Wrapped => wtext(WORDS).size(16).wrap(true).add_strong(self.rsc),
|
||||||
@@ -834,6 +859,7 @@ impl<Rsc: UiRsc + 'static> Build<'_, Rsc> {
|
|||||||
.wrap(false)
|
.wrap(false)
|
||||||
.add_strong(self.rsc),
|
.add_strong(self.rsc),
|
||||||
Kind::Rect { color, alpha } => rect(COLORS[*color].alpha(*alpha)).add_strong(self.rsc),
|
Kind::Rect { color, alpha } => rect(COLORS[*color].alpha(*alpha)).add_strong(self.rsc),
|
||||||
|
Kind::Image => Image::new(self.checkerboard()).add_strong(self.rsc),
|
||||||
Kind::Scroll { axis, inner } => {
|
Kind::Scroll { axis, inner } => {
|
||||||
let inner = self.node(inner);
|
let inner = self.node(inner);
|
||||||
let id = Scroll::new(inner, *axis).add(self.rsc);
|
let id = Scroll::new(inner, *axis).add(self.rsc);
|
||||||
|
|||||||
@@ -16,6 +16,15 @@ impl Widget for Image {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
/// One texture already uploaded, for a caller holding its handle: [`image`]
|
||||||
|
/// uploads what it is given, and several widgets showing one picture want
|
||||||
|
/// one upload and one slot between them.
|
||||||
|
pub fn new(handle: TextureHandle) -> Self {
|
||||||
|
Self { handle }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn image<State: UiRsc>(image: impl LoadableImage) -> impl WidgetFn<State, Image> {
|
pub fn image<State: UiRsc>(image: impl LoadableImage) -> impl WidgetFn<State, Image> {
|
||||||
let image = image.get_image().expect("Failed to load image");
|
let image = image.get_image().expect("Failed to load image");
|
||||||
move |state| Image {
|
move |state| Image {
|
||||||
|
|||||||
@@ -52,25 +52,15 @@ 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 = row - total.without_leftover();
|
let all_fixed = total.without_leftover();
|
||||||
// Whether anything is left over is a question in pixels: `rel(0.5)`
|
let room = row - all_fixed;
|
||||||
// beside 300 px is full at 600 and overfull at 400. Asked of `room`
|
// The three cases a rounded division needed -- the fixed parts
|
||||||
// itself, and answered back through the same expression, so the
|
|
||||||
// boundary is the drawing's own and not a second way of finding it:
|
|
||||||
// the three cases a rounded division needed -- the fixed parts
|
|
||||||
// growing slower than the box, faster, or exactly with it -- are the
|
// growing slower than the box, faster, or exactly with it -- are the
|
||||||
// sign of `room.rel`, which `through` already reads. What the
|
// sign of `room.rel`, which the range `longer_than` keeps already
|
||||||
// generated oracle checks is the consequence, since which children
|
// reads. What the generated oracle checks is the consequence, since
|
||||||
// exist at all turns on this.
|
// which children exist at all turns on this.
|
||||||
let any_leftover = total.leftover > Weight::ZERO;
|
let any_leftover = total.leftover > Weight::ZERO;
|
||||||
let has_room = any_leftover && painter.to_px(room, axis) > Px::ZERO;
|
let has_room = any_leftover && painter.longer_than(row, all_fixed, axis);
|
||||||
if any_leftover {
|
|
||||||
let holds = match has_room {
|
|
||||||
true => Holds::from(Px::STEP..=Px::MAX),
|
|
||||||
false => Holds::from(Px::MIN..=Px::ZERO),
|
|
||||||
};
|
|
||||||
painter.window_holds(axis, holds.through(room));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Across itself a span is as long as its longest child -- unless a
|
// Across itself a span is as long as its longest child -- unless a
|
||||||
// rule beside it gives that length outright, and then reading them
|
// rule beside it gives that length outright, and then reading them
|
||||||
|
|||||||
@@ -260,6 +260,66 @@ fn a_share_rule_beats_the_widgets_own_pixel_size() {
|
|||||||
assert_eq!(asked.get(), 400.0, "the share is all of the box");
|
assert_eq!(asked.get(), 400.0, "the share is all of the box");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A share with pixels or a fraction beside it is the longer of the two: it
|
||||||
|
/// fills what they leave of the box and overflows the box where they are
|
||||||
|
/// longer than it. A parent that divides nothing gives the same length as a
|
||||||
|
/// span with one child, because in both there is nobody else to divide with.
|
||||||
|
#[test]
|
||||||
|
fn a_share_is_a_minimum_wherever_nothing_divides_it() {
|
||||||
|
let asked = |rule: LayoutLen, in_a_span: bool| {
|
||||||
|
let mut h = Harness::new((400, 200));
|
||||||
|
let probe = rect(Color::RED).add(&mut h.rsc);
|
||||||
|
h.set_len(probe, Axis::X, rule);
|
||||||
|
match in_a_span {
|
||||||
|
true => h.set_root((probe,).span(Dir::RIGHT)),
|
||||||
|
false => h.set_root(probe.wrapper()),
|
||||||
|
}
|
||||||
|
h.region(&probe).unwrap().size().x
|
||||||
|
};
|
||||||
|
for (rule, want) in [
|
||||||
|
(LayoutLen::LEFTOVER, 400),
|
||||||
|
(LayoutLen::px(50) + LayoutLen::LEFTOVER, 400),
|
||||||
|
(LayoutLen::px(500) + LayoutLen::LEFTOVER, 500),
|
||||||
|
(LayoutLen::rel(0.5) + LayoutLen::LEFTOVER, 400),
|
||||||
|
(LayoutLen::px(500), 500),
|
||||||
|
] {
|
||||||
|
let want = Px::from_int(want);
|
||||||
|
assert_eq!(asked(rule, false), want, "{rule:?} where nothing divides");
|
||||||
|
assert_eq!(asked(rule, true), want, "{rule:?} in a span");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Which of the two is longer is a question in pixels, so the box is decided
|
||||||
|
/// again wherever the answer can change: a window that crosses the length the
|
||||||
|
/// pixels ask for, and the rule itself crossing it while the window holds
|
||||||
|
/// still. The first is a range the drawing holds for; the second cannot be
|
||||||
|
/// seen in what the widget declares, since a share declares nothing either
|
||||||
|
/// way, so it reaches the parent as a length only the parent can resolve.
|
||||||
|
#[test]
|
||||||
|
fn a_share_past_the_box_is_decided_again_on_either_side_of_the_crossing() {
|
||||||
|
let mut h = Harness::new((400, 200));
|
||||||
|
let probe = rect(Color::RED).add(&mut h.rsc);
|
||||||
|
h.set_len(probe, Axis::X, LayoutLen::px(500) + LayoutLen::LEFTOVER);
|
||||||
|
h.set_root(probe.wrapper());
|
||||||
|
assert_eq!(h.region(&probe).unwrap().size().x, Px::from_int(500));
|
||||||
|
|
||||||
|
h.resize((900, 200));
|
||||||
|
h.frame();
|
||||||
|
assert_eq!(h.region(&probe).unwrap().size().x, Px::from_int(900));
|
||||||
|
|
||||||
|
h.resize((400, 200));
|
||||||
|
h.frame();
|
||||||
|
assert_eq!(h.region(&probe).unwrap().size().x, Px::from_int(500));
|
||||||
|
|
||||||
|
h.set_len(probe, Axis::X, LayoutLen::px(50) + LayoutLen::LEFTOVER);
|
||||||
|
h.frame();
|
||||||
|
assert_eq!(h.region(&probe).unwrap().size().x, Px::from_int(400));
|
||||||
|
|
||||||
|
h.set_len(probe, Axis::X, LayoutLen::px(500) + LayoutLen::LEFTOVER);
|
||||||
|
h.frame();
|
||||||
|
assert_eq!(h.region(&probe).unwrap().size().x, Px::from_int(500));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_child_drawn_twice_moves_once() {
|
fn a_child_drawn_twice_moves_once() {
|
||||||
let mut h = Harness::new((400, 200));
|
let mut h = Harness::new((400, 200));
|
||||||
|
|||||||
+20
-3
@@ -5,7 +5,9 @@
|
|||||||
//! and the oracle another. And reducing a plan has to end, or a shrinker
|
//! and the oracle another. And reducing a plan has to end, or a shrinker
|
||||||
//! searching for the smallest counterexample never returns.
|
//! searching for the smallest counterexample never returns.
|
||||||
|
|
||||||
use iris::random::{Edits, Kind, Plan, Rng, SpanEdit, plan};
|
use iris::harness::Harness;
|
||||||
|
use iris::prelude::*;
|
||||||
|
use iris::random::{Edits, Kind, Plan, Rng, SpanEdit, grow, plan};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
fn some_edits(seed: u64, of: &Plan) -> Edits {
|
fn some_edits(seed: u64, of: &Plan) -> Edits {
|
||||||
@@ -67,8 +69,6 @@ fn some_edits(seed: u64, of: &Plan) -> Edits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use iris::prelude::*;
|
|
||||||
|
|
||||||
/// The two routes to an edited tree are one tree. `plan` resolves edits out
|
/// The two routes to an edited tree are one tree. `plan` resolves edits out
|
||||||
/// of the random stream as it draws; `edited` puts them on a tree that
|
/// of the random stream as it draws; `edited` puts them on a tree that
|
||||||
/// already exists, which is the only route a shrunk plan has, since no seed
|
/// already exists, which is the only route a shrunk plan has, since no seed
|
||||||
@@ -137,3 +137,20 @@ fn reducing_a_plan_all_the_way_ends() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Every image in a tree is the same picture, and a handle is a reference to
|
||||||
|
/// the texture rather than a copy of it, so one upload and one slot serve all
|
||||||
|
/// of them however many a tree grows -- and the trees are grown in hundreds.
|
||||||
|
#[test]
|
||||||
|
fn a_tree_of_images_uploads_one_texture() {
|
||||||
|
let mut images = 0;
|
||||||
|
let mut tree = plan(1, 4, &Edits::default());
|
||||||
|
tree.walk_mut(&mut |p| images += (p.kind == Kind::Image) as usize);
|
||||||
|
assert!(images > 1, "a tree of {images} images tests nothing");
|
||||||
|
|
||||||
|
let mut h = Harness::new((900, 1200));
|
||||||
|
let (root, _) = grow(&mut h.rsc, 1, 4, &Edits::default());
|
||||||
|
h.state.root = Some(root);
|
||||||
|
h.frame();
|
||||||
|
assert_eq!(h.rsc.ui().textures.count(), 1);
|
||||||
|
}
|
||||||
@@ -9,6 +9,12 @@
|
|||||||
//! reached through a region node's own entry rather than through the offer
|
//! reached through a region node's own entry rather than through the offer
|
||||||
//! that node was given. The last is a wrapping text handed back the width
|
//! that node was given. The last is a wrapping text handed back the width
|
||||||
//! it measured, rounded to a step below the line it measured there.
|
//! it measured, rounded to a step below the line it measured there.
|
||||||
|
//!
|
||||||
|
//! Each says which seed it was shrunk from, of the generator as it stood when
|
||||||
|
//! it was found. Those numbers no longer grow those trees -- a seed names one
|
||||||
|
//! only while the generator draws the same things in the same order, and the
|
||||||
|
//! leaves have grown an image since -- so what is written out below is the
|
||||||
|
//! record of the case, and the seed is where it came from.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
|||||||
+8
-4
@@ -25,10 +25,14 @@ fn depth() -> usize {
|
|||||||
env("IRIS_GENERATED_DEPTH", 4)
|
env("IRIS_GENERATED_DEPTH", 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The seeds the ordinary tests take. Eight that have never failed; 86,
|
/// The seeds the ordinary tests take: a corpus rather than a set of
|
||||||
/// which a `Scroll` fixed point once settled differently on; and 20, which
|
/// regression cases, since a seed names a tree only for as long as the
|
||||||
/// caught a locally redrawn widget being placed twice in the box its parent
|
/// generator draws the same things in the same order. Adding images to the
|
||||||
/// had already placed it in.
|
/// leaves moved every one of them, so 20 and 86 -- which once caught a widget
|
||||||
|
/// placed twice in a box its parent had already placed it in, and a `Scroll`
|
||||||
|
/// fixed point settling differently -- no longer grow those trees. Both
|
||||||
|
/// defects are pinned by the shrunk fixtures in `cases/unsettled.rs`, which
|
||||||
|
/// are trees rather than numbers.
|
||||||
const SEEDS: [u64; 10] = [1, 2, 3, 5, 8, 10, 13, 20, 86, 98];
|
const SEEDS: [u64; 10] = [1, 2, 3, 5, 8, 10, 13, 20, 86, 98];
|
||||||
|
|
||||||
fn check(seed: u64, depth: usize, case: Case) {
|
fn check(seed: u64, depth: usize, case: Case) {
|
||||||
|
|||||||
Reference in new issue
Block a user