#[cfg(feature = "layout-diagnostics")] use crate::layout_diagnostics::{self as diag, Counter}; use crate::{ Axis, DrawRegion, Holds, LayoutHolds, LayoutLen, Len, 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, /// The box its parent gave it, in the coordinates of `move_idx`: what a /// fraction of this widget's area is a fraction of, and what every region /// it writes composes within. The same box on the ask that measures and /// the ask that places, which is what keeps a fraction under it from /// being resolved twice. pub(super) region: UiRegion, /// Where this widget's drawing sits inside that box, in the box's own /// coordinates: `FULL` while its answer is not yet known, and the box /// its answer or its parent chose once one of them has. pub(super) placement: UiRegion, /// Whether this draw read its placement, which makes the drawing one /// that holds for that placement alone -- the way reading a length in /// pixels makes it hold for that length. pub(super) reads_placement: bool, /// That box in pixels, which its children's are a length of: threaded /// down from the box this widget was given 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, pub(super) primitives: Vec, pub(super) mask_region: Option, pub(super) inherited_children: Vec, pub(super) extent_own: [Holds; 2], /// Only children whose answers were read constrain this widget's answer. pub(super) answer_under: LayoutHolds, pub(super) children: Vec, /// The children asked about so far, so the first box each was asked in /// is the one recorded as its offer. pub(super) offered: Vec, /// The lengths of the box this widget was first asked about in, in /// pixels. Its children's offers are a fraction of it. pub(super) offered_px: PxVec2, /// Whether this draw is in a box of those lengths, 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, /// What this draw itself read of its box in pixels, per axis: every /// length until it reads one, then that one, unless it says otherwise. pub(super) 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(&mut self, primitive: P, region: DrawRegion) { 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: DrawRegion) { self.write_resolved( kind, primitive, region, region.resolve(self.region, self.placement), ); } fn write_resolved( &mut self, kind: PrimitiveKind

, primitive: P, region: DrawRegion, 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 at = DrawRegion::Extent(UiRegion::FULL); let primitive = primitive.into_primitive(self); self.primitive_at(primitive, at) } /// Writes in the frame by default. `DrawRegion::Extent` keeps the local /// geometry attached to this widget's box without reading its placement. pub fn primitive_within( &mut self, primitive: impl PrimitiveLike, region: impl Into, ) { let primitive = primitive.into_primitive(self); self.primitive_at(primitive, region.into()); } /// Sets a mask in the selected frame or extent coordinates. pub fn set_mask(&mut self, region: impl Into) { let region = region.into(); self.mask_region = Some(region); assert!(self.mask == MaskIdx::NONE); self.mask = self.rsc.ui_mut().masks.push(Mask { region: region.resolve(self.region, self.placement), move_idx: self.move_idx, }); } /// Draws a widget in the whole of this widget's own box: it gets the /// same region -- the same area for its fractions to be of -- and is put /// where this widget was put. What a container that is only a wrapper /// around one child wants, since its box is the child's. pub fn widget<'s, W: ?Sized>(&'s mut self, id: &'s StrongWidget) -> DrawResult<'s, 'a, W> { let own = self.placement; self.widget_at_inner(id, UiRegion::FULL, [Some(own.x), Some(own.y)], true, false) } /// What a widget's rules declare its lengths to be, which whoever draws /// it resolves into its box. 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) -> [Option; 2] { declared_lens(self.rsc.widgets(), id.id()) } /// 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.inherited_children.retain(|child| *child != id.id()); self.state.undraw_rec(id.id(), self.rsc); } /// Draws a child in `region`, relative to this widget's frame. The child /// resolves declared lengths and reports against that frame, then places /// its drawing by its own alignment. pub fn widget_within<'s, W: ?Sized>( &'s mut self, id: &'s StrongWidget, region: UiRegion, ) -> DrawResult<'s, 'a, W> { self.widget_at(id, region, [None; 2]) } /// Draws a widget in `region`, saying where in it the drawing goes. /// /// `region` is the child's own area: what a fraction it declares or /// reports is a fraction of, and the coordinates the regions it writes /// compose within. It is the same box on the ask that measures and the /// ask that places, which is what stops a fraction under it being /// resolved twice. /// /// `placement` is what of that region the child's drawing takes, per /// axis, wherever this widget is choosing. `None` leaves the axis to the /// child's own answer and alignment, which is what /// [`Self::widget_within`] passes. A span passes the whole row as the /// region, so `rel(0.5)` is half the row wherever the child sits in it, /// and places the child by passing the slot along its axis. pub fn widget_at<'s, W: ?Sized>( &'s mut self, id: &'s StrongWidget, region: UiRegion, placement: [Option; 2], ) -> DrawResult<'s, 'a, W> { self.widget_at_inner(id, region, placement, false, false) } fn widget_at_inner<'s, W: ?Sized>( &'s mut self, id: &'s StrongWidget, region: UiRegion, placement: [Option; 2], inherited: bool, measuring: bool, ) -> DrawResult<'s, 'a, W> { if inherited { if !self.inherited_children.contains(&id.id()) { self.inherited_children.push(id.id()); } } else { self.inherited_children.retain(|child| *child != id.id()); } 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, placement) = ask_box(region, declared, align, placement); let within = match local == UiRegion::FULL { true => self.region, false => local.within(&self.region), }; #[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 given_len = local.size(); let offer_len = match first_ask { true => given_len, false => self .state .active .get(&id.id()) .map_or(given_len, |a| a.offer_len), }; let offer_placement = if first_ask { placement } else { self.state .active .get(&id.id()) .map_or(placement, |a| a.offer_placement) }; let px = given_len.to_px(self.px); let offered_px = offer_len.to_px(self.offered_px); // The answer and what it holds for, both about the box 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(), within, DrawInfo { layer: self.layer, parent: Some(self.id), depth: self.depth + 1, parent_move: self.move_idx, region_node, mask: self.mask, given_region: local, offer_len, offer_placement, px, offered_px, placement, }, None, measuring, self.rsc, ); let in_parent = |holds: LayoutHolds| { let mut result = LayoutHolds::ANY; for axis in AXES { let n = axis as usize; result.frame[n] = holds.frame[n].through(local.axis(axis).len()); if inherited && declared[n].is_none() { result.extent[n] = holds.extent[n]; if holds.placement.is_some() { result.placement = Some(self.placement); } } else { let chosen = placement[n].unwrap_or(UiSpan::FULL).len(); result.frame[n] = result.frame[n].and( holds.extent[n] .through(chosen) .through(local.axis(axis).len()), ); } } result }; self.under = self.under.and(in_parent(holds)); let answer_holds = in_parent(answer_holds); DrawResult { child: id, painter: self, size: in_parent_frame(size, local.size(), declared), answer_holds, } } /// What a child says its length is without being drawn, if it can say. /// 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(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 } } } /// Measures a child's length from its hint, a retained answer, or `draw`. /// A fresh draw evaluates the offer without placing its answer. The caller /// must later place or undraw the child. pub fn measure_len( &mut self, child: &StrongWidget, axis: Axis, region: UiRegion, placement: [Option; 2], ) -> LayoutLen { let offered = placement; let declared = self.declared_lens(child); let align = self.rsc.widgets().alignment(child.id()); let (local, placement) = ask_box(region, declared, align, placement); let first_ask = self.at_offer && !self.offered.contains(&child.id()); if let Some(hint) = self.size_hint(child, axis) { return hint; } let px = local.size().to_px(self.px); let retained = self.state .retained_size(child.id(), px, placement, self.move_idx, self.rsc.widgets()); let Some((size, holds)) = retained else { return self .widget_at_inner(child, region, offered, false, true) .len(axis); }; #[cfg(feature = "layout-diagnostics")] diag::bump(Counter::RetainedSizeHits); self.depend_on(child); if first_ask { self.offered.push(child.id()); let active = self.state.active.get_mut(&child.id()).unwrap(); active.offer_len = local.size(); active.offer_placement = placement; } let placement = UiRegion { x: placement[0].unwrap_or(UiSpan::FULL), y: placement[1].unwrap_or(UiSpan::FULL), }; let holds = holds.in_frame(placement); for (axis, under) in AXES.into_iter().zip(self.answer_under.frame.iter_mut()) { *under = under.and(holds[axis as usize].through(local.axis(axis).len())); } in_parent_frame(size, local.size(), declared).axis(axis) } /// Whether this is the first box a child is asked about in during a draw /// that is itself in the box it was asked in -- the question a cold /// layout asks, whose answer is the one to keep. 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(&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 frame or extent coordinates. // TODO: merge the text methods into the primitive ones. pub fn glyphs(&mut self, text: &RenderedText, origin: impl Into) { let origin = origin.into(); // Glyph offsets and sizes are pixels, which compose additively. // Only the shared origin needs the frame/extent composition. let resolved = origin.resolve(self.region, self.placement); 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(), }, origin.map(place), place(resolved), ); } } /// The box this widget's parent gave it, in the coordinates its own /// primitives are written in -- so a region composed `within` it may be /// drawn directly. Its own box is [`Self::placement`] of this one. pub fn region(&self) -> UiRegion { self.region } /// Where this widget's drawing goes inside the box it was given, in that /// box's coordinates: what its own answer took of it, or what its parent /// chose for it. `FULL` on the ask that measures, since nothing has been /// placed yet. /// /// Reading it is what says the drawing depends on it, so a widget that /// positions its own content reads it and is drawn again once its box is /// known, and one that fills whatever it is given never is. pub fn placement(&mut self) -> UiRegion { self.reads_placement = true; self.placement } /// 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() } /// The part of this widget's box that something of `size` takes, at the /// near edge. A container that reports one child's size gives every child /// this, so what it draws is inside what it says it occupies. pub fn box_of(&self, size: Size) -> UiRegion { let lens = placed_lens(size, [None; 2], [false; 2]); placed_box(UiRegion::FULL, lens, RegionAlign::NEAR) } /// 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.placement.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) { let part = self.placement.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 the box this widget's parent gave it, 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 region_px_len(&mut self, axis: Axis) -> Px { let len = self.px.axis(axis); let own = &mut self.own[axis as usize]; if *own == Holds::ANY { *own = Holds::at(len); } len } /// [`Self::holds`] stated about the region rather than about this /// widget's own box, for a container whose drawing turns on the box it /// was given rather than on the part of it it took. pub fn region_holds(&mut self, axis: Axis, holds: impl Into) { let holds = holds.into(); debug_assert!( holds.contains(self.px.axis(axis)), "'{}' ({:?}) says its drawing holds for lengths that leave out its region", self.label(), self.id ); self.own[axis as usize] = holds; } pub fn text_data(&mut self) -> &mut TextData { &mut self.rsc.ui_mut().text } pub fn child_layer(&mut self) { self.layer = self.state.layers.child(self.layer); } /// The layer this widget's `n`th child draws on, addressed rather than /// walked to. A container that measures one child by drawing it can ask /// on the layer that child will end up on, and then the second ask is a /// reuse rather than a second drawing on another layer. pub fn child_layer_at(&mut self, n: usize) { let mut at = self.state.layers.child(self.own_layer); for _ in 0..n { at = self.state.layers.next(at); } self.layer = at; } pub fn next_layer(&mut self) { self.layer = self.state.layers.next(self.layer); } pub fn label(&self) -> &str { &self.rsc.widgets().data(self.id).unwrap().label } pub fn id(&self) -> &WidgetId { &self.id } } /// A child that has just been drawn. Reading its size records that this /// widget's own size depends on it; dropping it without reading draws the /// child and leaves the parent independent of what it came to. pub struct DrawResult<'p, 'a, W: ?Sized> { painter: &'p mut Painter<'a>, child: &'p StrongWidget, 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(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() } } /// 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; 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; 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, decided: bool) -> bool { reported.leftover != Weight::ZERO || declared.is_some() || decided } /// What of the box it was given a widget's drawing occupies, as lengths of /// that box: the size it reported wherever that is a part to be placed, and /// the whole of the box wherever the answer fills it. /// /// A reported fraction is a fraction of the box the widget drew in, where a /// declared one is a fraction of the box its parent handed down -- a span /// reporting `rel(1.0)` means all of what it was given, whatever that was a /// fraction of. So this is a length of the box rather than a length composed /// into it, and a box in pixels is this step from the given box's pixels. pub(crate) fn placed_lens( size: Size, declared: [Option; 2], decided: [bool; 2], ) -> UiVec2 { let mut lens = UiVec2::FULL_SIZE; for (axis, (declared, decided)) in AXES.into_iter().zip(declared.into_iter().zip(decided)) { let reported = size.axis(axis); if !fills(reported, declared, decided) { *lens.axis_mut(axis) = Len::from_parts(reported.rel, reported.px); } } lens } /// Where that drawing sits: those lengths taken of the box the widget was /// asked in, on the side of it that the widget's alignment says. pub(crate) fn placed_box(region: UiRegion, lens: UiVec2, align: RegionAlign) -> UiRegion { let mut placed = region; for axis in AXES { // The whole of the box is already where it sits, and the arithmetic // below is the identity for it. if lens.axis(axis) == Len::FULL { continue; } let span = placed.axis_mut(axis); let len = lens.axis(axis).within_len(span.len()); span.start += (span.len() - len).scale(align.axis(axis).rel()); span.end = span.start + len; } placed } /// A declared axis gets a frame of that length, aligned within the parent's /// slot (or the offer). Undeclared axes keep the offered frame and chosen /// placement, so their reported fractions retain that reference. pub(crate) fn ask_box( mut region: UiRegion, declared: [Option; 2], align: RegionAlign, placement: [Option; 2], ) -> (UiRegion, [Option; 2]) { let mut placed = [None; 2]; for (axis, (len, chosen)) in AXES.into_iter().zip(declared.into_iter().zip(placement)) { let Some(len) = len else { placed[axis as usize] = chosen; continue; }; let span = region.axis_mut(axis); let len = Len::from_parts(len.rel, len.px); let slot = chosen.unwrap_or(*span); span.start = slot.start + (slot.len() - len).scale(align.axis(axis).rel()); span.end = span.start + len; } (region, placed) }