Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b181bc8af | ||
|
|
f6242aa33c | ||
|
|
a888717ee9 | ||
|
|
e8a5792dcb |
No files matched your search
@@ -73,6 +73,21 @@ impl Holds {
|
||||
}
|
||||
}
|
||||
|
||||
/// What a box has to be for a part of it, this many pixels shorter, to
|
||||
/// stay in this range: the range moved by that much, an end that was
|
||||
/// unbounded staying so.
|
||||
pub const fn longer_by(self, px: Px) -> Self {
|
||||
let lo = match self.lo.raw() == Px::MIN.raw() {
|
||||
true => self.lo,
|
||||
false => self.lo.add(px),
|
||||
};
|
||||
let hi = match self.hi.raw() == Px::MAX.raw() {
|
||||
true => self.hi,
|
||||
false => self.hi.add(px),
|
||||
};
|
||||
Self { lo, hi }
|
||||
}
|
||||
|
||||
const fn raws(lo: i64, hi: i64) -> Self {
|
||||
Self {
|
||||
lo: Px::from_raw(narrow(lo)),
|
||||
|
||||
+49
-28
@@ -259,17 +259,24 @@ impl<'a> Painter<'a> {
|
||||
declared_lens(self.rsc.widgets(), id.id())
|
||||
}
|
||||
|
||||
/// What a child says its length is without being drawn, if it can say.
|
||||
/// Asking counts as reading its size.
|
||||
/// 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
|
||||
/// against this widget's frame, which is the frame a child asked with
|
||||
/// nothing narrowed gets. Asking counts as reading its size.
|
||||
pub fn size_hint<W: ?Sized>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Option<LayoutLen> {
|
||||
let widgets = self.rsc.widgets();
|
||||
// A rule is the answer where there is one: it wins over whatever the
|
||||
// widget would draw, so it has to win over what the widget says too.
|
||||
let hint = widgets.size_rules(id.id()).axis(axis).exact().or_else(|| {
|
||||
widgets
|
||||
.get_dyn(id.id())
|
||||
.and_then(|widget| widget.size_hint(axis))
|
||||
});
|
||||
let hint = widgets
|
||||
.size_rules(id.id())
|
||||
.axis(axis)
|
||||
.exact()
|
||||
.or_else(|| {
|
||||
widgets
|
||||
.get_dyn(id.id())
|
||||
.and_then(|widget| widget.size_hint(axis))
|
||||
})
|
||||
.map(|hint| hint.within_len(self.frame.axis(axis)));
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
diag::hint_read(id.id(), self.id, axis, hint);
|
||||
match hint {
|
||||
@@ -277,6 +284,11 @@ impl<'a> Painter<'a> {
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
diag::bump(Counter::HintHits);
|
||||
self.depend_on(id);
|
||||
// A fraction was just resolved against this frame, so what
|
||||
// this draw does with it is a function of the frame's length.
|
||||
if hint.rel != Rel::ZERO {
|
||||
self.frame_own_len[axis as usize] = Some(self.frame.axis(axis));
|
||||
}
|
||||
Some(hint)
|
||||
}
|
||||
None => {
|
||||
@@ -541,7 +553,7 @@ impl PrimitiveLike for &TextureHandle {
|
||||
/// method's `impl` block is where a `Painter`'s own boxes are, so it takes
|
||||
/// only what the child was asked with.
|
||||
impl Painter<'_> {
|
||||
/// Frame ranges are already ranges on the window and combine directly.
|
||||
/// Window ranges are already about the one unit and combine directly.
|
||||
/// A frame pin becomes this widget's own frame wherever a length of it
|
||||
/// is what reached the child; where only pixels did, no length of this
|
||||
/// frame can change the child's and the pin stops here.
|
||||
@@ -563,8 +575,8 @@ impl Painter<'_> {
|
||||
let mut result = LayoutHolds::ANY;
|
||||
for axis in AXES {
|
||||
let n = axis as usize;
|
||||
// Every frame range is already a range on the window: the
|
||||
// widget's own read converted through its frame exactly once.
|
||||
// Every read became pixels against the window, so a range on
|
||||
// it is already in this widget's terms.
|
||||
result.window[n] = holds.window[n];
|
||||
let reaches = narrow[n].is_none()
|
||||
&& !matches!(place[n].part(), Part::Sized(_))
|
||||
@@ -581,25 +593,34 @@ impl Painter<'_> {
|
||||
result.extent[n] = holds.extent[n];
|
||||
result.extent_len[n] = holds.extent_len[n];
|
||||
}
|
||||
// Its box is a part of this widget's own box, in that box's
|
||||
// own lengths, so what it holds for maps back through that
|
||||
// part into a range on this widget's box. A length it pinned
|
||||
// is this widget's length less the part's pixels where the
|
||||
// part is the whole of the box less pixels, which is the one
|
||||
// shape that inverts exactly; any other part pins this
|
||||
// widget's own length.
|
||||
(Part::Of(span), false) => {
|
||||
let part_len = span.len();
|
||||
result.extent[n] = holds.extent[n].through(part_len);
|
||||
result.extent_len[n] = holds.extent_len[n].map(|pinned| match part_len.rel {
|
||||
Rel::ONE => pinned - Len::from_parts(Rel::ZERO, part_len.px),
|
||||
_ => self.extent.axis(axis).len(),
|
||||
});
|
||||
// Its box is this widget's own less the inset. Where that is
|
||||
// pixels, its box is exactly that many shorter in any window,
|
||||
// so what it holds for is a range on this widget's box moved
|
||||
// by them, and a length it pinned is this widget's length
|
||||
// less them. An inset with a fraction in it is a different
|
||||
// number of pixels in each window, and taking it off a length
|
||||
// rounds once more than taking it off pixels does: there the
|
||||
// child's box is a fixed expression of this one, so this
|
||||
// widget's length is pinned and the range goes on the window
|
||||
// through the child's box, the way a slot's does.
|
||||
(Part::Inset { lead, trail }, false) => {
|
||||
let inset = lead + trail;
|
||||
match inset.rel == Rel::ZERO {
|
||||
true => {
|
||||
result.extent[n] = holds.extent[n].longer_by(inset.px);
|
||||
result.extent_len[n] = holds.extent_len[n].map(|pinned| pinned + inset);
|
||||
}
|
||||
false => {
|
||||
result.window[n] = result.window[n]
|
||||
.and(holds.extent[n].through(extent.axis(axis).len()));
|
||||
result.extent_len[n] = Some(self.extent.axis(axis).len());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Its box is a part of this widget's frame, or a length of
|
||||
// it decided here: a length of the frame is all that reaches
|
||||
// it, so what it holds for is a range on the frame and none
|
||||
// of it on this widget's own box.
|
||||
// Its box is a length this widget decided, from its own
|
||||
// frame or from a sibling's answer: no length of this
|
||||
// widget's box reaches it, so what it holds for is a range
|
||||
// on the window and none of it on that box.
|
||||
_ => {
|
||||
result.window[n] =
|
||||
result.window[n].and(holds.extent[n].through(extent.axis(axis).len()));
|
||||
|
||||
+14
-11
@@ -5,17 +5,20 @@ use crate::{AxisAlign, Len, PrimitiveHandle, UiRegion, UiSpan};
|
||||
pub enum Part {
|
||||
/// The whole of it.
|
||||
All,
|
||||
/// Frame lengths from where the box starts, which is what a container
|
||||
/// dividing room among its children speaks: a child's report is a length
|
||||
/// of the frame, so the cursor that sums those reports is one too. A
|
||||
/// moved box re-places every child by re-adding its start, exactly.
|
||||
/// Window lengths from where the box starts, which is what a container
|
||||
/// dividing room among its children speaks: a child's report is a window
|
||||
/// length, so the cursor that sums those reports is one too. A moved box
|
||||
/// re-places every child by re-adding its start, exactly. A fraction
|
||||
/// here is a fraction of the window and not of the box -- the whole of a
|
||||
/// box is [`Self::All`], not a `rel(1.0)` span.
|
||||
From(UiSpan),
|
||||
/// A part of the box in its own coordinates, which is what a container
|
||||
/// that insets one speaks: taking eleven pixels off the end needs no
|
||||
/// length, where saying the same thing in frame lengths would make the
|
||||
/// container read its own box -- and a box chosen from its own answer
|
||||
/// then feeds back into the answer.
|
||||
Of(UiSpan),
|
||||
/// The box less a window length at each end, which is what a container
|
||||
/// that insets one speaks -- padding, or a row asking a child in the
|
||||
/// room left from its cursor. Neither end names the box's length, so a
|
||||
/// container can say "from here to my end" without reading how long it
|
||||
/// is, and a box chosen from its own answer does not feed back into the
|
||||
/// answer.
|
||||
Inset { lead: Len, trail: Len },
|
||||
/// A box of this length, wherever in the parent's box the child's own
|
||||
/// alignment puts it, and that same length as its frame. Unlike `From`,
|
||||
/// it is a length decided from above rather than a place along a
|
||||
@@ -30,7 +33,7 @@ impl Part {
|
||||
match self {
|
||||
Self::All => extent,
|
||||
Self::From(span) => UiSpan::new(extent.start + span.start, extent.start + span.end),
|
||||
Self::Of(span) => span.within(&extent),
|
||||
Self::Inset { lead, trail } => UiSpan::new(extent.start + lead, extent.end - trail),
|
||||
Self::Sized(len) => {
|
||||
let start = extent.start + (extent.len() - len).scale(align.rel());
|
||||
UiSpan::new(start, start + len)
|
||||
|
||||
@@ -142,6 +142,10 @@ impl Widget for Branch {
|
||||
};
|
||||
Size::LEFTOVER
|
||||
}
|
||||
|
||||
fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
|
||||
Some(LayoutLen::LEFTOVER)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Spanned {
|
||||
|
||||
@@ -15,4 +15,8 @@ impl Widget for Masked {
|
||||
// draw, and the framework would place the drawing it clipped away.
|
||||
Size::LEFTOVER
|
||||
}
|
||||
|
||||
fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
|
||||
Some(LayoutLen::LEFTOVER)
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,10 @@ impl Widget for Pad {
|
||||
// The two stay distinct -- the box can be narrower still, where a row
|
||||
// asked this widget in the room left, and a text wraps at that.
|
||||
let inset = |lead: Px, trail: Px| {
|
||||
Place::Within(Part::Of(UiSpan::new(
|
||||
Len::from_parts(Rel::ZERO, lead),
|
||||
Len::from_parts(Rel::ONE, -trail),
|
||||
)))
|
||||
Place::Within(Part::Inset {
|
||||
lead: Len::from_parts(Rel::ZERO, lead),
|
||||
trail: Len::from_parts(Rel::ZERO, trail),
|
||||
})
|
||||
};
|
||||
let place = [
|
||||
inset(self.padding.left, self.padding.right),
|
||||
|
||||
@@ -44,7 +44,6 @@ impl Widget for Scroll {
|
||||
// have placed the whole scroll in a box longer than it.
|
||||
let slack = (self.container_len - self.content_len).max(Px::ZERO);
|
||||
let anchor = slack.mul(align.rel());
|
||||
let mut content = UiSpan::FULL;
|
||||
// Content that fills the viewport and has not been scrolled is the
|
||||
// viewport, and is handed back as it came. Writing the same box as
|
||||
// its own length in pixels is the same box in another form, and the
|
||||
@@ -52,18 +51,20 @@ impl Widget for Scroll {
|
||||
// one centred in `px 900`, since halving a difference is not halving
|
||||
// each part of it.
|
||||
let moved = anchor != Px::ZERO || self.amt != Px::ZERO;
|
||||
if moved || self.content_len != self.container_len {
|
||||
let start = Len::from_parts(Rel::ZERO, anchor - self.amt);
|
||||
content = UiSpan::new(start, start.offset(self.content_len));
|
||||
}
|
||||
let content = match moved || self.content_len != self.container_len {
|
||||
true => {
|
||||
let start = Len::from_parts(Rel::ZERO, anchor - self.amt);
|
||||
Part::From(UiSpan::new(start, start.offset(self.content_len)))
|
||||
}
|
||||
false => Part::All,
|
||||
};
|
||||
// The viewport is the inner's frame, so a fraction it declares or
|
||||
// reports is a fraction of what is on screen rather than of the
|
||||
// content box its own answer decided. Where it goes is the content
|
||||
// box, scrolled: its drawing moved there, not made again there.
|
||||
painter.place_at(
|
||||
&self.inner,
|
||||
self.axis
|
||||
.pair(Place::Fill(Part::From(content)), Place::Fill(Part::All)),
|
||||
self.axis.pair(Place::Fill(content), Place::Fill(Part::All)),
|
||||
);
|
||||
// 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
|
||||
@@ -71,6 +72,10 @@ impl Widget for Scroll {
|
||||
// is.
|
||||
Size::LEFTOVER
|
||||
}
|
||||
|
||||
fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
|
||||
Some(LayoutLen::LEFTOVER)
|
||||
}
|
||||
}
|
||||
|
||||
impl Scroll {
|
||||
|
||||
+67
-34
@@ -10,38 +10,55 @@ pub struct Span {
|
||||
impl Widget for Span {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
let axis = self.dir.axis;
|
||||
// The row: this span's own box, as a length of the frame its children
|
||||
// are laid out against. Its start is nothing's business -- a slot is
|
||||
// a length from it -- so what this reads is the length alone.
|
||||
let far = painter.extent_len(axis);
|
||||
let along = |from: Len, to: Len| match self.dir.sign {
|
||||
Sign::Pos => UiSpan::new(from, to),
|
||||
Sign::Neg => UiSpan::new(far - to, far - from),
|
||||
// The room left from the cursor to the row's end, said without the
|
||||
// row's length: a child measured in it does not make this drawing
|
||||
// depend on how long the row is.
|
||||
let room_from = |cursor: Len| match self.dir.sign {
|
||||
Sign::Pos => Part::Inset {
|
||||
lead: cursor,
|
||||
trail: Len::ZERO,
|
||||
},
|
||||
Sign::Neg => Part::Inset {
|
||||
lead: Len::ZERO,
|
||||
trail: cursor,
|
||||
},
|
||||
};
|
||||
// Across itself the child sits where its own alignment says, in the
|
||||
// whole of the row: a span is what contains its children there, and
|
||||
// nothing divides that axis.
|
||||
let across = Place::Within(Part::All);
|
||||
// A length for every child before their final slots are chosen. The
|
||||
// frame passes 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 them; what it is asked in is the room left from the cursor,
|
||||
// because a text has to wrap at the width actually there. This is
|
||||
// the one ask a fixed child gets: its slot is its answer, and the
|
||||
// drawing is moved there once the shares are known.
|
||||
// A length for every child before their final slots are chosen: from
|
||||
// a hint where one says, and from drawing otherwise. The frame passes
|
||||
// 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
|
||||
// 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.
|
||||
// This is the one ask a drawn fixed child gets: its slot is its
|
||||
// answer, and the drawing is moved there once the shares are known.
|
||||
// A hinted child is asked once, in its slot.
|
||||
let mut cursor = Len::rel_min();
|
||||
let mut sizes = Vec::with_capacity(self.children.len());
|
||||
let mut lens = Vec::with_capacity(self.children.len());
|
||||
let mut measured = Vec::with_capacity(self.children.len());
|
||||
for child in &self.children {
|
||||
let room = Place::Within(Part::From(along(cursor, far)));
|
||||
let size = painter
|
||||
.widget_at(child, [None; 2], axis.pair(room, across))
|
||||
.size();
|
||||
let len = size.axis(axis);
|
||||
let size = match painter.size_hint(child, axis) {
|
||||
Some(len) => {
|
||||
measured.push(None);
|
||||
len
|
||||
}
|
||||
None => {
|
||||
let room = Place::Within(room_from(cursor));
|
||||
let size = painter
|
||||
.widget_at(child, [None; 2], axis.pair(room, across))
|
||||
.size();
|
||||
measured.push(Some(size));
|
||||
size.axis(axis)
|
||||
}
|
||||
};
|
||||
let len = size;
|
||||
cursor.px += len.px + self.gap;
|
||||
cursor.rel += len.rel;
|
||||
sizes.push(size);
|
||||
lens.push(len);
|
||||
}
|
||||
let lens: Vec<LayoutLen> = sizes.iter().map(|size| size.axis(axis)).collect();
|
||||
|
||||
let gaps = self
|
||||
.gap
|
||||
@@ -54,9 +71,25 @@ impl Widget for Span {
|
||||
|sum, len| sum + *len,
|
||||
);
|
||||
|
||||
// The row: this span's own box as a length of the window, read only
|
||||
// where a slot depends on it -- shares divide what is left of it,
|
||||
// and a negative row counts from its end. Reading it pins the
|
||||
// drawing to this length; a positive row of fixed children is not
|
||||
// pinned and holds for any length its children do. Its start is
|
||||
// nothing's business: a slot is a length from it.
|
||||
let far = (total.leftover > Weight::ZERO || self.dir.sign == Sign::Neg)
|
||||
.then(|| painter.extent_len(axis));
|
||||
let along = |from: Len, to: Len| match self.dir.sign {
|
||||
Sign::Pos => UiSpan::new(from, to),
|
||||
Sign::Neg => {
|
||||
let far = far.expect("a negative row reads its length");
|
||||
UiSpan::new(far - to, far - from)
|
||||
}
|
||||
};
|
||||
// What is left for the shares to divide: the row less everything
|
||||
// fixed, as a length of the frame rather than a number of pixels.
|
||||
let room = far - Len::from_parts(total.rel, total.px);
|
||||
// Nothing where there are no shares, and nothing reads it there.
|
||||
let room = far.map_or(Len::ZERO, |far| far - Len::from_parts(total.rel, total.px));
|
||||
// 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`
|
||||
// itself, and answered back through the same expression, so the
|
||||
@@ -92,8 +125,8 @@ impl Widget for Span {
|
||||
let mut taken = Weight::ZERO;
|
||||
let mut start = Len::rel_min();
|
||||
let mut ortho = LayoutLen::ZERO;
|
||||
for (child, size) in self.children.iter().zip(&sizes) {
|
||||
let len = size.axis(axis);
|
||||
for ((child, len), measured) in self.children.iter().zip(&lens).zip(&measured) {
|
||||
let len = *len;
|
||||
// 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
|
||||
// pixels or a fraction keeps those and overflows.
|
||||
@@ -115,20 +148,20 @@ impl Widget for Span {
|
||||
// answer inside again. A share is decided here and nowhere
|
||||
// else: its slot narrows its frame, and the child is asked in
|
||||
// it, since a text wraps at the width it is actually given. A
|
||||
// fixed child's slot is its own answer, so its drawing is put
|
||||
// there as it is.
|
||||
// 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.
|
||||
let slot = along(from, start);
|
||||
let place = axis.pair(Place::Fill(Part::From(slot)), across);
|
||||
let used = match len.leftover > Weight::ZERO && shares {
|
||||
true => {
|
||||
let mut narrow = [None; 2];
|
||||
narrow[axis as usize] = Some(slot.len());
|
||||
painter.widget_at(child, narrow, place).len(!axis)
|
||||
}
|
||||
false => {
|
||||
let mut narrow = [None; 2];
|
||||
if len.leftover > Weight::ZERO && shares {
|
||||
narrow[axis as usize] = Some(slot.len());
|
||||
}
|
||||
let used = match (measured, narrow[axis as usize]) {
|
||||
(Some(size), None) => {
|
||||
painter.place_at(child, place);
|
||||
size.axis(!axis)
|
||||
}
|
||||
_ => painter.widget_at(child, narrow, place).len(!axis),
|
||||
};
|
||||
if shrinks {
|
||||
// Choosing between a fixed and a relative length from the
|
||||
|
||||
@@ -49,6 +49,15 @@ impl Widget for Stack {
|
||||
}
|
||||
size
|
||||
}
|
||||
|
||||
/// Without a sizing child a stack is whatever box it is given, which it
|
||||
/// can say without drawing anything.
|
||||
fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
|
||||
match self.size {
|
||||
StackSize::Default => Some(LayoutLen::LEFTOVER),
|
||||
StackSize::Child(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
||||
@@ -145,3 +145,19 @@ fn a_clipping_widget_reporting_more_than_its_box_is_caught() {
|
||||
h.set_root(clipper);
|
||||
h.frame();
|
||||
}
|
||||
|
||||
/// Content that fits sits in the viewport, not in a box of the window's
|
||||
/// length anchored at the viewport's start. `Part::From` takes window
|
||||
/// lengths, so a `rel(1.0)` span in one is the window, and only a scroll
|
||||
/// filling the window would land right.
|
||||
#[test]
|
||||
fn content_that_fits_is_placed_in_the_viewport_and_not_in_the_window() {
|
||||
let mut h = Harness::new((400, 400));
|
||||
let head = rect(Color::RED).height(100).add(&mut h.rsc);
|
||||
let inner = rect(Color::BLUE).height(50).add(&mut h.rsc);
|
||||
let scroll = Scroll::new(inner.add_strong(&mut h.rsc), Axis::Y).add(&mut h.rsc);
|
||||
h.set_root((head, scroll).span(Dir::DOWN));
|
||||
|
||||
assert_corners!(h, scroll, (0, 100), (400, 400));
|
||||
assert_corners!(h, inner, (0, 225), (400, 275));
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//! Prints where a cold layout puts every widget of many grown trees, so two
|
||||
//! commits can be compared on cold layout alone. The warm/cold oracle cannot
|
||||
//! see a change that moves cold layout, since both of its sides move; this
|
||||
//! can, by diffing its output across the change:
|
||||
//!
|
||||
//! IRIS_DUMP_SEEDS=400 IRIS_DUMP_DEPTH=5 cargo test --release \
|
||||
//! --test layout_dump -- --ignored --nocapture > /tmp/before.txt
|
||||
//!
|
||||
//! then the same after, and `diff` the two. A line is one widget: the seed,
|
||||
//! its index in creation order, and its box in window pixels, or `-` where
|
||||
//! it is not drawn.
|
||||
|
||||
use iris::harness::Harness;
|
||||
use iris::random::{Edits, grow};
|
||||
|
||||
fn env<T: std::str::FromStr>(name: &str, fallback: T) -> T {
|
||||
std::env::var(name)
|
||||
.ok()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.unwrap_or(fallback)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "a dump to diff across commits, not a check"]
|
||||
fn every_cold_layout_is_printed() {
|
||||
let seeds = env("IRIS_DUMP_SEEDS", 400_u64);
|
||||
let depth = env("IRIS_DUMP_DEPTH", 5_usize);
|
||||
let mut out = String::new();
|
||||
for seed in 1..=seeds {
|
||||
let mut harness = Harness::new((1920.0, 1200.0));
|
||||
let (root, tree) = grow(&mut harness.rsc, seed, depth, &Edits::default());
|
||||
harness.state.root = Some(root);
|
||||
harness.frame();
|
||||
for (index, id) in tree.ids.iter().enumerate() {
|
||||
match harness.region(id) {
|
||||
Some(region) => out.push_str(&format!("{seed} {index} {region:?}\n")),
|
||||
None => out.push_str(&format!("{seed} {index} -\n")),
|
||||
}
|
||||
}
|
||||
}
|
||||
print!("{out}");
|
||||
}
|
||||
Reference in new issue
Block a user