A widget that resolves a window length in pixels depends on that window wherever the length is a fraction of it, and nothing was recording that: Painter::to_px replaces window_px_len and pins the window it read, while a length that is only pixels is that many pixels in any window and pins nothing. Span still states the range it actually branched on, which replaces the pin with something wider. Scroll is where it showed: its content's answer is a window length now, so a viewport whose own box does not change with the window -- 40 px of a branch's box -- kept an end-snapped offset from the window before. Seed 942 at depth 6 under resize, pinned as unsettled::resizing_under_a_short_scroll_snaps_its_window_tall_content_again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
728 lines
29 KiB
Rust
728 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, Rel,
|
|
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, Placing},
|
|
};
|
|
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,
|
|
|
|
/// This widget's frame, per axis: a length of the window, and what a
|
|
/// fraction it or anything under it declares or reports is a fraction
|
|
/// of. A length rather than a box, so padding can take from both the
|
|
/// frame and the box without either becoming the other.
|
|
pub(super) frame: UiVec2,
|
|
/// Where this widget's drawing goes, in its region node's coordinates.
|
|
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 window in pixels. Frames and boxes become pixels against this one
|
|
/// unit, regardless of region-node boundaries.
|
|
pub(super) window: 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 whose size this widget read while drawing.
|
|
pub(super) size_deps: Vec<WidgetId>,
|
|
/// What this draw itself read of the window in pixels, per axis: every
|
|
/// window until it reads one, then that one, unless it says otherwise.
|
|
pub(super) frame_own: [Holds; 2],
|
|
/// Its frame's symbolic length where this draw read it, which makes the
|
|
/// drawing one that holds for that frame alone.
|
|
pub(super) frame_own_len: [Option<Len>; 2],
|
|
/// The window reads' equivalent for its own box.
|
|
pub(super) extent_own: [Holds; 2],
|
|
/// What each child's drawing depends on. Asking a child again replaces
|
|
/// its drawing, so it replaces this too rather than narrowing it.
|
|
pub(super) under: Vec<(WidgetId, 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 its region
|
|
/// node's coordinates.
|
|
fn resolve(&self, region: UiRegion) -> UiRegion {
|
|
region.within(&self.extent)
|
|
}
|
|
|
|
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, [None; 2], [Place::Within(Part::All); 2])
|
|
}
|
|
|
|
/// Asks a child, saying what its fractions are of and where it is asked.
|
|
///
|
|
/// `narrow` is a length this widget decided for the child's frame, per
|
|
/// axis, as a length of this widget's own frame: a resolved share, or a
|
|
/// box a sibling's answer decided. `None` 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. A declared length narrows the frame here whatever the
|
|
/// caller says. A narrowed frame is placed in the part by the child's
|
|
/// alignment and is the box the child is asked in.
|
|
///
|
|
/// `place` is where the child is asked, per axis, as a part of this
|
|
/// widget's box: see [`Place`]. The child draws once, in that box, and
|
|
/// its answer is placed inside it by re-expressing the drawing. Nothing
|
|
/// is drawn again in a box an answer chose; a container that puts the
|
|
/// answer somewhere else says so with [`Self::place_at`].
|
|
pub fn widget_at<'s, W: ?Sized>(
|
|
&'s mut self,
|
|
id: &'s StrongWidget<W>,
|
|
narrow: [Option<Len>; 2],
|
|
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 (frame, extent) =
|
|
frame_and_extent(self.extent, self.frame, place, narrow, declared, align);
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
if region_node {
|
|
diag::bump(Counter::RegionNodeDraws);
|
|
diag::region_node(id.id(), self.id, extent);
|
|
}
|
|
// A child listed twice would be moved twice.
|
|
let re_asked = self.children.contains(&id.id());
|
|
if !re_asked {
|
|
self.children.push(id.id());
|
|
}
|
|
let px = frame.to_px(self.window);
|
|
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,
|
|
part: extent,
|
|
place,
|
|
offer_place: place,
|
|
narrow,
|
|
re_asked,
|
|
px,
|
|
},
|
|
None,
|
|
self.rsc,
|
|
);
|
|
let holds = self.in_parent(holds, extent, place, narrow, declared);
|
|
let answer_holds = self.in_parent(answer_holds, extent, place, narrow, declared);
|
|
match self.under.iter_mut().find(|(child, _)| *child == id.id()) {
|
|
Some((_, kept)) => *kept = holds,
|
|
None => self.under.push((id.id(), holds)),
|
|
}
|
|
DrawResult {
|
|
child: id,
|
|
painter: self,
|
|
size,
|
|
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.under.retain(|(child, _)| *child != id.id());
|
|
self.state.undraw_rec(id.id(), self.rsc);
|
|
}
|
|
|
|
/// Puts a child asked about in this draw somewhere else in this
|
|
/// widget's box: its answer, placed in this part instead. The drawing
|
|
/// is re-expressed there rather than made again -- what a row does once
|
|
/// it knows every slot, having measured each child from its cursor.
|
|
pub fn place_at<W: ?Sized>(&mut self, id: &StrongWidget<W>, place: [Place; 2]) {
|
|
debug_assert!(
|
|
self.children.contains(&id.id()),
|
|
"'{}' placed a child it did not ask about in this draw",
|
|
self.label()
|
|
);
|
|
let at = self.placing();
|
|
self.state.place_in(id.id(), &at, place, self.rsc);
|
|
}
|
|
|
|
/// This widget as the thing its children are placed within.
|
|
fn placing(&self) -> Placing {
|
|
Placing {
|
|
id: self.id,
|
|
extent: self.extent,
|
|
frame: self.frame,
|
|
window: self.window,
|
|
depth: self.depth,
|
|
move_idx: self.move_idx,
|
|
mask: self.mask,
|
|
}
|
|
}
|
|
|
|
/// 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
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
/// The symbolic length of this widget's frame along one axis: what a
|
|
/// fraction it or anything under it declares is a fraction of. A
|
|
/// container reads it to hand a length of it down -- padding, which
|
|
/// takes its pixels off. Reading it pins the drawing to that frame, the
|
|
/// way [`Self::extent_len`] pins it to the box.
|
|
pub fn frame_len(&mut self, axis: Axis) -> Len {
|
|
let len = self.frame.axis(axis);
|
|
self.frame_own_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.window.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.window.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;
|
|
}
|
|
|
|
/// A window length in pixels, which is what every length in layout is
|
|
/// measured in. Reading one pins the drawing to this window wherever the
|
|
/// length is a fraction of it; one that is only pixels is that many
|
|
/// pixels in any window and pins nothing.
|
|
pub fn to_px(&mut self, len: Len, axis: Axis) -> Px {
|
|
let window = self.window.axis(axis);
|
|
if len.rel != Rel::ZERO {
|
|
let own = &mut self.frame_own[axis as usize];
|
|
if *own == Holds::ANY {
|
|
*own = Holds::at(window);
|
|
}
|
|
}
|
|
len.to_px(window)
|
|
}
|
|
|
|
/// A validity range already stated about the window. Containers use
|
|
/// this after branching on a window-unit length.
|
|
pub fn window_holds(&mut self, axis: Axis, holds: impl Into<Holds>) {
|
|
let holds = holds.into();
|
|
debug_assert!(
|
|
holds.contains(self.window.axis(axis)),
|
|
"'{}' ({:?}) says its drawing holds for windows that leave out this one",
|
|
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()
|
|
}
|
|
}
|
|
|
|
/// Moves what a child depends on into this widget's own terms: this
|
|
/// method's `impl` block is where a `Painter`'s own boxes are, so it takes
|
|
/// only what the child was asked with.
|
|
impl Painter<'_> {
|
|
/// Frame ranges are already ranges on the window and combine directly.
|
|
/// A frame pin becomes this widget's own frame wherever a length of it
|
|
/// is what reached the child; where only pixels did, no length of this
|
|
/// frame can change the child's and the pin stops here.
|
|
///
|
|
/// Extent validity maps back through the part of this widget's box,
|
|
/// where the box the child was asked in is that part; a declared length
|
|
/// places the box inside the part instead, and then only that length
|
|
/// reaches the child. A narrowed frame is not one of these: it decides
|
|
/// what fractions under the child mean and leaves the box the part it
|
|
/// was given.
|
|
fn in_parent(
|
|
&self,
|
|
holds: LayoutHolds,
|
|
extent: UiRegion,
|
|
place: [Place; 2],
|
|
narrow: [Option<Len>; 2],
|
|
declared: [Option<LayoutLen>; 2],
|
|
) -> LayoutHolds {
|
|
let mut result = LayoutHolds::ANY;
|
|
for axis in AXES {
|
|
let n = axis as usize;
|
|
// Every frame range is already a range on the window: the
|
|
// widget's own read converted through its frame exactly once.
|
|
result.frame[n] = holds.frame[n];
|
|
let reaches = narrow[n].is_none()
|
|
&& !matches!(place[n].part(), Part::Sized(_))
|
|
&& declared[n].is_none_or(|len| len.rel != Rel::ZERO);
|
|
result.frame_len[n] = holds.frame_len[n].and(reaches.then(|| self.frame.axis(axis)));
|
|
match (place[n].part(), declared[n].is_some()) {
|
|
// 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, false) => {
|
|
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. A length it pinned
|
|
// is this widget's length less the part's pixels where the
|
|
// part is the whole of the box less pixels, which is the one
|
|
// shape that inverts exactly; any other part pins this
|
|
// widget's own length.
|
|
(Part::Of(span), false) => {
|
|
let part_len = span.len();
|
|
result.extent[n] = holds.extent[n].through(part_len);
|
|
result.extent_len[n] = holds.extent_len[n].map(|pinned| match part_len.rel {
|
|
Rel::ONE => pinned - Len::from_parts(Rel::ZERO, part_len.px),
|
|
_ => self.extent.axis(axis).len(),
|
|
});
|
|
}
|
|
// Its box is a part of this widget's frame, or a length of
|
|
// it decided here: 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()));
|
|
}
|
|
}
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/// 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 frame length and the box a child is asked in, in the coordinates the
|
|
/// widget asking draws in.
|
|
///
|
|
/// `own` is that widget's own box, and `place` what of it the child is
|
|
/// given. `narrow` is a frame the container decided for the child -- a row's
|
|
/// slot, or padding's frame less its pixels -- and [`Part::Sized`] one a
|
|
/// sibling's answer decided; both are window lengths, like every other
|
|
/// length here, since a slot of a row is not a fraction of anything the row
|
|
/// can name. The child's declaration is a fraction of whichever reached it,
|
|
/// and is the only one of the three that also places the box: a box the
|
|
/// caller decided is what `place` names.
|
|
pub(crate) fn frame_and_extent(
|
|
own: UiRegion,
|
|
parent_frame: UiVec2,
|
|
place: [Place; 2],
|
|
narrow: [Option<Len>; 2],
|
|
declared: [Option<LayoutLen>; 2],
|
|
align: RegionAlign,
|
|
) -> (UiVec2, UiRegion) {
|
|
let part = part_of(own, place, align);
|
|
let mut frame = parent_frame;
|
|
let mut extent = part;
|
|
for axis in AXES {
|
|
let n = axis as usize;
|
|
let sized = match place[n].part() {
|
|
Part::Sized(len) => Some(len),
|
|
_ => None,
|
|
};
|
|
let base = sized
|
|
.or(narrow[n])
|
|
.unwrap_or_else(|| parent_frame.axis(axis));
|
|
let len = declared[n]
|
|
.map(|len| Len::from_parts(len.rel, len.px).within_len(base))
|
|
.unwrap_or(base);
|
|
*frame.axis_mut(axis) = len;
|
|
if declared[n].is_some() {
|
|
let slot = part.axis(axis);
|
|
let start = slot.start + (slot.len() - len).scale(align.axis(axis).rel());
|
|
*extent.axis_mut(axis) = UiSpan::new(start, start + len);
|
|
}
|
|
}
|
|
(frame, extent)
|
|
}
|
|
|
|
/// The part of a widget's own box a `place` names, in the coordinates that
|
|
/// box is in.
|
|
fn part_of(extent: UiRegion, place: [Place; 2], align: RegionAlign) -> UiRegion {
|
|
let mut part = extent;
|
|
for axis in AXES {
|
|
*part.axis_mut(axis) = place[axis as usize]
|
|
.part()
|
|
.of(*extent.axis(axis), align.axis(axis));
|
|
}
|
|
part
|
|
}
|