feat: add text item with IShape interface decoupling
Extract a pure virtual IShape interface from AbstractShapeType<T> so selectors and Document use dynamic_cast<IShape *> instead of qgraphicsitem_cast<AbstractShape *>. This decouples the selector system from the template hierarchy, enabling GraphicsTextItem (backed by QGraphicsTextItem) to work uniformly with all existing selectors. GraphicsTextItem supports inline text editing (double-click to edit, focus-out to finish), font/color properties via PropertyEditor, resize with proportional font scaling, move, rotate, and full serialization. Adds CM_place creation mode for single-click placement.
This commit is contained in:
parent
cd343895f9
commit
8ea5033f0d
|
|
@ -65,6 +65,7 @@ set(H_HEADER_FILES
|
||||||
include/graphicsItem/graphicsBaseItem.h
|
include/graphicsItem/graphicsBaseItem.h
|
||||||
include/graphicsItem/graphicsRectItem.h
|
include/graphicsItem/graphicsRectItem.h
|
||||||
include/graphicsItem/graphicsPolygonItem.h
|
include/graphicsItem/graphicsPolygonItem.h
|
||||||
|
include/graphicsItem/graphicsTextItem.h
|
||||||
include/graphicsItem/graphicsItemGroup.h
|
include/graphicsItem/graphicsItemGroup.h
|
||||||
)
|
)
|
||||||
set(CPP_SOURCE_FILES
|
set(CPP_SOURCE_FILES
|
||||||
|
|
@ -93,6 +94,7 @@ set(CPP_SOURCE_FILES
|
||||||
source/graphicsItem/graphicsBaseItem.cpp
|
source/graphicsItem/graphicsBaseItem.cpp
|
||||||
source/graphicsItem/graphicsRectItem.cpp
|
source/graphicsItem/graphicsRectItem.cpp
|
||||||
source/graphicsItem/graphicsPolygonItem.cpp
|
source/graphicsItem/graphicsPolygonItem.cpp
|
||||||
|
source/graphicsItem/graphicsTextItem.cpp
|
||||||
source/graphicsItem/graphicsItemGroup.cpp
|
source/graphicsItem/graphicsItemGroup.cpp
|
||||||
)
|
)
|
||||||
set(UI_FILES
|
set(UI_FILES
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ enum GraphicsItemType
|
||||||
GIT_rect = QGraphicsItem::UserType + 3,
|
GIT_rect = QGraphicsItem::UserType + 3,
|
||||||
GIT_roundRect = QGraphicsItem::UserType + 4,
|
GIT_roundRect = QGraphicsItem::UserType + 4,
|
||||||
GIT_ellipse = QGraphicsItem::UserType + 5,
|
GIT_ellipse = QGraphicsItem::UserType + 5,
|
||||||
GIT_polygon = QGraphicsItem::UserType + 6
|
GIT_polygon = QGraphicsItem::UserType + 6,
|
||||||
|
GIT_text = QGraphicsItem::UserType + 7
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,30 @@ enum ShapeType
|
||||||
T_group
|
T_group
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Common non-template interface for selector dispatch.
|
||||||
|
// All item types (rect, polygon, text, group) implement this via AbstractShapeType<T>.
|
||||||
|
class IShape
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IShape() {}
|
||||||
|
virtual int collidesWithHandle(const QPointF &point) = 0;
|
||||||
|
virtual void createOperationCopy() = 0;
|
||||||
|
virtual void removeOperationCopy() = 0;
|
||||||
|
virtual void moveOperationCopy(const QPointF &) = 0;
|
||||||
|
virtual void rotateOperationCopy(const double &) = 0;
|
||||||
|
virtual void syncRotationDataFromParent(const double &) = 0;
|
||||||
|
virtual double getSyncRotationDataFromParent() = 0;
|
||||||
|
virtual QPointF getSymmetricPointPos(int nHandle) = 0;
|
||||||
|
virtual void resize(int, double, double, const QPointF &) = 0;
|
||||||
|
virtual void updateCoordinate() = 0;
|
||||||
|
virtual void editShape(int, const QPointF &) = 0;
|
||||||
|
virtual void move(const QPointF &) = 0;
|
||||||
|
virtual QJsonObject serialize() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
//基类采用模板形式,QGraphicsItem是默认值,也可以是别的类型,比如QGraphicsItemGroup,这样不同的基类继承可以共用一些高层的行为定义
|
//基类采用模板形式,QGraphicsItem是默认值,也可以是别的类型,比如QGraphicsItemGroup,这样不同的基类继承可以共用一些高层的行为定义
|
||||||
template <typename BaseType = QGraphicsItem>
|
template <typename BaseType = QGraphicsItem>
|
||||||
class AbstractShapeType : public BaseType
|
class AbstractShapeType : public BaseType, public IShape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AbstractShapeType(QGraphicsItem *parent = 0)
|
explicit AbstractShapeType(QGraphicsItem *parent = 0)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#ifndef GRAPHICSTEXTITEM_H
|
||||||
|
#define GRAPHICSTEXTITEM_H
|
||||||
|
|
||||||
|
#include "graphicsBaseItem.h"
|
||||||
|
#include "IPropertyGroupProvider.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include <QGraphicsTextItem>
|
||||||
|
#include <QFont>
|
||||||
|
|
||||||
|
class GraphicsTextItem : public AbstractShapeType<QGraphicsTextItem>,
|
||||||
|
public IPropertyGroupProvider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString text READ toPlainText WRITE setPlainText)
|
||||||
|
Q_PROPERTY(QFont itemFont READ itemFont WRITE setItemFont)
|
||||||
|
Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
|
||||||
|
Q_PROPERTY(QColor backgroundColor READ brushColor WRITE setBrushColor)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GraphicsTextItem(QGraphicsItem *parent = nullptr);
|
||||||
|
virtual ~GraphicsTextItem();
|
||||||
|
|
||||||
|
// IPropertyGroupProvider
|
||||||
|
QString getPropertyGroup(const QString &propertyName) const override;
|
||||||
|
|
||||||
|
// Override QGraphicsItem::type()
|
||||||
|
enum { Type = GIT_text };
|
||||||
|
int type() const override { return Type; }
|
||||||
|
|
||||||
|
void resize(int nHandle, double dSX, double dSY, const QPointF &basePoint) override;
|
||||||
|
void updateCoordinate() override;
|
||||||
|
void move(const QPointF &pt) override;
|
||||||
|
QJsonObject serialize() const override;
|
||||||
|
static QGraphicsItem *createFromJson(const QJsonObject &obj);
|
||||||
|
|
||||||
|
void createOperationCopy() override;
|
||||||
|
void removeOperationCopy() override;
|
||||||
|
void moveOperationCopy(const QPointF &) override;
|
||||||
|
void rotateOperationCopy(const double &) override;
|
||||||
|
void syncRotationDataFromParent(const double &) override;
|
||||||
|
|
||||||
|
// Font property accessors
|
||||||
|
QFont itemFont() const { return font(); }
|
||||||
|
void setItemFont(const QFont &f) { setFont(f); updateSizeFromText(); }
|
||||||
|
|
||||||
|
// Text color accessor (foreground)
|
||||||
|
QColor textColor() const { return defaultTextColor(); }
|
||||||
|
void setTextColor(const QColor &c) { setDefaultTextColor(c); }
|
||||||
|
|
||||||
|
// Editing mode (separate from selection)
|
||||||
|
void startEditing();
|
||||||
|
void endEditing();
|
||||||
|
bool isEditing() const { return m_bEditing; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QPainterPath shape() override;
|
||||||
|
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
|
||||||
|
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
|
||||||
|
void focusOutEvent(QFocusEvent *event) override;
|
||||||
|
void focusInEvent(QFocusEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateHandles() override;
|
||||||
|
void updateSizeFromText();
|
||||||
|
|
||||||
|
QRectF m_lastBoundingRect;
|
||||||
|
double m_dLastPointSize = 14;
|
||||||
|
bool m_bEditing = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GRAPHICSTEXTITEM_H
|
||||||
|
|
@ -14,8 +14,9 @@
|
||||||
|
|
||||||
enum CreatingMethod
|
enum CreatingMethod
|
||||||
{
|
{
|
||||||
CM_drag, //多拽,默认的创建方式
|
CM_drag, // drag-to-create (rect, roundRect)
|
||||||
CM_click //单击点选,如多边形、线段等
|
CM_click, // click-to-place points (polygon)
|
||||||
|
CM_place // single click to place (text)
|
||||||
};
|
};
|
||||||
|
|
||||||
class GraphicsBaseItem;
|
class GraphicsBaseItem;
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ bool Document::saveAs(const QString &filePath)
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
if (auto *group = dynamic_cast<GraphicsItemGroup *>(item))
|
if (auto *group = dynamic_cast<GraphicsItemGroup *>(item))
|
||||||
obj = group->serialize();
|
obj = group->serialize();
|
||||||
else if (auto *shape = qgraphicsitem_cast<AbstractShape *>(item))
|
else if (auto *shape = dynamic_cast<IShape *>(item))
|
||||||
obj = shape->serialize();
|
obj = shape->serialize();
|
||||||
if (!obj.isEmpty())
|
if (!obj.isEmpty())
|
||||||
items.append(obj);
|
items.append(obj);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ GraphicElementsPanel::GraphicElementsPanel(QWidget *parent)
|
||||||
connect(ui->pushBtn_roundRect, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem()));
|
connect(ui->pushBtn_roundRect, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem()));
|
||||||
ui->pushBtn_polygon->setProperty("shap",GIT_polygon);
|
ui->pushBtn_polygon->setProperty("shap",GIT_polygon);
|
||||||
connect(ui->pushBtn_polygon, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem()));
|
connect(ui->pushBtn_polygon, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem()));
|
||||||
|
ui->pushBtn_text->setProperty("shap",GIT_text);
|
||||||
|
connect(ui->pushBtn_text, SIGNAL(clicked()), this, SLOT(onBtnClicked_GraphicsItem()));
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicElementsPanel::~GraphicElementsPanel()
|
GraphicElementsPanel::~GraphicElementsPanel()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,319 @@
|
||||||
|
#include "graphicsItem/graphicsTextItem.h"
|
||||||
|
#include "graphicsItem/itemControlHandle.h"
|
||||||
|
#include "graphicsItem/graphicsItemGroup.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QStyleOptionGraphicsItem>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QGraphicsScene>
|
||||||
|
#include <QFocusEvent>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
|
GraphicsTextItem::GraphicsTextItem(QGraphicsItem *parent)
|
||||||
|
: AbstractShapeType<QGraphicsTextItem>(parent)
|
||||||
|
{
|
||||||
|
m_type = T_item;
|
||||||
|
m_pen = QPen(Qt::NoPen);
|
||||||
|
m_brush = QBrush(Qt::NoBrush);
|
||||||
|
|
||||||
|
// 8 resize handles
|
||||||
|
m_vecHanle.reserve(H_left);
|
||||||
|
for (int i = H_leftTop; i <= H_left; i++) {
|
||||||
|
auto *h = new ItemControlHandle(this);
|
||||||
|
h->setType(T_resize);
|
||||||
|
h->setTag(i);
|
||||||
|
m_vecHanle.push_back(h);
|
||||||
|
}
|
||||||
|
// 4 rotate handles
|
||||||
|
for (int i = H_rotate_leftTop; i <= H_rotate_leftBottom; i++) {
|
||||||
|
auto *h = new ItemControlHandle(this);
|
||||||
|
h->setType(T_rotate);
|
||||||
|
h->setTag(i);
|
||||||
|
m_vecHanle.push_back(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFlag(QGraphicsItem::ItemIsMovable, false); // we handle move via selector
|
||||||
|
setFlag(QGraphicsItem::ItemIsSelectable, true);
|
||||||
|
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
|
||||||
|
setAcceptHoverEvents(true);
|
||||||
|
|
||||||
|
QFont defaultFont;
|
||||||
|
defaultFont.setPointSize(14);
|
||||||
|
setFont(defaultFont);
|
||||||
|
setDefaultTextColor(Qt::black);
|
||||||
|
setPlainText("Text");
|
||||||
|
|
||||||
|
updateSizeFromText();
|
||||||
|
m_boundingRect = QRectF(0, 0, m_dWidth, m_dHeight);
|
||||||
|
setTransformOriginPoint(m_boundingRect.center());
|
||||||
|
m_lastBoundingRect = m_boundingRect;
|
||||||
|
|
||||||
|
// Keep bounding rect in sync with text content during editing
|
||||||
|
connect(document(), &QTextDocument::contentsChanged, this, [this]() {
|
||||||
|
prepareGeometryChange();
|
||||||
|
updateSizeFromText();
|
||||||
|
m_boundingRect = QRectF(0, 0, m_dWidth, m_dHeight);
|
||||||
|
setTransformOriginPoint(m_boundingRect.center());
|
||||||
|
updateHandles();
|
||||||
|
m_lastBoundingRect = m_boundingRect;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsTextItem::~GraphicsTextItem() {}
|
||||||
|
|
||||||
|
QString GraphicsTextItem::getPropertyGroup(const QString &propertyName) const
|
||||||
|
{
|
||||||
|
QList<QString> properties = {
|
||||||
|
QStringLiteral("text"),
|
||||||
|
QStringLiteral("itemFont"),
|
||||||
|
QStringLiteral("textColor"),
|
||||||
|
QStringLiteral("backgroundColor")
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!properties.contains(propertyName))
|
||||||
|
return {};
|
||||||
|
else
|
||||||
|
return QStringLiteral("基本信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath GraphicsTextItem::shape()
|
||||||
|
{
|
||||||
|
QPainterPath path;
|
||||||
|
path.addRect(m_boundingRect);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::updateSizeFromText()
|
||||||
|
{
|
||||||
|
QSizeF textSize = document()->size();
|
||||||
|
double w = qMax(textSize.width(), 40.0);
|
||||||
|
double h = qMax(textSize.height(), 20.0);
|
||||||
|
m_dWidth = w;
|
||||||
|
m_dHeight = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::updateCoordinate()
|
||||||
|
{
|
||||||
|
if (!parentItem()) {
|
||||||
|
updateSizeFromText();
|
||||||
|
prepareGeometryChange();
|
||||||
|
m_boundingRect = QRectF(0, 0, m_dWidth, m_dHeight);
|
||||||
|
setTransformOriginPoint(m_boundingRect.center());
|
||||||
|
updateHandles();
|
||||||
|
}
|
||||||
|
m_lastBoundingRect = m_boundingRect;
|
||||||
|
m_dLastPointSize = font().pointSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::updateHandles()
|
||||||
|
{
|
||||||
|
AbstractShapeType<QGraphicsTextItem>::updateHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||||
|
{
|
||||||
|
// Background fill
|
||||||
|
if (m_brush.style() != Qt::NoBrush) {
|
||||||
|
painter->setPen(Qt::NoPen);
|
||||||
|
painter->setBrush(m_brush);
|
||||||
|
painter->drawRect(m_boundingRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text rendering via QGraphicsTextItem
|
||||||
|
QGraphicsTextItem::paint(painter, option, widget);
|
||||||
|
|
||||||
|
// Selection indicator
|
||||||
|
if (option->state & QStyle::State_Selected) {
|
||||||
|
int nPenWidth = 1;
|
||||||
|
painter->setPen(QPen(QColor(70, 70, 70), nPenWidth, Qt::DashLine));
|
||||||
|
painter->setBrush(Qt::NoBrush);
|
||||||
|
painter->drawRect(m_boundingRect_selected);
|
||||||
|
|
||||||
|
painter->setBrush(Qt::red);
|
||||||
|
painter->drawEllipse(transformOriginPoint(), 4, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::resize(int nHandle, double dSX, double dSY, const QPointF &basePoint)
|
||||||
|
{
|
||||||
|
switch (nHandle) {
|
||||||
|
case H_left:
|
||||||
|
case H_right:
|
||||||
|
dSY = 1;
|
||||||
|
break;
|
||||||
|
case H_top:
|
||||||
|
case H_bottom:
|
||||||
|
dSX = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTransform trans;
|
||||||
|
trans.translate(basePoint.x(), basePoint.y());
|
||||||
|
trans.scale(dSX, dSY);
|
||||||
|
trans.translate(-basePoint.x(), -basePoint.y());
|
||||||
|
|
||||||
|
prepareGeometryChange();
|
||||||
|
m_boundingRect = trans.mapRect(m_lastBoundingRect);
|
||||||
|
m_dWidth = m_boundingRect.width();
|
||||||
|
m_dHeight = m_boundingRect.height();
|
||||||
|
|
||||||
|
// Scale font size proportionally from pre-drag value
|
||||||
|
double avgScale = (qAbs(dSX) + qAbs(dSY)) / 2.0;
|
||||||
|
QFont f = font();
|
||||||
|
int newSize = qMax(1, qRound(m_dLastPointSize * avgScale));
|
||||||
|
f.setPointSize(newSize);
|
||||||
|
setFont(f);
|
||||||
|
|
||||||
|
setTransformOriginPoint(m_boundingRect.center());
|
||||||
|
updateHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::move(const QPointF &pt)
|
||||||
|
{
|
||||||
|
moveBy(pt.x(), pt.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant GraphicsTextItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
||||||
|
{
|
||||||
|
if (change == QGraphicsItem::ItemSelectedHasChanged) {
|
||||||
|
QGraphicsItemGroup *group = dynamic_cast<QGraphicsItemGroup *>(parentItem());
|
||||||
|
if (!group)
|
||||||
|
setHandleVisible(value.toBool());
|
||||||
|
else {
|
||||||
|
setSelected(false);
|
||||||
|
return QVariant::fromValue<bool>(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QGraphicsTextItem::itemChange(change, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::startEditing()
|
||||||
|
{
|
||||||
|
m_bEditing = true;
|
||||||
|
setHandleVisible(false);
|
||||||
|
setTextInteractionFlags(Qt::TextEditorInteraction);
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::endEditing()
|
||||||
|
{
|
||||||
|
m_bEditing = false;
|
||||||
|
setTextInteractionFlags(Qt::NoTextInteraction);
|
||||||
|
updateCoordinate();
|
||||||
|
if (isSelected())
|
||||||
|
setHandleVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::focusOutEvent(QFocusEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsTextItem::focusOutEvent(event);
|
||||||
|
endEditing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::focusInEvent(QFocusEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsTextItem::focusInEvent(event);
|
||||||
|
setHandleVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::createOperationCopy()
|
||||||
|
{
|
||||||
|
m_pOperationCopy = new QGraphicsPathItem(shape());
|
||||||
|
m_pOperationCopy->setPen(Qt::DashLine);
|
||||||
|
m_pOperationCopy->setPos(pos());
|
||||||
|
m_pOperationCopy->setTransformOriginPoint(transformOriginPoint());
|
||||||
|
m_pOperationCopy->setTransform(transform());
|
||||||
|
m_pOperationCopy->setRotation(rotation());
|
||||||
|
m_pOperationCopy->setScale(scale());
|
||||||
|
m_pOperationCopy->setZValue(zValue());
|
||||||
|
|
||||||
|
QGraphicsScene *s = scene();
|
||||||
|
if (s && m_pOperationCopy) {
|
||||||
|
s->addItem(m_pOperationCopy);
|
||||||
|
m_movingIniPos = pos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::removeOperationCopy()
|
||||||
|
{
|
||||||
|
QGraphicsScene *s = scene();
|
||||||
|
if (s && m_pOperationCopy) {
|
||||||
|
if (pos() != m_pOperationCopy->pos())
|
||||||
|
setPos(m_pOperationCopy->pos());
|
||||||
|
if (rotation() != m_pOperationCopy->rotation())
|
||||||
|
setRotation(m_pOperationCopy->rotation());
|
||||||
|
s->removeItem(m_pOperationCopy);
|
||||||
|
delete m_pOperationCopy;
|
||||||
|
m_pOperationCopy = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::moveOperationCopy(const QPointF &distance)
|
||||||
|
{
|
||||||
|
if (m_pOperationCopy)
|
||||||
|
m_pOperationCopy->setPos(m_movingIniPos + distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::rotateOperationCopy(const double &dAngle)
|
||||||
|
{
|
||||||
|
if (m_pOperationCopy)
|
||||||
|
m_pOperationCopy->setRotation(dAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsTextItem::syncRotationDataFromParent(const double &data)
|
||||||
|
{
|
||||||
|
m_dSyncRotationByParent += data;
|
||||||
|
if (m_dSyncRotationByParent > 180)
|
||||||
|
m_dSyncRotationByParent -= 360;
|
||||||
|
if (m_dSyncRotationByParent < -180)
|
||||||
|
m_dSyncRotationByParent += 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "util/serializationUtils.h"
|
||||||
|
|
||||||
|
QJsonObject GraphicsTextItem::serialize() const
|
||||||
|
{
|
||||||
|
QJsonObject obj = serializeCommon(this, "text");
|
||||||
|
obj["text"] = toPlainText();
|
||||||
|
QJsonObject fontObj;
|
||||||
|
fontObj["family"] = font().family();
|
||||||
|
fontObj["bold"] = font().bold();
|
||||||
|
fontObj["italic"] = font().italic();
|
||||||
|
fontObj["underline"] = font().underline();
|
||||||
|
fontObj["pointSize"] = font().pointSize();
|
||||||
|
obj["font"] = fontObj;
|
||||||
|
obj["textColor"] = defaultTextColor().name(QColor::HexArgb);
|
||||||
|
obj["backgroundColor"] = m_brush.color().name(QColor::HexArgb);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "util/serializationRegistry.h"
|
||||||
|
|
||||||
|
QGraphicsItem *GraphicsTextItem::createFromJson(const QJsonObject &obj)
|
||||||
|
{
|
||||||
|
auto *item = new GraphicsTextItem();
|
||||||
|
item->setPlainText(obj["text"].toString());
|
||||||
|
QJsonObject f = obj["font"].toObject();
|
||||||
|
QFont font(f["family"].toString(), f["pointSize"].toInt(14));
|
||||||
|
font.setBold(f["bold"].toBool());
|
||||||
|
font.setItalic(f["italic"].toBool());
|
||||||
|
font.setUnderline(f["underline"].toBool());
|
||||||
|
item->setFont(font);
|
||||||
|
item->setDefaultTextColor(QColor(obj["textColor"].toString()));
|
||||||
|
QColor bgColor(obj["backgroundColor"].toString());
|
||||||
|
if (bgColor.isValid())
|
||||||
|
item->setBrushColor(bgColor);
|
||||||
|
applyCommon(item, obj);
|
||||||
|
item->updateSizeFromText();
|
||||||
|
item->updateCoordinate();
|
||||||
|
item->setHandleVisible(false);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct TextItemRegistrar {
|
||||||
|
TextItemRegistrar() {
|
||||||
|
registerItemFactory("text", &GraphicsTextItem::createFromJson);
|
||||||
|
}
|
||||||
|
} _textItemRegistrar;
|
||||||
|
|
@ -228,8 +228,10 @@ void CMainWindow::onSignal_selectionChanged()
|
||||||
m_pPropertiesEditorView->setObject(m_pDocument->scene());
|
m_pPropertiesEditorView->setObject(m_pDocument->scene());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GraphicsBaseItem *item = static_cast<GraphicsBaseItem*>(selectedItems.first());
|
QGraphicsItem *gitem = selectedItems.first();
|
||||||
m_pPropertiesEditorView->setObject(static_cast<QObject*>(item));
|
QObject *obj = dynamic_cast<QObject *>(gitem);
|
||||||
|
if (obj)
|
||||||
|
m_pPropertiesEditorView->setObject(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- File operations -------------------------------------------------------
|
// -- File operations -------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <QGraphicsSceneMouseEvent>
|
#include <QGraphicsSceneMouseEvent>
|
||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <graphicsItem/graphicsBaseItem.h>
|
#include <graphicsItem/graphicsBaseItem.h>
|
||||||
|
#include <graphicsItem/graphicsTextItem.h>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
QPointF BaseSelector::s_ptMouseDown(0.0, 0.0);
|
QPointF BaseSelector::s_ptMouseDown(0.0, 0.0);
|
||||||
|
|
@ -37,7 +38,8 @@ void BaseSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScen
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
if (items.count() == 1) //只有一个选中
|
if (items.count() == 1) //只有一个选中
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
|
QGraphicsItem *gitem = items.first();
|
||||||
|
IShape *item = dynamic_cast<IShape *>(gitem);
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
//需要增加当前是否点击在控制点的判断函数
|
//需要增加当前是否点击在控制点的判断函数
|
||||||
|
|
@ -51,7 +53,7 @@ void BaseSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScen
|
||||||
{
|
{
|
||||||
m_opMode = OM_rotate;
|
m_opMode = OM_rotate;
|
||||||
//计算夹角
|
//计算夹角
|
||||||
QPointF originPoint = item->mapToScene(item->boundingRect().center());
|
QPointF originPoint = gitem->mapToScene(gitem->boundingRect().center());
|
||||||
double dLengthY = s_ptMouseLast.y() - originPoint.y();
|
double dLengthY = s_ptMouseLast.y() - originPoint.y();
|
||||||
double dLengthX = s_ptMouseLast.x() - originPoint.x();
|
double dLengthX = s_ptMouseLast.x() - originPoint.x();
|
||||||
s_dAngleMouseDownToItem = atan2(dLengthY, dLengthX) * 180 / M_PI;
|
s_dAngleMouseDownToItem = atan2(dLengthY, dLengthX) * 180 / M_PI;
|
||||||
|
|
@ -87,7 +89,8 @@ void BaseSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScen
|
||||||
for(int n = 0; n < items.size(); n++)
|
for(int n = 0; n < items.size(); n++)
|
||||||
{
|
{
|
||||||
//创建副本
|
//创建副本
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
|
IShape *item = dynamic_cast<IShape *>(items.at(n));
|
||||||
|
if (item)
|
||||||
item->createOperationCopy();
|
item->createOperationCopy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +111,8 @@ void BaseSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene
|
||||||
|
|
||||||
if (items.count() == 1)
|
if (items.count() == 1)
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
|
QGraphicsItem *gitem = items.first();
|
||||||
|
IShape *item = dynamic_cast<IShape *>(gitem);
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
if(s_nDragHandle == H_none)
|
if(s_nDragHandle == H_none)
|
||||||
|
|
@ -128,7 +132,7 @@ void BaseSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//划分为四组区间范围,分别水平组、垂直组、一三象限倾斜组、二四象限倾斜组,每组由两个对称区间构成
|
//划分为四组区间范围,分别水平组、垂直组、一三象限倾斜组、二四象限倾斜组,每组由两个对称区间构成
|
||||||
double dRotation = item->rotation();
|
double dRotation = gitem->rotation();
|
||||||
dRotation += item->getSyncRotationDataFromParent();
|
dRotation += item->getSyncRotationDataFromParent();
|
||||||
//让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断
|
//让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断
|
||||||
if (dRotation > 180)
|
if (dRotation > 180)
|
||||||
|
|
@ -295,7 +299,12 @@ void BaseSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, DesignerSc
|
||||||
void BaseSelector::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene)
|
void BaseSelector::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, DesignerScene* scene)
|
||||||
{
|
{
|
||||||
Q_UNUSED(event)
|
Q_UNUSED(event)
|
||||||
Q_UNUSED(scene)
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
|
if (items.count() == 1) {
|
||||||
|
GraphicsTextItem *textItem = dynamic_cast<GraphicsTextItem *>(items.first());
|
||||||
|
if (textItem && !textItem->isEditing())
|
||||||
|
textItem->startEditing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseSelector::setCursor(DesignerScene *scene, const QCursor &cursor)
|
void BaseSelector::setCursor(DesignerScene *scene, const QCursor &cursor)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "util/creatingSelector.h"
|
#include "util/creatingSelector.h"
|
||||||
#include "graphicsItem/graphicsRectItem.h"
|
#include "graphicsItem/graphicsRectItem.h"
|
||||||
#include "graphicsItem/graphicsPolygonItem.h"
|
#include "graphicsItem/graphicsPolygonItem.h"
|
||||||
|
#include "graphicsItem/graphicsTextItem.h"
|
||||||
#include <QGraphicsSceneMouseEvent>
|
#include <QGraphicsSceneMouseEvent>
|
||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
|
|
||||||
|
|
@ -49,6 +50,19 @@ void CreatingSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, Designer
|
||||||
m_pCreatingItem = new GraphicPolygonItem();
|
m_pCreatingItem = new GraphicPolygonItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GIT_text:
|
||||||
|
{
|
||||||
|
GraphicsTextItem *textItem = new GraphicsTextItem();
|
||||||
|
textItem->setPos(event->scenePos());
|
||||||
|
textItem->updateCoordinate();
|
||||||
|
textItem->setSelected(true);
|
||||||
|
scene->addItem(textItem);
|
||||||
|
emit scene->signalAddItem(textItem);
|
||||||
|
|
||||||
|
setCursor(scene, Qt::ArrowCursor);
|
||||||
|
emit setWorkingSelector(ST_base);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ void EditingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSc
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
if (items.count() == 1)
|
if (items.count() == 1)
|
||||||
{
|
{
|
||||||
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.first());
|
IShape *item = dynamic_cast<IShape *>(items.first());
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
if(s_nDragHandle > H_left)
|
if(s_nDragHandle > H_left)
|
||||||
|
|
@ -40,7 +40,7 @@ void EditingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Designe
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
if (items.count() == 1)
|
if (items.count() == 1)
|
||||||
{
|
{
|
||||||
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.first());
|
IShape *item = dynamic_cast<IShape *>(items.first());
|
||||||
if(item && s_ptMouseLast != s_ptMouseDown)
|
if(item && s_ptMouseLast != s_ptMouseDown)
|
||||||
{
|
{
|
||||||
item->updateCoordinate();
|
item->updateCoordinate();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ void MovingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSce
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
for(int n = 0; n < items.size(); n++)
|
for(int n = 0; n < items.size(); n++)
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
|
IShape *item = dynamic_cast<IShape *>(items.at(n));
|
||||||
if(item)
|
if(item)
|
||||||
item->moveOperationCopy(s_ptMouseLast - s_ptMouseDown);
|
item->moveOperationCopy(s_ptMouseLast - s_ptMouseDown);
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ void MovingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Designer
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
for(int n = 0; n < items.size(); n++)
|
for(int n = 0; n < items.size(); n++)
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
|
IShape *item = dynamic_cast<IShape *>(items.at(n));
|
||||||
if(item)
|
if(item)
|
||||||
item->removeOperationCopy();
|
item->removeOperationCopy();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,18 +24,19 @@ void RotationSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerS
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
if (items.count() == 1)
|
if (items.count() == 1)
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
|
QGraphicsItem *gitem = items.first();
|
||||||
|
IShape *item = dynamic_cast<IShape *>(gitem);
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
//计算夹角
|
//计算夹角
|
||||||
QPointF originPoint = item->mapToScene(item->boundingRect().center());
|
QPointF originPoint = gitem->mapToScene(gitem->boundingRect().center());
|
||||||
double dLengthY = s_ptMouseLast.y() - originPoint.y();
|
double dLengthY = s_ptMouseLast.y() - originPoint.y();
|
||||||
double dLengthX = s_ptMouseLast.x() - originPoint.x();
|
double dLengthX = s_ptMouseLast.x() - originPoint.x();
|
||||||
double dAngleMouseToItem = atan2(dLengthY, dLengthX) * 180 / M_PI;
|
double dAngleMouseToItem = atan2(dLengthY, dLengthX) * 180 / M_PI;
|
||||||
// if(atan2(dLengthY, dLengthX) < 0)
|
// if(atan2(dLengthY, dLengthX) < 0)
|
||||||
// dAngleMouseToItem += 360.0;
|
// dAngleMouseToItem += 360.0;
|
||||||
|
|
||||||
double rotationAngle = item->rotation() + (dAngleMouseToItem - s_dAngleMouseDownToItem);
|
double rotationAngle = gitem->rotation() + (dAngleMouseToItem - s_dAngleMouseDownToItem);
|
||||||
//让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断
|
//让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断
|
||||||
if (rotationAngle > 180)
|
if (rotationAngle > 180)
|
||||||
rotationAngle -= 360;
|
rotationAngle -= 360;
|
||||||
|
|
@ -53,7 +54,7 @@ void RotationSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Design
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
for(int n = 0; n < items.size(); n++)
|
for(int n = 0; n < items.size(); n++)
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
|
IShape *item = dynamic_cast<IShape *>(items.at(n));
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
item->removeOperationCopy();
|
item->removeOperationCopy();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ void ScalingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSc
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
if (items.count() == 1)
|
if (items.count() == 1)
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
|
QGraphicsItem *gitem = items.first();
|
||||||
|
IShape *item = dynamic_cast<IShape *>(gitem);
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
if(s_nDragHandle != H_none)
|
if(s_nDragHandle != H_none)
|
||||||
|
|
@ -40,8 +41,8 @@ void ScalingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSc
|
||||||
}
|
}
|
||||||
|
|
||||||
//计算缩放倍数
|
//计算缩放倍数
|
||||||
QPointF iniDelta = item->mapFromScene(s_ptMouseDown) - m_scaleBasePoint;
|
QPointF iniDelta = gitem->mapFromScene(s_ptMouseDown) - m_scaleBasePoint;
|
||||||
QPointF lastDelta = item->mapFromScene(s_ptMouseLast) - m_scaleBasePoint;
|
QPointF lastDelta = gitem->mapFromScene(s_ptMouseLast) - m_scaleBasePoint;
|
||||||
double sx = lastDelta.x() / iniDelta.x();
|
double sx = lastDelta.x() / iniDelta.x();
|
||||||
double sy = lastDelta.y() / iniDelta.y();
|
double sy = lastDelta.y() / iniDelta.y();
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ void ScalingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Designe
|
||||||
QList<QGraphicsItem *> items = scene->selectedItems();
|
QList<QGraphicsItem *> items = scene->selectedItems();
|
||||||
if (items.count() == 1)
|
if (items.count() == 1)
|
||||||
{
|
{
|
||||||
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
|
IShape *item = dynamic_cast<IShape *>(items.first());
|
||||||
if(item && s_ptMouseLast != s_ptMouseDown)
|
if(item && s_ptMouseLast != s_ptMouseDown)
|
||||||
{
|
{
|
||||||
item->updateCoordinate();
|
item->updateCoordinate();
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,19 @@
|
||||||
<string>多边形</string>
|
<string>多边形</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QPushButton" name="pushBtn_text">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>30</x>
|
||||||
|
<y>260</y>
|
||||||
|
<width>81</width>
|
||||||
|
<height>51</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>文本</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_2">
|
<widget class="QWidget" name="tab_2">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue