752 lines
30 KiB
Rust
752 lines
30 KiB
Rust
#[cfg(feature = "layout-diagnostics")]
|
|
use crate::layout_diagnostics::{self as diag, Counter};
|
|
use crate::{
|
|
Axis, Holds, LayoutHolds, LayoutLen, Len, Part, Place, Px, PxVec2, RegionAlign, Rel,
|
|
RenderedText, RetainedPrimitive, Size, StrongWidget, TextAttrs, TextBuffer, TextData,
|
|
TextureHandle, UiRegion, UiRenderState, UiRsc, UiSpan, UiVec2, Weight, WidgetId, Widgets,
|
|
render::{
|
|
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveInst, PrimitiveKind,
|
|
TexturePrimitive,
|
|
},
|
|
ui::render_state::{DrawInfo, Placing},
|
|
};
|
|
const AXES: [Axis; 2] = [Axis::X, Axis::Y];
|
|
|
|
/// makes your surfaces look pretty
|
|
pub struct Painter<'a> {
|
|
pub(super) state: &'a mut UiRenderState,
|
|
pub(super) rsc: &'a mut dyn UiRsc,
|
|
|
|
/// This widget's frame, per axis: a length of the window, and what a
|
|
/// fraction it or anything under it declares or reports is a fraction
|
|
/// of. A length rather than a box, so padding can take from both the
|
|
/// frame and the box without either becoming the other.
|
|
pub(super) frame: UiVec2,
|
|
/// Where this widget's drawing goes, in its region node's coordinates.
|
|
pub(super) extent: UiRegion,
|
|
/// The extent's symbolic length where this draw read it, which makes the
|
|
/// drawing one that holds for that length alone -- the way reading a
|
|
/// length in pixels makes it hold for that number of pixels.
|
|
pub(super) extent_len: [Option<Len>; 2],
|
|
/// The window in pixels. Frames and boxes become pixels against this one
|
|
/// unit, regardless of region-node boundaries.
|
|
pub(super) window: PxVec2,
|
|
pub(super) mask: MaskIdx,
|
|
pub(super) textures: Vec<TextureHandle>,
|
|
pub(super) primitives: Vec<RetainedPrimitive>,
|
|
pub(super) mask_region: Option<UiRegion>,
|
|
/// 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 read of the window in pixels, per axis: every
|
|
/// window until it reads one, then that one, unless it says otherwise.
|
|
pub(super) window_own: [Holds; 2],
|
|
/// Its frame's symbolic length where this draw read it, which makes the
|
|
/// drawing one that holds for that frame alone.
|
|
pub(super) frame_own_len: [Option<Len>; 2],
|
|
/// The window reads' equivalent for its own box.
|
|
pub(super) extent_own: [Holds; 2],
|
|
/// What each child's drawing depends on. Asking a child again replaces
|
|
/// its drawing, so it replaces this too rather than narrowing it.
|
|
pub(super) under: Vec<(WidgetId, LayoutHolds)>,
|
|
/// The movable region this widget's primitives are positioned through:
|
|
/// its own when opted in, otherwise the nearest ancestor's.
|
|
pub(super) move_idx: MoveIdx,
|
|
pub layer: usize,
|
|
/// The layer this widget was entered on, which its children's layers are
|
|
/// counted from however far `layer` has walked.
|
|
pub(super) own_layer: usize,
|
|
pub(super) depth: usize,
|
|
pub(super) id: WidgetId,
|
|
}
|
|
|
|
impl<'a> Painter<'a> {
|
|
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
|
let kind = self.rsc.ui_mut().primitives.kind::<P>();
|
|
self.write(kind, primitive, region);
|
|
}
|
|
|
|
/// Takes the kind, for a caller writing many of one primitive.
|
|
fn write<P: Primitive>(&mut self, kind: PrimitiveKind<P>, primitive: P, region: UiRegion) {
|
|
self.write_resolved(kind, primitive, region, self.resolve(region));
|
|
}
|
|
|
|
/// A box in this widget's extent coordinates, composed into its region
|
|
/// node's coordinates.
|
|
fn resolve(&self, region: UiRegion) -> UiRegion {
|
|
region.within(&self.extent)
|
|
}
|
|
|
|
fn write_resolved<P: Primitive>(
|
|
&mut self,
|
|
kind: PrimitiveKind<P>,
|
|
primitive: P,
|
|
region: UiRegion,
|
|
resolved: UiRegion,
|
|
) {
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
diag::bump(Counter::PrimitiveWrites);
|
|
let h = self.state.layers.write(
|
|
self.layer,
|
|
PrimitiveInst {
|
|
kind,
|
|
id: self.id,
|
|
primitive,
|
|
region: resolved,
|
|
mask_idx: self.mask,
|
|
move_idx: self.move_idx,
|
|
},
|
|
);
|
|
self.push_primitive(RetainedPrimitive { handle: h, region });
|
|
}
|
|
|
|
fn push_primitive(&mut self, h: RetainedPrimitive) {
|
|
if self.mask != MaskIdx::NONE {
|
|
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 frame
|
|
/// forwarded unchanged: what a container that is only a wrapper around
|
|
/// one child wants, and what every transparent container passes for the
|
|
/// frame.
|
|
pub fn widget<'s, W: ?Sized>(&'s mut self, id: &'s StrongWidget<W>) -> DrawResult<'s, 'a, W> {
|
|
self.widget_at(id, [None; 2], [Place::Within(Part::All); 2])
|
|
}
|
|
|
|
/// Asks a child, saying what its fractions are of and where it is asked.
|
|
///
|
|
/// `narrow` is a length this widget decided for the child's frame, per
|
|
/// axis, as a length of this widget's own frame: a resolved share, or a
|
|
/// box a sibling's answer decided. `None` forwards this widget's frame,
|
|
/// which is what a container that only divides room passes, so a
|
|
/// fraction under it means the same wherever it sits and however deeply
|
|
/// it is nested. A declared length narrows the frame here whatever the
|
|
/// caller says. A narrowed frame is placed in the part by the child's
|
|
/// alignment and is the box the child is asked in.
|
|
///
|
|
/// `place` is where the child is asked, per axis, as a part of this
|
|
/// widget's box: see [`Place`]. The child draws once, in that box, and
|
|
/// its answer is placed inside it by re-expressing the drawing. Nothing
|
|
/// is drawn again in a box an answer chose; a container that puts the
|
|
/// answer somewhere else says so with [`Self::place_at`].
|
|
pub fn widget_at<'s, W: ?Sized>(
|
|
&'s mut self,
|
|
id: &'s StrongWidget<W>,
|
|
narrow: [Option<Len>; 2],
|
|
place: [Place; 2],
|
|
) -> DrawResult<'s, 'a, W> {
|
|
let region_node = self.rsc.widgets().is_region_node(id.id());
|
|
let declared = self.declared_lens(id);
|
|
let align = self.rsc.widgets().alignment(id.id());
|
|
let (frame, extent) =
|
|
frame_and_extent(self.extent, self.frame, place, narrow, declared, align);
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
if region_node {
|
|
diag::bump(Counter::RegionNodeDraws);
|
|
diag::region_node(id.id(), self.id, extent);
|
|
}
|
|
// A child listed twice would be moved twice.
|
|
let re_asked = self.children.contains(&id.id());
|
|
if !re_asked {
|
|
self.children.push(id.id());
|
|
}
|
|
let px = frame.to_px(self.window);
|
|
let (size, answer_holds, holds) = self.state.draw_inner(
|
|
id.id(),
|
|
DrawInfo {
|
|
layer: self.layer,
|
|
parent: Some(self.id),
|
|
depth: self.depth + 1,
|
|
parent_move: self.move_idx,
|
|
region_node,
|
|
mask: self.mask,
|
|
frame,
|
|
part: extent,
|
|
placed: place,
|
|
asked: place,
|
|
narrow,
|
|
re_asked,
|
|
px,
|
|
},
|
|
None,
|
|
self.rsc,
|
|
);
|
|
let holds = self.in_parent(holds, extent, place, narrow, declared);
|
|
let answer_holds = self.in_parent(answer_holds, extent, place, narrow, declared);
|
|
match self.under.iter_mut().find(|(child, _)| *child == id.id()) {
|
|
Some((_, kept)) => *kept = holds,
|
|
None => self.under.push((id.id(), holds)),
|
|
}
|
|
DrawResult {
|
|
child: id,
|
|
painter: self,
|
|
size,
|
|
answer_holds,
|
|
}
|
|
}
|
|
|
|
/// Takes back a child that was drawn only to find out how long it is.
|
|
/// Its drawing is dropped and it is not one of this widget's children
|
|
/// this frame; what it answered is still something this widget asked.
|
|
pub fn undraw<W: ?Sized>(&mut self, id: &StrongWidget<W>) {
|
|
self.children.retain(|child| *child != id.id());
|
|
self.under.retain(|(child, _)| *child != id.id());
|
|
self.state.undraw_rec(id.id(), self.rsc);
|
|
}
|
|
|
|
/// Puts a child asked about in this draw somewhere else in this
|
|
/// widget's box: its answer, placed in this part instead. The drawing
|
|
/// is re-expressed there rather than made again -- what a row does once
|
|
/// it knows every slot, having measured each child from its cursor.
|
|
pub fn place_at<W: ?Sized>(&mut self, id: &StrongWidget<W>, place: [Place; 2]) {
|
|
debug_assert!(
|
|
self.children.contains(&id.id()),
|
|
"'{}' placed a child it did not ask about in this draw",
|
|
self.label()
|
|
);
|
|
let at = self.placing();
|
|
self.state.place_in(id.id(), &at, place, self.rsc);
|
|
}
|
|
|
|
/// This widget as the thing its children are placed within.
|
|
fn placing(&self) -> Placing {
|
|
Placing {
|
|
id: self.id,
|
|
extent: self.extent,
|
|
frame: self.frame,
|
|
window: self.window,
|
|
depth: self.depth,
|
|
move_idx: self.move_idx,
|
|
mask: self.mask,
|
|
}
|
|
}
|
|
|
|
/// What a widget's rules declare its lengths to be, which whoever draws
|
|
/// it resolves into its frame. Reading them depends on nothing -- the box
|
|
/// that comes of them is kept on the child, and `redraw` compares it
|
|
/// there.
|
|
fn declared_lens<W: ?Sized>(&self, id: &StrongWidget<W>) -> [Option<LayoutLen>; 2] {
|
|
declared_lens(self.rsc.widgets(), id.id())
|
|
}
|
|
|
|
/// What a child says its length is without being drawn, if it can say,
|
|
/// as the length its draw would report: a fraction in it is resolved
|
|
/// against this widget's frame, which is the frame 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(axis).exact().or_else(|| {
|
|
widgets
|
|
.get_dyn(id.id())
|
|
.and_then(|widget| widget.size_hint(axis))
|
|
});
|
|
let frame = self.frame.axis(axis);
|
|
let resolved = hint.map(|hint| hint.within_len(frame));
|
|
#[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 frame makes this draw a
|
|
// function of the frame's length. The fraction to ask about is
|
|
// the child's own: resolved against a frame of pixels, none is
|
|
// left to see it by.
|
|
if hint.rel != Rel::ZERO {
|
|
self.frame_own_len[axis as usize] = Some(frame);
|
|
}
|
|
}
|
|
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 frame or extent coordinates.
|
|
// TODO: merge the text methods into the primitive ones.
|
|
pub fn glyphs(&mut self, text: &RenderedText, origin: UiRegion) {
|
|
// Glyph offsets and sizes are pixels, which compose additively.
|
|
// Only the shared origin needs composing through the extent.
|
|
let resolved = self.resolve(origin);
|
|
let kind = self.rsc.ui_mut().primitives.kind::<GlyphPrimitive>();
|
|
for glyph in text.glyphs.iter() {
|
|
let place = |mut region: UiRegion| {
|
|
region.x.end = region.x.start;
|
|
region.y.end = region.y.start;
|
|
let mut region = region.offset(UiVec2::from_px(glyph.offset));
|
|
let size = PxVec2::new(
|
|
Px::from_int(glyph.entry.width as i32),
|
|
Px::from_int(glyph.entry.height as i32),
|
|
);
|
|
region.x.end = region.x.start.offset(size.x);
|
|
region.y.end = region.y.start.offset(size.y);
|
|
region
|
|
};
|
|
self.write_resolved(
|
|
kind,
|
|
GlyphPrimitive {
|
|
uv_min: glyph.entry.uv_min,
|
|
uv_max: glyph.entry.uv_max,
|
|
layer: glyph.entry.layer,
|
|
color: text.color,
|
|
flags: glyph.entry.flags(),
|
|
},
|
|
place(origin),
|
|
place(resolved),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// The symbolic length of this widget's own box along one axis, in the
|
|
/// lengths of its frame that it places its children in. Reading it pins
|
|
/// the drawing to that length -- and to nothing about where the box
|
|
/// starts, which is what lets a container move without being drawn
|
|
/// again. One axis at a time, because a container that divides one axis
|
|
/// holds for any length of the other.
|
|
pub fn extent_len(&mut self, axis: Axis) -> Len {
|
|
let len = self.extent.axis(axis).len();
|
|
self.extent_len[axis as usize] = Some(len);
|
|
len
|
|
}
|
|
|
|
/// The symbolic length of this widget's frame along one axis: what a
|
|
/// fraction it or anything under it declares is a fraction of. A
|
|
/// container reads it to hand a length of it down -- padding, which
|
|
/// takes its pixels off. Reading it pins the drawing to that frame, the
|
|
/// way [`Self::extent_len`] pins it to the box.
|
|
pub fn frame_len(&mut self, axis: Axis) -> Len {
|
|
let len = self.frame.axis(axis);
|
|
self.frame_own_len[axis as usize] = Some(len);
|
|
len
|
|
}
|
|
|
|
/// Where this widget sits in a box longer than the length it takes. A
|
|
/// widget that positions its own content reads it to place that content
|
|
/// the way the box around it would have placed the widget.
|
|
pub fn alignment(&self) -> RegionAlign {
|
|
self.rsc.widgets().alignment(self.id)
|
|
}
|
|
|
|
/// Whether a rule beside this widget gives its length on `axis` outright,
|
|
/// which makes whatever it reports for that axis moot. A rule that only
|
|
/// bounds the length is not one of these: the answer is still the
|
|
/// widget's to give, and something still has to work it out.
|
|
///
|
|
/// The widget under a rule does not otherwise learn of it -- this is for
|
|
/// a container deciding whether reading its children across an axis is
|
|
/// worth anything, since reading one is also what makes its own size
|
|
/// depend on it.
|
|
pub fn has_exact_size(&self, axis: Axis) -> bool {
|
|
self.rsc
|
|
.widgets()
|
|
.size_rules(self.id)
|
|
.axis(axis)
|
|
.exact()
|
|
.is_some()
|
|
}
|
|
|
|
/// This widget's own box in pixels. Reading it makes the drawing one
|
|
/// that holds for this box only, until `holds` says how far it goes.
|
|
pub fn px_size(&mut self) -> PxVec2 {
|
|
PxVec2::new(self.px_len(Axis::X), self.px_len(Axis::Y))
|
|
}
|
|
|
|
/// One axis of this widget's own box in pixels. Prefer this to
|
|
/// [`Self::px_size`] when the other axis cannot affect the drawing.
|
|
pub fn px_len(&mut self, axis: Axis) -> Px {
|
|
let part = self.extent.axis(axis).len();
|
|
let len = part.to_px(self.window.axis(axis));
|
|
let own = &mut self.extent_own[axis as usize];
|
|
if *own == Holds::ANY {
|
|
*own = Holds::at(len);
|
|
}
|
|
len
|
|
}
|
|
|
|
/// The lengths of this widget's own box on `axis` that what it is drawing
|
|
/// holds for -- the same primitives, in the same fractions and offsets
|
|
/// of the box, and the same reported size. A widget that read its length
|
|
/// in pixels holds for that one alone until it says otherwise.
|
|
pub fn holds(&mut self, axis: Axis, holds: impl Into<Holds>) {
|
|
let part = self.extent.axis(axis).len();
|
|
let holds = holds.into();
|
|
debug_assert!(
|
|
holds.contains(part.to_px(self.window.axis(axis))),
|
|
"'{}' ({:?}) says its drawing holds for lengths that leave out its own box",
|
|
self.label(),
|
|
self.id
|
|
);
|
|
self.extent_own[axis as usize] = holds;
|
|
}
|
|
|
|
/// A window length in pixels, which is what every length in layout is
|
|
/// measured in. Reading one pins the drawing to this window wherever the
|
|
/// length is a fraction of it; one that is only pixels is that many
|
|
/// pixels in any window and pins nothing.
|
|
pub fn to_px(&mut self, len: Len, axis: Axis) -> Px {
|
|
let window = self.window.axis(axis);
|
|
if len.rel != Rel::ZERO {
|
|
let own = &mut self.window_own[axis as usize];
|
|
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(axis)),
|
|
"'{}' ({:?}) says its drawing holds for windows that leave out this one",
|
|
self.label(),
|
|
self.id
|
|
);
|
|
self.window_own[axis as usize] = holds;
|
|
}
|
|
|
|
pub fn text_data(&mut self) -> &mut TextData {
|
|
&mut self.rsc.ui_mut().text
|
|
}
|
|
|
|
pub fn child_layer(&mut self) {
|
|
self.layer = self.state.layers.child(self.layer);
|
|
}
|
|
|
|
/// The layer this widget's `n`th child draws on, addressed rather than
|
|
/// walked to. A container that measures one child by drawing it can ask
|
|
/// on the layer that child will end up on, and then the second ask is a
|
|
/// reuse rather than a second drawing on another layer.
|
|
pub fn child_layer_at(&mut self, n: usize) {
|
|
let mut at = self.state.layers.child(self.own_layer);
|
|
for _ in 0..n {
|
|
at = self.state.layers.next(at);
|
|
}
|
|
self.layer = at;
|
|
}
|
|
|
|
pub fn next_layer(&mut self) {
|
|
self.layer = self.state.layers.next(self.layer);
|
|
}
|
|
|
|
pub fn label(&self) -> &str {
|
|
&self.rsc.widgets().data(self.id).unwrap().label
|
|
}
|
|
|
|
pub fn id(&self) -> &WidgetId {
|
|
&self.id
|
|
}
|
|
}
|
|
|
|
/// A child that has just been drawn. Reading its size records that this
|
|
/// widget's own size depends on it; dropping it without reading draws the
|
|
/// child and leaves the parent independent of what it came to.
|
|
pub struct DrawResult<'p, 'a, W: ?Sized> {
|
|
painter: &'p mut Painter<'a>,
|
|
child: &'p StrongWidget<W>,
|
|
size: Size,
|
|
answer_holds: LayoutHolds,
|
|
}
|
|
|
|
impl<W: ?Sized> DrawResult<'_, '_, W> {
|
|
pub fn size(self) -> Size {
|
|
#[cfg(feature = "layout-diagnostics")]
|
|
{
|
|
diag::bump(Counter::SizeReads);
|
|
diag::size_read(self.child.id(), self.painter.id, self.size);
|
|
}
|
|
self.painter.depend_on(self.child);
|
|
self.painter.answer_under = self.painter.answer_under.and(self.answer_holds);
|
|
self.size
|
|
}
|
|
|
|
pub fn len(self, axis: Axis) -> LayoutLen {
|
|
self.size().axis(axis)
|
|
}
|
|
}
|
|
|
|
/// What `Painter::primitive` takes: a primitive, or something that yields one
|
|
/// and does whatever else drawing it needs.
|
|
pub trait PrimitiveLike {
|
|
type Primitive: Primitive;
|
|
fn into_primitive(self, painter: &mut Painter) -> Self::Primitive;
|
|
}
|
|
|
|
impl<P: Primitive> PrimitiveLike for P {
|
|
type Primitive = P;
|
|
fn into_primitive(self, _: &mut Painter) -> P {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl PrimitiveLike for &TextureHandle {
|
|
type Primitive = TexturePrimitive;
|
|
|
|
/// Retains a share of the handle, so the slot the primitive names cannot
|
|
/// be freed and reused while it is still drawn.
|
|
fn into_primitive(self, painter: &mut Painter) -> TexturePrimitive {
|
|
painter.textures.push(self.clone());
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
/// Moves what a child depends on into this widget's own terms: this
|
|
/// method's `impl` block is where a `Painter`'s own boxes are, so it takes
|
|
/// only what the child was asked with.
|
|
impl Painter<'_> {
|
|
/// Window ranges are already about the one unit and combine directly.
|
|
/// A frame pin becomes this widget's own frame wherever a length of it
|
|
/// is what reached the child; where only pixels did, no length of this
|
|
/// frame can change the child's and the pin stops here.
|
|
///
|
|
/// Extent validity maps back through the part of this widget's box,
|
|
/// where the box the child was asked in is that part; a declared length
|
|
/// places the box inside the part instead, and then only that length
|
|
/// reaches the child. A narrowed frame is not one of these: it decides
|
|
/// what fractions under the child mean and leaves the box the part it
|
|
/// was given.
|
|
fn in_parent(
|
|
&self,
|
|
holds: LayoutHolds,
|
|
extent: UiRegion,
|
|
place: [Place; 2],
|
|
narrow: [Option<Len>; 2],
|
|
declared: [Option<LayoutLen>; 2],
|
|
) -> LayoutHolds {
|
|
let mut result = LayoutHolds::ANY;
|
|
for axis in AXES {
|
|
let n = axis as usize;
|
|
// Every read became pixels against the window, so a range on
|
|
// it is already in this widget's terms.
|
|
result.window[n] = holds.window[n];
|
|
let reaches = narrow[n].is_none()
|
|
&& !matches!(place[n].part(), Part::Sized(_))
|
|
&& declared[n].is_none_or(|len| len.rel != Rel::ZERO);
|
|
result.frame_len[n] = holds.frame_len[n].and(reaches.then(|| self.frame.axis(axis)));
|
|
match (place[n].part(), declared[n].is_some()) {
|
|
// Its box is this widget's own, or a part of it in that
|
|
// box's own lengths: so what it holds for is a range on this
|
|
// widget's own box, which is what lets that box move without
|
|
// a redraw. A length it pinned is this widget's length
|
|
// wherever the part is the whole of it, and pins the same
|
|
// way.
|
|
(Part::All, false) => {
|
|
result.extent[n] = holds.extent[n];
|
|
result.extent_len[n] = holds.extent_len[n];
|
|
}
|
|
// Its box is a part of this widget's own box, in that box's
|
|
// own lengths, so what it holds for maps back through that
|
|
// part into a range on this widget's box. A length it pinned
|
|
// is this widget's length less the part's pixels where the
|
|
// part is the whole of the box less pixels, which is the one
|
|
// shape that inverts exactly; any other part pins this
|
|
// widget's own length.
|
|
(Part::Of(span), false) => {
|
|
let part_len = span.len();
|
|
result.extent[n] = holds.extent[n].through(part_len);
|
|
result.extent_len[n] = holds.extent_len[n].map(|pinned| match part_len.rel {
|
|
Rel::ONE => pinned - Len::from_parts(Rel::ZERO, part_len.px),
|
|
_ => self.extent.axis(axis).len(),
|
|
});
|
|
}
|
|
// Its box is a length this widget decided, from its own
|
|
// frame 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[n] =
|
|
result.window[n].and(holds.extent[n].through(extent.axis(axis).len()));
|
|
}
|
|
}
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/// What a widget declares a length of its box to be. `leftover` is not one: a
|
|
/// share of what is left over is only a length to the widget dividing one,
|
|
/// so it passes up in the size instead.
|
|
pub(crate) fn declared_lens(widgets: &Widgets, id: WidgetId) -> [Option<LayoutLen>; 2] {
|
|
let rules = widgets.size_rules(id);
|
|
let widget = widgets.get_dyn(id);
|
|
AXES.map(|axis| {
|
|
rules.axis(axis).declared().or_else(|| {
|
|
// A hint still narrows the box where no rule does, which is how a
|
|
// widget with a natural pixel size -- an image, a gap -- gets that
|
|
// size rather than the whole offer. That is the offer's business
|
|
// rather than a declaration's, and this falls away once a widget
|
|
// occupies its reported size inside the box it was offered.
|
|
widget
|
|
.and_then(|widget| widget.size_hint(axis))
|
|
.filter(|len| len.leftover == Weight::ZERO)
|
|
})
|
|
})
|
|
}
|
|
|
|
/// Whether what a widget reported along an axis is the whole of the box it
|
|
/// is in rather than a part to be placed inside it. A share fills, because a
|
|
/// share is a length only to whoever divides one, and whoever did is the one
|
|
/// that handed down this box. A declared axis does too: the rule already gave
|
|
/// the region its length, and the rule's length is what the widget reports
|
|
/// there. And an axis the parent decided from the answer is
|
|
/// the answer already.
|
|
pub(crate) fn fills(reported: LayoutLen, declared: Option<LayoutLen>, decided: bool) -> bool {
|
|
reported.leftover != Weight::ZERO || declared.is_some() || decided
|
|
}
|
|
|
|
/// Where a widget's drawing goes inside the part its parent gave it: what
|
|
/// it reported, on the side of the part its alignment says, and the whole
|
|
/// part wherever the answer fills it.
|
|
///
|
|
/// The length it reported is a length of its frame, and the part is one too,
|
|
/// so this takes one from the other rather than composing it into the part.
|
|
/// That is what makes a fraction the same fraction wherever the part it is
|
|
/// placed in sits and however long it is -- the fraction is resolved once,
|
|
/// here, against the frame it was reported of.
|
|
pub(crate) fn placed_extent(
|
|
part: UiRegion,
|
|
size: Size,
|
|
declared: [Option<LayoutLen>; 2],
|
|
fill: [bool; 2],
|
|
align: RegionAlign,
|
|
) -> UiRegion {
|
|
let mut placed = part;
|
|
for axis in AXES {
|
|
let n = axis as usize;
|
|
let reported = size.axis(axis);
|
|
if fills(reported, declared[n], fill[n]) {
|
|
continue;
|
|
}
|
|
let len = Len::from_parts(reported.rel, reported.px);
|
|
let span = placed.axis_mut(axis);
|
|
span.start += (span.len() - len).scale(align.axis(axis).rel());
|
|
span.end = span.start + len;
|
|
}
|
|
placed
|
|
}
|
|
|
|
/// The frame length and the box a child is asked in, in the coordinates the
|
|
/// widget asking draws in.
|
|
///
|
|
/// `own` is that widget's own box, and `place` what of it the child is
|
|
/// given. `narrow` is a frame the container decided for the child -- a row's
|
|
/// slot, or padding's frame less its pixels -- and [`Part::Sized`] one a
|
|
/// sibling's answer decided; both are window lengths, like every other
|
|
/// length here, since a slot of a row is not a fraction of anything the row
|
|
/// can name. The child's declaration is a fraction of whichever reached it,
|
|
/// and is the only one of the three that also places the box: a box the
|
|
/// caller decided is what `place` names.
|
|
pub(crate) fn frame_and_extent(
|
|
own: UiRegion,
|
|
parent_frame: UiVec2,
|
|
place: [Place; 2],
|
|
narrow: [Option<Len>; 2],
|
|
declared: [Option<LayoutLen>; 2],
|
|
align: RegionAlign,
|
|
) -> (UiVec2, UiRegion) {
|
|
let part = part_of(own, place, align);
|
|
let mut frame = parent_frame;
|
|
let mut extent = part;
|
|
for axis in AXES {
|
|
let n = axis as usize;
|
|
let sized = match place[n].part() {
|
|
Part::Sized(len) => Some(len),
|
|
_ => None,
|
|
};
|
|
let base = sized
|
|
.or(narrow[n])
|
|
.unwrap_or_else(|| parent_frame.axis(axis));
|
|
let len = declared[n]
|
|
.map(|len| Len::from_parts(len.rel, len.px).within_len(base))
|
|
.unwrap_or(base);
|
|
*frame.axis_mut(axis) = len;
|
|
if declared[n].is_some() {
|
|
let slot = part.axis(axis);
|
|
let start = slot.start + (slot.len() - len).scale(align.axis(axis).rel());
|
|
*extent.axis_mut(axis) = UiSpan::new(start, start + len);
|
|
}
|
|
}
|
|
(frame, extent)
|
|
}
|
|
|
|
/// The part of a widget's own box a `place` names, in the coordinates that
|
|
/// box is in.
|
|
fn part_of(extent: UiRegion, place: [Place; 2], align: RegionAlign) -> UiRegion {
|
|
let mut part = extent;
|
|
for axis in AXES {
|
|
*part.axis_mut(axis) = place[axis as usize]
|
|
.part()
|
|
.of(*extent.axis(axis), align.axis(axis));
|
|
}
|
|
part
|
|
}
|