use super::*; use crate::util::impl_axis_index; use crate::{Px, PxVec2, Rel, UiNum, Weight, util::impl_op}; #[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Size { pub x: LayoutLen, pub y: LayoutLen, } /// What a widget asks for along one axis: a [`Len`] -- pixels and a fraction /// of the box it is given -- plus a share of whatever is left over once /// everything fixed has been taken. The parts add up rather than choosing /// between one another. /// /// Only a container dividing its room can answer a share, so a length nobody /// divides is a `Len`: a position, a padding, a cap, anything already /// resolved. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct LayoutLen { pub px: Px, pub rel: Rel, pub leftover: Weight, } impl From for LayoutLen { fn from(value: N) -> Self { LayoutLen::px(value.to_f32()) } } impl From<(Nx, Ny)> for Size { fn from((x, y): (Nx, Ny)) -> Self { Self { x: x.into(), y: y.into(), } } } /// A length with no share in it is a length a container does not have to /// divide, which is one it can always give. impl From for LayoutLen { fn from(len: Len) -> Self { Self { px: len.px, rel: len.rel, leftover: Weight::ZERO, } } } impl From for Size { fn from(value: LayoutLen) -> Self { Self { x: value, y: value } } } impl Size { pub const ZERO: Self = Self { x: LayoutLen::ZERO, y: LayoutLen::ZERO, }; pub const LEFTOVER: Self = Self { x: LayoutLen::LEFTOVER, y: LayoutLen::LEFTOVER, }; /// From something measured outside layout -- a texture, a shaped line -- /// which is where a size in floats comes from. pub fn px(v: Vec2) -> Self { Self::from_px(PxVec2::from_f32(v)) } pub const fn from_px(v: PxVec2) -> Self { Self { x: LayoutLen { px: v.x, ..LayoutLen::ZERO }, y: LayoutLen { px: v.y, ..LayoutLen::ZERO }, } } pub fn rel(v: Vec2) -> Self { Self { x: LayoutLen::rel(v.x), y: LayoutLen::rel(v.y), } } pub fn leftover(v: Vec2) -> Self { Self { x: LayoutLen::leftover(v.x), y: LayoutLen::leftover(v.y), } } pub fn to_uivec2(self) -> UiVec2 { UiVec2 { x: self.x.apply_leftover(), y: self.y.apply_leftover(), } } pub fn from_axis(axis: Axis, aligned: LayoutLen, ortho: LayoutLen) -> Self { match axis { Axis::X => Self { x: aligned, y: ortho, }, Axis::Y => Self { x: ortho, y: aligned, }, } } } impl LayoutLen { pub const ZERO: Self = Self { px: Px::ZERO, rel: Rel::ZERO, leftover: Weight::ZERO, }; pub const LEFTOVER: Self = Self { px: Px::ZERO, rel: Rel::ZERO, leftover: Weight::ONE, }; /// The whole of what is left over counts as the whole box, which is what /// a length means to something that is not dividing a box between /// siblings -- a scroll asking how long its content is. pub fn apply_leftover(&self) -> Len { let share = match self.leftover > Weight::ZERO { true => Rel::ONE, false => Rel::ZERO, }; Len::from_parts(self.rel.add(share), self.px) } /// Only pixels: the same number of them whatever box it lands in, and /// whatever anyone else in the row asks for. A length that is any part /// of a box or of what is left over is not one. pub fn is_px(self) -> bool { self.rel == Rel::ZERO && self.leftover == Weight::ZERO } /// Nothing but a claim on what is left over, so there is no length here /// at all where nothing is. pub fn is_only_leftover(self) -> bool { self.leftover > Weight::ZERO && self.without_leftover() == Len::ZERO } /// This as a length of a box, where it is one. `leftover` is not: a /// share of what is left over is a length only to whoever divides one, /// so it passes up in the reported size instead and is resolved there. pub fn declared(self) -> Option { (self.leftover == Weight::ZERO).then(|| self.without_leftover()) } /// What this takes whatever is left over: the reading of a length for /// anyone not dividing a box between siblings, where a share is a claim /// on someone else's room rather than a length of its own. /// [`Self::apply_leftover`] is the opposite reading of the same value. pub const fn without_leftover(self) -> Len { Len::from_parts(self.rel, self.px) } /// This length, given as a part of a box `len` long, as a part of the /// box `len` is itself a part of. The share is untouched: it is a claim /// on whoever divides the room, not a fraction of anything. pub const fn within_len(self, len: Len) -> Self { let part = self.without_leftover().within_len(len); Self { px: part.px, rel: part.rel, leftover: self.leftover, } } pub fn px(px: impl UiNum) -> Self { Self { px: Px::from_num(px), ..Self::ZERO } } pub fn rel(rel: impl UiNum) -> Self { Self { rel: Rel::from_num(rel), ..Self::ZERO } } pub fn leftover(ratio: impl UiNum) -> Self { Self { leftover: Weight::from_num(ratio), ..Self::ZERO } } } pub mod len_fns { use super::*; pub fn px(px: impl UiNum) -> LayoutLen { LayoutLen::px(px) } pub fn rel(rel: impl UiNum) -> LayoutLen { LayoutLen::rel(rel) } pub fn leftover(ratio: impl UiNum) -> LayoutLen { LayoutLen::leftover(ratio) } } impl_op!(same LayoutLen Add add; px rel leftover); impl_op!(same LayoutLen Sub sub; px rel leftover); impl_op!(same Size Add add; x y); impl_op!(same Size Sub sub; x y); impl Default for LayoutLen { fn default() -> Self { Self::leftover(1.0) } } impl std::fmt::Display for Size { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "({}, {})", self.x, self.y) } } impl std::fmt::Display for LayoutLen { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.px != Px::ZERO { write!(f, "{} px;", self.px)?; } if self.rel != Rel::ZERO { write!(f, "{} rel;", self.rel)?; } if self.leftover != Weight::ZERO { write!(f, "{} leftover;", self.leftover)?; } Ok(()) } } impl_axis_index!(Size => LayoutLen);