use crate::prelude::*; pub struct Pad { pub padding: Padding, pub inner: StrongWidget, } impl Widget for Pad { fn draw(&mut self, painter: &mut Painter) -> Size { // The inner's own alignment, not the near edge. This reports the // inner's size plus the padding, so where the box is that answer the // inset box is exactly the inner and alignment has no room to move // it; where the box is bigger -- a share of a row, a rule over this // widget -- the slack is the inner's to sit in, and forcing the near // edge pinned it to a corner it had not asked for. // // Padding is an inset of both: it comes off the frame, so `rel(1)` // under it fills this widget rather than overflowing it by the // padding, and it comes off the box, so what is drawn sits inside. // The two stay distinct -- the box can be narrower still, where a row // asked this widget in the room left, and a text wraps at that. let inset = |lead: Px, trail: Px| { Place::Within(Part::Inset { lead: Len::from_parts(Rel::ZERO, lead), trail: Len::from_parts(Rel::ZERO, trail), }) }; let place = [ inset(self.padding.left, self.padding.right), inset(self.padding.top, self.padding.bottom), ]; // Read from this widget's own frame rather than written as a // fraction of it: a frame is a length of the window like everything // else here, and taking the padding off is the whole of what this // widget does to it. let narrow = [ (Axis::X, self.padding.left + self.padding.right), (Axis::Y, self.padding.top + self.padding.bottom), ] .map(|(axis, pixels)| Some(painter.frame_len(axis) - Len::from_parts(Rel::ZERO, pixels))); let inner = painter.widget_at(&self.inner, narrow, place).size(); Size { x: LayoutLen { px: inner.x.px + self.padding.left + self.padding.right, ..inner.x }, y: LayoutLen { px: inner.y.px + self.padding.top + self.padding.bottom, ..inner.y }, } } } pub struct Padding { pub left: Px, pub right: Px, pub top: Px, pub bottom: Px, } impl Padding { pub const ZERO: Self = Self { left: Px::ZERO, right: Px::ZERO, top: Px::ZERO, bottom: Px::ZERO, }; pub fn uniform(amt: impl UiNum) -> Self { let amt = Px::from_num(amt); Self { left: amt, right: amt, top: amt, bottom: amt, } } /// `region` less this padding on each side. pub fn region_of(&self, mut region: UiRegion) -> UiRegion { region.x.start.px += self.left; region.y.start.px += self.top; region.x.end.px -= self.right; region.y.end.px -= self.bottom; region } pub fn region(&self) -> UiRegion { self.region_of(UiRegion::FULL) } pub fn x(amt: impl UiNum) -> Self { let amt = Px::from_num(amt); Self { left: amt, right: amt, ..Self::ZERO } } pub fn y(amt: impl UiNum) -> Self { let amt = Px::from_num(amt); Self { top: amt, bottom: amt, ..Self::ZERO } } pub fn top(amt: impl UiNum) -> Self { let mut s = Self::ZERO; s.top = Px::from_num(amt); s } pub fn bottom(amt: impl UiNum) -> Self { let mut s = Self::ZERO; s.bottom = Px::from_num(amt); s } pub fn left(amt: impl UiNum) -> Self { let mut s = Self::ZERO; s.left = Px::from_num(amt); s } pub fn right(amt: impl UiNum) -> Self { let mut s = Self::ZERO; s.right = Px::from_num(amt); s } pub fn with_top(mut self, amt: impl UiNum) -> Self { self.top = Px::from_num(amt); self } pub fn with_bottom(mut self, amt: impl UiNum) -> Self { self.bottom = Px::from_num(amt); self } pub fn with_left(mut self, amt: impl UiNum) -> Self { self.left = Px::from_num(amt); self } pub fn with_right(mut self, amt: impl UiNum) -> Self { self.right = Px::from_num(amt); self } } impl From for Padding { fn from(amt: T) -> Self { Self::uniform(amt) } }