Macro interface: follow per-window dialogs and egui 0.37 accessibility

Main now keeps a dialog with its own workbook window, so the step editor is
parked with its workbook rather than drawn over another one, and closing that
workbook drops the editor without drawing it again: forget_closed_editor
clears it each frame. The Macros, Record Macro and editor text boxes are
labelled by their captions, and the tests pick the question's Cancel and the
chart menu's Delete by role, as the formula bar and ribbon share those names.
This commit is contained in:
prxnvt committed 2026-10-26 00:38:32 -04:00
1 parent c801e50e40
commit e40291fc3d
5 files changed
+41 -23

No files matched your search

+1
View File
@@ -1797,6 +1797,7 @@ impl SheetApp {
});
}
dialogs::show(self, &ctx);
macro_ui::forget_closed_editor(self);
macro_ui::question_box(self, &ctx);
widgets::message_box(self, &ctx);
widgets::toast(self, &ctx);
+10 -7
View File
@@ -1074,13 +1074,16 @@ fn body(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, ed: &mut Editor) {
let debugging = app.session.macro_debug.is_some();
// Name, description, Save; they wrap in a narrow window.
ui.horizontal_wrapped(|ui| {
ui.label("Name:");
let name = ui.add_enabled(!debugging, egui::TextEdit::singleline(&mut ed.draft.name).desired_width(180.0).id_salt("me_name"));
ui.label("Description:");
let desc = ui.add_enabled(
!debugging,
egui::TextEdit::singleline(&mut ed.draft.description).desired_width((ui.available_width() - 90.0).max(80.0)).id_salt("me_desc"),
);
let label = ui.label("Name:");
let name =
ui.add_enabled(!debugging, egui::TextEdit::singleline(&mut ed.draft.name).desired_width(180.0).id_salt("me_name")).labelled_by(label.id);
let label = ui.label("Description:");
let desc = ui
.add_enabled(
!debugging,
egui::TextEdit::singleline(&mut ed.draft.description).desired_width((ui.available_width() - 90.0).max(80.0)).id_salt("me_desc"),
)
.labelled_by(label.id);
if name.changed() || desc.changed() {
ed.dirty = true;
}
+19 -10
View File
@@ -181,6 +181,15 @@ pub fn note_result(app: &mut SheetApp, id: &str, params: &Json, v: &Json) {
app.toast = Some((text, crate::now_ms()));
}
/// Drops the step editor once its workbook is closed. The editor's dialog stays with its
/// workbook's window, so it may not be drawn again to notice.
pub fn forget_closed_editor(app: &mut SheetApp) {
if app.macros.editor.as_ref().is_some_and(|ed| !app.session.documents().iter().any(|d| d.uid == ed.doc)) {
app.macros.editor = None;
app.grid.macro_overlay = None;
}
}
/// The prompt for [`MacroUi::question`]: its text, a button for each choice and Cancel.
pub fn question_box(app: &mut SheetApp, ctx: &egui::Context) {
if app.message.is_some() {
@@ -396,8 +405,8 @@ pub fn macros_dialog(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, open: &mut
ui.horizontal_top(|ui| {
ui.vertical(|ui| {
ui.set_width(330.0);
ui.label("Macro name:");
if ui.add(egui::TextEdit::singleline(&mut name).desired_width(330.0).id_salt("macro_name")).changed() {
let label = ui.label("Macro name:");
if ui.add(egui::TextEdit::singleline(&mut name).desired_width(330.0).id_salt("macro_name")).labelled_by(label.id).changed() {
kind = Kind::Macro;
}
egui::Frame::NONE.fill(t.input_bg).stroke(egui::Stroke::new(1.0, t.input_border)).inner_margin(2.0).show(ui, |ui| {
@@ -528,9 +537,9 @@ fn options(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog) {
ui.label(egui::RichText::new(c).color(Tokens::get(ui.ctx()).danger));
}
ui.add_space(4.0);
ui.label("Description:");
let label = ui.label("Description:");
let mut desc = d.values.get("description").and_then(Json::as_str).unwrap_or("").to_string();
ui.add(egui::TextEdit::multiline(&mut desc).desired_rows(3).desired_width(f32::INFINITY).id_salt("macro_description"));
ui.add(egui::TextEdit::multiline(&mut desc).desired_rows(3).desired_width(f32::INFINITY).id_salt("macro_description")).labelled_by(label.id);
ui.add_space(6.0);
let mut done = false;
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
@@ -551,8 +560,8 @@ fn options(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog) {
/// The shortcut letter box, after "Ctrl+Shift+" (or "⌘⇧" on a Mac). Keeps one capital letter.
fn shortcut_field(ui: &mut Ui, key: &mut String, id: &str) {
ui.horizontal(|ui| {
ui.label(if ui.ctx().os() == egui::os::OperatingSystem::Mac { "⌘ ⇧ +" } else { "Ctrl+Shift+" });
ui.add(egui::TextEdit::singleline(key).char_limit(1).desired_width(24.0).id_salt(id));
let label = ui.label(if ui.ctx().os() == egui::os::OperatingSystem::Mac { "⌘ ⇧ +" } else { "Ctrl+Shift+" });
ui.add(egui::TextEdit::singleline(key).char_limit(1).desired_width(24.0).id_salt(id)).labelled_by(label.id);
});
*key = key.to_ascii_uppercase();
}
@@ -620,14 +629,14 @@ pub fn record_dialog(app: &mut SheetApp, ui: &mut Ui, d: &mut Dialog, open: &mut
let mut name = get(d, "name");
let mut key = get(d, "shortcut");
let mut desc = get(d, "description");
ui.label("Macro name:");
ui.add(egui::TextEdit::singleline(&mut name).desired_width(f32::INFINITY).id_salt("record_name"));
let label = ui.label("Macro name:");
ui.add(egui::TextEdit::singleline(&mut name).desired_width(f32::INFINITY).id_salt("record_name")).labelled_by(label.id);
ui.add_space(4.0);
ui.label("Shortcut key:");
shortcut_field(ui, &mut key, "record_shortcut");
ui.add_space(4.0);
ui.label("Description:");
ui.add(egui::TextEdit::multiline(&mut desc).desired_rows(3).desired_width(f32::INFINITY).id_salt("record_description"));
let label = ui.label("Description:");
ui.add(egui::TextEdit::multiline(&mut desc).desired_rows(3).desired_width(f32::INFINITY).id_salt("record_description")).labelled_by(label.id);
ui.checkbox(&mut app.macros.relative, "Use Relative References");
let problem = record_problem(app, &name, &key);
if let Some(p) = &problem {
+2 -1
View File
@@ -152,7 +152,8 @@ fn right_clicking_a_chart_opens_its_menu_not_the_cells() {
let (mut h, id) = fixture();
let inside = corner(&h, id) - vec2(100.0, 80.0);
right_click(&mut h, inside, vec2(0.0, 0.0));
h.get_by_role_and_label(Role::Button, "Delete").click();
// Home › Cells › Delete has the same accessible name; the menu is drawn over the ribbon, last.
h.get_all_by_role_and_label(Role::Button, "Delete").last().unwrap().click();
h.run_steps(3);
assert!(h.state().session.doc().unwrap().wb.active().unwrap().charts.is_empty());
assert!(h.state().grid.object_menu.is_none());
+9 -5
View File
@@ -386,12 +386,15 @@ fn the_editor_works_only_on_the_workbook_it_opened_in() {
h.state_mut().message = None;
h.state_mut().macros.editor = Some(ed);
h.run_steps(3);
assert!(h.query_by_label_contains("Switch back to it").is_some(), "the editor says whose macro it is");
// Back in its own workbook, the editor saves there.
// The editor stays with its own workbook's window: it is not drawn over the other one.
assert!(h.state().dialog.as_ref().is_none_or(|d| d.name() != "macroEditor"), "the editor is not shown here");
// Back in its own workbook, the editor shows again and saves there.
let i = h.state().session.documents().iter().position(|d| d.uid == book1).unwrap();
h.state_mut().session.set_active(i);
h.run_steps(3);
assert!(h.state().dialog.as_ref().is_some_and(|d| d.name() == "macroEditor"), "the editor is back with its workbook");
let mut ed = h.state_mut().macros.editor.take().unwrap();
let app = h.state_mut();
let i = app.session.documents().iter().position(|d| d.uid == book1).unwrap();
app.session.set_active(i);
assert!(gridcraft_ui_egui::macro_editor::save(app, &mut ed));
assert_eq!(app.session.active().unwrap().wb.macros.iter().map(|m| m.name.as_str()).collect::<Vec<_>>(), ["Fresh"]);
app.macros.editor = Some(ed);
@@ -435,7 +438,8 @@ fn quitting_asks_about_the_editors_unsaved_steps_first() {
assert!(!sent.contains(&egui::ViewportCommand::Close), "{sent:?}");
assert!(h.query_by_label_contains("Save changes to macro “Unsaved”").is_some());
// Cancel keeps the app and the draft.
h.get_by_label("Cancel").click();
// The formula bar's Cancel button has the same name; the question's is the button drawn last.
h.get_all_by_role_and_label(egui::Role::Button, "Cancel").last().unwrap().click();
let sent = step_commands(&mut h);
assert!(!sent.contains(&egui::ViewportCommand::Close), "{sent:?}");
assert_eq!(h.state().macros.editor.as_ref().map(|e| e.draft.name.as_str()), Some("Unsaved"));