From 620c2ce239b4b5c7252dec7767036ee521c9ee76 Mon Sep 17 00:00:00 2001 From: Paddle Date: Sun, 17 Sep 2023 12:23:35 +0200 Subject: [PATCH] Introduce EditableDatumLabel. A class that handles a SoDatumLabel with a spinbox to edit its value. --- src/Gui/CMakeLists.txt | 2 + src/Gui/EditableDatumLabel.cpp | 301 +++++++++++++++++++++++++++++++++ src/Gui/EditableDatumLabel.h | 91 ++++++++++ 3 files changed, 394 insertions(+) create mode 100644 src/Gui/EditableDatumLabel.cpp create mode 100644 src/Gui/EditableDatumLabel.h diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 2d42991bda..248ea7ac4e 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -1152,6 +1152,7 @@ SET(FreeCADGui_CPP_SRCS DocumentPyImp.cpp DocumentObserver.cpp DocumentObserverPython.cpp + EditableDatumLabel.cpp ExpressionBinding.cpp ExpressionBindingPy.cpp GraphicsViewZoom.cpp @@ -1183,6 +1184,7 @@ SET(FreeCADGui_SRCS DocumentModel.h DocumentObserver.h DocumentObserverPython.h + EditableDatumLabel.h ExpressionBinding.h ExpressionBindingPy.h ExpressionCompleter.h diff --git a/src/Gui/EditableDatumLabel.cpp b/src/Gui/EditableDatumLabel.cpp new file mode 100644 index 0000000000..ef60716ba0 --- /dev/null +++ b/src/Gui/EditableDatumLabel.cpp @@ -0,0 +1,301 @@ +/// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2023 Ondsel * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include +# include +# include +#endif // _PreComp_ + +#include +#include +#include + +#include "EditableDatumLabel.h" + + + +using namespace Gui; + + +struct NodeData { + EditableDatumLabel* label; +}; + +EditableDatumLabel::EditableDatumLabel(View3DInventorViewer* view, Base::Placement plc, SbColor color, bool autoDistance) + : isSet(false) + , autoDistance(autoDistance) + , autoDistanceReverse(false) + , viewer(view) + , spinBox(nullptr) + , cameraSensor(nullptr) +{ + root = new SoAnnotation; + root->ref(); + root->renderCaching = SoSeparator::OFF; + + transform = new SoTransform(); + transform->ref(); + root->addChild(transform); + + label = new SoDatumLabel(); + label->ref(); + label->string = " "; + label->textColor = color; + label->size.setValue(17); + label->lineWidth = 2.0; + label->useAntialiasing = false; + label->datumtype = SoDatumLabel::DISTANCE; + label->param1 = 0.; + label->param2 = 0.; + if (autoDistance) + setLabelRecommendedDistance(); + root->addChild(label); + + setPlacement(plc); +} + +EditableDatumLabel::~EditableDatumLabel() +{ + deactivate(); + transform->unref(); + root->unref(); + label->unref(); +} + +void EditableDatumLabel::activate() +{ + if (viewer) { + static_cast(viewer->getSceneGraph())->addChild(root); + } + + //track camera movements to update spinbox position. + NodeData* info = new NodeData{ this }; + cameraSensor = new SoNodeSensor([](void* data, SoSensor* sensor) { + Q_UNUSED(sensor); + NodeData* info = static_cast(data); + info->label->positionSpinbox(); + if (info->label->autoDistance) + info->label->setLabelRecommendedDistance(); + }, info); + cameraSensor->attach(viewer->getCamera()); +} + +void EditableDatumLabel::deactivate() +{ + stopEdit(); + + if (cameraSensor) + cameraSensor->detach(); + + if (viewer) { + static_cast(viewer->getSceneGraph())->removeChild(root); + } +} + +void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj) +{ + QWidget* mdi = viewer->parentWidget(); + + label->string = " "; + + spinBox = new QuantitySpinBox(mdi); + spinBox->setUnit(Base::Unit::Length); + spinBox->setMinimum(-INT_MAX); + spinBox->setMaximum(INT_MAX); + spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); + spinBox->setKeyboardTracking(false); + if(eventFilteringObj) + spinBox->installEventFilter(eventFilteringObj); + + spinBox->show(); + setSpinboxValue(val); + //Note: adjustSize apparantly uses the Min/Max values to set the size. So if we don't set them to INT_MAX, the spinbox are much to big. + spinBox->adjustSize(); + setFocusToSpinbox(); + + connect(spinBox, qOverload(&QuantitySpinBox::valueChanged), + this, [this](double value) { + this->isSet = true; + this->valueChanged(value); + }); +} + +void EditableDatumLabel::stopEdit() +{ + if (spinBox) { + spinBox->deleteLater(); + spinBox = nullptr; + } +} + +double EditableDatumLabel::getValue() +{ + return spinBox->rawValue(); +} + +void EditableDatumLabel::setSpinboxValue(double val) +{ + if (!spinBox) { + Base::Console().Warning("Spinbox doesn't exist in EditableDatumLabel::setSpinboxValue."); + return; + } + + QSignalBlocker block(spinBox); + spinBox->setValue(val); + positionSpinbox(); + + if (spinBox->hasFocus()) { + spinBox->selectNumber(); + } +} +void EditableDatumLabel::setFocusToSpinbox() +{ + if (!spinBox) { + Base::Console().Warning("Spinbox doesn't exist in EditableDatumLabel::setFocusToSpinbox."); + return; + } + + spinBox->setFocus(); + spinBox->selectNumber(); +} + +void EditableDatumLabel::positionSpinbox() +{ + if (!spinBox) { + return; + } + + QSize wSize = spinBox->size(); + QPoint pxCoord = viewer->toQPoint(viewer->getPointOnViewport(getTextCenterPoint())); + pxCoord.setX(std::max(pxCoord.x() - wSize.width() / 2, 0)); + pxCoord.setY(std::max(pxCoord.y() - wSize.height() / 2, 0)); + spinBox->move(pxCoord); +} + +const SbVec3f EditableDatumLabel::getTextCenterPoint() +{ + //Here we need the 3d point and not the 2d point as are the SoLabel points. + // First we get the 2D point (on the sketch/image plane) of the middle of the text label. + SbVec3f point2D = label->textOffset; + // Get the translation and rotation values from the transform + SbVec3f translation = transform->translation.getValue(); + SbRotation rotation = transform->rotation.getValue(); + + // Calculate the inverse transformation + SbVec3f invTranslation = -translation; + SbRotation invRotation = rotation.inverse(); + + // Transform the 2D coordinates to 3D + // Plane form + SbVec3f RX(1, 0, 0), RY(0, 1, 0); + + // move to position of Sketch + invRotation.multVec(RX, RX); + invRotation.multVec(RY, RY); + invRotation.multVec(invTranslation, invTranslation); + + // we use invTranslation as the Base because in setPlacement we set transform->translation using + // placement.getPosition() to fix the Zoffset. But this applies the X & Y translation too. + Base::Vector3d pos(invTranslation[0], invTranslation[1], invTranslation[2]); + Base::Vector3d RXb(RX[0], RX[1], RX[2]); + Base::Vector3d RYb(RY[0], RY[1], RY[2]); + Base::Vector3d P2D(point2D[0], point2D[1], point2D[2]); + P2D.TransformToCoordinateSystem(pos, RXb, RYb); + + return SbVec3f(P2D.x, P2D.y, P2D.z); +} + +void EditableDatumLabel::setPlacement(Base::Placement plc) +{ + double x, y, z, w; + plc.getRotation().getValue(x, y, z, w); + //Base::Console().Warning("x%f, y%f, z%f, w%f\n", x, y, z, w); + transform->rotation.setValue(x, y, z, w); + Base::Vector3d pos = plc.getPosition(); + transform->translation.setValue(pos.x, pos.y, pos.z); + + Base::Vector3d RN(0, 0, 1); + RN = plc.getRotation().multVec(RN); + label->norm.setValue(SbVec3f(RN.x, RN.y, RN.z)); +} + +void EditableDatumLabel::setColor(SbColor color) +{ + label->textColor = color; +} + +void EditableDatumLabel::setFocus() +{ + if (spinBox) { + spinBox->selectNumber(); + } +} + +void EditableDatumLabel::setPoints(SbVec3f p1, SbVec3f p2) +{ + label->setPoints(p1, p2); + //TODO: here the position of the spinbox is not going to be center of p1, p2 because the point given by getTextCenterPoint + // is not updated yet. it will be only on redraw so it is actually positioning on previous position. + + positionSpinbox(); + if (autoDistance) + setLabelRecommendedDistance(); +} + +void EditableDatumLabel::setPoints(Base::Vector3d p1, Base::Vector3d p2) +{ + setPoints(SbVec3f(p1.x, p1.y, p1.z), SbVec3f(p2.x, p2.y, p2.z)); +} + +void EditableDatumLabel::setLabelType(SoDatumLabel::Type type) +{ + label->datumtype = type; +} + +void EditableDatumLabel::setLabelDistance(double distance) +{ + label->param1 = distance; +} + +void EditableDatumLabel::setLabelRecommendedDistance() +{ + // Takes the 3d view size, and set the label distance to a % of that, such that the distance does not depend on the zoom level. + float width = -1.; + float length = -1.; + viewer->getDimensions(width, length); + + if (width == -1. || length == -1.) + return; + + label->param1 = (autoDistanceReverse ? -1 : 1) * (width + length) * 0.03; +} + +void EditableDatumLabel::setLabelAutoDistanceReverse(bool val) +{ + autoDistanceReverse = val; +} + +#include "moc_EditableDatumLabel.cpp" diff --git a/src/Gui/EditableDatumLabel.h b/src/Gui/EditableDatumLabel.h new file mode 100644 index 0000000000..1c939a49b8 --- /dev/null +++ b/src/Gui/EditableDatumLabel.h @@ -0,0 +1,91 @@ + /// SPDX-License-Identifier: LGPL-2.1-or-later + /**************************************************************************** + * * + * Copyright (c) 2023 Ondsel * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + ***************************************************************************/ + +#ifndef GUI_EDITABLEDATUMLABEL_H +#define GUI_EDITABLEDATUMLABEL_H + +#include +#include + +#include "SoDatumLabel.h" + +#include + +class SoNodeSensor; +class SoTransform; + +namespace Gui { + +class View3DInventorViewer; + + +class GuiExport EditableDatumLabel : public QObject +{ + Q_OBJECT +public: + EditableDatumLabel(View3DInventorViewer* view, Base::Placement plc, SbColor color, bool autoDistance = false); + ~EditableDatumLabel(); + + void activate(); + void deactivate(); + + void startEdit(double val, QObject* eventFilteringObj = nullptr); + void stopEdit(); + double getValue(); + void setSpinboxValue(double val); + void setPlacement(Base::Placement plc); + void setColor(SbColor color); + void setFocus(); + void setPoints(SbVec3f p1, SbVec3f p2); + void setPoints(Base::Vector3d p1, Base::Vector3d p2); + void setFocusToSpinbox(); + void setLabelType(SoDatumLabel::Type type); + void setLabelDistance(double distance); + void setLabelRecommendedDistance(); + void setLabelAutoDistanceReverse(bool val); + + SoDatumLabel* label; + bool isSet; + bool autoDistance; + bool autoDistanceReverse; + +Q_SIGNALS: + void valueChanged(double val); + +private: + void positionSpinbox(); + const SbVec3f getTextCenterPoint(); + +private: + SoSeparator* root; + SoTransform* transform; + View3DInventorViewer* viewer; + QuantitySpinBox* spinBox; + SoNodeSensor* cameraSensor; + SbVec3f midpos; +}; + +} + + +#endif // GUI_EDITABLEDATUMLABEL_H