Skip to content

Sketcher: contextual right click context menu based on selection#11884

Merged
WandererFan merged 1 commit intoFreeCAD:mainfrom
maxwxyz:sketcher-right-click-menu
Jan 8, 2024
Merged

Sketcher: contextual right click context menu based on selection#11884
WandererFan merged 1 commit intoFreeCAD:mainfrom
maxwxyz:sketcher-right-click-menu

Conversation

@maxwxyz
Copy link
Collaborator

@maxwxyz maxwxyz commented Jan 2, 2024

fixes #11818
This PR introduces a contextual right click context menu in the Sketcher WB which shows different commands based on the selection of the user (none, one or multiple geometry elements, or constraints).
The current context menu is static and is overloaded with commands which are either greyed out or not working in Sketcher.

New:

sketcher-context-menu.mp4

Old:
grafik

@github-actions github-actions bot added the Mod: Sketcher Related to the Sketcher Workbench label Jan 2, 2024
@maxwxyz
Copy link
Collaborator Author

maxwxyz commented Jan 2, 2024

No selection:
grafik

Selection of a geometry (in this example one line):
grafik

Selection of constraints:
grafik

@PaddleStroke
Copy link
Contributor

A first comment on the functionality :

  • 'Fit all' : Wouldn't it make more sense to bring it up under 'view section'
  • 'Draw style' : I would remove it it seems unecessary here.
  • 'Selected associated geo' : For consistency with the other menu maybe it would be best to move it to after the toggles.

Copy link
Contributor

@PaddleStroke PaddleStroke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not finished reviewing. Tomorrow I will check the core of the feature

@PaddleStroke
Copy link
Contributor

PaddleStroke commented Jan 3, 2024

Overall very good, it seems you are learning fast and your contribution is smart. Thanks for contributing !

By the way, to ease your life, here is the code block with the modifications I offered. I have not tested so please do.


                case STATUS_NONE: {
                    int selectedEdges = 0;
                    int selectedLines = 0;
                    int selectedConics = 0;
                    int selectedPoints = 0;
                    int selectedConstraints = 0;

                    Gui::MenuItem geom;
                    geom.setCommand("Sketcher context");

                    std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());

                    if (selection.size() > 0) {
                        const std::vector<std::string> SubNames = selection[0].getSubNames();

                        for (auto name : SubNames) {
                            if (name.substr(0, 4) == "Edge") {
                                ++selectedEdges;

                                int geoId = std::atoi(name.substr(4, 4000).c_str()) - 1;
                                if (geoId >= 0) {
                                    const Part::Geometry* geo = getSketchObject()->getGeometry(geoId);
                                    if (isLineSegment(*geo)) {
                                        ++selectedLines;
                                    }
                                    else {
                                        ++selectedConics;
                                    }
                                }
                            }
                            else if (name.substr(0, 4) == "Vert") {
                                ++selectedPoints;
                            }
                            else if (name.substr(0, 4) == "Cons") {
                                ++selectedConstraints;
                            }
                        }

                        if (selectedEdges >= 1 && selectedPoints == 0) {
                            // Here you want to make special cases for line vs arcs vs circle and so on.
                            geom << "Sketcher_Dimension"
                                << "Sketcher_ConstrainHorVer"
                                << "Sketcher_ConstrainVertical"
                                << "Sketcher_ConstrainHorizontal";

                            if (selectedEdges > 1) {
                                geom << "Sketcher_ConstrainParallel"
                                    << "Sketcher_ConstrainPerpendicular"
                                    << "Sketcher_ConstrainTangent"
                                    << "Sketcher_ConstrainEqual";
                            }
                        }
                        else if (selectedEdges == 1 && selectedPoints >= 1) {
                            // Here you want to make special cases for line vs arcs vs circle and so on.
                            geom << "Sketcher_Dimension"
                                << "Sketcher_ConstrainCoincidentUnified"
                                << "Sketcher_ConstrainHorVer"
                                << "Sketcher_ConstrainVertical"
                                << "Sketcher_ConstrainHorizontal";
                        }
                        else if (selectedEdges == 0 && selectedPoints >= 1) {
                             geom << "Sketcher_Dimension";

                             if (selectedPoints > 1) {
                                geom << "Sketcher_ConstrainCoincidentUnified"
                                    << "Sketcher_ConstrainHorVer"
                                    << "Sketcher_ConstrainVertical"
                                    << "Sketcher_ConstrainHorizontal";
                             }
                        }
                        else if (selectedConstraints >= 1) {
                            geom << "Sketcher_SelectElementsAssociatedWithConstraints"
                                << "Sketcher_ToggleDrivingConstraint"
                                << "Sketcher_ToggleActiveConstraint"
                                << "Separator"
                                << "Std_Delete";
                        }

                        geom << "Separator"
                            << "Sketcher_ToggleConstruction"
                            << "Separator"
                            << "Sketcher_CreatePointFillet"
                            << "Sketcher_Trimming"
                            << "Sketcher_Extend"
                            << "Separator"
                            << "Sketcher_CompDimensionTools"
                            << "Sketcher_CompConstrainTools"
                            << "Sketcher_SelectConstraints"
                            << "Separator"
                            << "Std_Delete";
                    }
                    else {  //without selection
                        geom << "Sketcher_ViewSketch"
                            << "Sketcher_ViewSection"
                            << "Std_ViewFitAll"
                            << "Separator"
                            << "Sketcher_CreatePoint"
                            << "Sketcher_CreatePolyline"
                            << "Sketcher_CreateArc"
                            << "Sketcher_CreateCircle"
                            << "Sketcher_CreateRectangle"
                            << "Sketcher_CreateHexagon"
                            << "Sketcher_CreateBSpline"
                            << "Separator"
                            << "Sketcher_ToggleConstruction"
                            << "Separator"
                            << "Sketcher_CreatePointFillet"
                            << "Sketcher_Trimming"
                            << "Sketcher_Extend"
                            << "Separator"
                            << "Sketcher_External"
                            << "Separator"
                            << "Sketcher_CompDimensionTools"
                            << "Sketcher_CompConstrainTools"
                            << "Separator"
                            << "Sketcher_DeleteAllGeometry"
                            << "Sketcher_DeleteAllConstraints"
                            << "Separator"
                            << "Sketcher_LeaveSketch";
                    }

To make special cases of line vs circle, have a look at how to get the geometry from the name in sketcher_dimension tool (see line 1510 of CommandConstraints.cpp, function handleInitialSelection)

@PaddleStroke
Copy link
Contributor

PaddleStroke commented Jan 3, 2024

@maxwxyz I have just edited my previous block of code to integrate counting edges and conics. I let you adapt the menu based on these values.

@PaddleStroke
Copy link
Contributor

If you want finer control than line vs conics, you can add more variables :

                                    if (isLineSegment(*geo)) {
                                        ++selectedLines;
                                    }
                                    else if (isCircle(*geo)) {
                                        ++selectedCircles;
                                    }
                                    ...

@maxwxyz

This comment was marked as resolved.

@PaddleStroke
Copy link
Contributor

PaddleStroke commented Jan 3, 2024

Also the CI macOS build is failing :
image

You can get more information looking into the details (try ctrl-F for error in the logs).

Having read the code, I would venture a guess that this is caused by the 'goto' as it is the only unusual thing I have seen. So if you pickup my code block push it to see if it solves the CI failure before trying to debug it.

@maxwxyz

This comment was marked as resolved.

@PaddleStroke
Copy link
Contributor

Ok maybe the macos build failure is unrelated to your PR then.

Btw one last thing, this feature is adding a lot of lines of code to the mouseButtonPressed function. So please make a new function to put all this new code in, for example : ViewProviderSketch::generateMenuBasedOnSelection()

@maxwxyz
Copy link
Collaborator Author

maxwxyz commented Jan 3, 2024

@PaddleStroke Thanks for reviewing.

  • I've created a separate function and called this function from the previous spot and also replaced the weird cases where currently a different menu was called (left click on edge, hold and then right click) with the new function.
  • Context menu was adjusted to present valid constraints for cases if conics are in the selection and refined for other cases
  • Your new tools for offset and rotate were added as well when geometry is selected (translate/array has to be included when merged).

off-topic: Fillet/Trim/Extend edge does currently not work with a pre-selection. Is this already in progress or should I create an issue?

@maxwxyz

This comment was marked as resolved.

@Syres916
Copy link
Contributor

Syres916 commented Jan 3, 2024

@maxwxyz do you actually need those additional lines in ViewProviderSketch.h ?

@maxwxyz

This comment was marked as resolved.

@Syres916
Copy link
Contributor

Syres916 commented Jan 3, 2024

@maxwxyz it's going to be a few hours before I have more than one CPU core free as I'm doing a Git Bisect from 2021 to trace a bug. If it's still not solved then I'll compile your PR just to make sure I get the same error as being reported in the CI builds.

@maxwxyz

This comment was marked as resolved.

@Syres916
Copy link
Contributor

Syres916 commented Jan 3, 2024

@Syres916 I've built successfully with conda on Windows and get no warnings or errors concerning the changed files.

There's always some 'specifics' with Linux that Windows compiling won't highlight, that's why the CI's are across platforms though I don't have a lot of confidence in the Mac one at present.

@maxwxyz maxwxyz force-pushed the sketcher-right-click-menu branch from c00d98e to 2d6e18f Compare January 3, 2024 14:46
@PaddleStroke
Copy link
Contributor

Please fix my last comment (and squash the change), then compile and make sure everything works as expected. Then I think it's ready to merge @chennes.

@maxwxyz maxwxyz force-pushed the sketcher-right-click-menu branch from 2d6e18f to 09f6a2b Compare January 3, 2024 16:24
@maxwxyz
Copy link
Collaborator Author

maxwxyz commented Jan 3, 2024

@PaddleStroke @chennes done and thanks!

@PaddleStroke
Copy link
Contributor

Ah sorry one thing I missed :

switch (Mode) {
                case STATUS_SKETCH_UseHandler:
                    // delegate to handler whether to quit or do otherwise
                    sketchHandler->pressRightButton(Base::Vector2d(x, y));
                    return true;
                case STATUS_NONE: {
                    generateContextMenu();
                    return true;
                }
                case STATUS_SELECT_Point:{
                    generateContextMenu();
                    return true;
                }
                case STATUS_SELECT_Edge: {
                    generateContextMenu();
                    return true;
                }
                case STATUS_SELECT_Cross:
                case STATUS_SELECT_Constraint: {
                    generateContextMenu();
                    return true;
                }
                case STATUS_SKETCH_DragPoint:
                case STATUS_SKETCH_DragCurve:
                case STATUS_SKETCH_DragConstraint:
                case STATUS_SKETCH_StartRubberBand:
                case STATUS_SKETCH_UseRubberBand:
                    break;
            }

Can be simplified to :

switch (Mode) {
                case STATUS_SKETCH_UseHandler:
                    // delegate to handler whether to quit or do otherwise
                    sketchHandler->pressRightButton(Base::Vector2d(x, y));
                    return true;
                case STATUS_NONE: 
                case STATUS_SELECT_Point:
                case STATUS_SELECT_Edge:
                case STATUS_SELECT_Cross:
                case STATUS_SELECT_Constraint: {
                    generateContextMenu();
                    return true;
                }
                case STATUS_SKETCH_DragPoint:
                case STATUS_SKETCH_DragCurve:
                case STATUS_SKETCH_DragConstraint:
                case STATUS_SKETCH_StartRubberBand:
                case STATUS_SKETCH_UseRubberBand:
                    break;
            }

@maxwxyz maxwxyz force-pushed the sketcher-right-click-menu branch from 09f6a2b to 55a6e7f Compare January 3, 2024 18:25
@maxwxyz
Copy link
Collaborator Author

maxwxyz commented Jan 3, 2024

all done

@maxwxyz maxwxyz force-pushed the sketcher-right-click-menu branch from 55a6e7f to 195813b Compare January 4, 2024 08:15
{
return editCoinManager != nullptr;
}
void ViewProviderSketch::generateContextMenu()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with merging this code as-is, and I know there is a LONG history in FreeCAD of methods that look like this, but IMO it should really be something like eight individual methods, so would look something like:

void ViewProviderSketch::generateContextMenu()
{
    // I'm using structured bindings here, but you could write countSelection in a number of different ways
    // to achieve the same basic idea...
    const auto [selectedEdges, selectedLines, selectedConics, selectedPoints, selectedConstraints] = countSelection();
    if (selectedEdges >= 1 && selectedPoints == 0) {
        setContextMenuForEdges(menu, selectedConics, selectedLines);
    } else if (selectedEdges == 1 && selectedPoints >= 1) {
        setContextMenuForOneEdgeAndPoints(menu, selectedConics, selectedPoints);
    } 
    // and so forth, for each of your top-level conditionals -- the functions can be local 
    // to the CPP file, in an anonymous namespace, since they don't need access to `this`
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chennes shall I redo it or do it like this for future PRs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then you are creating a lot of functions which are cluttering VPSketch. Even if only the cpp file.
So the solution might be to create a class in another file to handle this. But it seems a bit overkill. And also Max is a new c++ contributor asking him that wouldn't be easy.
So perhaps we can keep like this for now? This function is long but only because of the menu names taking many lines not because of complexity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also say that this is fine. Every branch of the if consists basically of one statement - which is pretty long but it still is one statement. Introducing more functions into that case would be harder to maintain as every added or removed scenario will require adding or removing a function which won't be reusable anyways as it is so specific.

@WandererFan WandererFan merged commit 476089a into FreeCAD:main Jan 8, 2024
@maxwxyz maxwxyz deleted the sketcher-right-click-menu branch January 8, 2024 17:40
@PaddleStroke
Copy link
Contributor

Congrats on a nice feature @maxwxyz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Mod: Sketcher Related to the Sketcher Workbench

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Problem] Sketcher: right click menu in 3d view

6 participants