Preferences: Reset Preferences shares the row of Export and Import

Export Preferences… and Import Preferences… had a row to themselves,
and Reset Preferences started the next row, left of Previous, Next,
Cancel and OK. Reset Preferences now follows Import Preferences… on the
first row, which wraps when it is wider than the dialog, and the row
below holds only the category buttons, Cancel and OK.

Tests check that the three buttons share a row in English, and that in
every language the dialog keeps its 760 pt content width with each
button inside it and above the OK row. Hindi, Gujarati, Georgian,
Japanese, Korean and Chinese are measured with fallback fonts, since
the tests do not load the craft-fonts faces or installed fonts those
languages are drawn with.
This commit is contained in:
dotCooCoo
2026-11-14 13:24:53 -08:00
parent 107078ad10
commit 75ec4fea77
2 changed files with 73 additions and 4 deletions
+5 -4
View File
@@ -404,7 +404,8 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
});
});
ui.add_space(14.0);
ui.horizontal(|ui| {
// Export, Import and Reset share one row, which wraps when it is wider than the dialog.
ui.horizontal_wrapped(|ui| {
if widgets::secondary_button(ui, tl!("Export Preferences…")).on_hover_text(tl!("Save the applied preferences to a JSON file")).clicked()
{
export_preferences(app, &mut d);
@@ -417,15 +418,15 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
{
import_preferences(app, &mut d);
}
});
ui.add_space(8.0);
ui.horizontal(|ui| {
if widgets::secondary_button(ui, tl!("Reset Preferences"))
.on_hover_text(tl!("Restore every preference to its default (applied on OK)"))
.clicked()
{
reset = true;
}
});
ui.add_space(8.0);
ui.horizontal(|ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if widgets::primary_button(ui, tl!("OK")).clicked() {
ok = true;
+68
View File
@@ -335,6 +335,74 @@ fn reset_preferences(app: &mut VectorcraftApp) {
crate::dialogs::confirm(app).unwrap();
}
/// The Preferences dialog drawn in `lang` with the open dialog available: the label and screen
/// rectangle of each control, from the accessibility tree, and the dialog's content rectangle
/// (inside its 20 pt margin and frame stroke).
fn dialog_controls(lang: crate::i18n::Lang) -> (Vec<(String, egui::Rect)>, egui::Rect) {
crate::i18n::set_current(lang);
let mut a = app();
a.services.pick_open = Some(Box::new(|_: &crate::FilePick| None));
open(&mut a, Some("General"));
let ctx = egui::Context::default();
ctx.enable_accesskit();
crate::theme::install_fonts(&ctx);
let mut v = vec![];
for time in [0.0, 0.25, 0.5] {
let screen_rect = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1200.0, 900.0)));
let raw = egui::RawInput { screen_rect, time: Some(time), ..Default::default() };
let mut out = ctx.run_ui(raw, |ui| show(&mut a, ui.ctx()));
out.textures_delta.clear();
let nodes = out.platform_output.accesskit_update.map(|u| u.nodes).unwrap_or_default();
v = nodes
.iter()
.filter_map(|(_, n)| {
let b = n.bounds()?;
Some((n.label()?.to_string(), egui::Rect::from_min_max(egui::pos2(b.x0 as f32, b.y0 as f32), egui::pos2(b.x1 as f32, b.y1 as f32))))
})
.collect();
}
crate::i18n::set_current(crate::i18n::Lang::EN);
let window = ctx.memory(|m| m.area_rect(egui::Id::unique("dialog-preferences"))).expect("the dialog window is shown");
let content = window.shrink(20.0 + ctx.global_style().visuals.window_stroke.width);
(v, content)
}
/// Export Preferences…, Import Preferences… and Reset Preferences share one row above OK and
/// Cancel.
#[test]
fn export_import_and_reset_share_one_row() {
let (controls, _) = dialog_controls(crate::i18n::Lang::EN);
let top = |label: &str| controls.iter().find(|(t, _)| t == label).map(|(_, r)| r.top()).unwrap_or_else(|| panic!("no `{label}` in {controls:?}"));
let row = top("Export Preferences…");
assert!((top("Import Preferences…") - row).abs() < 0.5, "Import is on Export's row");
assert!((top("Reset Preferences") - row).abs() < 0.5, "Reset is on Export's row");
assert!(top("OK") > row + 20.0, "OK is on the row below");
}
/// In every language the dialog keeps its 760 pt content width, and the three buttons above OK
/// lie inside it and above the row of OK and Cancel.
#[test]
fn export_import_and_reset_fit_in_every_language() {
for lang in crate::i18n::Lang::all() {
let (controls, content) = dialog_controls(lang);
assert!(content.width() <= 760.5, "{}: the dialog's content grew to {content:?}", lang.code());
let find = |label: &str| {
let label = crate::i18n::tr(lang, label);
controls.iter().find(|(t, _)| t == label).map(|(_, r)| *r).unwrap_or_else(|| panic!("{}: no `{label}` in {controls:?}", lang.code()))
};
let ok = find("OK");
for label in ["Export Preferences…", "Import Preferences…", "Reset Preferences"] {
let r = find(label);
assert!(
r.right() <= content.right() + 0.5 && r.left() >= content.left() - 0.5,
"{}: `{label}` at {r:?} is outside the dialog's content {content:?}",
lang.code()
);
assert!(r.bottom() <= ok.top(), "{}: `{label}` at {r:?} reaches the OK row at {ok:?}", lang.code());
}
}
}
/// Reset Preferences then OK resets the preferences, General included, and leaves the open
/// document's units, with no undo step in the document.
#[test]