radiance_egui/containers/
sides.rs1use emath::{Align, NumExt as _};
2
3use crate::{Layout, Ui, UiBuilder};
4
5#[must_use = "You should call sides.show()"]
44#[derive(Clone, Copy, Debug, Default)]
45pub struct Sides {
46 height: Option<f32>,
47 spacing: Option<f32>,
48 kind: SidesKind,
49 wrap_mode: Option<crate::TextWrapMode>,
50}
51
52#[derive(Clone, Copy, Debug, Default)]
53enum SidesKind {
54 #[default]
55 Extend,
56 ShrinkLeft,
57 ShrinkRight,
58}
59
60impl Sides {
61 #[inline]
62 pub fn new() -> Self {
63 Default::default()
64 }
65
66 #[inline]
71 pub fn height(mut self, height: f32) -> Self {
72 self.height = Some(height);
73 self
74 }
75
76 #[inline]
81 pub fn spacing(mut self, spacing: f32) -> Self {
82 self.spacing = Some(spacing);
83 self
84 }
85
86 #[inline]
91 pub fn shrink_left(mut self) -> Self {
92 self.kind = SidesKind::ShrinkLeft;
93 self
94 }
95
96 #[inline]
101 pub fn shrink_right(mut self) -> Self {
102 self.kind = SidesKind::ShrinkRight;
103 self
104 }
105
106 #[inline]
111 pub fn extend(mut self) -> Self {
112 self.kind = SidesKind::Extend;
113 self
114 }
115
116 #[inline]
120 pub fn wrap_mode(mut self, wrap_mode: crate::TextWrapMode) -> Self {
121 self.wrap_mode = Some(wrap_mode);
122 self
123 }
124
125 #[inline]
130 pub fn truncate(mut self) -> Self {
131 self.wrap_mode = Some(crate::TextWrapMode::Truncate);
132 self
133 }
134
135 #[inline]
140 pub fn wrap(mut self) -> Self {
141 self.wrap_mode = Some(crate::TextWrapMode::Wrap);
142 self
143 }
144
145 pub fn show<RetL, RetR>(
146 self,
147 ui: &mut Ui,
148 add_left: impl FnOnce(&mut Ui) -> RetL,
149 add_right: impl FnOnce(&mut Ui) -> RetR,
150 ) -> (RetL, RetR) {
151 let Self {
152 height,
153 spacing,
154 mut kind,
155 mut wrap_mode,
156 } = self;
157 let height = height.unwrap_or_else(|| ui.spacing().interact_size.y);
158 let spacing = spacing.unwrap_or_else(|| ui.spacing().item_spacing.x);
159
160 let mut top_rect = ui.available_rect_before_wrap();
161 top_rect.max.y = top_rect.min.y + height;
162
163 if ui.is_sizing_pass() {
164 kind = SidesKind::Extend;
165 wrap_mode = None;
166 }
167
168 match kind {
169 SidesKind::ShrinkLeft => {
170 let (right_rect, result_right) = Self::create_ui(
171 ui,
172 top_rect,
173 Layout::right_to_left(Align::Center),
174 add_right,
175 None,
176 );
177 let available_width = top_rect.width() - right_rect.width() - spacing;
178 let left_rect_constraint =
179 top_rect.with_max_x(top_rect.min.x + available_width.at_least(0.0));
180 let (left_rect, result_left) = Self::create_ui(
181 ui,
182 left_rect_constraint,
183 Layout::left_to_right(Align::Center),
184 add_left,
185 wrap_mode,
186 );
187
188 ui.advance_cursor_after_rect(left_rect | right_rect);
189 (result_left, result_right)
190 }
191 SidesKind::ShrinkRight => {
192 let (left_rect, result_left) = Self::create_ui(
193 ui,
194 top_rect,
195 Layout::left_to_right(Align::Center),
196 add_left,
197 None,
198 );
199 let right_rect_constraint = top_rect.with_min_x(left_rect.max.x + spacing);
200 let (right_rect, result_right) = Self::create_ui(
201 ui,
202 right_rect_constraint,
203 Layout::right_to_left(Align::Center),
204 add_right,
205 wrap_mode,
206 );
207
208 ui.advance_cursor_after_rect(left_rect | right_rect);
209 (result_left, result_right)
210 }
211 SidesKind::Extend => {
212 let (left_rect, result_left) = Self::create_ui(
213 ui,
214 top_rect,
215 Layout::left_to_right(Align::Center),
216 add_left,
217 None,
218 );
219 let right_max_rect = top_rect.with_min_x(left_rect.max.x);
220 let (right_rect, result_right) = Self::create_ui(
221 ui,
222 right_max_rect,
223 Layout::right_to_left(Align::Center),
224 add_right,
225 None,
226 );
227
228 let mut final_rect = left_rect | right_rect;
229 let min_width = left_rect.width() + spacing + right_rect.width();
230
231 if ui.is_sizing_pass() {
232 final_rect.max.x = left_rect.min.x + min_width;
233 } else {
234 final_rect.max.x = final_rect.max.x.max(left_rect.min.x + min_width);
235 }
236
237 ui.advance_cursor_after_rect(final_rect);
238 (result_left, result_right)
239 }
240 }
241 }
242
243 fn create_ui<Ret>(
244 ui: &mut Ui,
245 max_rect: emath::Rect,
246 layout: Layout,
247 add_content: impl FnOnce(&mut Ui) -> Ret,
248 wrap_mode: Option<crate::TextWrapMode>,
249 ) -> (emath::Rect, Ret) {
250 let mut child_ui = ui.new_child(UiBuilder::new().max_rect(max_rect).layout(layout));
251 if let Some(wrap_mode) = wrap_mode {
252 child_ui.style_mut().wrap_mode = Some(wrap_mode);
253 }
254 let result = add_content(&mut child_ui);
255 (child_ui.min_rect(), result)
256 }
257}