Skip to main content

agg_gui/widgets/
reserve_inset.rs

1//! ReserveInset — transparent wrapper that marks its child as edge chrome.
2//!
3//! Wrap an edge-hugging overlay (a left button rail, a bottom control
4//! tray) in this and the strip the child ends up occupying is
5//! automatically registered with [`crate::overlay_insets`] every frame.
6//! Anchored floating content ([`crate::card`], tooltips) then stays out
7//! from underneath it with zero coordination from the app — which is the
8//! point: a developer shouldn't have to remember every sibling overlay to
9//! place a card correctly.
10//!
11//! The wrapper is layout-transparent: it forwards size, anchors, margin,
12//! and visibility from the child unchanged, so `.with_h_anchor(...)` etc.
13//! configured on the child keep working when it's used as a `Stack`
14//! overlay. The reservation happens in [`Widget::set_bounds`] — the one
15//! call made exactly when the parent has decided the final on-screen
16//! strip — using [`crate::widget::current_viewport`] for the far-edge
17//! distances. Use it for direct overlay children of the root `Stack`,
18//! whose bounds are viewport-relative.
19
20use crate::draw_ctx::DrawCtx;
21use crate::event::{Event, EventResult};
22use crate::geometry::{Rect, Size};
23use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
24use crate::widget::{current_viewport, Widget};
25
26/// Which viewport edge the wrapped chrome hugs.
27#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28pub enum ReservedEdge {
29    Left,
30    Right,
31    Top,
32    Bottom,
33}
34
35/// See module docs. Construct via [`ReserveInset::left`] etc. and use in
36/// place of the bare child.
37pub struct ReserveInset {
38    bounds: Rect,
39    children: Vec<Box<dyn Widget>>, // exactly 1
40    edge: ReservedEdge,
41}
42
43impl ReserveInset {
44    pub fn new(edge: ReservedEdge, child: Box<dyn Widget>) -> Self {
45        Self {
46            bounds: Rect::default(),
47            children: vec![child],
48            edge,
49        }
50    }
51    pub fn left(child: Box<dyn Widget>) -> Self {
52        Self::new(ReservedEdge::Left, child)
53    }
54    pub fn right(child: Box<dyn Widget>) -> Self {
55        Self::new(ReservedEdge::Right, child)
56    }
57    pub fn top(child: Box<dyn Widget>) -> Self {
58        Self::new(ReservedEdge::Top, child)
59    }
60    pub fn bottom(child: Box<dyn Widget>) -> Self {
61        Self::new(ReservedEdge::Bottom, child)
62    }
63
64    fn child(&self) -> &dyn Widget {
65        self.children[0].as_ref()
66    }
67}
68
69impl Widget for ReserveInset {
70    fn type_name(&self) -> &'static str {
71        "ReserveInset"
72    }
73    fn bounds(&self) -> Rect {
74        self.bounds
75    }
76
77    fn set_bounds(&mut self, b: Rect) {
78        self.bounds = b;
79        if let Some(child) = self.children.first_mut() {
80            child.set_bounds(Rect::new(0.0, 0.0, b.width, b.height));
81        }
82        if !self.is_visible() || b.width <= 0.0 || b.height <= 0.0 {
83            return;
84        }
85        // Reserve the strip between the viewport edge and the child's far
86        // side, so a rail floating a few px off the edge still shadows
87        // everything left of it (Y-up: `y` measures from the bottom).
88        let viewport = current_viewport();
89        let strip = match self.edge {
90            ReservedEdge::Left => Insets {
91                left: b.x + b.width,
92                ..Insets::default()
93            },
94            ReservedEdge::Right => Insets {
95                right: (viewport.width - b.x).max(0.0),
96                ..Insets::default()
97            },
98            ReservedEdge::Bottom => Insets {
99                bottom: b.y + b.height,
100                ..Insets::default()
101            },
102            ReservedEdge::Top => Insets {
103                top: (viewport.height - b.y).max(0.0),
104                ..Insets::default()
105            },
106        };
107        crate::overlay_insets::reserve(strip);
108    }
109
110    fn children(&self) -> &[Box<dyn Widget>] {
111        &self.children
112    }
113    fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
114        &mut self.children
115    }
116
117    // Layout-transparent: the child's own sizing and placement properties
118    // drive the parent container exactly as if it were unwrapped.
119    fn margin(&self) -> Insets {
120        self.child().margin()
121    }
122    fn h_anchor(&self) -> HAnchor {
123        self.child().h_anchor()
124    }
125    fn v_anchor(&self) -> VAnchor {
126        self.child().v_anchor()
127    }
128    fn min_size(&self) -> Size {
129        self.child().min_size()
130    }
131    fn max_size(&self) -> Size {
132        self.child().max_size()
133    }
134    fn widget_base(&self) -> Option<&WidgetBase> {
135        self.children[0].widget_base()
136    }
137    fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
138        self.children[0].widget_base_mut()
139    }
140    fn is_visible(&self) -> bool {
141        self.child().is_visible()
142    }
143    fn measure_min_height(&self, available_w: f64) -> f64 {
144        self.child().measure_min_height(available_w)
145    }
146
147    fn layout(&mut self, available: Size) -> Size {
148        self.children[0].layout(available)
149    }
150
151    fn paint(&mut self, _ctx: &mut dyn DrawCtx) {}
152
153    fn on_event(&mut self, _e: &Event) -> EventResult {
154        EventResult::Ignored
155    }
156}