Merge #337: M3: renaming a style folder no longer closes the app (@Barzabel)

This commit is contained in:
bflatastic
2026-10-15 17:51:44 +07:00
3 changed files with 63 additions and 2 deletions
+2
View File
@@ -149,6 +149,8 @@ mod shortcut_keys_tests;
#[cfg(test)]
mod shortcuts_tests;
#[cfg(test)]
mod style_folder_rename_tests;
#[cfg(test)]
mod text_enter_tests;
#[cfg(test)]
mod titlebar_tests;
@@ -0,0 +1,56 @@
//! Renaming a folder of local styles, drawn headless with the screen reader tree on: Rename folder
//! in the folder's menu opens its name for editing. On every frame the focused node must be in the
//! AccessKit tree; the platform adapter (UI Automation on Windows) aborts the app when it isn't
//! (the same fault as #224).
use egui::{Event, Key, Modifiers, PointerButton, Pos2};
use figcraft_engine::Session;
use serde_json::json;
use crate::layers_drag_tests::Ui;
use crate::live_tests::{flat, text_center};
/// One frame with `events`, checking the focused node is in the screen reader tree.
fn checked_frame(u: &mut Ui, events: Vec<Event>, k: usize) -> Vec<egui::Shape> {
let out = u.frame_output(events);
let tree = out.platform_output.accesskit_update.expect("an AccessKit update");
assert!(tree.nodes.iter().any(|(i, _)| *i == tree.focus), "frame {k}: the focused node {:?} isn't in the tree", tree.focus);
flat(&out.shapes)
}
/// A click with `button` at `p` and the frames that follow, each checked.
fn click(u: &mut Ui, p: Pos2, button: PointerButton) -> Vec<egui::Shape> {
for _ in 0..5 {
u.frame(vec![Event::PointerMoved(p)]);
}
let mut shapes = Vec::new();
for (k, pressed) in [Some(true), Some(false)].into_iter().chain(std::iter::repeat_n(None, 20)).enumerate() {
let events = pressed.map(|pressed| Event::PointerButton { pos: p, button, pressed, modifiers: Modifiers::NONE });
shapes = checked_frame(u, events.into_iter().collect(), k);
}
shapes
}
#[test]
fn renaming_a_style_folder_keeps_the_focus_in_the_screen_reader_tree() {
let mut s = Session::new();
for name in ["Brand/Primary", "Brand/Accent"] {
s.execute("style.create", &json!({"type": "paint", "name": name, "value": [{"type": "solid", "color": "#0D99FF"}]})).unwrap();
}
let mut u = Ui::new(s);
u.ctx.enable_accesskit();
let shapes = checked_frame(&mut u, vec![], 0);
// The Design panel lists the local styles with nothing selected; the folder's menu renames it.
let menu = click(&mut u, text_center(&shapes, "Brand"), PointerButton::Secondary);
click(&mut u, text_center(&menu, "Rename folder"), PointerButton::Primary);
checked_frame(&mut u, vec![Event::Key { key: Key::A, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::COMMAND }], 0);
checked_frame(&mut u, vec![Event::Text("Theme".into())], 1);
for pressed in [true, false] {
checked_frame(&mut u, vec![Event::Key { key: Key::Enter, physical_key: None, pressed, repeat: false, modifiers: Modifiers::NONE }], 2);
}
for k in 0..5 {
checked_frame(&mut u, vec![], k + 3);
}
let names: Vec<String> = u.app.session.doc().unwrap().styles.iter().map(|s| s.name.clone()).collect();
assert_eq!(names, ["Theme/Primary", "Theme/Accent"]);
}
+5 -2
View File
@@ -1042,7 +1042,10 @@ pub fn local_styles(app: &mut FigApp, ui: &mut Ui, doc: &Document) {
let (r, resp) = ui.allocate_exact_size(vec2(w, 24.0), Sense::click());
let text_r = Rect::from_min_max(pos2(r.min.x + widgets::PAD, r.min.y + 2.0), pos2(r.max.x - widgets::PAD, r.max.y - 2.0));
if renaming.as_deref() == Some(*f) {
let (out, open) = widgets::rename_field(ui, folder_rename_id().with(*f), f, text_r, regular(11.0), false);
// Focused as it is first drawn: a focus asked for before its field is drawn
// isn't in the screen reader tree (UI Automation aborts the app, #224).
let start = ui.data_mut(|d| d.remove_temp::<bool>(folder_rename_id().with("start"))).unwrap_or(false);
let (out, open) = widgets::rename_field(ui, folder_rename_id().with(*f), f, text_r, regular(11.0), start);
if let Some(name) = out {
run = Some(("style.renameFolder", json!({"folder": f, "name": name})));
}
@@ -1055,7 +1058,7 @@ pub fn local_styles(app: &mut FigApp, ui: &mut Ui, doc: &Document) {
crate::right::context_popup(&resp).show(|ui| {
if crate::right::menu_item(ui, tr("Rename folder"), "") {
ui.data_mut(|d| d.insert_temp(folder_rename_id(), f.to_string()));
ui.memory_mut(|m| m.request_focus(folder_rename_id().with(*f).with("te")));
ui.data_mut(|d| d.insert_temp(folder_rename_id().with("start"), true));
ui.close();
}
});