Compare commits
2
Commits
86a7e8dfc3
...
2272634dc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2272634dc5 | ||
|
|
b0f9f046da |
No files matched your search
@@ -1,4 +1,6 @@
|
||||
use crate::{LayerId, MaskIdx, MoveIdx, PrimitiveHandle, Size, TextureHandle, UiRegion, WidgetId};
|
||||
use crate::{
|
||||
LayerId, MaskIdx, MoveIdx, PrimitiveHandle, Size, TextureHandle, UiRegion, WidgetId, util::Vec2,
|
||||
};
|
||||
|
||||
/// important non rendering data for retained drawing
|
||||
#[derive(Debug)]
|
||||
@@ -7,6 +9,10 @@ pub struct ActiveData {
|
||||
pub region: UiRegion,
|
||||
/// What the widget said it used of `region`, the last time it drew.
|
||||
pub size: Size,
|
||||
/// The pixel size of the box it drew against. `region` alone cannot say:
|
||||
/// it is a fraction of a slot's box, and the same fraction of a box that
|
||||
/// has since changed is a different number of pixels.
|
||||
pub px: Vec2,
|
||||
pub parent: Option<WidgetId>,
|
||||
pub textures: Vec<TextureHandle>,
|
||||
pub primitives: Vec<PrimitiveHandle>,
|
||||
|
||||
+47
-21
@@ -132,6 +132,7 @@ impl UiRenderState {
|
||||
(parent_move, region)
|
||||
}
|
||||
};
|
||||
let px = self.px_of(move_idx, local);
|
||||
rsc.widgets_mut().needs_redraw.remove(&id);
|
||||
self.draw_started.insert(id);
|
||||
|
||||
@@ -180,6 +181,7 @@ impl UiRenderState {
|
||||
id,
|
||||
region,
|
||||
size,
|
||||
px,
|
||||
parent,
|
||||
textures,
|
||||
primitives,
|
||||
@@ -227,6 +229,14 @@ impl UiRenderState {
|
||||
}
|
||||
}
|
||||
|
||||
/// The pixel size of a region held in `slot`'s coordinates.
|
||||
fn px_of(&self, slot: MoveIdx, region: UiRegion) -> Vec2 {
|
||||
self.moves
|
||||
.resolve(slot, region)
|
||||
.size()
|
||||
.to_abs(self.output_size)
|
||||
}
|
||||
|
||||
/// The drawing a widget already has, kept for a new box if the box has not
|
||||
/// changed in a way it depends on.
|
||||
fn try_reuse(
|
||||
@@ -245,8 +255,16 @@ impl UiRenderState {
|
||||
if active.parent_move != parent_move {
|
||||
return None;
|
||||
}
|
||||
let (size, old, slot) = (active.size, active.region, active.move_idx);
|
||||
if old == region {
|
||||
let (size, old, slot, was) = (active.size, active.region, active.move_idx, active.px);
|
||||
// In pixels, because `region` is a fraction of a slot's box and that
|
||||
// box may be what changed -- an unchanged fraction of a box half the
|
||||
// size is half the widget.
|
||||
let px = self.px_of(parent_move, region);
|
||||
let mut changed = [false; 2];
|
||||
for (axis, c) in AXES.into_iter().zip(changed.iter_mut()) {
|
||||
*c = px.axis(axis) != was.axis(axis);
|
||||
}
|
||||
if !changed.iter().any(|&c| c) && old == region {
|
||||
return Some(size);
|
||||
}
|
||||
// Only a placed widget can be given a different box without drawing
|
||||
@@ -255,28 +273,23 @@ impl UiRenderState {
|
||||
if slot == parent_move {
|
||||
return None;
|
||||
}
|
||||
let mut changed = [false; 2];
|
||||
for (axis, c) in AXES.into_iter().zip(changed.iter_mut()) {
|
||||
*c = region.axis(axis).len() != old.axis(axis).len();
|
||||
}
|
||||
if changed.iter().any(|&c| c) {
|
||||
let widget = rsc.widgets().get_dyn(id)?;
|
||||
let redraws = AXES
|
||||
.into_iter()
|
||||
.zip(changed)
|
||||
.any(|(axis, c)| c && widget.on_resize(axis) != OnResize::Scale);
|
||||
if redraws {
|
||||
// Anything under it that has to be drawn again is drawn by drawing
|
||||
// this, because whatever reads that widget's size sits in between
|
||||
// and has to lay out around what it comes to.
|
||||
if redraws || self.redraws_under(id, changed, rsc) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
// Anything under it that has to be drawn again is drawn by drawing
|
||||
// this, because whatever reads that widget's size sits in between and
|
||||
// has to lay out around whatever it comes to.
|
||||
if changed.iter().any(|&c| c) && self.redraws_under(id, changed, rsc) {
|
||||
return None;
|
||||
}
|
||||
self.moves.set(slot, region);
|
||||
self.active.get_mut(&id).unwrap().region = region;
|
||||
let active = self.active.get_mut(&id).unwrap();
|
||||
active.region = region;
|
||||
active.px = px;
|
||||
Some(size)
|
||||
}
|
||||
|
||||
@@ -293,10 +306,26 @@ impl UiRenderState {
|
||||
let Some(active) = self.active.get(&id) else {
|
||||
return false;
|
||||
};
|
||||
let size_deps = &active.size_deps;
|
||||
active.children.iter().any(|&child| {
|
||||
let Some(data) = self.active.get(&child) else {
|
||||
return false;
|
||||
};
|
||||
let Some(widget) = rsc.widgets().get_dyn(child) else {
|
||||
return true;
|
||||
};
|
||||
// What it drew to learn this child's size was the child in *this*
|
||||
// box, so a different box is a different answer -- unless the
|
||||
// child gave an exact one without being drawn at all.
|
||||
if size_deps.contains(&child) {
|
||||
let measured = AXES
|
||||
.into_iter()
|
||||
.zip(changed)
|
||||
.any(|(axis, c)| c && widget.size_hint(axis).is_none());
|
||||
if measured {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
let mut own = changed;
|
||||
for (axis, c) in AXES.into_iter().zip(own.iter_mut()) {
|
||||
*c &= data.region.axis(axis).len().rel != 0.0;
|
||||
@@ -304,13 +333,10 @@ impl UiRenderState {
|
||||
if !own.iter().any(|&c| c) {
|
||||
return false;
|
||||
}
|
||||
let redraws = match rsc.widgets().get_dyn(child) {
|
||||
Some(widget) => AXES
|
||||
.into_iter()
|
||||
.zip(own)
|
||||
.any(|(axis, c)| c && widget.on_resize(axis) != OnResize::Scale),
|
||||
None => true,
|
||||
};
|
||||
let redraws = AXES
|
||||
.into_iter()
|
||||
.zip(own)
|
||||
.any(|(axis, c)| c && widget.on_resize(axis) != OnResize::Scale);
|
||||
redraws || self.redraws_under(child, own, rsc)
|
||||
})
|
||||
}
|
||||
|
||||
+13
-1
@@ -17,6 +17,10 @@
|
||||
# custom one would otherwise inherit the other's output and quietly screenshot
|
||||
# the wrong size.
|
||||
#
|
||||
# `--resize WxH@Hz` changes the output under the app once it is up, then
|
||||
# screenshots. A resize is its own case: what it has to match is a cold start
|
||||
# at that size, byte for byte, and nothing in `cargo test` can see it.
|
||||
#
|
||||
# `--replay FILE` drives a `.touch` recording into the window through
|
||||
# `replay-touch`, which reads it with the same parser `iris::harness` uses. A
|
||||
# recording is `<ms> down|move|up <x> <y>` in the output's own pixels. With
|
||||
@@ -46,6 +50,7 @@ run="${XDG_RUNTIME_DIR:-/tmp}/iris-headless"
|
||||
seconds=3
|
||||
shot=""
|
||||
replay=""
|
||||
resize=""
|
||||
example=""
|
||||
kind=example
|
||||
mode=1920x1200@60Hz
|
||||
@@ -57,13 +62,14 @@ while [ $# -gt 0 ]; do
|
||||
--seconds) seconds=$2; shift 2 ;;
|
||||
--bin) kind=bin; shift ;;
|
||||
--mode) mode=$2; shift 2 ;;
|
||||
--resize) resize=$2; shift 2 ;;
|
||||
--replay) replay=$2; shift 2 ;;
|
||||
--dir) workdir=$(cd "$2" && pwd); shift 2 ;;
|
||||
--) shift; break ;;
|
||||
*) example=$1; shift ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$example" ] || { echo "usage: $0 NAME [--bin] [--dir DIR] [--mode WxH@Hz] [--replay TOUCH] [--shot PNG] [--seconds N] [-- cargo args]" >&2; exit 2; }
|
||||
[ -n "$example" ] || { echo "usage: $0 NAME [--bin] [--dir DIR] [--mode WxH@Hz] [--resize WxH@Hz] [--replay TOUCH] [--shot PNG] [--seconds N] [-- cargo args]" >&2; exit 2; }
|
||||
[ -z "$replay" ] || [ -f "$replay" ] || { echo "run-headless: no touch script at $replay" >&2; exit 2; }
|
||||
[ -z "$shot" ] || need grim "the screenshot --shot writes"
|
||||
|
||||
@@ -142,6 +148,12 @@ while [ $i -lt "$((seconds * 2))" ]; do
|
||||
i=$((i + 1)); sleep 0.5
|
||||
done
|
||||
|
||||
if [ -n "$resize" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
swaymsg output HEADLESS-1 mode "$resize" >/dev/null
|
||||
echo "run-headless: resized to $resize" >&2
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
if [ -n "$replay" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
if [ -n "$shot" ]; then
|
||||
grim "${shot%.png}-before.png"
|
||||
|
||||
+4
-6
@@ -17,9 +17,7 @@ use iris::prelude::*;
|
||||
use iris::random::{Lens, Rng, Tree, grow};
|
||||
|
||||
const DEPTH: usize = 4;
|
||||
/// Seeds whose trees agree. The ones left out are `a_wrapping_child_of_a_row`
|
||||
/// below, which is a defect older than the chain.
|
||||
const SEEDS: [u64; 6] = [2, 3, 4, 5, 8, 9];
|
||||
const SEEDS: [u64; 6] = [1, 2, 3, 5, 8, 13];
|
||||
|
||||
fn plant(h: &mut Harness, seed: u64, edits: &HashMap<usize, Lens>) -> Tree {
|
||||
let (root, tree) = grow(&mut h.rsc, seed, DEPTH, edits);
|
||||
@@ -161,9 +159,9 @@ fn a_size_change_after_a_resize_lands_the_same_way() {
|
||||
/// same defect, and it wants fixing where the two draws meet -- LAYOUT.md §4 --
|
||||
/// rather than anywhere in the chain.
|
||||
#[test]
|
||||
#[ignore = "known divergence, and the reproduction for fixing it"]
|
||||
fn a_wrapping_child_of_a_row_settles_somewhere_else_each_time() {
|
||||
for seed in 1..=30 {
|
||||
#[ignore = "a hundred seeds, rather than the six the others check"]
|
||||
fn a_long_run_of_seeds_agrees() {
|
||||
for seed in 1..=100 {
|
||||
changed_size(seed);
|
||||
resized(seed);
|
||||
resized_then_changed(seed);
|
||||
|
||||
+7
-4
@@ -320,11 +320,14 @@ fn a_widened_row_redraws_what_reads_its_length_and_nothing_else() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_fixed_length_child_is_not_redrawn_when_the_box_around_it_grows() {
|
||||
fn a_declared_length_child_is_not_redrawn_when_the_box_around_it_grows() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
// It would be drawn again for a width it does not have: its own box is
|
||||
// a fixed 80 wherever the row's edges end up.
|
||||
let (fixed, draws) = counted(&mut h, Size::from((80, 200)), OnResize::Redraw);
|
||||
// Its box is a fixed 80 wherever the row's edges end up, so drawing it
|
||||
// again would be for a width it does not have. The declared width is what
|
||||
// lets the span say that without drawing it: a width the span learnt by
|
||||
// drawing the child in its own box is only an answer for that box.
|
||||
let (counter, draws) = counted(&mut h, Size::from((80, 200)), OnResize::Redraw);
|
||||
let fixed = counter.width(80).add(&mut h.rsc);
|
||||
let (rest, _) = counted(&mut h, Size::REST, OnResize::Scale);
|
||||
let row = (fixed, rest).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
let bar = rect(Color::RED).width(100).add(&mut h.rsc);
|
||||
|
||||
Reference in new issue
Block a user