Rename: a refused inline rename keeps the editor open (#10654) (#10952)

* Rename: a refused inline rename keeps the editor and says why (#10654)

commit_rename dropped the command's Result, so a refusal closed the editor
with no message and left the old name. It now returns the error, shows it in
the status line, and the inline editor stays open.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Rename: keep the command's status line when the editor stays open (#10654)

The command completion already explains the refusal; do not overwrite it with
the raw engine error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Vvynot
2026-11-02 13:44:34 +00:00
committed by GitHub
parent 8ac4c03602
commit 2ccca96929
5 changed files with 52 additions and 17 deletions
+3 -1
View File
@@ -2824,7 +2824,9 @@ fn inline_rename(app: &mut SolveApp, ui: &mut egui::Ui, row: Rect, label_left: f
if escape {
// Dropping the state cancels the edit.
} else if commit {
crate::context_menu::commit_rename(app, &rename);
if crate::context_menu::commit_rename(app, &rename).is_err() {
app.menu.rename = Some(rename);
}
} else {
app.menu.rename = Some(rename);
}
+25
View File
@@ -106,6 +106,31 @@ fn f2_on_a_clicked_browser_group_renames_the_group() {
}
}
/// #10654: a refused inline rename keeps the editor open and says why, instead of closing
/// silently and leaving the old name.
#[test]
fn a_refused_inline_rename_keeps_the_editor_and_shows_the_error() {
let mut h = Harness::new();
h.app.run("solid.box", json!({"length":10,"width":10,"height":10,"body_name":"A"})).unwrap();
h.app.run("solid.box", json!({"length":10,"width":10,"height":10,"body_name":"B"})).unwrap();
h.app.preferences.browser_bodies_folders = crate::prefs::BodiesFolders::Always;
h.settle();
h.app.menu.rename = Some(crate::context_menu::Rename {
what: crate::context_menu::RenameWhat::Body("B".into()),
text: "A".into(),
at: egui::Pos2::ZERO,
focused: true,
inline: true,
});
h.frames(2);
h.step(&json!({"key":"Enter"})).unwrap();
h.frames(2);
assert!(h.app.menu.rename.is_some(), "the editor stays open after a refused rename");
let (text, _, error) = h.app.status.clone().expect("the refusal is shown");
assert!(error && text.contains("another body is called"), "{text}");
assert!(h.app.session.world_state().body("B").is_some(), "the body keeps its name");
}
#[test]
fn folder_visibility_target_clears_when_the_user_moves_on() {
for action in ["body click", "selection command", "empty click", "escape", "design tab", "model edit", "tool"] {
+22 -14
View File
@@ -2036,24 +2036,30 @@ pub fn start_rename(app: &mut SolveApp, what: RenameWhat, at: Pos2) {
app.menu.rename = Some(Rename { what, text, at, focused: false, inline: false });
}
pub(crate) fn commit_rename(app: &mut SolveApp, r: &Rename) {
pub(crate) fn commit_rename(app: &mut SolveApp, r: &Rename) -> Result<(), String> {
let name = r.text.trim().to_string();
if name.is_empty() {
return;
return Ok(());
}
match &r.what {
let result = match &r.what {
RenameWhat::Body(old) => {
// The command renames the body where it is hidden or locked too.
drop(app.run_heavy("body.rename", json!({ "body": old, "name": name })));
app.run_heavy("body.rename", json!({ "body": old, "name": name })).map(|_| ())
}
RenameWhat::Feature(f) => drop(app.run("timeline.rename", json!({ "feature": f, "name": name }))),
RenameWhat::Component(c) => drop(app.run("component.rename", json!({ "component": c, "name": name }))),
RenameWhat::Group(g) => crate::browser::group_action(app, "ui.renameGroup", &json!({ "group": g, "name": name })),
RenameWhat::TimelineGroup(g) => drop(app.run("timeline.rename_group", json!({ "group": g, "name": name }))),
RenameWhat::Canvas(c) => drop(app.run("canvas.edit", json!({ "canvas": c, "name": name }))),
RenameWhat::View(v) => drop(app.run("view.rename", json!({ "view": v, "name": name }))),
RenameWhat::Joint(j) => drop(app.run("joint.edit", json!({ "joint": j, "name": name }))),
}
RenameWhat::Feature(f) => app.run("timeline.rename", json!({ "feature": f, "name": name })).map(|_| ()),
RenameWhat::Component(c) => app.run("component.rename", json!({ "component": c, "name": name })).map(|_| ()),
RenameWhat::Group(g) => {
crate::browser::group_action(app, "ui.renameGroup", &json!({ "group": g, "name": name }));
Ok(())
}
RenameWhat::TimelineGroup(g) => app.run("timeline.rename_group", json!({ "group": g, "name": name })).map(|_| ()),
RenameWhat::Canvas(c) => app.run("canvas.edit", json!({ "canvas": c, "name": name })).map(|_| ()),
RenameWhat::View(v) => app.run("view.rename", json!({ "view": v, "name": name })).map(|_| ()),
RenameWhat::Joint(j) => app.run("joint.edit", json!({ "joint": j, "name": name })).map(|_| ()),
};
// The command's own status line explains a refusal; the caller decides whether to keep the
// editor open on it.
result
}
// ---------------------------------------------------------------------------------------------
@@ -2583,7 +2589,9 @@ fn show_rename(app: &mut SolveApp, ctx: &egui::Context) {
done = Some(true);
}
match done {
Some(true) => commit_rename(app, &r),
Some(true) => {
let _ = commit_rename(app, &r);
}
Some(false) => {}
None => app.menu.rename = Some(r),
}
@@ -2596,7 +2604,7 @@ pub fn finish_rename(app: &mut SolveApp, text: Option<&str>, commit: bool) -> bo
r.text = s.to_string();
}
if commit {
commit_rename(app, &r);
let _ = commit_rename(app, &r);
}
true
}
@@ -1091,7 +1091,7 @@ fn a_body_renamed_in_the_browser_keeps_the_pattern_after_it() {
let before = mesh(&h);
// What Enter does, without the frames after it that would see the rename in.
let rename = h.app.menu.rename.take().unwrap();
crate::context_menu::commit_rename(&mut h.app, &rename);
let _ = crate::context_menu::commit_rename(&mut h.app, &rename);
assert!(h.app.computing_since().is_some(), "computing");
assert_eq!(names(&h), ["Body1"], "the design is as it was while it computes");
until_settled(&mut h);
+1 -1
View File
@@ -1068,7 +1068,7 @@ fn timeline_group_name(
if escape {
// Dropping the shared state cancels the edit and leaves the old name intact.
} else if commit {
crate::context_menu::commit_rename(app, &rename);
let _ = crate::context_menu::commit_rename(app, &rename);
} else {
app.menu.rename = Some(rename);
}