From b250a2b8581dd3f0c6c073d80a3be83859ddbfec Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Tue, 1 Sep 2026 06:41:31 +0200 Subject: [PATCH] Tell the user the badge is a button, and refresh the DoF when one is deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A glyph reads as decoration until something says otherwise, so the badges shipped last commit were discoverable only by accident. Two places now say it, chosen because they are where the eye already is: - the moment of applying, which is the one the user is watching ("Applied constraint · its badge is on the sketch — click the badge to remove it"). The hint line could not carry this alone: it only refreshes when the (mode, step, picks) tuple changes, and applying a constraint changes none of them. - the Select-mode hint line, appended only while the live sketch actually holds a constraint, so it never advertises a badge that is not on screen. Also fixes what the previous commit got wrong: remove_constraint_near solved through solve_sketch_entities directly, which relaxes the geometry but leaves m_dof and the per-entity conflict flags untouched and never fires on_solve_state. Deleting a constraint therefore left the DoF readout describing the system as it was BEFORE the deletion, and any red over-constrained tint stranded on screen. It goes through resolve_live() now, the same path every other live edit uses. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA --- src/slic3r/GUI/CAD/DesignCanvas.cpp | 5 +++++ src/slic3r/GUI/CAD/DesignCanvas.hpp | 3 +++ src/slic3r/GUI/CAD/DesignPanel.cpp | 10 +++++++++- src/slic3r/GUI/CAD/DesignSketchTool.cpp | 6 +++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/CAD/DesignCanvas.cpp b/src/slic3r/GUI/CAD/DesignCanvas.cpp index bba4d42014..6234fa6f61 100644 --- a/src/slic3r/GUI/CAD/DesignCanvas.cpp +++ b/src/slic3r/GUI/CAD/DesignCanvas.cpp @@ -1489,6 +1489,11 @@ const std::vector& DesignCanvas::sketch_entities() const return m_sketch_tool.entities(); } +int DesignCanvas::sketch_constraint_count() const +{ + return int(m_sketch_tool.constraints().size()); +} + bool DesignCanvas::try_add_sketch_constraints(const std::vector& defs) { return m_sketch_tool.try_add_constraints(defs); diff --git a/src/slic3r/GUI/CAD/DesignCanvas.hpp b/src/slic3r/GUI/CAD/DesignCanvas.hpp index 7f8de25a04..fe87936f8b 100644 --- a/src/slic3r/GUI/CAD/DesignCanvas.hpp +++ b/src/slic3r/GUI/CAD/DesignCanvas.hpp @@ -299,6 +299,9 @@ public: // append->solve->keep-or-rollback, rather than reaching into mcp_sketch_tool(). const std::vector& sketch_selection() const; const std::vector& sketch_entities() const; + // How many constraints the LIVE session holds. Only a count: the hint line needs to know + // whether any badge is on screen to talk about, nothing more. + int sketch_constraint_count() const; bool try_add_sketch_constraints(const std::vector& defs); // In-canvas bbox transform of imported Text/SVG art (replaces the Move/Scale dialog). diff --git a/src/slic3r/GUI/CAD/DesignPanel.cpp b/src/slic3r/GUI/CAD/DesignPanel.cpp index 5266b1f3a9..cddecfbbb1 100644 --- a/src/slic3r/GUI/CAD/DesignPanel.cpp +++ b/src/slic3r/GUI/CAD/DesignPanel.cpp @@ -6329,6 +6329,12 @@ void DesignPanel::on_sketch_step(int mode, int step, int picks) if (step == 0 && picks == 0 && DesignSketchTool::Mode(mode) != DesignSketchTool::Mode::Select && !text.IsEmpty()) text += _L(" · Esc goes back to Select"); + // Badges are clickable and nothing said so — a glyph reads as decoration until something + // tells you it is a target. Only in Select mode (the only mode where the click is wired) and + // only once a constraint exists, so it never advertises a badge that is not on screen. + if (DesignSketchTool::Mode(mode) == DesignSketchTool::Mode::Select && m_viewport != nullptr && + m_viewport->sketch_constraint_count() > 0 && !text.IsEmpty()) + text += _L(" · click a constraint badge to remove it"); m_sketch_step = text; if (text.IsEmpty() || m_status == nullptr) return; m_status->SetForegroundColour(wxNullColour); @@ -7842,7 +7848,9 @@ void DesignPanel::apply_live_constraint(SketchConstraintType type) } m_viewport->request_repaint(); m_status->SetForegroundColour(wxNullColour); - set_status(_L("Applied constraint")); + // Say where it went and how to undo it, here at the moment of applying: the hint line + // only refreshes when the step tuple changes, which applying a constraint does not. + set_status(_L("Applied constraint · its badge is on the sketch — click the badge to remove it")); m_status->Refresh(); }; diff --git a/src/slic3r/GUI/CAD/DesignSketchTool.cpp b/src/slic3r/GUI/CAD/DesignSketchTool.cpp index f205b800eb..68c2196912 100644 --- a/src/slic3r/GUI/CAD/DesignSketchTool.cpp +++ b/src/slic3r/GUI/CAD/DesignSketchTool.cpp @@ -2382,7 +2382,11 @@ bool DesignSketchTool::remove_constraint_near(const Vec2d& p) } if (best < 0) return false; m_constraints.erase(m_constraints.begin() + best); - solve_sketch_entities(m_entities, m_constraints); + // resolve_live(), not a bare solve: it is the path that recomputes the DoF, clears the + // per-entity conflict flags and fires on_solve_state. Solving directly would relax the + // geometry while leaving the DoF readout and any red over-constrained tint stale — the + // readout would still describe the constraint that was just deleted. + resolve_live(); m_glyph_hits.clear(); // stale until the next render rebuilds them return true; }