From aae45a2b8a1ac1efeb7aed86f2b2ab135fe23cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Br=C3=A6strup=20Sayoc?= Date: Tue, 18 Feb 2025 14:46:24 +0100 Subject: [PATCH] TechDraw: extract QGIDatumLabel --- src/Mod/TechDraw/Gui/CMakeLists.txt | 2 + src/Mod/TechDraw/Gui/CommandCreateDims.cpp | 1 + src/Mod/TechDraw/Gui/MDIViewPage.cpp | 1 + src/Mod/TechDraw/Gui/QGIDatumLabel.cpp | 579 +++++++++++++++++++++ src/Mod/TechDraw/Gui/QGIDatumLabel.h | 134 +++++ src/Mod/TechDraw/Gui/QGIViewDimension.cpp | 539 +------------------ src/Mod/TechDraw/Gui/QGIViewDimension.h | 100 +--- 7 files changed, 719 insertions(+), 637 deletions(-) create mode 100644 src/Mod/TechDraw/Gui/QGIDatumLabel.cpp create mode 100644 src/Mod/TechDraw/Gui/QGIDatumLabel.h diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index 331a8789fc..d5ae5364ab 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -272,6 +272,8 @@ SET(TechDrawGuiView_SRCS QGIView.h QGIArrow.cpp QGIArrow.h + QGIDatumLabel.cpp + QGIDatumLabel.h QGIEdge.cpp QGIEdge.h QGIFace.cpp diff --git a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp index a00deaf48a..18a4b58203 100644 --- a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp @@ -63,6 +63,7 @@ #include "CommandExtensionDims.h" #include "DimensionValidators.h" #include "DrawGuiUtil.h" +#include "QGIDatumLabel.h" #include "QGIViewDimension.h" #include "QGVPage.h" #include "MDIViewPage.h" diff --git a/src/Mod/TechDraw/Gui/MDIViewPage.cpp b/src/Mod/TechDraw/Gui/MDIViewPage.cpp index 6ce1da837b..9fc4e8593e 100644 --- a/src/Mod/TechDraw/Gui/MDIViewPage.cpp +++ b/src/Mod/TechDraw/Gui/MDIViewPage.cpp @@ -62,6 +62,7 @@ #include #include "MDIViewPage.h" +#include "QGIDatumLabel.h" #include "QGIEdge.h" #include "QGIFace.h" #include "QGIVertex.h" diff --git a/src/Mod/TechDraw/Gui/QGIDatumLabel.cpp b/src/Mod/TechDraw/Gui/QGIDatumLabel.cpp new file mode 100644 index 0000000000..f3ebb6879d --- /dev/null +++ b/src/Mod/TechDraw/Gui/QGIDatumLabel.cpp @@ -0,0 +1,579 @@ +/*************************************************************************** + * Copyright (c) 2013 Luke Parry * + * * + * 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 +#include + +#include +#include +#include +#include +#include + +#include "PreferencesGui.h" +#include "QGCustomText.h" +#include "QGIDatumLabel.h" +#include "QGIViewDimension.h" +#include "TaskSelectLineAttributes.h" +#include "ViewProviderDimension.h" + + +#define NORMAL 0 +#define PRE 1 +#define SEL 2 + +using namespace TechDraw; +using Format = DimensionFormatter::Format; + +namespace TechDrawGui { + +QGIDatumLabel::QGIDatumLabel() : m_dragState(DragState::NoDrag) +{ + verticalSep = false; + posX = 0; + posY = 0; + + parent = nullptr; + + setCacheMode(QGraphicsItem::NoCache); + setFlag(ItemSendsGeometryChanges, true); + setFlag(ItemIsMovable, true); + setSelectability(true); + setFiltersChildEvents(true); + + m_textItems = new QGraphicsItemGroup(); + m_textItems->setParentItem(this); + m_dimText = new QGCustomText(); + m_dimText->setTightBounding(true); + m_dimText->setParentItem(m_textItems); + m_tolTextOver = new QGCustomText(); + m_tolTextOver->setTightBounding(true); + m_tolTextOver->setParentItem(m_textItems); + m_tolTextUnder = new QGCustomText(); + m_tolTextUnder->setTightBounding(true); + m_tolTextUnder->setParentItem(m_textItems); + m_unitText = new QGCustomText(); + m_unitText->setTightBounding(true); + m_unitText->setParentItem(m_textItems); + + m_frame = new QGraphicsRectItem(); + QPen framePen; + framePen.setWidthF(Rez::guiX(0.5)); + framePen.setColor(m_dimText->defaultTextColor()); + framePen.setJoinStyle(Qt::MiterJoin); + m_frame->setPen(framePen); + + m_ctrl = false; +} + +void QGIDatumLabel::setFramed(bool framed) +{ + if(framed) { + m_frame->setVisible(true); + m_frame->setParentItem(this); + } + else { + m_frame->setVisible(false); + m_frame->setParentItem(nullptr); + } +} + +QVariant QGIDatumLabel::itemChange(GraphicsItemChange change, const QVariant& value) +{ + if (change == ItemSelectedHasChanged && scene()) { + if (isSelected()) { + setPrettySel(); + } + else { + setPrettyNormal(); + if (m_dragState == DragState::Dragging) { + //stop the drag if we are no longer selected. + m_dragState = DragState::NoDrag; + Q_EMIT dragFinished(); + } + } + } + else if (change == ItemPositionHasChanged && scene()) { + if (!(QApplication::keyboardModifiers() & Qt::AltModifier)) { + QPointF newPos = value.toPointF(); //position within parent! + snapPosition(newPos); + } + + setLabelCenter(); + m_dragState = DragState::Dragging; + Q_EMIT dragging(m_ctrl); + } + + return QGraphicsItem::itemChange(change, value); +} + +void QGIDatumLabel::snapPosition(QPointF& pos) +{ + qreal snapPercent = 0.4; + double dimSpacing = Rez::guiX(activeDimAttributes.getCascadeSpacing()); + + auto* qgivd = dynamic_cast(parentItem()); + if (!qgivd) { + return; + } + auto* dim(dynamic_cast(qgivd->getViewObject())); + if (!dim) { + return; + } + + // We only have snap for distances constraints + std::string type = dim->Type.getValueAsString(); + if(type != "Distance" && type != "DistanceX" && type != "DistanceY") { + return; + } + + // 1 - We try to snap the label to its center position. + pointPair pp = dim->getLinearPoints(); + Base::Vector3d p1_3d = Rez::guiX(pp.first()); + Base::Vector3d p2_3d = Rez::guiX(pp.second()); + Base::Vector2d p1 = Base::Vector2d(p1_3d.x, p1_3d.y); + Base::Vector2d p2 = Base::Vector2d(p2_3d.x, p2_3d.y); + if (type == "DistanceX") { + p2 = Base::Vector2d(p2.x, p1.y); + } + else if (type == "DistanceY") { + p2 = Base::Vector2d(p1.x, p2.y); + } + Base::Vector2d mid = (p1 + p2) * 0.5; + Base::Vector2d dir = p2 - p1; + Base::Vector2d normal = Base::Vector2d(-dir.y, dir.x); + + Base::Vector2d toCenter = getPosToCenterVec(); + + Base::Vector2d posV = Base::Vector2d(pos.x(), pos.y()) + toCenter; + + Base::Vector2d projPnt; + projPnt.ProjectToLine(posV - mid, normal); + projPnt = projPnt + mid; + + if ((projPnt - posV).Length() < dimSpacing * snapPercent) { + posV = projPnt; + pos.setX(posV.x - toCenter.x); + pos.setY(posV.y - toCenter.y); + } + + // 2 - We check for coord/chain dimensions to offer proper snapping + auto* qgiv = dynamic_cast(qgivd->parentItem()); + if (qgiv) { + auto* dvp = dynamic_cast(qgiv->getViewObject()); + if (dvp) { + snapPercent = 0.2; + std::vector dims = dvp->getDimensions(); + for (auto& d : dims) { + if (d == dim) { continue; } + + std::string typei = d->Type.getValueAsString(); + if (typei != "Distance" && typei != "DistanceX" && typei != "DistanceY") { + continue; + } + + pp = d->getLinearPoints(); + Base::Vector3d ip1_3d = Rez::guiX(pp.first()); + Base::Vector3d ip2_3d = Rez::guiX(pp.second()); + + Base::Vector2d ip1 = Base::Vector2d(ip1_3d.x, ip1_3d.y); + Base::Vector2d ip2 = Base::Vector2d(ip2_3d.x, ip2_3d.y); + if (typei == "DistanceX") { + ip2 = Base::Vector2d(ip2.x, ip1.y); + } + else if (typei == "DistanceY") { + ip2 = Base::Vector2d(ip1.x, ip2.y); + } + + Base::Vector2d idir = ip2 - ip1; + + if (fabs(dir.x * idir.y - dir.y * idir.x) > Precision::Confusion()) { + //dimensions not parallel + continue; + } + + auto* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(d)); + if (!vp) { continue; } + auto* qgivDi(dynamic_cast(vp->getQView())); + if (!qgivDi) { continue; } + auto labeli = qgivDi->getDatumLabel(); + if (!labeli) { continue; } + QPointF posi = labeli->pos(); + Base::Vector2d toCenteri = labeli->getPosToCenterVec(); + Base::Vector2d posVi = Base::Vector2d(posi.x(), posi.y()) + toCenteri; + + Base::Vector2d projPnt2; + projPnt2.ProjectToLine(posV - posVi, idir); + projPnt2 = projPnt2 + posVi; + + if ((projPnt2 - posV).Length() < dimSpacing * snapPercent) { + posV = projPnt2; + pos.setX(posV.x - toCenter.x); + pos.setY(posV.y - toCenter.y); + break; + } + else if (fabs((projPnt2 - posV).Length() - fabs(dimSpacing)) < dimSpacing * snapPercent) { + posV = projPnt2 + (posV - projPnt2).Normalize() * dimSpacing; + pos.setX(posV.x - toCenter.x); + pos.setY(posV.y - toCenter.y); + break; + } + } + } + } + + + setPos(pos); // no infinite loop because if pos doesn't change then itemChanged is not triggered. +} + +void QGIDatumLabel::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + if (event->modifiers() & Qt::ControlModifier) { + m_ctrl = true; + } + + QGraphicsItem::mousePressEvent(event); +} + +void QGIDatumLabel::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) +{ + // Base::Console().Message("QGIDL::mouseReleaseEvent()\n"); + m_ctrl = false; + if (m_dragState == DragState::Dragging) { + m_dragState = DragState::NoDrag; + Q_EMIT dragFinished(); + } + + QGraphicsItem::mouseReleaseEvent(event); +} + +void QGIDatumLabel::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + QGIViewDimension* qgivDimension = dynamic_cast(parentItem()); + if (!qgivDimension) { + qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No parent item"; + return; + } + + auto ViewProvider = dynamic_cast( + qgivDimension->getViewProvider(qgivDimension->getViewObject())); + if (!ViewProvider) { + qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No valid view provider"; + return; + } + + ViewProvider->startDefaultEditMode(); + QGraphicsItem::mouseDoubleClickEvent(event); +} + +void QGIDatumLabel::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + Q_EMIT hover(true); + if (!isSelected()) { + setPrettyPre(); + } + else { + setPrettySel(); + } + QGraphicsItem::hoverEnterEvent(event); +} + +void QGIDatumLabel::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + Q_EMIT hover(false); + if (!isSelected()) { + setPrettyNormal(); + } + else { + setPrettySel(); + } + + QGraphicsItem::hoverLeaveEvent(event); +} + +QRectF QGIDatumLabel::boundingRect() const +{ + return childrenBoundingRect(); +} + +QRectF QGIDatumLabel::tightBoundingRect() const +{ + QRectF totalRect; + for (QGraphicsItem* item : m_textItems->childItems()) { + auto* customText = dynamic_cast(item); + if (customText && !customText->toPlainText().isEmpty()) { + QRectF itemRect = customText->alignmentRect(); + QPointF pos = customText->pos(); + itemRect.translate(pos.x(), pos.y()); + totalRect = totalRect.isNull() ? itemRect : totalRect.united(itemRect); + } + } + int fontSize = m_dimText->font().pixelSize(); + int paddingLeft = fontSize * 0.2; + int paddingTop = fontSize * 0.1; + int paddingRight = fontSize * 0.2; + int paddingBottom = fontSize * 0.1; + return totalRect.adjusted(-paddingLeft, -paddingTop, paddingRight, paddingBottom); +} + +void QGIDatumLabel::updateFrameRect() { + prepareGeometryChange(); + m_frame->setRect(tightBoundingRect()); +} + +void QGIDatumLabel::setLineWidth(double lineWidth) +{ + QPen pen = m_frame->pen(); + pen.setWidthF(lineWidth); + m_frame->setPen(pen); +} + +void QGIDatumLabel::setFrameColor(QColor color) +{ + QPen pen = m_frame->pen(); + pen.setColor(color); + m_frame->setPen(pen); +} + +void QGIDatumLabel::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, + QWidget* widget) +{ + Q_UNUSED(widget); + Q_UNUSED(painter); + QStyleOptionGraphicsItem myOption(*option); + myOption.state &= ~QStyle::State_Selected; + + // painter->setPen(Qt::blue); + // painter->drawRect(boundingRect()); //good for debugging +} + +void QGIDatumLabel::setPosFromCenter(const double& xCenter, const double& yCenter) +{ + prepareGeometryChange(); + auto* qgivd = dynamic_cast(parentItem()); + if (!qgivd) { + return; + } + const auto dim(dynamic_cast(qgivd->getViewObject())); + if (!dim) { + return; + } + + //set label's Qt position(top, left) given boundingRect center point + Base::Vector2d vec = getPosToCenterVec(); + setPos(xCenter - vec.x, yCenter - vec.y); + + QString uText = m_unitText->toPlainText(); + if ((uText.size() > 0) && (uText.at(0) != QChar::fromLatin1(' '))) { + QString vText = m_dimText->toPlainText(); + vText = vText + uText; + m_dimText->setPlainText(vText); + m_unitText->setPlainText(QString()); + } + + QRectF labelBox = m_dimText->alignmentRect(); + double right = labelBox.right(); + double middle = labelBox.center().y(); + + //set unit position + QRectF unitBox = m_unitText->alignmentRect(); + double unitWidth = unitBox.width(); + double unitRight = right + unitWidth; + // Set the m_unitText font *baseline* at same height as the m_dimText font baseline + m_unitText->setPos(right, 0.0); + + //set tolerance position + QRectF overBox = m_tolTextOver->alignmentRect(); + double tolLeft = unitRight; + + // Adjust for difference in tight and original bounding box sizes, note the y-coord down system + QPointF tol_adj = m_tolTextOver->tightBoundingAdjust(); + m_tolTextOver->justifyLeftAt(tolLeft + tol_adj.x(), middle + tol_adj.y()/2.0, false); + tol_adj = m_tolTextUnder->tightBoundingAdjust(); + m_tolTextUnder->justifyLeftAt(tolLeft + tol_adj.x(), middle + overBox.height() + tol_adj.y()/2.0, false); +} + +void QGIDatumLabel::setLabelCenter() +{ + //save label's bRect center (posX, posY) given Qt position (top, left) + Base::Vector2d vec = getPosToCenterVec(); + posX = x() + vec.x; + posY = y() + vec.y; +} + +Base::Vector2d QGIDatumLabel::getPosToCenterVec() +{ + QPointF center = tightBoundingRect().center(); + + return Base::Vector2d(center.x(), center.y()); +} + +void QGIDatumLabel::setFont(QFont font) +{ + prepareGeometryChange(); + m_dimText->setFont(font); + m_unitText->setFont(font); + QFont tFont(font); + double fontSize = font.pixelSize(); + double tolAdj = getTolAdjust(); + tFont.setPixelSize(std::max(1, static_cast(fontSize * tolAdj))); + m_tolTextOver->setFont(tFont); + m_tolTextUnder->setFont(tFont); + updateFrameRect(); +} + +void QGIDatumLabel::setDimString(QString text, qreal maxWidth) +{ + prepareGeometryChange(); + m_dimText->setPlainText(text); + m_dimText->setTextWidth(maxWidth); + updateFrameRect(); +} + +void QGIDatumLabel::setToleranceString() +{ + prepareGeometryChange(); + QGIViewDimension* qgivd = dynamic_cast(parentItem()); + if (!qgivd) { + return; + } + const auto dim(dynamic_cast(qgivd->getViewObject())); + if (!dim) { + return; + // don't show if both are zero or if EqualTolerance is true + } + else if (!dim->hasOverUnderTolerance() || dim->EqualTolerance.getValue() + || dim->TheoreticalExact.getValue()) { + m_tolTextOver->hide(); + m_tolTextUnder->hide(); + // we must explicitly empty the text otherwise the frame drawn for + // TheoreticalExact would be as wide as necessary for the text + m_tolTextOver->setPlainText(QString()); + m_tolTextUnder->setPlainText(QString()); + updateFrameRect(); + return; + } + + std::pair labelTexts, unitTexts; + + if (dim->ArbitraryTolerances.getValue()) { + labelTexts = dim->getFormattedToleranceValues(Format::FORMATTED);//copy tolerance spec + unitTexts.first = ""; + unitTexts.second = ""; + } + else { + if (dim->isMultiValueSchema()) { + labelTexts = dim->getFormattedToleranceValues(Format::UNALTERED);//don't format multis + unitTexts.first = ""; + unitTexts.second = ""; + } + else { + labelTexts = dim->getFormattedToleranceValues(Format::FORMATTED);// prefix value [unit] postfix + unitTexts = dim->getFormattedToleranceValues(Format::UNIT); //just the unit + } + } + + if (labelTexts.first.empty()) { + m_tolTextUnder->hide(); + } + else { + m_tolTextUnder->setPlainText(QString::fromUtf8(labelTexts.first.c_str())); + m_tolTextUnder->show(); + } + if (labelTexts.second.empty()) { + m_tolTextOver->hide(); + } + else { + m_tolTextOver->setPlainText(QString::fromUtf8(labelTexts.second.c_str())); + m_tolTextOver->show(); + } + + updateFrameRect(); +} + + +int QGIDatumLabel::getPrecision() +{ + if (Preferences::useGlobalDecimals()) { + return Base::UnitsApi::getDecimals(); + } + return Preferences::getPreferenceGroup("Dimensions")->GetInt("AltDecimals", 2); +} + +double QGIDatumLabel::getTolAdjust() +{ + return Preferences::getPreferenceGroup("Dimensions")->GetFloat("TolSizeAdjust", 0.50); +} + + +void QGIDatumLabel::setPrettySel() +{ + // Base::Console().Message("QGIDL::setPrettySel()\n"); + m_dimText->setPrettySel(); + m_tolTextOver->setPrettySel(); + m_tolTextUnder->setPrettySel(); + m_unitText->setPrettySel(); + setFrameColor(PreferencesGui::selectQColor()); + Q_EMIT setPretty(SEL); +} + +void QGIDatumLabel::setPrettyPre() +{ + // Base::Console().Message("QGIDL::setPrettyPre()\n"); + m_dimText->setPrettyPre(); + m_tolTextOver->setPrettyPre(); + m_tolTextUnder->setPrettyPre(); + m_unitText->setPrettyPre(); + setFrameColor(PreferencesGui::preselectQColor()); + Q_EMIT setPretty(PRE); +} + +void QGIDatumLabel::setPrettyNormal() +{ + // Base::Console().Message("QGIDL::setPrettyNormal()\n"); + m_dimText->setPrettyNormal(); + m_tolTextOver->setPrettyNormal(); + m_tolTextUnder->setPrettyNormal(); + m_unitText->setPrettyNormal(); + setFrameColor(PreferencesGui::normalQColor()); + Q_EMIT setPretty(NORMAL); +} + +void QGIDatumLabel::setColor(QColor color) +{ + // Base::Console().Message("QGIDL::setColor(%s)\n", qPrintable(c.name())); + m_colNormal = color; + m_dimText->setColor(m_colNormal); + m_tolTextOver->setColor(m_colNormal); + m_tolTextUnder->setColor(m_colNormal); + m_unitText->setColor(m_colNormal); + setFrameColor(m_colNormal); +} + +void QGIDatumLabel::setSelectability(bool val) +{ + setFlag(ItemIsSelectable, val); + setAcceptHoverEvents(val); + setAcceptedMouseButtons(val ? Qt::AllButtons : Qt::NoButton); +} + +} // namespace TechDrawGui diff --git a/src/Mod/TechDraw/Gui/QGIDatumLabel.h b/src/Mod/TechDraw/Gui/QGIDatumLabel.h new file mode 100644 index 0000000000..00ee277774 --- /dev/null +++ b/src/Mod/TechDraw/Gui/QGIDatumLabel.h @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (c) 2013 Luke Parry * + * * + * 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 TECHDRAWGUI_QGRAPHICSITEMVIEWDATUMLABEL_H +#define TECHDRAWGUI_QGRAPHICSITEMVIEWDATUMLABEL_H + +#include + +#include "Enums.h" +#include "QGCustomText.h" +#include "QGIViewDimension.h" + + +namespace TechDrawGui { + +class QGIDatumLabel : public QGraphicsObject +{ +Q_OBJECT + +public: + QGIDatumLabel(); + ~QGIDatumLabel() override = default; + + enum {Type = QGraphicsItem::UserType + 107}; + int type() const override { return Type;} + + QRectF boundingRect() const override; + QRectF tightBoundingRect() const; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void paint( QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget = nullptr ) override; + void setLabelCenter(); + void setPosFromCenter(const double &xCenter, const double &yCenter); + double X() const { return posX; } + double Y() const { return posY; } //minus posY? + Base::Vector2d getPosToCenterVec(); + + void setFont(QFont font); + QFont getFont() const { return m_dimText->font(); } + void setDimString(QString text, qreal maxWidth=-1); + void setToleranceString(); + void setPrettySel(); + void setPrettyPre(); + void setPrettyNormal(); + void setColor(QColor color); + void setSelectability(bool val); + void setFrameColor(QColor color); + + QGCustomText* getDimText() { return m_dimText; } + void setDimText(QGCustomText* newText) { m_dimText = newText; } + QGCustomText* getTolTextOver() { return m_tolTextOver; } + void setTolTextOver(QGCustomText* newTol) { m_tolTextOver = newTol; } + QGCustomText* getTolTextUnder() { return m_tolTextUnder; } + void setTolTextUnder(QGCustomText* newTol) { m_tolTextOver = newTol; } + + double getTolAdjust(); + + bool isFramed() const { return m_frame->parentItem(); } // If empty pointer, then no frame + void setFramed(bool framed); + + double getLineWidth() const { return m_frame->pen().widthF(); } + void setLineWidth(double lineWidth); + void setQDim(QGIViewDimension* qDim) { parent = qDim;} + +Q_SIGNALS: + void setPretty(int state); + void dragging(bool); + void hover(bool state); + void selected(bool state); + void dragFinished(); + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; + void updateFrameRect(); + + int getPrecision(); + + void snapPosition(QPointF& position); + + bool getVerticalSep() const { return verticalSep; } + void setVerticalSep(bool sep) { verticalSep = sep; } + std::vector getSeps() const { return seps; } + void setSeps(std::vector newSeps) { seps = newSeps; } + +private: + bool verticalSep; + std::vector seps; + + QGIViewDimension* parent; + + QGCustomText* m_dimText; + QGCustomText* m_tolTextOver; + QGCustomText* m_tolTextUnder; + QGCustomText* m_unitText; + QGraphicsItemGroup* m_textItems; + QGraphicsRectItem* m_frame; + QColor m_colNormal; + bool m_ctrl; + + double posX; + double posY; + + DragState m_dragState; + +private: +}; + +} // namespace TechDrawGui + +#endif diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index c4eff8be7f..70e392f231 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -54,6 +54,7 @@ #include "QGIViewDimension.h" #include "PreferencesGui.h" #include "QGIArrow.h" +#include "QGIDatumLabel.h" #include "QGIDimLines.h" #include "QGIVertex.h" #include "QGCustomSvg.h" @@ -81,544 +82,6 @@ enum class SnapMode }; -QGIDatumLabel::QGIDatumLabel() : m_dragState(DragState::NoDrag) -{ - verticalSep = false; - posX = 0; - posY = 0; - - parent = nullptr; - - setCacheMode(QGraphicsItem::NoCache); - setFlag(ItemSendsGeometryChanges, true); - setFlag(ItemIsMovable, true); - setSelectability(true); - setFiltersChildEvents(true); - - m_textItems = new QGraphicsItemGroup(); - m_textItems->setParentItem(this); - m_dimText = new QGCustomText(); - m_dimText->setTightBounding(true); - m_dimText->setParentItem(m_textItems); - m_tolTextOver = new QGCustomText(); - m_tolTextOver->setTightBounding(true); - m_tolTextOver->setParentItem(m_textItems); - m_tolTextUnder = new QGCustomText(); - m_tolTextUnder->setTightBounding(true); - m_tolTextUnder->setParentItem(m_textItems); - m_unitText = new QGCustomText(); - m_unitText->setTightBounding(true); - m_unitText->setParentItem(m_textItems); - - m_frame = new QGraphicsRectItem(); - QPen framePen; - framePen.setWidthF(Rez::guiX(0.5)); - framePen.setColor(m_dimText->defaultTextColor()); - framePen.setJoinStyle(Qt::MiterJoin); - m_frame->setPen(framePen); - - m_ctrl = false; -} - -void QGIDatumLabel::setFramed(bool framed) -{ - if(framed) { - m_frame->setVisible(true); - m_frame->setParentItem(this); - } - else { - m_frame->setVisible(false); - m_frame->setParentItem(nullptr); - } -} - -QVariant QGIDatumLabel::itemChange(GraphicsItemChange change, const QVariant& value) -{ - if (change == ItemSelectedHasChanged && scene()) { - if (isSelected()) { - setPrettySel(); - } - else { - setPrettyNormal(); - if (m_dragState == DragState::Dragging) { - //stop the drag if we are no longer selected. - m_dragState = DragState::NoDrag; - Q_EMIT dragFinished(); - } - } - } - else if (change == ItemPositionHasChanged && scene()) { - if (!(QApplication::keyboardModifiers() & Qt::AltModifier)) { - QPointF newPos = value.toPointF(); //position within parent! - snapPosition(newPos); - } - - setLabelCenter(); - m_dragState = DragState::Dragging; - Q_EMIT dragging(m_ctrl); - } - - return QGraphicsItem::itemChange(change, value); -} - -void QGIDatumLabel::snapPosition(QPointF& pos) -{ - qreal snapPercent = 0.4; - double dimSpacing = Rez::guiX(activeDimAttributes.getCascadeSpacing()); - - auto* qgivd = dynamic_cast(parentItem()); - if (!qgivd) { - return; - } - auto* dim(dynamic_cast(qgivd->getViewObject())); - if (!dim) { - return; - } - - // We only have snap for distances constraints - std::string type = dim->Type.getValueAsString(); - if(type != "Distance" && type != "DistanceX" && type != "DistanceY") { - return; - } - - // 1 - We try to snap the label to its center position. - pointPair pp = dim->getLinearPoints(); - Base::Vector3d p1_3d = Rez::guiX(pp.first()); - Base::Vector3d p2_3d = Rez::guiX(pp.second()); - Base::Vector2d p1 = Base::Vector2d(p1_3d.x, p1_3d.y); - Base::Vector2d p2 = Base::Vector2d(p2_3d.x, p2_3d.y); - if (type == "DistanceX") { - p2 = Base::Vector2d(p2.x, p1.y); - } - else if (type == "DistanceY") { - p2 = Base::Vector2d(p1.x, p2.y); - } - Base::Vector2d mid = (p1 + p2) * 0.5; - Base::Vector2d dir = p2 - p1; - Base::Vector2d normal = Base::Vector2d(-dir.y, dir.x); - - Base::Vector2d toCenter = getPosToCenterVec(); - - Base::Vector2d posV = Base::Vector2d(pos.x(), pos.y()) + toCenter; - - Base::Vector2d projPnt; - projPnt.ProjectToLine(posV - mid, normal); - projPnt = projPnt + mid; - - if ((projPnt - posV).Length() < dimSpacing * snapPercent) { - posV = projPnt; - pos.setX(posV.x - toCenter.x); - pos.setY(posV.y - toCenter.y); - } - - // 2 - We check for coord/chain dimensions to offer proper snapping - auto* qgiv = dynamic_cast(qgivd->parentItem()); - if (qgiv) { - auto* dvp = dynamic_cast(qgiv->getViewObject()); - if (dvp) { - snapPercent = 0.2; - std::vector dims = dvp->getDimensions(); - for (auto& d : dims) { - if (d == dim) { continue; } - - std::string typei = d->Type.getValueAsString(); - if (typei != "Distance" && typei != "DistanceX" && typei != "DistanceY") { - continue; - } - - pp = d->getLinearPoints(); - Base::Vector3d ip1_3d = Rez::guiX(pp.first()); - Base::Vector3d ip2_3d = Rez::guiX(pp.second()); - - Base::Vector2d ip1 = Base::Vector2d(ip1_3d.x, ip1_3d.y); - Base::Vector2d ip2 = Base::Vector2d(ip2_3d.x, ip2_3d.y); - if (typei == "DistanceX") { - ip2 = Base::Vector2d(ip2.x, ip1.y); - } - else if (typei == "DistanceY") { - ip2 = Base::Vector2d(ip1.x, ip2.y); - } - - Base::Vector2d idir = ip2 - ip1; - - if (fabs(dir.x * idir.y - dir.y * idir.x) > Precision::Confusion()) { - //dimensions not parallel - continue; - } - - auto* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(d)); - if (!vp) { continue; } - auto* qgivDi(dynamic_cast(vp->getQView())); - if (!qgivDi) { continue; } - auto labeli = qgivDi->getDatumLabel(); - if (!labeli) { continue; } - QPointF posi = labeli->pos(); - Base::Vector2d toCenteri = labeli->getPosToCenterVec(); - Base::Vector2d posVi = Base::Vector2d(posi.x(), posi.y()) + toCenteri; - - Base::Vector2d projPnt2; - projPnt2.ProjectToLine(posV - posVi, idir); - projPnt2 = projPnt2 + posVi; - - if ((projPnt2 - posV).Length() < dimSpacing * snapPercent) { - posV = projPnt2; - pos.setX(posV.x - toCenter.x); - pos.setY(posV.y - toCenter.y); - break; - } - else if (fabs((projPnt2 - posV).Length() - fabs(dimSpacing)) < dimSpacing * snapPercent) { - posV = projPnt2 + (posV - projPnt2).Normalize() * dimSpacing; - pos.setX(posV.x - toCenter.x); - pos.setY(posV.y - toCenter.y); - break; - } - } - } - } - - - setPos(pos); // no infinite loop because if pos doesn't change then itemChanged is not triggered. -} - -void QGIDatumLabel::mousePressEvent(QGraphicsSceneMouseEvent* event) -{ - if (event->modifiers() & Qt::ControlModifier) { - m_ctrl = true; - } - - QGraphicsItem::mousePressEvent(event); -} - -void QGIDatumLabel::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) -{ - // Base::Console().Message("QGIDL::mouseReleaseEvent()\n"); - m_ctrl = false; - if (m_dragState == DragState::Dragging) { - m_dragState = DragState::NoDrag; - Q_EMIT dragFinished(); - } - - QGraphicsItem::mouseReleaseEvent(event); -} - -void QGIDatumLabel::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) -{ - QGIViewDimension* qgivDimension = dynamic_cast(parentItem()); - if (!qgivDimension) { - qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No parent item"; - return; - } - - auto ViewProvider = dynamic_cast( - qgivDimension->getViewProvider(qgivDimension->getViewObject())); - if (!ViewProvider) { - qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No valid view provider"; - return; - } - - ViewProvider->startDefaultEditMode(); - QGraphicsItem::mouseDoubleClickEvent(event); -} - -void QGIDatumLabel::hoverEnterEvent(QGraphicsSceneHoverEvent* event) -{ - Q_EMIT hover(true); - if (!isSelected()) { - setPrettyPre(); - } - else { - setPrettySel(); - } - QGraphicsItem::hoverEnterEvent(event); -} - -void QGIDatumLabel::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) -{ - Q_EMIT hover(false); - if (!isSelected()) { - setPrettyNormal(); - } - else { - setPrettySel(); - } - - QGraphicsItem::hoverLeaveEvent(event); -} - -QRectF QGIDatumLabel::boundingRect() const -{ - return childrenBoundingRect(); -} - -QRectF QGIDatumLabel::tightBoundingRect() const -{ - QRectF totalRect; - for (QGraphicsItem* item : m_textItems->childItems()) { - auto* customText = dynamic_cast(item); - if (customText && !customText->toPlainText().isEmpty()) { - QRectF itemRect = customText->alignmentRect(); - QPointF pos = customText->pos(); - itemRect.translate(pos.x(), pos.y()); - totalRect = totalRect.isNull() ? itemRect : totalRect.united(itemRect); - } - } - int fontSize = m_dimText->font().pixelSize(); - int paddingLeft = fontSize * 0.2; - int paddingTop = fontSize * 0.1; - int paddingRight = fontSize * 0.2; - int paddingBottom = fontSize * 0.1; - return totalRect.adjusted(-paddingLeft, -paddingTop, paddingRight, paddingBottom); -} - -void QGIDatumLabel::updateFrameRect() { - prepareGeometryChange(); - m_frame->setRect(tightBoundingRect()); -} - -void QGIDatumLabel::setLineWidth(double lineWidth) -{ - QPen pen = m_frame->pen(); - pen.setWidthF(lineWidth); - m_frame->setPen(pen); -} - -void QGIDatumLabel::setFrameColor(QColor color) -{ - QPen pen = m_frame->pen(); - pen.setColor(color); - m_frame->setPen(pen); -} - -void QGIDatumLabel::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, - QWidget* widget) -{ - Q_UNUSED(widget); - Q_UNUSED(painter); - QStyleOptionGraphicsItem myOption(*option); - myOption.state &= ~QStyle::State_Selected; - - // painter->setPen(Qt::blue); - // painter->drawRect(boundingRect()); //good for debugging -} - -void QGIDatumLabel::setPosFromCenter(const double& xCenter, const double& yCenter) -{ - prepareGeometryChange(); - auto* qgivd = dynamic_cast(parentItem()); - if (!qgivd) { - return; - } - const auto dim(dynamic_cast(qgivd->getViewObject())); - if (!dim) { - return; - } - - //set label's Qt position(top, left) given boundingRect center point - Base::Vector2d vec = getPosToCenterVec(); - setPos(xCenter - vec.x, yCenter - vec.y); - - QString uText = m_unitText->toPlainText(); - if ((uText.size() > 0) && (uText.at(0) != QChar::fromLatin1(' '))) { - QString vText = m_dimText->toPlainText(); - vText = vText + uText; - m_dimText->setPlainText(vText); - m_unitText->setPlainText(QString()); - } - - QRectF labelBox = m_dimText->alignmentRect(); - double right = labelBox.right(); - double middle = labelBox.center().y(); - - //set unit position - QRectF unitBox = m_unitText->alignmentRect(); - double unitWidth = unitBox.width(); - double unitRight = right + unitWidth; - // Set the m_unitText font *baseline* at same height as the m_dimText font baseline - m_unitText->setPos(right, 0.0); - - //set tolerance position - QRectF overBox = m_tolTextOver->alignmentRect(); - double tolLeft = unitRight; - - // Adjust for difference in tight and original bounding box sizes, note the y-coord down system - QPointF tol_adj = m_tolTextOver->tightBoundingAdjust(); - m_tolTextOver->justifyLeftAt(tolLeft + tol_adj.x(), middle + tol_adj.y()/2.0, false); - tol_adj = m_tolTextUnder->tightBoundingAdjust(); - m_tolTextUnder->justifyLeftAt(tolLeft + tol_adj.x(), middle + overBox.height() + tol_adj.y()/2.0, false); -} - -void QGIDatumLabel::setLabelCenter() -{ - //save label's bRect center (posX, posY) given Qt position (top, left) - Base::Vector2d vec = getPosToCenterVec(); - posX = x() + vec.x; - posY = y() + vec.y; -} - -Base::Vector2d QGIDatumLabel::getPosToCenterVec() -{ - QPointF center = tightBoundingRect().center(); - - return Base::Vector2d(center.x(), center.y()); -} - -void QGIDatumLabel::setFont(QFont font) -{ - prepareGeometryChange(); - m_dimText->setFont(font); - m_unitText->setFont(font); - QFont tFont(font); - double fontSize = font.pixelSize(); - double tolAdj = getTolAdjust(); - tFont.setPixelSize(std::max(1, (int)(fontSize * tolAdj))); - m_tolTextOver->setFont(tFont); - m_tolTextUnder->setFont(tFont); - updateFrameRect(); -} - -void QGIDatumLabel::setDimString(QString text) -{ - prepareGeometryChange(); - m_dimText->setPlainText(text); - updateFrameRect(); -} - -void QGIDatumLabel::setDimString(QString text, qreal maxWidth) -{ - prepareGeometryChange(); - m_dimText->setPlainText(text); - m_dimText->setTextWidth(maxWidth); - updateFrameRect(); -} - -void QGIDatumLabel::setToleranceString() -{ - prepareGeometryChange(); - QGIViewDimension* qgivd = dynamic_cast(parentItem()); - if (!qgivd) { - return; - } - const auto dim(dynamic_cast(qgivd->getViewObject())); - if (!dim) { - return; - // don't show if both are zero or if EqualTolerance is true - } - else if (!dim->hasOverUnderTolerance() || dim->EqualTolerance.getValue() - || dim->TheoreticalExact.getValue()) { - m_tolTextOver->hide(); - m_tolTextUnder->hide(); - // we must explicitly empty the text otherwise the frame drawn for - // TheoreticalExact would be as wide as necessary for the text - m_tolTextOver->setPlainText(QString()); - m_tolTextUnder->setPlainText(QString()); - updateFrameRect(); - return; - } - - std::pair labelTexts, unitTexts; - - if (dim->ArbitraryTolerances.getValue()) { - labelTexts = dim->getFormattedToleranceValues(Format::FORMATTED);//copy tolerance spec - unitTexts.first = ""; - unitTexts.second = ""; - } - else { - if (dim->isMultiValueSchema()) { - labelTexts = dim->getFormattedToleranceValues(Format::UNALTERED);//don't format multis - unitTexts.first = ""; - unitTexts.second = ""; - } - else { - labelTexts = dim->getFormattedToleranceValues(Format::FORMATTED);// prefix value [unit] postfix - unitTexts = dim->getFormattedToleranceValues(Format::UNIT); //just the unit - } - } - - if (labelTexts.first.empty()) { - m_tolTextUnder->hide(); - } - else { - m_tolTextUnder->setPlainText(QString::fromUtf8(labelTexts.first.c_str())); - m_tolTextUnder->show(); - } - if (labelTexts.second.empty()) { - m_tolTextOver->hide(); - } - else { - m_tolTextOver->setPlainText(QString::fromUtf8(labelTexts.second.c_str())); - m_tolTextOver->show(); - } - - updateFrameRect(); -} - - -int QGIDatumLabel::getPrecision() -{ - if (Preferences::useGlobalDecimals()) { - return Base::UnitsApi::getDecimals(); - } - return Preferences::getPreferenceGroup("Dimensions")->GetInt("AltDecimals", 2); -} - -double QGIDatumLabel::getTolAdjust() -{ - return Preferences::getPreferenceGroup("Dimensions")->GetFloat("TolSizeAdjust", 0.50); -} - - -void QGIDatumLabel::setPrettySel() -{ - // Base::Console().Message("QGIDL::setPrettySel()\n"); - m_dimText->setPrettySel(); - m_tolTextOver->setPrettySel(); - m_tolTextUnder->setPrettySel(); - m_unitText->setPrettySel(); - setFrameColor(PreferencesGui::selectQColor()); - Q_EMIT setPretty(SEL); -} - -void QGIDatumLabel::setPrettyPre() -{ - // Base::Console().Message("QGIDL::setPrettyPre()\n"); - m_dimText->setPrettyPre(); - m_tolTextOver->setPrettyPre(); - m_tolTextUnder->setPrettyPre(); - m_unitText->setPrettyPre(); - setFrameColor(PreferencesGui::preselectQColor()); - Q_EMIT setPretty(PRE); -} - -void QGIDatumLabel::setPrettyNormal() -{ - // Base::Console().Message("QGIDL::setPrettyNormal()\n"); - m_dimText->setPrettyNormal(); - m_tolTextOver->setPrettyNormal(); - m_tolTextUnder->setPrettyNormal(); - m_unitText->setPrettyNormal(); - setFrameColor(PreferencesGui::normalQColor()); - Q_EMIT setPretty(NORMAL); -} - -void QGIDatumLabel::setColor(QColor color) -{ - // Base::Console().Message("QGIDL::setColor(%s)\n", qPrintable(c.name())); - m_colNormal = color; - m_dimText->setColor(m_colNormal); - m_tolTextOver->setColor(m_colNormal); - m_tolTextUnder->setColor(m_colNormal); - m_unitText->setColor(m_colNormal); - setFrameColor(m_colNormal); -} - -void QGIDatumLabel::setSelectability(bool val) -{ - setFlag(ItemIsSelectable, val); - setAcceptHoverEvents(val); - setAcceptedMouseButtons(val ? Qt::AllButtons : Qt::NoButton); -} - -//************************************************************** QGIViewDimension::QGIViewDimension() : dvDimension(nullptr), hasHover(false), m_lineWidth(0.0) { setHandlesChildEvents(false); diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.h b/src/Mod/TechDraw/Gui/QGIViewDimension.h index 23179b8dd2..1546bfcaaf 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.h +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.h @@ -52,111 +52,13 @@ namespace TechDrawGui { class QGCustomText; class QGIArrow; +class QGIDatumLabel; class QGIDimLines; class QGIViewDimension; class QGCustomSvg; class ViewProviderDimension; enum class DragState; -class QGIDatumLabel : public QGraphicsObject -{ -Q_OBJECT - -public: - QGIDatumLabel(); - ~QGIDatumLabel() override = default; - - enum {Type = QGraphicsItem::UserType + 107}; - int type() const override { return Type;} - - QRectF boundingRect() const override; - QRectF tightBoundingRect() const; - void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; - void paint( QPainter *painter, - const QStyleOptionGraphicsItem *option, - QWidget *widget = nullptr ) override; - void setLabelCenter(); - void setPosFromCenter(const double &xCenter, const double &yCenter); - double X() const { return posX; } - double Y() const { return posY; } //minus posY? - Base::Vector2d getPosToCenterVec(); - - void setFont(QFont font); - QFont getFont() const { return m_dimText->font(); } - void setDimString(QString text); - void setDimString(QString text, qreal maxWidth); - void setToleranceString(); - void setPrettySel(); - void setPrettyPre(); - void setPrettyNormal(); - void setColor(QColor color); - void setSelectability(bool val); - void setFrameColor(QColor color); - - QGCustomText* getDimText() { return m_dimText; } - void setDimText(QGCustomText* newText) { m_dimText = newText; } - QGCustomText* getTolTextOver() { return m_tolTextOver; } - void setTolTextOver(QGCustomText* newTol) { m_tolTextOver = newTol; } - QGCustomText* getTolTextUnder() { return m_tolTextUnder; } - void setTolTextUnder(QGCustomText* newTol) { m_tolTextOver = newTol; } - - double getTolAdjust(); - - bool isFramed() const { return m_frame->parentItem(); } // If empty pointer, then no frame - void setFramed(bool framed); - - double getLineWidth() const { return m_frame->pen().widthF(); } - void setLineWidth(double lineWidth); - void setQDim(QGIViewDimension* qDim) { parent = qDim;} - -Q_SIGNALS: - void setPretty(int state); - void dragging(bool); - void hover(bool state); - void selected(bool state); - void dragFinished(); - -protected: - QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; - void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; - void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; - void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; - void updateFrameRect(); - - int getPrecision(); - - void snapPosition(QPointF& position); - - bool getVerticalSep() const { return verticalSep; } - void setVerticalSep(bool sep) { verticalSep = sep; } - std::vector getSeps() const { return seps; } - void setSeps(std::vector newSeps) { seps = newSeps; } - -private: - bool verticalSep; - std::vector seps; - - QGIViewDimension* parent; - - QGCustomText* m_dimText; - QGCustomText* m_tolTextOver; - QGCustomText* m_tolTextUnder; - QGCustomText* m_unitText; - QGraphicsItemGroup* m_textItems; - QGraphicsRectItem* m_frame; - QColor m_colNormal; - bool m_ctrl; - - double posX; - double posY; - - DragState m_dragState; - -private: -}; - -//******************************************************************* class TechDrawGuiExport QGIViewDimension : public QGIView {