Skip to main content

agg_gui/widgets/window/
builder.rs

1//! Builder methods for [`super::Window`] — extracted into a submodule so
2//! `window.rs` stays under the 800-line guardrail. These are all plain
3//! setter / configuration calls that mutate `self` and return it; they
4//! live in a sibling `impl Window` block that has full access to private
5//! fields by virtue of being inside the parent `window` module.
6
7use std::cell::Cell;
8use std::rc::Rc;
9
10use crate::geometry::{Rect, Size};
11use crate::layout_props::{HAnchor, Insets, VAnchor};
12
13use super::{Window, VISIBILITY_FADE_SECS};
14
15impl Window {
16    /// Treat the window's content as live: invalidate the backbuffer
17    /// every frame so custom paint code reading from a mutating data
18    /// source (network feed, simulation state, sensor stream) is
19    /// rasterised fresh.  Automatically skipped when the window is
20    /// collapsed or hidden — no wasted work when the user can't see
21    /// the content.
22    ///
23    /// Use this for diagnostics graphs, telemetry views, or any widget
24    /// whose `paint()` reads from an `Rc<RefCell<…>>` model that the
25    /// framework can't observe.  The alternative — composing live UI
26    /// from widgets that auto-invalidate on setter calls
27    /// ([`Label::set_text`](crate::widgets::Label), etc.) — is preferred
28    /// when feasible, but for custom direct-to-`DrawCtx` widgets this
29    /// is the simplest correct fix.
30    ///
31    /// See [`Window::new`] for the full discussion of the back-buffer
32    /// invalidation contract and the canonical "stale pixels" gotcha
33    /// this flag solves.
34    pub fn with_live_content(mut self, live: bool) -> Self {
35        self.live_content = live;
36        self
37    }
38
39    /// Register a callback fired whenever this window requests a raise
40    /// (click-to-front or visibility rising-edge from the sidebar).
41    /// Receives the window title.  The demo uses this to feed a shared
42    /// z-order tracker that gets persisted to disk.
43    pub fn on_raised(mut self, cb: impl FnMut(&str) + 'static) -> Self {
44        self.on_raised = Some(Box::new(cb));
45        self
46    }
47
48    pub fn with_bounds(mut self, b: Rect) -> Self {
49        self.pre_collapse_h = b.height;
50        self.bounds = b;
51        if self.maximized {
52            self.pre_maximize_bounds = b;
53        }
54        self
55    }
56    pub fn with_font_size(mut self, size: f64) -> Self {
57        self.font_size = size;
58        self
59    }
60
61    pub fn with_visible_cell(mut self, cell: Rc<Cell<bool>>) -> Self {
62        let visible = cell.get();
63        self.last_visible.set(visible);
64        self.fade_out_active.set(false);
65        self.visibility_anim =
66            crate::animation::Tween::new(if visible { 1.0 } else { 0.0 }, VISIBILITY_FADE_SECS);
67        self.visible_cell = Some(cell);
68        self
69    }
70
71    pub fn with_reset_cell(mut self, cell: Rc<Cell<Option<Rect>>>) -> Self {
72        self.reset_to = Some(cell);
73        self
74    }
75
76    pub fn with_position_cell(mut self, cell: Rc<Cell<Rect>>) -> Self {
77        self.position_cell = Some(cell);
78        self
79    }
80
81    /// Wire the window's canvas-maximized state into external persistence.
82    ///
83    /// Call after [`with_bounds`] when restoring saved state so the current
84    /// bounds become the pre-maximize bounds used by the first layout pass.
85    pub fn with_maximized_cell(mut self, cell: Rc<Cell<bool>>) -> Self {
86        self.maximized = cell.get();
87        if self.maximized {
88            self.pre_maximize_bounds = self.bounds;
89        }
90        self.maximized_cell = Some(cell);
91        self
92    }
93
94    pub fn with_margin(mut self, m: Insets) -> Self {
95        self.base.margin = m;
96        self
97    }
98    pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
99        self.base.h_anchor = h;
100        self
101    }
102    pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
103        self.base.v_anchor = v;
104        self
105    }
106    pub fn with_min_size(mut self, s: Size) -> Self {
107        self.base.min_size = s;
108        self
109    }
110    pub fn with_max_size(mut self, s: Size) -> Self {
111        self.base.max_size = s;
112        self
113    }
114
115    pub fn with_constrain(mut self, constrain: bool) -> Self {
116        self.constrain = constrain;
117        self
118    }
119
120    /// Opt this window in/out of the generic retained GL-FBO backbuffer.
121    /// Disabling renders directly into the inherited parent target.
122    pub fn with_gl_backbuffer(mut self, enabled: bool) -> Self {
123        self.use_gl_backbuffer = enabled;
124        self.backbuffer.invalidate();
125        self
126    }
127
128    /// Make the window size itself to the content's preferred size every frame.
129    /// Top-left pin: as content grows/shrinks, the title bar stays where it is.
130    pub fn with_auto_size(mut self, auto: bool) -> Self {
131        self.auto_size = auto;
132        self
133    }
134
135    /// Toggle user-dragged resize.  `false` hides every edge/corner handle
136    /// and disables resize hit-tests.  Default: `true`.  Matches egui's
137    /// `Window::resizable(bool)`.
138    pub fn with_resizable(mut self, on: bool) -> Self {
139        self.resizable = on;
140        self
141    }
142
143    /// Fine-grained axis-locking of the resize handles — pass `(true, false)`
144    /// for a horizontally-only resizable window, etc.  Implies
145    /// `with_resizable(true)`.  Matches egui's `Window::resizable([h, v])`.
146    pub fn with_resizable_axes(mut self, h: bool, v: bool) -> Self {
147        self.resizable = h || v;
148        self.resizable_h = h;
149        self.resizable_v = v;
150        self
151    }
152
153    /// Lock the window's height to its content's required height.
154    /// The user can grab a vertical resize handle but the next
155    /// layout snaps back — egui's W4 "no scroll, no clip, no
156    /// whitespace" contract.  Requires the content tree to expose
157    /// its required height via [`Widget::measure_min_height`]; our
158    /// `FlexColumn`, `Label`, `TextArea`, and `Container::with_fit_height`
159    /// all do.
160    pub fn with_tight_content_fit(mut self, on: bool) -> Self {
161        self.tight_content_fit = on;
162        self
163    }
164
165    /// Floor-only variant of [`with_tight_content_fit`]: refuses to
166    /// shrink past content but allows the user to pull the window
167    /// taller (whitespace below).  Used for windows whose content
168    /// includes a flex-fill child like a multiline `TextArea` —
169    /// matches egui's W5 where the TextEdit fills extra height and
170    /// the user can grow the window further.
171    pub fn with_height_floor_to_content(mut self, on: bool) -> Self {
172        self.floor_content_height = on;
173        self
174    }
175
176    /// Wrap the window's content in a built-in vertical [`ScrollView`].
177    /// Matches egui's `Window::vscroll(true)`: lets the user shrink the
178    /// window below content height without the caller having to wrap the
179    /// content in a `ScrollView` manually.  Eager — happens at builder
180    /// time so the rest of the layout / event / paint paths see a single
181    /// child as usual.  Has no effect when called with `false` (matches
182    /// the default).
183    ///
184    /// Don't combine with [`with_auto_size`]: the ScrollView claims its
185    /// full available height, which would make auto-sizing grow the
186    /// window to the canvas.  egui's demo never combines the two flags
187    /// either.
188    pub fn with_vscroll(mut self, vscroll: bool) -> Self {
189        if vscroll {
190            if let Some(content) = self.children.pop() {
191                let scroll = crate::widgets::ScrollView::new(content)
192                    .vertical(true)
193                    .horizontal(false);
194                self.children.push(Box::new(scroll));
195            }
196        }
197        self
198    }
199
200    pub fn on_close(mut self, cb: impl FnMut() + 'static) -> Self {
201        self.on_close = Some(Box::new(cb));
202        self
203    }
204}