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
qsizegrip.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
5#include "qsizegrip.h"
6
7#include "qapplication.h"
8#include "qevent.h"
10#include "qwindow.h"
11#include <qpa/qplatformwindow.h>
12#include "qstyle.h"
13#include "qstyleoption.h"
14#include "qlayout.h"
15#include "qdebug.h"
16
17#include <private/qwidget_p.h>
18#include "private/qapplication_p.h"
19#include <qpa/qplatformtheme.h>
20#include <QtWidgets/qabstractscrollarea.h>
21
22#include <QtCore/qpointer.h>
23
25
27{
28 while (w && !w->isWindow() && w->windowType() != Qt::SubWindow)
29 w = w->parentWidget();
30 return w;
31}
32
34{
35 Q_DECLARE_PUBLIC(QSizeGrip)
36public:
38 void init();
41 int d;
42 int dxMax;
43 int dyMax;
47
48 Qt::Corner corner() const;
49 inline bool atBottom() const
50 {
51 return m_corner == Qt::BottomRightCorner || m_corner == Qt::BottomLeftCorner;
52 }
53
54 inline bool atLeft() const
55 {
56 return m_corner == Qt::BottomLeftCorner || m_corner == Qt::TopLeftCorner;
57 }
58
60 {
61 Q_Q(QSizeGrip);
62 QWidget *w = qt_sizegrip_topLevelWidget(q);
63 if (tlw == w)
64 return;
65 if (tlw)
66 tlw->removeEventFilter(q);
67 tlw = w;
68 if (tlw)
69 tlw->installEventFilter(q);
70 }
71
72 // This slot is invoked by QLayout when the size grip is added to
73 // a layout or reparented after the tlw is shown. This re-implementation is basically
74 // the same as QWidgetPrivate::_q_showIfNotHidden except that it checks
75 // for Qt::WindowFullScreen and Qt::WindowMaximized as well.
77 {
78 Q_Q(QSizeGrip);
79 bool showSizeGrip = !isExplicitlyHidden();
81 if (tlw && showSizeGrip) {
82 Qt::WindowStates sizeGripNotVisibleState = Qt::WindowFullScreen;
83 sizeGripNotVisibleState |= Qt::WindowMaximized;
84 // Don't show the size grip if the tlw is maximized or in full screen mode.
85 showSizeGrip = !(tlw->windowState() & sizeGripNotVisibleState);
86 }
87 if (showSizeGrip)
88 q->setVisible(true);
89 }
90
92};
93
94QSizeGripPrivate::QSizeGripPrivate()
95 : dxMax(0)
96 , dyMax(0)
97 , gotMousePress(false)
98 , tlw(nullptr)
99 , m_platformSizeGrip(false)
100{
101}
102
104{
105 Q_Q(const QSizeGrip);
106 QWidget *tlw = qt_sizegrip_topLevelWidget(const_cast<QSizeGrip *>(q));
107 const QPoint sizeGripPos = q->mapTo(tlw, QPoint(0, 0));
108 bool isAtBottom = sizeGripPos.y() >= tlw->height() / 2;
109 bool isAtLeft = sizeGripPos.x() <= tlw->width() / 2;
110 if (isAtLeft)
111 return isAtBottom ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
112 else
113 return isAtBottom ? Qt::BottomRightCorner : Qt::TopRightCorner;
114}
115
116/*!
117 \class QSizeGrip
118
119 \brief The QSizeGrip class provides a resize handle for resizing top-level windows.
120
121 \ingroup mainwindow-classes
122 \ingroup basicwidgets
123 \inmodule QtWidgets
124
125 This widget works like the standard Windows resize handle. In the
126 X11 version this resize handle generally works differently from
127 the one provided by the system if the X11 window manager does not
128 support necessary modern post-ICCCM specifications.
129
130 Put this widget anywhere in a widget tree and the user can use it
131 to resize the top-level window or any widget with the Qt::SubWindow
132 flag set. Generally, this should be in the lower right-hand corner.
133
134 Note that QStatusBar already uses this widget, so if you have a
135 status bar (e.g., you are using QMainWindow), then you don't need
136 to use this widget explicitly. The same goes for QDialog, for which
137 you can just call \l {QDialog::setSizeGripEnabled()}
138 {QDialog::setSizeGripEnabled()}.
139
140 On some platforms the size grip automatically hides itself when the
141 window is shown full screen or maximised.
142
143 \note On macOS, size grips are no longer part of the human interface
144 guideline, and won't show unless used in a QMdiSubWindow. Set another
145 style on size grips that you want to be visible in main windows.
146
147 \image fusion-statusbar-sizegrip.png {Size grip at the bottom-right corner}
148 \caption A size grip widget at the bottom-right corner of a main window, shown in the
149 \l{Qt Widget Gallery}{Fusion widget style}.
150
151 The QSizeGrip class inherits QWidget and reimplements the \l
152 {QWidget::mousePressEvent()}{mousePressEvent()} and \l
153 {QWidget::mouseMoveEvent()}{mouseMoveEvent()} functions to feature
154 the resize functionality, and the \l
155 {QWidget::paintEvent()}{paintEvent()} function to render the
156 size grip widget.
157
158 \sa QStatusBar, QWidget::windowState()
159*/
160
161
162/*!
163 Constructs a resize corner as a child widget of the given \a
164 parent.
165*/
166QSizeGrip::QSizeGrip(QWidget * parent)
167 : QWidget(*new QSizeGripPrivate, parent, { })
168{
169 Q_D(QSizeGrip);
170 d->init();
171}
172
173
175{
176 Q_Q(QSizeGrip);
177 m_corner = q->isLeftToRight() ? Qt::BottomRightCorner : Qt::BottomLeftCorner;
178
179#if !defined(QT_NO_CURSOR)
180 q->setCursor(m_corner == Qt::TopLeftCorner || m_corner == Qt::BottomRightCorner
181 ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor);
182#endif
183 q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
185}
186
187
188/*!
189 Destroys this size grip.
190*/
191QSizeGrip::~QSizeGrip()
192{
193}
194
195/*!
196 \reimp
197*/
198QSize QSizeGrip::sizeHint() const
199{
200 QStyleOption opt(0);
201 opt.initFrom(this);
202 return style()->sizeFromContents(QStyle::CT_SizeGrip, &opt, QSize(13, 13), this);
203}
204
205/*!
206 Paints the resize grip.
207
208 Resize grips are usually rendered as small diagonal textured lines
209 in the lower-right corner. The paint event is passed in the \a
210 event parameter.
211*/
212void QSizeGrip::paintEvent(QPaintEvent *event)
213{
214 Q_UNUSED(event);
215 Q_D(QSizeGrip);
216 QStylePainter painter(this);
217 QStyleOptionSizeGrip opt;
218 opt.initFrom(this);
219 opt.corner = d->m_corner;
220 painter.drawControl(QStyle::CE_SizeGrip, opt);
221}
222
223/*!
224 \fn void QSizeGrip::mousePressEvent(QMouseEvent * event)
225
226 Receives the mouse press events for the widget, and primes the
227 resize operation. The mouse press event is passed in the \a event
228 parameter.
229*/
230
231static Qt::Edges edgesFromCorner(Qt::Corner corner)
232{
233 switch (corner) {
234 case Qt::TopLeftCorner: return Qt::TopEdge | Qt::LeftEdge;
235 case Qt::TopRightCorner: return Qt::TopEdge | Qt::RightEdge;
236 case Qt::BottomLeftCorner: return Qt::BottomEdge | Qt::LeftEdge;
237 case Qt::BottomRightCorner: return Qt::BottomEdge | Qt::RightEdge;
238 }
239 return Qt::Edges{};
240}
241
242static bool usePlatformSizeGrip(const QWidget *tlw)
243{
244 const QString &platformName = QGuiApplication::platformName();
245 if (platformName.contains(u"xcb")) // ### FIXME QTBUG-69716
246 return false;
247 if (tlw->testAttribute(Qt::WA_TranslucentBackground)
248 && platformName == u"windows") {
249 return false; // QTBUG-90628, flicker when using translucency
250 }
251 return true;
252}
253
254void QSizeGrip::mousePressEvent(QMouseEvent * e)
255{
256 if (e->button() != Qt::LeftButton) {
257 QWidget::mousePressEvent(e);
258 return;
259 }
260
261 Q_D(QSizeGrip);
262 QWidget *tlw = qt_sizegrip_topLevelWidget(this);
263 d->p = e->globalPosition().toPoint();
264 d->gotMousePress = true;
265 d->r = tlw->geometry();
266
267 // Does the platform provide size grip support?
268 d->m_platformSizeGrip = false;
269 if (tlw->isWindow()
270 && tlw->windowHandle()
271 && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)
272 && !tlw->testAttribute(Qt::WA_DontShowOnScreen)
273 && !tlw->hasHeightForWidth()
274 && usePlatformSizeGrip(tlw)) {
275 QPlatformWindow *platformWindow = tlw->windowHandle()->handle();
276 const Qt::Edges edges = edgesFromCorner(d->m_corner);
277 d->m_platformSizeGrip = platformWindow->startSystemResize(edges);
278 }
279
280 if (d->m_platformSizeGrip)
281 return;
282
283 // Find available desktop/workspace geometry.
284 QRect availableGeometry;
285 bool hasVerticalSizeConstraint = true;
286 bool hasHorizontalSizeConstraint = true;
287 if (tlw->isWindow()) {
288 if (QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::InteractiveResizeAcrossScreens).toBool())
289 availableGeometry = tlw->screen()->availableVirtualGeometry();
290 else
291 availableGeometry = QWidgetPrivate::availableScreenGeometry(tlw);
292 }
293 else {
294 const QWidget *tlwParent = tlw->parentWidget();
295 // Check if tlw is inside QAbstractScrollArea/QScrollArea.
296 // If that's the case tlw->parentWidget() will return the viewport
297 // and tlw->parentWidget()->parentWidget() will return the scroll area.
298#if QT_CONFIG(scrollarea)
299 QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(tlwParent->parentWidget());
300 if (scrollArea) {
301 hasHorizontalSizeConstraint = scrollArea->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff;
302 hasVerticalSizeConstraint = scrollArea->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff;
303 }
304#endif // QT_CONFIG(scrollarea)
305 availableGeometry = tlwParent->contentsRect();
306 }
307
308 // Find frame geometries, title bar height, and decoration sizes.
309 const QRect frameGeometry = tlw->frameGeometry();
310 const int titleBarHeight = qMax(tlw->geometry().y() - frameGeometry.y(), 0);
311 const int bottomDecoration = qMax(frameGeometry.height() - tlw->height() - titleBarHeight, 0);
312 const int leftRightDecoration = qMax((frameGeometry.width() - tlw->width()) / 2, 0);
313
314 // Determine dyMax depending on whether the sizegrip is at the bottom
315 // of the widget or not.
316 if (d->atBottom()) {
317 if (hasVerticalSizeConstraint)
318 d->dyMax = availableGeometry.bottom() - d->r.bottom() - bottomDecoration;
319 else
320 d->dyMax = INT_MAX;
321 } else {
322 if (hasVerticalSizeConstraint)
323 d->dyMax = availableGeometry.y() - d->r.y() + titleBarHeight;
324 else
325 d->dyMax = -INT_MAX;
326 }
327
328 // In RTL mode, the size grip is to the left; find dxMax from the desktop/workspace
329 // geometry, the size grip geometry and the width of the decoration.
330 if (d->atLeft()) {
331 if (hasHorizontalSizeConstraint)
332 d->dxMax = availableGeometry.x() - d->r.x() + leftRightDecoration;
333 else
334 d->dxMax = -INT_MAX;
335 } else {
336 if (hasHorizontalSizeConstraint)
337 d->dxMax = availableGeometry.right() - d->r.right() - leftRightDecoration;
338 else
339 d->dxMax = INT_MAX;
340 }
341}
342
343
344/*!
345 \fn void QSizeGrip::mouseMoveEvent(QMouseEvent * event)
346 Resizes the top-level widget containing this widget. The mouse
347 move event is passed in the \a event parameter.
348*/
349void QSizeGrip::mouseMoveEvent(QMouseEvent * e)
350{
351 Q_D(QSizeGrip);
352 if (e->buttons() != Qt::LeftButton || d->m_platformSizeGrip) {
353 QWidget::mouseMoveEvent(e);
354 return;
355 }
356
357 QWidget* tlw = qt_sizegrip_topLevelWidget(this);
358 if (!d->gotMousePress || tlw->testAttribute(Qt::WA_WState_ConfigPending))
359 return;
360
361 QPoint np(e->globalPosition().toPoint());
362
363 // Don't extend beyond the available geometry; bound to dyMax and dxMax.
364 QSize ns;
365 if (d->atBottom())
366 ns.rheight() = d->r.height() + qMin(np.y() - d->p.y(), d->dyMax);
367 else
368 ns.rheight() = d->r.height() - qMax(np.y() - d->p.y(), d->dyMax);
369
370 if (d->atLeft())
371 ns.rwidth() = d->r.width() - qMax(np.x() - d->p.x(), d->dxMax);
372 else
373 ns.rwidth() = d->r.width() + qMin(np.x() - d->p.x(), d->dxMax);
374
375 ns = QLayout::closestAcceptableSize(tlw, ns);
376
377 QPoint p;
378 QRect nr(p, ns);
379 if (d->atBottom()) {
380 if (d->atLeft())
381 nr.moveTopRight(d->r.topRight());
382 else
383 nr.moveTopLeft(d->r.topLeft());
384 } else {
385 if (d->atLeft())
386 nr.moveBottomRight(d->r.bottomRight());
387 else
388 nr.moveBottomLeft(d->r.bottomLeft());
389 }
390
391 tlw->setGeometry(nr);
392}
393
394/*!
395 \reimp
396*/
397void QSizeGrip::mouseReleaseEvent(QMouseEvent *mouseEvent)
398{
399 if (mouseEvent->button() == Qt::LeftButton) {
400 Q_D(QSizeGrip);
401 d->gotMousePress = false;
402 d->p = QPoint();
403 } else {
404 QWidget::mouseReleaseEvent(mouseEvent);
405 }
406}
407
408/*!
409 \reimp
410*/
411void QSizeGrip::moveEvent(QMoveEvent * /*moveEvent*/)
412{
413 Q_D(QSizeGrip);
414 // We're inside a resize operation; no update necessary.
415 if (!d->p.isNull())
416 return;
417
418 d->m_corner = d->corner();
419#if !defined(QT_NO_CURSOR)
420 setCursor(d->m_corner == Qt::TopLeftCorner || d->m_corner == Qt::BottomRightCorner
421 ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor);
422#endif
423}
424
425/*!
426 \reimp
427*/
428void QSizeGrip::showEvent(QShowEvent *showEvent)
429{
430 QWidget::showEvent(showEvent);
431}
432
433/*!
434 \reimp
435*/
436void QSizeGrip::hideEvent(QHideEvent *hideEvent)
437{
438 QWidget::hideEvent(hideEvent);
439}
440
441/*!
442 \reimp
443*/
444void QSizeGrip::setVisible(bool visible)
445{
446 QWidget::setVisible(visible);
447}
448
449/*! \reimp */
450bool QSizeGrip::eventFilter(QObject *o, QEvent *e)
451{
452 Q_D(QSizeGrip);
453 if (d->isExplicitlyHidden()
454 || e->type() != QEvent::WindowStateChange
455 || o != d->tlw) {
456 return QWidget::eventFilter(o, e);
457 }
458 Qt::WindowStates sizeGripNotVisibleState = Qt::WindowFullScreen;
459 sizeGripNotVisibleState |= Qt::WindowMaximized;
460 // Don't show the size grip if the tlw is maximized or in full screen mode.
461 setVisible(!(d->tlw->windowState() & sizeGripNotVisibleState));
462 setAttribute(Qt::WA_WState_ExplicitShowHide, false);
463 return QWidget::eventFilter(o, e);
464}
465
466/*!
467 \reimp
468*/
469bool QSizeGrip::event(QEvent *event)
470{
471 return QWidget::event(event);
472}
473
474QT_END_NAMESPACE
475
476#include "moc_qsizegrip.cpp"
void _q_showIfNotHidden()
Definition qsizegrip.cpp:76
QPointer< QWidget > tlw
Definition qsizegrip.cpp:46
bool atBottom() const
Definition qsizegrip.cpp:49
void updateTopLevelWidget()
Definition qsizegrip.cpp:59
Qt::Corner m_corner
Definition qsizegrip.cpp:44
bool atLeft() const
Definition qsizegrip.cpp:54
Qt::Corner corner() const
static Qt::Edges edgesFromCorner(Qt::Corner corner)
static bool usePlatformSizeGrip(const QWidget *tlw)
static QT_BEGIN_NAMESPACE QWidget * qt_sizegrip_topLevelWidget(QWidget *w)
Definition qsizegrip.cpp:26