Merge pull request #23 from storytold/pr/ui-dialog-menu-sizing
UI: dialogs and menus size to their content
This commit is contained in:
commit
25c8f32fdd
|
|
@ -195,7 +195,8 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
|||
});
|
||||
let heading = (spec.heading)(&d);
|
||||
egui::Window::new(heading.as_str())
|
||||
.id(egui::Id::new("dialog"))
|
||||
// One window per kind, so a dialog never inherits another dialog's size.
|
||||
.id(egui::Id::new(("dialog", d.kind.as_str())))
|
||||
.order(egui::Order::Foreground)
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
|
|
@ -211,7 +212,10 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
|||
ui.add_space(12.0);
|
||||
cancel = (spec.body)(app, ui, &mut d);
|
||||
ui.add_space(16.0);
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
// The button row is as tall as the buttons: a right-to-left layout would otherwise take
|
||||
// all the height left in the window, so the window could never shrink to its content.
|
||||
let row = egui::vec2(ui.available_width(), ui.spacing().interact_size.y);
|
||||
ui.allocate_ui_with_layout(row, egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if let Some(label) = spec.ok
|
||||
&& widgets::primary_button(ui, label).clicked()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ fn app() -> VectorcraftApp {
|
|||
fn frame(app: &mut VectorcraftApp, input: egui::RawInput) {
|
||||
let ctx = egui::Context::default();
|
||||
theme::install_fonts(&ctx);
|
||||
theme::apply(&ctx, Default::default());
|
||||
let mut out = ctx.run_ui(input, |ui| show(app, ui.ctx()));
|
||||
out.textures_delta.clear();
|
||||
}
|
||||
|
|
@ -176,3 +177,25 @@ fn effect_dialog_applies_to_its_appearance_item() {
|
|||
assert_eq!(n.appearance.items[1].effects().len(), 1, "the effect goes to the stroke");
|
||||
assert!(n.appearance.effects.is_empty() && n.appearance.items[0].effects().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dialogs_are_as_tall_as_their_content() {
|
||||
// The save prompt, shown after a taller dialog, is as tall as its text and buttons: no empty
|
||||
// band around the buttons, nothing inherited from the other dialog.
|
||||
let mut app = app();
|
||||
let ctx = egui::Context::default();
|
||||
theme::install_fonts(&ctx);
|
||||
theme::apply(&ctx, Default::default());
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1600.0, 1000.0));
|
||||
let height = |app: &mut VectorcraftApp, kind: &str, fields: serde_json::Value| {
|
||||
open(app, kind, fields);
|
||||
for _ in 0..30 {
|
||||
let input = egui::RawInput { screen_rect: Some(screen), ..Default::default() };
|
||||
ctx.run_ui(input, |ui| show(app, ui.ctx())).textures_delta.clear();
|
||||
}
|
||||
ctx.memory(|m| m.area_rect(egui::Id::new(("dialog", kind)))).unwrap().height()
|
||||
};
|
||||
let tall = height(&mut app, "command", json!({"__command": "object.move", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6, "g": 7, "h": 8}));
|
||||
let prompt = height(&mut app, crate::unsaved::KIND, json!({"index": 0, "name": "Untitled-1", "then": "close"}));
|
||||
assert!(prompt < 180.0 && prompt < tall, "save prompt {prompt} pt tall (the dialog before it: {tall} pt)");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1338,10 +1338,7 @@ pub fn menu_bar(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
|
|||
} else {
|
||||
egui::RichText::new(*title).size(13.0).color(t.text)
|
||||
};
|
||||
ui.menu_button(text, |ui| {
|
||||
ui.set_min_width(230.0);
|
||||
render_items(app, ui, items, &mut clicked);
|
||||
});
|
||||
ui.menu_button(text, |ui| menu_body(app, ui, items, &mut clicked));
|
||||
}
|
||||
});
|
||||
if let Some((id, p)) = clicked {
|
||||
|
|
@ -1349,6 +1346,12 @@ pub fn menu_bar(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
|
|||
}
|
||||
}
|
||||
|
||||
/// A top-level menu's popup: as wide as its widest item (label plus shortcut), at least 230 pt.
|
||||
fn menu_body(app: &VectorcraftApp, ui: &mut egui::Ui, items: &[Item], clicked: &mut Option<(String, Value)>) {
|
||||
ui.set_min_width(230.0);
|
||||
render_items(app, ui, items, clicked);
|
||||
}
|
||||
|
||||
fn render_items(app: &VectorcraftApp, ui: &mut egui::Ui, items: &[Item], clicked: &mut Option<(String, Value)>) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
for it in items {
|
||||
|
|
@ -1367,7 +1370,7 @@ fn render_items(app: &VectorcraftApp, ui: &mut egui::Ui, items: &[Item], clicked
|
|||
}
|
||||
Item::Todo(label, sc) => {
|
||||
ui.add_enabled_ui(false, |ui| {
|
||||
ui.add(egui::Button::new(*label).shortcut_text(pretty_shortcut(sc)).min_size(egui::vec2(ui.available_width(), 0.0)));
|
||||
ui.add(egui::Button::new(*label).shortcut_text(pretty_shortcut(sc)));
|
||||
})
|
||||
.response
|
||||
.on_disabled_hover_text("Coming soon — tracked in the parity plan");
|
||||
|
|
@ -1386,7 +1389,7 @@ fn render_items(app: &VectorcraftApp, ui: &mut egui::Ui, items: &[Item], clicked
|
|||
Some(false) => format!(" {label}"),
|
||||
None => label,
|
||||
};
|
||||
let r = ui.add_enabled(en, egui::Button::new(text).shortcut_text(sc).min_size(egui::vec2(ui.available_width(), 0.0)));
|
||||
let r = ui.add_enabled(en, egui::Button::new(text).shortcut_text(sc));
|
||||
if r.clicked() {
|
||||
*clicked = Some(click_target(label_of(it), id, p));
|
||||
ui.close();
|
||||
|
|
@ -1847,4 +1850,28 @@ mod tests {
|
|||
menu_entries(&app).into_iter().filter(|e| e.command.as_deref() == Some("effect.expandAppearance")).map(|e| e.path).collect();
|
||||
assert_eq!(paths, [vec!["Object".to_string()]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn menus_are_as_wide_as_their_items() {
|
||||
// Items size the popup to the widest label and shortcut instead of the widest a menu may be.
|
||||
let app = VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default());
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1600.0, 1000.0));
|
||||
let tree = menu_tree();
|
||||
let (_, file) = tree.iter().find(|(t, _)| *t == "File").unwrap();
|
||||
let id = egui::Id::new("test-menu");
|
||||
for _ in 0..3 {
|
||||
let input = egui::RawInput { screen_rect: Some(screen), ..Default::default() };
|
||||
let mut out = ctx.run_ui(input, |ui| {
|
||||
egui::Area::new(id).show(ui.ctx(), |ui| {
|
||||
egui::containers::menu::menu_style(ui.style_mut());
|
||||
ui.with_layout(egui::Layout::top_down_justified(egui::Align::Min), |ui| menu_body(&app, ui, file, &mut None));
|
||||
});
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
}
|
||||
let w = ctx.memory(|m| m.area_rect(id)).unwrap().width();
|
||||
assert!((230.0..340.0).contains(&w), "File menu is {w} pt wide");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue