`Painter::text_data` had no caller. It was left in the previous round because it is the only way a widget inside `draw` can reach `TextData`, and the app's pending integration might have wanted it; nothing in iris is kept for the app's sake, since the app is to be largely rewritten against this API rather than ported call by call (Bryan, 2026-09-20).
747 lines
30 KiB
Rust
747 lines
30 KiB
Rust
#[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, 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<TextureHandle>,
|
|
pub(super) primitives: Vec<RetainedPrimitive>,
|
|
pub(super) mask_region: Option<UiRegion>,
|
|
/// The previous drawing's owned mask, available for this draw to reclaim.
|
|
pub(super) mask_slot: Option<MaskIdx>,
|
|
/// 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 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<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 region, composed into its region node's
|
|
/// coordinates.
|
|
fn resolve(&self, region: UiRegion) -> UiRegion {
|
|
region.within(&self.region)
|
|
}
|
|
|
|
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 {
|
|
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<W>) -> 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<W>,
|
|
place: impl Into<PlaceDesc>,
|
|
) -> DrawResult<'s, 'a, W> {
|
|
let place = self.resolve_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 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: place,
|
|
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<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 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<W>,
|
|
place: impl Into<PlaceDesc>,
|
|
) -> 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: self.id,
|
|
region: self.region,
|
|
rel_base: self.rel_base,
|
|
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<W: ?Sized>(&self, id: &StrongWidget<W>) -> 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<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].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<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 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::<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 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<Holds>) {
|
|
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<Holds>) {
|
|
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<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]
|
|
}
|
|
}
|
|
|
|
/// 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()
|
|
}
|
|
}
|
|
|
|
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 a widget's box is where a rule or its own hint says so outright.
|
|
pub(super) 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(|len| len.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(super) fn fills(&self, declared: Option<Len>, decided: bool) -> bool {
|
|
self.leftover != Weight::ZERO || declared.is_some() || 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,
|
|
declared: Declared,
|
|
align: RegionAlign,
|
|
) -> UiRegion {
|
|
let mut placed = region;
|
|
for axis in Axis::BOTH {
|
|
let reported = size[axis];
|
|
if reported.fills(declared[axis], self[axis].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. 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(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 = match self[axis].rel_base {
|
|
RelBase::Len(len) => len,
|
|
RelBase::Inherit | RelBase::WithRegion => 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)
|
|
}
|
|
}
|