diff --git a/src/slic3r/GUI/DesignOffer.hpp b/src/slic3r/GUI/DesignOffer.hpp index 9213dfef0b..6fb95d48e3 100644 --- a/src/slic3r/GUI/DesignOffer.hpp +++ b/src/slic3r/GUI/DesignOffer.hpp @@ -165,6 +165,10 @@ static const OfferVerb kOfferVerbs[] = { {"sk_scale", "Scale", 5, nullptr, "fly:design_move#2", nullptr, 0x000f0000u, 0, 0, false, true, "Move", "design_scale", "Pick entities, then drag the handle or click the factor; click empty to apply"}, {"sk_dimension", "Dimension", 6, "D", "key:D", nullptr, 0x000f8000u, 0, 0, false, true, nullptr, "design_dimension", "Dimension — click 2 points or an entity"}, {"sk_constrain", "Constrain", 6, "K", "key:K", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_constrain", "Constrain the selected sketch entities to each other"}, + // Same verb, model-mode vocabulary: offered when a SKETCH is selected (bit 14, SkLoop), the + // state a user is in right after finishing one. Without this row the only way in was the + // toolbar icon, and constraints read as absent — see the Onshape-comparison report. + {"constrain", "Constrain sketch", 7, nullptr, "btn:constrain", "Select a sketch to constrain it", 0x00004000u, 0, 1, false, false, nullptr, "design_constrain", "Add dimensions and relations (coincident, tangent, parallel...) to the selected sketch"}, {"sk_construct", "Construction", 6, "Q", "key:Q", nullptr, 0x000b8000u, 0, 0, false, true, nullptr, nullptr, "Toggle construction: geometry that guides but is never built"}, {"sk_extend", "Extend", 7, "X", "key:X", nullptr, 0x000b0000u, 0, 0, false, true, nullptr, "design_extend", "Extend — click a line/arc to extend it"}, {"sk_delete", "Delete", 7, "Del", "btn:delete", nullptr, 0x000f0000u, 0, 0, false, true, nullptr, "design_delete", "Delete the selected sketch entities"}, diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index 3ebc63d2a0..434dc3b945 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -994,6 +994,16 @@ DesignPanel::DesignPanel(wxWindow* parent) m_verb_actions["btn:delete_body"] = [this] { on_delete_body(); }; m_verb_actions["btn:edit"] = [this] { on_edit_feature(); }; m_verb_actions["btn:mass"] = [this] { on_mass_properties(); }; + // Reachable from the offer menu on a SELECTED SKETCH, not only from the toolbar icon. + // A user evaluating against Onshape reported that "adding constraints seems to be + // missing" — with nineteen constraint types and a solver shipped. The only paths in + // were an icon-only button and a sketch-mode-only offer row filed under "Reference", + // so after finishing a sketch there was no affordance where users actually look. + m_verb_actions["btn:constrain"] = [this] { + on_begin_constrain(); + if (m_viewport && (m_viewport->is_constraining() || m_viewport->is_constraining_entities())) + set_ui_mode(UiMode::Constrain); + }; // Dress-up: finishing operations on the faces and edges of an existing solid — nothing // that moves a body (see the Placement drawer) and nothing that creates geometry. @@ -6872,6 +6882,15 @@ bool DesignPanel::enter_constrain_inline() void DesignPanel::on_begin_constrain(int sel_override) { int sel = (sel_override >= 0) ? sel_override : tree_selection(); + // Fall back to the sketch owning the region picked in the viewport. The offer menu reaches + // this verb from a SkLoop selection (a region clicked on screen), which carries no tree + // selection — without this, choosing "Constrain sketch" from the offer would answer + // "Select a sketch in the tree first" about a sketch the user has visibly selected. + if ((sel == wxNOT_FOUND || sel >= int(m_doc.features.size())) && m_sel_sketch_feat >= 0 + && m_sel_sketch_feat < int(m_doc.features.size())) { + sel = m_sel_sketch_feat; + set_tree_selection(sel); // keep the tree in step with what the viewport says + } if (sel == wxNOT_FOUND || sel >= int(m_doc.features.size())) { m_status->SetForegroundColour(wxColour(235, 110, 110)); set_status(_L("Select a sketch in the tree first"));