Skip to main content

rosace_widgets/tree/
positioned.rs

1use rosace_core::types::{Point, Rect, Size};
2use rosace_layout::Constraints;
3use super::{Widget, Children, LayoutCtx, PaintCtx, BoxedWidget};
4
5/// Absolutely places a child inside a [`Stack`](super::stack::Stack) using
6/// edge anchors. Receives the full stack rect and positions its child from
7/// the given top/left/right/bottom (+ optional explicit width/height).
8///
9/// A child with no anchors fills the stack (the default Stack behavior).
10pub struct Positioned {
11    child: BoxedWidget,
12    top: Option<f32>, left: Option<f32>, right: Option<f32>, bottom: Option<f32>,
13    width: Option<f32>, height: Option<f32>,
14}
15
16impl Positioned {
17    pub fn new(child: impl Widget + 'static) -> Self {
18        Self { child: Box::new(child), top: None, left: None, right: None, bottom: None, width: None, height: None }
19    }
20    pub fn top(mut self, v: f32) -> Self { self.top = Some(v); self }
21    pub fn left(mut self, v: f32) -> Self { self.left = Some(v); self }
22    pub fn right(mut self, v: f32) -> Self { self.right = Some(v); self }
23    pub fn bottom(mut self, v: f32) -> Self { self.bottom = Some(v); self }
24    pub fn width(mut self, v: f32) -> Self { self.width = Some(v); self }
25    pub fn height(mut self, v: f32) -> Self { self.height = Some(v); self }
26}
27
28impl Widget for Positioned {
29    fn children(&self) -> Children<'_> { Children::One(&*self.child) }
30
31    fn layout(&self, ctx: &LayoutCtx) -> Size {
32        // Fills the stack; the Stack sizes itself from non-positioned children.
33        self.child.layout(ctx)
34    }
35
36    fn paint(&self, ctx: &mut PaintCtx) {
37        let s = ctx.rect; // full stack rect
38        // Resolve size: explicit, else derived from opposite anchors, else measured.
39        let measured = self.child.layout(&ctx.layout_ctx(Constraints::loose(s.size.width, s.size.height)));
40        let w = self.width.or_else(|| match (self.left, self.right) {
41            (Some(l), Some(r)) => Some((s.size.width - l - r).max(0.0)),
42            _ => None,
43        }).unwrap_or(measured.width);
44        let h = self.height.or_else(|| match (self.top, self.bottom) {
45            (Some(t), Some(b)) => Some((s.size.height - t - b).max(0.0)),
46            _ => None,
47        }).unwrap_or(measured.height);
48
49        let x = match (self.left, self.right) {
50            (Some(l), _) => s.origin.x + l,
51            (None, Some(r)) => s.origin.x + s.size.width - r - w,
52            (None, None) => s.origin.x,
53        };
54        let y = match (self.top, self.bottom) {
55            (Some(t), _) => s.origin.y + t,
56            (None, Some(b)) => s.origin.y + s.size.height - b - h,
57            (None, None) => s.origin.y,
58        };
59        let rect = Rect { origin: Point { x, y }, size: Size { width: w, height: h } };
60        self.child.paint(&mut ctx.child(rect));
61    }
62}