#1903: undo-history footer Cancel closes the popup (#1916)

Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
SlavenDj
2026-10-18 09:56:34 -04:00
committed by GitHub
co-authored by Brandon Thomas
parent 4dcd4749ca
commit 2caa9db109
+43 -2
View File
@@ -86,6 +86,11 @@ fn undo_list_row_id(i: usize) -> egui::Id {
undo_list_button_id().with(("row", i))
}
/// The footer Cancel row's id (#1903).
pub(crate) fn undo_list_cancel_id() -> egui::Id {
undo_list_button_id().with("cancel")
}
/// The Undo list's rows, newest first: each step named as the Undo button's tooltip names it.
pub(crate) fn undo_list_rows(app: &WordApp) -> Vec<String> {
app.session.undo_history().into_iter().map(|l| tl!(l).to_string()).collect()
@@ -147,8 +152,21 @@ fn undo_list(ui: &mut Ui, app: &mut WordApp) {
}
});
ui.separator();
let footer = undo_list_footer(hovered.map_or(0, |h| h + 1));
ui.vertical_centered(|ui| ui.label(egui::RichText::new(footer).font(regular(12.0)).color(t.text)));
let n = hovered.map_or(0, |h| h + 1);
if n == 0 {
// Cancel is a real row: it closes the popup without undoing anything (#1903).
let (r, _) = ui.allocate_exact_size(vec2(ui.available_width(), ROW), Sense::hover());
let resp = ui.interact(r, undo_list_cancel_id(), Sense::click());
if resp.hovered() {
ui.painter().rect_filled(r, 0.0, t.selection);
}
ui.painter().text(pos2(r.min.x + 6.0, r.center().y), Align2::LEFT_CENTER, undo_list_footer(0), regular(12.0), t.text);
if resp.clicked() {
ui.close();
}
} else {
ui.vertical_centered(|ui| ui.label(egui::RichText::new(undo_list_footer(n)).font(regular(12.0)).color(t.text)));
}
if let Some(i) = clicked {
let _ = app.run("edit.undoTo", json!({"count": i + 1}));
ui.close();
@@ -680,6 +698,29 @@ mod tests {
}
}
/// #1903: the footer's Cancel row closes the Undo list without undoing anything.
#[test]
fn undo_list_cancel_closes_without_undoing() {
let mut a = WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::new()), crate::Services::default());
a.run("text.insert", json!({"text": "Hello"})).unwrap();
let before: Vec<String> = a.session.undo_history().into_iter().map(str::to_string).collect();
assert!(!before.is_empty());
let (ctx, mut n) = (ctx(), 0);
frame(&ctx, &mut n, vec![], &mut a);
let arrow = ctx.read_response(undo_list_button_id()).unwrap();
let popup = egui::Popup::default_response_id(&arrow);
click(&ctx, &mut n, arrow.rect.center(), &mut a);
assert!(egui::Popup::is_id_open(&ctx, popup), "the arrow opens the Undo list");
frame(&ctx, &mut n, vec![], &mut a);
let cancel = ctx.read_response(undo_list_cancel_id()).expect("Cancel is drawn");
click(&ctx, &mut n, cancel.rect.center(), &mut a);
let after: Vec<String> = a.session.undo_history().into_iter().map(str::to_string).collect();
assert_eq!(after, before, "nothing undone");
frame(&ctx, &mut n, vec![], &mut a);
assert!(!egui::Popup::is_id_open(&ctx, popup), "Cancel closes the list");
}
#[test]
fn mac_title_bar_centres_and_clears_the_traffic_lights() {
// The lights are centred in the bar, and content keeps the same gap after them.