Replies: 1 comment 3 replies
-
This is the difficult layout problem that is being discussed in #4378. It's a fundamental problem of immediate UIs, because you only know how much space you needed after the frame is already drawn, and you can't go back and move things. Currently the simplest and most common workaround is to measure how much space the UI took and allocate that much space in the next frame: Details
struct MyApp {
value: f64,
}
impl Default for MyApp {
fn default() -> Self {
Self { value: 42.0 }
}
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.add(egui::DragValue::new(&mut self.value));
ui.group(|ui| {
ui.style_mut().wrap = Some(false);
let max_size = ui
.data(|data| data.get_temp("max-size".into()))
.unwrap_or(Vec2::new(400.0, 300.0));
let new_size = ui
.allocate_ui_with_layout(
max_size,
egui::Layout::top_down(egui::Align::RIGHT).with_cross_justify(false),
|ui| {
ui.label(egui::RichText::new(format!("{:.4}", self.value)).size(60.0));
ui.label(egui::RichText::new(format!("{}", "VDC")).size(20.0));
},
)
.response
.rect
.size();
ui.data_mut(|data| data.insert_temp("max-size".into(), new_size));
});
});
}
} This will work for any UI you put in the frame, but it will have a 1-frame delay, so you might see some glitches when the size changes. Screencast.from.2024-05-07.09-53-03.webmA more involved solution that avoids the frame delay is to calculate the sizes beforehand. The drawback is that the code is more complicated (and slower, since it is doing the work twice), and you will have to update it if you add/change widgets. In this case, it could be done like this: Details
struct MyApp {
value: f64,
}
impl Default for MyApp {
fn default() -> Self {
Self { value: 42.0 }
}
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.add(egui::DragValue::new(&mut self.value));
ui.group(|ui| {
ui.style_mut().wrap = Some(false);
let text_elements = [
egui::RichText::new(format!("{:.4}", self.value)).size(60.0),
egui::RichText::new(format!("{}", "VDC")).size(20.0),
]
.map(egui::WidgetText::from);
let sizes: Vec<_> = text_elements
.iter()
.map(|t| {
let fallback = egui::TextStyle::Body.resolve(ui.style());
t.clone()
.into_galley(ui, None, f32::INFINITY, fallback)
.size()
})
.collect();
let max_size = sizes
.into_iter()
.fold(Vec2::new(0.0, 0.0), |mut total, next| {
total.x = total.x.max(next.x);
total.y += next.y;
total
});
ui.allocate_ui_with_layout(
max_size,
egui::Layout::top_down(egui::Align::RIGHT).with_cross_justify(false),
|ui| {
for t in text_elements {
ui.label(t);
}
},
);
});
});
}
} Screencast.from.2024-05-07.09-50-31.webm |
Beta Was this translation helpful? Give feedback.
-
Consider this frame code:
It looks like this:
That is because I was able to at least find allocate_ui_with_layout where I can put in a hard limit.
What I actually want: within in the frame, I want to right-align my labels. The labels have formatting on them so they are the determining factor (based on selected font size etc which can be set by the user later on). Without layout, that works beautifully. The frame is exactly as wide as the labels need it to be.
Once I add the layout, the frame streches across the whole windoww horizontally. Not wwhat I want at all.
I found the with_cross_jutsify option, thinking I could tell the layout not to expand beyond the needed size. It does absolutely nothing.
So now my question: How to accomplish the seemingly simple taks to tell a layout to not exceed the needed size of it's child contents?
I urgently want to get rid of this 400x300 hack.
Beta Was this translation helpful? Give feedback.
All reactions