Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwidgetresizehandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include "qframe.h"
8#include "qapplication.h"
9#include "private/qwidget_p.h"
10#include "qcursor.h"
11#if QT_CONFIG(sizegrip)
12#include "qsizegrip.h"
13#endif
14#include "qevent.h"
15#include "qdebug.h"
16#include "private/qlayoutengine_p.h"
17
19
20using namespace Qt::StringLiterals;
21
22#define RANGE 4
23
25static bool resizeVerticalDirectionFixed = false;
26
27QWidgetResizeHandler::QWidgetResizeHandler(QWidget *parent, QWidget *cw)
28 : QObject(parent), widget(parent), childWidget(cw ? cw : parent),
29 fw(0), extrahei(0), buttonDown(false), active(false)
30{
31 mode = Nowhere;
32 widget->setMouseTracking(true);
33 QFrame *frame = qobject_cast<QFrame*>(widget);
34 range = frame ? frame->frameWidth() : RANGE;
35 range = qMax(RANGE, range);
36 enabled = true;
37 widget->installEventFilter(this);
38}
39
41{
42 if (b == enabled)
43 return;
44
45 enabled = b;
46 if (!enabled)
47 setMouseCursor(Nowhere);
48}
49
51{
52 return enabled;
53}
54
55bool QWidgetResizeHandler::eventFilter(QObject *o, QEvent *ee)
56{
57 if (!isEnabled()
58 || (ee->type() != QEvent::MouseButtonPress
59 && ee->type() != QEvent::MouseButtonRelease
60 && ee->type() != QEvent::MouseMove
61 && ee->type() != QEvent::KeyPress
62 && ee->type() != QEvent::ShortcutOverride)
63 )
64 return false;
65
66 Q_ASSERT(o == widget);
67 QWidget *w = widget;
68 if (QApplication::activePopupWidget()) {
69 if (buttonDown && ee->type() == QEvent::MouseButtonRelease)
70 buttonDown = false;
71 return false;
72 }
73
74 switch (ee->type()) {
75 case QEvent::MouseButtonPress: {
76 QMouseEvent *e = static_cast<QMouseEvent *>(ee);
77 if (w->isMaximized())
78 break;
79 const QRect widgetRect = widget->rect().marginsAdded(QMargins(range, range, range, range));
80 const QPoint cursorPoint = widget->mapFromGlobal(e->globalPosition().toPoint());
81 if (!widgetRect.contains(cursorPoint))
82 return false;
83 if (e->button() == Qt::LeftButton) {
84 buttonDown = false;
85 emit activate();
87 buttonDown = true;
88 moveOffset = widget->mapFromGlobal(e->globalPosition().toPoint());
89 invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
90 if (mode != Center)
91 return true;
92 }
93 } break;
94 case QEvent::MouseButtonRelease:
95 if (w->isMaximized())
96 break;
97 if (static_cast<QMouseEvent *>(ee)->button() == Qt::LeftButton) {
98 active = false;
99 buttonDown = false;
100 widget->releaseMouse();
101 widget->releaseKeyboard();
102 if (mode != Center)
103 return true;
104 }
105 break;
106 case QEvent::MouseMove: {
107 if (w->isMaximized())
108 break;
109 QMouseEvent *e = static_cast<QMouseEvent *>(ee);
110 buttonDown = buttonDown && (e->buttons() & Qt::LeftButton); // safety, state machine broken!
112 if (mode != Center)
113 return true;
114 } break;
115 case QEvent::KeyPress:
116 keyPressEvent(static_cast<QKeyEvent *>(ee));
117 break;
118 case QEvent::ShortcutOverride:
119 buttonDown &= ((QGuiApplication::mouseButtons() & Qt::LeftButton) != Qt::NoButton);
120 if (buttonDown) {
121 ee->accept();
122 return true;
123 }
124 break;
125 default:
126 break;
127 }
128
129 return false;
130}
131
133{
134 QPoint pos = widget->mapFromGlobal(e->globalPosition().toPoint());
135 if (!active && !buttonDown) {
136 if (pos.y() <= range && pos.x() <= range)
137 mode = TopLeft;
138 else if (pos.y() >= widget->height()-range && pos.x() >= widget->width()-range)
139 mode = BottomRight;
140 else if (pos.y() >= widget->height()-range && pos.x() <= range)
141 mode = BottomLeft;
142 else if (pos.y() <= range && pos.x() >= widget->width()-range)
143 mode = TopRight;
144 else if (pos.y() <= range)
145 mode = Top;
146 else if (pos.y() >= widget->height()-range)
147 mode = Bottom;
148 else if (pos.x() <= range)
149 mode = Left;
150 else if ( pos.x() >= widget->width()-range)
151 mode = Right;
152 else if (widget->rect().contains(pos))
153 mode = Center;
154 else
155 mode = Nowhere;
156
157 if (widget->isMinimized() || !isEnabled())
158 mode = Center;
159#ifndef QT_NO_CURSOR
160 setMouseCursor(mode);
161#endif
162 return;
163 }
164
165 if (mode == Center)
166 return;
167
168 if (widget->testAttribute(Qt::WA_WState_ConfigPending))
169 return;
170
171
172 QPoint globalPos = (!widget->isWindow() && widget->parentWidget()) ?
173 widget->parentWidget()->mapFromGlobal(e->globalPosition().toPoint()) : e->globalPosition().toPoint();
174 if (!widget->isWindow() && !widget->parentWidget()->rect().contains(globalPos)) {
175 if (globalPos.x() < 0)
176 globalPos.rx() = 0;
177 if (globalPos.y() < 0)
178 globalPos.ry() = 0;
179 if (globalPos.x() > widget->parentWidget()->width())
180 globalPos.rx() = widget->parentWidget()->width();
181 if (globalPos.y() > widget->parentWidget()->height())
182 globalPos.ry() = widget->parentWidget()->height();
183 }
184
185 QPoint p = globalPos + invertedMoveOffset;
186 QPoint pp = globalPos - moveOffset;
187
188 // Workaround for window managers which refuse to move a tool window partially offscreen.
189 if (QGuiApplication::platformName() == "xcb"_L1) {
190 const QRect desktop = QWidgetPrivate::availableScreenGeometry(widget);
191 pp.rx() = qMax(pp.x(), desktop.left());
192 pp.ry() = qMax(pp.y(), desktop.top());
193 p.rx() = qMin(p.x(), desktop.right());
194 p.ry() = qMin(p.y(), desktop.bottom());
195 }
196
197 QSize ms = qSmartMinSize(childWidget);
198 int mw = ms.width();
199 int mh = ms.height();
200 if (childWidget != widget) {
201 mw += 2 * fw;
202 mh += 2 * fw + extrahei;
203 }
204
205 QSize maxsize(childWidget->maximumSize());
206 if (childWidget != widget)
207 maxsize += QSize(2 * fw, 2 * fw + extrahei);
208 QSize mpsize(widget->geometry().right() - pp.x() + 1,
209 widget->geometry().bottom() - pp.y() + 1);
210 mpsize = mpsize.expandedTo(widget->minimumSize()).expandedTo(QSize(mw, mh))
211 .boundedTo(maxsize);
212 QPoint mp(widget->geometry().right() - mpsize.width() + 1,
213 widget->geometry().bottom() - mpsize.height() + 1);
214
215 QRect geom = widget->geometry();
216
217 switch (mode) {
218 case TopLeft:
219 geom = QRect(mp, widget->geometry().bottomRight()) ;
220 break;
221 case BottomRight:
222 geom = QRect(widget->geometry().topLeft(), p) ;
223 break;
224 case BottomLeft:
225 geom = QRect(QPoint(mp.x(), widget->geometry().y()), QPoint(widget->geometry().right(), p.y())) ;
226 break;
227 case TopRight:
228 geom = QRect(QPoint(widget->geometry().x(), mp.y()), QPoint(p.x(), widget->geometry().bottom())) ;
229 break;
230 case Top:
231 geom = QRect(QPoint(widget->geometry().left(), mp.y()), widget->geometry().bottomRight()) ;
232 break;
233 case Bottom:
234 geom = QRect(widget->geometry().topLeft(), QPoint(widget->geometry().right(), p.y())) ;
235 break;
236 case Left:
237 geom = QRect(QPoint(mp.x(), widget->geometry().top()), widget->geometry().bottomRight()) ;
238 break;
239 case Right:
240 geom = QRect(widget->geometry().topLeft(), QPoint(p.x(), widget->geometry().bottom())) ;
241 break;
242 default:
243 break;
244 }
245
246 geom = QRect(geom.topLeft(),
247 geom.size().expandedTo(widget->minimumSize())
248 .expandedTo(QSize(mw, mh))
249 .boundedTo(maxsize));
250
251 if (geom != widget->geometry() &&
252 (widget->isWindow() || widget->parentWidget()->rect().intersects(geom))) {
253 widget->setGeometry(geom);
254 }
255}
256
257void QWidgetResizeHandler::setMouseCursor(MousePosition m)
258{
259#ifdef QT_NO_CURSOR
260 Q_UNUSED(m);
261#else
262 QObjectList children = widget->children();
263 for (int i = 0; i < children.size(); ++i) {
264 if (QWidget *w = qobject_cast<QWidget*>(children.at(i))) {
265 if (!w->testAttribute(Qt::WA_SetCursor)) {
266 w->setCursor(Qt::ArrowCursor);
267 }
268 }
269 }
270
271 switch (m) {
272 case TopLeft:
273 case BottomRight:
274 widget->setCursor(Qt::SizeFDiagCursor);
275 break;
276 case BottomLeft:
277 case TopRight:
278 widget->setCursor(Qt::SizeBDiagCursor);
279 break;
280 case Top:
281 case Bottom:
282 widget->setCursor(Qt::SizeVerCursor);
283 break;
284 case Left:
285 case Right:
286 widget->setCursor(Qt::SizeHorCursor);
287 break;
288 default:
289 widget->setCursor(Qt::ArrowCursor);
290 break;
291 }
292#endif // QT_NO_CURSOR
293}
294
296{
297 if (!isResizing())
298 return;
299 bool is_control = e->modifiers() & Qt::ControlModifier;
300 int delta = is_control?1:8;
301 QPoint pos = QCursor::pos();
302 switch (e->key()) {
303 case Qt::Key_Left:
304 pos.rx() -= delta;
305 if (pos.x() <= QGuiApplication::primaryScreen()->virtualGeometry().left()) {
306 if (mode == TopLeft || mode == BottomLeft) {
307 moveOffset.rx() += delta;
308 invertedMoveOffset.rx() += delta;
309 } else {
310 moveOffset.rx() -= delta;
311 invertedMoveOffset.rx() -= delta;
312 }
313 }
314 if (isResizing() && !resizeHorizontalDirectionFixed) {
316 if (mode == BottomRight)
317 mode = BottomLeft;
318 else if (mode == TopRight)
319 mode = TopLeft;
320#ifndef QT_NO_CURSOR
321 setMouseCursor(mode);
322 widget->grabMouse(widget->cursor());
323#else
324 widget->grabMouse();
325#endif
326 }
327 break;
328 case Qt::Key_Right:
329 pos.rx() += delta;
330 if (pos.x() >= QGuiApplication::primaryScreen()->virtualGeometry().right()) {
331 if (mode == TopRight || mode == BottomRight) {
332 moveOffset.rx() += delta;
333 invertedMoveOffset.rx() += delta;
334 } else {
335 moveOffset.rx() -= delta;
336 invertedMoveOffset.rx() -= delta;
337 }
338 }
339 if (isResizing() && !resizeHorizontalDirectionFixed) {
341 if (mode == BottomLeft)
342 mode = BottomRight;
343 else if (mode == TopLeft)
344 mode = TopRight;
345#ifndef QT_NO_CURSOR
346 setMouseCursor(mode);
347 widget->grabMouse(widget->cursor());
348#else
349 widget->grabMouse();
350#endif
351 }
352 break;
353 case Qt::Key_Up:
354 pos.ry() -= delta;
355 if (pos.y() <= QGuiApplication::primaryScreen()->virtualGeometry().top()) {
356 if (mode == TopLeft || mode == TopRight) {
357 moveOffset.ry() += delta;
358 invertedMoveOffset.ry() += delta;
359 } else {
360 moveOffset.ry() -= delta;
361 invertedMoveOffset.ry() -= delta;
362 }
363 }
364 if (isResizing() && !resizeVerticalDirectionFixed) {
366 if (mode == BottomLeft)
367 mode = TopLeft;
368 else if (mode == BottomRight)
369 mode = TopRight;
370#ifndef QT_NO_CURSOR
371 setMouseCursor(mode);
372 widget->grabMouse(widget->cursor());
373#else
374 widget->grabMouse();
375#endif
376 }
377 break;
378 case Qt::Key_Down:
379 pos.ry() += delta;
380 if (pos.y() >= QGuiApplication::primaryScreen()->virtualGeometry().bottom()) {
381 if (mode == BottomLeft || mode == BottomRight) {
382 moveOffset.ry() += delta;
383 invertedMoveOffset.ry() += delta;
384 } else {
385 moveOffset.ry() -= delta;
386 invertedMoveOffset.ry() -= delta;
387 }
388 }
389 if (isResizing() && !resizeVerticalDirectionFixed) {
391 if (mode == TopLeft)
392 mode = BottomLeft;
393 else if (mode == TopRight)
394 mode = BottomRight;
395#ifndef QT_NO_CURSOR
396 setMouseCursor(mode);
397 widget->grabMouse(widget->cursor());
398#else
399 widget->grabMouse();
400#endif
401 }
402 break;
403 case Qt::Key_Space:
404 case Qt::Key_Return:
405 case Qt::Key_Enter:
406 case Qt::Key_Escape:
407 active = false;
408 widget->releaseMouse();
409 widget->releaseKeyboard();
410 buttonDown = false;
411 break;
412 default:
413 return;
414 }
415 QCursor::setPos(pos);
416}
417
418
420{
421 if (!enabled)
422 return;
423
424 active = true;
425 moveOffset = widget->mapFromGlobal(QCursor::pos());
426 if (moveOffset.x() < widget->width()/2) {
427 if (moveOffset.y() < widget->height()/2)
428 mode = TopLeft;
429 else
430 mode = BottomLeft;
431 } else {
432 if (moveOffset.y() < widget->height()/2)
433 mode = TopRight;
434 else
435 mode = BottomRight;
436 }
437 invertedMoveOffset = widget->rect().bottomRight() - moveOffset;
438#ifndef QT_NO_CURSOR
439 setMouseCursor(mode);
440 widget->grabMouse(widget->cursor() );
441#else
442 widget->grabMouse();
443#endif
444 widget->grabKeyboard();
447}
448
449QT_END_NAMESPACE
450
451#include "moc_qwidgetresizehandler_p.cpp"
bool eventFilter(QObject *o, QEvent *e) override
Filters events if this object has been installed as an event filter for the watched object.
void mouseMoveEvent(QMouseEvent *e)
void keyPressEvent(QKeyEvent *e)
static bool resizeHorizontalDirectionFixed
static bool resizeVerticalDirectionFixed
#define RANGE