Make Iris layout dependencies explicit
This commit is contained in:
1 parent
2bc0ff1866
commit
3ae034a47b
25 files changed
+609
-186
No files matched your search
+92
-20
@@ -25,12 +25,46 @@ pub struct Painter<'a> {
|
||||
/// Previous handles, consumed in draw order and freed if left over.
|
||||
pub(super) recycle: std::iter::Peekable<std::vec::IntoIter<PrimitiveHandle>>,
|
||||
pub(super) children: Vec<WidgetId>,
|
||||
pub(super) reuse_child_sizes: bool,
|
||||
pub(super) size_dependencies: Vec<WidgetId>,
|
||||
pub(super) size: Option<Size>,
|
||||
/// Whether a retained child's length on each axis is still valid. A
|
||||
/// child's length may change when the parent's orthogonal extent changes
|
||||
/// (most importantly, wrapped text gets taller when it gets narrower),
|
||||
/// but not merely because a content-sized parent grew along that same
|
||||
/// axis around one of its siblings.
|
||||
pub(super) reuse_child_sizes: [bool; 2],
|
||||
pub layer: usize,
|
||||
pub(super) id: WidgetId,
|
||||
}
|
||||
|
||||
/// A child draw whose size has not necessarily been observed by its parent.
|
||||
/// Holding this value keeps the painter borrowed, so `.size()` can only name
|
||||
/// the child from the immediately preceding draw.
|
||||
pub struct DrawResult<'p, 'a> {
|
||||
painter: &'p mut Painter<'a>,
|
||||
child: WidgetId,
|
||||
}
|
||||
|
||||
impl DrawResult<'_, '_> {
|
||||
/// Return the child's reported size and record the layout dependency.
|
||||
pub fn size(self) -> Size {
|
||||
if !self.painter.size_dependencies.contains(&self.child) {
|
||||
self.painter.size_dependencies.push(self.child);
|
||||
}
|
||||
self.painter.state.active[&self.child].size
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Painter<'a> {
|
||||
/// Record the size this widget used. Every `Widget::draw` calls this
|
||||
/// exactly once; parents observe it through [`DrawResult::size`].
|
||||
pub fn set_size(&mut self, size: Size) {
|
||||
assert!(
|
||||
self.size.replace(size).is_none(),
|
||||
"a widget set its size more than once during one draw"
|
||||
);
|
||||
}
|
||||
|
||||
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
||||
self.write_primitive(primitive, region, Drawn::Yes);
|
||||
}
|
||||
@@ -207,15 +241,19 @@ impl<'a> Painter<'a> {
|
||||
self.mask = self.own_mask;
|
||||
}
|
||||
|
||||
/// Draws a widget within this widget's region, returning the size it
|
||||
/// reported using.
|
||||
pub fn widget<W: ?Sized>(&mut self, id: &StrongWidget<W>) -> Size {
|
||||
/// Draw a widget within this widget's region. Reading the result's size
|
||||
/// records that this widget's layout depends on the child.
|
||||
pub fn widget<'p, W: ?Sized>(&'p mut self, id: &StrongWidget<W>) -> DrawResult<'p, 'a> {
|
||||
self.widget_at(id, self.region)
|
||||
}
|
||||
|
||||
/// Draws a widget somewhere within this one.
|
||||
/// Useful for drawing child widgets in select areas.
|
||||
pub fn widget_within<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) -> Size {
|
||||
pub fn widget_within<'p, W: ?Sized>(
|
||||
&'p mut self,
|
||||
id: &StrongWidget<W>,
|
||||
region: UiRegion,
|
||||
) -> DrawResult<'p, 'a> {
|
||||
self.widget_at(id, region.within(&self.region))
|
||||
}
|
||||
|
||||
@@ -260,17 +298,29 @@ impl<'a> Painter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn known_len<W: ?Sized>(&self, id: &StrongWidget<W>, axis: Axis) -> Option<Len> {
|
||||
if let Some(len) = self.rsc.widgets().get_dyn(id.id())?.size_hint(axis) {
|
||||
return Some(len.fold_dp(self.density()));
|
||||
pub fn known_len<W: ?Sized>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Option<Len> {
|
||||
let len = if let Some(len) = self.rsc.widgets().get_dyn(id.id())?.size_hint(axis) {
|
||||
Some(len.fold_dp(self.density()))
|
||||
} else if !self.reuse_child_sizes[match axis {
|
||||
Axis::X => 0,
|
||||
Axis::Y => 1,
|
||||
}] || self.rsc.widgets().needs_redraw.contains(&id.id())
|
||||
{
|
||||
None
|
||||
} else {
|
||||
self.state.active.get(&id.id()).map(|a| a.size.axis(axis))
|
||||
};
|
||||
if len.is_some() && !self.size_dependencies.contains(&id.id()) {
|
||||
self.size_dependencies.push(id.id());
|
||||
}
|
||||
if !self.reuse_child_sizes || self.rsc.widgets().needs_redraw.contains(&id.id()) {
|
||||
return None;
|
||||
}
|
||||
self.state.active.get(&id.id()).map(|a| a.size.axis(axis))
|
||||
len
|
||||
}
|
||||
|
||||
fn widget_at<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) -> Size {
|
||||
fn widget_at<'p, W: ?Sized>(
|
||||
&'p mut self,
|
||||
id: &StrongWidget<W>,
|
||||
region: UiRegion,
|
||||
) -> DrawResult<'p, 'a> {
|
||||
self.children.push(id.id());
|
||||
// Passed directly rather than looked up from `self.active`: this
|
||||
// widget's own `ActiveData` (which would carry its `move_slot`) is
|
||||
@@ -289,19 +339,26 @@ impl<'a> Painter<'a> {
|
||||
self.mask,
|
||||
Retained::default(),
|
||||
self.rsc,
|
||||
)
|
||||
);
|
||||
DrawResult {
|
||||
painter: self,
|
||||
child: id.id(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Place an already-drawn child's used area, redrawing only if its size changes.
|
||||
pub fn place<W: ?Sized>(&mut self, id: &StrongWidget<W>, region: UiRegion) -> Size {
|
||||
pub fn place<'p, W: ?Sized>(
|
||||
&'p mut self,
|
||||
id: &StrongWidget<W>,
|
||||
region: UiRegion,
|
||||
) -> DrawResult<'p, 'a> {
|
||||
let region = region.within(&self.region);
|
||||
let retained = self
|
||||
.state
|
||||
.active
|
||||
.get(&id.id())
|
||||
.map(|active| (active.layer, active.mask));
|
||||
if let Some(size) = self.state.place(id.id(), region, self.rsc) {
|
||||
size
|
||||
if self.state.place(id.id(), region, self.rsc).is_some() {
|
||||
} else if let Some((layer, mask)) = retained {
|
||||
self.children.push(id.id());
|
||||
self.rsc.widgets_mut().needs_redraw.insert(id.id());
|
||||
@@ -315,9 +372,24 @@ impl<'a> Painter<'a> {
|
||||
mask,
|
||||
Retained::default(),
|
||||
self.rsc,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
self.widget_at(id, region)
|
||||
self.children.push(id.id());
|
||||
let parent_move_slot = self.child_move_slot.unwrap_or(self.move_slot);
|
||||
self.state.draw_inner(
|
||||
self.layer,
|
||||
id.id(),
|
||||
region,
|
||||
Some(self.id),
|
||||
parent_move_slot.idx() as u32,
|
||||
self.mask,
|
||||
Retained::default(),
|
||||
self.rsc,
|
||||
);
|
||||
}
|
||||
DrawResult {
|
||||
painter: self,
|
||||
child: id.id(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +398,7 @@ impl<'a> Painter<'a> {
|
||||
id: &StrongWidget<W>,
|
||||
used: Size,
|
||||
within: UiRegion,
|
||||
) -> Size {
|
||||
) -> DrawResult<'_, 'a> {
|
||||
let region = self.fit_region(used, within);
|
||||
self.place(id, region)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user