File indexing completed on 2024-04-14 04:36:49

0001 /*
0002  * SPDX-FileCopyrightText: (C) 2020 Carl Schwan <carl@carlschwan.eu>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005  */
0006 
0007 #include "resizerectangle.h"
0008 
0009 #include <QDebug>
0010 #include <QSGFlatColorMaterial>
0011 #include <QSGGeometry>
0012 #include <QSGGeometryNode>
0013 
0014 ResizeRectangle::ResizeRectangle(QQuickItem *parent)
0015     : QQuickItem(parent)
0016 {
0017     setAcceptedMouseButtons(Qt::LeftButton);
0018     setFlag(ItemHasContents);
0019 }
0020 
0021 void ResizeRectangle::componentComplete()
0022 {
0023     QQuickItem::componentComplete();
0024     QQmlEngine *engine = qmlEngine(this);
0025     m_handleComponent = new QQmlComponent(engine, QUrl(QStringLiteral("qrc:/BasicResizeHandle.qml")));
0026 
0027     auto handleItem = qobject_cast<QQuickItem *>(m_handleComponent->create());
0028     qDebug() << handleItem;
0029     m_handleBottomLeft = qobject_cast<ResizeHandle *>(handleItem);
0030     m_handleBottomLeft->setParent(this);
0031     m_handleBottomLeft->setParentItem(this);
0032     m_handleBottomLeft->setResizeCorner(ResizeHandle::BottomLeft);
0033     m_handleBottomLeft->setX(m_insideX - 5);
0034     m_handleBottomLeft->setY(m_insideY + m_insideHeight - 5);
0035     m_handleBottomLeft->setRectangle(this);
0036 
0037     handleItem = qobject_cast<QQuickItem *>(m_handleComponent->create());
0038     m_handleBottomRight = qobject_cast<ResizeHandle *>(handleItem);
0039     m_handleBottomRight->setParent(this);
0040     m_handleBottomRight->setParentItem(this);
0041     m_handleBottomRight->setResizeCorner(ResizeHandle::BottomRight);
0042     m_handleBottomRight->setX(m_insideX + m_insideWidth - 5);
0043     m_handleBottomRight->setY(m_insideY + m_insideHeight - 5);
0044     m_handleBottomRight->setRectangle(this);
0045 
0046     handleItem = qobject_cast<QQuickItem *>(m_handleComponent->create());
0047     m_handleTopLeft = qobject_cast<ResizeHandle *>(handleItem);
0048     m_handleTopLeft->setParent(this);
0049     m_handleTopLeft->setParentItem(this);
0050     m_handleTopLeft->setResizeCorner(ResizeHandle::TopLeft);
0051     m_handleTopLeft->setX(m_insideX - 5);
0052     m_handleTopLeft->setY(m_insideY - 5);
0053     m_handleTopLeft->setRectangle(this);
0054 
0055     handleItem = qobject_cast<QQuickItem *>(m_handleComponent->create());
0056     m_handleTopRight = qobject_cast<ResizeHandle *>(handleItem);
0057     m_handleTopRight->setParent(this);
0058     m_handleTopRight->setParentItem(this);
0059     m_handleTopRight->setResizeCorner(ResizeHandle::TopRight);
0060     m_handleTopRight->setX(m_insideX + m_insideWidth - 5);
0061     m_handleTopRight->setY(m_insideY - 5);
0062     m_handleTopRight->setRectangle(this);
0063 }
0064 
0065 void ResizeRectangle::updateHandles()
0066 {
0067     if (isComponentComplete()) {
0068         m_handleTopRight->setX(m_insideX + m_insideWidth - 5);
0069         m_handleTopRight->setY(m_insideY - 5);
0070         m_handleTopLeft->setX(m_insideX - 5);
0071         m_handleTopLeft->setY(m_insideY - 5);
0072         m_handleBottomRight->setX(m_insideX + m_insideWidth - 5);
0073         m_handleBottomRight->setY(m_insideY + m_insideHeight - 5);
0074         m_handleBottomLeft->setX(m_insideX - 5);
0075         m_handleBottomLeft->setY(m_insideY + m_insideHeight - 5);
0076     }
0077 }
0078 
0079 qreal ResizeRectangle::insideX() const
0080 {
0081     return m_insideX;
0082 }
0083 
0084 void ResizeRectangle::setInsideX(qreal x)
0085 {
0086     x = qBound(0.0, x, this->width() - m_insideWidth);
0087     if (m_insideX == x) {
0088         return;
0089     }
0090     m_insideX = x;
0091     updateHandles();
0092     Q_EMIT insideXChanged();
0093     update();
0094 }
0095 
0096 qreal ResizeRectangle::insideY() const
0097 {
0098     return m_insideY;
0099 }
0100 
0101 void ResizeRectangle::setInsideY(qreal y)
0102 {
0103     y = qBound(0.0, y, this->height() - m_insideHeight);
0104     if (m_insideY == y) {
0105         return;
0106     }
0107     m_insideY = y;
0108     updateHandles();
0109     Q_EMIT insideYChanged();
0110     update();
0111 }
0112 
0113 qreal ResizeRectangle::insideWidth() const
0114 {
0115     return m_insideWidth;
0116 }
0117 
0118 void ResizeRectangle::setInsideWidth(qreal width)
0119 {
0120     width = qMin(width, this->width());
0121     if (m_insideWidth == width) {
0122         return;
0123     }
0124     m_insideWidth = width;
0125     updateHandles();
0126     Q_EMIT insideWidthChanged();
0127     update();
0128 }
0129 
0130 qreal ResizeRectangle::insideHeight() const
0131 {
0132     return m_insideHeight;
0133 }
0134 
0135 void ResizeRectangle::setInsideHeight(qreal height)
0136 {
0137     height = qMin(height, this->height());
0138     if (m_insideHeight == height) {
0139         return;
0140     }
0141     m_insideHeight = height;
0142     updateHandles();
0143     Q_EMIT insideHeightChanged();
0144     update();
0145 }
0146 
0147 QSGNode *ResizeRectangle::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
0148 {
0149     QSGGeometryNode *node = nullptr;
0150     QSGGeometry *geometry = nullptr;
0151 
0152     const int vertexCount = 12;
0153     const int indexCount = 8 * 3;
0154     if (!oldNode) {
0155         node = new QSGGeometryNode;
0156         geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount, indexCount);
0157         geometry->setIndexDataPattern(QSGGeometry::StaticPattern);
0158         geometry->setDrawingMode(QSGGeometry::DrawTriangles);
0159         node->setGeometry(geometry);
0160         node->setFlag(QSGNode::OwnsGeometry);
0161 
0162         QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
0163         material->setColor(QColor(0, 0, 0, 70));
0164         node->setMaterial(material);
0165         node->setFlag(QSGNode::OwnsMaterial);
0166     } else {
0167         node = static_cast<QSGGeometryNode *>(oldNode);
0168         geometry = node->geometry();
0169         geometry->allocate(vertexCount, indexCount);
0170     }
0171 
0172     QSGGeometry::Point2D *points = geometry->vertexDataAsPoint2D();
0173     points[0].set(0, 0);
0174     points[1].set(0, height());
0175     points[2].set(m_insideX, 0);
0176     points[3].set(m_insideX, height());
0177 
0178     points[4].set(m_insideX + m_insideWidth, 0);
0179     points[5].set(m_insideX + m_insideWidth, height());
0180     points[6].set(width(), 0);
0181     points[7].set(width(), height());
0182 
0183     points[8].set(m_insideX, m_insideY);
0184     points[9].set(m_insideX + m_insideWidth, m_insideY);
0185     points[10].set(m_insideX + m_insideWidth, m_insideY + m_insideHeight);
0186     points[11].set(m_insideX, m_insideY + m_insideHeight);
0187 
0188     quint16 *indices = geometry->indexDataAsUShort();
0189     // left
0190     indices[0 + 0] = 0;
0191     indices[0 + 1] = 1;
0192     indices[0 + 2] = 2;
0193 
0194     indices[3 + 0] = 3;
0195     indices[3 + 1] = 1;
0196     indices[3 + 2] = 2;
0197 
0198     // right
0199     indices[6 + 0] = 4;
0200     indices[6 + 1] = 5;
0201     indices[6 + 2] = 6;
0202 
0203     indices[9 + 0] = 7;
0204     indices[9 + 1] = 5;
0205     indices[9 + 2] = 6;
0206 
0207     // top
0208     indices[12 + 0] = 2;
0209     indices[12 + 1] = 8;
0210     indices[12 + 2] = 4;
0211 
0212     indices[15 + 0] = 9;
0213     indices[15 + 1] = 8;
0214     indices[15 + 2] = 4;
0215 
0216     // bottom
0217     indices[18 + 0] = 3;
0218     indices[18 + 1] = 11;
0219     indices[18 + 2] = 10;
0220 
0221     indices[21 + 0] = 3;
0222     indices[21 + 1] = 5;
0223     indices[21 + 2] = 10;
0224 
0225     geometry->markIndexDataDirty();
0226     geometry->markVertexDataDirty();
0227     node->markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);
0228     return node;
0229 }
0230 
0231 void ResizeRectangle::mouseReleaseEvent(QMouseEvent *event)
0232 {
0233     m_mouseClickedOnRectangle = false;
0234     event->accept();
0235 }
0236 
0237 void ResizeRectangle::mousePressEvent(QMouseEvent *event)
0238 {
0239     m_mouseDownPosition = event->pos();
0240     m_mouseDownGeometry = QPointF(m_insideX, m_insideY);
0241     if (m_mouseDownPosition.x() >= m_insideX && m_mouseDownPosition.x() <= m_insideX + m_insideWidth && m_mouseDownPosition.y() >= m_insideY
0242         && m_mouseDownPosition.y() <= m_insideY + m_insideHeight) {
0243         m_mouseClickedOnRectangle = true;
0244     }
0245     event->accept();
0246 }
0247 
0248 void ResizeRectangle::mouseMoveEvent(QMouseEvent *event)
0249 {
0250     if (m_mouseClickedOnRectangle) {
0251         const QPointF difference = m_mouseDownPosition - event->pos();
0252         const qreal x = m_mouseDownGeometry.x() - difference.x();
0253         const qreal y = m_mouseDownGeometry.y() - difference.y();
0254         setInsideX(x);
0255         setInsideY(y);
0256     }
0257 }
0258 
0259 void ResizeRectangle::mouseDoubleClickEvent(QMouseEvent *event)
0260 {
0261     Q_EMIT acceptSize();
0262     event->accept();
0263 }
0264 
0265 #include "moc_resizerectangle.cpp"