#[cfg(feature = "layout-diagnostics")] use crate::layout_diagnostics::{self as diag, Counter}; use crate::{ Axis, Declared, Holds, LayoutHolds, LayoutLen, Len, PlaceDesc, Px, PxVec2, RegionAlign, Rel, RenderedText, RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, 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 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, /// 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> { 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 state_rel_base(&mut self, mut place: PlaceDesc) -> PlaceDesc { for axis in AXES { if let Some(span) = place[axis].narrows_rel_base() { let len = span.len(); let stated = (len != Len::FULL).then(|| len.within_len(self.rel_base(axis))); place[axis] = place[axis].with_rel_base(stated); } } 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 place = self.state_rel_base(place.into()); 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 (rel_base, region) = place.rel_base_and_region(self.region, self.rel_base, declared, align); #[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 px = rel_base.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, rel_base, region, placed: place, asked: place, re_asked, px, }, None, self.rsc, ); let holds = self.in_parent(holds, region, place, declared); let answer_holds = self.in_parent(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, 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.state_rel_base(place.into()); let states_rel_base = AXES .iter() .any(|&axis| place[axis].stated_rel_base().is_some()); 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: self.id, region: self.region, rel_base: self.rel_base, 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 rel base. Reading them depends on nothing -- the box /// that comes of them is kept on the child, and `redraw` compares it /// there. fn declared_lens(&self, id: &StrongWidget) -> Declared { self.rsc.widgets().declared_lens(id.id()) } /// 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 { 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].exact().or_else(|| { widgets .get_dyn(id.id()) .and_then(|widget| widget.size_hint(axis)) }); 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] .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 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) } /// 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 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, 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() } } /// 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<'_> { /// 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 AXES { let declared = declared[axis]; let holds = holds[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; let at = place[axis]; let reaches = at.stated_rel_base().is_none() && !at.is_sized() && declared.is_none_or(|len| len.rel != Rel::ZERO); result.rel_base = holds.rel_base.and(reaches.then(|| self.rel_base[axis])); match (at.within_span(), declared.is_some()) { // 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. (Some(span), false) => { 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 a widget's box is where a rule or its own hint says so outright. pub(crate) fn declared_lens(&self, id: WidgetId) -> Declared { let rules = self.size_rules(id); let widget = self.get_dyn(id); Declared::from_axes(|axis| { rules[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)) .and_then(LayoutLen::declared) }) }) } } 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. 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(self, declared: Option, decided: bool) -> bool { self.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 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. impl PlaceDesc { pub(crate) fn placement( self, region: UiRegion, size: Size, declared: Declared, align: RegionAlign, ) -> UiRegion { let mut placed = region; for axis in AXES { let reported = size[axis]; if reported.fills(declared[axis], self[axis].does_fill()) { 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. That is a window length, 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 that also places the box: a box the caller decided is /// what `place` names. pub(crate) 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 AXES { let base = self[axis] .stated_rel_base() .unwrap_or_else(|| parent_rel_base[axis]); let len = declared[axis] .map(|len| len.within_len(base)) .unwrap_or(base); rel_base[axis] = len; if declared[axis].is_some() { region[axis] = given[axis].place(len, align[axis]); } } (rel_base, region) } }