// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * 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" #include "DrawSketchKeyboardManager.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< typename HandlerT, // The name of the actual handler of the tool typename SelectModeT, // The state machine defining the working of the tool int PAutoConstraintSize, // The initial size of the AutoConstraint vector typename OnViewParametersT, // The number of parameter spinboxes in the 3D view typename WidgetParametersT, // The number of parameter spinboxes in the default widget typename WidgetCheckboxesT, // The number of checkboxes in the default widget typename WidgetComboboxesT, // The number of comboboxes in the default widget typename ConstructionMethodT = ConstructionMethods::DefaultConstructionMethod, bool PFirstComboboxIsConstructionMethod = false> // The handler template or class having this // as inner class class DrawSketchDefaultWidgetController : public DrawSketchController { public: /** @name Meta-programming definitions and members */ //@{ using ControllerBase = DrawSketchController; //@} private: int nParameter = WidgetParametersT::defaultMethodSize(); int nCheckbox = WidgetCheckboxesT::defaultMethodSize(); int nCombobox = WidgetComboboxesT::defaultMethodSize(); SketcherToolDefaultWidget* toolWidget; using Connection = fastsignals::advanced_connection; Connection connectionParameterTabOrEnterPressed; 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: explicit DrawSketchDefaultWidgetController(HandlerT* dshandler) : ControllerBase(dshandler) , toolWidget(nullptr) {} DrawSketchDefaultWidgetController(const DrawSketchDefaultWidgetController&) = delete; DrawSketchDefaultWidgetController(DrawSketchDefaultWidgetController&&) = delete; DrawSketchDefaultWidgetController& operator=(const DrawSketchDefaultWidgetController&) = delete; DrawSketchDefaultWidgetController& operator=(DrawSketchDefaultWidgetController&&) = delete; ~DrawSketchDefaultWidgetController() override { connectionParameterTabOrEnterPressed.disconnect(); 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 // we block the auto passing of focus back to OVP that would occur in mouseMove // triggered in finishControlsChanged ControllerBase::focusAutoPassing = false; ControllerBase::finishControlsChanged(); ControllerBase::focusAutoPassing = true; } void parameterTabOrEnterPressed(int parameterindex) { Q_UNUSED(parameterindex); passFocusToNextParameter(); } /** 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 overridden 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, [[maybe_unused]] 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 computeNextDrawSketchHandlerMode() 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 one. 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 second checkbox if there is one. 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); } } /** on third shortcut, it toggles the third checkbox if there is one. Must be specialised if * this is not intended */ void thirdKeyShortcut() override { if (nCheckbox >= 3) { auto thirdchecked = toolWidget->getCheckboxChecked(WCheckbox::ThirdBox); toolWidget->setCheckboxChecked(WCheckbox::ThirdBox, !thirdchecked); } } /** on fourth shortcut, it toggles the fourth checkbox if there is one. Must be specialised if * this is not intended */ void fourthKeyShortcut() override { if (nCheckbox >= 4) { auto fourthchecked = toolWidget->getCheckboxChecked(WCheckbox::FourthBox); toolWidget->setCheckboxChecked(WCheckbox::FourthBox, !fourthchecked); } } /** on tab, we cycle through OVP and widget parameters */ void tabShortcut() override { passFocusToNextParameter(); } //@} 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 {} /// here we can pass focus to either OVP or widget parameters. void setFocusToParameter(unsigned int parameterindex) { // To be able to cycle through OVP and widget, we use a parameter index that goes from // 0 to (onViewParameters.size() + nParameter) if (!ControllerBase::setFocusToOnViewParameter(parameterindex)) { parameterindex = parameterindex - ControllerBase::onViewParameters.size(); if (parameterindex < static_cast(nParameter)) { toolWidget->setParameterFocus(parameterindex); ControllerBase::parameterWithFocus = ControllerBase::onViewParameters.size() + parameterindex; } } } /// Here we can pass focus to either OVP or widget parameters. void passFocusToNextParameter() { unsigned int index = ControllerBase::parameterWithFocus + 1; if (index >= ControllerBase::onViewParameters.size() + nParameter) { index = 0; } auto trySetFocus = [this](unsigned int& idx) -> bool { while (idx < ControllerBase::onViewParameters.size()) { if (ControllerBase::isOnViewParameterOfCurrentMode(idx) && ControllerBase::isOnViewParameterVisible(idx)) { setFocusToParameter(idx); return true; } idx++; } if (idx < ControllerBase::onViewParameters.size() + nParameter) { setFocusToParameter(idx); return true; } return false; }; if (!trySetFocus(index)) { // We have not found a parameter in this mode after current. // So we go back to start and retry. index = 0; trySetFocus(index); } // At that point if no onViewParameter is found, there is none. } //@} private: /// Initialisation of the widget void initDefaultWidget(QWidget* widget) { toolWidget = static_cast(widget); // NOLINT connectionParameterTabOrEnterPressed = toolWidget->registerParameterTabOrEnterPressed( std::bind(&DrawSketchDefaultWidgetController::parameterTabOrEnterPressed, this, sp::_1) ); 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() { fastsignals::shared_connection_block parameter_focus_block( connectionParameterTabOrEnterPressed ); fastsignals::shared_connection_block parameter_block(connectionParameterValueChanged); fastsignals::shared_connection_block checkbox_block(connectionCheckboxCheckedChanged); fastsignals::shared_connection_block combobox_block(connectionComboboxSelectionChanged); nParameter = WidgetParametersT::size(handler->constructionMethod()); nCheckbox = WidgetCheckboxesT::size(handler->constructionMethod()); nCombobox = WidgetComboboxesT::size(handler->constructionMethod()); toolWidget->initNParameters(nParameter, ControllerBase::getKeyManager()); toolWidget->initNCheckboxes(nCheckbox); toolWidget->initNComboboxes(nCombobox); configureToolWidget(); // 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 fastsignals::shared_connection_block combobox_block(connectionComboboxSelectionChanged); toolWidget->setComboboxIndex(WCombobox::FirstCombo, methodint); } } } 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) { fastsignals::shared_connection_block combobox_block(connectionComboboxSelectionChanged); toolWidget->setComboboxIndex(WCombobox::FirstCombo, actualconstructionmethod); } } } //@} }; } // namespace SketcherGui #endif // SKETCHERGUI_DrawSketchDefaultWidgetController_H