use crate::prelude::*; use std::marker::PhantomData; pub struct Span { pub children: Vec, pub dir: Dir, pub gap: Px, } impl Widget for Span { fn size_request(&self, requests: &mut SizeRequests, axis: Axis) -> Option { if axis != self.dir.axis { // A share can be hidden when the other axis has no room. Its // cross-axis length then contributes nothing to the drawn answer. return None; } let mut total = RequestedLen::from(Len::from_parts(Rel::ZERO, self.gaps())); for child in &self.children { let child = requests.widget(child, axis)?; total = requests.sum(total, child); } Some(total) } fn draw(&mut self, painter: &mut Painter) -> Size { painter.with_requests(|painter, lens, values| self.layout(painter, lens, values)) } } impl Span { fn layout( &self, painter: &mut Painter, lens: &mut Vec, values: &mut Vec, ) -> Size { let axis = self.dir.axis; // The row this span lays its children out along, as a length of the // rel base they are laid out against. Where it starts is nothing's // business -- a slot is a length from there -- so what this reads is // the length alone. let row = painter.region_len(axis); self.collect(painter, row, lens, true); let gaps = self.gaps(); let fixed = lens .iter() .try_fold(Len::from_parts(Rel::ZERO, gaps), |sum, len| { Some(sum + len.linear()?.without_leftover()) }); if let Some(fixed) = fixed && lens.iter().any(|len| len.has_leftover()) && !painter.longer_than(row, fixed, axis) { // With no share to assign, intrinsic drawings keep the remaining // offer, including overflow. Their answer is only moved into a slot. self.collect(painter, row, lens, false); } let nonlinear = lens.iter().any(|len| len.linear().is_none()); if nonlinear { painter.allocate(lens, row - Len::from_parts(Rel::ZERO, gaps), axis, values); } let allocated = nonlinear.then(|| &values[..]); let total = match allocated { Some(allocated) => LayoutLen { px: allocated.iter().fold(gaps, |sum, len| sum + *len), ..LayoutLen::ZERO }, None => lens.iter().fold( LayoutLen { px: gaps, ..LayoutLen::ZERO }, |sum, len| sum + len.linear().unwrap(), ), }; let all_fixed = total.without_leftover(); let room = row - all_fixed; let any_leftover = total.leftover > Weight::ZERO; let has_room = any_leftover && painter.longer_than(row, all_fixed, axis); // Across itself a span is as long as its longest child -- unless a // rule beside it gives that length outright, and then reading them // answers nothing and makes its size depend on theirs for it. A rule // that only bounds the length does not count: the answer is still // this span's to give. let shrinks = !painter.has_exact_size(!axis); // What the fixed parts and the gaps before here take, which is a sum // of lengths and exact, and how much of the leftover weight is // spoken for. Both ends of a slot are read from those two rather // than stepped from the last child: the share of the room is // rounded, and taking each end from the one before it would carry // every rounding along the row. let mut fixed = Len::ZERO; let mut taken = Weight::ZERO; let mut ortho = LayoutLen::ZERO; // Nothing divides the room where no child asked for any of it, and a // ratio of a whole of nothing has no answer. let reached = |fixed: Len, taken: Weight| match any_leftover { false => fixed, true => fixed + room.scale(Rel::ratio(taken, total.leftover)), }; for (index, (child, request)) in self.children.iter().zip(lens.iter()).enumerate() { // An allocated row already has a length for every child; without // one the request is the length and the room is divided here. // Either way a child asking for nothing but a part of what is // left over, when nothing is, is not drawn at all -- one that // also asked for pixels or a fraction keeps those and overflows. let (len, shares, nothing_left) = match allocated { Some(allocated) => { let len = LayoutLen { px: allocated[index], ..LayoutLen::ZERO }; let shares = request.has_leftover(); (len, shares, shares && len.px == Px::ZERO) } None => { let len = request.linear().unwrap(); let shares = len.leftover > Weight::ZERO && has_room; (len, shares, len.is_only_leftover() && !has_room) } }; if nothing_left { painter.undraw(child); fixed.px += self.gap; continue; } let from = reached(fixed, taken); if shares { taken += len.leftover; } fixed += len.without_leftover(); let to = reached(fixed, taken); // Along the row the span says where the child goes, and that slot // is the child's box outright rather than something to place an // answer inside again. A share is decided here and nowhere // else: its slot narrows its rel base, and the child is asked in // it, since a text wraps at the width it is actually given. A // fixed child's slot is its own answer, so a drawing made in the // room is put there as it is, and one not made yet is made here. let slot = self.slot(row, from, to); let mut place = slot.shifted_desc().allocated().on_axis(axis); if shares { place = place.rel_base(axis, slot.len()); } let used = painter.place_at(child, place).len(!axis); if shrinks { // Choosing between a fixed and a relative length from the // span's own eventual width admits multiple fixed points. // A scalable child therefore makes the span scalable too; // only fixed children are compared with one another. if !used.is_px() { ortho = LayoutLen::LEFTOVER; } else if ortho.leftover == Weight::ZERO { ortho.px = ortho.px.max(used.px); } } fixed.px += self.gap; } // Where nothing was allocated the weight is carried whole rather // than collapsed to one share, so nesting spans divides the same // space rather than re-dividing a share of it: four `leftover(1)` // children under two spans under one span get a quarter each, which // one share per level does not give. Resolution happens at the // nearest ancestor with a length, and the root always has one -- // or, where a comparison deferred the row, at the ancestor whose // allocation discovery carried these requests to, and `total` is // pixels by the time it gets here. let ortho = match shrinks { true => ortho, false => LayoutLen::rel(1.0), }; Size::from_axis(axis, total, ortho) } } impl Span { /// What the gaps between this span's children take, which is a length of /// the row before anything is divided. fn gaps(&self) -> Px { self.gap .mul_int(self.children.len().saturating_sub(1) as i32) } fn collect( &self, painter: &mut Painter, row: Len, lens: &mut Vec, discover: bool, ) { let axis = self.dir.axis; let mut cursor = Len::ZERO; lens.clear(); for child in &self.children { let request = if discover { painter.size_request(child, axis) } else { painter.size_hint(child, axis).map(Into::into) }; let len = match request { Some(len) => len, None => { let room = self.slot(row, cursor, row).shifted_desc().on_axis(axis); let len = painter.widget_at(child, room).len(axis); painter.measured_request(child, axis, len) } }; cursor += painter.minimum_request(&len, axis); cursor.px += self.gap; lens.push(len); } } /// The stretch of the row between two distances from where this span /// starts laying children out, as a span of its own box. A negative /// direction lays out from the far end, so the same two distances mirror /// in a row `row` long. fn slot(&self, row: Len, from: Len, to: Len) -> UiSpan { match self.dir.sign { Sign::Pos => from.to(to), Sign::Neg => (row - to).to(row - from), } } pub fn empty(dir: Dir) -> Self { Self { children: Vec::new(), dir, gap: Px::ZERO, } } pub fn gap(mut self, gap: impl UiNum) -> Self { self.gap = Px::from_num(gap); self } pub fn push(&mut self, w: StrongWidget) { self.children.push(w); } pub fn pop(&mut self) -> Option { self.children.pop() } } pub struct SpanBuilder, Tag> { pub children: Wa, pub dir: Dir, pub gap: Px, _pd: PhantomData<(State, Tag)>, } impl, Tag> WidgetFnTrait for SpanBuilder { type Widget = Span; #[track_caller] fn run(self, rsc: &mut Rsc) -> Self::Widget { Span { children: self.children.add(rsc).arr.into_iter().collect(), dir: self.dir, gap: self.gap, } } } impl, Tag> SpanBuilder { pub fn new(children: Wa, dir: Dir) -> Self { Self { children, dir, gap: Px::ZERO, _pd: PhantomData, } } pub fn gap(mut self, gap: impl UiNum) -> Self { self.gap = Px::from_num(gap); self } } impl std::ops::Deref for Span { type Target = Vec; fn deref(&self) -> &Self::Target { &self.children } } impl std::ops::DerefMut for Span { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.children } }