mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
CAD: undo() never rolled back variables, so a bad one bricked the recipe
checkpoint() snapshotted `features` and undo() restored `features`, but
`variables` is a separate member of CadDocument. Every caller of the documented
checkpoint -> mutate -> recompute -> undo-on-failure pattern therefore failed to
roll a variable back: the bad value stayed in the document and every later
recompute failed, which is exactly the corruption the pattern exists to prevent.
Feature `expr` bindings were unaffected only because expr lives inside
CadFeature and rode along in the features snapshot — which is why the feature
side appeared to work.
This was a kernel gap, not a GUI one: McpControl::action_set_variable has the
same sequence and was equally broken.
The undo/redo stacks now hold a {features, variables} Snapshot. Nothing here is
serialized, so no recipe version change and no golden-fixture regeneration.
Two tests, both verified to FAIL against a faithful reproduction of the bug
(undo() leaving `variables` untouched) at test_caddocument.cpp:4420 and :4441:
one covers restoring a variable's previous value, the other covers removing a
variable that did not exist before the checkpoint. Worth recording that the
first mutation attempt was NOT faithful — it dropped the restore but kept
std::move(variables) into the redo stack, which empties the map as a side effect
and made the second test pass for the wrong reason. A mutation has to reproduce
the original defect, not merely break the code.
Second defect, same area: undo() calls recompute(), which succeeds and clears
doc.error, so the reason an edit was rejected was destroyed before anything
could display it. Six sites — four in DesignPanel, two in McpControl — now carry
the message across the rollback. on_remove_variable additionally asserted
"referenced by a feature expression" as fact; it now offers that as the likely
cause and appends the real error, since that diagnosis is wrong for any other
failure.
Suite 141 cases / 1972 assertions (was 139/1960). GUI compiles clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
33275f2c74
commit
c565d6ba86
@@ -8792,7 +8792,9 @@ void DesignPanel::on_add_variable()
|
||||
m_doc.checkpoint();
|
||||
m_doc.variables[name_str] = expr_str;
|
||||
bool ok = m_doc.recompute();
|
||||
if (!ok) { m_doc.undo(); }
|
||||
// undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
|
||||
// rejected is gone before anything can display it. Carry it across the rollback.
|
||||
if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
|
||||
after_tree_edit(ok);
|
||||
refresh_variables();
|
||||
}
|
||||
@@ -8817,7 +8819,9 @@ void DesignPanel::on_edit_variable()
|
||||
m_doc.checkpoint();
|
||||
m_doc.variables[name_str] = expr_str;
|
||||
bool ok = m_doc.recompute();
|
||||
if (!ok) { m_doc.undo(); }
|
||||
// undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
|
||||
// rejected is gone before anything can display it. Carry it across the rollback.
|
||||
if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
|
||||
after_tree_edit(ok);
|
||||
refresh_variables();
|
||||
}
|
||||
@@ -8837,11 +8841,16 @@ void DesignPanel::on_remove_variable()
|
||||
m_doc.variables.erase(name_str);
|
||||
bool ok = m_doc.recompute();
|
||||
if (!ok) {
|
||||
// Capture before undo(): its recompute succeeds and clears doc.error.
|
||||
const std::string why = m_doc.error;
|
||||
m_doc.undo();
|
||||
// A failed recompute on remove means a feature expr still references it
|
||||
// Almost always a feature expr still referencing the variable — but say so as the
|
||||
// likely cause and keep the real message, rather than asserting a diagnosis that
|
||||
// would be wrong for any other failure.
|
||||
m_doc.error = wxString::Format(
|
||||
_L("Variable '%s' is referenced by a feature expression and cannot be removed"),
|
||||
m_var_list->GetItemText(sel, 0)).ToUTF8().data();
|
||||
_L("Variable '%s' could not be removed (likely still referenced by a feature "
|
||||
"expression): %s"),
|
||||
m_var_list->GetItemText(sel, 0), wxString::FromUTF8(why)).ToUTF8().data();
|
||||
}
|
||||
after_tree_edit(ok);
|
||||
refresh_variables();
|
||||
@@ -8916,7 +8925,9 @@ void DesignPanel::on_set_expr()
|
||||
m_doc.checkpoint();
|
||||
m_doc.features[m_edit_index].expr[field] = expr;
|
||||
bool ok = m_doc.recompute();
|
||||
if (!ok) { m_doc.undo(); }
|
||||
// undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
|
||||
// rejected is gone before anything can display it. Carry it across the rollback.
|
||||
if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
|
||||
after_tree_edit(ok);
|
||||
if (ok) m_expr_text->Clear();
|
||||
|
||||
@@ -8958,7 +8969,9 @@ void DesignPanel::on_clear_expr()
|
||||
m_doc.checkpoint();
|
||||
feat_expr.erase(field);
|
||||
bool ok = m_doc.recompute();
|
||||
if (!ok) { m_doc.undo(); }
|
||||
// undo() recomputes, which SUCCEEDS and clears doc.error — so the reason the edit was
|
||||
// rejected is gone before anything can display it. Carry it across the rollback.
|
||||
if (!ok) { const std::string why = m_doc.error; m_doc.undo(); m_doc.error = why; }
|
||||
after_tree_edit(ok);
|
||||
|
||||
if (m_edit_index >= 0 && m_edit_index < int(m_doc.features.size())) {
|
||||
|
||||
Reference in New Issue
Block a user