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}