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"