From 82fed5bbfc361b71c23881547eac3577b0bb60cf Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Fri, 20 Oct 2023 20:29:05 +0200 Subject: [PATCH] Sketcher: New flexible DSH Architecture ======================================= Rewrite of the architecture to accomodate on-view parameters and to enable code reuse between the default widget and custom widgets. --- src/Mod/Sketcher/Gui/CMakeLists.txt | 6 +- .../Gui/DrawSketchControllableHandler.h | 198 +++ src/Mod/Sketcher/Gui/DrawSketchController.h | 573 ++++++++ .../Sketcher/Gui/DrawSketchDefaultHandler.h | 22 +- .../Gui/DrawSketchDefaultWidgetController.h | 371 +++++ .../Gui/DrawSketchDefaultWidgetHandler.h | 1287 ----------------- .../Gui/DrawSketchKeyboardManager.cpp | 129 ++ .../Sketcher/Gui/DrawSketchKeyboardManager.h | 104 ++ .../Gui/SketcherToolDefaultWidget.cpp | 78 - .../Sketcher/Gui/SketcherToolDefaultWidget.h | 38 - 10 files changed, 1392 insertions(+), 1414 deletions(-) create mode 100644 src/Mod/Sketcher/Gui/DrawSketchControllableHandler.h create mode 100644 src/Mod/Sketcher/Gui/DrawSketchController.h create mode 100644 src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h delete mode 100644 src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetHandler.h create mode 100644 src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.cpp create mode 100644 src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.h diff --git a/src/Mod/Sketcher/Gui/CMakeLists.txt b/src/Mod/Sketcher/Gui/CMakeLists.txt index c2e2dda293..f9204881d5 100644 --- a/src/Mod/Sketcher/Gui/CMakeLists.txt +++ b/src/Mod/Sketcher/Gui/CMakeLists.txt @@ -133,7 +133,11 @@ SET(SketcherGui_SRCS DrawSketchHandler.cpp DrawSketchHandler.h DrawSketchDefaultHandler.h - DrawSketchDefaultWidgetHandler.h + DrawSketchController.h + DrawSketchDefaultWidgetController.h + DrawSketchControllableHandler.h + DrawSketchKeyboardManager.h + DrawSketchKeyboardManager.cpp Workbench.cpp Workbench.h EditDatumDialog.cpp diff --git a/src/Mod/Sketcher/Gui/DrawSketchControllableHandler.h b/src/Mod/Sketcher/Gui/DrawSketchControllableHandler.h new file mode 100644 index 0000000000..4835f6aecf --- /dev/null +++ b/src/Mod/Sketcher/Gui/DrawSketchControllableHandler.h @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (c) 2023 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_DrawSketchControllableHandler_H +#define SKETCHERGUI_DrawSketchControllableHandler_H + +#include + +#include "DrawSketchDefaultHandler.h" + +namespace SketcherGui +{ + +/** @brief Template class intended for handlers controllable by a DrawSketchController + * + * @details + * The template encapsulates a minimal interaction between a DrawSketchDefaultHandler and a + * a DrawSketchController, and it is intended to facilitate the creation of enhanced DSHs having + * controlling entities such as widgets and on-screen controls. + */ +template +class DrawSketchControllableHandler + : public DrawSketchDefaultHandler +{ + /** @name Meta-programming definitions and members */ + //@{ + using HandlerType = typename ControllerT::HandlerType; + using SelectModeType = typename ControllerT::SelectModeType; + using ConstructionMethodType = typename ControllerT::ContructionMethodType; + //@} + + /** @name Convenience definitions */ + //@{ + using DSDefaultHandler = DrawSketchDefaultHandler; + + using ConstructionMachine = ConstructionMethodMachine; + //@} + + // Enable access to the actual controller provided + friend ControllerT; + // Enable access to the parent controller (if any). + // Non-derived controllers shall define ControllerBase as void to interoperate with this class. + friend typename ControllerT::ControllerBase; + +public: + DrawSketchControllableHandler( + ConstructionMethodType constructionmethod = static_cast(0)) + : DSDefaultHandler(constructionmethod) + , toolWidgetManager(static_cast(this)) + {} + + ~DrawSketchControllableHandler() override = default; + + /** @name functions NOT intended for specialisation or further overriding */ + //@{ + void mouseMove(Base::Vector2d onSketchPos) override + { + toolWidgetManager.mouseMoved(onSketchPos); + + toolWidgetManager.enforceControlParameters(onSketchPos); + updateDataAndDrawToPosition(onSketchPos); + toolWidgetManager.adaptParameters(onSketchPos); + } + + bool pressButton(Base::Vector2d onSketchPos) override + { + toolWidgetManager.enforceControlParameters(onSketchPos); + + onButtonPressed(onSketchPos); + return true; + } + + bool releaseButton(Base::Vector2d onSketchPos) override + { + Q_UNUSED(onSketchPos); + DSDefaultHandler::finish(); + return true; + } + //@} + + +protected: + /** @name functions requiring specialisation */ + //@{ + std::string getToolName() const override + { + return DrawSketchHandler::getToolName(); + } + QString getCrosshairCursorSVGName() const override + { + return DrawSketchHandler::getCrosshairCursorSVGName(); + } + //@} + +private: + /** @name functions requiring specialisation */ + //@{ + // For every machine state, it updates the EditData temporary + // curve, and draws the temporary curve during edit mode. + void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override + { + Q_UNUSED(onSketchPos) + }; + + void executeCommands() override {}; + void createAutoConstraints() override {}; + //@} + + /** @name functions which MAY require specialisation*/ + //@{ + /** Default implementation is that on every mouse click the mode is changed to the next seek + On the last seek, it changes to SelectMode::End + If this behaviour is not acceptable, then the function must be specialised.*/ + void onButtonPressed(Base::Vector2d onSketchPos) override + { + DSDefaultHandler::onButtonPressed(onSketchPos); + } + + void beforeCreateAutoConstraints() override + { + toolWidgetManager.addConstraints(); + } + + void onWidgetChanged() override + { + toolWidgetManager.initControls(DSDefaultHandler::toolwidget); + } + + void onReset() override + { + toolWidgetManager.resetControls(); + } + + void onModeChanged() override + { + toolWidgetManager.onHandlerModeChanged(); + DSDefaultHandler::onModeChanged(); + + toolWidgetManager.afterHandlerModeChanged(); + } + + void onConstructionMethodChanged() override + { + toolWidgetManager.onConstructionMethodChanged(); + } + + void registerPressedKey(bool pressed, int key) override + { + DSDefaultHandler::registerPressedKey(pressed, key); + + if (key == SoKeyboardEvent::U && !pressed && !this->isLastState()) { + toolWidgetManager.firstKeyShortcut(); + } + + if (key == SoKeyboardEvent::J && !pressed && !this->isLastState()) { + toolWidgetManager.secondKeyShortcut(); + } + + if (key == SoKeyboardEvent::TAB && !pressed) { + toolWidgetManager.tabShortcut(); + } + } + //@} + +protected: + ControllerT toolWidgetManager; +}; + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_DrawSketchControllableHandler_H diff --git a/src/Mod/Sketcher/Gui/DrawSketchController.h b/src/Mod/Sketcher/Gui/DrawSketchController.h new file mode 100644 index 0000000000..b66f5b46cf --- /dev/null +++ b/src/Mod/Sketcher/Gui/DrawSketchController.h @@ -0,0 +1,573 @@ +/*************************************************************************** + * Copyright (c) 2023 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef SKETCHERGUI_DrawSketchController_H +#define SKETCHERGUI_DrawSketchController_H + +#include +#include + +#include "DrawSketchDefaultHandler.h" +#include "SketcherToolDefaultWidget.h" + +#include "DrawSketchKeyboardManager.h" + +namespace SketcherGui +{ + +/** @brief template class for creating a type encapsulating an int value associated to each of + the possible construction modes supported by the tool. + + @details Different construction modes of a DSH may use different types of controls. This class + allows to instantiate a handler template class to provide such construction mode specific + controls. + + Each different type of control is a template class deriving from this. + */ +template // Initial sizes for each mode +class ControlAmount +{ +public: + template + static constexpr int size(constructionT constructionmethod) + { + auto modeint = static_cast(constructionmethod); + + return constructionMethodParameters[modeint]; + } + + static constexpr int defaultMethodSize() + { + return size(0); + } + +private: + static constexpr std::array constructionMethodParameters = {{sizes...}}; +}; + +/** @brief Type encapsulating the number of On view parameters*/ +template // Initial sizes for each mode +class OnViewParameters: public ControlAmount +{ +}; + +namespace sp = std::placeholders; + +/** @brief Class defining a generic handler controller operable with a DrawSketchControllableHandler + * + * @details + * This class is intended as a parent for controller classes. This controller class provides the + * essential controller functionalities, including on-view parameters. This controller class does + * NOT control based on (taskbox) widgets. + * + * For an example of a tool using directly this class see DrawSketchHandlerPoint. + * + * Controls based on taskbox widgets may derive from this class and add on top any widget mandated + * functionality. For the default widget (SketcherToolDefaultWidget), see + * DrawSketchDefaultWidgetController. For custom widgets, an appropriate class, preferably deriving + * from this controller needs to be provided. + */ +template // The enum comprising all the + // supported construction methods +class DrawSketchController +{ +public: + /** @name Meta-programming definitions and members */ + //@{ + using ControllerBase = void; // No base controller for parent class. + + using HandlerType = HandlerT; + using SelectModeType = SelectModeT; + using ContructionMethodType = ConstructionMethodT; + static constexpr const int AutoConstraintInitialSize = PAutoConstraintSize; + //@} + + /** @name Convenience definitions */ + //@{ + using DSDefaultHandler = + DrawSketchDefaultHandler; + using ConstructionMachine = ConstructionMethodMachine; + + using ConstructionMethod = ConstructionMethodT; + using SelectMode = SelectModeT; + //@} + +protected: + HandlerT* handler; // real derived type + bool init = false; // true if the controls have been initialised. + bool firstMoveInit = false; // true if first mouse movement not yet performed (resets) + + Base::Vector2d prevCursorPosition; + Base::Vector2d lastControlEnforcedPosition; + + int onViewIndexWithFocus = 0; // track the index of the on-view parameter having the focus + int nOnViewParameter = OnViewParametersT::defaultMethodSize(); + + + /** @name Named indices for controlling on-view controls */ + //@{ + enum OnViewParameter + { + First, + Second, + Third, + Fourth, + Fifth, + Sixth, + Seventh, + Eighth, + Ninth, + Tenth, + nOnViewParameters // Must Always be the last one + }; + //@} + + std::vector> onViewParameters; + + /// Class to keep track of colors used by the on-view parameters + class ColorManager + { + public: + SbColor dimConstrColor, dimConstrDeactivatedColor; + + ColorManager() + { + init(); + } + + private: + void init() + { + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/View"); + + dimConstrColor = SbColor(1.0f, 0.149f, 0.0f); + dimConstrDeactivatedColor = SbColor(0.8f, 0.8f, 0.8f); + + float transparency = 0.f; + unsigned long color = (unsigned long)(dimConstrColor.getPackedValue()); + color = hGrp->GetUnsigned("ConstrainedDimColor", color); + dimConstrColor.setPackedValue((uint32_t)color, transparency); + + color = (unsigned long)(dimConstrDeactivatedColor.getPackedValue()); + color = hGrp->GetUnsigned("DeactivatedConstrDimColor", color); + dimConstrDeactivatedColor.setPackedValue((uint32_t)color, transparency); + } + }; + +public: + /** Creates the controller. + * @param dshandler a controllable DSH handler + */ + DrawSketchController(HandlerT* dshandler) + : handler(dshandler) + , keymanager(std::make_unique()) + {} + + ~DrawSketchController() + {} + + /** @name functions NOT intended for specialisation offering a NVI for extension */ + /** These functions offer a NVI to ensure the order on initialisation. It is heavily encouraged + * to extend functionality using the NVI interface (by overriding the NVI functions). + */ + //@{ + + /** @brief Initialises controls, such as the widget and the on-view parameters via NVI.*/ + void initControls(QWidget* widget) + { + doInitControls(widget); // NVI + + resetControls(); + init = true; + } + + /** @brief Resets the controls, such as the widget and the on-view parameters */ + void resetControls() + { + doResetControls(); // NVI + + firstMoveInit = false; + } + + /** @brief function triggered by the handler when the mouse has been moved */ + void mouseMoved(Base::Vector2d originalSketchPosition) + { + onMouseMoved(originalSketchPosition); // NVI + + if (!firstMoveInit) { + firstMoveInit = true; + } + } + + /** @brief function triggered by the handler to ensure its operating position takes into + * account widget mandated parameters */ + void enforceControlParameters(Base::Vector2d& onSketchPos) + { + prevCursorPosition = onSketchPos; + + doEnforceControlParameters(onSketchPos); // specialisation interface + + lastControlEnforcedPosition = onSketchPos; // store enforced cursor position. + + afterEnforceControlParameters(); // NVI + } + + /** function that is called by the handler when the construction mode changed */ + void onConstructionMethodChanged() + { + nOnViewParameter = OnViewParametersT::size(handler->constructionMethod()); + + doConstructionMethodChanged(); // NVI + + handler->updateCursor(); + + handler->reset(); // reset of handler to restart. + } + //@} + + /** @name functions NOT intended for specialisation offering specialisation interface for + * extension */ + /** These functions offer a specialisation interface to ensure the order on initialisation. It + * is heavily encouraged to extend functionality using the specialisation interface (by + * specialising the NVI functions). + */ + //@{ + + /** slot triggering when a on view parameter has changed + * It is intended to remote control the DrawSketchDefaultWidgetHandler + */ + void onViewValueChanged(int onviewparameterindex, double value) + { + if (isOnViewParameterOfCurrentMode(onviewparameterindex + 1)) { + setFocusToOnViewParameter(onviewparameterindex + 1); + } + + /* That is not supported with on-view parameters. + // -> A machine does not forward to a next state when adapting the parameter (though it + // may forward to + // a next state if all the parameters are fulfilled, see + // doChangeDrawSketchHandlerMode). This ensures that the geometry has been defined + // (either by mouse clicking or by widget). Autoconstraints on point should be picked + // when the state is reached upon machine state advancement. + // + // -> A machine goes back to a previous state if a parameter of a previous state is + // modified. This ensures + // that appropriate autoconstraints are picked. + if (isOnViewParameterOfPreviousMode(onviewparameterindex)) { + // change to previous state + handler->setState(getState(onviewparameterindex)); + }*/ + + adaptDrawingToOnViewParameterChange(onviewparameterindex, + value); // specialisation interface + + finishControlsChanged(); + } + + void adaptParameters() + { + adaptParameters(lastControlEnforcedPosition); // specialisation interface + } + //@} + + /** @name Specialisation Interface */ + /** These functions offer a specialisation interface. Non-virtual functions are specific to + * this controller. Virtual functions may depend on input from a derived controller, and thus + * the specialisation needs to be of an overriden version (so as to be able to access members + * of the derived controller). + */ + //@{ + /// Change DSH to reflect a value entered in the view + void adaptDrawingToOnViewParameterChange(int onviewparameterindex, double value) + { + Q_UNUSED(onviewparameterindex); + Q_UNUSED(value); + } + + /** Returns the state to which the on-view parameter corresponds in the current construction + * method. */ + auto getState(int parameterindex) const + { + Q_UNUSED(parameterindex); + return handler->getFirstState(); + } + + /// function to create constraints based on control information. + virtual void addConstraints() + {} + + /// Configures on-view parameters + void configureOnViewParameters() + {} + + /** Change DSH to reflect the SelectMode it should be in based on values entered in the + * controls + */ + virtual void doChangeDrawSketchHandlerMode() + {} + + /** function that is called by the handler when the selection mode changed */ + void onHandlerModeChanged() + { + setModeOnViewParameters(); + } + + /** function that is called by the handler with a Vector2d position to update the widget + * + * It MUST be specialised if you want the parameters to update on mouseMove + */ + virtual void adaptParameters(Base::Vector2d onSketchPos) + { + Q_UNUSED(onSketchPos) + } + + /** function that is called by the handler with a mouse position, enabling the + * widget to override it having regard to the widget information. + * + * It MUST be specialised if you want to override mouse position based on parameters. + */ + void doEnforceControlParameters(Base::Vector2d& onSketchPos) + { + Q_UNUSED(onSketchPos) + } + + /** on first shortcut, it toggles the first checkbox if there is go. Must be specialised if + * this is not intended */ + virtual void firstKeyShortcut() + {} + + virtual void secondKeyShortcut() + {} + + virtual void tabShortcut() + { + passFocusToNextOnViewParameter(); + } + //@} + + + /// triggered by the controllable DSH after a mode change has been effected + virtual void afterHandlerModeChanged() + { + if (!handler->isState(SelectModeT::End) || handler->continuousMode) { + handler->mouseMove(prevCursorPosition); + } + } + +protected: + /** @name NVI for extension of controller functionality in derived classes */ + //@{ + virtual void doInitControls(QWidget* widget) + { + Q_UNUSED(widget) + } + + virtual void doResetControls() + { + resetOnViewParameters(); + } + virtual void onMouseMoved(Base::Vector2d originalSketchPosition) + { + Q_UNUSED(originalSketchPosition) + + if (!firstMoveInit) { + setModeOnViewParameters(); + } + } + + virtual void afterEnforceControlParameters() + { + // Give focus to current on-view parameter. In case user interacted outside of 3dview. + setFocusToOnViewParameter(onViewIndexWithFocus); + } + + virtual void doConstructionMethodChanged() + {} + //@} + +protected: + /** @name helper functions */ + //@{ + /// function to assist in adaptDrawingToComboboxChange specialisation + /// assigns the modevalue to the modeenum + /// it also triggers an update of the cursor + template + void setMode(T& modeenum, int modevalue) + { + auto mode = static_cast(modevalue); + + modeenum = mode; + + handler->updateCursor(); + + handler->resetControls(); // resetControls of handler to restart. + } + + /// function to redraw before and after any eventual mode change in reaction to a control + /// change + void finishControlsChanged() + { + handler->mouseMove(prevCursorPosition); + + auto currentstate = handler->state(); + // ensure that object at point is preselected, so that autoconstraints are generated + handler->preselectAtPoint(lastControlEnforcedPosition); + + doChangeDrawSketchHandlerMode(); + + // if the state changed and is not the last state (End). And is init (ie tool has not + // reset) + if (!handler->isLastState() && handler->state() != currentstate && firstMoveInit) { + // mode has changed, so reprocess the previous position to the new widget state + handler->mouseMove(prevCursorPosition); + } + } + + /** @brief Initialises on-screen parameters */ + void initNOnViewParameters(int n) + { + Gui::View3DInventorViewer* viewer = handler->getViewer(); + Base::Placement placement = handler->sketchgui->getSketchObject()->Placement.getValue(); + + onViewParameters.clear(); + + for (int i = 0; i < n; i++) { + + // the returned is a naked pointer + auto parameter = onViewParameters + .emplace_back(std::make_unique( + viewer, + placement, + colorManager.dimConstrDeactivatedColor, + /*autoDistance = */ true)) + .get(); + + QObject::connect(parameter, &Gui::EditableDatumLabel::valueChanged, [=](double value) { + parameter->setColor(colorManager.dimConstrColor); + onViewValueChanged(i, value); + }); + } + } + + /// Allows a on-view parameter to take any mouse mandated value (as opposed to enforce one) + void unsetOnViewParameter(Gui::EditableDatumLabel* onViewParameter) + { + onViewParameter->isSet = false; + onViewParameter->setColor(colorManager.dimConstrDeactivatedColor); + } + + /** Activates the correct set of on-view parameters corresponding to current + * mode. It may be specialized if necessary.*/ + void setModeOnViewParameters() + { + bool firstOfMode = true; + for (size_t i = 0; i < onViewParameters.size(); i++) { + if (!isOnViewParameterOfCurrentMode(i)) { + onViewParameters[i]->stopEdit(); + if (!onViewParameters[i]->isSet || handler->state() == SelectMode::End) { + onViewParameters[i]->deactivate(); + } + } + else { + if (firstOfMode) { + onViewIndexWithFocus = i; + firstOfMode = false; + } + + onViewParameters[i]->activate(); + + // points/value will be overridden by the mouseMove triggered by the mode change. + onViewParameters[i]->setPoints(Base::Vector3d(), Base::Vector3d()); + onViewParameters[i]->startEdit(0.0, keymanager.get()); + } + } + } + + /// This function gives the focus to a spinbox and tracks the focus. + void setFocusToOnViewParameter(unsigned int onviewparameterindex) + { + if (onviewparameterindex < onViewParameters.size()) { + onViewParameters[onviewparameterindex]->setFocusToSpinbox(); + onViewIndexWithFocus = onviewparameterindex; + } + } + + /// Switches focus to the next parameter in the current state machine. + void passFocusToNextOnViewParameter() + { + unsigned int index = onViewIndexWithFocus + 1; + + if (index >= onViewParameters.size()) { + index = 0; + } + while (index < onViewParameters.size()) { + if (isOnViewParameterOfCurrentMode(index)) { + setFocusToOnViewParameter(index); + break; + } + index++; + } + } + + /** Returns whether the provided on-view parameter index belongs to the current state of the + * state machine */ + bool isOnViewParameterOfCurrentMode(unsigned int onviewparameterindex) const + { + return onviewparameterindex < onViewParameters.size() + && getState(onviewparameterindex) == handler->state(); + } + + /** Returns whether the provided on-view parameter index belongs to the previous state of the + * state machine */ + bool isOnViewParameterOfPreviousMode(unsigned int onviewparameterindex) const + { + return onviewparameterindex < onViewParameters.size() + && getState(onviewparameterindex) < handler->state(); + } + + /** Resets the on-view parameter controls */ + void resetOnViewParameters() + { + initNOnViewParameters(nOnViewParameter); + onViewIndexWithFocus = 0; + + configureOnViewParameters(); + } + //@} + +private: + ColorManager colorManager; + std::unique_ptr keymanager; +}; + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_DrawSketchController_H diff --git a/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h b/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h index 1acb05dabe..070dceb807 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchDefaultHandler.h @@ -28,8 +28,10 @@ #include #include +#include #include +#include #include #include @@ -389,7 +391,7 @@ public: applyCursor(); } - virtual ~DrawSketchDefaultHandler() + ~DrawSketchDefaultHandler() override {} /** @name public DrawSketchHandler interface @@ -397,26 +399,26 @@ public: * overridden/specialised instead. */ //@{ - virtual void mouseMove(Base::Vector2d onSketchPos) override + void mouseMove(Base::Vector2d onSketchPos) override { updateDataAndDrawToPosition(onSketchPos); } - virtual bool pressButton(Base::Vector2d onSketchPos) override + bool pressButton(Base::Vector2d onSketchPos) override { onButtonPressed(onSketchPos); return true; } - virtual bool releaseButton(Base::Vector2d onSketchPos) override + bool releaseButton(Base::Vector2d onSketchPos) override { Q_UNUSED(onSketchPos); finish(); return true; } - virtual void registerPressedKey(bool pressed, int key) override + void registerPressedKey(bool pressed, int key) override { if (key == SoKeyboardEvent::M && pressed && !this->isLastState()) { this->iterateToNextConstructionMethod(); @@ -548,7 +550,7 @@ private: virtual void createAutoConstraints() {} - virtual void onConstructionMethodChanged() override {}; + void onConstructionMethodChanged() override {}; virtual void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) { @@ -569,7 +571,7 @@ protected: See documentation of the functions above*/ //@{ /** @brief Minimal handle activation respecting avoid redundants and continuous mode.*/ - virtual void activated() override + void activated() override { avoidRedundants = sketchgui->AvoidRedundant.getValue() && sketchgui->Autoconstraints.getValue(); @@ -592,10 +594,10 @@ protected: /** @brief Default behaviour that upon arriving to the End state of the state machine, the * command is finished. */ - virtual void onModeChanged() override + void onModeChanged() override { - finish(); // internally checks that state is SelectMode::End, and only finishes then. angleSnappingControl(); + finish(); // internally checks that state is SelectMode::End, and only finishes then. }; //@} @@ -885,7 +887,7 @@ protected: } // This is an awful situation. It should not be possible if the DSH works properly. It is - // just a saveguard. + // just a safeguard. if (sketchobject->getLastHasConflicts()) { THROWM(Base::RuntimeError, QT_TRANSLATE_NOOP( diff --git a/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h b/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h new file mode 100644 index 0000000000..74a2806b61 --- /dev/null +++ b/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetController.h @@ -0,0 +1,371 @@ +/*************************************************************************** + * Copyright (c) 2023 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef SKETCHERGUI_DrawSketchDefaultWidgetController_H +#define SKETCHERGUI_DrawSketchDefaultWidgetController_H + +#include +#include + +#include "DrawSketchController.h" + +namespace SketcherGui +{ + +/** @brief Type encapsulating the number of parameters in the widget*/ +template // Initial sizes for each mode +class WidgetParameters: public ControlAmount +{ +}; + +/** @brief Type encapsulating the number of checkboxes in the widget*/ +template // Initial sizes for each mode +class WidgetCheckboxes: public ControlAmount +{ +}; + +/** @brief Type encapsulating the number of comboboxes in the widget*/ +template // Initial sizes for each mode +class WidgetComboboxes: public ControlAmount +{ +}; + +namespace sp = std::placeholders; + +/** @brief Class defining a handler controller making use of parameters provided by a widget of type + * SketcherToolDefaultWidget. + * + * @details + * This class derives from DrawSketchController and thus provides on-view parameters too. In + * addition it manages a SketcherToolDefaultWidget in which a number of spinboxes, checkboxes and + * comboboxes are provided. + * + * It also provides automatic handling of change of creation method. + * + * This class is not intended to control based on a custom widget. + */ +template // The handler template or class having this as inner class +class DrawSketchDefaultWidgetController: public DrawSketchController +{ +public: + /** @name Meta-programming definitions and members */ + //@{ + using ControllerBase = DrawSketchController; + //@} + +protected: + int nParameter = WidgetParametersT::defaultMethodSize(); + int nCheckbox = WidgetCheckboxesT::defaultMethodSize(); + int nCombobox = WidgetComboboxesT::defaultMethodSize(); + + SketcherToolDefaultWidget* toolWidget; + + using Connection = boost::signals2::connection; + + Connection connectionParameterValueChanged; + Connection connectionCheckboxCheckedChanged; + Connection connectionComboboxSelectionChanged; + + /** @name Named indices for controls of the default widget (SketcherToolDefaultWidget) */ + //@{ + using WParameter = SketcherToolDefaultWidget::Parameter; + using WCheckbox = SketcherToolDefaultWidget::Checkbox; + using WCombobox = SketcherToolDefaultWidget::Combobox; + //@} + + using SelectMode = SelectModeT; + using ControllerBase::handler; + +public: + DrawSketchDefaultWidgetController(HandlerT* dshandler) + : ControllerBase(dshandler) + {} + + ~DrawSketchDefaultWidgetController() + { + connectionParameterValueChanged.disconnect(); + connectionCheckboxCheckedChanged.disconnect(); + connectionComboboxSelectionChanged.disconnect(); + } + + /** @name functions NOT intended for specialisation offering specialisation interface for + * extension */ + /** These functions offer a specialisation interface to ensure the order on initialisation. It + * is heavily encouraged to extend functionality using the specialisation interface. + */ + //@{ + /** boost slot triggering when a parameter has changed in the widget + * It is intended to remote control the DrawSketchDefaultWidgetHandler + */ + void parameterValueChanged(int parameterindex, double value) + { + adaptDrawingToParameterChange(parameterindex, value); // specialisation interface + + ControllerBase::finishControlsChanged(); + } + + /** boost slot triggering when a checkbox has changed in the widget + * It is intended to remote control the DrawSketchDefaultWidgetHandler + */ + void checkboxCheckedChanged(int checkboxindex, bool value) + { + adaptDrawingToCheckboxChange(checkboxindex, value); // specialisation interface + + ControllerBase::finishControlsChanged(); + } + + /** boost slot triggering when a combobox has changed in the widget + * It is intended to remote control the DrawSketchDefaultWidgetHandler + */ + void comboboxSelectionChanged(int comboboxindex, int value) + { + adaptDrawingToComboboxChange(comboboxindex, value); // specialisation interface + + ControllerBase::finishControlsChanged(); + } + //@} + + /** @name Specialisation Interface */ + /** These functions offer a specialisation interface. Non-virtual functions are specific to + * this controller. Virtual functions may depend on input from a derived controller, and thus + * the specialisation needs to be of an overriden version (so as to be able to access members + * of the derived controller). + */ + /// Change DSH to reflect a value entered in the widget + void adaptDrawingToParameterChange(int parameterindex, double value) + { + Q_UNUSED(parameterindex); + Q_UNUSED(value); + } + + /// Change DSH to reflect a checkbox changed in the widget + void adaptDrawingToCheckboxChange(int checkboxindex, bool value) + { + Q_UNUSED(checkboxindex); + Q_UNUSED(value); + } + + /// Change DSH to reflect a comboBox changed in the widget + void adaptDrawingToComboboxChange(int comboboxindex, int value) + { + Q_UNUSED(comboboxindex); + + if constexpr (PFirstComboboxIsConstructionMethod == true) { + + if (comboboxindex == WCombobox::FirstCombo && handler->ConstructionMethodsCount() > 1) { + handler->setConstructionMethod(static_cast(value)); + } + } + } + + /// function to create constraints based on widget information. + void addConstraints() override + {} + + /// function to configure the default widget. + void configureToolWidget() + {} + + /** function that is called by the handler with a Vector2d position to update the widget*/ + void doChangeDrawSketchHandlerMode() override + {} + + /** function that is called by the handler with a Vector2d position to update the widget */ + void adaptParameters(Base::Vector2d onSketchPos) override + { + Q_UNUSED(onSketchPos) + } + + /** on first shortcut, it toggles the first checkbox if there is go. Must be specialised if + * this is not intended */ + void firstKeyShortcut() override + { + if (nCheckbox >= 1) { + auto firstchecked = toolWidget->getCheckboxChecked(WCheckbox::FirstBox); + toolWidget->setCheckboxChecked(WCheckbox::FirstBox, !firstchecked); + } + } + + /** on second shortcut, it toggles the first checkbox if there is go. Must be specialised if + * this is not intended */ + void secondKeyShortcut() override + { + if (nCheckbox >= 2) { + auto secondchecked = toolWidget->getCheckboxChecked(WCheckbox::SecondBox); + toolWidget->setCheckboxChecked(WCheckbox::SecondBox, !secondchecked); + } + } + + //@} + +protected: + /** @name DrawSketchController NVI */ + //@{ + /// Initialises widget control + void doInitControls(QWidget* widget) override + { + initDefaultWidget(widget); + ControllerBase::doInitControls(widget); + } + + /// Resets widget controls + void doResetControls() override + { + ControllerBase::doResetControls(); + resetDefaultWidget(); + } + + /// Automatic default method update in combobox + void doConstructionMethodChanged() override + { + nParameter = WidgetParametersT::size(handler->constructionMethod()); + nCheckbox = WidgetCheckboxesT::size(handler->constructionMethod()); + nCombobox = WidgetComboboxesT::size(handler->constructionMethod()); + + // update the combobox only if necessary (if the change was not triggered by the + // combobox) + if constexpr (PFirstComboboxIsConstructionMethod == true) { + auto currentindex = toolWidget->getComboboxIndex(WCombobox::FirstCombo); + auto methodint = static_cast(handler->constructionMethod()); + + if (currentindex != methodint) { + // avoid triggering of method change + boost::signals2::shared_connection_block combobox_block( + connectionComboboxSelectionChanged); + toolWidget->setComboboxIndex(WCombobox::FirstCombo, methodint); + } + } + } + //@} + +private: + /// Initialisation of the widget + void initDefaultWidget(QWidget* widget) + { + toolWidget = static_cast(widget); + + connectionParameterValueChanged = toolWidget->registerParameterValueChanged( + std::bind(&DrawSketchDefaultWidgetController::parameterValueChanged, + this, + sp::_1, + sp::_2)); + + connectionCheckboxCheckedChanged = toolWidget->registerCheckboxCheckedChanged( + std::bind(&DrawSketchDefaultWidgetController::checkboxCheckedChanged, + this, + sp::_1, + sp::_2)); + + connectionComboboxSelectionChanged = toolWidget->registerComboboxSelectionChanged( + std::bind(&DrawSketchDefaultWidgetController::comboboxSelectionChanged, + this, + sp::_1, + sp::_2)); + } + + /// Resets the widget + void resetDefaultWidget() + { + boost::signals2::shared_connection_block parameter_block(connectionParameterValueChanged); + boost::signals2::shared_connection_block checkbox_block(connectionCheckboxCheckedChanged); + boost::signals2::shared_connection_block combobox_block(connectionComboboxSelectionChanged); + + toolWidget->initNParameters(nParameter); + toolWidget->initNCheckboxes(nCheckbox); + toolWidget->initNComboboxes(nCombobox); + + configureToolWidget(); + } + +private: + /** @name helper functions */ + //@{ + /// returns the status to which the handler was updated + bool syncHandlerToCheckbox(int checkboxindex, bool& handlerboolean) + { + bool status = toolWidget->getCheckboxChecked(checkboxindex); + handlerboolean = status; + + return status; + } + + /// returns true if checkbox was changed, and false if no sync was necessary + bool syncCheckboxToHandler(int checkboxindex, bool handlerboolean) + { + bool status = toolWidget->getCheckboxChecked(checkboxindex); + if (handlerboolean != status) { + toolWidget->setCheckboxChecked(checkboxindex, handlerboolean); + return true; + } + + return false; + } + + /// Syncs the handler to the construction method selection in the combobox + void syncHandlerToConstructionMethodCombobox() + { + + if constexpr (PFirstComboboxIsConstructionMethod == true) { + auto constructionmethod = toolWidget->getComboboxIndex(WCombobox::FirstCombo); + + handler->initConstructionMethod(static_cast(constructionmethod)); + } + } + /// Syncs the construction method selection in the combobox to the handler selection + void syncConstructionMethodComboboxToHandler() + { + + if constexpr (PFirstComboboxIsConstructionMethod == true) { + auto constructionmethod = toolWidget->getComboboxIndex(WCombobox::FirstCombo); + + auto actualconstructionmethod = static_cast(handler->constructionMethod()); + + if (constructionmethod != actualconstructionmethod) { + toolWidget->setComboboxIndex(WCombobox::FirstCombo, actualconstructionmethod); + } + } + } + //@} +}; + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_DrawSketchDefaultWidgetController_H diff --git a/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetHandler.h b/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetHandler.h deleted file mode 100644 index a88992232f..0000000000 --- a/src/Mod/Sketcher/Gui/DrawSketchDefaultWidgetHandler.h +++ /dev/null @@ -1,1287 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2022 Abdullah Tahiri * - * * - * This file is part of the FreeCAD CAx development system. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Library General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * - * * - ***************************************************************************/ - - -#ifndef SKETCHERGUI_DrawSketchHandlerDefaultWidget_H -#define SKETCHERGUI_DrawSketchHandlerDefaultWidget_H - -#include "DrawSketchDefaultHandler.h" - -#include "SketcherToolDefaultWidget.h" - -#include -#include - -#include - -namespace bp = boost::placeholders; - -namespace SketcherGui -{ - -/** @brief Template class intended for handlers that interact with SketcherToolDefaultWidget. - * - * @details - * The template encapsulates a DSH and a nested class ToolWidgetManager which is responsible for - * handling the interaction between the DSH and SketcherToolDefaultWidget. - * - * This class can be used: - * 1. By instantiating it and specialising it (both NVI and ToolWidgetManager). - * 2. By deriving from it to implement the DSH via the NVI interface and by specialising the nested - * ToolWidgetManager class functions. - * - * Method 2 enables to add new data members. - * - * Template Types/Parameters: - * PTool : Parameter to specialise behaviour to a specific tool - * SelectModeT : The type of statemachine to be used (see namespace StateMachines above). - * PInitEditCurveSize : Initial size of the EditCurve vector - * PInitAutoConstraintSize : Initial size of the AutoConstraint vector - * WidgetParametersT: The number of parameter spinboxes in the default widget - * WidgetCheckboxesT: The number of checkboxes in the default widget - * - * Widget: - * - Automatically initialises the parameters necessary - * - Automatically connects boost signals of the widget to slots. - * - Interface to modify point coordinates based on widget state. - * - Interface to update Widget with value. - * - * Widget MUST specialise: - * - configureToolWidget() for example to initialise widget labels. - * - doUpdateDrawSketchHandlerValues() to update EditData when Widget values are changed. - * - addConstraints() to add any constraint mandated by the Widget. - * - * Widget MAY need to specialise, if default behaviour is not appropriate: - * - doChangeDrawSketchHandlerMode() - * - onHandlerModeChanged() - * - adaptParameters() - * - doEnforceWidgetParameters() - * - * Handler provides: - * - generic initialisation - * - structured command finalisation - * - handling of continuous creation mode - * - interaction with widget - * - * Handler MUST specialise: - * - getToolName => provide the string name of the tool - * - getCrosshairCursorSVGName => provide the string for the svg icon of the cursor - * - updateEditDataAndDrawToPositionImpl => function to update the EditData structure and draw a - * temporal curve - * - executeCommands => execution of commands to create the geometry - * - createAutoConstraints => execution of commands to create autoconstraints - * (widget mandated constraints are called BEFORE this) - * - * Question: Do I need to use this handler or derive from this handler to make a new handler using - * the default tool widget? - * - * No, you do not NEED to. But you are encouraged to. Structuring a handler following this NVI, - * apart from substantial savings in amount of code typed, enables a much easier and less verbose - * implementation of a handler using a default widget (toolwidget), and will lead to easier to - * maintain code. - */ - -template // Initial sizes for each mode -class WidgetInitialValues -{ -public: - template - static constexpr int size(constructionT constructionmethod) - { - auto modeint = static_cast(constructionmethod); - - return constructionMethodParameters[modeint]; - } - - static constexpr int defaultMethodSize() - { - return size(0); - } - -private: - static constexpr std::array constructionMethodParameters = {{sizes...}}; -}; - -template // Initial sizes for each mode -class OnViewParameters: public WidgetInitialValues -{ -}; - -template // Initial sizes for each mode -class WidgetParameters: public WidgetInitialValues -{ -}; - -template // Initial sizes for each mode -class WidgetCheckboxes: public WidgetInitialValues -{ -}; - -template // Initial sizes for each mode -class WidgetComboboxes: public WidgetInitialValues -{ -}; - -template -class DrawSketchDefaultWidgetHandler: public DrawSketchDefaultHandler -{ - using DSDefaultHandler = - DrawSketchDefaultHandler; - using ConstructionMachine = ConstructionMethodMachine; - -private: - class ToolWidgetManager - { - int nOnViewParameter = OnViewParametersT::defaultMethodSize(); - int nParameter = WidgetParametersT::defaultMethodSize(); - int nCheckbox = WidgetCheckboxesT::defaultMethodSize(); - int nCombobox = WidgetComboboxesT::defaultMethodSize(); - - // std::array - // constructionMethodParameters; - - SketcherToolDefaultWidget* toolWidget; - DrawSketchDefaultWidgetHandler* handler; // used to access private implementations - HandlerT* dHandler; // real derived type - bool init = false; // returns true if the widget has been configured - - using Connection = boost::signals2::connection; - - Connection connectionParameterValueChanged; - Connection connectionCheckboxCheckedChanged; - Connection connectionComboboxSelectionChanged; - - Base::Vector2d prevCursorPosition; - Base::Vector2d lastWidgetEnforcedPosition; - - enum WLabel - { - First, - Second, - Third, - Fourth, - Fifth, - Sixth, - Seventh, - Eighth, - Ninth, - Tenth, - nLabels // Must Always be the last one - }; - using WParameter = SketcherToolDefaultWidget::Parameter; - using WCheckbox = SketcherToolDefaultWidget::Checkbox; - using WCombobox = SketcherToolDefaultWidget::Combobox; - using SelectMode = SelectModeT; - - std::vector onViewParameters; - - public: - bool firstMoveInit = false; - int labelIndexWithFocus = 0; - - public: - ToolWidgetManager(DrawSketchDefaultWidgetHandler* dshandler) - : handler(dshandler) - , dHandler(static_cast(dshandler)) - {} - - ~ToolWidgetManager() - { - connectionParameterValueChanged.disconnect(); - connectionCheckboxCheckedChanged.disconnect(); - connectionComboboxSelectionChanged.disconnect(); - - for (auto* label : onViewParameters) { - delete label; - } - } - - /** @name functions NOT intended for specialisation */ - //@{ - void initWidget(QWidget* widget) - { - toolWidget = static_cast(widget); - - connectionParameterValueChanged = toolWidget->registerParameterValueChanged( - boost::bind(&ToolWidgetManager::parameterValueChanged, this, bp::_1, bp::_2)); - - connectionCheckboxCheckedChanged = toolWidget->registerCheckboxCheckedChanged( - boost::bind(&ToolWidgetManager::checkboxCheckedChanged, this, bp::_1, bp::_2)); - - connectionComboboxSelectionChanged = toolWidget->registerComboboxSelectionChanged( - boost::bind(&ToolWidgetManager::comboboxSelectionChanged, this, bp::_1, bp::_2)); - - reset(); - init = true; - } - - void reset() - { - - boost::signals2::shared_connection_block parameter_block( - connectionParameterValueChanged); - boost::signals2::shared_connection_block checkbox_block( - connectionCheckboxCheckedChanged); - boost::signals2::shared_connection_block combobox_block( - connectionComboboxSelectionChanged); - - initNLabels(nOnViewParameter); - toolWidget->initNParameters(nParameter); - toolWidget->initNCheckboxes(nCheckbox); - toolWidget->initNComboboxes(nCombobox); - - configureToolWidget(); - - firstMoveInit = false; - labelIndexWithFocus = 0; - } - - void initNLabels(int n) - { - Gui::View3DInventorViewer* viewer = handler->getViewer(); - Base::Placement placement = handler->sketchgui->getSketchObject()->Placement.getValue(); - - for (auto* label : onViewParameters) { - delete label; - } - onViewParameters.clear(); - - for (int i = 0; i < n; i++) { - auto* label = new Gui::EditableDatumLabel(viewer, - placement, - SbColor(0.8f, 0.8f, 0.8f), - /*autoDistance = */ true); - QObject::connect(label, &Gui::EditableDatumLabel::valueChanged, [=](double value) { - label->setColor(SbColor(1.0f, 0.149f, 0.0f)); - labelValueChanged(i, value); - }); - onViewParameters.push_back(label); - } - } - - void enforceWidgetParameters(Base::Vector2d& onSketchPos) - { - prevCursorPosition = onSketchPos; - - doEnforceWidgetParameters(onSketchPos); - - lastWidgetEnforcedPosition = onSketchPos; // store enforced cursor position. - - // Give focus to current label. In case user interacted outside of 3dview. - setFocusToLabel(labelIndexWithFocus); - } - - /** slot triggering when a on view parameter has changed - * It is intended to remote control the DrawSketchDefaultWidgetHandler - */ - void labelValueChanged(int labelindex, double value) - { - if (isLabelOfCurrentMode(labelindex + 1)) { - setFocusToLabel(labelindex + 1); - } - - // -> A machine does not forward to a next state when adapting the parameter (though it - // may forward to - // a next state if all the parameters are fulfilled, see - // doChangeDrawSketchHandlerMode). This ensures that the geometry has been defined - // (either by mouse clicking or by widget). Autoconstraints on point should be picked - // when the state is reached upon machine state advancement. - // - // -> A machine goes back to a previous state if a parameter of a previous state is - // modified. This ensures - // that appropriate autoconstraints are picked. - if (isLabelOfPreviousMode(labelindex)) { - // change to previous state - handler->setState(getState(labelindex)); - } - - enforceWidgetParametersOnPreviousCursorPosition(); - - adaptDrawingToLabelChange(labelindex, value); - - finishWidgetChanged(); - } - - /** boost slot triggering when a parameter has changed in the widget - * It is intended to remote control the DrawSketchDefaultWidgetHandler - */ - void parameterValueChanged(int parameterindex, double value) - { - enforceWidgetParametersOnPreviousCursorPosition(); - - adaptDrawingToParameterChange(parameterindex, value); - - finishWidgetChanged(); - } - - /** boost slot triggering when a checkbox has changed in the widget - * It is intended to remote control the DrawSketchDefaultWidgetHandler - */ - void checkboxCheckedChanged(int checkboxindex, bool value) - { - enforceWidgetParametersOnPreviousCursorPosition(); - - adaptDrawingToCheckboxChange(checkboxindex, value); - - onHandlerModeChanged(); // re-focus/select spinbox - - finishWidgetChanged(); - } - - /** boost slot triggering when a combobox has changed in the widget - * It is intended to remote control the DrawSketchDefaultWidgetHandler - */ - void comboboxSelectionChanged(int comboboxindex, int value) - { - enforceWidgetParametersOnPreviousCursorPosition(); - - adaptDrawingToComboboxChange(comboboxindex, value); - - finishWidgetChanged(); - } - - void adaptParameters() - { - adaptParameters(lastWidgetEnforcedPosition); - } - - //@} - - /** @name functions which MUST be specialised */ - //@{ - /// Change DSH to reflect a value entered in the view - void adaptDrawingToLabelChange(int labelindex, double value) - { - Q_UNUSED(labelindex); - Q_UNUSED(value); - } - - /// Change DSH to reflect a value entered in the widget - void adaptDrawingToParameterChange(int parameterindex, double value) - { - Q_UNUSED(parameterindex); - Q_UNUSED(value); - } - - /// Change DSH to reflect a checkbox changed in the widget - void adaptDrawingToCheckboxChange(int checkboxindex, bool value) - { - Q_UNUSED(checkboxindex); - Q_UNUSED(value); - } - - /// Change DSH to reflect a comboBox changed in the widget - void adaptDrawingToComboboxChange(int comboboxindex, int value) - { - Q_UNUSED(comboboxindex); - Q_UNUSED(value); - - if constexpr (PFirstComboboxIsConstructionMethod == true) { - - if (comboboxindex == WCombobox::FirstCombo - && handler->ConstructionMethodsCount() > 1) { - handler->iterateToNextConstructionMethod(); - } - } - } - - /** Returns the state to which the widget parameter corresponds in the current construction - * method - */ - auto getState(int parameterindex) const - { - Q_UNUSED(parameterindex); - return handler->getFirstState(); - } - - /// function to create constraints based on widget information. - void addConstraints() - {} - //@} - - /** @name functions which MAY need to be specialised */ - //@{ - /// Function to specialise to set the correct widget strings and commands - void configureToolWidget() - { - if constexpr (std::is_same_v) { - toolWidget->setParameterLabel( - WParameter::First, - QApplication::translate("ToolWidgetManager_p1", "x of point")); - toolWidget->setParameterLabel( - WParameter::Second, - QApplication::translate("ToolWidgetManager_p2", "y of point")); - } - else if constexpr (std::is_same_v) { - toolWidget->setParameterLabel( - WParameter::First, - QApplication::translate("ToolWidgetManager_p1", "x of 1st point")); - toolWidget->setParameterLabel( - WParameter::Second, - QApplication::translate("ToolWidgetManager_p2", "y of 1st point")); - toolWidget->setParameterLabel( - WParameter::Third, - QApplication::translate("ToolWidgetManager_p3", "x of 2nd point")); - toolWidget->setParameterLabel( - WParameter::Fourth, - QApplication::translate("ToolWidgetManager_p4", "y of 2nd point")); - } - else if constexpr (std::is_same_v) { - toolWidget->setParameterLabel( - WParameter::First, - QApplication::translate("ToolWidgetManager_p1", "x of 1st point")); - toolWidget->setParameterLabel( - WParameter::Second, - QApplication::translate("ToolWidgetManager_p2", "y of 1st point")); - toolWidget->setParameterLabel( - WParameter::Third, - QApplication::translate("ToolWidgetManager_p3", "x of 2nd point")); - toolWidget->setParameterLabel( - WParameter::Fourth, - QApplication::translate("ToolWidgetManager_p4", "y of 2nd point")); - toolWidget->setParameterLabel( - WParameter::Fifth, - QApplication::translate("ToolWidgetManager_p5", "x of 3rd point")); - toolWidget->setParameterLabel( - WParameter::Sixth, - QApplication::translate("ToolWidgetManager_p6", "y of 3rd point")); - } - else if constexpr (std::is_same_v) { - toolWidget->setParameterLabel( - WParameter::First, - QApplication::translate("ToolWidgetManager_p1", "x of 1st point")); - toolWidget->setParameterLabel( - WParameter::Second, - QApplication::translate("ToolWidgetManager_p2", "y of 1st point")); - toolWidget->setParameterLabel( - WParameter::Third, - QApplication::translate("ToolWidgetManager_p3", "x of 2nd point")); - toolWidget->setParameterLabel( - WParameter::Fourth, - QApplication::translate("ToolWidgetManager_p4", "y of 2nd point")); - toolWidget->setParameterLabel( - WParameter::Fifth, - QApplication::translate("ToolWidgetManager_p5", "x of 3rd point")); - toolWidget->setParameterLabel( - WParameter::Sixth, - QApplication::translate("ToolWidgetManager_p6", "y of 3rd point")); - } - } - - /// Function triggered before the first mouse move, to be used to initialize the on view - /// labels. - void beforeFirstMouseMove(Base::Vector2d onSketchPos) - { - Q_UNUSED(onSketchPos); - } - - /** Change DSH to reflect the SelectMode it should be in based on values entered in the - * widget - * - * This is just a default implementation for common stateMachines, that may - * or may not do what you expect. It assumes two parameters per seek state. - * - * It MUST be specialised otherwise - */ - void doChangeDrawSketchHandlerMode() - { - if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First) - && toolWidget->isParameterSet(WParameter::Second)) { - - handler->setState(SelectMode::End); - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First) - && toolWidget->isParameterSet(WParameter::Second)) { - - handler->setState(SelectMode::SeekSecond); - } - } break; - case SelectMode::SeekSecond: { - if (toolWidget->isParameterSet(WParameter::Third) - || toolWidget->isParameterSet(WParameter::Fourth)) { - - if (toolWidget->isParameterSet(WParameter::Third) - && toolWidget->isParameterSet(WParameter::Fourth)) { - - handler->setState(SelectMode::End); - } - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First) - && toolWidget->isParameterSet(WParameter::Second)) { - - handler->setState(SelectMode::SeekSecond); - } - } break; - case SelectMode::SeekSecond: { - if (toolWidget->isParameterSet(WParameter::Third) - || toolWidget->isParameterSet(WParameter::Fourth)) { - - if (toolWidget->isParameterSet(WParameter::Third) - && toolWidget->isParameterSet(WParameter::Fourth)) { - - handler->setState(SelectMode::SeekThird); - } - } - } break; - case SelectMode::SeekThird: { - if (toolWidget->isParameterSet(WParameter::Fifth) - || toolWidget->isParameterSet(WParameter::Sixth)) { - - if (toolWidget->isParameterSet(WParameter::Fifth) - && toolWidget->isParameterSet(WParameter::Sixth)) { - - handler->setState(SelectMode::End); - } - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First) - && toolWidget->isParameterSet(WParameter::Second)) { - - handler->setState(SelectMode::SeekSecond); - } - } break; - case SelectMode::SeekSecond: { - if (toolWidget->isParameterSet(WParameter::Third) - || toolWidget->isParameterSet(WParameter::Fourth)) { - - if (toolWidget->isParameterSet(WParameter::Third) - && toolWidget->isParameterSet(WParameter::Fourth)) { - - handler->setState(SelectMode::SeekThird); - } - } - } break; - case SelectMode::SeekThird: { - if (toolWidget->isParameterSet(WParameter::Fifth)) { - - handler->setState(SelectMode::SeekFourth); - } - } break; - case SelectMode::SeekFourth: { - if (toolWidget->isParameterSet(WParameter::Sixth)) { - - handler->setState(SelectMode::End); - } - } break; - default: - break; - } - } - } - - /** function that is called by the handler when the selection mode changed - * - * This is just a default implementation for common stateMachines, that may - * or may not do what you expect. It assumes two parameters per seek state. - * - * It MUST be specialised otherwise - */ - void onHandlerModeChanged() - { - if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: - toolWidget->setParameterFocus(WParameter::First); - break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: - toolWidget->setParameterFocus(WParameter::First); - break; - case SelectMode::SeekSecond: - toolWidget->setParameterFocus(WParameter::Third); - break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: - toolWidget->setParameterFocus(WParameter::First); - break; - case SelectMode::SeekSecond: - toolWidget->setParameterFocus(WParameter::Third); - break; - case SelectMode::SeekThird: - toolWidget->setParameterFocus(WParameter::Fifth); - break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: - toolWidget->setParameterFocus(WParameter::First); - break; - case SelectMode::SeekSecond: - toolWidget->setParameterFocus(WParameter::Third); - break; - case SelectMode::SeekThird: - toolWidget->setParameterFocus(WParameter::Fifth); - break; - case SelectMode::SeekFourth: - toolWidget->setParameterFocus(WParameter::Sixth); - break; - default: - break; - } - } - } - - /** function that is called by the handler when the construction mode changed - * - * This is just a default implementation for common stateMachines, that may - * or may not do what you expect. It assumes two parameters per seek state. - * - * It MUST be specialised otherwise - */ - void onConstructionMethodChanged() - { - - nOnViewParameter = OnViewParametersT::size(handler->constructionMethod()); - nParameter = WidgetParametersT::size(handler->constructionMethod()); - nCheckbox = WidgetCheckboxesT::size(handler->constructionMethod()); - nCombobox = WidgetComboboxesT::size(handler->constructionMethod()); - - // update the combobox only if necessary (if the change was not triggered by the - // combobox) - if constexpr (PFirstComboboxIsConstructionMethod == true) { - auto currentindex = toolWidget->getComboboxIndex(WCombobox::FirstCombo); - auto methodint = static_cast(handler->constructionMethod()); - - if (currentindex != methodint) { - // avoid triggering of method change - boost::signals2::shared_connection_block combobox_block( - connectionComboboxSelectionChanged); - toolWidget->setComboboxIndex(WCombobox::FirstCombo, methodint); - } - } - - dHandler->updateCursor(); - - dHandler->reset(); // reset of handler to restart. - } - - /** function that is called by the handler with a Vector2d position to update the widget - * - * This is just a default implementation for common stateMachines, that may - * or may not do what you expect. It assumes two parameters per seek state. - * - * It MUST be specialised if the states correspond to different parameters - */ - void adaptParameters(Base::Vector2d onSketchPos) - { - if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (!toolWidget->isParameterSet(WParameter::First)) { - toolWidget->updateVisualValue(WParameter::First, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Second)) { - toolWidget->updateVisualValue(WParameter::Second, onSketchPos.y); - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (!toolWidget->isParameterSet(WParameter::First)) { - toolWidget->updateVisualValue(WParameter::First, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Second)) { - toolWidget->updateVisualValue(WParameter::Second, onSketchPos.y); - } - } break; - case SelectMode::SeekSecond: { - if (!toolWidget->isParameterSet(WParameter::Third)) { - toolWidget->updateVisualValue(WParameter::Third, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Fourth)) { - toolWidget->updateVisualValue(WParameter::Fourth, onSketchPos.y); - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (!toolWidget->isParameterSet(WParameter::First)) { - toolWidget->updateVisualValue(WParameter::First, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Second)) { - toolWidget->updateVisualValue(WParameter::Second, onSketchPos.y); - } - } break; - case SelectMode::SeekSecond: { - if (!toolWidget->isParameterSet(WParameter::Third)) { - toolWidget->updateVisualValue(WParameter::Third, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Fourth)) { - toolWidget->updateVisualValue(WParameter::Fourth, onSketchPos.y); - } - } break; - case SelectMode::SeekThird: { - if (!toolWidget->isParameterSet(WParameter::Fifth)) { - toolWidget->updateVisualValue(WParameter::Fifth, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Sixth)) { - toolWidget->updateVisualValue(WParameter::Sixth, onSketchPos.y); - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (!toolWidget->isParameterSet(WParameter::First)) { - toolWidget->updateVisualValue(WParameter::First, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Second)) { - toolWidget->updateVisualValue(WParameter::Second, onSketchPos.y); - } - } break; - case SelectMode::SeekSecond: { - if (!toolWidget->isParameterSet(WParameter::Third)) { - toolWidget->updateVisualValue(WParameter::Third, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Fourth)) { - toolWidget->updateVisualValue(WParameter::Fourth, onSketchPos.y); - } - } break; - case SelectMode::SeekThird: { - if (!toolWidget->isParameterSet(WParameter::Fifth)) { - toolWidget->updateVisualValue(WParameter::Fifth, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Sixth)) { - toolWidget->updateVisualValue(WParameter::Sixth, onSketchPos.y); - } - } break; - case SelectMode::SeekFourth: { - if (!toolWidget->isParameterSet(WParameter::Fifth)) { - toolWidget->updateVisualValue(WParameter::Fifth, onSketchPos.x); - } - - if (!toolWidget->isParameterSet(WParameter::Sixth)) { - toolWidget->updateVisualValue(WParameter::Sixth, onSketchPos.y); - } - } break; - default: - break; - } - } - } - - /** function that is called by the handler with a mouse position, enabling the - * widget to override it having regard to the widget information. - * - * This is just a default implementation for common stateMachines, that may - * or may not do what you expect. It assumes two parameters per seek state. - * - * It MUST be specialised if the states correspond to different parameters - */ - void doEnforceWidgetParameters(Base::Vector2d& onSketchPos) - { - if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First)) { - onSketchPos.x = toolWidget->getParameter(WParameter::First); - } - - if (toolWidget->isParameterSet(WParameter::Second)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Second); - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First)) { - onSketchPos.x = toolWidget->getParameter(WParameter::First); - } - - if (toolWidget->isParameterSet(WParameter::Second)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Second); - } - } break; - case SelectMode::SeekSecond: { - if (toolWidget->isParameterSet(WParameter::Third)) { - onSketchPos.x = toolWidget->getParameter(WParameter::Third); - } - - if (toolWidget->isParameterSet(WParameter::Fourth)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Fourth); - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First)) { - onSketchPos.x = toolWidget->getParameter(WParameter::First); - } - - if (toolWidget->isParameterSet(WParameter::Second)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Second); - } - } break; - case SelectMode::SeekSecond: { - if (toolWidget->isParameterSet(WParameter::Third)) { - onSketchPos.x = toolWidget->getParameter(WParameter::Third); - } - - if (toolWidget->isParameterSet(WParameter::Fourth)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Fourth); - } - } break; - case SelectMode::SeekThird: { - if (toolWidget->isParameterSet(WParameter::Fifth)) { - onSketchPos.x = toolWidget->getParameter(WParameter::Fifth); - } - - if (toolWidget->isParameterSet(WParameter::Sixth)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Sixth); - } - } break; - default: - break; - } - } - else if constexpr (std::is_same_v) { - switch (handler->state()) { - case SelectMode::SeekFirst: { - if (toolWidget->isParameterSet(WParameter::First)) { - onSketchPos.x = toolWidget->getParameter(WParameter::First); - } - - if (toolWidget->isParameterSet(WParameter::Second)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Second); - } - } break; - case SelectMode::SeekSecond: { - if (toolWidget->isParameterSet(WParameter::Third)) { - onSketchPos.x = toolWidget->getParameter(WParameter::Third); - } - - if (toolWidget->isParameterSet(WParameter::Fourth)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Fourth); - } - } break; - case SelectMode::SeekThird: { - if (toolWidget->isParameterSet(WParameter::Fifth)) { - onSketchPos.x = toolWidget->getParameter(WParameter::Fifth); - } - - if (toolWidget->isParameterSet(WParameter::Sixth)) { - onSketchPos.y = toolWidget->getParameter(WParameter::Sixth); - } - } break; - case SelectMode::SeekFourth: { - // nothing. It has to be reimplemented. - } break; - default: - break; - } - } - } - - /** on first shortcut, it toggles the first checkbox if there is go. Must be specialised if - * this is not intended */ - void firstKeyShortcut() - { - if (nCheckbox >= 1) { - auto firstchecked = toolWidget->getCheckboxChecked(WCheckbox::FirstBox); - toolWidget->setCheckboxChecked(WCheckbox::FirstBox, !firstchecked); - } - } - - void secondKeyShortcut() - { - if (nCheckbox >= 2) { - auto secondchecked = toolWidget->getCheckboxChecked(WCheckbox::SecondBox); - toolWidget->setCheckboxChecked(WCheckbox::SecondBox, !secondchecked); - } - } - - /* This function give the focus to a spinbox and register to which it gave it.*/ - void setFocusToLabel(unsigned int labelindex) - { - if (labelindex < onViewParameters.size()) { - onViewParameters[labelindex]->setFocusToSpinbox(); - labelIndexWithFocus = labelindex; - } - } - //@} - - void passFocusToNextLabel() - { - unsigned int index = labelIndexWithFocus + 1; - - if (index >= onViewParameters.size()) { - index = 0; - } - while (index < onViewParameters.size()) { - if (isLabelOfCurrentMode(index)) { - setFocusToLabel(index); - break; - } - index++; - } - } - - private: - /** @name helper functions */ - //@{ - /// function to assist in adaptDrawingToComboboxChange specialisation - /// assigns the modevalue to the modeenum and updates the number of parameters according to - /// map it also triggers an update of the cursor - /* - template - void setModeAndAdaptParameters(T & modeenum, int modevalue, const std::vector & - parametersmap) - { - if (modevalue < static_cast(parametersmap.size())) { - auto mode = static_cast(modevalue); - - nOnViewParameter = OnViewParametersT::constructionMethodParameters[modevalue]; - nParameter = WidgetParametersT::constructionMethodParameters[modevalue]; - nCheckbox = WidgetCheckboxesT::constructionMethodParameters[modevalue]; - nCombobox = WidgetComboboxesT::constructionMethodParameters[modevalue]; - - - modeenum = mode; - - dHandler->updateCursor(); - - reset(); //reset the widget to take into account the change of nparameter - dHandler->reset(); //reset of handler to restart. - } - } - */ - - /// function to assist in adaptDrawingToComboboxChange specialisation - /// assigns the modevalue to the modeenum - /// it also triggers an update of the cursor - template - void setMode(T& modeenum, int modevalue) - { - auto mode = static_cast(modevalue); - - modeenum = mode; - - dHandler->updateCursor(); - - dHandler->reset(); // reset of handler to restart. - } - - /// function to redraw before and after any eventual mode change in reaction to a widget - /// change - void finishWidgetChanged() - { - - // handler->moveCursorToSketchPoint(lastWidgetEnforcedPosition); - - auto currentstate = handler->state(); - // ensure that object at point is preselected, so that autoconstraints are generated - handler->preselectAtPoint(lastWidgetEnforcedPosition); - // ensure drawing in the previous mode - handler->updateDataAndDrawToPosition(lastWidgetEnforcedPosition); - - doChangeDrawSketchHandlerMode(); - - // if the state changed and is not the last state (End) - if (!handler->isLastState() && handler->state() != currentstate) { - // mode has changed, so reprocess the previous position to the new widget state - enforceWidgetParametersOnPreviousCursorPosition(); - - // update the widget if state changed - adaptParameters(lastWidgetEnforcedPosition); - - // ensure drawing in the next mode - handler->updateDataAndDrawToPosition(lastWidgetEnforcedPosition); - } - } - - void enforceWidgetParametersOnPreviousCursorPosition() - { - auto simulatedCursorPosition = - prevCursorPosition; // ensure prevCursorPosition is preserved - - doEnforceWidgetParameters( - simulatedCursorPosition); // updates lastWidgetEnforcedPosition with new widget - // state - - lastWidgetEnforcedPosition = - simulatedCursorPosition; // store enforced cursor position. - } - - /// returns the status to which the handler was updated - bool syncHandlerToCheckbox(int checkboxindex, bool& handlerboolean) - { - bool status = toolWidget->getCheckboxChecked(checkboxindex); - handlerboolean = status; - - return status; - } - - /// returns true if checkbox was changed, and false if no sync was necessary - bool syncCheckboxToHandler(int checkboxindex, bool handlerboolean) - { - bool status = toolWidget->getCheckboxChecked(checkboxindex); - if (handlerboolean != status) { - toolWidget->setCheckboxChecked(checkboxindex, handlerboolean); - return true; - } - - return false; - } - - void syncHandlerToConstructionMethodCombobox() - { - - if constexpr (PFirstComboboxIsConstructionMethod == true) { - auto constructionmethod = toolWidget->getComboboxIndex(WCombobox::FirstCombo); - - handler->initConstructionMethod( - static_cast(constructionmethod)); - } - } - void syncConstructionMethodComboboxToHandler() - { - - if constexpr (PFirstComboboxIsConstructionMethod == true) { - auto constructionmethod = toolWidget->getComboboxIndex(WCombobox::FirstCombo); - - auto actualconstructionmethod = static_cast(handler->constructionMethod()); - - if (constructionmethod != actualconstructionmethod) { - toolWidget->setComboboxIndex(WCombobox::FirstCombo, actualconstructionmethod); - } - } - } - - bool isLabelOfCurrentMode(unsigned int labelindex) const - { - return labelindex < onViewParameters.size() && getState(labelindex) == handler->state(); - } - - bool isLabelOfPreviousMode(unsigned int labelindex) const - { - return labelindex < onViewParameters.size() && getState(labelindex) < handler->state(); - } - //@} - }; - -public: - DrawSketchDefaultWidgetHandler( - ConstructionMethodT constructionmethod = static_cast(0)) - : DSDefaultHandler(constructionmethod) - , toolWidgetManager(this) - {} - - virtual ~DrawSketchDefaultWidgetHandler() = default; - - /** @name functions NOT intended for specialisation */ - //@{ - virtual void mouseMove(Base::Vector2d onSketchPos) override - { - if (!toolWidgetManager.firstMoveInit) { - toolWidgetManager.firstMoveInit = true; - toolWidgetManager.beforeFirstMouseMove(onSketchPos); - } - toolWidgetManager.enforceWidgetParameters(onSketchPos); - toolWidgetManager.adaptParameters(onSketchPos); - updateDataAndDrawToPosition(onSketchPos); - } - - virtual bool pressButton(Base::Vector2d onSketchPos) override - { - toolWidgetManager.enforceWidgetParameters(onSketchPos); - - onButtonPressed(onSketchPos); - return true; - } - - virtual bool releaseButton(Base::Vector2d onSketchPos) override - { - Q_UNUSED(onSketchPos); - DSDefaultHandler::finish(); - return true; - } - //@} - - -protected: - /** @name functions requiring specialisation */ - //@{ - virtual std::string getToolName() const override - { - return DrawSketchHandler::getToolName(); - } - virtual QString getCrosshairCursorSVGName() const override - { - return DrawSketchHandler::getCrosshairCursorSVGName(); - } - //@} - -private: - /** @name functions requiring specialisation */ - //@{ - // For every machine state, it updates the EditData temporary - // curve, and draws the temporary curve during edit mode. - virtual void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override - { - Q_UNUSED(onSketchPos) - }; - - virtual void executeCommands() override {}; - virtual void createAutoConstraints() override {}; - //@} - - /** @name functions which MAY require specialisation*/ - //@{ - /** Default implementation is that on every mouse click the mode is changed to the next seek - On the last seek, it changes to SelectMode::End - If this behaviour is not acceptable, then the function must be specialised.*/ - virtual void onButtonPressed(Base::Vector2d onSketchPos) override - { - DSDefaultHandler::onButtonPressed(onSketchPos); - } - - virtual void beforeCreateAutoConstraints() override - { - toolWidgetManager.addConstraints(); - } - - virtual void onWidgetChanged() override - { - toolWidgetManager.initWidget(DSDefaultHandler::toolwidget); - } - - virtual void onReset() override - { - toolWidgetManager.reset(); - } - - virtual void onModeChanged() override - { - toolWidgetManager.onHandlerModeChanged(); - DSDefaultHandler::onModeChanged(); - } - - virtual void onConstructionMethodChanged() override - { - toolWidgetManager.onConstructionMethodChanged(); - } - - virtual void registerPressedKey(bool pressed, int key) override - { - DSDefaultHandler::registerPressedKey(pressed, key); - - if (key == SoKeyboardEvent::U && !pressed && !this->isLastState()) { - toolWidgetManager.firstKeyShortcut(); - } - - if (key == SoKeyboardEvent::J && !pressed && !this->isLastState()) { - toolWidgetManager.secondKeyShortcut(); - } - - if (key == SoKeyboardEvent::TAB && !pressed) { - toolWidgetManager.passFocusToNextLabel(); - } - } - //@} - -protected: - ToolWidgetManager toolWidgetManager; -}; - -} // namespace SketcherGui - - -#endif // SKETCHERGUI_DrawSketchHandlerDefaultWidget_H diff --git a/src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.cpp b/src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.cpp new file mode 100644 index 0000000000..9f0fa9f272 --- /dev/null +++ b/src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (c) 2023 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#include +#include +#include +#include +#include +#endif + +#include "ViewProviderSketch.h" + +#include "DrawSketchKeyboardManager.h" + +using namespace SketcherGui; + + +DrawSketchKeyboardManager::DrawSketchKeyboardManager() + : QObject(nullptr) + , keyMode(KeyboardEventHandlingMode::DSHControl) +{ + // get the active viewer, so that we can send it key events + auto doc = Gui::Application::Instance->activeDocument(); + + if (doc) { + auto temp = dynamic_cast(doc->getActiveView()); + if (temp) { + vpViewer = temp->getViewer(); + keyMode = KeyboardEventHandlingMode::ViewProvider; + } + } + + timer.setSingleShot(true); + + QObject::connect(&timer, &QTimer::timeout, [this]() { + onTimeOut(); + }); +} + +bool DrawSketchKeyboardManager::isMode(KeyboardEventHandlingMode mode) +{ + return mode == keyMode; +} + +DrawSketchKeyboardManager::KeyboardEventHandlingMode DrawSketchKeyboardManager::getMode() +{ + return keyMode; +} + +bool DrawSketchKeyboardManager::eventFilter(QObject* object, QEvent* event) +{ + Q_UNUSED(object); + + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { + /*If a key shortcut is required to work on sketcher when a tool using ui controls is being + * used, then you have to add this key to the below section such that the spinbox doesn't + * keep the keypress event for itself. Note if you want the event to be handled by the + * spinbox too, you can return false.*/ + + auto keyEvent = static_cast(event); + + detectKeyboardEventHandlingMode(keyEvent); // determine the handler + + if (vpViewer && isMode(KeyboardEventHandlingMode::ViewProvider)) { + return QApplication::sendEvent(vpViewer, keyEvent); + } + + return false; // do not intercept the event and feed it to the widget + } + + return false; +} + +void DrawSketchKeyboardManager::detectKeyboardEventHandlingMode(QKeyEvent* keyEvent) +{ + QRegularExpression rx(QStringLiteral("^[0-9]$")); + auto match = rx.match(keyEvent->text()); + if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return + || keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab + || keyEvent->key() == Qt::Key_Backspace || keyEvent->key() == Qt::Key_Delete + || keyEvent->key() == Qt::Key_Minus || keyEvent->key() == Qt::Key_Period + || keyEvent->key() == Qt::Key_Comma || match.hasMatch()) { + keyMode = KeyboardEventHandlingMode::DSHControl; + timer.start(timeOutValue); + } +} + +void DrawSketchKeyboardManager::onTimeOut() +{ + keyMode = KeyboardEventHandlingMode::ViewProvider; +} + +/// sets the timeout to the amount of milliseconds. +void DrawSketchKeyboardManager::setTimeOut(int milliseconds) +{ + timeOutValue = milliseconds; +} + +// returns the current timeout amount +int DrawSketchKeyboardManager::timeOut() +{ + return timeOutValue; +} + + +#include "moc_DrawSketchKeyboardManager.cpp" diff --git a/src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.h b/src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.h new file mode 100644 index 0000000000..dcb2e9b940 --- /dev/null +++ b/src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (c) 2023 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SketcherGui_DrawSketchKeyboardManager_H +#define SketcherGui_DrawSketchKeyboardManager_H + + +#include +#include + +#include + +#include +#include +#include +#include + + +namespace Gui +{ +class ViewProvider; +} // namespace Gui + +namespace SketcherGui +{ + +class ViewProviderSketch; + +/** Class implementing an event filter for DrawSketchHandler tools, enabling seamless introduction + * of values to parameters, including units, while still allowing operation of shortcuts. + * + * The basic mechanism to decide which control should respond is based on using timers, type of + * entered event. + */ +class DrawSketchKeyboardManager: public QObject +{ + Q_OBJECT + +public: + DrawSketchKeyboardManager(); + + + /** Indicates whether the DSH control (e.g. on-view parameter or widget) should handle keyboard + * input or should signal it via boost */ + enum class KeyboardEventHandlingMode + { + DSHControl, + ViewProvider + }; + + /// returns whether the provided entity will currently receive the event. + bool isMode(KeyboardEventHandlingMode mode); + + /// returns which entity will currently receive the event. + KeyboardEventHandlingMode getMode(); + + bool eventFilter(QObject* object, QEvent* event); + + /// sets the timeout to the amount of milliseconds. + void setTimeOut(int milliseconds); + + // returns the current timeout amount + int timeOut(); + +private: + /// This function decides whether events should be send to the ViewProvider + /// or to the UI control of DSH. + void detectKeyboardEventHandlingMode(QKeyEvent* keyEvent); + + void onTimeOut(); + +private: + /// Viewer responsible for the active document + Gui::View3DInventorViewer* vpViewer = nullptr; + KeyboardEventHandlingMode keyMode; + + QTimer timer; + + int timeOutValue = 2000; +}; + +} // namespace SketcherGui + +#endif // SketcherGui_DrawSketchKeyboardManager_H diff --git a/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.cpp b/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.cpp index 966e24d7fd..90cb495505 100644 --- a/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.cpp +++ b/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.cpp @@ -27,8 +27,6 @@ #include #include #include -#include -#include #endif #include "ui_SketcherToolDefaultWidget.h" @@ -37,8 +35,6 @@ #include #include #include -#include -#include #include #include #include @@ -53,70 +49,6 @@ using namespace SketcherGui; using namespace Gui::TaskView; -SketcherToolDefaultWidget::KeyboardManager::KeyboardManager() - : keyMode(SketcherToolDefaultWidget::KeyboardManager::KeyboardEventHandlingMode::Widget) -{ - // get the active viewer, so that we can send it key events - auto doc = Gui::Application::Instance->activeDocument(); - - if (doc) { - auto temp = dynamic_cast(doc->getActiveView()); - if (temp) { - vpViewer = temp->getViewer(); - keyMode = KeyboardEventHandlingMode::ViewProvider; - } - } - - timer.setSingleShot(true); - - QObject::connect(&timer, &QTimer::timeout, [this]() { - onTimeOut(); - }); -} - -bool SketcherToolDefaultWidget::KeyboardManager::isMode( - SketcherToolDefaultWidget::KeyboardManager::KeyboardEventHandlingMode mode) -{ - return mode == keyMode; -} - -SketcherToolDefaultWidget::KeyboardManager::KeyboardEventHandlingMode -SketcherToolDefaultWidget::KeyboardManager::getMode() -{ - return keyMode; -} - -bool SketcherToolDefaultWidget::KeyboardManager::handleKeyEvent(QKeyEvent* keyEvent) -{ - detectKeyboardEventHandlingMode(keyEvent); // determine the handler - - if (vpViewer && isMode(KeyboardEventHandlingMode::ViewProvider)) { - return QApplication::sendEvent(vpViewer, keyEvent); - } - - return false; // do not intercept the event and feed it to the widget -} - -void SketcherToolDefaultWidget::KeyboardManager::detectKeyboardEventHandlingMode( - QKeyEvent* keyEvent) -{ - QRegularExpression rx(QStringLiteral("^[0-9]$")); - auto match = rx.match(keyEvent->text()); - if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return - || keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab - || keyEvent->key() == Qt::Key_Backspace || keyEvent->key() == Qt::Key_Delete - || keyEvent->key() == Qt::Key_Minus || keyEvent->key() == Qt::Key_Period - || keyEvent->key() == Qt::Key_Comma || match.hasMatch()) { - keyMode = KeyboardEventHandlingMode::Widget; - timer.start(timeOut); - } -} - -void SketcherToolDefaultWidget::KeyboardManager::onTimeOut() -{ - keyMode = KeyboardEventHandlingMode::ViewProvider; -} - SketcherToolDefaultWidget::SketcherToolDefaultWidget(QWidget* parent) : QWidget(parent) , ui(new Ui_SketcherToolDefaultWidget) @@ -224,16 +156,6 @@ bool SketcherToolDefaultWidget::eventFilter(QObject* object, QEvent* event) } } } - else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { - /*If a key shortcut is required to work on sketcher when a tool using Tool Setting widget - is being used, then you have to add this key to the below section such that the spinbox - doesn't keep the keypress event for itself. Note if you want the event to be handled by - the spinbox too, you can return false.*/ - - auto keyEvent = static_cast(event); - - return keymanager.handleKeyEvent(keyEvent); - } return false; } diff --git a/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.h b/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.h index e4ce688fcb..ecac9dbd37 100644 --- a/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.h +++ b/src/Mod/Sketcher/Gui/SketcherToolDefaultWidget.h @@ -65,42 +65,6 @@ class SketcherToolDefaultWidget: public QWidget Italic, }; - /** Class to decide which control is responsible of handling an key event - *using timers, type of entered event, ... - */ - class KeyboardManager - { - public: - KeyboardManager(); - /// Indicates whether the widget should handle keyboard input or should signal it via boost - enum class KeyboardEventHandlingMode - { - Widget, - ViewProvider - }; - - bool isMode(KeyboardEventHandlingMode mode); - KeyboardEventHandlingMode getMode(); - - bool handleKeyEvent(QKeyEvent* keyEvent); - - private: - /// This function decides whether events should be send to the ViewProvider - /// or to the UI control of the Default widget. - void detectKeyboardEventHandlingMode(QKeyEvent* keyEvent); - - void onTimeOut(); - - private: - /// Viewer responsible for the active document - Gui::View3DInventorViewer* vpViewer = nullptr; - KeyboardEventHandlingMode keyMode; - - QTimer timer; - - const int timeOut = 1000; - }; - public: /// Parameter spinbox number/label enum Parameter @@ -255,8 +219,6 @@ private: /// vector using parameter as index indicating whether the value of a parameter was set by the /// widget std::vector isSet; - - KeyboardManager keymanager; };