#[cfg(feature = "layout-diagnostics")] use crate::layout_diagnostics::{self as diag, Counter}; use crate::{ Axis, Bound, Bounds, Declared, DrawScratch, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc, PlaceFit, Px, PxVec2, RegionAlign, Rel, RenderedText, RequestArena, RequestedLen, RetainedPrimitive, Size, SizeRequests, StrongWidget, TextAttrs, TextBuffer, TextureHandle, UiRegion, UiRenderState, UiRsc, UiVec2, Weight, WidgetId, Widgets, render::{ GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind, TexturePrimitive, }, ui::{ place::{PlaceSpan, RelBase}, render_state::{DrawInfo, Placing}, }, }; /// 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 rel base, 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 /// rel base and the box without either becoming the other. pub(super) rel_base: UiVec2, /// The box this widget was asked in, in its region node's coordinates: /// what it draws in, and what its children's places are parts of. pub(super) region: UiRegion, /// 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, pub(super) primitives: Vec, pub(super) mask_region: Option, /// The previous drawing's owned mask, available for this draw to reclaim. pub(super) mask_slot: Option, /// Only children whose answers were read constrain this widget's answer. pub(super) answer_under: LayoutHolds, pub(super) children: Vec, /// The children whose size this widget read while drawing. pub(super) size_deps: Vec, pub(super) request_deps: Vec, pub(super) scratch: DrawScratch, /// What this draw itself reads, as against what its children's drawings /// hold for: every window and every length of its own region until it /// reads one, then that one unless it says otherwise, and the rel base or /// region length it read symbolically, each of which makes the drawing /// hold for that length alone. pub(super) own: LayoutHolds, /// 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> { /// Reuses this widget's allocation buffers across draws. A child drawn /// part way through gets a painter of its own, with buffers of its own, /// so nothing it does while this one is mid-row can reach these. pub fn with_requests( &mut self, f: impl FnOnce(&mut Self, &mut Vec, &mut Vec) -> T, ) -> T { let mut requests = std::mem::take(&mut self.scratch.requests); let mut lengths = std::mem::take(&mut self.scratch.lengths); requests.clear(); lengths.clear(); let result = f(self, &mut requests, &mut lengths); self.scratch.requests = requests; self.scratch.lengths = lengths; result } /// Discovers a composable request without painting a provisional box. pub fn size_request( &mut self, child: &StrongWidget, axis: Axis, ) -> Option { if let Some(len) = self.size_hint(child, axis) { self.request_deps.push(child.id()); return Some(len.into()); } let start = self.request_deps.len(); let mut requests = SizeRequests { arena: &mut self.state.requests, measured: None, widgets: self.rsc.widgets(), dependencies: &mut self.request_deps, rel_base: self.rel_base[axis], }; // Intrinsic fixed content must keep its offered box for wrapping; // only a declaration or a share chooses the box it is drawn in. let request = requests.widget(child, axis).filter(|request| { request.has_leftover() || self.rsc.widgets().size_rules(child.id())[axis] .request .is_some() }); if request.is_some() { self.rel_base(axis); } else { // A discarded request contributes no dependency: the measured // draw below records the size and box it actually used instead. self.request_deps.truncate(start); } request } /// Completes discovery after a child was measured. Only this call may use /// drawn answers: before the ask they could belong to an obsolete box. pub fn measured_request( &mut self, child: &StrongWidget, axis: Axis, len: LayoutLen, ) -> RequestedLen { let start = self.request_deps.len(); let bound = self.rsc.widgets().size_rules(child.id())[axis].bound; let mut requests = SizeRequests { arena: &mut self.state.requests, measured: Some(&self.state.active), widgets: self.rsc.widgets(), dependencies: &mut self.request_deps, rel_base: self.rel_base[axis], }; if let Some(request) = requests.widget(child, axis) && request.linear().is_none() && request.has_leftover() { self.rel_base(axis); return request; } let shares = len.leftover > Weight::ZERO; let request = match shares { true => requests.bounded(len.into(), bound), false => len.into(), }; self.request_deps.truncate(start); // Only a bound that is a fraction was read against the rel base; one // in pixels binds at the same length under any of them. if shares && bound.has_fraction() { self.rel_base(axis); } request } /// Divides `room` between `requests`, one length per request in /// `output`. A deferred comparison is decided here, against this window: /// which side of a crossing the solution falls is a question in pixels, /// so the drawing holds only for the window that answered it. pub fn allocate( &mut self, requests: &[RequestedLen], room: Len, axis: Axis, output: &mut Vec, ) { let window = self.window[axis]; self.own[axis].window = self.own[axis].window.and(Holds::at(window)); output.clear(); output.extend( self.state .requests .allocate(requests, room.to_px(window), window), ) } /// The least a request can come to, which is what it takes of the row /// before anything is divided. A comparison is read at no share at all. pub fn minimum_request(&mut self, request: &RequestedLen, axis: Axis) -> Len { match request.linear() { Some(len) => len.without_leftover(), None => { let window = self.window[axis]; self.own[axis].window = self.own[axis].window.and(Holds::at(window)); Len::from_parts(Rel::ZERO, self.state.requests.minimum(*request, window)) } } } fn primitive_at(&mut self, primitive: P, region: UiRegion) { let kind = self.rsc.ui_mut().primitives.kind::

(); self.write(kind, primitive, region); } /// Takes the kind, for a caller writing many of one primitive. fn write(&mut self, kind: PrimitiveKind

, primitive: P, region: UiRegion) { self.write_resolved(kind, primitive, region, self.resolve(region)); } /// A box in this widget's region, composed into its region node's /// coordinates. fn resolve(&self, region: UiRegion) -> UiRegion { region.within(&self.region) } fn write_resolved( &mut self, kind: PrimitiveKind

, 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 { 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; let mask = Mask { region: resolved, move_idx, }; let masks = &mut self.rsc.ui_mut().masks; self.mask = match self.mask_slot.take() { Some(idx) => { *masks.get_mut(idx) = mask; idx } None => { let idx = masks.push(mask); // The owner keeps the slot alive even with no primitives. masks.push_ref(idx); idx } }; } /// Draws a widget in the whole of this widget's own box, with the rel /// base forwarded unchanged: what a container that is only a wrapper /// around one child wants. pub fn widget<'s, W: ?Sized>(&'s mut self, id: &'s StrongWidget) -> DrawResult<'s, 'a, W> { self.widget_at(id, UiRegion::FULL) } /// Resolves what the place says about the child's rel base into a length, /// where that is this widget's own narrowed the way the region is. An /// axis the region leaves whole is not read at all, so a wrapper that /// only moves its child does not pin its drawing to a rel base. fn resolve_rel_base(&mut self, mut place: PlaceDesc) -> PlaceDesc { for axis in Axis::BOTH { let at = &mut place[axis]; let (RelBase::WithRegion, PlaceSpan::Within(span)) = (at.rel_base, at.span) else { continue; }; let len = span.len(); at.rel_base = match len == Len::FULL { true => RelBase::Inherit, false => RelBase::Len(len.within_len(self.rel_base(axis))), }; } place } /// Asks a child, saying what its fractions are of and where it is asked. /// /// `place` says where the child goes and what its fractions are of: /// see [`PlaceDesc`]. A `UiRegion` converts into the common case, which /// is a box of this widget's own with the answer placed inside it. /// /// The child draws once, in the region that comes of it, and its answer /// is placed inside that region 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, place: impl Into, ) -> DrawResult<'s, 'a, W> { let offer = self.resolve_rel_base(place.into()); let Ask { rel_base, region, place, declared, bounds, holds: ask_holds, inputs, } = self.placing().ask( self.rsc.widgets(), &mut self.state.requests, self.window, id.id(), offer, ); self.own = self.own.and(inputs); let region_node = self.rsc.widgets().is_region_node(id.id()); #[cfg(feature = "layout-diagnostics")] if region_node { diag::bump(Counter::RegionNodeDraws); diag::region_node(id.id(), self.id, region); } // 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 drawn = 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, rel_base, region, placed: place, asked: offer, declared, bounds, ask_holds, re_asked, }, None, self.rsc, ); let holds = self.in_parent(drawn.drawing_holds, region, place, declared); let answer_holds = self.in_parent(drawn.answer.holds, region, place, 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: drawn.answer.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(&mut self, id: &StrongWidget) { 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 in `place` of this widget's box, where that box is the /// answer the child already gave: 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. /// /// A child this draw has not asked about, and one whose rel base this /// narrows, is asked here instead: there is no answer to re-express, or /// the question has changed. So a container that places every child the /// same way says it once, and which of the two happens is this widget's /// business rather than the caller's. pub fn place_at<'s, W: ?Sized>( &'s mut self, id: &'s StrongWidget, place: impl Into, ) -> DrawResult<'s, 'a, W> { let place = self.resolve_rel_base(place.into()); let states_rel_base = Axis::BOTH .iter() .any(|&axis| matches!(place[axis].rel_base, RelBase::Len(_))); if states_rel_base || !self.children.contains(&id.id()) { return self.widget_at(id, place); } let at = self.placing(); self.state.place_in(id.id(), &at, place, self.rsc); let active = &self.state.active[&id.id()]; let size = active.measured().unwrap_or(active.size); DrawResult { child: id, painter: self, size, // Read where it was asked; moving it is not a second answer. answer_holds: LayoutHolds::ANY, } } /// This widget as the thing its children are placed within. fn placing(&self) -> Placing { Placing { id: Some(self.id), region: self.region, rel_base: self.rel_base, depth: self.depth, move_idx: self.move_idx, mask: self.mask, } } /// What a child says its length is without being drawn, if it can say, /// as the length its draw would report: a fraction in it is resolved /// against this widget's rel base, which is the rel base a child asked with /// nothing narrowed gets. Asking counts as reading its size. pub fn size_hint(&mut self, id: &StrongWidget, axis: Axis) -> Option { // A bound is composed into a request rather than applied to a hint, // so a bounded widget cannot say its length without being asked: what // it comes to is a comparison only the ask or the allocator makes. // A miss rather than no read at all, so the counters see it. let bounded = self.rsc.widgets().size_rules(id.id())[axis].bound != Bound::ANY; let hint = (!bounded) .then(|| self.rsc.widgets().exact_len(id.id(), axis)) .flatten(); let rel_base = self.rel_base[axis]; let resolved = hint.map(|hint| hint.within_len(rel_base)); #[cfg(feature = "layout-diagnostics")] { diag::hint_read(id.id(), self.id, axis, resolved); diag::bump(match resolved { Some(_) => Counter::HintHits, None => Counter::HintMisses, }); } if let Some(hint) = hint { self.depend_on(id); // Resolving a fraction against this rel base makes this draw a // function of the rel base's length. The fraction to ask about is // the child's own: resolved against a rel base of pixels, none is // left to see it by. if hint.rel != Rel::ZERO { self.own[axis].rel_base = Some(rel_base); } } resolved } fn depend_on(&mut self, child: &StrongWidget) { 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, ) -> &'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 rel base or region 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 region. let resolved = self.resolve(origin); let kind = self.rsc.ui_mut().primitives.kind::(); 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 rel base 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 region_len(&mut self, axis: Axis) -> Len { let len = self.region[axis].len(); self.own[axis].region_len = Some(len); len } /// This widget's rel base along one axis: what a fraction it or anything /// under it declares or reports 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 rel base, the way /// [`Self::region_len`] pins it to the box. pub fn rel_base(&mut self, axis: Axis) -> Len { let len = self.rel_base[axis]; self.own[axis].rel_base = 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] .request .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 len = self.region[axis].len(); let px = len.to_px(self.window[axis]); let own = &mut self.own[axis].region; if *own == Holds::ANY { *own = Holds::at(px); } px } /// 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) { let len = self.region[axis].len(); let holds = holds.into(); debug_assert!( holds.contains(len.to_px(self.window[axis])), "'{}' ({:?}) says its drawing holds for lengths that leave out its own box", self.label(), self.id ); self.own[axis].region = 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]; if len.rel != Rel::ZERO { let own = &mut self.own[axis].window; if *own == Holds::ANY { *own = Holds::at(window); } } len.to_px(window) } /// [`Len::longer_than`], asked on this widget's behalf: the windows the /// comparison comes out the same way on are windows its drawing holds /// for, and nowhere else does it. What a container has left for the /// shares it divides is the one thing that asks. /// /// Narrowed rather than stated, because whatever else this widget read /// about the window is a reason its drawing holds where it does too. pub fn longer_than(&mut self, len: Len, than: Len, axis: Axis) -> bool { let window = self.window[axis]; let (longer, holds) = len.longer_than(than, window); debug_assert!( holds.contains(window), "'{}' ({:?}) compared two lengths and kept a range without this window", self.label(), self.id ); self.own[axis].window = self.own[axis].window.and(holds); longer } /// The windows this drawing holds for, stated rather than taken: a /// container that branched on a length in pixels says which side of the /// boundary it was on, which is wider than the one window reading that /// length pins, and replaces it. pub fn window_holds(&mut self, axis: Axis, holds: impl Into) { let holds = holds.into(); debug_assert!( holds.contains(self.window[axis]), "'{}' ({:?}) says its drawing holds for windows that leave out this one", self.label(), self.id ); self.own[axis].window = holds; } 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, size: Size, answer_holds: LayoutHolds, } impl 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] } } /// 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 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() } } impl Painter<'_> { /// Moves what a child depends on into this widget's own terms, taking /// only what the child was asked with. /// /// Window ranges are already about the one unit and combine directly. /// A rel base pin becomes this widget's own rel base wherever a length of it /// is what reached the child; where only pixels did, no length of this /// rel base can change the child's and the pin stops here. /// /// A child's 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 rel base 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, region: UiRegion, place: PlaceDesc, declared: Declared, ) -> LayoutHolds { let mut result = LayoutHolds::ANY; for axis in Axis::BOTH { let declared = declared[axis]; let holds = holds[axis]; let at = place[axis]; let result = &mut result[axis]; // Every read became pixels against the window, so a range on // it is already in this widget's terms. result.window = holds.window; // A length this widget named -- a resolved share, a box a sibling // decided, a box it sized outright, which is its own base -- is // not a length of this widget's rel base, so a pin on it stops // here. So does a declaration in pixels: no length of either base // is in it to see. let reaches = !matches!(at.rel_base, RelBase::Len(_)) && declared.is_none_or(|len| len.rel != Rel::ZERO); result.rel_base = holds.rel_base.and(reaches.then(|| self.rel_base[axis])); match (at.span, declared) { // 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. (PlaceSpan::Within(span), None) => { let part_len = span.len(); result.region = holds.region.through(part_len); result.region_len = holds.region_len.map(|pinned| match part_len.rel { Rel::ONE => pinned - Len::from_parts(Rel::ZERO, part_len.px), _ => self.region[axis].len(), }); } // Its box is a length this widget decided, from its own // rel base or from a sibling's answer: no length of this // widget's box reaches it, so what it holds for is a range // on the window and none of it on that box. _ => { result.window = result.window.and(holds.region.through(region[axis].len())); } } } result } } impl Widgets { /// What says a widget's length on one axis without drawing it, if anything /// does. 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 -- a /// share included, since a share is a length only to whoever divides one, /// and that is the parent rather than this widget. fn exact_len(&self, id: WidgetId, axis: Axis) -> Option { // A request is a length the rule gives, and the hint below must not // narrow the box in its place: what the request comes to is not known // until the parent allocates, and it is the parent's answer, not this // widget's. let rule = &self.size_rules(id)[axis]; if rule.deferred().is_some() { return None; } rule.exact().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. self.get_dyn(id)?.size_hint(axis) }) } } /// One ask of a widget: the box it draws in, what its fractions are of, and /// what deciding those read. pub(super) struct Ask { pub rel_base: UiVec2, pub region: UiRegion, /// The place the ask came to, which a rule of the widget's own can take /// past the box its parent offered. pub place: PlaceDesc, /// What the widget's box is on each axis where something says so /// outright: its rule, its hint, or a bound the offer fell outside. /// An intrinsic answer can still occupy less than a capped box. pub declared: Declared, /// Its bounds, resolved against the rel base its rules were resolved /// against, for the answer to be held to where the box was not. pub bounds: Bounds, /// What the ask itself holds for, kept on the widget asked about: a rule /// compared against the offer in pixels holds only for the windows on its /// side of the crossing, and that range reaches whoever asked through the /// drawing it is part of. Kept on the widget asked about rather than on /// the asker because the root has no asker. pub holds: LayoutHolds, /// Inputs read against the parent before declarations choose a new base. /// These belong to the asker; the widget's own holds describe its output box. pub inputs: LayoutHolds, } impl Placing { /// Asks about a widget at `place` of this box, with the widget's own /// rules applied to what the place offers it. `place` is resolved: what /// a rel base of the asker's is a fraction of, the asker worked out. /// /// Every ask is this one, the root's included -- there the box is the /// window and nothing above narrowed it, which is what [`Self::WINDOW`] /// says. pub(super) fn ask( &self, widgets: &Widgets, requests: &mut RequestArena, window: PxVec2, id: WidgetId, mut place: PlaceDesc, ) -> Ask { let align = widgets.alignment(id); let rules = widgets.size_rules(id); let mut holds = LayoutHolds::ANY; let mut inputs = LayoutHolds::ANY; let mut declared = Declared::NONE; let mut bounds = Bounds::ANY; for axis in Axis::BOTH { let base = place.base(axis, self.rel_base); let stated = widgets.exact_len(id, axis); if let Some(len) = stated.and_then(|len| len.declared()) { if len.rel != Rel::ZERO { inputs[axis].rel_base = Some(self.rel_base[axis]); } declared[axis] = Some(len.within_len(base)); } if let Some(request) = rules[axis].deferred() { inputs[axis].rel_base = Some(self.rel_base[axis]); inputs[axis].region_len = Some(self.region[axis].len()); inputs[axis].window = Holds::at(window[axis]); let offer = place.of(self.region, align)[axis].len(); let px = if place[axis].fit == PlaceFit::Allocated { offer.to_px(window[axis]) } else { let request = requests.import(request, base); let request = requests.bounded(request, rules[axis].bound.within_len(base)); holds[axis].window = Holds::at(window[axis]); requests .allocate(&[request], offer.to_px(window[axis]), window[axis]) .next() .unwrap() }; let len = Len::from_parts(Rel::ZERO, px); place[axis].rel_base = RelBase::Len(len); declared[axis] = Some(len); holds[axis].rel_base = Some(len); continue; } // A share fills what the pixels and fraction beside it leave of // the box and overflows where they are longer, which is the rule // a span follows with one child. Only the overflow is a box of // the widget's own: a share that fits is the box it was given, // which is what this place already says. let (share, kept) = self.share_past_the_offer(stated, window[axis], place, align, axis); holds[axis].window = holds[axis].window.and(kept); if let Some(len) = share { place[axis] = len.as_desc().fills(); } let bound = rules[axis].bound; if bound != Bound::ANY { if bound.has_fraction() { inputs[axis].rel_base = Some(self.rel_base[axis]); } // A solved slot may narrow the widget's rel base, but the // allocator evaluated its bounds against this parent's base. let bound_base = if place[axis].fit == PlaceFit::Allocated { self.rel_base[axis] } else { base }; bounds[axis] = bound.within_len(bound_base); let offer = declared[axis].unwrap_or_else(|| place.of(self.region, align)[axis].len()); let (held, kept) = bounds[axis].outside(offer, window[axis]); holds[axis].window = holds[axis].window.and(kept); // The comparison reads the incoming box, before a bound // replaces it. Placement keeps that decision; only an ask // may compare a new offer. if declared[axis].is_none() { inputs[axis].region_len = Some(self.region[axis].len()); } if let Some(len) = held { declared[axis] = Some(len); } } } let (rel_base, region) = place.rel_base_and_region(self.region, self.rel_base, declared, align); Ask { rel_base, region, place, declared, bounds, holds, inputs, } } /// The box a widget's own share asks for where that is longer than the /// box `place` gives it, and nothing where the share fits -- with the /// windows that answer holds for, which is a range either way. /// /// A share is a length only to whoever divides one, and nothing divides a /// box handed to one child: what is left of it after the pixels and the /// fraction beside the share is what the share takes, so the length comes /// to the whole box until those are longer than it and to them once they /// are. Only that second case is a box its parent did not give, and the /// crossing between them is a question in pixels. fn share_past_the_offer( &self, stated: Option, window: Px, place: PlaceDesc, align: RegionAlign, axis: Axis, ) -> (Option, Holds) { // A place that is the widget's placement outright is a box its parent // decided, and a parent that divides one has already given the share // whatever it was owed. Only an offer -- a box with the answer still // to be placed inside it -- is a box a share reads. if place[axis].fit.fills() { return (None, Holds::ANY); } // A share with nothing beside it is the box whatever the box is, so // there is no comparison to make and no range to keep for one. let Some(stated) = stated else { return (None, Holds::ANY); }; if stated.leftover == Weight::ZERO || stated.is_only_leftover() { return (None, Holds::ANY); } let fixed = stated .without_leftover() .within_len(place.base(axis, self.rel_base)); let offer = place.of(self.region, align)[axis].len(); let (longer, holds) = fixed.longer_than(offer, window); (longer.then_some(fixed), holds) } } impl LayoutLen { /// 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. An axis the parent decided /// from the answer is the answer already. pub(super) fn fills(&self, decided: bool) -> bool { self.leftover != Weight::ZERO || decided } } impl PlaceDesc { /// 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 rel base, 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 rel base it was reported of. pub(super) fn placement(self, region: UiRegion, size: Size, align: RegionAlign) -> UiRegion { let mut placed = region; for axis in Axis::BOTH { let reported = size[axis]; if reported.fills(self[axis].fit.fills()) { continue; } placed[axis] = placed[axis].place(reported.without_leftover(), align[axis]); } placed } /// The rel base 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, including any rel base it states -- a row's slot, or padding's rel /// base less its pixels. The ask has already resolved declarations into /// window lengths, so placement only aligns them inside the given box. pub(super) fn rel_base_and_region( self, own: UiRegion, parent_rel_base: UiVec2, declared: Declared, align: RegionAlign, ) -> (UiVec2, UiRegion) { let given = self.of(own, align); let mut rel_base = parent_rel_base; let mut region = given; for axis in Axis::BOTH { let base = self.base(axis, parent_rel_base); let len = declared[axis].unwrap_or(base); rel_base[axis] = len; if declared[axis].is_some() { region[axis] = given[axis].place(len, align[axis]); } } (rel_base, region) } }