diff --git a/src/slic3r/GUI/DesignCanvas.cpp b/src/slic3r/GUI/DesignCanvas.cpp index 43ccacb67e..8fa0278ecb 100644 --- a/src/slic3r/GUI/DesignCanvas.cpp +++ b/src/slic3r/GUI/DesignCanvas.cpp @@ -1338,6 +1338,16 @@ bool DesignCanvas::is_constraining_entities() const return m_sketch_tool.is_constraining_entities(); } +int DesignCanvas::sketch_selection_count() const +{ + return int(m_sketch_tool.selection().size()); +} + +bool DesignCanvas::sketch_first_selected_type(SketchEntity::Type& out) const +{ + return m_sketch_tool.first_selected_type(out); +} + bool DesignCanvas::selected_constrain_entities(int& e0, int& e1) const { return m_sketch_tool.selected_constrain_entities(e0, e1); diff --git a/src/slic3r/GUI/DesignCanvas.hpp b/src/slic3r/GUI/DesignCanvas.hpp index 7d7487325e..cc6d1b2184 100644 --- a/src/slic3r/GUI/DesignCanvas.hpp +++ b/src/slic3r/GUI/DesignCanvas.hpp @@ -261,6 +261,10 @@ public: // Entity-aware Constrain (Fase 4.2): pick Line entities of a committed sketch. void begin_constrain_entities(const std::vector& ents, const SketchPlane& plane); bool is_constraining_entities() const; + // Sketch selection, for the offer menu: how many entities are selected and what the first + // one is. Returns 0 when nothing is selected. + int sketch_selection_count() const; + bool sketch_first_selected_type(SketchEntity::Type& out) const; // In-canvas bbox transform of imported Text/SVG art (replaces the Move/Scale dialog). void begin_imported_transform(int feat, diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index f379934667..3d7128d560 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -5534,8 +5534,29 @@ bool DesignPanel::sketch_map_applies() const // sketch_plane_from_selection, which deliberately uses m_pick_face. snaporca-3a2.) int DesignPanel::offer_selection_kind() const { - if (sketch_map_applies()) + if (sketch_map_applies()) { + // Classify WHAT is selected in the sketch, rather than collapsing every sketch state to + // SkNone. The offer table has always described verbs for a selected line, arc, point or + // pair — Trim, Extend, Fillet, Chamfer, Offset, Mirror, the arrays, Move/Rotate/Scale, + // Constrain, Delete — but nothing ever RETURNED those kinds, so all thirteen were + // unreachable from the menu and the sketch-editing vocabulary did not exist in the one + // place this app tells users to look. A user comparing against Onshape reported exactly + // that about constraints (OrcaSlicer PR #15238). + const int n = m_viewport ? m_viewport->sketch_selection_count() : 0; + if (n >= 2) return int(OfferSel::Sk2Ent); + if (n == 1) { + SketchEntity::Type t = SketchEntity::Type::Line; + if (m_viewport && m_viewport->sketch_first_selected_type(t)) { + switch (t) { + case SketchEntity::Type::Line: return int(OfferSel::SkLine); + case SketchEntity::Type::Point: return int(OfferSel::SkPoint); + // Arc, Circle, Ellipse, EllipseArc, BSpline all take the curve vocabulary. + default: return int(OfferSel::SkArc); + } + } + } return int(OfferSel::SkNone); + } const int nb = int(m_doc.bodies.size()); if (m_sel_solid_vertex && m_sel_solid_body >= 0 && m_sel_solid_body < nb) diff --git a/src/slic3r/GUI/DesignSketchTool.cpp b/src/slic3r/GUI/DesignSketchTool.cpp index bcfd6bd311..f80d34cac3 100644 --- a/src/slic3r/GUI/DesignSketchTool.cpp +++ b/src/slic3r/GUI/DesignSketchTool.cpp @@ -9432,11 +9432,17 @@ bool DesignSketchTool::on_mouse_impl(wxMouseEvent& evt, GLCanvas3D& canvas) return true; } if (evt.RightDown()) { - // Merely dropping a selection is not a gesture terminator: the m_right_consumed flag - // this return value feeds means "the tool USED this right-click", and suppressing the - // offer menu on a plain deselection would leave no way to reach the right-click menu - // again once any geometry exists. So clear, but hand the click back. - clear_selection(); + // Hand the click back (return false) so the offer opens: the m_right_consumed flag + // this return value feeds means "the tool USED this right-click", and a plain + // right-click in Select mode is not a gesture terminator. + // + // But do NOT drop the selection on the way out. The offer menu describes WHAT IS + // SELECTED, so clearing first guaranteed it could only ever describe nothing: select + // a line, right-click, and the sketch verbs — Trim, Extend, Fillet, Chamfer, Offset, + // Mirror, the arrays, Constrain — were all greyed, because by the time the menu was + // built the line was no longer selected. Reported from the machine as "selected a + // line, right-click exit from selection: only create and reference are usable". + // Deselecting still has a gesture: left-click on empty space, a few lines above. return false; } return false; // let drag orbit the camera diff --git a/src/slic3r/GUI/DesignSketchTool.hpp b/src/slic3r/GUI/DesignSketchTool.hpp index 0f80134cc5..c52429b9e4 100644 --- a/src/slic3r/GUI/DesignSketchTool.hpp +++ b/src/slic3r/GUI/DesignSketchTool.hpp @@ -425,6 +425,15 @@ public: // Selection (Mode::Select): pick points/lines/arcs/circles of the in-session // sketch; Shift/Ctrl extends, double-click grabs the whole connected loop. const std::vector& selection() const { return m_selection; } + // Type of the first selected entity. False when nothing is selected, so the offer menu can + // tell a line from an arc from a point and stop collapsing every sketch selection to "none". + bool first_selected_type(SketchEntity::Type& out) const { + if (m_selection.empty()) return false; + const int i = m_selection.front(); + if (i < 0 || i >= int(m_entities.size())) return false; + out = m_entities[i].type; + return true; + } void clear_selection(); void delete_selected(); // erase selected entities // Abort any pending/queued draw-then-edit value-field sequence. Removing an entity that