1 /*
2 SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "resizehandle.h"
8
9 #include <QCursor>
10 #include <cmath>
11
ResizeHandle(QQuickItem * parent)12 ResizeHandle::ResizeHandle(QQuickItem *parent)
13 : QQuickItem(parent)
14 {
15 setAcceptedMouseButtons(Qt::LeftButton);
16
17 QQuickItem *candidate = parent;
18 while (candidate) {
19 ConfigOverlay *overlay = qobject_cast<ConfigOverlay *>(candidate);
20 if (overlay) {
21 setConfigOverlay(overlay);
22 break;
23 }
24
25 candidate = candidate->parentItem();
26 }
27
28 connect(this, &QQuickItem::parentChanged, this, [this]() {
29 QQuickItem *candidate = parentItem();
30 while (candidate) {
31 ConfigOverlay *overlay = qobject_cast<ConfigOverlay *>(candidate);
32 if (overlay) {
33 setConfigOverlay(overlay);
34 break;
35 }
36
37 candidate = candidate->parentItem();
38 }
39 });
40
41 auto syncCursor = [this]() {
42 switch (m_resizeCorner) {
43 case Left:
44 case Right:
45 setCursor(QCursor(Qt::SizeHorCursor));
46 break;
47 case Top:
48 case Bottom:
49 setCursor(QCursor(Qt::SizeVerCursor));
50 break;
51 case TopLeft:
52 case BottomRight:
53 setCursor(QCursor(Qt::SizeFDiagCursor));
54 break;
55 case TopRight:
56 case BottomLeft:
57 default:
58 setCursor(Qt::SizeBDiagCursor);
59 }
60 };
61
62 syncCursor();
63 connect(this, &ResizeHandle::resizeCornerChanged, this, syncCursor);
64 }
65
~ResizeHandle()66 ResizeHandle::~ResizeHandle()
67 {
68 }
69
resizeBlocked() const70 bool ResizeHandle::resizeBlocked() const
71 {
72 return m_resizeWidthBlocked || m_resizeHeightBlocked;
73 }
74
setPressed(bool pressed)75 void ResizeHandle::setPressed(bool pressed)
76 {
77 if (pressed == m_pressed) {
78 return;
79 }
80
81 m_pressed = pressed;
82 emit pressedChanged();
83 }
84
isPressed() const85 bool ResizeHandle::isPressed() const
86 {
87 return m_pressed;
88 }
89
resizeLeft() const90 bool ResizeHandle::resizeLeft() const
91 {
92 return m_resizeCorner == Left || m_resizeCorner == TopLeft || m_resizeCorner == BottomLeft;
93 }
94
resizeTop() const95 bool ResizeHandle::resizeTop() const
96 {
97 return m_resizeCorner == Top || m_resizeCorner == TopLeft || m_resizeCorner == TopRight;
98 }
99
resizeRight() const100 bool ResizeHandle::resizeRight() const
101 {
102 return m_resizeCorner == Right || m_resizeCorner == TopRight || m_resizeCorner == BottomRight;
103 }
104
resizeBottom() const105 bool ResizeHandle::resizeBottom() const
106 {
107 return m_resizeCorner == Bottom || m_resizeCorner == BottomLeft || m_resizeCorner == BottomRight;
108 }
109
setResizeBlocked(bool width,bool height)110 void ResizeHandle::setResizeBlocked(bool width, bool height)
111 {
112 if (m_resizeWidthBlocked == width && m_resizeHeightBlocked == height) {
113 return;
114 }
115
116 m_resizeWidthBlocked = width;
117 m_resizeHeightBlocked = height;
118
119 emit resizeBlockedChanged();
120 }
121
mousePressEvent(QMouseEvent * event)122 void ResizeHandle::mousePressEvent(QMouseEvent *event)
123 {
124 ItemContainer *itemContainer = m_configOverlay->itemContainer();
125 if (!itemContainer) {
126 return;
127 }
128 m_mouseDownPosition = event->windowPos();
129 m_mouseDownGeometry = QRectF(itemContainer->x(), itemContainer->y(), itemContainer->width(), itemContainer->height());
130 setResizeBlocked(false, false);
131 setPressed(true);
132 event->accept();
133 }
134
mouseMoveEvent(QMouseEvent * event)135 void ResizeHandle::mouseMoveEvent(QMouseEvent *event)
136 {
137 if (!m_configOverlay || !m_configOverlay->itemContainer()) {
138 return;
139 }
140
141 ItemContainer *itemContainer = m_configOverlay->itemContainer();
142 AppletsLayout *layout = itemContainer->layout();
143
144 if (!layout) {
145 return;
146 }
147
148 layout->releaseSpace(itemContainer);
149 const QPointF difference = m_mouseDownPosition - event->windowPos();
150
151 QSizeF minimumSize = QSize(layout->minimumItemWidth(), layout->minimumItemHeight());
152 if (itemContainer->layoutAttached()) {
153 minimumSize.setWidth(qMax(minimumSize.width(), itemContainer->layoutAttached()->property("minimumWidth").toReal()));
154 minimumSize.setHeight(qMax(minimumSize.height(), itemContainer->layoutAttached()->property("minimumHeight").toReal()));
155 }
156
157 // Now make minimumSize an integer number of cells
158 minimumSize.setWidth(ceil(minimumSize.width() / layout->cellWidth()) * layout->cellWidth());
159 minimumSize.setHeight(ceil(minimumSize.height() / layout->cellWidth()) * layout->cellHeight());
160
161 // Horizontal resize
162 if (resizeLeft()) {
163 const qreal width = qMax(minimumSize.width(), m_mouseDownGeometry.width() + difference.x());
164 const qreal x = m_mouseDownGeometry.x() + (m_mouseDownGeometry.width() - width);
165
166 // -1 to have a bit of margins around
167 if (layout->isRectAvailable(x - 1, m_mouseDownGeometry.y(), width, m_mouseDownGeometry.height())) {
168 itemContainer->setX(x);
169 itemContainer->setWidth(width);
170 setResizeBlocked(m_mouseDownGeometry.width() + difference.x() < minimumSize.width(), m_resizeHeightBlocked);
171 } else {
172 setResizeBlocked(true, m_resizeHeightBlocked);
173 }
174 } else if (resizeRight()) {
175 const qreal width = qMax(minimumSize.width(), m_mouseDownGeometry.width() - difference.x());
176
177 if (layout->isRectAvailable(m_mouseDownGeometry.x(), m_mouseDownGeometry.y(), width, m_mouseDownGeometry.height())) {
178 itemContainer->setWidth(width);
179 setResizeBlocked(m_mouseDownGeometry.width() - difference.x() < minimumSize.width(), m_resizeHeightBlocked);
180 } else {
181 setResizeBlocked(true, m_resizeHeightBlocked);
182 }
183 }
184
185 // Vertical Resize
186 if (resizeTop()) {
187 const qreal height = qMax(minimumSize.height(), m_mouseDownGeometry.height() + difference.y());
188 const qreal y = m_mouseDownGeometry.y() + (m_mouseDownGeometry.height() - height);
189
190 // -1 to have a bit of margins around
191 if (layout->isRectAvailable(m_mouseDownGeometry.x(), y - 1, m_mouseDownGeometry.width(), m_mouseDownGeometry.height())) {
192 itemContainer->setY(y);
193 itemContainer->setHeight(height);
194 setResizeBlocked(m_resizeWidthBlocked, m_mouseDownGeometry.height() + difference.y() < minimumSize.height());
195 } else {
196 setResizeBlocked(m_resizeWidthBlocked, true);
197 }
198 } else if (resizeBottom()) {
199 const qreal height = qMax(minimumSize.height(), m_mouseDownGeometry.height() - difference.y());
200
201 if (layout->isRectAvailable(m_mouseDownGeometry.x(), m_mouseDownGeometry.y(), m_mouseDownGeometry.width(), height)) {
202 itemContainer->setHeight(qMax(height, minimumSize.height()));
203 setResizeBlocked(m_resizeWidthBlocked, m_mouseDownGeometry.height() - difference.y() < minimumSize.height());
204 } else {
205 setResizeBlocked(m_resizeWidthBlocked, true);
206 }
207 }
208
209 event->accept();
210 }
211
mouseReleaseEvent(QMouseEvent * event)212 void ResizeHandle::mouseReleaseEvent(QMouseEvent *event)
213 {
214 setPressed(false);
215 if (!m_configOverlay || !m_configOverlay->itemContainer()) {
216 return;
217 }
218
219 ItemContainer *itemContainer = m_configOverlay->itemContainer();
220 AppletsLayout *layout = itemContainer->layout();
221
222 if (!layout) {
223 return;
224 }
225
226 layout->positionItem(itemContainer);
227
228 event->accept();
229
230 setResizeBlocked(false, false);
231 emit resizeBlockedChanged();
232 }
233
mouseUngrabEvent()234 void ResizeHandle::mouseUngrabEvent()
235 {
236 setPressed(false);
237 }
238
setConfigOverlay(ConfigOverlay * handle)239 void ResizeHandle::setConfigOverlay(ConfigOverlay *handle)
240 {
241 if (handle == m_configOverlay) {
242 return;
243 }
244
245 m_configOverlay = handle;
246 }
247
248 #include "moc_resizehandle.cpp"
249