Let OnResize answer for the window too, and delete the second rule
A resize had its own mechanism: `reads_output` recorded that a widget had looked at the output, `update` scanned every active widget for one whose `output_px` had moved, marked it and its whole reader chain, and `resize_marks` kept those marks from counting as content dirtiness -- while `on_resize` answered the same question for every other box. Two answers to "does this drawing survive its box changing length", and the one that applied to the window ignored what the widget had declared. With the output held as the root of the chain there is one question. A resize offers the root widget its box again, `try_reuse` answers per axis from `on_resize`, and `redraws_under` prices the subtree. Gone with it: `reads_output`, `resized`, `resize_marks`, the scan, the eager reader marking, and the shallowest-first branch in `redraw_updates`, which only existed because resize marking worked differently -- the settle loop now has one order. Two things this needed. An unslotted widget may be reused when only its parent's box changed length: it has nothing of its own to write, and what it drew is a fraction of that box, so the slot above it already carries the change. And `root_readers` holds the widgets whose size came from the output rather than their own box -- `MaxSize` -- since no box of theirs need have changed; they are marked per axis, from a set kept as they draw rather than by scanning. `a_resize_does_not_redraw_what_the_shader_can_move` now says `Scale`, which is what it was always describing, and `a_resize_redraws_what_does _not_scale` is its other half. `ReadsWidth` declares `Scale` across the axis it does not read, so per-axis precision comes from the widget rather than from which output axis it happened to touch. Resize phase, seed 1 depth 8: 6.45M instructions per frame to 5.84M. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9f4311774b
commit
ef815dadfd
4 files changed
+105
-81
No files matched your search
@@ -31,8 +31,6 @@ pub struct ActiveData {
|
|||||||
pub size_output_inputs: [bool; 2],
|
pub size_output_inputs: [bool; 2],
|
||||||
/// The output dimensions against which those dependencies were observed.
|
/// The output dimensions against which those dependencies were observed.
|
||||||
pub output_px: Vec2,
|
pub output_px: Vec2,
|
||||||
/// Output axes it read directly or while resolving its offered box.
|
|
||||||
pub reads_output: [bool; 2],
|
|
||||||
/// The slot its primitives are positioned through: its own if its parent
|
/// The slot its primitives are positioned through: its own if its parent
|
||||||
/// placed it, otherwise the nearest ancestor that has one.
|
/// placed it, otherwise the nearest ancestor that has one.
|
||||||
pub move_idx: MoveIdx,
|
pub move_idx: MoveIdx,
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ pub struct Painter<'a> {
|
|||||||
/// Offered pixel axes which can affect the size this draw reports.
|
/// Offered pixel axes which can affect the size this draw reports.
|
||||||
pub(super) size_box_inputs: [bool; 2],
|
pub(super) size_box_inputs: [bool; 2],
|
||||||
pub(super) size_output_inputs: [bool; 2],
|
pub(super) size_output_inputs: [bool; 2],
|
||||||
pub(super) reads_output: [bool; 2],
|
|
||||||
/// The slot this widget's primitives are positioned through: its own if
|
/// The slot this widget's primitives are positioned through: its own if
|
||||||
/// its parent placed it, otherwise the nearest ancestor that has one.
|
/// its parent placed it, otherwise the nearest ancestor that has one.
|
||||||
pub(super) move_idx: MoveIdx,
|
pub(super) move_idx: MoveIdx,
|
||||||
@@ -286,7 +285,6 @@ impl<'a> Painter<'a> {
|
|||||||
/// The output's size in pixels. A widget that reads it draws again when
|
/// The output's size in pixels. A widget that reads it draws again when
|
||||||
/// the output changes, since nothing else can put that right.
|
/// the output changes, since nothing else can put that right.
|
||||||
pub fn output_size(&mut self) -> Vec2 {
|
pub fn output_size(&mut self) -> Vec2 {
|
||||||
self.reads_output = [true; 2];
|
|
||||||
self.size_output_inputs = [true; 2];
|
self.size_output_inputs = [true; 2];
|
||||||
self.state.output_size
|
self.state.output_size
|
||||||
}
|
}
|
||||||
@@ -294,7 +292,6 @@ impl<'a> Painter<'a> {
|
|||||||
/// One axis of the output in pixels. Prefer this to [`Self::output_size`]
|
/// One axis of the output in pixels. Prefer this to [`Self::output_size`]
|
||||||
/// when the other axis cannot affect the size this widget reports.
|
/// when the other axis cannot affect the size this widget reports.
|
||||||
pub fn output_len(&mut self, axis: Axis) -> f32 {
|
pub fn output_len(&mut self, axis: Axis) -> f32 {
|
||||||
self.reads_output[axis as usize] = true;
|
|
||||||
self.size_output_inputs[axis as usize] = true;
|
self.size_output_inputs[axis as usize] = true;
|
||||||
self.state.output_size.axis(axis)
|
self.state.output_size.axis(axis)
|
||||||
}
|
}
|
||||||
@@ -303,7 +300,6 @@ impl<'a> Painter<'a> {
|
|||||||
/// the boxes it sits within, so a widget that reads it draws again when
|
/// the boxes it sits within, so a widget that reads it draws again when
|
||||||
/// the output changes.
|
/// the output changes.
|
||||||
pub fn px_size(&mut self) -> Vec2 {
|
pub fn px_size(&mut self) -> Vec2 {
|
||||||
self.reads_output = [true; 2];
|
|
||||||
self.size_box_inputs = [true; 2];
|
self.size_box_inputs = [true; 2];
|
||||||
let region = self.state.moves.resolve(self.move_idx, self.region);
|
let region = self.state.moves.resolve(self.move_idx, self.region);
|
||||||
region.size().to_px(self.state.output_size)
|
region.size().to_px(self.state.output_size)
|
||||||
@@ -312,7 +308,6 @@ impl<'a> Painter<'a> {
|
|||||||
/// One axis of this widget's box in pixels. Prefer this to
|
/// One axis of this widget's box in pixels. Prefer this to
|
||||||
/// [`Self::px_size`] when the other axis cannot affect the reported size.
|
/// [`Self::px_size`] when the other axis cannot affect the reported size.
|
||||||
pub fn px_len(&mut self, axis: Axis) -> f32 {
|
pub fn px_len(&mut self, axis: Axis) -> f32 {
|
||||||
self.reads_output[axis as usize] = true;
|
|
||||||
self.size_box_inputs[axis as usize] = true;
|
self.size_box_inputs[axis as usize] = true;
|
||||||
let region = self.state.moves.resolve(self.move_idx, self.region);
|
let region = self.state.moves.resolve(self.move_idx, self.region);
|
||||||
region
|
region
|
||||||
|
|||||||
+77
-72
@@ -21,14 +21,12 @@ pub struct UiRenderState {
|
|||||||
old_root: Option<WidgetId>,
|
old_root: Option<WidgetId>,
|
||||||
/// The slot every chain bottoms out in, holding the output as a box.
|
/// The slot every chain bottoms out in, holding the output as a box.
|
||||||
root_move: MoveIdx,
|
root_move: MoveIdx,
|
||||||
resized: [bool; 2],
|
/// Widgets whose reported size depends on the root box rather than on
|
||||||
|
/// their own, so nothing below them changing length can reach them.
|
||||||
|
root_readers: HashSet<WidgetId>,
|
||||||
/// Content/state dirtiness whose retained size cannot answer a layout
|
/// Content/state dirtiness whose retained size cannot answer a layout
|
||||||
/// question until that widget has drawn again.
|
/// question until that widget has drawn again.
|
||||||
invalid_sizes: HashSet<WidgetId>,
|
invalid_sizes: HashSet<WidgetId>,
|
||||||
/// Marks introduced only to traverse resize dependency paths. Unlike
|
|
||||||
/// content dirtiness, these may retain an answer whose observed pixel
|
|
||||||
/// axes did not change.
|
|
||||||
resize_marks: HashSet<WidgetId>,
|
|
||||||
/// What has already been drawn during the pass under way, so a widget
|
/// What has already been drawn during the pass under way, so a widget
|
||||||
/// reached by redrawing an ancestor is not drawn again on its own
|
/// reached by redrawing an ancestor is not drawn again on its own
|
||||||
/// account. Emptied when the pass ends.
|
/// account. Emptied when the pass ends.
|
||||||
@@ -46,13 +44,12 @@ impl UiRenderState {
|
|||||||
layers: Default::default(),
|
layers: Default::default(),
|
||||||
output_size: Vec2::ZERO,
|
output_size: Vec2::ZERO,
|
||||||
old_root: None,
|
old_root: None,
|
||||||
resized: [false; 2],
|
|
||||||
invalid_sizes: Default::default(),
|
invalid_sizes: Default::default(),
|
||||||
resize_marks: Default::default(),
|
|
||||||
draw_started: Default::default(),
|
draw_started: Default::default(),
|
||||||
slots: Default::default(),
|
slots: Default::default(),
|
||||||
moves: Default::default(),
|
moves: Default::default(),
|
||||||
root_move: MoveIdx::NONE,
|
root_move: MoveIdx::NONE,
|
||||||
|
root_readers: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,14 +69,25 @@ impl UiRenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, size: impl Into<Vec2>) {
|
pub fn resize(&mut self, size: impl Into<Vec2>) {
|
||||||
let size = size.into();
|
self.output_size = size.into();
|
||||||
for (axis, resized) in AXES.into_iter().zip(self.resized.iter_mut()) {
|
|
||||||
*resized |= size.axis(axis) != self.output_size.axis(axis);
|
|
||||||
}
|
|
||||||
self.output_size = size;
|
|
||||||
self.write_root();
|
self.write_root();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Which axes of the root widget's box are no longer the ones the root
|
||||||
|
/// slot holds, which is all a resize now is: one slot written, found by
|
||||||
|
/// the same comparison every other box change is found by.
|
||||||
|
fn root_axes_changed(&self) -> [bool; 2] {
|
||||||
|
let Some(active) = self.old_root.and_then(|root| self.active.get(&root)) else {
|
||||||
|
return [false; 2];
|
||||||
|
};
|
||||||
|
let px = self.px_of(active.parent_move, active.region);
|
||||||
|
let mut changed = [false; 2];
|
||||||
|
for (axis, c) in AXES.into_iter().zip(changed.iter_mut()) {
|
||||||
|
*c = pixel_len_changed(active.px.axis(axis), px.axis(axis));
|
||||||
|
}
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
pub fn output_size(&self) -> Vec2 {
|
pub fn output_size(&self) -> Vec2 {
|
||||||
self.output_size
|
self.output_size
|
||||||
}
|
}
|
||||||
@@ -92,7 +100,6 @@ impl UiRenderState {
|
|||||||
self.invalid_sizes.clear();
|
self.invalid_sizes.clear();
|
||||||
self.invalid_sizes
|
self.invalid_sizes
|
||||||
.extend(rsc.widgets().needs_redraw.iter().copied());
|
.extend(rsc.widgets().needs_redraw.iter().copied());
|
||||||
self.resize_marks.clear();
|
|
||||||
// safety mechanism for memory leaks; might wanna return a result instead so user can
|
// safety mechanism for memory leaks; might wanna return a result instead so user can
|
||||||
// decide whether to panic or not
|
// decide whether to panic or not
|
||||||
if !rsc.widgets().waiting.is_empty() {
|
if !rsc.widgets().waiting.is_empty() {
|
||||||
@@ -113,53 +120,52 @@ impl UiRenderState {
|
|||||||
if self.root_changed(root) {
|
if self.root_changed(root) {
|
||||||
self.redraw_all(root, rsc);
|
self.redraw_all(root, rsc);
|
||||||
self.old_root = root.map(|r| r.id());
|
self.old_root = root.map(|r| r.id());
|
||||||
} else if self.resized.iter().any(|&resized| resized) {
|
} else if self.root_axes_changed().iter().any(|&c| c) {
|
||||||
// A region is a fraction of the output plus an offset, resolved
|
// Every box is a part of the root box, so writing it is a box
|
||||||
// against the window in the shader, so a resize moves the whole
|
// that changed length like any other. Offering the root widget
|
||||||
// drawing on its own. Only a widget that read pixels can be wrong.
|
// its box again puts that through `try_reuse`, which answers per
|
||||||
{
|
// axis and lets `redraws_under` price the subtree -- rather than
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
// marking it, which would redraw it whichever axis moved. What
|
||||||
let _marking = diag::timer(TimerKind::ResizeMarking);
|
// that cannot reach is a widget whose size came from the root box
|
||||||
let dependents: Vec<_> = self
|
// instead of its own, since its own box need not have changed.
|
||||||
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
|
let _marking = diag::timer(TimerKind::ResizeMarking);
|
||||||
|
let changed = self.root_axes_changed();
|
||||||
|
for id in self.root_readers.clone() {
|
||||||
|
let reads = self
|
||||||
.active
|
.active
|
||||||
.iter()
|
.get(&id)
|
||||||
.filter_map(|(&id, active)| {
|
.map_or([false; 2], |active| active.size_output_inputs);
|
||||||
AXES.into_iter()
|
if !AXES
|
||||||
.zip(self.resized)
|
.into_iter()
|
||||||
.any(|(axis, changed)| {
|
.zip(changed)
|
||||||
changed
|
.any(|(axis, c)| c && reads[axis as usize])
|
||||||
&& active.reads_output[axis as usize]
|
{
|
||||||
&& pixel_len_changed(
|
continue;
|
||||||
active.output_px.axis(axis),
|
|
||||||
self.output_size.axis(axis),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.then_some(id)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
for id in dependents {
|
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
|
||||||
diag::bump(Counter::ResizeDependents);
|
|
||||||
rsc.widgets_mut().needs_redraw.insert(id);
|
|
||||||
if let Some(top) = self.mark_readers(id, rsc) {
|
|
||||||
rsc.widgets_mut().needs_redraw.insert(top);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.resize_marks.extend(
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
rsc.widgets()
|
diag::bump(Counter::ResizeDependents);
|
||||||
.needs_redraw
|
rsc.widgets_mut().needs_redraw.insert(id);
|
||||||
.iter()
|
}
|
||||||
.filter(|id| !self.invalid_sizes.contains(id))
|
if let Some(root) = root {
|
||||||
.copied(),
|
self.draw_inner(
|
||||||
|
0,
|
||||||
|
root.id(),
|
||||||
|
UiRegion::FULL,
|
||||||
|
None,
|
||||||
|
1,
|
||||||
|
self.root_move,
|
||||||
|
false,
|
||||||
|
MaskIdx::NONE,
|
||||||
|
None,
|
||||||
|
rsc,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rsc.widgets().has_updates() {
|
if rsc.widgets().has_updates() {
|
||||||
self.redraw_updates(rsc);
|
self.redraw_updates(rsc);
|
||||||
}
|
}
|
||||||
self.resized = [false; 2];
|
|
||||||
self.invalid_sizes.clear();
|
self.invalid_sizes.clear();
|
||||||
self.resize_marks.clear();
|
|
||||||
self.draw_started.clear();
|
self.draw_started.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +248,6 @@ impl UiRenderState {
|
|||||||
depth,
|
depth,
|
||||||
size_box_inputs: [false; 2],
|
size_box_inputs: [false; 2],
|
||||||
size_output_inputs: [false; 2],
|
size_output_inputs: [false; 2],
|
||||||
reads_output: [false; 2],
|
|
||||||
move_idx,
|
move_idx,
|
||||||
rsc,
|
rsc,
|
||||||
};
|
};
|
||||||
@@ -269,7 +274,6 @@ impl UiRenderState {
|
|||||||
size_deps,
|
size_deps,
|
||||||
size_box_inputs,
|
size_box_inputs,
|
||||||
size_output_inputs,
|
size_output_inputs,
|
||||||
reads_output,
|
|
||||||
move_idx,
|
move_idx,
|
||||||
layer,
|
layer,
|
||||||
depth: _,
|
depth: _,
|
||||||
@@ -297,7 +301,6 @@ impl UiRenderState {
|
|||||||
size_box_inputs,
|
size_box_inputs,
|
||||||
size_output_inputs,
|
size_output_inputs,
|
||||||
output_px: self.output_size,
|
output_px: self.output_size,
|
||||||
reads_output,
|
|
||||||
move_idx,
|
move_idx,
|
||||||
parent_move,
|
parent_move,
|
||||||
mask,
|
mask,
|
||||||
@@ -310,10 +313,13 @@ impl UiRenderState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match active.size_output_inputs.iter().any(|&reads| reads) {
|
||||||
|
true => self.root_readers.insert(id),
|
||||||
|
false => self.root_readers.remove(&id),
|
||||||
|
};
|
||||||
rsc.on_draw(&active);
|
rsc.on_draw(&active);
|
||||||
self.active.insert(id, active);
|
self.active.insert(id, active);
|
||||||
self.invalid_sizes.remove(&id);
|
self.invalid_sizes.remove(&id);
|
||||||
self.resize_marks.remove(&id);
|
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,8 +396,7 @@ impl UiRenderState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn size_is_invalid(&self, id: WidgetId, widgets: &Widgets) -> bool {
|
fn size_is_invalid(&self, id: WidgetId, widgets: &Widgets) -> bool {
|
||||||
self.invalid_sizes.contains(&id)
|
self.invalid_sizes.contains(&id) || widgets.needs_redraw.contains(&id)
|
||||||
|| (widgets.needs_redraw.contains(&id) && !self.resize_marks.contains(&id))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dirty_size_under(&self, id: WidgetId, widgets: &Widgets) -> bool {
|
fn dirty_size_under(&self, id: WidgetId, widgets: &Widgets) -> bool {
|
||||||
@@ -458,10 +463,14 @@ impl UiRenderState {
|
|||||||
self.keep_depth(id, depth);
|
self.keep_depth(id, depth);
|
||||||
return Some(size);
|
return Some(size);
|
||||||
}
|
}
|
||||||
// Only a placed widget can be given a different box without drawing
|
// Only a placed widget can be given a different *region* without
|
||||||
// again: everything it drew is a fraction of its slot's box, so one
|
// drawing again: it has an entry of its own to say where it went,
|
||||||
// entry says where all of it went.
|
// where an unslotted one shares its parent's and has nothing to
|
||||||
if slot == parent_move {
|
// write. Its parent's box changing length is not that -- everything
|
||||||
|
// it drew is a fraction of that box, so the slot already above it
|
||||||
|
// carries the change and `on_resize` below decides whether the
|
||||||
|
// drawing survives it.
|
||||||
|
if slot == parent_move && old_region != region {
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
{
|
{
|
||||||
diag::bump(Counter::ReuseUnslotted);
|
diag::bump(Counter::ReuseUnslotted);
|
||||||
@@ -495,7 +504,9 @@ impl UiRenderState {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.moves.set(slot, region);
|
if slot != parent_move {
|
||||||
|
self.moves.set(slot, region);
|
||||||
|
}
|
||||||
self.keep_depth(id, depth);
|
self.keep_depth(id, depth);
|
||||||
let active = self.active.get_mut(&id).unwrap();
|
let active = self.active.get_mut(&id).unwrap();
|
||||||
active.region = region;
|
active.region = region;
|
||||||
@@ -612,7 +623,6 @@ impl UiRenderState {
|
|||||||
self.root_move = MoveIdx::NONE;
|
self.root_move = MoveIdx::NONE;
|
||||||
self.layers.clear();
|
self.layers.clear();
|
||||||
self.invalid_sizes.clear();
|
self.invalid_sizes.clear();
|
||||||
self.resize_marks.clear();
|
|
||||||
self.draw_started.clear();
|
self.draw_started.clear();
|
||||||
rsc.widgets_mut().needs_redraw.clear();
|
rsc.widgets_mut().needs_redraw.clear();
|
||||||
rsc.free();
|
rsc.free();
|
||||||
@@ -631,10 +641,7 @@ impl UiRenderState {
|
|||||||
// reader and gives each changing box its final constraints first.
|
// reader and gives each changing box its final constraints first.
|
||||||
while let Some(id) = {
|
while let Some(id) = {
|
||||||
let dirty = rsc.widgets().needs_redraw.iter().copied();
|
let dirty = rsc.widgets().needs_redraw.iter().copied();
|
||||||
match self.resized.iter().any(|&resized| resized) {
|
dirty.max_by_key(|&id| self.depth(id))
|
||||||
true => dirty.min_by_key(|&id| self.depth(id)),
|
|
||||||
false => dirty.max_by_key(|&id| self.depth(id)),
|
|
||||||
}
|
|
||||||
} {
|
} {
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
diag::bump(Counter::QueuePops);
|
diag::bump(Counter::QueuePops);
|
||||||
@@ -685,7 +692,7 @@ impl UiRenderState {
|
|||||||
widgets: &Widgets,
|
widgets: &Widgets,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.root_changed(root)
|
self.root_changed(root)
|
||||||
|| self.resized.iter().any(|&resized| resized)
|
|| self.root_axes_changed().iter().any(|&c| c)
|
||||||
|| widgets.has_updates()
|
|| widgets.has_updates()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +730,7 @@ impl UiRenderState {
|
|||||||
/// redraws a widget that's currently active (drawn)
|
/// redraws a widget that's currently active (drawn)
|
||||||
pub fn redraw(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) {
|
pub fn redraw(&mut self, id: WidgetId, rsc: &mut dyn UiRsc) {
|
||||||
self.draw_started.remove(&id);
|
self.draw_started.remove(&id);
|
||||||
if rsc.widgets().needs_redraw.contains(&id) && !self.resize_marks.contains(&id) {
|
if rsc.widgets().needs_redraw.contains(&id) {
|
||||||
self.invalid_sizes.insert(id);
|
self.invalid_sizes.insert(id);
|
||||||
}
|
}
|
||||||
// A widget can only answer whether its size changed by drawing in the
|
// A widget can only answer whether its size changed by drawing in the
|
||||||
@@ -736,9 +743,7 @@ impl UiRenderState {
|
|||||||
AXES.into_iter()
|
AXES.into_iter()
|
||||||
.any(|axis| pixel_len_changed(active.px.axis(axis), px.axis(axis)))
|
.any(|axis| pixel_len_changed(active.px.axis(axis), px.axis(axis)))
|
||||||
});
|
});
|
||||||
if (self.resized.iter().any(|&resized| resized) || box_changed)
|
if box_changed && let Some(top) = self.mark_readers(id, rsc) {
|
||||||
&& let Some(top) = self.mark_readers(id, rsc)
|
|
||||||
{
|
|
||||||
#[cfg(feature = "layout-diagnostics")]
|
#[cfg(feature = "layout-diagnostics")]
|
||||||
diag::bump(Counter::EagerReaderRedraws);
|
diag::bump(Counter::EagerReaderRedraws);
|
||||||
self.redraw(top, rsc);
|
self.redraw(top, rsc);
|
||||||
|
|||||||
+28
-2
@@ -191,6 +191,8 @@ impl Widget for ReadsOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads the output across one axis only, and says so: its drawing follows
|
||||||
|
/// a taller box on its own, so only a wider one is worth a draw.
|
||||||
struct ReadsWidth {
|
struct ReadsWidth {
|
||||||
draws: Rc<Cell<usize>>,
|
draws: Rc<Cell<usize>>,
|
||||||
}
|
}
|
||||||
@@ -200,12 +202,19 @@ impl Widget for ReadsWidth {
|
|||||||
self.draws.set(self.draws.get() + 1);
|
self.draws.set(self.draws.get() + 1);
|
||||||
Size::px((painter.output_len(Axis::X) / 4.0, 20.0).into())
|
Size::px((painter.output_len(Axis::X) / 4.0, 20.0).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_resize(&self, axis: Axis) -> OnResize {
|
||||||
|
match axis {
|
||||||
|
Axis::X => OnResize::Redraw,
|
||||||
|
Axis::Y => OnResize::Scale,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
||||||
let mut h = Harness::new((400, 200));
|
let mut h = Harness::new((400, 200));
|
||||||
let (leaf, draws) = counted(&mut h, Size::REST, OnResize::Redraw);
|
let (leaf, draws) = counted(&mut h, Size::REST, OnResize::Scale);
|
||||||
h.set_root(leaf);
|
h.set_root(leaf);
|
||||||
let settled = draws.get();
|
let settled = draws.get();
|
||||||
|
|
||||||
@@ -216,11 +225,28 @@ fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
draws.get(),
|
draws.get(),
|
||||||
settled,
|
settled,
|
||||||
"its box is the same fraction of a different output"
|
"a scaling drawing follows its box, and the output is one"
|
||||||
);
|
);
|
||||||
assert_corners!(h, leaf, (0, 0), (800, 100));
|
assert_corners!(h, leaf, (0, 0), (800, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The output is the root of the box chain, so a resize is a box that changed
|
||||||
|
/// length and `OnResize` answers for it -- there is not a second rule for the
|
||||||
|
/// window. A drawing that does not scale is redrawn whichever box moved.
|
||||||
|
#[test]
|
||||||
|
fn a_resize_redraws_what_does_not_scale() {
|
||||||
|
let mut h = Harness::new((400, 200));
|
||||||
|
let (leaf, draws) = counted(&mut h, Size::REST, OnResize::Redraw);
|
||||||
|
h.set_root(leaf);
|
||||||
|
let settled = draws.get();
|
||||||
|
|
||||||
|
h.resize((800, 100));
|
||||||
|
h.frame();
|
||||||
|
|
||||||
|
assert_eq!(draws.get(), settled + 1, "its box is a different length");
|
||||||
|
assert_corners!(h, leaf, (0, 0), (800, 100));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_resize_redraws_what_read_the_output() {
|
fn a_resize_redraws_what_read_the_output() {
|
||||||
let mut h = Harness::new((400, 200));
|
let mut h = Harness::new((400, 200));
|
||||||
|
|||||||
Reference in new issue
Block a user