use crate::util::impl_axis_index; use crate::{Axis, AxisAlign, Len, PrimitiveHandle, RegionAlign, UiRegion, UiSpan}; /// How a child's region along one axis comes from the region of the widget /// asking, and what its fractions are of. /// /// The three ways of saying a region are the three the geometry already has: /// a span composed into the caller's box, a span shifted to where that box /// starts, and a length placed in it by alignment. Which one is meant cannot /// be read off the numbers, since two of them take the same span and apply /// it differently, so it is said here. #[derive(Clone, Copy, Debug, PartialEq)] pub struct PlaceDescAxis { span: PlaceSpan, fills: bool, rel_base: RelBase, } #[derive(Clone, Copy, Debug, PartialEq)] enum PlaceSpan { Within(UiSpan), Shifted(UiSpan), Sized(Len), } /// What a child's fractions are of, where the caller has not named a length. #[derive(Clone, Copy, Debug, PartialEq)] enum RelBase { /// The caller's own, unchanged. Inherit, /// The caller's own, narrowed the way the region is. WithRegion, /// This length of the window. Len(Len), } impl PlaceDescAxis { /// The whole of the caller's box. pub const WHOLE: Self = UiSpan::FULL.within_desc(); /// This region is the child's placement: its answer is not placed inside /// it again. A container uses it where it hands back exactly what the /// child asked for -- a row placing a child at the length it reported. pub const fn fills(mut self) -> Self { self.fills = true; self } /// This along `axis`, and the whole of the caller's box across it: what /// a container dividing one axis says, since nothing divides the other. /// [`PlaceDesc::from_axis`] says the across one where it is not the /// whole. pub const fn axis(self, axis: Axis) -> PlaceDesc { PlaceDesc::from_axis(axis, self, Self::WHOLE) } /// What the child's fractions are of, as a length of the window: a /// resolved share, or a box a sibling's answer decided. pub const fn rel_base(mut self, len: Len) -> Self { self.rel_base = RelBase::Len(len); self } /// Whether the region is the placement outright, rather than a box the /// answer is placed inside. pub(crate) const fn does_fill(self) -> bool { self.fills } /// Where it lands in the coordinates `own` is in. pub(crate) fn of(self, own: UiSpan, align: AxisAlign) -> UiSpan { match self.span { PlaceSpan::Within(span) => span.within(&own), PlaceSpan::Shifted(mut span) => { span.shift(own.start); span } PlaceSpan::Sized(len) => own.place(len, align), } } /// The child's rel base, where this says one outright. `None` forwards /// the caller's own, and [`RelBase::WithRegion`] is resolved by whoever /// can read that rel base, so it does not reach here. pub(crate) const fn stated_rel_base(self) -> Option { match self.rel_base { RelBase::Len(len) => Some(len), _ => None, } } /// The length this narrows the caller's rel base by, where it does. /// `None` leaves that rel base alone, and reading it is then a /// dependency the caller does not take. pub(crate) const fn narrows_rel_base(self) -> Option { match (self.rel_base, self.span) { (RelBase::WithRegion, PlaceSpan::Within(span)) => Some(span), _ => None, } } /// The span it composes into the caller's box, where that is what it /// does: the one case whose validity maps back through the part. pub(crate) const fn within_span(self) -> Option { match self.span { PlaceSpan::Within(span) => Some(span), _ => None, } } /// Whether the caller decided this length rather than a place along its /// own box, which is what stops its length reaching the child at all. pub(crate) const fn is_sized(self) -> bool { matches!(self.span, PlaceSpan::Sized(_)) } /// The same, with its rel base stated outright. pub(crate) const fn with_rel_base(mut self, len: Option) -> Self { self.rel_base = match len { Some(len) => RelBase::Len(len), None => RelBase::Inherit, }; self } } /// Where a child is asked, on both axes. A [`UiRegion`] converts into the /// common case: that box of the caller's own, the answer placed inside it. #[derive(Clone, Copy, Debug, PartialEq)] pub struct PlaceDesc { pub x: PlaceDescAxis, pub y: PlaceDescAxis, } impl PlaceDesc { /// The whole of the caller's box, on both axes. pub const WHOLE: Self = Self::splat(PlaceDescAxis::WHOLE); pub const fn new(x: PlaceDescAxis, y: PlaceDescAxis) -> Self { Self { x, y } } /// The same on both axes. pub const fn splat(place: PlaceDescAxis) -> Self { Self { x: place, y: place } } /// A description per axis, where the two differ and neither is the /// axis a container divides. pub fn per_axis(f: impl Fn(Axis) -> PlaceDescAxis) -> Self { Self::new(f(Axis::X), f(Axis::Y)) } /// `aligned` on `axis` and `ortho` on the other, which is how a /// container that divides one axis says what it is doing. pub const fn from_axis(axis: Axis, aligned: PlaceDescAxis, ortho: PlaceDescAxis) -> Self { match axis { Axis::X => Self::new(aligned, ortho), Axis::Y => Self::new(ortho, aligned), } } /// Both regions are the child's placement. See [`PlaceDescAxis::fills`]. pub const fn fills(self) -> Self { Self::new(self.x.fills(), self.y.fills()) } /// The child's rel base on one axis. See [`PlaceDescAxis::rel_base`]. pub const fn rel_base(mut self, axis: Axis, len: Len) -> Self { self[axis] = self[axis].rel_base(len); self } /// The box each axis names, in the coordinates `own` is in. pub(crate) fn of(self, own: UiRegion, align: RegionAlign) -> UiRegion { UiRegion::new(self.x.of(own.x, align.x), self.y.of(own.y, align.y)) } } impl UiSpan { /// This span composed into the caller's own box, so it moves and scales /// with it: [`UiSpan::within`], which is what a container that insets /// one speaks. Taking eleven pixels off the end needs no length, where /// saying the same thing in window lengths would make the container read /// its own box -- and a box chosen from its own answer then feeds back /// into the answer. /// /// The child's rel base is narrowed the same way, so padding takes its /// pixels off both and `rel(1)` under it fills the caller rather than /// overflowing it. pub const fn within_desc(self) -> PlaceDescAxis { PlaceDescAxis { span: PlaceSpan::Within(self), fills: false, rel_base: RelBase::WithRegion, } } /// This span shifted to where the caller's own box starts: window /// lengths along a cursor, which is what a container dividing room among /// its children speaks. A child's report is a window length, so the /// cursor that sums those reports is one too, and a moved box re-places /// every child by re-adding its start, exactly. /// /// The child's rel base passes through: how far along the cursor a child /// sits says nothing about what a fraction under it is of. The same span /// says [`Self::within_desc`] as a part of that box instead, and which is /// meant cannot be read off the numbers. pub const fn shifted_desc(self) -> PlaceDescAxis { PlaceDescAxis { span: PlaceSpan::Shifted(self), fills: false, rel_base: RelBase::Inherit, } } } impl Len { /// A box this long, placed in the caller's own by the child's alignment: /// the rule that places an answer, with the length given from above /// rather than reported. What a stack's sizing child decides for the /// rest. It is the child's rel base too. pub const fn as_desc(self) -> PlaceDescAxis { PlaceDescAxis { span: PlaceSpan::Sized(self), fills: false, rel_base: RelBase::Len(self), } } } impl From for PlaceDesc { fn from(region: UiRegion) -> Self { Self::new(region.x.within_desc(), region.y.within_desc()) } } impl From for PlaceDesc { fn from(place: PlaceDescAxis) -> Self { Self::splat(place) } } /// A primitive as it was written: its box in the widget's own box's /// coordinates, which is what a move of that box re-composes from. #[derive(Debug)] pub struct RetainedPrimitive { pub handle: PrimitiveHandle, pub region: UiRegion, } impl_axis_index!(PlaceDesc => PlaceDescAxis);