From 582eae5ba388d5ab246795eecbf9dc401334436e Mon Sep 17 00:00:00 2001 From: George Peden Date: Sun, 7 Sep 2025 11:59:44 -1000 Subject: [PATCH 01/11] Add missing hints for ConstrainRadiam tool - Add hints entry for Sketcher_ConstrainRadiam in hints array - ConstrainRadiam now shows 'pick circle or arc' hint like other radius/diameter tools - Fixes part of issue #22282 - missing radius constraint hints --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 8c37e47311..8d207abd38 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1230,6 +1230,10 @@ private: .selectionStep = 0, .hints = {{QObject::tr("%1 pick circle or arc"), {Gui::InputHint::UserInput::MouseLeft}}}}, + {.commandName = "Sketcher_ConstrainRadiam", + .selectionStep = 0, + .hints = {{QObject::tr("%1 pick circle or arc"), {Gui::InputHint::UserInput::MouseLeft}}}}, + // Angle {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 0, From 871ee4ca32602c3505448f1c6163d78363d471a5 Mon Sep 17 00:00:00 2001 From: George Peden Date: Sun, 7 Sep 2025 12:31:14 -1000 Subject: [PATCH 02/11] Add context-aware hints for ConstrainAngle tool - Add special case for Sketcher_ConstrainAngle in getToolHints() - Hints now remember what user has selected to provide appropriate guidance - Step 0: 'pick edge or first point' (covers all workflows) - Step 1: Context-aware based on first selection: - If point first: 'pick first edge' (point+edge+edge workflow) - If line first: 'pick second line or point' (line+line or edge+point+edge) - Step 2: Context-aware based on selection history: - Point+edge+edge: 'pick second edge' - Edge+point+edge: 'pick second edge' - Fixes part of issue #22282 - missing angle constraint hints --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 32 +++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 8d207abd38..1cb68b96f4 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1125,6 +1125,30 @@ public: } } + // Special case for Sketcher_ConstrainAngle to generate context-aware hints + if (commandName == "Sketcher_ConstrainAngle") { + if (selectionStep == 0) { + return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selectionStep == 1 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Edge + Edge workflow + return {{QObject::tr("%1 pick first edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else { + // Could be Line + Line or Edge + Point + Edge workflow + // Tell user what they can actually pick next + return {{QObject::tr("%1 pick second line or point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } else if (selectionStep == 2 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Edge + Edge workflow + return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { + // Edge + Point + Edge workflow + return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } + } + // For everything else, use the static table return lookupConstraintHints(commandName, selectionStep); } @@ -1237,11 +1261,15 @@ private: // Angle {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick line"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second line"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr("%1 pick second element"), {Gui::InputHint::UserInput::MouseLeft}}}}, + + {.commandName = "Sketcher_ConstrainAngle", + .selectionStep = 2, + .hints = {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, // Symmetry {.commandName = "Sketcher_ConstrainSymmetric", From 17533deb50b70cda8c57798080155e059528671b Mon Sep 17 00:00:00 2001 From: George Peden Date: Sun, 7 Sep 2025 12:47:24 -1000 Subject: [PATCH 03/11] Add context-aware hints for remaining constraint tools - Add context-aware hints for ConstrainPerpendicular - Add context-aware hints for ConstrainTangent - Add context-aware hints for ConstrainSymmetric - All hints now remember user selections to provide appropriate guidance - Covers all workflows mentioned in issue #22282 feedback - Completes comprehensive coverage of missing constraint hints --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 66 +++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 1cb68b96f4..630f19189f 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1149,6 +1149,72 @@ public: } } + // Special case for Sketcher_ConstrainPerpendicular to generate context-aware hints + if (commandName == "Sketcher_ConstrainPerpendicular") { + if (selectionStep == 0) { + return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selectionStep == 1 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Edge + Edge workflow + return {{QObject::tr("%1 pick first edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else { + // Edge + Edge workflow + return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } else if (selectionStep == 2 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Edge + Edge workflow + return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } + } + + // Special case for Sketcher_ConstrainTangent to generate context-aware hints + if (commandName == "Sketcher_ConstrainTangent") { + if (selectionStep == 0) { + return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selectionStep == 1 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Edge + Edge workflow + return {{QObject::tr("%1 pick first edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else { + // Could be Edge + Edge or Edge + Point + Edge workflow + return {{QObject::tr("%1 pick second edge or point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } else if (selectionStep == 2 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Edge + Edge workflow + return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { + // Edge + Point + Edge workflow + return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } + } + + // Special case for Sketcher_ConstrainSymmetric to generate context-aware hints + if (commandName == "Sketcher_ConstrainSymmetric") { + if (selectionStep == 0) { + return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selectionStep == 1 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Edge + Point or Point + Point + Edge/Point workflow + return {{QObject::tr("%1 pick edge or second point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else { + // Edge + Point workflow + return {{QObject::tr("%1 pick symmetry point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } else if (selectionStep == 2 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { + // Point + Point + Edge/Point workflow + return {{QObject::tr("%1 pick symmetry line or point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && !isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { + // Point + Edge + Point workflow + return {{QObject::tr("%1 pick symmetry point"), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } + } + // For everything else, use the static table return lookupConstraintHints(commandName, selectionStep); } From 8b36da67825eac6b2555a7a01cf0a6891ebada1b Mon Sep 17 00:00:00 2001 From: George Peden Date: Sun, 7 Sep 2025 19:07:09 -1000 Subject: [PATCH 04/11] Refactor hint strings to use constants - Add class-level constants for all hint texts - Replace all hardcoded strings in static table and context-aware hints - Eliminate duplication and improve maintainability - Addresses Copilot PR review feedback on code duplication All hint strings are now defined once and reused consistently. --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 84 +++++++++++++-------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 630f19189f..0bcb98517f 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -997,6 +997,30 @@ protected: class DrawSketchHandlerGenConstraint: public DrawSketchHandler { public: + // Helper constants for all hint texts + static constexpr const char* PICK_EDGE_OR_FIRST_POINT = "%1 pick edge or first point"; + static constexpr const char* PICK_FIRST_EDGE = "%1 pick first edge"; + static constexpr const char* PICK_SECOND_EDGE = "%1 pick second edge"; + static constexpr const char* PICK_SECOND_LINE_OR_POINT = "%1 pick second line or point"; + static constexpr const char* PICK_SECOND_EDGE_OR_POINT = "%1 pick second edge or point"; + static constexpr const char* PICK_SYMMETRY_POINT = "%1 pick symmetry point"; + static constexpr const char* PICK_SYMMETRY_LINE_OR_POINT = "%1 pick symmetry line or point"; + static constexpr const char* PICK_SECOND_LINE = "%1 pick second line"; + static constexpr const char* PICK_SECOND_POINT_OR_EDGE = "%1 pick second point or edge"; + static constexpr const char* PICK_POINT_OR_EDGE = "%1 pick point or edge"; + static constexpr const char* PICK_SECOND_POINT = "%1 pick second point"; + static constexpr const char* PICK_EDGE = "%1 pick edge"; + static constexpr const char* PICK_CIRCLE_OR_ARC = "%1 pick circle or arc"; + static constexpr const char* PICK_EDGE_TO_BLOCK = "%1 pick edge to block"; + static constexpr const char* PICK_POINT_TO_LOCK = "%1 pick point to lock"; + static constexpr const char* PICK_POINT_OR_CURVE = "%1 pick point or curve"; + static constexpr const char* PICK_SECOND_POINT_OR_CURVE = "%1 pick second point or curve"; + static constexpr const char* PICK_OPTIONAL_TANGENT_POINT = "%1 pick optional tangent point"; + static constexpr const char* PICK_OPTIONAL_PERPENDICULAR_POINT = "%1 pick optional perpendicular point"; + static constexpr const char* PICK_LINE = "%1 pick line"; + static constexpr const char* PICK_SYMMETRY_LINE = "%1 pick symmetry line"; + static constexpr const char* PICK_POINT = "%1 pick point"; + explicit DrawSketchHandlerGenConstraint(CmdSketcherConstraint* _cmd) : cmd(_cmd) , seqIndex(0) @@ -1237,97 +1261,97 @@ private: // Coincident {.commandName = "Sketcher_ConstrainCoincidentUnified", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainCoincidentUnified", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, // Distance X/Y {.commandName = "Sketcher_ConstrainDistanceX", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainDistanceX", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainDistanceY", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainDistanceY", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, // Horizontal/Vertical {.commandName = "Sketcher_ConstrainHorizontal", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainHorizontal", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainVertical", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainVertical", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainHorVer", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainHorVer", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, // Block/Lock {.commandName = "Sketcher_ConstrainBlock", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge to block"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE_TO_BLOCK), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainLock", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick point to lock"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_POINT_TO_LOCK), {Gui::InputHint::UserInput::MouseLeft}}}}, // Coincident (individual) {.commandName = "Sketcher_ConstrainCoincident", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick point or curve"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_POINT_OR_CURVE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainCoincident", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point or curve"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT_OR_CURVE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainEqual", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainEqual", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, // Radius/Diameter {.commandName = "Sketcher_ConstrainRadius", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick circle or arc"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_CIRCLE_OR_ARC), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainDiameter", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick circle or arc"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_CIRCLE_OR_ARC), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainRadiam", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick circle or arc"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_CIRCLE_OR_ARC), {Gui::InputHint::UserInput::MouseLeft}}}}, // Angle {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 1, @@ -1335,7 +1359,7 @@ private: {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 2, - .hints = {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, // Symmetry {.commandName = "Sketcher_ConstrainSymmetric", @@ -1344,7 +1368,7 @@ private: {.commandName = "Sketcher_ConstrainSymmetric", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainSymmetric", .selectionStep = 2, @@ -1353,11 +1377,11 @@ private: // Tangent {.commandName = "Sketcher_ConstrainTangent", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainTangent", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainTangent", .selectionStep = 2, @@ -1366,11 +1390,11 @@ private: // Perpendicular {.commandName = "Sketcher_ConstrainPerpendicular", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainPerpendicular", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainPerpendicular", .selectionStep = 2, @@ -1388,11 +1412,11 @@ private: // Distance {.commandName = "Sketcher_ConstrainDistance", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainDistance", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, }; } From 584472f779280ffa8231423e86e4b9f174c17dd9 Mon Sep 17 00:00:00 2001 From: George Peden Date: Mon, 8 Sep 2025 10:16:23 -1000 Subject: [PATCH 05/11] Refactor: Replace all literal hint strings with constants - Added class-level static constexpr constants for all hint strings - Replaced all QObject::tr("%1 pick...") with constants in both DrawSketchHandlerGenConstraint and DrawSketchHandlerDimension classes - Eliminates string duplication and improves maintainability - Addresses Copilot's PR feedback on string refactoring --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 69 +++++++++++---------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 0bcb98517f..6572972cd0 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1020,6 +1020,7 @@ public: static constexpr const char* PICK_LINE = "%1 pick line"; static constexpr const char* PICK_SYMMETRY_LINE = "%1 pick symmetry line"; static constexpr const char* PICK_POINT = "%1 pick point"; + static constexpr const char* PLACE_DIMENSION = "%1 place dimension"; explicit DrawSketchHandlerGenConstraint(CmdSketcherConstraint* _cmd) : cmd(_cmd) @@ -1139,12 +1140,12 @@ public: // Special case for Sketcher_ConstrainPointOnObject to generate dynamic step hint if (commandName == "Sketcher_ConstrainPointOnObject") { if (selectionStep == 0) { - return {{QObject::tr("%1 pick point or edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (selectionStep == 1 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { - return {{QObject::tr("%1 pick edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else { - return {{QObject::tr("%1 pick point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } } @@ -1152,23 +1153,23 @@ public: // Special case for Sketcher_ConstrainAngle to generate context-aware hints if (commandName == "Sketcher_ConstrainAngle") { if (selectionStep == 0) { - return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (selectionStep == 1 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Edge workflow - return {{QObject::tr("%1 pick first edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_FIRST_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else { // Could be Line + Line or Edge + Point + Edge workflow // Tell user what they can actually pick next - return {{QObject::tr("%1 pick second line or point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_LINE_OR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } else if (selectionStep == 2 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Edge workflow - return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Edge + Point + Edge workflow - return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } } } @@ -1176,19 +1177,19 @@ public: // Special case for Sketcher_ConstrainPerpendicular to generate context-aware hints if (commandName == "Sketcher_ConstrainPerpendicular") { if (selectionStep == 0) { - return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (selectionStep == 1 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Edge workflow - return {{QObject::tr("%1 pick first edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_FIRST_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else { // Edge + Edge workflow - return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } } else if (selectionStep == 2 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Edge workflow - return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } } } @@ -1196,22 +1197,22 @@ public: // Special case for Sketcher_ConstrainTangent to generate context-aware hints if (commandName == "Sketcher_ConstrainTangent") { if (selectionStep == 0) { - return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (selectionStep == 1 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Edge workflow - return {{QObject::tr("%1 pick first edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_FIRST_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else { // Could be Edge + Edge or Edge + Point + Edge workflow - return {{QObject::tr("%1 pick second edge or point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_EDGE_OR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } else if (selectionStep == 2 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Edge workflow - return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Edge + Point + Edge workflow - return {{QObject::tr("%1 pick second edge"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } } } @@ -1219,22 +1220,22 @@ public: // Special case for Sketcher_ConstrainSymmetric to generate context-aware hints if (commandName == "Sketcher_ConstrainSymmetric") { if (selectionStep == 0) { - return {{QObject::tr("%1 pick edge or first point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (selectionStep == 1 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Point or Point + Point + Edge/Point workflow - return {{QObject::tr("%1 pick edge or second point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else { // Edge + Point workflow - return {{QObject::tr("%1 pick symmetry point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SYMMETRY_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } else if (selectionStep == 2 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Point + Point + Edge/Point workflow - return {{QObject::tr("%1 pick symmetry line or point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SYMMETRY_LINE_OR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && !isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Point + Edge + Point workflow - return {{QObject::tr("%1 pick symmetry point"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SYMMETRY_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } } @@ -1355,7 +1356,7 @@ private: {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second element"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainAngle", .selectionStep = 2, @@ -1364,7 +1365,7 @@ private: // Symmetry {.commandName = "Sketcher_ConstrainSymmetric", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainSymmetric", .selectionStep = 1, @@ -1372,7 +1373,7 @@ private: {.commandName = "Sketcher_ConstrainSymmetric", .selectionStep = 2, - .hints = {{QObject::tr("%1 pick symmetry line"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SYMMETRY_LINE), {Gui::InputHint::UserInput::MouseLeft}}}}, // Tangent {.commandName = "Sketcher_ConstrainTangent", @@ -1385,7 +1386,7 @@ private: {.commandName = "Sketcher_ConstrainTangent", .selectionStep = 2, - .hints = {{QObject::tr("%1 pick optional tangent point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_OPTIONAL_TANGENT_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, // Perpendicular {.commandName = "Sketcher_ConstrainPerpendicular", @@ -1398,16 +1399,16 @@ private: {.commandName = "Sketcher_ConstrainPerpendicular", .selectionStep = 2, - .hints = {{QObject::tr("%1 pick optional perpendicular point"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_OPTIONAL_PERPENDICULAR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}}, // Parallel {.commandName = "Sketcher_ConstrainParallel", .selectionStep = 0, - .hints = {{QObject::tr("%1 pick line"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_LINE), {Gui::InputHint::UserInput::MouseLeft}}}}, {.commandName = "Sketcher_ConstrainParallel", .selectionStep = 1, - .hints = {{QObject::tr("%1 pick second line"), {Gui::InputHint::UserInput::MouseLeft}}}}, + .hints = {{QObject::tr(PICK_SECOND_LINE), {Gui::InputHint::UserInput::MouseLeft}}}}, // Distance {.commandName = "Sketcher_ConstrainDistance", @@ -1695,6 +1696,10 @@ public: class DrawSketchHandlerDimension : public DrawSketchHandler { public: + // Helper constants for hint texts + static constexpr const char* PICK_EDGE = "%1 pick edge"; + static constexpr const char* PICK_SECOND_POINT_OR_EDGE = "%1 pick second point or edge"; + static constexpr const char* PLACE_DIMENSION = "%1 place dimension"; explicit DrawSketchHandlerDimension(std::vector SubNames) : specialConstraint(SpecialConstraint::None) , availableConstraint(AvailableConstraint::FIRST) @@ -1944,11 +1949,11 @@ public: std::list getToolHints() const override { if (selectionEmpty()) { - return {{QObject::tr("%1 pick geometry"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (selPoints.size() == 1 && selLine.empty() && selCircleArc.empty()) { - return {{QObject::tr("%1 pick second point or geometry"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else { - return {{QObject::tr("%1 place dimension"), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PLACE_DIMENSION), {Gui::InputHint::UserInput::MouseLeft}}}; } } From dbd72f9c60f7c890b7c5f525055ddeca48bf18d3 Mon Sep 17 00:00:00 2001 From: George Peden Date: Mon, 8 Sep 2025 14:24:38 -1000 Subject: [PATCH 06/11] Improve context-aware hints for Sketcher constraints - ConstrainDistanceX/Y: Fix hints to show 'pick second point' instead of 'pick second point or edge' when first selection is a point - ConstrainPerpendicular: Add context-aware hints for edge+point+edge workflow - ConstrainTangent: Add context-aware hints for edge+point+edge workflow - ConstrainSymmetric: Fix hints to show 'pick edge or second point' instead of 'pick edge or first point' when first selection is a point - ConstrainSymmetric: Fix hints to show 'pick symmetry line' instead of 'pick symmetry line or point' for point+point+edge workflow All changes are hint improvements only - no functionality changes to existing workflows. --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 64 +++++++++++++-------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 6572972cd0..6ebee50cb3 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -999,12 +999,14 @@ class DrawSketchHandlerGenConstraint: public DrawSketchHandler public: // Helper constants for all hint texts static constexpr const char* PICK_EDGE_OR_FIRST_POINT = "%1 pick edge or first point"; + static constexpr const char* PICK_EDGE_OR_SECOND_POINT = "%1 pick edge or second point"; static constexpr const char* PICK_FIRST_EDGE = "%1 pick first edge"; static constexpr const char* PICK_SECOND_EDGE = "%1 pick second edge"; static constexpr const char* PICK_SECOND_LINE_OR_POINT = "%1 pick second line or point"; static constexpr const char* PICK_SECOND_EDGE_OR_POINT = "%1 pick second edge or point"; static constexpr const char* PICK_SYMMETRY_POINT = "%1 pick symmetry point"; static constexpr const char* PICK_SYMMETRY_LINE_OR_POINT = "%1 pick symmetry line or point"; + static constexpr const char* PICK_SYMMETRY_LINE = "%1 pick symmetry line"; static constexpr const char* PICK_SECOND_LINE = "%1 pick second line"; static constexpr const char* PICK_SECOND_POINT_OR_EDGE = "%1 pick second point or edge"; static constexpr const char* PICK_POINT_OR_EDGE = "%1 pick point or edge"; @@ -1018,7 +1020,6 @@ public: static constexpr const char* PICK_OPTIONAL_TANGENT_POINT = "%1 pick optional tangent point"; static constexpr const char* PICK_OPTIONAL_PERPENDICULAR_POINT = "%1 pick optional perpendicular point"; static constexpr const char* PICK_LINE = "%1 pick line"; - static constexpr const char* PICK_SYMMETRY_LINE = "%1 pick symmetry line"; static constexpr const char* PICK_POINT = "%1 pick point"; static constexpr const char* PLACE_DIMENSION = "%1 place dimension"; @@ -1183,13 +1184,16 @@ public: // Point + Edge + Edge workflow return {{QObject::tr(PICK_FIRST_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else { - // Edge + Edge workflow - return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + // Edge + Edge or Edge + Point + Edge workflow + return {{QObject::tr(PICK_SECOND_EDGE_OR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } else if (selectionStep == 2 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Edge workflow return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { + // Edge + Point + Edge workflow + return {{QObject::tr(PICK_SECOND_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } } } @@ -1217,6 +1221,36 @@ public: } } + // Special case for Sketcher_ConstrainDistanceX to generate context-aware hints + if (commandName == "Sketcher_ConstrainDistanceX") { + if (selectionStep == 0) { + return {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selectionStep == 1 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Point workflow + return {{QObject::tr(PICK_SECOND_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; + } else { + // Edge workflow - no second selection needed + return {{QObject::tr(PLACE_DIMENSION), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } + } + + // Special case for Sketcher_ConstrainDistanceY to generate context-aware hints + if (commandName == "Sketcher_ConstrainDistanceY") { + if (selectionStep == 0) { + return {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selectionStep == 1 && !selSeq.empty()) { + if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { + // Point + Point workflow + return {{QObject::tr(PICK_SECOND_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; + } else { + // Edge workflow - no second selection needed + return {{QObject::tr(PLACE_DIMENSION), {Gui::InputHint::UserInput::MouseLeft}}}; + } + } + } + // Special case for Sketcher_ConstrainSymmetric to generate context-aware hints if (commandName == "Sketcher_ConstrainSymmetric") { if (selectionStep == 0) { @@ -1224,15 +1258,15 @@ public: } else if (selectionStep == 1 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId)) { // Point + Edge + Point or Point + Point + Edge/Point workflow - return {{QObject::tr(PICK_EDGE_OR_FIRST_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_EDGE_OR_SECOND_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else { // Edge + Point workflow return {{QObject::tr(PICK_SYMMETRY_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } else if (selectionStep == 2 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { - // Point + Point + Edge/Point workflow - return {{QObject::tr(PICK_SYMMETRY_LINE_OR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; + // Point + Point + Edge workflow + return {{QObject::tr(PICK_SYMMETRY_LINE), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && !isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Point + Edge + Point workflow return {{QObject::tr(PICK_SYMMETRY_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; @@ -9690,8 +9724,7 @@ CmdSketcherConstrainSymmetric::CmdSketcherConstrainSymmetric() {SelVertex, SelEdgeOrAxis, SelVertex}, {SelVertexOrRoot, SelVertexOrRoot, SelEdge}, {SelVertexOrRoot, SelVertexOrRoot, SelExternalEdge}, - {SelVertex, SelVertex, SelEdgeOrAxis}, - {SelVertexOrRoot, SelVertexOrRoot, SelVertexOrRoot}}; + {SelVertex, SelVertex, SelEdgeOrAxis}}; } void CmdSketcherConstrainSymmetric::activated(int iMsg) @@ -9969,21 +10002,6 @@ void CmdSketcherConstrainSymmetric::applyConstraint(std::vector& selS } return; } - case 8:// {SelVertexOrRoot, SelVertexOrRoot, SelVertexOrRoot} - { - GeoId1 = selSeq.at(0).GeoId; - GeoId2 = selSeq.at(1).GeoId; - GeoId3 = selSeq.at(2).GeoId; - PosId1 = selSeq.at(0).PosId; - PosId2 = selSeq.at(1).PosId; - PosId3 = selSeq.at(2).PosId; - - if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) { - showNoConstraintBetweenFixedGeometry(Obj); - return; - } - break; - } default: break; } From b82408c545002b5fa5bb3e6f9b75f04bf1627027 Mon Sep 17 00:00:00 2001 From: George Peden Date: Mon, 8 Sep 2025 17:16:17 -1000 Subject: [PATCH 07/11] Fix Sketcher Dimension hints - add context-aware hints based on selection - Add PICK_POINT_OR_EDGE constant to DrawSketchHandlerDimension class - Implement nuanced hints for Dimension tool based on selected geometry: - Empty selection: 'pick point or edge' - Single point/line/circle: 'pick second point or edge' - Multiple selections: 'pick second point or edge' - Remove stray character causing build error - Addresses feedback on issue #22282 for comprehensive Sketcher hints coverage --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 6ebee50cb3..071c0f4890 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1732,6 +1732,7 @@ class DrawSketchHandlerDimension : public DrawSketchHandler public: // Helper constants for hint texts static constexpr const char* PICK_EDGE = "%1 pick edge"; + static constexpr const char* PICK_POINT_OR_EDGE = "%1 pick point or edge"; static constexpr const char* PICK_SECOND_POINT_OR_EDGE = "%1 pick second point or edge"; static constexpr const char* PLACE_DIMENSION = "%1 place dimension"; explicit DrawSketchHandlerDimension(std::vector SubNames) @@ -1983,11 +1984,20 @@ public: std::list getToolHints() const override { if (selectionEmpty()) { - return {{QObject::tr(PICK_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; - } else if (selPoints.size() == 1 && selLine.empty() && selCircleArc.empty()) { + return {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selPoints.size() == 1 && selLine.empty() && selCircleArc.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { + // Single point - can add more points, lines, circles, etc. + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selLine.size() == 1 && selPoints.empty() && selCircleArc.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { + // Single line - can add more points, lines, circles, etc. + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + } else if (selCircleArc.size() == 1 && selPoints.empty() && selLine.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { + // Single circle/arc - can add more points, lines, circles, etc. return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } else { - return {{QObject::tr(PLACE_DIMENSION), {Gui::InputHint::UserInput::MouseLeft}}}; + // Multiple selections or complex combinations - check if more selections are possible + // For now, assume more selections are possible unless we have a complete constraint + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; } } From abf9762abb904b2adc2ab36f484a3b9354deb458 Mon Sep 17 00:00:00 2001 From: George Peden Date: Sat, 20 Sep 2025 11:19:38 -0700 Subject: [PATCH 08/11] Fix ConstrainSymmetric two points + symmetry point workflow - Fix hint text to say 'pick symmetry line or symmetry point' for two points + symmetry point workflow - Add missing sequence {SelVertexOrRoot, SelVertexOrRoot, SelVertexOrRoot} to allowedSelSequences - Add case 8 to handle two points + symmetry point constraint creation - Fix duplicate constraint creation by adding return statement in case 8 - Add getSelection().clearSelection() for consistency Fixes reviewer comment #2 from PR #22282 --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 38 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 071c0f4890..0a95dcae9c 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1266,7 +1266,7 @@ public: } else if (selectionStep == 2 && !selSeq.empty()) { if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Point + Point + Edge workflow - return {{QObject::tr(PICK_SYMMETRY_LINE), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SYMMETRY_LINE_OR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && !isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Point + Edge + Point workflow return {{QObject::tr(PICK_SYMMETRY_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; @@ -9734,7 +9734,8 @@ CmdSketcherConstrainSymmetric::CmdSketcherConstrainSymmetric() {SelVertex, SelEdgeOrAxis, SelVertex}, {SelVertexOrRoot, SelVertexOrRoot, SelEdge}, {SelVertexOrRoot, SelVertexOrRoot, SelExternalEdge}, - {SelVertex, SelVertex, SelEdgeOrAxis}}; + {SelVertex, SelVertex, SelEdgeOrAxis}, + {SelVertexOrRoot, SelVertexOrRoot, SelVertexOrRoot}}; } void CmdSketcherConstrainSymmetric::activated(int iMsg) @@ -10012,6 +10013,39 @@ void CmdSketcherConstrainSymmetric::applyConstraint(std::vector& selS } return; } + case 8:// {SelVertexOrRoot, SelVertexOrRoot, SelVertexOrRoot} + { + // Simple point + point + point symmetry + GeoId1 = selSeq.at(0).GeoId; + GeoId2 = selSeq.at(1).GeoId; + GeoId3 = selSeq.at(2).GeoId; + PosId1 = selSeq.at(0).PosId; + PosId2 = selSeq.at(1).PosId; + PosId3 = selSeq.at(2).PosId; + + if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) { + showNoConstraintBetweenFixedGeometry(Obj); + return; + } + + // undo command open + openCommand(QT_TRANSLATE_NOOP("Command", "Add symmetric constraint")); + Gui::cmdAppObjectArgs( + Obj, + "addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d))", + GeoId1, + static_cast(PosId1), + GeoId2, + static_cast(PosId2), + GeoId3, + static_cast(PosId3)); + + // finish the transaction and update + commitCommand(); + tryAutoRecompute(Obj); + getSelection().clearSelection(); + return; + } default: break; } From 1ac117f1c840aa60dd7d4b47db3d5d4b0bb60911 Mon Sep 17 00:00:00 2001 From: George Peden Date: Sat, 20 Sep 2025 12:26:03 -0700 Subject: [PATCH 09/11] Add MODE_HINT to dimension tool - Add MODE_HINT constant to DrawSketchHandlerDimension class - Shows 'switch mode' hint for M key in dimension tool - Addresses reviewer feedback for missing M key hint --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 0a95dcae9c..38ee925df2 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1022,6 +1022,7 @@ public: static constexpr const char* PICK_LINE = "%1 pick line"; static constexpr const char* PICK_POINT = "%1 pick point"; static constexpr const char* PLACE_DIMENSION = "%1 place dimension"; + static constexpr const char* MODE_HINT = "%1 switch mode"; explicit DrawSketchHandlerGenConstraint(CmdSketcherConstraint* _cmd) : cmd(_cmd) @@ -1735,6 +1736,7 @@ public: static constexpr const char* PICK_POINT_OR_EDGE = "%1 pick point or edge"; static constexpr const char* PICK_SECOND_POINT_OR_EDGE = "%1 pick second point or edge"; static constexpr const char* PLACE_DIMENSION = "%1 place dimension"; + static constexpr const char* MODE_HINT = "%1 switch mode"; explicit DrawSketchHandlerDimension(std::vector SubNames) : specialConstraint(SpecialConstraint::None) , availableConstraint(AvailableConstraint::FIRST) @@ -1984,20 +1986,25 @@ public: std::list getToolHints() const override { if (selectionEmpty()) { - return {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else if (selPoints.size() == 1 && selLine.empty() && selCircleArc.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { // Single point - can add more points, lines, circles, etc. - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else if (selLine.size() == 1 && selPoints.empty() && selCircleArc.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { // Single line - can add more points, lines, circles, etc. - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else if (selCircleArc.size() == 1 && selPoints.empty() && selLine.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { // Single circle/arc - can add more points, lines, circles, etc. - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else { // Multiple selections or complex combinations - check if more selections are possible // For now, assume more selections are possible unless we have a complete constraint - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } } From a9401819989ebd82af2b05d8631a22c25d73c7f1 Mon Sep 17 00:00:00 2001 From: longrackslabs Date: Sun, 21 Sep 2025 13:49:06 -0700 Subject: [PATCH 10/11] =?UTF-8?q?Fix=20ConstrainSymmetric=20hint=20for=20p?= =?UTF-8?q?oint=E2=86=92edge=E2=86=92point=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change 'pick symmetry point' to 'pick point' when the workflow is point → symmetry line → point, since the final selection is a regular point, not a special 'symmetry point'. --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 38ee925df2..3e58bcf633 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1270,7 +1270,7 @@ public: return {{QObject::tr(PICK_SYMMETRY_LINE_OR_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } else if (isVertex(selSeq[0].GeoId, selSeq[0].PosId) && !isVertex(selSeq[1].GeoId, selSeq[1].PosId)) { // Point + Edge + Point workflow - return {{QObject::tr(PICK_SYMMETRY_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; + return {{QObject::tr(PICK_POINT), {Gui::InputHint::UserInput::MouseLeft}}}; } } } From 99f27f1a5645f814e56249ca136e3cd751947b03 Mon Sep 17 00:00:00 2001 From: longrackslabs Date: Sun, 21 Sep 2025 14:02:46 -0700 Subject: [PATCH 11/11] Add 'or click to finish' to Sketcher_Dimension hints After selecting first geometry, users can now see that they can click empty space to finish the dimension and set its value, rather than having to pick additional geometry. Added PICK_SECOND_POINT_OR_EDGE_OR_CLICK_TO_FINISH constant and updated all selection cases in DrawSketchHandlerDimension::getToolHints(). --- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 3e58bcf633..aa597f70fd 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1735,6 +1735,7 @@ public: static constexpr const char* PICK_EDGE = "%1 pick edge"; static constexpr const char* PICK_POINT_OR_EDGE = "%1 pick point or edge"; static constexpr const char* PICK_SECOND_POINT_OR_EDGE = "%1 pick second point or edge"; + static constexpr const char* PICK_SECOND_POINT_OR_EDGE_OR_CLICK_TO_FINISH = "%1 pick second point or edge, or click to finish"; static constexpr const char* PLACE_DIMENSION = "%1 place dimension"; static constexpr const char* MODE_HINT = "%1 switch mode"; explicit DrawSketchHandlerDimension(std::vector SubNames) @@ -1990,20 +1991,20 @@ std::list getToolHints() const override { {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else if (selPoints.size() == 1 && selLine.empty() && selCircleArc.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { // Single point - can add more points, lines, circles, etc. - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE_OR_CLICK_TO_FINISH), {Gui::InputHint::UserInput::MouseLeft}}, {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else if (selLine.size() == 1 && selPoints.empty() && selCircleArc.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { // Single line - can add more points, lines, circles, etc. - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE_OR_CLICK_TO_FINISH), {Gui::InputHint::UserInput::MouseLeft}}, {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else if (selCircleArc.size() == 1 && selPoints.empty() && selLine.empty() && selEllipseAndCo.empty() && selSplineAndCo.empty()) { // Single circle/arc - can add more points, lines, circles, etc. - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE_OR_CLICK_TO_FINISH), {Gui::InputHint::UserInput::MouseLeft}}, {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } else { // Multiple selections or complex combinations - check if more selections are possible // For now, assume more selections are possible unless we have a complete constraint - return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE), {Gui::InputHint::UserInput::MouseLeft}}, + return {{QObject::tr(PICK_SECOND_POINT_OR_EDGE_OR_CLICK_TO_FINISH), {Gui::InputHint::UserInput::MouseLeft}}, {QObject::tr(MODE_HINT), {Gui::InputHint::UserInput::KeyM}}}; } }