The review pass over the two commits before it: - `LayoutHolds`'s comment said a pin does not compose into the parent. It does, where the box it pinned is the parent's own box, which is the one case where the parent's own length is what was pinned. - `DrawInfo::offer` was stored beside the two fields it is computed from. It is a method now, with the open question written where it is asked rather than only in the handoff. - `Painter::own` is `frame_own` beside `extent_own`, since a widget's own box is the extent and the frame is what it is a part of. - One expression for the length a rule gives a frame (`narrowed_by`) and one for what a widget answered (`ActiveData::measured`), each of which had two. - The counter said "the placement it was pinned to" for what is now a length; the deferral in `redraw` named the seeds that made it necessary before the last commit rather than the ones that do now; three comments claimed an inset that does not exist yet. No behaviour change: 108 suite tests, 20 core, the 11 generated cases, the same two shrinker seeds failing at 400/5, and `view` and `tabs` byte-identical to the renders taken before it.
716 lines
28 KiB
Rust
716 lines
28 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, or 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) frame_own: [Holds; 2],
|
|
/// The same for its own box.
|
|
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, 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 (local, extent) = frame_and_extent(
|
|
frame,
|
|
part_of(self.extent, place),
|
|
narrowed_by(declared, frame),
|
|
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,
|
|
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.frame_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.frame_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 own box a `place` names, in the coordinates that
|
|
/// box is in.
|
|
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 length a rule gives a child's frame, per axis: a fraction in it is a
|
|
/// fraction of the frame the child was given, which is the one length the
|
|
/// rule can mean.
|
|
pub(crate) fn narrowed_by(declared: [Option<LayoutLen>; 2], frame: UiRegion) -> [Option<Len>; 2] {
|
|
AXES.map(|axis| {
|
|
declared[axis as usize]
|
|
.map(|len| Len::from_parts(len.rel, len.px).within_len(frame.axis(axis).len()))
|
|
})
|
|
}
|
|
|
|
/// 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)
|
|
}
|