TechDraw: extract QGIDatumLabel

This commit is contained in:
Benjamin Bræstrup Sayoc
2025-02-18 14:46:24 +01:00
committed by Chris Hennes
parent 7b21ed43da
commit aae45a2b8a
7 changed files with 719 additions and 637 deletions

View File

@@ -272,6 +272,8 @@ SET(TechDrawGuiView_SRCS
QGIView.h
QGIArrow.cpp
QGIArrow.h
QGIDatumLabel.cpp
QGIDatumLabel.h
QGIEdge.cpp
QGIEdge.h
QGIFace.cpp

View File

@@ -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"

View File

@@ -62,6 +62,7 @@
#include <Mod/TechDraw/App/Preferences.h>
#include "MDIViewPage.h"
#include "QGIDatumLabel.h"
#include "QGIEdge.h"
#include "QGIFace.h"
#include "QGIVertex.h"

View File

@@ -0,0 +1,579 @@
/***************************************************************************
* Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk> *
* *
* 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 <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <Gui/Application.h>
#include <Mod/TechDraw/App/DimensionFormatter.h>
#include <Mod/TechDraw/App/DimensionGeometry.h>
#include <Mod/TechDraw/App/DrawViewDimension.h>
#include <Mod/TechDraw/App/Preferences.h>
#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<QGIViewDimension*>(parentItem());
if (!qgivd) {
return;
}
auto* dim(dynamic_cast<TechDraw::DrawViewDimension*>(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<QGIView*>(qgivd->parentItem());
if (qgiv) {
auto* dvp = dynamic_cast<TechDraw::DrawViewPart*>(qgiv->getViewObject());
if (dvp) {
snapPercent = 0.2;
std::vector<TechDraw::DrawViewDimension*> 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<ViewProviderDimension*>(Gui::Application::Instance->getViewProvider(d));
if (!vp) { continue; }
auto* qgivDi(dynamic_cast<QGIViewDimension*>(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<QGIViewDimension*>(parentItem());
if (!qgivDimension) {
qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No parent item";
return;
}
auto ViewProvider = dynamic_cast<ViewProviderDimension*>(
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<QGCustomText*>(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<QGIViewDimension*>(parentItem());
if (!qgivd) {
return;
}
const auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(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<int>(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<QGIViewDimension*>(parentItem());
if (!qgivd) {
return;
}
const auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(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<std::string, std::string> 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

View File

@@ -0,0 +1,134 @@
/***************************************************************************
* Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk> *
* *
* 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 <QGraphicsObject>
#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<int> getSeps() const { return seps; }
void setSeps(std::vector<int> newSeps) { seps = newSeps; }
private:
bool verticalSep;
std::vector<int> 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

View File

@@ -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<QGIViewDimension*>(parentItem());
if (!qgivd) {
return;
}
auto* dim(dynamic_cast<TechDraw::DrawViewDimension*>(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<QGIView*>(qgivd->parentItem());
if (qgiv) {
auto* dvp = dynamic_cast<TechDraw::DrawViewPart*>(qgiv->getViewObject());
if (dvp) {
snapPercent = 0.2;
std::vector<TechDraw::DrawViewDimension*> 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<ViewProviderDimension*>(Gui::Application::Instance->getViewProvider(d));
if (!vp) { continue; }
auto* qgivDi(dynamic_cast<QGIViewDimension*>(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<QGIViewDimension*>(parentItem());
if (!qgivDimension) {
qWarning() << "QGIDatumLabel::mouseDoubleClickEvent: No parent item";
return;
}
auto ViewProvider = dynamic_cast<ViewProviderDimension*>(
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<QGCustomText*>(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<QGIViewDimension*>(parentItem());
if (!qgivd) {
return;
}
const auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(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<QGIViewDimension*>(parentItem());
if (!qgivd) {
return;
}
const auto dim(dynamic_cast<TechDraw::DrawViewDimension*>(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<std::string, std::string> 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);

View File

@@ -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<int> getSeps() const { return seps; }
void setSeps(std::vector<int> newSeps) { seps = newSeps; }
private:
bool verticalSep;
std::vector<int> 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
{