File indexing completed on 2024-05-12 17:08:56

0001 /*
0002     SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "resizehandle.h"
0008 
0009 #include <QCursor>
0010 #include <cmath>
0011 
0012 ResizeHandle::ResizeHandle(QQuickItem *parent)
0013     : QQuickItem(parent)
0014 {
0015     setAcceptedMouseButtons(Qt::LeftButton);
0016 
0017     QQuickItem *candidate = parent;
0018     while (candidate) {
0019         ConfigOverlay *overlay = qobject_cast<ConfigOverlay *>(candidate);
0020         if (overlay) {
0021             setConfigOverlay(overlay);
0022             break;
0023         }
0024 
0025         candidate = candidate->parentItem();
0026     }
0027 
0028     connect(this, &QQuickItem::parentChanged, this, [this]() {
0029         QQuickItem *candidate = parentItem();
0030         while (candidate) {
0031             ConfigOverlay *overlay = qobject_cast<ConfigOverlay *>(candidate);
0032             if (overlay) {
0033                 setConfigOverlay(overlay);
0034                 break;
0035             }
0036 
0037             candidate = candidate->parentItem();
0038         }
0039     });
0040 
0041     auto syncCursor = [this]() {
0042         switch (m_resizeCorner) {
0043         case Left:
0044         case Right:
0045             setCursor(QCursor(Qt::SizeHorCursor));
0046             break;
0047         case Top:
0048         case Bottom:
0049             setCursor(QCursor(Qt::SizeVerCursor));
0050             break;
0051         case TopLeft:
0052         case BottomRight:
0053             setCursor(QCursor(Qt::SizeFDiagCursor));
0054             break;
0055         case TopRight:
0056         case BottomLeft:
0057         default:
0058             setCursor(Qt::SizeBDiagCursor);
0059         }
0060     };
0061 
0062     syncCursor();
0063     connect(this, &ResizeHandle::resizeCornerChanged, this, syncCursor);
0064 }
0065 
0066 ResizeHandle::~ResizeHandle()
0067 {
0068 }
0069 
0070 bool ResizeHandle::resizeBlocked() const
0071 {
0072     return m_resizeWidthBlocked || m_resizeHeightBlocked;
0073 }
0074 
0075 void ResizeHandle::setPressed(bool pressed)
0076 {
0077     if (pressed == m_pressed) {
0078         return;
0079     }
0080 
0081     m_pressed = pressed;
0082     Q_EMIT pressedChanged();
0083 }
0084 
0085 bool ResizeHandle::isPressed() const
0086 {
0087     return m_pressed;
0088 }
0089 
0090 bool ResizeHandle::resizeLeft() const
0091 {
0092     return m_resizeCorner == Left || m_resizeCorner == TopLeft || m_resizeCorner == BottomLeft;
0093 }
0094 
0095 bool ResizeHandle::resizeTop() const
0096 {
0097     return m_resizeCorner == Top || m_resizeCorner == TopLeft || m_resizeCorner == TopRight;
0098 }
0099 
0100 bool ResizeHandle::resizeRight() const
0101 {
0102     return m_resizeCorner == Right || m_resizeCorner == TopRight || m_resizeCorner == BottomRight;
0103 }
0104 
0105 bool ResizeHandle::resizeBottom() const
0106 {
0107     return m_resizeCorner == Bottom || m_resizeCorner == BottomLeft || m_resizeCorner == BottomRight;
0108 }
0109 
0110 void ResizeHandle::setResizeBlocked(bool width, bool height)
0111 {
0112     if (m_resizeWidthBlocked == width && m_resizeHeightBlocked == height) {
0113         return;
0114     }
0115 
0116     m_resizeWidthBlocked = width;
0117     m_resizeHeightBlocked = height;
0118 
0119     Q_EMIT resizeBlockedChanged();
0120 }
0121 
0122 void ResizeHandle::mousePressEvent(QMouseEvent *event)
0123 {
0124     ItemContainer *itemContainer = m_configOverlay->itemContainer();
0125     if (!itemContainer) {
0126         return;
0127     }
0128     m_mouseDownPosition = event->windowPos();
0129     m_mouseDownGeometry = QRectF(itemContainer->x(), itemContainer->y(), itemContainer->width(), itemContainer->height());
0130     setResizeBlocked(false, false);
0131     setPressed(true);
0132     event->accept();
0133 }
0134 
0135 void ResizeHandle::mouseMoveEvent(QMouseEvent *event)
0136 {
0137     if (!m_configOverlay || !m_configOverlay->itemContainer()) {
0138         return;
0139     }
0140 
0141     ItemContainer *itemContainer = m_configOverlay->itemContainer();
0142     AppletsLayout *layout = itemContainer->layout();
0143 
0144     if (!layout) {
0145         return;
0146     }
0147 
0148     layout->releaseSpace(itemContainer);
0149     const QPointF difference = m_mouseDownPosition - event->windowPos();
0150 
0151     QSizeF minimumSize = QSize(layout->minimumItemWidth(), layout->minimumItemHeight());
0152     if (itemContainer->layoutAttached()) {
0153         minimumSize.setWidth(qMax(minimumSize.width(), itemContainer->layoutAttached()->property("minimumWidth").toReal()));
0154         minimumSize.setHeight(qMax(minimumSize.height(), itemContainer->layoutAttached()->property("minimumHeight").toReal()));
0155     }
0156 
0157     // Now make minimumSize an integer number of cells
0158     minimumSize.setWidth(ceil(minimumSize.width() / layout->cellWidth()) * layout->cellWidth());
0159     minimumSize.setHeight(ceil(minimumSize.height() / layout->cellWidth()) * layout->cellHeight());
0160 
0161     // Horizontal resize
0162     if (resizeLeft()) {
0163         const qreal width = qMax(minimumSize.width(), m_mouseDownGeometry.width() + difference.x());
0164         const qreal x = m_mouseDownGeometry.x() + (m_mouseDownGeometry.width() - width);
0165 
0166         // -1 to have a bit of margins around
0167         if (layout->isRectAvailable(x - 1, m_mouseDownGeometry.y(), width, m_mouseDownGeometry.height())) {
0168             itemContainer->setX(x);
0169             itemContainer->setWidth(width);
0170             setResizeBlocked(m_mouseDownGeometry.width() + difference.x() < minimumSize.width(), m_resizeHeightBlocked);
0171         } else {
0172             setResizeBlocked(true, m_resizeHeightBlocked);
0173         }
0174     } else if (resizeRight()) {
0175         const qreal width = qMax(minimumSize.width(), m_mouseDownGeometry.width() - difference.x());
0176 
0177         if (layout->isRectAvailable(m_mouseDownGeometry.x(), m_mouseDownGeometry.y(), width, m_mouseDownGeometry.height())) {
0178             itemContainer->setWidth(width);
0179             setResizeBlocked(m_mouseDownGeometry.width() - difference.x() < minimumSize.width(), m_resizeHeightBlocked);
0180         } else {
0181             setResizeBlocked(true, m_resizeHeightBlocked);
0182         }
0183     }
0184 
0185     // Vertical Resize
0186     if (resizeTop()) {
0187         const qreal height = qMax(minimumSize.height(), m_mouseDownGeometry.height() + difference.y());
0188         const qreal y = m_mouseDownGeometry.y() + (m_mouseDownGeometry.height() - height);
0189 
0190         // -1 to have a bit of margins around
0191         if (layout->isRectAvailable(m_mouseDownGeometry.x(), y - 1, m_mouseDownGeometry.width(), m_mouseDownGeometry.height())) {
0192             itemContainer->setY(y);
0193             itemContainer->setHeight(height);
0194             setResizeBlocked(m_resizeWidthBlocked, m_mouseDownGeometry.height() + difference.y() < minimumSize.height());
0195         } else {
0196             setResizeBlocked(m_resizeWidthBlocked, true);
0197         }
0198     } else if (resizeBottom()) {
0199         const qreal height = qMax(minimumSize.height(), m_mouseDownGeometry.height() - difference.y());
0200 
0201         if (layout->isRectAvailable(m_mouseDownGeometry.x(), m_mouseDownGeometry.y(), m_mouseDownGeometry.width(), height)) {
0202             itemContainer->setHeight(qMax(height, minimumSize.height()));
0203             setResizeBlocked(m_resizeWidthBlocked, m_mouseDownGeometry.height() - difference.y() < minimumSize.height());
0204         } else {
0205             setResizeBlocked(m_resizeWidthBlocked, true);
0206         }
0207     }
0208 
0209     event->accept();
0210 }
0211 
0212 void ResizeHandle::mouseReleaseEvent(QMouseEvent *event)
0213 {
0214     setPressed(false);
0215     if (!m_configOverlay || !m_configOverlay->itemContainer()) {
0216         return;
0217     }
0218 
0219     ItemContainer *itemContainer = m_configOverlay->itemContainer();
0220     AppletsLayout *layout = itemContainer->layout();
0221 
0222     if (!layout) {
0223         return;
0224     }
0225 
0226     layout->positionItem(itemContainer);
0227 
0228     event->accept();
0229 
0230     setResizeBlocked(false, false);
0231     Q_EMIT resizeBlockedChanged();
0232 }
0233 
0234 void ResizeHandle::mouseUngrabEvent()
0235 {
0236     setPressed(false);
0237 }
0238 
0239 void ResizeHandle::setConfigOverlay(ConfigOverlay *handle)
0240 {
0241     if (handle == m_configOverlay) {
0242         return;
0243     }
0244 
0245     m_configOverlay = handle;
0246 }
0247 
0248 #include "moc_resizehandle.cpp"