mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 14:32:36 +00:00
Mate conflicts: mark the row that carries them, and name the way out
detect_mate_conflicts() has been filling m_doc.mate_conflicts on every recompute
since the kernel half landed, and nothing read it. The diagnostics existed and
were invisible — a conflicting assembly looked exactly like a working one.
The tree row is where they go, because the tree is where the user is already
looking for which feature to change. Three states, in precedence order:
disabled -> dim. A SUPPRESSED mate is the user's answer to a conflict, so it
must read as suppressed rather than keep shouting about it.
conflict -> warn.
otherwise -> normal.
Selecting a marked row puts the reason on the status line — "Mate3 already
positions Body 2", the cycle, the self-mate — and names the eye as the way to
suppress it. A message that describes a problem with no action is a message that
gets ignored; the action here is already one click away on the row just selected.
Deliberately NOT a modal, and deliberately not treated as a document error. The
document still evaluates with a conflict present: the mate graph merely has more
than one answer for a body, and which one wins is the thing the user needs to
see. Blocking the loop to say so would interrupt without helping.
The dimming of non-involved bodies from the original UX proposal is still not
implemented, on purpose: under transform composition a failure mid-chain
propagates, so "not involved" is not a well-defined set, and dimming the wrong
bodies would hide the context needed to understand the conflict.
Reviewed and compiled (libslic3r_gui, RC=0); not exercised. snaporca-bioq.
This commit is contained in:
@@ -2934,6 +2934,15 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
m_doc.features[sel].type != CadFeatureType::Sketch &&
|
||||
!m_doc.body.IsNull());
|
||||
m_viewport->set_body_highlight(body);
|
||||
// A conflicting mate says WHY on selection, and names the one action that resolves it.
|
||||
// Suppress is the generic per-feature enable toggle (the eye), so the answer is already
|
||||
// one click away on a row the user has just selected — the message points at it instead
|
||||
// of describing a problem with no way out.
|
||||
if (const std::string* why = (sel >= 0) ? mate_conflict_reason(sel) : nullptr) {
|
||||
m_status->SetForegroundColour(wxColour(235, 110, 110));
|
||||
set_status(wxString::FromUTF8(*why) + _L(" — the eye suppresses this mate"));
|
||||
m_status->Refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// Feature-tree edit actions: act on the selected feature (delete / reorder). These sit in the
|
||||
@@ -5852,6 +5861,16 @@ int DesignPanel::tree_icon_for(CadFeatureType t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The reason detect_mate_conflicts() recorded for this feature, or nullptr. A linear scan: an
|
||||
// assembly has a handful of mates and at most that many conflicts, so a map would cost more to
|
||||
// build than the scans it saves.
|
||||
const std::string* DesignPanel::mate_conflict_reason(int feature) const
|
||||
{
|
||||
for (const auto& c : m_doc.mate_conflicts)
|
||||
if (c.first == feature) return &c.second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// What to say when nothing is selected and no tool is open. Lives in one place because it is
|
||||
// needed from two: after an edit empties the document, and at startup — where after_tree_edit()
|
||||
// has never run, which is exactly why a fresh tab used to show a blank line.
|
||||
@@ -5946,12 +5965,23 @@ void DesignPanel::refresh_tree()
|
||||
// translucent rectangles (otherwise a Plane feature is invisible in the canvas).
|
||||
refresh_datum_planes();
|
||||
update_reference_planes(); // body added/removed -> show/hide the XY/XZ/YZ origin planes
|
||||
for (const auto& f : m_doc.features) {
|
||||
for (size_t fi = 0; fi < m_doc.features.size(); ++fi) {
|
||||
const CadFeature& f = m_doc.features[fi];
|
||||
const int img = tree_icon_for(f.type);
|
||||
wxTreeItemId id = m_tree->AppendItem(root, wxString::FromUTF8(f.name), img, img);
|
||||
// Hidden (disabled) features are greyed so the show/hide state reads at a glance.
|
||||
m_tree->SetItemTextColour(id, f.enabled ? dp_item_text()
|
||||
: dp_item_dim());
|
||||
// Three states, in this order of precedence:
|
||||
// disabled -> dim. A SUPPRESSED mate is the user's answer to a conflict, so it must
|
||||
// read as suppressed rather than keep shouting about the conflict.
|
||||
// conflict -> warn. detect_mate_conflicts() refills m_doc.mate_conflicts on every
|
||||
// recompute; the mark goes on the row that carries it, because the tree
|
||||
// is where the user is already looking for which feature to change.
|
||||
// otherwise -> normal.
|
||||
// A conflict is NOT a document error — the document still evaluates — so the row is
|
||||
// marked and never hidden, and the reason goes to the status line on selection rather
|
||||
// than into a modal that interrupts without offering an action.
|
||||
m_tree->SetItemTextColour(id, !f.enabled ? dp_item_dim()
|
||||
: mate_conflict_reason(int(fi)) != nullptr ? wxColour(235, 110, 110)
|
||||
: dp_item_text());
|
||||
m_tree_items.push_back(id);
|
||||
}
|
||||
refresh_parts(); // bodies live in their own list below the tree, never clipped by history
|
||||
|
||||
@@ -644,6 +644,9 @@ private:
|
||||
// Every status write goes through here so long hints wrap instead of clipping.
|
||||
void set_status(const wxString& text);
|
||||
wxString idle_hint() const; // what to say when nothing is selected
|
||||
// Reason detect_mate_conflicts() recorded for a feature, or nullptr. Marks the tree row and
|
||||
// feeds the status line; a conflict is a diagnostic, not a document error.
|
||||
const std::string* mate_conflict_reason(int feature) const;
|
||||
|
||||
wxMenuItem* append_offer_item(wxMenu* menu, int id, const wxString& text,
|
||||
const struct OfferVerb& v);
|
||||
|
||||
Reference in New Issue
Block a user