Dialogs: share the buttons-first layout Settings uses

Settings keeps its buttons in view by laying them out first, at the
bottom of its body, and giving its pages the height left above them,
clipped to it. That layout is now dialogs::above_footer, so other
dialogs can keep their buttons in view the same way. Settings looks and
works as before.
This commit is contained in:
tylerjenningsw committed 2026-10-12 21:24:41 -04:00
1 parent 470ea7a3af
commit bcfb25ca93
2 files changed
+40 -30

No files matched your search

+16
View File
@@ -192,6 +192,22 @@ pub(crate) fn modal_fit(ctx: &egui::Context, title: &str, size: egui::Vec2, t: &
});
}
/// The buttons of a [`modal_fit`] body of `height`, kept in view: `footer` lays them out first,
/// at the bottom (bottom up: its first row is the lowest), and the rest of the body gets the room
/// above them. Returns that room (a left-to-right row, clipped to it, so a long or wide body
/// scrolls or clips under the buttons instead of pushing them out of the dialog) and its height.
pub(crate) fn above_footer(ui: &mut egui::Ui, height: f32, footer: impl FnOnce(&mut egui::Ui)) -> (egui::Ui, f32) {
let area = ui.allocate_space(vec2(ui.available_width(), height)).1;
let mut foot = ui.new_child(egui::UiBuilder::new().max_rect(area).layout(egui::Layout::bottom_up(egui::Align::Min)));
footer(&mut foot);
foot.add_space(8.0);
let body_h = (foot.min_rect().top() - area.top()).max(0.0);
let room = Rect::from_min_size(area.min, vec2(area.width(), body_h));
let mut body = ui.new_child(egui::UiBuilder::new().max_rect(room).layout(egui::Layout::left_to_right(egui::Align::Min)));
body.set_clip_rect(room.intersect(body.clip_rect()));
(body, body_h)
}
pub fn show(app: &mut EffectcraftApp, ctx: &egui::Context) {
let Some(d) = app.dialog else { return };
let t = app.tokens;
+24 -30
View File
@@ -10,7 +10,7 @@
use crate::i18n::{tr, tr_args};
use effectcraft_engine::prefs::{APPEARANCE_MODES, DARK_THEMES, Item, Kind, LIGHT_THEMES, Page, pages};
use effectcraft_engine::segment::Task;
use egui::{Color32, Rect, RichText, vec2};
use egui::{Color32, RichText, vec2};
use serde_json::{Value, json};
use crate::theme::{ThemeKind, Tokens};
@@ -633,37 +633,31 @@ pub fn show(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
let mut acts: Vec<Act> = vec![];
let (mut ok, mut cancel) = (false, false);
super::dialogs::modal_fit(ctx, "Settings", vec2(820.0, 600.0), t, |ui, height| {
// The buttons go first, from the bottom up, and the pages get the height they leave: at
// a high UI Scale the pages scroll and the buttons stay in view.
let area = ui.allocate_space(vec2(ui.available_width(), height)).1;
let mut foot = ui.new_child(egui::UiBuilder::new().max_rect(area).layout(egui::Layout::bottom_up(egui::Align::Min)));
foot.horizontal(|ui| {
let r = ui.add_enabled(idx > 0, egui::Button::new(tr("Previous")));
app.auto.add("settings.previous", r.rect, "Previous");
if r.clicked() {
page = all[idx - 1].id.to_string();
}
let r = ui.add_enabled(idx + 1 < all.len(), egui::Button::new(tr("Next")));
app.auto.add("settings.next", r.rect, "Next");
if r.clicked() {
page = all[idx + 1].id.to_string();
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let r = ui.add(egui::Button::new(RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent));
app.auto.add("settings.ok", r.rect, "OK");
ok |= r.clicked();
let r = ui.button(tr("Cancel"));
app.auto.add("settings.cancel", r.rect, "Cancel");
cancel |= r.clicked();
// The buttons go first and the pages get the height they leave: at a high UI Scale the
// pages scroll and the buttons stay in view.
let (mut body, body_h) = super::dialogs::above_footer(ui, height, |foot| {
foot.horizontal(|ui| {
let r = ui.add_enabled(idx > 0, egui::Button::new(tr("Previous")));
app.auto.add("settings.previous", r.rect, "Previous");
if r.clicked() {
page = all[idx - 1].id.to_string();
}
let r = ui.add_enabled(idx + 1 < all.len(), egui::Button::new(tr("Next")));
app.auto.add("settings.next", r.rect, "Next");
if r.clicked() {
page = all[idx + 1].id.to_string();
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let r = ui.add(egui::Button::new(RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent));
app.auto.add("settings.ok", r.rect, "OK");
ok |= r.clicked();
let r = ui.button(tr("Cancel"));
app.auto.add("settings.cancel", r.rect, "Cancel");
cancel |= r.clicked();
});
});
foot.separator();
});
foot.separator();
foot.add_space(8.0);
let body_h = (foot.min_rect().top() - area.top()).max(0.0);
// (A child, clipped to its share, so a wide page can't widen the dialog under the buttons.)
let pages = Rect::from_min_size(area.min, vec2(area.width(), body_h));
let mut body = ui.new_child(egui::UiBuilder::new().max_rect(pages).layout(egui::Layout::left_to_right(egui::Align::Min)));
body.set_clip_rect(pages.intersect(body.clip_rect()));
{
let ui = &mut body;
// Page list (left, like After Effects).