Three things the measurements asked for, all about how much a box that came from an answer costs. **A part in the box's own coordinates.** Saying "less eleven pixels at the end" in frame lengths from the box's start means reading how long the box is, and a container whose box is its own answer then depends on its own answer: `Pad` drew sixty-four times in one resize frame at seed 13, chasing its own width. `Part::Of` says the same thing as a part of the box, which composes without a length -- pixels are pixels wherever the box lands -- and what a child under it holds for maps back through that part onto the container's own box rather than onto the frame. **One axis of the box at a time.** `extent_len` pinned both axes, so a span dividing one of them held for one length of the other as well, and a resize broke every span whose cross-axis answer moved. **No lazy placement.** Leaving a child's answer to be placed at the end of the parent's draw, rather than as the child answers, was meant to save a recomposition. It costs one instead: the drawing is put in the part first and in the answer's box after, and where it does not hold for both that is two drawings rather than one. Seed 1 at depth 8 went from 391 widget draws on a resize to 29 with it gone. The test that pinned three draws for a numeric leaf in a span goes with it. Seed 1 at depth 8, widget draws / distinct widgets / update, against #18's head and against the commit this branch started from: | phase |e44dea3|34cafb6| here | | --- | --- | --- | --- | | cold | 369/261/10.6 | 463/274/13.3 | 516/288/12.0 | | repaint | 1 | 1 | 1 | | many | 157/95/0.33 | 263/108/0.59 | 187/119/0.52 | | size | 16/12/0.018 | 3/3 | 3/3/0.010 | | scroll | 2/0.002 | 1 | 1/0.004 | | resize | 13/13/0.019 | 22/15/0.032 | 24/76/0.090 | Seed 13 at depth 8 is where the protocol still costs: `many` 1091 draws against #18's 524, and `resize` 2215 against a frame #18 does not draw at all. Both are the same shape -- an answer measured in one box and drawn in another -- and the handoff says where that comes from. Checked: fmt, clippy with -D warnings, 108 suite tests, 20 core tests, the 11 generated cases, and the shrinker at 400 trees of depth 5, which fails seeds 2 (repaint) and 108 (reorder).
714 lines
29 KiB
Rust
714 lines
29 KiB
Rust
#[cfg(feature = "layout-diagnostics")]
|
|
use crate::layout_diagnostics::{self as diag, Counter};
|
|
use crate::{
|
|
Axis, Holds, LayoutHolds, LayoutLen, Len, Part, Place, Px, PxVec2, RegionAlign, RenderedText,
|
|
RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData, TextureHandle,
|
|
UiRegion, UiRenderState, UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
|
render::{
|
|
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind,
|
|
TexturePrimitive,
|
|
},
|
|
ui::render_state::DrawInfo,
|
|
};
|
|
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
|
|
|
/// makes your surfaces look pretty
|
|
pub struct Painter<'a> {
|
|
pub(super) state: &'a mut UiRenderState,
|
|
pub(super) rsc: &'a mut dyn UiRsc,
|
|
|
|
/// What a fraction this widget declares or reports is a fraction of, in
|
|
/// the coordinates of `move_idx`: forwarded from its parent unchanged
|
|
/// through a span, a stack or a scroll, and narrowed only by what was
|
|
/// decided above it -- a declared length, an inset, the root. Its length
|
|
/// is the same on every ask of the widget, which is what keeps a fraction
|
|
/// under it from being resolved twice.
|
|
pub(super) frame: UiRegion,
|
|
/// Where this widget's drawing goes, in the frame's own coordinates.
|
|
/// Everything it writes is in these coordinates, and its children are
|
|
/// placed as parts of it.
|
|
pub(super) extent: UiRegion,
|
|
/// The extent's symbolic length where this draw read it, which makes the
|
|
/// drawing one that holds for that length alone -- the way reading a
|
|
/// length in pixels makes it hold for that number of pixels.
|
|
pub(super) extent_len: [Option<Len>; 2],
|
|
/// The frame in pixels, which its children's frames are a length of:
|
|
/// threaded down rather than composed back up the chain, so every length
|
|
/// in layout is one multiply from its parent's and [`Holds::through`]
|
|
/// inverts exactly that.
|
|
pub(super) px: PxVec2,
|
|
pub(super) mask: MaskIdx,
|
|
pub(super) textures: Vec<TextureHandle>,
|
|
pub(super) primitives: Vec<RetainedPrimitive>,
|
|
pub(super) mask_region: Option<UiRegion>,
|
|
/// Only children whose answers were read constrain this widget's answer.
|
|
pub(super) answer_under: LayoutHolds,
|
|
pub(super) children: Vec<WidgetId>,
|
|
/// The children asked about so far, so the first place each was asked in
|
|
/// is the one recorded as its offer.
|
|
pub(super) offered: Vec<WidgetId>,
|
|
/// Whether this draw is at the place its parent first asked about, which
|
|
/// makes the questions it asks the ones a cold layout asks and their
|
|
/// answers the ones to keep.
|
|
pub(super) at_offer: bool,
|
|
/// The children whose size this widget read while drawing.
|
|
pub(super) size_deps: Vec<WidgetId>,
|
|
/// What this draw itself read of its frame in pixels, per axis: every
|
|
/// length until it reads one, then that one, unless it says otherwise.
|
|
pub(super) own: [Holds; 2],
|
|
/// The same for its extent.
|
|
pub(super) extent_own: [Holds; 2],
|
|
/// Dependencies of every child drawing, including unmeasured overlays.
|
|
pub(super) under: LayoutHolds,
|
|
/// The movable region this widget's primitives are positioned through:
|
|
/// its own when opted in, otherwise the nearest ancestor's.
|
|
pub(super) move_idx: MoveIdx,
|
|
pub layer: usize,
|
|
/// The layer this widget was entered on, which its children's layers are
|
|
/// counted from however far `layer` has walked.
|
|
pub(super) own_layer: usize,
|
|
pub(super) depth: usize,
|
|
pub(super) id: WidgetId,
|
|
}
|
|
|
|
impl<'a> Painter<'a> {
|
|
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
|
let kind = self.rsc.ui_mut().primitives.kind::<P>();
|
|
self.write(kind, primitive, region);
|
|
}
|
|
|
|
/// Takes the kind, for a caller writing many of one primitive.
|
|
fn write<P: Primitive>(&mut self, kind: PrimitiveKind<P>, primitive: P, region: UiRegion) {
|
|
self.write_resolved(kind, primitive, region, self.resolve(region));
|
|
}
|
|
|
|
/// A box in this widget's extent coordinates, composed into the
|
|
/// coordinates its move slot is in: through the extent, then through the
|
|
/// frame the extent is a part of. The same two steps a recomposition
|
|
/// replays, so a moved drawing lands where a cold one does.
|
|
fn resolve(&self, region: UiRegion) -> UiRegion {
|
|
region.within(&self.extent).within(&self.frame)
|
|
}
|
|
|
|
fn write_resolved<P: Primitive>(
|
|
&mut self,
|
|
kind: PrimitiveKind<P>,
|
|
primitive: P,
|
|
region: UiRegion,
|
|
resolved: UiRegion,
|
|
) {
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
diag::bump(Counter::PrimitiveWrites);
|
|
let h = self.state.layers.write(
|
|
self.layer,
|
|
PrimitiveInst {
|
|
kind,
|
|
id: self.id,
|
|
primitive,
|
|
region: resolved,
|
|
mask_idx: self.mask,
|
|
move_idx: self.move_idx,
|
|
},
|
|
);
|
|
self.push_primitive(RetainedPrimitive { handle: h, region });
|
|
}
|
|
|
|
fn push_primitive(&mut self, h: RetainedPrimitive) {
|
|
if self.mask != MaskIdx::NONE {
|
|
// TODO: I have no clue if this works at all :joy:
|
|
self.rsc.ui_mut().masks.push_ref(self.mask);
|
|
}
|
|
self.primitives.push(h);
|
|
}
|
|
|
|
/// Writes a primitive over the whole of this widget's own box.
|
|
pub fn primitive(&mut self, primitive: impl PrimitiveLike) {
|
|
let primitive = primitive.into_primitive(self);
|
|
self.primitive_at(primitive, UiRegion::FULL)
|
|
}
|
|
|
|
/// Writes a primitive in a part of this widget's own box, in that box's
|
|
/// coordinates.
|
|
pub fn primitive_within(&mut self, primitive: impl PrimitiveLike, region: UiRegion) {
|
|
let primitive = primitive.into_primitive(self);
|
|
self.primitive_at(primitive, region);
|
|
}
|
|
|
|
/// Sets a mask, in this widget's own box's coordinates.
|
|
pub fn set_mask(&mut self, region: UiRegion) {
|
|
self.mask_region = Some(region);
|
|
assert!(self.mask == MaskIdx::NONE);
|
|
let resolved = self.resolve(region);
|
|
let move_idx = self.move_idx;
|
|
self.mask = self.rsc.ui_mut().masks.push(Mask {
|
|
region: resolved,
|
|
move_idx,
|
|
});
|
|
}
|
|
|
|
/// Draws a widget in the whole of this widget's own box, with the frame
|
|
/// forwarded unchanged: what a container that is only a wrapper around
|
|
/// one child wants, and what every transparent container passes for the
|
|
/// frame.
|
|
pub fn widget<'s, W: ?Sized>(&'s mut self, id: &'s StrongWidget<W>) -> DrawResult<'s, 'a, W> {
|
|
self.widget_at(id, UiRegion::FULL, [Place::Within(Part::All); 2])
|
|
}
|
|
|
|
/// Draws a child, saying what its fractions are of and where its drawing
|
|
/// goes.
|
|
///
|
|
/// `frame` is that reference, in this widget's own frame coordinates:
|
|
/// [`UiRegion::FULL`] forwards this widget's frame, which is what a
|
|
/// container that only divides room passes, so a fraction under it means
|
|
/// the same wherever it sits and however deeply it is nested. Narrowing
|
|
/// it is for what is decided from above -- an inset's margins -- and a
|
|
/// declared length narrows it here.
|
|
///
|
|
/// `place` is where the drawing goes, per axis, as a part of this
|
|
/// widget's extent: see [`Place`]. A narrowed frame is its own extent,
|
|
/// since the narrowing is what said where the drawing goes.
|
|
pub fn widget_at<'s, W: ?Sized>(
|
|
&'s mut self,
|
|
id: &'s StrongWidget<W>,
|
|
frame: UiRegion,
|
|
place: [Place; 2],
|
|
) -> DrawResult<'s, 'a, W> {
|
|
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 narrow = AXES.map(|axis| {
|
|
let n = axis as usize;
|
|
// A rule's fraction is a fraction of the frame the child was
|
|
// given, which is the one length the rule can mean.
|
|
declared[n]
|
|
.map(|len| Len::from_parts(len.rel, len.px).within_len(frame.axis(axis).len()))
|
|
});
|
|
let (local, extent) = frame_and_extent(frame, part_of(self.extent, place), narrow, align);
|
|
let within = match local == UiRegion::FULL {
|
|
true => self.frame,
|
|
false => local.within(&self.frame),
|
|
};
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
if region_node {
|
|
diag::bump(Counter::RegionNodeDraws);
|
|
diag::region_node(id.id(), self.id, within);
|
|
}
|
|
// A child listed twice would be moved twice.
|
|
if !self.children.contains(&id.id()) {
|
|
self.children.push(id.id());
|
|
}
|
|
let first_ask = self.offer(id.id());
|
|
let offer_place = if first_ask {
|
|
place
|
|
} else {
|
|
self.state
|
|
.active
|
|
.get(&id.id())
|
|
.map_or(place, |a| a.offer_place)
|
|
};
|
|
let px = local.size().to_px(self.px);
|
|
// The answer and what it holds for, both about the place asked in.
|
|
// The child's record may say something else once its drawing has been
|
|
// placed: a drawing made again in its placed box holds for that box.
|
|
let (size, answer_holds, holds) = self.state.draw_inner(
|
|
id.id(),
|
|
DrawInfo {
|
|
layer: self.layer,
|
|
parent: Some(self.id),
|
|
depth: self.depth + 1,
|
|
parent_move: self.move_idx,
|
|
region_node,
|
|
mask: self.mask,
|
|
frame: local,
|
|
frame_abs: within,
|
|
part: extent,
|
|
place,
|
|
offer_place,
|
|
// The question its parent measured it by, asked again: the
|
|
// same widget in the same place, however this draw came
|
|
// about.
|
|
offer: place == offer_place,
|
|
px,
|
|
},
|
|
None,
|
|
self.rsc,
|
|
);
|
|
let compose = |holds| in_parent(holds, local, extent, place, declared);
|
|
self.under = self.under.and(compose(holds));
|
|
DrawResult {
|
|
child: id,
|
|
painter: self,
|
|
size: in_parent_frame(size, local.size(), declared),
|
|
answer_holds: compose(answer_holds),
|
|
}
|
|
}
|
|
|
|
/// Takes back a child that was drawn only to find out how long it is.
|
|
/// Its drawing is dropped and it is not one of this widget's children
|
|
/// this frame; what it answered is still something this widget asked.
|
|
pub fn undraw<W: ?Sized>(&mut self, id: &StrongWidget<W>) {
|
|
self.children.retain(|child| *child != id.id());
|
|
self.state.undraw_rec(id.id(), self.rsc);
|
|
}
|
|
|
|
/// What a widget's rules declare its lengths to be, which whoever draws
|
|
/// it resolves into its frame. Reading them depends on nothing -- the box
|
|
/// that comes of them is kept on the child, and `redraw` compares it
|
|
/// there.
|
|
fn declared_lens<W: ?Sized>(&self, id: &StrongWidget<W>) -> [Option<LayoutLen>; 2] {
|
|
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.
|
|
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))
|
|
});
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
diag::hint_read(id.id(), self.id, axis, hint);
|
|
match hint {
|
|
Some(hint) => {
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
diag::bump(Counter::HintHits);
|
|
self.depend_on(id);
|
|
Some(hint)
|
|
}
|
|
None => {
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
diag::bump(Counter::HintMisses);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Whether this is the first box a child is asked about in during a draw
|
|
/// that is itself the one its parent measured -- the question a cold
|
|
/// layout asks, whose answer is the one to keep. A drawing made again in
|
|
/// a box chosen from an answer asks about that box instead, and what it
|
|
/// hears back is not a measurement of anything.
|
|
fn offer(&mut self, child: WidgetId) -> bool {
|
|
if !self.at_offer || self.offered.contains(&child) {
|
|
return false;
|
|
}
|
|
self.offered.push(child);
|
|
true
|
|
}
|
|
|
|
fn depend_on<W: ?Sized>(&mut self, child: &StrongWidget<W>) {
|
|
if !self.size_deps.contains(&child.id()) {
|
|
self.size_deps.push(child.id());
|
|
}
|
|
}
|
|
|
|
pub fn render_text<'b>(
|
|
&mut self,
|
|
buffer: &'b mut TextBuffer,
|
|
attrs: &TextAttrs,
|
|
width: Option<f32>,
|
|
) -> &'b RenderedText {
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
diag::render_text(self.id, self.rsc.widgets().label(self.id), width);
|
|
let ui = self.rsc.ui_mut();
|
|
ui.text.render(buffer, attrs, width)
|
|
}
|
|
|
|
/// Writes glyphs in the selected frame or extent coordinates.
|
|
// TODO: merge the text methods into the primitive ones.
|
|
pub fn glyphs(&mut self, text: &RenderedText, origin: UiRegion) {
|
|
// Glyph offsets and sizes are pixels, which compose additively.
|
|
// Only the shared origin needs composing through the extent.
|
|
let resolved = self.resolve(origin);
|
|
let kind = self.rsc.ui_mut().primitives.kind::<GlyphPrimitive>();
|
|
for glyph in text.glyphs.iter() {
|
|
let place = |mut region: UiRegion| {
|
|
region.x.end = region.x.start;
|
|
region.y.end = region.y.start;
|
|
let mut region = region.offset(UiVec2::from_px(glyph.offset));
|
|
let size = PxVec2::new(
|
|
Px::from_int(glyph.entry.width as i32),
|
|
Px::from_int(glyph.entry.height as i32),
|
|
);
|
|
region.x.end = region.x.start.offset(size.x);
|
|
region.y.end = region.y.start.offset(size.y);
|
|
region
|
|
};
|
|
self.write_resolved(
|
|
kind,
|
|
GlyphPrimitive {
|
|
uv_min: glyph.entry.uv_min,
|
|
uv_max: glyph.entry.uv_max,
|
|
layer: glyph.entry.layer,
|
|
color: text.color,
|
|
flags: glyph.entry.flags(),
|
|
},
|
|
place(origin),
|
|
place(resolved),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// The symbolic length of this widget's own box along one axis, in the
|
|
/// lengths of its frame that it places its children in. Reading it pins
|
|
/// the drawing to that length -- and to nothing about where the box
|
|
/// starts, which is what lets a container move without being drawn
|
|
/// again. One axis at a time, because a container that divides one axis
|
|
/// holds for any length of the other.
|
|
pub fn extent_len(&mut self, axis: Axis) -> Len {
|
|
let len = self.extent.axis(axis).len();
|
|
self.extent_len[axis as usize] = Some(len);
|
|
len
|
|
}
|
|
|
|
/// Where this widget sits in a box longer than the length it takes. A
|
|
/// widget that positions its own content reads it to place that content
|
|
/// the way the box around it would have placed the widget.
|
|
pub fn alignment(&self) -> RegionAlign {
|
|
self.rsc.widgets().alignment(self.id)
|
|
}
|
|
|
|
/// Whether a rule beside this widget gives its length on `axis` outright,
|
|
/// which makes whatever it reports for that axis moot. A rule that only
|
|
/// bounds the length is not one of these: the answer is still the
|
|
/// widget's to give, and something still has to work it out.
|
|
///
|
|
/// The widget under a rule does not otherwise learn of it -- this is for
|
|
/// a container deciding whether reading its children across an axis is
|
|
/// worth anything, since reading one is also what makes its own size
|
|
/// depend on it.
|
|
pub fn has_exact_size(&self, axis: Axis) -> bool {
|
|
self.rsc
|
|
.widgets()
|
|
.size_rules(self.id)
|
|
.axis(axis)
|
|
.exact()
|
|
.is_some()
|
|
}
|
|
|
|
/// This widget's own box in pixels. Reading it makes the drawing one
|
|
/// that holds for this box only, until `holds` says how far it goes.
|
|
pub fn px_size(&mut self) -> PxVec2 {
|
|
PxVec2::new(self.px_len(Axis::X), self.px_len(Axis::Y))
|
|
}
|
|
|
|
/// One axis of this widget's own box in pixels. Prefer this to
|
|
/// [`Self::px_size`] when the other axis cannot affect the drawing.
|
|
pub fn px_len(&mut self, axis: Axis) -> Px {
|
|
let part = self.extent.axis(axis).len();
|
|
let len = part.to_px(self.px.axis(axis));
|
|
let own = &mut self.extent_own[axis as usize];
|
|
if *own == Holds::ANY {
|
|
*own = Holds::at(len);
|
|
}
|
|
len
|
|
}
|
|
|
|
/// The lengths of this widget's own box on `axis` that what it is drawing
|
|
/// holds for -- the same primitives, in the same fractions and offsets
|
|
/// of the box, and the same reported size. A widget that read its length
|
|
/// in pixels holds for that one alone until it says otherwise.
|
|
pub fn holds(&mut self, axis: Axis, holds: impl Into<Holds>) {
|
|
let part = self.extent.axis(axis).len();
|
|
let holds = holds.into();
|
|
debug_assert!(
|
|
holds.contains(part.to_px(self.px.axis(axis))),
|
|
"'{}' ({:?}) says its drawing holds for lengths that leave out its own box",
|
|
self.label(),
|
|
self.id
|
|
);
|
|
self.extent_own[axis as usize] = holds;
|
|
}
|
|
|
|
/// One axis of this widget's frame in pixels -- what a fraction of its
|
|
/// area resolves against, and so what a container divides among its
|
|
/// children. Its own box is a part of this one.
|
|
pub fn frame_px_len(&mut self, axis: Axis) -> Px {
|
|
let len = self.px.axis(axis);
|
|
let own = &mut self.own[axis as usize];
|
|
if *own == Holds::ANY {
|
|
*own = Holds::at(len);
|
|
}
|
|
len
|
|
}
|
|
|
|
/// [`Self::holds`] stated about the frame rather than about this
|
|
/// widget's own box, for a container whose drawing turns on what its
|
|
/// fractions are of rather than on the part of it it took.
|
|
pub fn frame_holds(&mut self, axis: Axis, holds: impl Into<Holds>) {
|
|
let holds = holds.into();
|
|
debug_assert!(
|
|
holds.contains(self.px.axis(axis)),
|
|
"'{}' ({:?}) says its drawing holds for lengths that leave out its frame",
|
|
self.label(),
|
|
self.id
|
|
);
|
|
self.own[axis as usize] = holds;
|
|
}
|
|
|
|
pub fn text_data(&mut self) -> &mut TextData {
|
|
&mut self.rsc.ui_mut().text
|
|
}
|
|
|
|
pub fn child_layer(&mut self) {
|
|
self.layer = self.state.layers.child(self.layer);
|
|
}
|
|
|
|
/// The layer this widget's `n`th child draws on, addressed rather than
|
|
/// walked to. A container that measures one child by drawing it can ask
|
|
/// on the layer that child will end up on, and then the second ask is a
|
|
/// reuse rather than a second drawing on another layer.
|
|
pub fn child_layer_at(&mut self, n: usize) {
|
|
let mut at = self.state.layers.child(self.own_layer);
|
|
for _ in 0..n {
|
|
at = self.state.layers.next(at);
|
|
}
|
|
self.layer = at;
|
|
}
|
|
|
|
pub fn next_layer(&mut self) {
|
|
self.layer = self.state.layers.next(self.layer);
|
|
}
|
|
|
|
pub fn label(&self) -> &str {
|
|
&self.rsc.widgets().data(self.id).unwrap().label
|
|
}
|
|
|
|
pub fn id(&self) -> &WidgetId {
|
|
&self.id
|
|
}
|
|
}
|
|
|
|
/// A child that has just been drawn. Reading its size records that this
|
|
/// widget's own size depends on it; dropping it without reading draws the
|
|
/// child and leaves the parent independent of what it came to.
|
|
pub struct DrawResult<'p, 'a, W: ?Sized> {
|
|
painter: &'p mut Painter<'a>,
|
|
child: &'p StrongWidget<W>,
|
|
size: Size,
|
|
answer_holds: LayoutHolds,
|
|
}
|
|
|
|
impl<W: ?Sized> DrawResult<'_, '_, W> {
|
|
pub fn size(self) -> Size {
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
{
|
|
diag::bump(Counter::SizeReads);
|
|
diag::size_read(self.child.id(), self.painter.id, self.size);
|
|
}
|
|
self.painter.depend_on(self.child);
|
|
self.painter.answer_under = self.painter.answer_under.and(self.answer_holds);
|
|
self.size
|
|
}
|
|
|
|
pub fn len(self, axis: Axis) -> LayoutLen {
|
|
self.size().axis(axis)
|
|
}
|
|
}
|
|
|
|
/// What `Painter::primitive` takes: a primitive, or something that yields one
|
|
/// and does whatever else drawing it needs.
|
|
pub trait PrimitiveLike {
|
|
type Primitive: Primitive;
|
|
fn into_primitive(self, painter: &mut Painter) -> Self::Primitive;
|
|
}
|
|
|
|
impl<P: Primitive> PrimitiveLike for P {
|
|
type Primitive = P;
|
|
fn into_primitive(self, _: &mut Painter) -> P {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl PrimitiveLike for &TextureHandle {
|
|
type Primitive = TexturePrimitive;
|
|
|
|
/// Retains a share of the handle, so the slot the primitive names cannot
|
|
/// be freed and reused while it is still drawn.
|
|
fn into_primitive(self, painter: &mut Painter) -> TexturePrimitive {
|
|
painter.textures.push(self.clone());
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
/// What a child depends on, said about the boxes the widget that drew it
|
|
/// has rather than the ones the child was given.
|
|
///
|
|
/// `frame` is the child's frame in this widget's frame coordinates and
|
|
/// `extent` the box it was given, in the child's own frame coordinates. Both
|
|
/// reach it as one length, so what it holds for maps back through that
|
|
/// length exactly -- and where the box it was given is this widget's own,
|
|
/// what it says about that box is what this widget can say about its own.
|
|
pub(crate) fn in_parent(
|
|
holds: LayoutHolds,
|
|
frame: UiRegion,
|
|
extent: UiRegion,
|
|
place: [Place; 2],
|
|
declared: [Option<LayoutLen>; 2],
|
|
) -> LayoutHolds {
|
|
let mut result = LayoutHolds::ANY;
|
|
for axis in AXES {
|
|
let n = axis as usize;
|
|
let frame_len = frame.axis(axis).len();
|
|
result.frame[n] = holds.frame[n].through(frame_len);
|
|
match (place[n].part(), declared[n]) {
|
|
// Its box is this widget's own, or a part of it in that box's
|
|
// own lengths: so what it holds for is a range on this widget's
|
|
// own box, which is what lets that box move without a redraw. A
|
|
// length it pinned is this widget's length wherever the part is
|
|
// the whole of it, and pins the same way.
|
|
(Part::All, None) => {
|
|
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.
|
|
(Part::Of(span), None) => {
|
|
result.extent[n] = holds.extent[n].through(span.len());
|
|
}
|
|
// Its box is a part of this widget's frame: 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.
|
|
_ => {
|
|
result.frame[n] = result.frame[n].and(
|
|
holds.extent[n]
|
|
.through(extent.axis(axis).len())
|
|
.through(frame_len),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
result
|
|
}
|
|
|
|
/// A child's answer as lengths of the parent's own region. A widget reports
|
|
/// a fraction of its own region, and `of` is that region as a length of this
|
|
/// one. Pixels come through untouched, being that many pixels wherever they
|
|
/// end up. A declared axis is already the parent's: it resolved the rule in
|
|
/// its own region, and the rule is what the report says.
|
|
fn in_parent_frame(size: Size, of: UiVec2, declared: [Option<LayoutLen>; 2]) -> Size {
|
|
let mut size = size;
|
|
for (axis, declared) in AXES.into_iter().zip(declared) {
|
|
if declared.is_none() {
|
|
*size.axis_mut(axis) = size.axis(axis).within_len(of.axis(axis));
|
|
}
|
|
}
|
|
size
|
|
}
|
|
|
|
/// What a widget declares a length of its box to be. `leftover` is not one: a
|
|
/// share of what is left over is only a length to the widget dividing one,
|
|
/// so it passes up in the size instead.
|
|
pub(crate) fn declared_lens(widgets: &Widgets, id: WidgetId) -> [Option<LayoutLen>; 2] {
|
|
let rules = widgets.size_rules(id);
|
|
let widget = widgets.get_dyn(id);
|
|
AXES.map(|axis| {
|
|
rules.axis(axis).declared().or_else(|| {
|
|
// A hint still narrows the box where no rule does, which is how a
|
|
// widget with a natural pixel size -- an image, a gap -- gets that
|
|
// size rather than the whole offer. That is the offer's business
|
|
// rather than a declaration's, and this falls away once a widget
|
|
// occupies its reported size inside the box it was offered.
|
|
widget
|
|
.and_then(|widget| widget.size_hint(axis))
|
|
.filter(|len| len.leftover == Weight::ZERO)
|
|
})
|
|
})
|
|
}
|
|
|
|
/// Whether what a widget reported along an axis is the whole of the box it
|
|
/// is in rather than a part to be placed inside it. A share fills, because a
|
|
/// share is a length only to whoever divides one, and whoever did is the one
|
|
/// that handed down this box. A declared axis does too: the rule already gave
|
|
/// the region its length, and the rule's length is what the widget reports
|
|
/// there. And an axis the parent decided from the answer is
|
|
/// the answer already.
|
|
pub(crate) fn fills(reported: LayoutLen, declared: Option<LayoutLen>, decided: bool) -> bool {
|
|
reported.leftover != Weight::ZERO || declared.is_some() || decided
|
|
}
|
|
|
|
/// Where a widget's drawing goes inside the part its parent gave it: what
|
|
/// it reported, on the side of the part its alignment says, and the whole
|
|
/// part wherever the answer fills it.
|
|
///
|
|
/// The length it reported is a length of its frame, and the part is one too,
|
|
/// so this takes one from the other rather than composing it into the part.
|
|
/// That is what makes a fraction the same fraction wherever the part it is
|
|
/// placed in sits and however long it is -- the fraction is resolved once,
|
|
/// here, against the frame it was reported of.
|
|
pub(crate) fn placed_extent(
|
|
part: UiRegion,
|
|
size: Size,
|
|
declared: [Option<LayoutLen>; 2],
|
|
fill: [bool; 2],
|
|
align: RegionAlign,
|
|
) -> UiRegion {
|
|
let mut placed = part;
|
|
for axis in AXES {
|
|
let n = axis as usize;
|
|
let reported = size.axis(axis);
|
|
if fills(reported, declared[n], fill[n]) {
|
|
continue;
|
|
}
|
|
let len = Len::from_parts(reported.rel, reported.px);
|
|
let span = placed.axis_mut(axis);
|
|
span.start += (span.len() - len).scale(align.axis(axis).rel());
|
|
span.end = span.start + len;
|
|
}
|
|
placed
|
|
}
|
|
|
|
/// The part of a widget's extent a `place` names, in the coordinates its
|
|
/// extent is in: a span is measured in frame lengths from where the extent
|
|
/// starts, so nothing under it depends on where that is, and an extent that
|
|
/// moved re-places every child by re-adding its start.
|
|
pub(crate) fn part_of(extent: UiRegion, place: [Place; 2]) -> UiRegion {
|
|
let mut part = extent;
|
|
for axis in AXES {
|
|
*part.axis_mut(axis) = place[axis as usize].part().of(*extent.axis(axis));
|
|
}
|
|
part
|
|
}
|
|
|
|
/// The frame a child is asked in and the box its drawing goes in, both in
|
|
/// the coordinates of the widget asking.
|
|
///
|
|
/// `frame` is what the caller said the child's fractions are of, and `part`
|
|
/// what of the caller's own box the drawing takes. `narrow` is the length a
|
|
/// declared rule gives the frame, which makes the frame the box the drawing
|
|
/// goes in -- a rule is what decided where it goes, and there is nothing
|
|
/// left to place inside it. A caller that narrowed the frame itself said the
|
|
/// same thing.
|
|
///
|
|
/// The length is the caller's to supply so that a widget asked again gets
|
|
/// the frame it already has rather than a second resolution of its rule.
|
|
pub(crate) fn frame_and_extent(
|
|
mut frame: UiRegion,
|
|
part: UiRegion,
|
|
narrow: [Option<Len>; 2],
|
|
align: RegionAlign,
|
|
) -> (UiRegion, UiRegion) {
|
|
let mut extent = part;
|
|
for (axis, narrow) in AXES.into_iter().zip(narrow) {
|
|
let span = frame.axis_mut(axis);
|
|
let narrowed = match narrow {
|
|
Some(len) => {
|
|
let slot = part.axis(axis);
|
|
let start = slot.start + (slot.len() - len).scale(align.axis(axis).rel());
|
|
*span = UiSpan::new(start, start + len);
|
|
true
|
|
}
|
|
None => *span != UiSpan::FULL,
|
|
};
|
|
if narrowed {
|
|
*extent.axis_mut(axis) = UiSpan::FULL;
|
|
}
|
|
}
|
|
(frame, extent)
|
|
}
|