diff --git a/docs/ux/mockups/gen_offer_table.py b/docs/ux/mockups/gen_offer_table.py index 2ef0938c93..ebb847a4ed 100644 --- a/docs/ux/mockups/gen_offer_table.py +++ b/docs/ux/mockups/gen_offer_table.py @@ -87,6 +87,11 @@ def main(): " int need_sketches;", " bool need_sheet;", " bool sketch_mode; // belongs to the sketch-mode vocabulary, not the model one", + " // Second level INSIDE a row, for tools that come in variants: \"Rectangle\" holds corner,", + " // centre, oblique and rounded. nullptr = sits directly in the row. Keeps the row's own", + " // address fixed (L4.1) while the variants hang one level below it, mirroring the toolbar's", + " // grouping instead of flattening 19 create tools into one wall.", + " const char* family;", "};", "", "// Row labels, in ratified order.", @@ -106,12 +111,13 @@ def main(): mask |= 1 << sels.index(a) n = v.get("needs") or {} lines.append( - " {%s, %s, %d, %s, %s, %s, 0x%08xu, %d, %d, %s, %s}," % ( + " {%s, %s, %d, %s, %s, %s, 0x%08xu, %d, %d, %s, %s, %s}," % ( cstr(v["id"]), cstr(v["name"]), slots.index(v["slot"]), cstr(v.get("key")), cstr(v.get("action")), cstr(v.get("refusal")), mask, n.get("bodies", 0), n.get("sketches", 0), "true" if n.get("sheet") else "false", - "true" if v.get("mode") == "sketch" else "false")) + "true" if v.get("mode") == "sketch" else "false", + cstr(v.get("family")))) lines += [ "};", f"static const int kOfferVerbCount = {len(A['verbs'])};", diff --git a/docs/ux/tool_atlas.json b/docs/ux/tool_atlas.json index 600d3eee62..95e407bb26 100644 --- a/docs/ux/tool_atlas.json +++ b/docs/ux/tool_atlas.json @@ -954,11 +954,33 @@ "needs": {}, "refusal": null, "gui": true, - "action": "key:L" + "action": "key:L", + "family": "Line" + }, + { + "id": "sk_polyline", + "name": "Polyline", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_line#1", + "mode": "sketch", + "family": "Line" }, { "id": "sk_rect", - "name": "Rectangle", + "name": "Corner rectangle", "slot": "create", "key": "R", "feature": "Sketch", @@ -974,11 +996,75 @@ "needs": {}, "refusal": null, "gui": true, - "action": "key:R" + "action": "key:R", + "family": "Rectangle" + }, + { + "id": "sk_rect_center", + "name": "Centre rectangle", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_rect#1", + "mode": "sketch", + "family": "Rectangle" + }, + { + "id": "sk_rect_oblique", + "name": "Oblique rectangle", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_rect#2", + "mode": "sketch", + "family": "Rectangle" + }, + { + "id": "sk_rect_rounded", + "name": "Rounded rectangle", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_rect#3", + "mode": "sketch", + "family": "Rectangle" }, { "id": "sk_circle", - "name": "Circle", + "name": "Centre circle", "slot": "create", "key": "C", "feature": "Sketch", @@ -994,11 +1080,54 @@ "needs": {}, "refusal": null, "gui": true, - "action": "key:C" + "action": "key:C", + "family": "Circle" + }, + { + "id": "sk_circle_2pt", + "name": "2-point circle", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_circle#1", + "mode": "sketch", + "family": "Circle" + }, + { + "id": "sk_circle_3pt", + "name": "3-point circle", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_circle#2", + "mode": "sketch", + "family": "Circle" }, { "id": "sk_arc_t", - "name": "Arc", + "name": "3-point arc", "slot": "create", "key": "A", "feature": "Sketch", @@ -1014,7 +1143,50 @@ "needs": {}, "refusal": null, "gui": true, - "action": "key:A" + "action": "key:A", + "family": "Arc" + }, + { + "id": "sk_arc_tangent", + "name": "Tangent arc", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_arc3pt#1", + "mode": "sketch", + "family": "Arc" + }, + { + "id": "sk_arc_center", + "name": "Centre-point arc", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_arc3pt#2", + "mode": "sketch", + "family": "Arc" }, { "id": "sk_slot", @@ -1034,7 +1206,29 @@ "needs": {}, "refusal": null, "gui": true, - "action": "key:S" + "action": "key:S", + "family": "Slot" + }, + { + "id": "sk_slot_arc", + "name": "Arc slot", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_slot#1", + "mode": "sketch", + "family": "Slot" }, { "id": "sk_ellipse", @@ -1054,7 +1248,29 @@ "needs": {}, "refusal": null, "gui": true, - "action": "key:E" + "action": "key:E", + "family": "Ellipse" + }, + { + "id": "sk_ellipse_arc", + "name": "Elliptical arc", + "slot": "create", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_none", + "sk_point", + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_ellipse#1", + "mode": "sketch", + "family": "Ellipse" }, { "id": "sk_spline", @@ -1186,6 +1402,44 @@ "gui": true, "action": "key:H" }, + { + "id": "sk_array", + "name": "Linear array", + "slot": "repeat", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_array#0", + "mode": "sketch", + "family": "Array" + }, + { + "id": "sk_array_polar", + "name": "Polar array", + "slot": "repeat", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_line", + "sk_arc", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_array#1", + "mode": "sketch", + "family": "Array" + }, { "id": "sk_mirror", "name": "Mirror", @@ -1221,7 +1475,48 @@ "needs": {}, "refusal": null, "gui": true, - "action": null + "action": "fly:design_move#0", + "family": "Move" + }, + { + "id": "sk_rotate", + "name": "Rotate", + "slot": "transform", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_line", + "sk_arc", + "sk_point", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_move#1", + "mode": "sketch", + "family": "Move" + }, + { + "id": "sk_scale", + "name": "Scale", + "slot": "transform", + "key": null, + "feature": "Sketch", + "mcp": null, + "accepts": [ + "sk_line", + "sk_arc", + "sk_point", + "sk_2ent" + ], + "needs": {}, + "refusal": null, + "gui": true, + "action": "fly:design_move#2", + "mode": "sketch", + "family": "Move" }, { "id": "sk_dimension", diff --git a/src/slic3r/GUI/DesignOffer.hpp b/src/slic3r/GUI/DesignOffer.hpp index a0155a91f7..46d5b40019 100644 --- a/src/slic3r/GUI/DesignOffer.hpp +++ b/src/slic3r/GUI/DesignOffer.hpp @@ -58,6 +58,11 @@ struct OfferVerb { int need_sketches; bool need_sheet; bool sketch_mode; // belongs to the sketch-mode vocabulary, not the model one + // Second level INSIDE a row, for tools that come in variants: "Rectangle" holds corner, + // centre, oblique and rounded. nullptr = sits directly in the row. Keeps the row's own + // address fixed (L4.1) while the variants hang one level below it, mirroring the toolbar's + // grouping instead of flattening 19 create tools into one wall. + const char* family; }; // Row labels, in ratified order. @@ -74,68 +79,82 @@ static const char* const kOfferRowNames[] = { static const int kOfferRowCount = 8; static const OfferVerb kOfferVerbs[] = { - {"sketch", "Sketch", 0, "Shift+S", "key:S+S", "Click a face or a reference plane in the viewport, then a sketch tool", 0x00000403u, 0, 0, false, false}, - {"extrude", "Extrude", 1, "Shift+E", "key:S+E", "Create a sketch, or pick a solid face, first", 0x00004002u, 0, 0, false, false}, - {"revolve", "Revolve", 1, "Shift+R", "key:S+R", "Create a sketch profile to revolve first", 0x00004000u, 0, 0, false, false}, - {"sweep", "Sweep", 1, "Shift+W", "key:S+W", "Create a profile sketch to sweep first", 0x00004000u, 0, 2, false, false}, - {"loft", "Loft", 1, "Shift+L", "key:S+L", "Create at least two profile sketches to loft", 0x00004000u, 0, 2, false, false}, - {"thicken", "Thicken", 1, nullptr, "fly:material#4", "Thicken needs a solid body — add or import one first", 0x0000000au, 1, 0, false, false}, - {"rib", "Rib", 1, nullptr, "fly:material#5", "Rib needs a solid body — add or import one first", 0x00010000u, 1, 0, false, false}, - {"boolean", "Combine", 1, "Shift+B", "key:S+B", "Boolean needs two bodies — create or import a second solid", 0x00000200u, 2, 0, false, false}, - {"surf_extrude", "Surface Extrude", 1, "Shift+G", "key:S+G", "Create a sketch first", 0x00004000u, 0, 0, false, false}, - {"surf_revolve", "Surface Revolve", 1, nullptr, "fly:surface#1", "Create a sketch profile to revolve first", 0x00004000u, 0, 0, false, false}, - {"surf_loft", "Surface Loft", 1, nullptr, "fly:surface#2", "Create at least two profile sketches to loft", 0x00004000u, 0, 2, false, false}, - {"surf_fill", "Surface Fill", 1, nullptr, "fly:surface#3", "Create a closed sketch first", 0x00004000u, 0, 0, false, false}, - {"thicken_surf", "Thicken Surface", 1, nullptr, "fly:surface#5", "target is not a sheet body", 0x00000100u, 0, 0, true, false}, - {"hole", "Hole", 2, "Shift+H", "key:S+H", "Pick a face or a plane to drill into", 0x00000402u, 1, 0, false, false}, - {"thread", "Thread", 2, "Shift+T", "key:S+T", "Pick a cylindrical surface (bore / outer) or a circular edge for a thread", 0x00000024u, 1, 0, false, false}, - {"shell", "Shell", 2, "Shift+K", "key:S+K", "Shell needs a solid body", 0x00000082u, 1, 0, false, false}, - {"cut", "Cut", 2, "Shift+X", "key:S+X", "Create a solid body to cut first", 0x00000480u, 1, 0, false, false}, - {"split", "Split", 2, nullptr, nullptr, "Split needs a solid body", 0x00000080u, 1, 0, false, false}, - {"fillet", "Fillet", 3, "Shift+F", "key:S+F", "Pick an edge to round", 0x000000b2u, 1, 0, false, false}, - {"chamfer", "Chamfer", 3, nullptr, "key:S+F", "Pick an edge to bevel", 0x000000b2u, 1, 0, false, false}, - {"draft", "Draft", 3, "Shift+D", "key:S+D", "Pick a face to taper", 0x0000000au, 1, 0, false, false}, - {"surf_offset", "Surface Offset", 3, nullptr, "fly:surface#4", "target is not a sheet body", 0x00000100u, 0, 0, true, false}, - {"pattern", "Pattern", 4, "Shift+N", "key:S+N", "Create a solid body to pattern first", 0x00006082u, 1, 0, false, false}, - {"mirror", "Mirror", 4, "Shift+Z", "key:S+Z", "Mirror needs a body — add or import one first", 0x00000480u, 1, 0, false, false}, - {"pat_curve", "Pattern on Curve", 4, nullptr, nullptr, "Pattern on curve needs a body and a curve", 0x00000090u, 1, 0, false, false}, - {"transform", "Move", 5, "Shift+Y", "key:S+Y", "Transform needs a body — add or import one first", 0x00002180u, 1, 0, false, false}, - {"mate", "Mate", 5, nullptr, "fly:placement#2", "A mate needs two coordinate systems", 0x00001202u, 2, 0, false, false}, - {"align", "Align to", 5, nullptr, nullptr, "Align needs a body", 0x00000002u, 1, 0, false, false}, - {"plane", "Plane", 6, "Shift+P", "key:S+P", nullptr, 0x00000453u, 0, 0, false, false}, - {"axis", "Axis", 6, "Shift+A", "key:S+A", nullptr, 0x00000057u, 0, 0, false, false}, - {"coordsys_v", "Coord Sys", 6, "Shift+C", "key:S+C", nullptr, 0x00000043u, 0, 0, false, false}, - {"helix", "Helix", 6, nullptr, "fly:plane#3", nullptr, 0x00000405u, 0, 0, false, false}, - {"project", "Project", 6, nullptr, "fly:plane#4", "Project needs a body — add or import one first", 0x00000482u, 1, 0, false, false}, - {"measure", "Measure", 6, nullptr, nullptr, nullptr, 0x000b03feu, 0, 0, false, false}, - {"mass_props", "Mass", 6, nullptr, "btn:mass", nullptr, 0x00000080u, 1, 0, false, false}, - {"interference", "Interference", 6, nullptr, nullptr, nullptr, 0x00000200u, 2, 0, false, false}, - {"edit_feature", "Edit", 7, nullptr, "btn:edit", nullptr, 0x00007d8eu, 0, 0, false, false}, - {"delete_face", "Delete Face", 7, nullptr, "fly:dressup#3", "Delete Face needs a body — add or import one first", 0x0000000eu, 1, 0, false, false}, - {"colour", "Colour", 7, nullptr, "btn:colour", nullptr, 0x00000180u, 1, 0, false, false}, - {"delete", "Delete", 7, "Del", "btn:delete", nullptr, 0x000f7f80u, 0, 0, false, false}, - {"sk_line_t", "Line", 0, "L", "key:L", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_rect", "Rectangle", 0, "R", "key:R", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_circle", "Circle", 0, "C", "key:C", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_arc_t", "Arc", 0, "A", "key:A", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_slot", "Slot", 0, "S", "key:S", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_ellipse", "Ellipse", 0, "E", "key:E", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_spline", "Spline", 0, "B", "key:B", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_polygon", "Polygon", 0, "G", "key:G", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_point_t", "Point", 0, "P", "key:P", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_offset", "Offset", 1, "O", "key:O", nullptr, 0x000b0000u, 0, 0, false, true}, - {"sk_trim", "Trim", 2, "T", "key:T", nullptr, 0x000b0000u, 0, 0, false, true}, - {"sk_fillet", "Fillet", 3, "F", "key:F", nullptr, 0x00090000u, 0, 0, false, true}, - {"sk_chamfer", "Chamfer", 3, "H", "key:H", nullptr, 0x00090000u, 0, 0, false, true}, - {"sk_mirror", "Mirror", 4, "M", "key:M", nullptr, 0x000b0000u, 0, 0, false, true}, - {"sk_move", "Move", 5, nullptr, nullptr, nullptr, 0x000f0000u, 0, 0, false, true}, - {"sk_dimension", "Dimension", 6, "D", "key:D", nullptr, 0x000f8000u, 0, 0, false, true}, - {"sk_constrain", "Constrain", 6, "K", "key:K", nullptr, 0x000f0000u, 0, 0, false, true}, - {"sk_construct", "Construction", 6, "Q", "key:Q", nullptr, 0x000b8000u, 0, 0, false, true}, - {"sk_extend", "Extend", 7, "X", "key:X", nullptr, 0x000b0000u, 0, 0, false, true}, - {"sk_delete", "Delete", 7, "Del", "btn:delete", nullptr, 0x000f0000u, 0, 0, false, true}, + {"sketch", "Sketch", 0, "Shift+S", "key:S+S", "Click a face or a reference plane in the viewport, then a sketch tool", 0x00000403u, 0, 0, false, false, nullptr}, + {"extrude", "Extrude", 1, "Shift+E", "key:S+E", "Create a sketch, or pick a solid face, first", 0x00004002u, 0, 0, false, false, nullptr}, + {"revolve", "Revolve", 1, "Shift+R", "key:S+R", "Create a sketch profile to revolve first", 0x00004000u, 0, 0, false, false, nullptr}, + {"sweep", "Sweep", 1, "Shift+W", "key:S+W", "Create a profile sketch to sweep first", 0x00004000u, 0, 2, false, false, nullptr}, + {"loft", "Loft", 1, "Shift+L", "key:S+L", "Create at least two profile sketches to loft", 0x00004000u, 0, 2, false, false, nullptr}, + {"thicken", "Thicken", 1, nullptr, "fly:material#4", "Thicken needs a solid body — add or import one first", 0x0000000au, 1, 0, false, false, nullptr}, + {"rib", "Rib", 1, nullptr, "fly:material#5", "Rib needs a solid body — add or import one first", 0x00010000u, 1, 0, false, false, nullptr}, + {"boolean", "Combine", 1, "Shift+B", "key:S+B", "Boolean needs two bodies — create or import a second solid", 0x00000200u, 2, 0, false, false, nullptr}, + {"surf_extrude", "Surface Extrude", 1, "Shift+G", "key:S+G", "Create a sketch first", 0x00004000u, 0, 0, false, false, nullptr}, + {"surf_revolve", "Surface Revolve", 1, nullptr, "fly:surface#1", "Create a sketch profile to revolve first", 0x00004000u, 0, 0, false, false, nullptr}, + {"surf_loft", "Surface Loft", 1, nullptr, "fly:surface#2", "Create at least two profile sketches to loft", 0x00004000u, 0, 2, false, false, nullptr}, + {"surf_fill", "Surface Fill", 1, nullptr, "fly:surface#3", "Create a closed sketch first", 0x00004000u, 0, 0, false, false, nullptr}, + {"thicken_surf", "Thicken Surface", 1, nullptr, "fly:surface#5", "target is not a sheet body", 0x00000100u, 0, 0, true, false, nullptr}, + {"hole", "Hole", 2, "Shift+H", "key:S+H", "Pick a face or a plane to drill into", 0x00000402u, 1, 0, false, false, nullptr}, + {"thread", "Thread", 2, "Shift+T", "key:S+T", "Pick a cylindrical surface (bore / outer) or a circular edge for a thread", 0x00000024u, 1, 0, false, false, nullptr}, + {"shell", "Shell", 2, "Shift+K", "key:S+K", "Shell needs a solid body", 0x00000082u, 1, 0, false, false, nullptr}, + {"cut", "Cut", 2, "Shift+X", "key:S+X", "Create a solid body to cut first", 0x00000480u, 1, 0, false, false, nullptr}, + {"split", "Split", 2, nullptr, nullptr, "Split needs a solid body", 0x00000080u, 1, 0, false, false, nullptr}, + {"fillet", "Fillet", 3, "Shift+F", "key:S+F", "Pick an edge to round", 0x000000b2u, 1, 0, false, false, nullptr}, + {"chamfer", "Chamfer", 3, nullptr, "key:S+F", "Pick an edge to bevel", 0x000000b2u, 1, 0, false, false, nullptr}, + {"draft", "Draft", 3, "Shift+D", "key:S+D", "Pick a face to taper", 0x0000000au, 1, 0, false, false, nullptr}, + {"surf_offset", "Surface Offset", 3, nullptr, "fly:surface#4", "target is not a sheet body", 0x00000100u, 0, 0, true, false, nullptr}, + {"pattern", "Pattern", 4, "Shift+N", "key:S+N", "Create a solid body to pattern first", 0x00006082u, 1, 0, false, false, nullptr}, + {"mirror", "Mirror", 4, "Shift+Z", "key:S+Z", "Mirror needs a body — add or import one first", 0x00000480u, 1, 0, false, false, nullptr}, + {"pat_curve", "Pattern on Curve", 4, nullptr, nullptr, "Pattern on curve needs a body and a curve", 0x00000090u, 1, 0, false, false, nullptr}, + {"transform", "Move", 5, "Shift+Y", "key:S+Y", "Transform needs a body — add or import one first", 0x00002180u, 1, 0, false, false, nullptr}, + {"mate", "Mate", 5, nullptr, "fly:placement#2", "A mate needs two coordinate systems", 0x00001202u, 2, 0, false, false, nullptr}, + {"align", "Align to", 5, nullptr, nullptr, "Align needs a body", 0x00000002u, 1, 0, false, false, nullptr}, + {"plane", "Plane", 6, "Shift+P", "key:S+P", nullptr, 0x00000453u, 0, 0, false, false, nullptr}, + {"axis", "Axis", 6, "Shift+A", "key:S+A", nullptr, 0x00000057u, 0, 0, false, false, nullptr}, + {"coordsys_v", "Coord Sys", 6, "Shift+C", "key:S+C", nullptr, 0x00000043u, 0, 0, false, false, nullptr}, + {"helix", "Helix", 6, nullptr, "fly:plane#3", nullptr, 0x00000405u, 0, 0, false, false, nullptr}, + {"project", "Project", 6, nullptr, "fly:plane#4", "Project needs a body — add or import one first", 0x00000482u, 1, 0, false, false, nullptr}, + {"measure", "Measure", 6, nullptr, nullptr, nullptr, 0x000b03feu, 0, 0, false, false, nullptr}, + {"mass_props", "Mass", 6, nullptr, "btn:mass", nullptr, 0x00000080u, 1, 0, false, false, nullptr}, + {"interference", "Interference", 6, nullptr, nullptr, nullptr, 0x00000200u, 2, 0, false, false, nullptr}, + {"edit_feature", "Edit", 7, nullptr, "btn:edit", nullptr, 0x00007d8eu, 0, 0, false, false, nullptr}, + {"delete_face", "Delete Face", 7, nullptr, "fly:dressup#3", "Delete Face needs a body — add or import one first", 0x0000000eu, 1, 0, false, false, nullptr}, + {"colour", "Colour", 7, nullptr, "btn:colour", nullptr, 0x00000180u, 1, 0, false, false, nullptr}, + {"delete", "Delete", 7, "Del", "btn:delete", nullptr, 0x000f7f80u, 0, 0, false, false, nullptr}, + {"sk_line_t", "Line", 0, "L", "key:L", nullptr, 0x000f8000u, 0, 0, false, true, "Line"}, + {"sk_polyline", "Polyline", 0, nullptr, "fly:design_line#1", nullptr, 0x000f8000u, 0, 0, false, true, "Line"}, + {"sk_rect", "Corner rectangle", 0, "R", "key:R", nullptr, 0x000f8000u, 0, 0, false, true, "Rectangle"}, + {"sk_rect_center", "Centre rectangle", 0, nullptr, "fly:design_rect#1", nullptr, 0x000f8000u, 0, 0, false, true, "Rectangle"}, + {"sk_rect_oblique", "Oblique rectangle", 0, nullptr, "fly:design_rect#2", nullptr, 0x000f8000u, 0, 0, false, true, "Rectangle"}, + {"sk_rect_rounded", "Rounded rectangle", 0, nullptr, "fly:design_rect#3", nullptr, 0x000f8000u, 0, 0, false, true, "Rectangle"}, + {"sk_circle", "Centre circle", 0, "C", "key:C", nullptr, 0x000f8000u, 0, 0, false, true, "Circle"}, + {"sk_circle_2pt", "2-point circle", 0, nullptr, "fly:design_circle#1", nullptr, 0x000f8000u, 0, 0, false, true, "Circle"}, + {"sk_circle_3pt", "3-point circle", 0, nullptr, "fly:design_circle#2", nullptr, 0x000f8000u, 0, 0, false, true, "Circle"}, + {"sk_arc_t", "3-point arc", 0, "A", "key:A", nullptr, 0x000f8000u, 0, 0, false, true, "Arc"}, + {"sk_arc_tangent", "Tangent arc", 0, nullptr, "fly:design_arc3pt#1", nullptr, 0x000f8000u, 0, 0, false, true, "Arc"}, + {"sk_arc_center", "Centre-point arc", 0, nullptr, "fly:design_arc3pt#2", nullptr, 0x000f8000u, 0, 0, false, true, "Arc"}, + {"sk_slot", "Slot", 0, "S", "key:S", nullptr, 0x000f8000u, 0, 0, false, true, "Slot"}, + {"sk_slot_arc", "Arc slot", 0, nullptr, "fly:design_slot#1", nullptr, 0x000f8000u, 0, 0, false, true, "Slot"}, + {"sk_ellipse", "Ellipse", 0, "E", "key:E", nullptr, 0x000f8000u, 0, 0, false, true, "Ellipse"}, + {"sk_ellipse_arc", "Elliptical arc", 0, nullptr, "fly:design_ellipse#1", nullptr, 0x000f8000u, 0, 0, false, true, "Ellipse"}, + {"sk_spline", "Spline", 0, "B", "key:B", nullptr, 0x000f8000u, 0, 0, false, true, nullptr}, + {"sk_polygon", "Polygon", 0, "G", "key:G", nullptr, 0x000f8000u, 0, 0, false, true, nullptr}, + {"sk_point_t", "Point", 0, "P", "key:P", nullptr, 0x000f8000u, 0, 0, false, true, nullptr}, + {"sk_offset", "Offset", 1, "O", "key:O", nullptr, 0x000b0000u, 0, 0, false, true, nullptr}, + {"sk_trim", "Trim", 2, "T", "key:T", nullptr, 0x000b0000u, 0, 0, false, true, nullptr}, + {"sk_fillet", "Fillet", 3, "F", "key:F", nullptr, 0x00090000u, 0, 0, false, true, nullptr}, + {"sk_chamfer", "Chamfer", 3, "H", "key:H", nullptr, 0x00090000u, 0, 0, false, true, nullptr}, + {"sk_array", "Linear array", 4, nullptr, "fly:design_array#0", nullptr, 0x000b0000u, 0, 0, false, true, "Array"}, + {"sk_array_polar", "Polar array", 4, nullptr, "fly:design_array#1", nullptr, 0x000b0000u, 0, 0, false, true, "Array"}, + {"sk_mirror", "Mirror", 4, "M", "key:M", nullptr, 0x000b0000u, 0, 0, false, true, nullptr}, + {"sk_move", "Move", 5, nullptr, "fly:design_move#0", nullptr, 0x000f0000u, 0, 0, false, true, "Move"}, + {"sk_rotate", "Rotate", 5, nullptr, "fly:design_move#1", nullptr, 0x000f0000u, 0, 0, false, true, "Move"}, + {"sk_scale", "Scale", 5, nullptr, "fly:design_move#2", nullptr, 0x000f0000u, 0, 0, false, true, "Move"}, + {"sk_dimension", "Dimension", 6, "D", "key:D", nullptr, 0x000f8000u, 0, 0, false, true, nullptr}, + {"sk_constrain", "Constrain", 6, "K", "key:K", nullptr, 0x000f0000u, 0, 0, false, true, nullptr}, + {"sk_construct", "Construction", 6, "Q", "key:Q", nullptr, 0x000b8000u, 0, 0, false, true, nullptr}, + {"sk_extend", "Extend", 7, "X", "key:X", nullptr, 0x000b0000u, 0, 0, false, true, nullptr}, + {"sk_delete", "Delete", 7, "Del", "btn:delete", nullptr, 0x000f0000u, 0, 0, false, true, nullptr}, }; -static const int kOfferVerbCount = 60; +static const int kOfferVerbCount = 74; }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index 3c9cab6bc8..cbfa26b244 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -1077,6 +1077,17 @@ DesignPanel::DesignPanel(wxWindow* parent) fo->modes.push_back(v.mode); fo->hints.push_back(v.hint); fo->icon_names.emplace_back(v.icon); + // …and the offer reaches the same tool by its ratified address, exactly as + // feat_dropdown does for the model verbs. Without this the offer could name a + // family but only ever arm its FIRST tool: picking "Rectangle" ran key:R and + // gave you a corner rectangle, with oblique and rounded unreachable. + // Keyed on the icon id (already unique per family) so no call site grows an + // argument. snaporca-6vs. + const DesignSketchTool::Mode mode = v.mode; + const wxString hint = v.hint; + m_verb_actions["fly:" + std::string(def_icon) + "#" + + std::to_string(fo->modes.size() - 1)] = + [mode, hint, select_tool] { select_tool(mode, hint); }; } fo->btn = b; fo->drop.Create(b); @@ -5140,9 +5151,28 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos) ->Enable(live[0]->action != nullptr); bound.push_back(live[0]); } else { + // Two levels, not one. A verb with a `family` joins a nested submenu of that name + // (Rectangle -> corner / centre / oblique / rounded); one without sits directly in + // the row. Families keep the order of their first member, so the row's layout is + // stable across selections — the whole point of a fixed address. auto* sub = new wxMenu(); + std::vector> groups; // insertion-ordered for (const OfferVerb* v : live) { - sub->Append(base + int(bound.size()), label(*v))->Enable(v->action != nullptr); + wxMenu* target = sub; + if (v->family && *v->family) { + auto it = std::find_if(groups.begin(), groups.end(), + [&](const auto& g) { return g.first == v->family; }); + if (it == groups.end()) { + auto* g = new wxMenu(); + groups.emplace_back(v->family, g); + sub->AppendSubMenu(g, tr(v->family)); + target = g; + } else { + target = it->second; + } + } + target->Append(base + int(bound.size()), label(*v)) + ->Enable(v->action != nullptr); bound.push_back(v); } menu.AppendSubMenu(sub, fam);