Skip to main content

agg_gui/widgets/
spacers.rs

1//! Spacing primitives: `Spacer` (flex filler) and `Separator` (divider line).
2//!
3//! Split out of `primitives.rs` so each widget module stays under the project's
4//! 800-line file-size limit; behaviour is unchanged.
5
6use crate::color::Color;
7use crate::draw_ctx::DrawCtx;
8use crate::event::{Event, EventResult};
9use crate::geometry::{Rect, Size};
10use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
11use crate::widget::Widget;
12
13// ---------------------------------------------------------------------------
14// Spacer — flexible empty space for use in flex layouts
15// ---------------------------------------------------------------------------
16
17/// An invisible leaf widget that expands to fill available space.
18///
19/// Used as a `flex` child in [`FlexColumn`][crate::FlexColumn] or
20/// [`FlexRow`][crate::FlexRow] to push siblings apart.
21pub struct Spacer {
22    bounds: Rect,
23    children: Vec<Box<dyn Widget>>,
24    base: WidgetBase,
25}
26
27impl Spacer {
28    pub fn new() -> Self {
29        Self {
30            bounds: Rect::default(),
31            children: Vec::new(),
32            base: WidgetBase::new(),
33        }
34    }
35
36    pub fn with_margin(mut self, m: Insets) -> Self {
37        self.base.margin = m;
38        self
39    }
40    pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
41        self.base.h_anchor = h;
42        self
43    }
44    pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
45        self.base.v_anchor = v;
46        self
47    }
48    pub fn with_min_size(mut self, s: Size) -> Self {
49        self.base.min_size = s;
50        self
51    }
52    pub fn with_max_size(mut self, s: Size) -> Self {
53        self.base.max_size = s;
54        self
55    }
56}
57
58impl Default for Spacer {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64impl Widget for Spacer {
65    fn type_name(&self) -> &'static str {
66        "Spacer"
67    }
68    fn bounds(&self) -> Rect {
69        self.bounds
70    }
71    fn set_bounds(&mut self, b: Rect) {
72        self.bounds = b;
73    }
74    fn children(&self) -> &[Box<dyn Widget>] {
75        &self.children
76    }
77    fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
78        &mut self.children
79    }
80
81    fn margin(&self) -> Insets {
82        self.base.margin
83    }
84    fn widget_base(&self) -> Option<&WidgetBase> {
85        Some(&self.base)
86    }
87    fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
88        Some(&mut self.base)
89    }
90    fn h_anchor(&self) -> HAnchor {
91        self.base.h_anchor
92    }
93    fn v_anchor(&self) -> VAnchor {
94        self.base.v_anchor
95    }
96    fn min_size(&self) -> Size {
97        self.base.min_size
98    }
99    fn max_size(&self) -> Size {
100        self.base.max_size
101    }
102
103    fn layout(&mut self, available: Size) -> Size {
104        available
105    }
106
107    fn paint(&mut self, _ctx: &mut dyn DrawCtx) {}
108
109    fn on_event(&mut self, _: &Event) -> EventResult {
110        EventResult::Ignored
111    }
112}
113
114// ---------------------------------------------------------------------------
115// Separator — a thin horizontal or vertical divider line
116// ---------------------------------------------------------------------------
117
118/// A thin horizontal or vertical divider line.
119///
120/// When no explicit colour is set via [`with_color`](Separator::with_color),
121/// the separator reads its colour from the active theme's `separator` field at
122/// paint time, so it automatically adapts to dark / light mode.
123pub struct Separator {
124    bounds: Rect,
125    children: Vec<Box<dyn Widget>>,
126    base: WidgetBase,
127    vertical: bool,
128    line_inset: f64,
129    /// `None` → use `ctx.visuals().separator` at paint time.
130    color: Option<Color>,
131}
132
133impl Separator {
134    /// Create a horizontal separator (the common case).
135    pub fn horizontal() -> Self {
136        Self {
137            bounds: Rect::default(),
138            children: Vec::new(),
139            base: WidgetBase::new(),
140            vertical: false,
141            line_inset: 4.0,
142            color: None,
143        }
144    }
145
146    /// Create a vertical separator.
147    pub fn vertical() -> Self {
148        Self {
149            vertical: true,
150            ..Self::horizontal()
151        }
152    }
153
154    pub fn with_line_inset(mut self, m: f64) -> Self {
155        self.line_inset = m;
156        self
157    }
158    pub fn with_color(mut self, c: Color) -> Self {
159        self.color = Some(c);
160        self
161    }
162
163    pub fn with_margin(mut self, m: Insets) -> Self {
164        self.base.margin = m;
165        self
166    }
167    pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
168        self.base.h_anchor = h;
169        self
170    }
171    pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
172        self.base.v_anchor = v;
173        self
174    }
175    pub fn with_min_size(mut self, s: Size) -> Self {
176        self.base.min_size = s;
177        self
178    }
179    pub fn with_max_size(mut self, s: Size) -> Self {
180        self.base.max_size = s;
181        self
182    }
183}
184
185impl Widget for Separator {
186    fn type_name(&self) -> &'static str {
187        "Separator"
188    }
189    fn bounds(&self) -> Rect {
190        self.bounds
191    }
192    fn set_bounds(&mut self, b: Rect) {
193        self.bounds = b;
194    }
195    fn children(&self) -> &[Box<dyn Widget>] {
196        &self.children
197    }
198    fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>> {
199        &mut self.children
200    }
201
202    fn margin(&self) -> Insets {
203        self.base.margin
204    }
205    fn widget_base(&self) -> Option<&WidgetBase> {
206        Some(&self.base)
207    }
208    fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
209        Some(&mut self.base)
210    }
211    fn h_anchor(&self) -> HAnchor {
212        self.base.h_anchor
213    }
214    fn v_anchor(&self) -> VAnchor {
215        self.base.v_anchor
216    }
217    fn min_size(&self) -> Size {
218        self.base.min_size
219    }
220    fn max_size(&self) -> Size {
221        self.base.max_size
222    }
223
224    fn layout(&mut self, available: Size) -> Size {
225        if self.vertical {
226            Size::new(1.0 + self.line_inset * 2.0, available.height)
227        } else {
228            Size::new(available.width, 1.0 + self.line_inset * 2.0)
229        }
230    }
231
232    fn paint(&mut self, ctx: &mut dyn DrawCtx) {
233        let w = self.bounds.width;
234        let h = self.bounds.height;
235        let color = self.color.unwrap_or_else(|| ctx.visuals().separator);
236        ctx.set_fill_color(color);
237        ctx.begin_path();
238        if self.vertical {
239            ctx.rect(self.line_inset, 0.0, 1.0, h);
240        } else {
241            ctx.rect(0.0, self.line_inset, w, 1.0);
242        }
243        ctx.fill();
244    }
245
246    fn on_event(&mut self, _: &Event) -> EventResult {
247        EventResult::Ignored
248    }
249}