diff options
Diffstat (limited to 'src/widgets/kernel')
-rw-r--r-- | src/widgets/kernel/kernel.pri | 6 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication.cpp | 84 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication_p.h | 2 | ||||
-rw-r--r-- | src/widgets/kernel/qgesture.cpp | 4 | ||||
-rw-r--r-- | src/widgets/kernel/qlayout.cpp | 27 | ||||
-rw-r--r-- | src/widgets/kernel/qlayout.h | 1 | ||||
-rw-r--r-- | src/widgets/kernel/qmacgesturerecognizer_p.h | 18 | ||||
-rw-r--r-- | src/widgets/kernel/qopenglwidget.cpp | 13 | ||||
-rw-r--r-- | src/widgets/kernel/qshortcut.cpp | 12 | ||||
-rw-r--r-- | src/widgets/kernel/qtestsupport_widgets.cpp | 113 | ||||
-rw-r--r-- | src/widgets/kernel/qtestsupport_widgets.h | 61 | ||||
-rw-r--r-- | src/widgets/kernel/qtooltip.cpp | 54 | ||||
-rw-r--r-- | src/widgets/kernel/qwhatsthis.cpp | 10 | ||||
-rw-r--r-- | src/widgets/kernel/qwhatsthis.h | 4 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 128 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.h | 1 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget_p.h | 4 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetbackingstore.cpp | 89 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetwindow.cpp | 115 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetwindow_p.h | 3 |
20 files changed, 542 insertions, 207 deletions
diff --git a/src/widgets/kernel/kernel.pri b/src/widgets/kernel/kernel.pri index 1bdcecbc81..c2f6e4ce75 100644 --- a/src/widgets/kernel/kernel.pri +++ b/src/widgets/kernel/kernel.pri @@ -35,7 +35,8 @@ HEADERS += \ kernel/qgesturemanager_p.h \ kernel/qdesktopwidget_p.h \ kernel/qwidgetwindow_p.h \ - kernel/qwindowcontainer_p.h + kernel/qwindowcontainer_p.h \ + kernel/qtestsupport_widgets.h SOURCES += \ kernel/qaction.cpp \ @@ -60,7 +61,8 @@ SOURCES += \ kernel/qdesktopwidget.cpp \ kernel/qwidgetsvariant.cpp \ kernel/qwidgetwindow.cpp \ - kernel/qwindowcontainer.cpp + kernel/qwindowcontainer.cpp \ + kernel/qtestsupport_widgets.cpp macx: { HEADERS += kernel/qmacgesturerecognizer_p.h diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index ba315d4338..581b7c9c94 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -110,6 +110,8 @@ #include <qpa/qplatformwindow.h> +#include <qtwidgets_tracepoints_p.h> + //#define ALIEN_DEBUG static void initResources() @@ -1025,17 +1027,17 @@ QString QApplication::styleSheet() const void QApplication::setStyleSheet(const QString& styleSheet) { QApplicationPrivate::styleSheet = styleSheet; - QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle*>(QApplicationPrivate::app_style); + QStyleSheetStyle *styleSheetStyle = qt_styleSheet(QApplicationPrivate::app_style); if (styleSheet.isEmpty()) { // application style sheet removed - if (!proxy) + if (!styleSheetStyle) return; // there was no stylesheet before - setStyle(proxy->base); - } else if (proxy) { // style sheet update, just repolish - proxy->repolish(qApp); + setStyle(styleSheetStyle->base); + } else if (styleSheetStyle) { // style sheet update, just repolish + styleSheetStyle->repolish(qApp); } else { // stylesheet set the first time - QStyleSheetStyle *newProxy = new QStyleSheetStyle(QApplicationPrivate::app_style); - QApplicationPrivate::app_style->setParent(newProxy); - setStyle(newProxy); + QStyleSheetStyle *newStyleSheetStyle = new QStyleSheetStyle(QApplicationPrivate::app_style); + QApplicationPrivate::app_style->setParent(newStyleSheetStyle); + setStyle(newStyleSheetStyle); } } @@ -1145,11 +1147,11 @@ void QApplication::setStyle(QStyle *style) QStyle *old = QApplicationPrivate::app_style; // save #ifndef QT_NO_STYLE_STYLESHEET - if (!QApplicationPrivate::styleSheet.isEmpty() && !qobject_cast<QStyleSheetStyle *>(style)) { + if (!QApplicationPrivate::styleSheet.isEmpty() && !qt_styleSheet(style)) { // we have a stylesheet already and a new style is being set - QStyleSheetStyle *newProxy = new QStyleSheetStyle(style); - style->setParent(newProxy); - QApplicationPrivate::app_style = newProxy; + QStyleSheetStyle *newStyleSheetStyle = new QStyleSheetStyle(style); + style->setParent(newStyleSheetStyle); + QApplicationPrivate::app_style = newStyleSheetStyle; } else #endif // QT_NO_STYLE_STYLESHEET QApplicationPrivate::app_style = style; @@ -1199,8 +1201,8 @@ void QApplication::setStyle(QStyle *style) } #ifndef QT_NO_STYLE_STYLESHEET - if (QStyleSheetStyle *oldProxy = qobject_cast<QStyleSheetStyle *>(old)) { - oldProxy->deref(); + if (QStyleSheetStyle *oldStyleSheetStyle = qt_styleSheet(old)) { + oldStyleSheetStyle->deref(); } else #endif if (old && old->parent() == qApp) { @@ -2627,7 +2629,7 @@ QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPoint bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, QWidget *nativeWidget, QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver, - bool spontaneous) + bool spontaneous, bool onlyDispatchEnterLeave) { Q_ASSERT(receiver); Q_ASSERT(event); @@ -2688,11 +2690,17 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, // We need this quard in case someone opens a modal dialog / popup. If that's the case // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver. const bool wasLeaveAfterRelease = leaveAfterRelease != 0; - bool result; - if (spontaneous) - result = QApplication::sendSpontaneousEvent(receiver, event); - else - result = QApplication::sendEvent(receiver, event); + bool result = true; + // This code is used for sending the synthetic enter/leave events for cases where it is needed + // due to other events causing the widget under the mouse to change. However in those cases + // we do not want to send the mouse event associated with this call, so this enables us to + // not send the unneeded mouse event + if (!onlyDispatchEnterLeave) { + if (spontaneous) + result = QApplication::sendSpontaneousEvent(receiver, event); + else + result = QApplication::sendEvent(receiver, event); + } if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) { @@ -2767,9 +2775,10 @@ void QApplicationPrivate::sendSyntheticEnterLeave(QWidget *widget) if (widget->data->in_destructor && qt_button_down == widget) qt_button_down = 0; - // Send enter/leave events followed by a mouse move on the entered widget. + // A mouse move is not actually sent, but we utilize the sendMouseEvent() call to send the + // enter/leave events as appropriate QMouseEvent e(QEvent::MouseMove, pos, windowPos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier); - sendMouseEvent(widgetUnderCursor, &e, widgetUnderCursor, tlw, &qt_button_down, qt_last_mouse_receiver); + sendMouseEvent(widgetUnderCursor, &e, widgetUnderCursor, tlw, &qt_button_down, qt_last_mouse_receiver, true, true); #else // !QT_NO_CURSOR Q_UNUSED(widget); #endif // QT_NO_CURSOR @@ -2947,8 +2956,10 @@ bool QApplication::notify(QObject *receiver, QEvent *e) d->checkReceiverThread(receiver); #endif - if (receiver->isWindowType()) - QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(receiver), e); + if (receiver->isWindowType()) { + if (QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(receiver), e)) + return true; // Platform plugin ate the event + } if(e->spontaneous()) { // Capture the current mouse and keyboard states. Doing so here is @@ -3288,6 +3299,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), wheel->angleDelta(), wheel->delta(), wheel->orientation(), wheel->buttons(), wheel->modifiers(), phase, wheel->source(), wheel->inverted()); + we.setTimestamp(wheel->timestamp()); bool eventAccepted; do { we.spont = spontaneous && w == receiver; @@ -3324,6 +3336,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) const QPoint &relpos = QApplicationPrivate::wheel_widget->mapFromGlobal(wheel->globalPos()); QWheelEvent we(relpos, wheel->globalPos(), wheel->pixelDelta(), wheel->angleDelta(), wheel->delta(), wheel->orientation(), wheel->buttons(), wheel->modifiers(), wheel->phase(), wheel->source()); + we.setTimestamp(wheel->timestamp()); we.spont = true; we.ignore(); d->notify_helper(QApplicationPrivate::wheel_widget, &we); @@ -3649,7 +3662,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) break; w = w->parentWidget(); } - foreach (QGesture *g, allGestures) + for (QGesture *g : qAsConst(allGestures)) gestureEvent->setAccepted(g, false); gestureEvent->m_accept = false; // to make sure we check individual gestures } else { @@ -3696,11 +3709,19 @@ bool QApplication::notify(QObject *receiver, QEvent *e) bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) { + // These tracepoints (and the whole function, actually) are very similar + // to the ones in QCoreApplicationPrivate::notify_helper; the reason for their + // duplication is because tracepoint symbols are not exported by QtCore. + // If you adjust the tracepoints here, consider adjusting QCoreApplicationPrivate too. + Q_TRACE(QApplication_notify_entry, receiver, e, e->type()); + // send to all application event filters if (threadRequiresCoreApplication() && receiver->d_func()->threadData->thread == mainThread() - && sendThroughApplicationEventFilters(receiver, e)) + && sendThroughApplicationEventFilters(receiver, e)) { + Q_TRACE(QApplication_notify_event_filtered, receiver, e, e->type()); return true; + } if (receiver->isWidgetType()) { QWidget *widget = static_cast<QWidget *>(receiver); @@ -3720,11 +3741,18 @@ bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) } // send to all receiver event filters - if (sendThroughObjectEventFilters(receiver, e)) + if (sendThroughObjectEventFilters(receiver, e)) { + Q_TRACE(QApplication_notify_event_filtered, receiver, e, e->type()); return true; + } + + Q_TRACE(QApplication_notify_before_delivery, receiver, e, e->type()); // deliver the event - bool consumed = receiver->event(e); + const bool consumed = receiver->event(e); + + Q_TRACE(QApplication_notify_after_delivery, receiver, e, e->type(), consumed); + QCoreApplicationPrivate::setEventSpontaneous(e, false); return consumed; } diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h index 488ca6cbfd..2d9468cc21 100644 --- a/src/widgets/kernel/qapplication_p.h +++ b/src/widgets/kernel/qapplication_p.h @@ -226,7 +226,7 @@ public: QWidget *buttonDown, QWidget *alienWidget); static bool sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, QWidget *native, QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver, - bool spontaneous = true); + bool spontaneous = true, bool onlyDispatchEnterLeave = false); void sendSyntheticEnterLeave(QWidget *widget); static QWindow *windowForWidget(const QWidget *widget) diff --git a/src/widgets/kernel/qgesture.cpp b/src/widgets/kernel/qgesture.cpp index 7f8bf18e90..fc715687c6 100644 --- a/src/widgets/kernel/qgesture.cpp +++ b/src/widgets/kernel/qgesture.cpp @@ -916,7 +916,7 @@ QGesture *QGestureEvent::gesture(Qt::GestureType type) const QList<QGesture *> QGestureEvent::activeGestures() const { QList<QGesture *> gestures; - foreach (QGesture *gesture, m_gestures) { + for (QGesture *gesture : m_gestures) { if (gesture->state() != Qt::GestureCanceled) gestures.append(gesture); } @@ -929,7 +929,7 @@ QList<QGesture *> QGestureEvent::activeGestures() const QList<QGesture *> QGestureEvent::canceledGestures() const { QList<QGesture *> gestures; - foreach (QGesture *gesture, m_gestures) { + for (QGesture *gesture : m_gestures) { if (gesture->state() == Qt::GestureCanceled) gestures.append(gesture); } diff --git a/src/widgets/kernel/qlayout.cpp b/src/widgets/kernel/qlayout.cpp index 9ce1c1c2d4..eac5674161 100644 --- a/src/widgets/kernel/qlayout.cpp +++ b/src/widgets/kernel/qlayout.cpp @@ -109,10 +109,11 @@ static int menuBarHeightForWidth(QWidget *menubar, int w) /*! Constructs a new top-level QLayout, with parent \a parent. - \a parent may not be 0. + \a parent may not be a \c nullptr. - There can be only one top-level layout for a widget. It is - returned by QWidget::layout(). + The layout is set directly as the top-level layout for + \a parent. There can be only one top-level layout for a + widget. It is returned by QWidget::layout(). */ QLayout::QLayout(QWidget *parent) : QObject(*new QLayoutPrivate, parent) @@ -1246,6 +1247,26 @@ int QLayout::indexOf(QWidget *widget) const } /*! + \since 5.12 + Searches for layout item \a layoutItem in this layout (not including child + layouts). + + Returns the index of \a layoutItem, or -1 if \a layoutItem is not found. +*/ +int QLayout::indexOf(QLayoutItem *layoutItem) const +{ + int i = 0; + QLayoutItem *item = itemAt(i); + while (item) { + if (item == layoutItem) + return i; + ++i; + item = itemAt(i); + } + return -1; +} + +/*! \enum QLayout::SizeConstraint The possible values are: diff --git a/src/widgets/kernel/qlayout.h b/src/widgets/kernel/qlayout.h index bcc33a0811..616f4e7164 100644 --- a/src/widgets/kernel/qlayout.h +++ b/src/widgets/kernel/qlayout.h @@ -122,6 +122,7 @@ public: virtual QLayoutItem *itemAt(int index) const = 0; virtual QLayoutItem *takeAt(int index) = 0; virtual int indexOf(QWidget *) const; + QT6_VIRTUAL int indexOf(QLayoutItem *) const; virtual int count() const = 0; bool isEmpty() const override; QSizePolicy::ControlTypes controlTypes() const override; diff --git a/src/widgets/kernel/qmacgesturerecognizer_p.h b/src/widgets/kernel/qmacgesturerecognizer_p.h index e381a6cc2f..739fc201b7 100644 --- a/src/widgets/kernel/qmacgesturerecognizer_p.h +++ b/src/widgets/kernel/qmacgesturerecognizer_p.h @@ -66,9 +66,9 @@ class QMacSwipeGestureRecognizer : public QGestureRecognizer public: QMacSwipeGestureRecognizer(); - QGesture *create(QObject *target); - QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); - void reset(QGesture *gesture); + QGesture *create(QObject *target) override; + QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event) override; + void reset(QGesture *gesture) override; }; class QMacPinchGestureRecognizer : public QGestureRecognizer @@ -76,9 +76,9 @@ class QMacPinchGestureRecognizer : public QGestureRecognizer public: QMacPinchGestureRecognizer(); - QGesture *create(QObject *target); - QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); - void reset(QGesture *gesture); + QGesture *create(QObject *target) override; + QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event) override; + void reset(QGesture *gesture) override; }; class QMacPanGestureRecognizer : public QObject, public QGestureRecognizer @@ -86,9 +86,9 @@ class QMacPanGestureRecognizer : public QObject, public QGestureRecognizer public: QMacPanGestureRecognizer(); - QGesture *create(QObject *target); - QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); - void reset(QGesture *gesture); + QGesture *create(QObject *target) override; + QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event) override; + void reset(QGesture *gesture) override; protected: void timerEvent(QTimerEvent *ev) override; private: diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index c96b6812c4..ed0fe0ed91 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -906,9 +906,19 @@ void QOpenGLWidgetPrivate::invalidateFbo() const int gl_color_attachment0 = 0x8CE0; // GL_COLOR_ATTACHMENT0 const int gl_depth_attachment = 0x8D00; // GL_DEPTH_ATTACHMENT const int gl_stencil_attachment = 0x8D20; // GL_STENCIL_ATTACHMENT +#ifdef Q_OS_WASM + // webgl does not allow separate depth and stencil attachments + // QTBUG-69913 + const int gl_depth_stencil_attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT + + const GLenum attachments[] = { + gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment, gl_depth_stencil_attachment + }; +#else const GLenum attachments[] = { gl_color_attachment0, gl_depth_attachment, gl_stencil_attachment }; +#endif f->glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof attachments / sizeof *attachments, attachments); } else { f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -938,7 +948,8 @@ QImage QOpenGLWidgetPrivate::grabFramebuffer() q->makeCurrent(); } - QImage res = qt_gl_read_framebuffer(q->size() * q->devicePixelRatioF(), false, false); + const bool hasAlpha = q->format().hasAlpha(); + QImage res = qt_gl_read_framebuffer(q->size() * q->devicePixelRatioF(), hasAlpha, hasAlpha); res.setDevicePixelRatio(q->devicePixelRatioF()); // While we give no guarantees of what is going to be left bound, prefer the diff --git a/src/widgets/kernel/qshortcut.cpp b/src/widgets/kernel/qshortcut.cpp index fde039c75e..a680ff7913 100644 --- a/src/widgets/kernel/qshortcut.cpp +++ b/src/widgets/kernel/qshortcut.cpp @@ -149,8 +149,16 @@ static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidge bool visible = w->isVisible(); #if QT_CONFIG(menubar) if (QMenuBar *menuBar = qobject_cast<QMenuBar *>(w)) { - if (menuBar->isNativeMenuBar()) - visible = true; + if (auto *pmb = menuBar->platformMenuBar()) { + if (menuBar->parentWidget()) { + visible = true; + } else { + if (auto *ww = qobject_cast<QWidgetWindow *>(pmb->parentWindow())) + w = ww->widget(); // Good enough since we only care about the window + else + return false; // This is not a QWidget window. We won't deliver + } + } } #endif diff --git a/src/widgets/kernel/qtestsupport_widgets.cpp b/src/widgets/kernel/qtestsupport_widgets.cpp new file mode 100644 index 0000000000..b227e6ff5d --- /dev/null +++ b/src/widgets/kernel/qtestsupport_widgets.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestsupport_widgets.h" + +#include "qwidget.h" + +#include <QtGui/qwindow.h> +#include <QtGui/qtestsupport_gui.h> + +QT_BEGIN_NAMESPACE + +/*! \fn bool qWaitForWindowActive(QWidget *widget, int timeout) + \relates QTest + \since 5.0 + + Waits for \a timeout milliseconds or until the \a widget's window is active. + + Returns \c true if \c widget's window is active within \a timeout milliseconds, otherwise returns \c false. + + \sa QTest::qWaitForWindowExposed(), QWidget::isActiveWindow() +*/ +Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowActive(QWidget *widget, int timeout) +{ + if (QWindow *window = widget->window()->windowHandle()) + return QTest::qWaitForWindowActive(window, timeout); + return false; +} + +/*! \fn bool qWaitForWindowExposed(QWidget *widget, int timeout) + \relates QTest + \since 5.0 + + Waits for \a timeout milliseconds or until the \a widget's window is exposed. + Returns \c true if \c widget's window is exposed within \a timeout milliseconds, otherwise returns \c false. + + This is mainly useful for asynchronous systems like X11, where a window will be mapped to screen some + time after being asked to show itself on the screen. + + Note that a window that is mapped to screen may still not be considered exposed if the window client + area is completely covered by other windows, or if the window is otherwise not visible. This function + will then time out when waiting for such a window. + + A specific configuration where this happens is when using QGLWidget as a viewport widget on macOS: + The viewport widget gets the expose event, not the parent widget. + + \sa QTest::qWaitForWindowActive() +*/ +Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool QTest::qWaitForWindowExposed(QWidget *widget, int timeout) +{ + if (QWindow *window = widget->window()->windowHandle()) + return QTest::qWaitForWindowExposed(window, timeout); + return false; +} + +/*! \fn bool qWaitForWindowShown(QWidget *widget, int timeout) + \relates QTest + \since 5.0 + \deprecated + + Waits for \a timeout milliseconds or until the \a widget's window is exposed. + Returns \c true if \c widget's window is exposed within \a timeout milliseconds, otherwise returns \c false. + + This function does the same as qWaitForWindowExposed(). + + Example: + + \code + QWidget widget; + widget.show(); + QTest::qWaitForWindowShown(&widget); + \endcode + + \sa QTest::qWaitForWindowActive(), QTest::qWaitForWindowExposed() +*/ + +QT_END_NAMESPACE diff --git a/src/widgets/kernel/qtestsupport_widgets.h b/src/widgets/kernel/qtestsupport_widgets.h new file mode 100644 index 0000000000..ca1406b0b2 --- /dev/null +++ b/src/widgets/kernel/qtestsupport_widgets.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTSUPPORT_WIDGETS_H +#define QTESTSUPPORT_WIDGETS_H + +#include "qtwidgetsglobal.h" + +QT_BEGIN_NAMESPACE + +class QWidget; + +namespace QTest { +Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool qWaitForWindowActive(QWidget *widget, int timeout = 5000); +Q_WIDGETS_EXPORT Q_REQUIRED_RESULT bool qWaitForWindowExposed(QWidget *widget, int timeout = 5000); + +#if QT_DEPRECATED_SINCE(5, 0) +QT_DEPRECATED Q_REQUIRED_RESULT inline static bool qWaitForWindowShown(QWidget *widget, int timeout = 5000) +{ return QTest::qWaitForWindowExposed(widget, timeout); } +#endif +} + +QT_END_NAMESPACE + +#endif diff --git a/src/widgets/kernel/qtooltip.cpp b/src/widgets/kernel/qtooltip.cpp index ed7184302a..8c5573d3a3 100644 --- a/src/widgets/kernel/qtooltip.cpp +++ b/src/widgets/kernel/qtooltip.cpp @@ -123,11 +123,11 @@ class QTipLabel : public QLabel { Q_OBJECT public: - QTipLabel(const QString &text, QWidget *w, int msecDisplayTime); + QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime); ~QTipLabel(); static QTipLabel *instance; - void updateSize(); + void updateSize(const QPoint &pos); bool eventFilter(QObject *, QEvent *) override; @@ -135,7 +135,7 @@ public: bool fadingOut; - void reuseTip(const QString &text, int msecDisplayTime); + void reuseTip(const QString &text, int msecDisplayTime, const QPoint &pos); void hideTip(); void hideTipImmediately(); void setTipRect(QWidget *w, const QRect &r); @@ -171,7 +171,7 @@ private: QTipLabel *QTipLabel::instance = 0; -QTipLabel::QTipLabel(const QString &text, QWidget *w, int msecDisplayTime) +QTipLabel::QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime) #ifndef QT_NO_STYLE_STYLESHEET : QLabel(w, Qt::ToolTip | Qt::BypassGraphicsProxyWidget), styleSheetParent(0), widget(0) #else @@ -192,7 +192,7 @@ QTipLabel::QTipLabel(const QString &text, QWidget *w, int msecDisplayTime) setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0); setMouseTracking(true); fadingOut = false; - reuseTip(text, msecDisplayTime); + reuseTip(text, msecDisplayTime, pos); } void QTipLabel::restartExpireTimer(int msecDisplayTime) @@ -204,7 +204,7 @@ void QTipLabel::restartExpireTimer(int msecDisplayTime) hideTimer.stop(); } -void QTipLabel::reuseTip(const QString &text, int msecDisplayTime) +void QTipLabel::reuseTip(const QString &text, int msecDisplayTime, const QPoint &pos) { #ifndef QT_NO_STYLE_STYLESHEET if (styleSheetParent){ @@ -214,20 +214,30 @@ void QTipLabel::reuseTip(const QString &text, int msecDisplayTime) } #endif - setWordWrap(Qt::mightBeRichText(text)); + setWordWrap(true); setText(text); - updateSize(); + updateSize(pos); restartExpireTimer(msecDisplayTime); } -void QTipLabel::updateSize() +void QTipLabel::updateSize(const QPoint &pos) { QFontMetrics fm(font()); QSize extra(1, 0); // Make it look good with the default ToolTip font on Mac, which has a small descent. if (fm.descent() == 2 && fm.ascent() >= 11) ++extra.rheight(); - resize(sizeHint() + extra); + QSize sh = sizeHint(); + if (wordWrap()) { + const QRect screenRect = QGuiApplication::screenAt(pos)->geometry(); + if (sh.width() > screenRect.width()) { + // Try to use widely accepted 75chars max length or 80% of the screen width else. + // See https://en.wikipedia.org/wiki/Line_length + sh.setWidth(qMin(fm.averageCharWidth() * 75, static_cast<int>(screenRect.width() * .8))); + sh.setHeight(heightForWidth(sh.width())); + } + } + resize(sh + extra); } void QTipLabel::paintEvent(QPaintEvent *ev) @@ -254,13 +264,13 @@ void QTipLabel::resizeEvent(QResizeEvent *e) void QTipLabel::mouseMoveEvent(QMouseEvent *e) { - if (rect.isNull()) - return; - QPoint pos = e->globalPos(); - if (widget) - pos = widget->mapFromGlobal(pos); - if (!rect.contains(pos)) - hideTip(); + if (!rect.isNull()) { + QPoint pos = e->globalPos(); + if (widget) + pos = widget->mapFromGlobal(pos); + if (!rect.contains(pos)) + hideTip(); + } QLabel::mouseMoveEvent(e); } @@ -381,7 +391,7 @@ int QTipLabel::getTipScreen(const QPoint &pos, QWidget *w) void QTipLabel::placeTip(const QPoint &pos, QWidget *w) { #ifndef QT_NO_STYLE_STYLESHEET - if (testAttribute(Qt::WA_StyleSheet) || (w && qobject_cast<QStyleSheetStyle *>(w->style()))) { + if (testAttribute(Qt::WA_StyleSheet) || (w && qt_styleSheet(w->style()))) { //the stylesheet need to know the real parent QTipLabel::instance->setProperty("_q_stylesheet_parent", QVariant::fromValue(w)); //we force the style to be the QStyleSheetStyle, and force to clear the cache as well. @@ -394,7 +404,7 @@ void QTipLabel::placeTip(const QPoint &pos, QWidget *w) QTipLabel::instance, SLOT(styleSheetParentDestroyed())); // QTBUG-64550: A font inherited by the style sheet might change the size, // particular on Windows, where the tip is not parented on a window. - QTipLabel::instance->updateSize(); + QTipLabel::instance->updateSize(pos); } } #endif //QT_NO_STYLE_STYLESHEET @@ -497,7 +507,7 @@ void QToolTip::showText(const QPoint &pos, const QString &text, QWidget *w, cons if (w) localPos = w->mapFromGlobal(pos); if (QTipLabel::instance->tipChanged(localPos, text, w)){ - QTipLabel::instance->reuseTip(text, msecDisplayTime); + QTipLabel::instance->reuseTip(text, msecDisplayTime, pos); QTipLabel::instance->setTipRect(w, rect); QTipLabel::instance->placeTip(pos, w); } @@ -511,10 +521,10 @@ void QToolTip::showText(const QPoint &pos, const QString &text, QWidget *w, cons // raised when the tooltip will be shown QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED - new QTipLabel(text, QApplication::desktop()->screen(QTipLabel::getTipScreen(pos, w)), msecDisplayTime); + new QTipLabel(text, pos, QApplication::desktop()->screen(QTipLabel::getTipScreen(pos, w)), msecDisplayTime); QT_WARNING_POP #else - new QTipLabel(text, w, msecDisplayTime); // sets QTipLabel::instance to itself + new QTipLabel(text, pos, w, msecDisplayTime); // sets QTipLabel::instance to itself #endif QTipLabel::instance->setTipRect(w, rect); QTipLabel::instance->placeTip(pos, w); diff --git a/src/widgets/kernel/qwhatsthis.cpp b/src/widgets/kernel/qwhatsthis.cpp index 96d0cf61c4..1fa83d3238 100644 --- a/src/widgets/kernel/qwhatsthis.cpp +++ b/src/widgets/kernel/qwhatsthis.cpp @@ -48,7 +48,9 @@ #include "qscreen.h" #include "qpainter.h" #include "qtimer.h" +#if QT_CONFIG(action) #include "qaction.h" +#endif // QT_CONFIG(action) #include "qcursor.h" #include "qbitmap.h" #include "qtextdocument.h" @@ -366,7 +368,9 @@ class QWhatsThisPrivate : public QObject ~QWhatsThisPrivate(); static QWhatsThisPrivate *instance; bool eventFilter(QObject *, QEvent *) override; +#if QT_CONFIG(action) QPointer<QAction> action; +#endif // QT_CONFIG(action) static void say(QWidget *, const QString &, int x = 0, int y = 0); static void notifyToplevels(QEvent *e); bool leaveOnMouseRelease; @@ -408,8 +412,10 @@ QWhatsThisPrivate::QWhatsThisPrivate() QWhatsThisPrivate::~QWhatsThisPrivate() { +#if QT_CONFIG(action) if (action) action->setChecked(false); +#endif // QT_CONFIG(action) #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif @@ -485,6 +491,7 @@ bool QWhatsThisPrivate::eventFilter(QObject *o, QEvent *e) return true; } +#if QT_CONFIG(action) class QWhatsThisAction: public QAction { Q_OBJECT @@ -516,6 +523,7 @@ void QWhatsThisAction::actionTriggered() QWhatsThisPrivate::instance->action = this; } } +#endif // QT_CONFIG(action) /*! This function switches the user interface into "What's This?" @@ -672,10 +680,12 @@ void QWhatsThis::hideText() The returned QAction provides a convenient way to let users enter "What's This?" mode. */ +#if QT_CONFIG(action) QAction *QWhatsThis::createAction(QObject *parent) { return new QWhatsThisAction(parent); } +#endif // QT_CONFIG(action) QT_END_NAMESPACE diff --git a/src/widgets/kernel/qwhatsthis.h b/src/widgets/kernel/qwhatsthis.h index 1f0f82b2a2..59c0b01c9b 100644 --- a/src/widgets/kernel/qwhatsthis.h +++ b/src/widgets/kernel/qwhatsthis.h @@ -48,7 +48,9 @@ QT_REQUIRE_CONFIG(whatsthis); QT_BEGIN_NAMESPACE +#if QT_CONFIG(action) class QAction; +#endif // QT_CONFIG(action) class Q_WIDGETS_EXPORT QWhatsThis { @@ -62,7 +64,9 @@ public: static void showText(const QPoint &pos, const QString &text, QWidget *w = nullptr); static void hideText(); +#if QT_CONFIG(action) static QAction *createAction(QObject *parent = nullptr); +#endif // QT_CONFIG(action) }; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 1e249dc191..4adccceebb 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1138,7 +1138,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) q->data = &data; -#ifndef QT_NO_THREAD +#if QT_CONFIG(thread) if (!parent) { Q_ASSERT_X(q->thread() == qApp->thread(), "QWidget", "Widgets must be created in the GUI thread."); @@ -1849,7 +1849,7 @@ void QWidgetPrivate::deleteExtra() deleteSysExtra(); #ifndef QT_NO_STYLE_STYLESHEET // dereference the stylesheet style - if (QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(extra->style)) + if (QStyleSheetStyle *proxy = qt_styleSheet(extra->style)) proxy->deref(); #endif if (extra->topextra) { @@ -1905,19 +1905,21 @@ void QWidgetPrivate::deleteTLSysExtra() } /* - Returns \c true if there are widgets above this which overlap with + Returns \c region of widgets above this which overlap with \a rect, which is in parent's coordinate system (same as crect). */ -bool QWidgetPrivate::isOverlapped(const QRect &rect) const +QRegion QWidgetPrivate::overlappedRegion(const QRect &rect, bool breakAfterFirst) const { Q_Q(const QWidget); const QWidget *w = q; QRect r = rect; + QPoint p; + QRegion region; while (w) { if (w->isWindow()) - return false; + break; QWidgetPrivate *pd = w->parentWidget()->d_func(); bool above = false; for (int i = 0; i < pd->children.size(); ++i) { @@ -1929,19 +1931,23 @@ bool QWidgetPrivate::isOverlapped(const QRect &rect) const continue; } - if (qRectIntersects(sibling->d_func()->effectiveRectFor(sibling->data->crect), r)) { + const QRect siblingRect = sibling->d_func()->effectiveRectFor(sibling->data->crect); + if (qRectIntersects(siblingRect, r)) { const QWExtra *siblingExtra = sibling->d_func()->extra; if (siblingExtra && siblingExtra->hasMask && !sibling->d_func()->graphicsEffect && !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) { continue; } - return true; + region += siblingRect.translated(-p); + if (breakAfterFirst) + break; } } w = w->parentWidget(); r.translate(pd->data.crect.topLeft()); + p += pd->data.crect.topLeft(); } - return false; + return region; } void QWidgetPrivate::syncBackingStore() @@ -2398,7 +2404,8 @@ static inline void fillRegion(QPainter *painter, const QRegion &rgn, const QBrus #endif } else if (brush.gradient() - && brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) { + && (brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode + || brush.gradient()->coordinateMode() == QGradient::ObjectMode)) { painter->save(); painter->setClipRegion(rgn); painter->fillRect(0, 0, painter->device()->width(), painter->device()->height(), brush); @@ -2659,7 +2666,7 @@ void QWidget::setStyleSheet(const QString& styleSheet) return; d->createExtra(); - QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(d->extra->style); + QStyleSheetStyle *proxy = qt_styleSheet(d->extra->style); d->extra->styleSheet = styleSheet; if (styleSheet.isEmpty()) { // stylesheet removed if (!proxy) @@ -2724,12 +2731,12 @@ void QWidget::setStyle(QStyle *style) setAttribute(Qt::WA_SetStyle, style != 0); d->createExtra(); #ifndef QT_NO_STYLE_STYLESHEET - if (QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(style)) { + if (QStyleSheetStyle *styleSheetStyle = qt_styleSheet(style)) { //if for some reason someone try to set a QStyleSheetStyle, ref it //(this may happen for exemple in QButtonDialogBox which propagates its style) - proxy->ref(); + styleSheetStyle->ref(); d->setStyle_helper(style, false); - } else if (qobject_cast<QStyleSheetStyle *>(d->extra->style) || !qApp->styleSheet().isEmpty()) { + } else if (qt_styleSheet(d->extra->style) || !qApp->styleSheet().isEmpty()) { // if we have an application stylesheet or have a proxy already, propagate d->setStyle_helper(new QStyleSheetStyle(style), true); } else @@ -2737,48 +2744,22 @@ void QWidget::setStyle(QStyle *style) d->setStyle_helper(style, false); } -void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate, bool -#if 0 // Used to be included in Qt4 for Q_WS_MAC - metalHack -#endif - ) +void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate) { Q_Q(QWidget); - QStyle *oldStyle = q->style(); -#ifndef QT_NO_STYLE_STYLESHEET - QPointer<QStyle> origStyle; -#endif + QStyle *oldStyle = q->style(); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // the metalhack boolean allows Qt/Mac to do a proper re-polish depending - // on how the Qt::WA_MacBrushedMetal attribute is set. It is only ever - // set when changing that attribute and passes the widget's CURRENT style. - // therefore no need to do a reassignment. - if (!metalHack) -#endif - { - createExtra(); + createExtra(); #ifndef QT_NO_STYLE_STYLESHEET - origStyle = extra->style.data(); + QPointer<QStyle> origStyle = extra->style; #endif - extra->style = newStyle; - } + extra->style = newStyle; // repolish - if (q->windowType() != Qt::Desktop) { - if (polished) { - oldStyle->unpolish(q); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (metalHack) - macUpdateMetalAttribute(); -#endif - q->style()->polish(q); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - } else if (metalHack) { - macUpdateMetalAttribute(); -#endif - } + if (polished && q->windowType() != Qt::Desktop) { + oldStyle->unpolish(q); + q->style()->polish(q); } if (propagate) { @@ -2792,8 +2773,8 @@ void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate, bool } #ifndef QT_NO_STYLE_STYLESHEET - if (!qobject_cast<QStyleSheetStyle*>(newStyle)) { - if (const QStyleSheetStyle* cssStyle = qobject_cast<QStyleSheetStyle*>(origStyle.data())) { + if (!qt_styleSheet(newStyle)) { + if (const QStyleSheetStyle* cssStyle = qt_styleSheet(origStyle)) { cssStyle->clearWidgetFont(q); } } @@ -2804,7 +2785,7 @@ void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate, bool #ifndef QT_NO_STYLE_STYLESHEET // dereference the old stylesheet style - if (QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle *>(origStyle.data())) + if (QStyleSheetStyle *proxy = qt_styleSheet(origStyle)) proxy->deref(); #endif } @@ -2815,7 +2796,9 @@ void QWidgetPrivate::inheritStyle() #ifndef QT_NO_STYLE_STYLESHEET Q_Q(QWidget); - QStyleSheetStyle *proxy = extra ? qobject_cast<QStyleSheetStyle *>(extra->style) : 0; + QStyle *extraStyle = extra ? (QStyle*)extra->style : nullptr; + + QStyleSheetStyle *proxy = qt_styleSheet(extraStyle); if (!q->styleSheet().isEmpty()) { Q_ASSERT(proxy); @@ -2823,16 +2806,16 @@ void QWidgetPrivate::inheritStyle() return; } - QStyle *origStyle = proxy ? proxy->base : (extra ? (QStyle*)extra->style : 0); + QStyle *origStyle = proxy ? proxy->base : extraStyle; QWidget *parent = q->parentWidget(); QStyle *parentStyle = (parent && parent->d_func()->extra) ? (QStyle*)parent->d_func()->extra->style : 0; // If we have stylesheet on app or parent has stylesheet style, we need // to be running a proxy - if (!qApp->styleSheet().isEmpty() || qobject_cast<QStyleSheetStyle *>(parentStyle)) { + if (!qApp->styleSheet().isEmpty() || qt_styleSheet(parentStyle)) { QStyle *newStyle = parentStyle; if (q->testAttribute(Qt::WA_SetStyle)) newStyle = new QStyleSheetStyle(origStyle); - else if (QStyleSheetStyle *newProxy = qobject_cast<QStyleSheetStyle *>(parentStyle)) + else if (QStyleSheetStyle *newProxy = qt_styleSheet(parentStyle)) newProxy->ref(); setStyle_helper(newStyle, true); @@ -2841,7 +2824,7 @@ void QWidgetPrivate::inheritStyle() // So, we have no stylesheet on parent/app and we have an empty stylesheet // we just need our original style back - if (origStyle == (extra ? (QStyle*)extra->style : 0)) // is it any different? + if (origStyle == extraStyle) // is it any different? return; // We could have inherited the proxy from our parent (which has a custom style) @@ -4690,9 +4673,8 @@ void QWidget::setFont(const QFont &font) #ifndef QT_NO_STYLE_STYLESHEET const QStyleSheetStyle* style; - if (d->extra && (style = qobject_cast<const QStyleSheetStyle*>(d->extra->style))) { + if (d->extra && (style = qt_styleSheet(d->extra->style))) style->saveWidgetFont(this, font); - } #endif setAttribute(Qt::WA_SetFont, font.resolve() != 0); @@ -4788,7 +4770,7 @@ void QWidgetPrivate::updateFont(const QFont &font) Q_Q(QWidget); #ifndef QT_NO_STYLE_STYLESHEET const QStyleSheetStyle* cssStyle; - cssStyle = extra ? qobject_cast<const QStyleSheetStyle*>(extra->style) : 0; + cssStyle = extra ? qt_styleSheet(extra->style) : 0; const bool useStyleSheetPropagationInWidgetStyles = QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); #endif @@ -6981,8 +6963,10 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second) determineLastFocusChild(second, lastFocusChildOfSecond); // If the tab order is already correct, exit early - if (lastFocusChildOfFirst->d_func()->focus_next == second) + if (lastFocusChildOfFirst == second || + lastFocusChildOfFirst->d_func()->focus_next == second) { return; + } // Note that we need to handle two different sections in the tab chain; The section // that 'first' belongs to (firstSection), where we are about to insert 'second', and @@ -7381,7 +7365,8 @@ QByteArray QWidget::saveGeometry() const // Version history: // - Qt 4.2 - 4.8.6, 5.0 - 5.3 : Version 1.0 // - Qt 4.8.6 - today, 5.4 - today: Version 2.0, save screen width in addition to check for high DPI scaling. - quint16 majorVersion = 2; + // - Qt 5.12 - today : Version 3.0, save QWidget::geometry() + quint16 majorVersion = 3; quint16 minorVersion = 0; const int screenNumber = QDesktopWidgetPrivate::screenNumber(this); stream << magicNumber @@ -7397,7 +7382,8 @@ QByteArray QWidget::saveGeometry() const << qint32(screenNumber) << quint8(windowState() & Qt::WindowMaximized) << quint8(windowState() & Qt::WindowFullScreen) - << qint32(QDesktopWidgetPrivate::screenGeometry(screenNumber).width()); // 1.1 onwards + << qint32(QDesktopWidgetPrivate::screenGeometry(screenNumber).width()) // added in 2.0 + << geometry(); // added in 3.0 return array; } @@ -7437,7 +7423,7 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) if (storedMagicNumber != magicNumber) return false; - const quint16 currentMajorVersion = 2; + const quint16 currentMajorVersion = 3; quint16 majorVersion = 0; quint16 minorVersion = 0; @@ -7448,7 +7434,8 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) // (Allow all minor versions.) QRect restoredFrameGeometry; - QRect restoredNormalGeometry; + QRect restoredGeometry; + QRect restoredNormalGeometry; qint32 restoredScreenNumber; quint8 maximized; quint8 fullScreen; @@ -7462,6 +7449,10 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) if (majorVersion > 1) stream >> restoredScreenWidth; + if (majorVersion > 2) + stream >> restoredGeometry; + + // ### Qt 6 - Perhaps it makes sense to dumb down the restoreGeometry() logic, see QTBUG-69104 if (restoredScreenNumber >= QDesktopWidgetPrivate::numScreens()) restoredScreenNumber = QDesktopWidgetPrivate::primaryScreen(); @@ -7548,14 +7539,11 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) setWindowState(ws); d_func()->topData()->normalGeometry = restoredNormalGeometry; } else { - QPoint offset; -#if 0 // Used to be included in Qt4 for Q_WS_X11 - if (isFullScreen()) - offset = d_func()->topData()->fullScreenOffset; -#endif setWindowState(windowState() & ~(Qt::WindowMaximized | Qt::WindowFullScreen)); - move(restoredFrameGeometry.topLeft() + offset); - resize(restoredNormalGeometry.size()); + if (majorVersion > 2) + setGeometry(restoredGeometry); + else + setGeometry(restoredNormalGeometry); } return true; } diff --git a/src/widgets/kernel/qwidget.h b/src/widgets/kernel/qwidget.h index 9f9f167002..9d5fe89c70 100644 --- a/src/widgets/kernel/qwidget.h +++ b/src/widgets/kernel/qwidget.h @@ -714,6 +714,7 @@ private: friend class QWidgetWindow; friend class QAccessibleWidget; friend class QAccessibleTable; + friend class QAccessibleTabButton; #ifndef QT_NO_GESTURES friend class QGestureManager; friend class QWinNativePanGestureRecognizer; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index e8b550b1cd..20c4a682ed 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -395,7 +395,7 @@ public: void setLocale_helper(const QLocale &l, bool forceUpdate = false); void resolveLocale(); - void setStyle_helper(QStyle *newStyle, bool propagate, bool metalHack = false); + void setStyle_helper(QStyle *newStyle, bool propagate); void inheritStyle(); void setUpdatesEnabled_helper(bool ); @@ -453,7 +453,7 @@ public: // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). void invalidateBuffer(const QRegion &); void invalidateBuffer(const QRect &); - bool isOverlapped(const QRect&) const; + QRegion overlappedRegion(const QRect &rect, bool breakAfterFirst = false) const; void syncBackingStore(); void syncBackingStore(const QRegion ®ion); diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index 3b093283cd..53769ef9ed 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -59,6 +59,7 @@ #include <private/qgraphicseffect_p.h> #endif #include <QtGui/private/qwindow_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <qpa/qplatformbackingstore.h> @@ -759,7 +760,7 @@ void QWidgetBackingStore::updateLists(QWidget *cur) QList<QObject*> children = cur->children(); for (int i = 0; i < children.size(); ++i) { QWidget *child = qobject_cast<QWidget*>(children.at(i)); - if (!child) + if (!child || child->isWindow()) continue; updateLists(child); @@ -793,6 +794,25 @@ QWidgetBackingStore::~QWidgetBackingStore() delete dirtyOnScreenWidgets; } +static QVector<QRect> getSortedRectsToScroll(const QRegion ®ion, int dx, int dy) +{ + QVector<QRect> rects; + std::copy(region.begin(), region.end(), std::back_inserter(rects)); + if (rects.count() > 1) { + std::sort(rects.begin(), rects.end(), [=](const QRect &r1, const QRect &r2) { + if (r1.y() == r2.y()) { + if (dx > 0) + return r1.x() > r2.x(); + return r1.x() < r2.x(); + } + if (dy > 0) + return r1.y() > r2.y(); + return r1.y() < r2.y(); + }); + } + return rects; +} + //parent's coordinates; move whole rect; update parent and widget //assume the screen blt has already been done, so we don't need to refresh that part void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) @@ -820,12 +840,12 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) const QRect parentRect(rect & clipR); const bool nativeWithTextureChild = textureChildSeen && q->internalWinId(); - bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild + const bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild #if QT_CONFIG(graphicsview) // No accelerate move for proxy widgets. && !tlw->d_func()->extra->proxyWidget #endif - && !isOverlapped(sourceRect) && !isOverlapped(destRect); + ; if (!accelerateMove) { QRegion parentR(effectiveRectFor(parentRect)); @@ -841,18 +861,39 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) QWidgetBackingStore *wbs = x->backingStoreTracker.data(); QRegion childExpose(newRect & clipR); + QRegion overlappedExpose; - if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw)) - childExpose -= destRect; + if (sourceRect.isValid()) { + overlappedExpose = (overlappedRegion(sourceRect) | overlappedRegion(destRect)) & clipR; + + const qreal factor = QHighDpiScaling::factor(q->windowHandle()); + if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { + const QVector<QRect> rectsToScroll + = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); + for (QRect rect : rectsToScroll) { + if (wbs->bltRect(rect, dx, dy, pw)) { + childExpose -= rect.translated(dx, dy); + } + } + } + + childExpose -= overlappedExpose; + } if (!pw->updatesEnabled()) return; const bool childUpdatesEnabled = q->updatesEnabled(); - if (childUpdatesEnabled && !childExpose.isEmpty()) { - childExpose.translate(-data.crect.topLeft()); - wbs->markDirty(childExpose, q); - isMoved = true; + if (childUpdatesEnabled) { + if (!overlappedExpose.isEmpty()) { + overlappedExpose.translate(-data.crect.topLeft()); + invalidateBuffer(overlappedExpose); + } + if (!childExpose.isEmpty()) { + childExpose.translate(-data.crect.topLeft()); + wbs->markDirty(childExpose, q); + isMoved = true; + } } QRegion parentExpose(parentRect); @@ -888,13 +929,12 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0; - QRect scrollRect = rect & clipRect(); - bool overlapped = false; - bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent) - && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft()))); + const QRect clipR = clipRect(); + const QRect scrollRect = rect & clipR; + const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent); if (!accelerateScroll) { - if (overlapped) { + if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) { QRegion region(scrollRect); subtractOpaqueSiblings(region); invalidateBuffer(region); @@ -906,12 +946,23 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; const QRect sourceRect = destRect.translated(-dx, -dy); + const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft()))) + .translated(-data.crect.topLeft()) & clipR; QRegion childExpose(scrollRect); - if (sourceRect.isValid()) { - if (wbs->bltRect(sourceRect, dx, dy, q)) - childExpose -= destRect; + + const qreal factor = QHighDpiScaling::factor(q->windowHandle()); + if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { + const QVector<QRect> rectsToScroll + = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); + for (const QRect &rect : rectsToScroll) { + if (wbs->bltRect(rect, dx, dy, q)) { + childExpose -= rect.translated(dx, dy); + } + } } + childExpose -= overlappedExpose; + if (inDirtyList) { if (rect == q->rect()) { dirty.translate(dx, dy); @@ -928,6 +979,8 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) if (!q->updatesEnabled()) return; + if (!overlappedExpose.isEmpty()) + invalidateBuffer(overlappedExpose); if (!childExpose.isEmpty()) { wbs->markDirty(childExpose, q); isScrolled = true; @@ -991,7 +1044,7 @@ static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) } } - if (QWidgetPrivate::get(tlw)->textureChildSeen) { + if (QWidgetPrivate::get(widget)->textureChildSeen) { // No render-to-texture widgets in the (sub-)tree due to hidden or native // children. Returning null results in using the normal backingstore flush path // without OpenGL-based compositing. This is very desirable normally. However, diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 1f3057b008..1a2ac4a4dd 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -311,8 +311,10 @@ bool QWidgetWindow::event(QEvent *event) #if QT_CONFIG(draganddrop) case QEvent::DragEnter: + handleDragEnterEvent(static_cast<QDragEnterEvent *>(event)); + return true; case QEvent::DragMove: - handleDragEnterMoveEvent(static_cast<QDragMoveEvent *>(event)); + handleDragMoveEvent(static_cast<QDragMoveEvent *>(event)); return true; case QEvent::DragLeave: handleDragLeaveEvent(static_cast<QDragLeaveEvent *>(event)); @@ -841,6 +843,7 @@ void QWidgetWindow::handleWheelEvent(QWheelEvent *event) QPoint mapped = widget->mapFrom(rootWidget, pos); QWheelEvent translated(mapped, event->globalPos(), event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted()); + translated.setTimestamp(event->timestamp()); QGuiApplication::forwardEvent(widget, &translated, event); } @@ -848,62 +851,80 @@ void QWidgetWindow::handleWheelEvent(QWheelEvent *event) #if QT_CONFIG(draganddrop) -void QWidgetWindow::handleDragEnterMoveEvent(QDragMoveEvent *event) +static QWidget *findDnDTarget(QWidget *parent, const QPoint &pos) { - Q_ASSERT(event->type() ==QEvent::DragMove || !m_dragTarget); // Find a target widget under mouse that accepts drops (QTBUG-22987). - QWidget *widget = m_widget->childAt(event->pos()); + QWidget *widget = parent->childAt(pos); if (!widget) - widget = m_widget; + widget = parent; for ( ; widget && !widget->isWindow() && !widget->acceptDrops(); widget = widget->parentWidget()) ; if (widget && !widget->acceptDrops()) - widget = 0; - // Target widget unchanged: DragMove - if (widget && widget == m_dragTarget.data()) { - Q_ASSERT(event->type() == QEvent::DragMove); - const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); - QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers()); - translated.setDropAction(event->dropAction()); - if (event->isAccepted()) { // Handling 'DragEnter' should suffice for the application. - translated.accept(); - translated.setDropAction(event->dropAction()); - } - QGuiApplication::forwardEvent(widget, &translated, event); - if (translated.isAccepted()) { - event->accept(); - } else { - event->ignore(); - } - event->setDropAction(translated.dropAction()); - return; - } - // Target widget changed: Send DragLeave to previous, DragEnter to new if there is any - if (m_dragTarget.data()) { - QDragLeaveEvent le; - QGuiApplication::forwardEvent(m_dragTarget.data(), &le, event); - m_dragTarget = 0; - } + widget = nullptr; + return widget; +} + +void QWidgetWindow::handleDragEnterEvent(QDragEnterEvent *event, QWidget *widget) +{ + Q_ASSERT(m_dragTarget == nullptr); + if (!widget) + widget = findDnDTarget(m_widget, event->pos()); if (!widget) { - event->ignore(); - return; + event->ignore(); + return; } m_dragTarget = widget; + const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); - QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers()); - QGuiApplication::forwardEvent(widget, &translated, event); - if (translated.isAccepted()) { - event->accept(); - } else { + QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(), + event->mouseButtons(), event->keyboardModifiers()); + translated.setDropAction(event->dropAction()); + translated.setAccepted(event->isAccepted()); + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + event->setAccepted(translated.isAccepted()); + event->setDropAction(translated.dropAction()); +} + +void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) +{ + auto *widget = findDnDTarget(m_widget, event->pos()); + if (!widget) { event->ignore(); + if (m_dragTarget) { // Send DragLeave to previous + QDragLeaveEvent leaveEvent; + QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); + m_dragTarget = nullptr; + } + } else { + const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); + QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(), + event->mouseButtons(), event->keyboardModifiers()); + translated.setDropAction(event->dropAction()); + translated.setAccepted(event->isAccepted()); + + if (widget == m_dragTarget) { // Target widget unchanged: Send DragMove + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + } else { + if (m_dragTarget) { // Send DragLeave to previous + QDragLeaveEvent leaveEvent; + QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); + m_dragTarget = nullptr; + } + // Send DragEnter to new widget. + handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget); + // The drag enter event is always immediately followed by a drag move event, + // see QDragEnterEvent documentation. + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + } + event->setAccepted(translated.isAccepted()); + event->setDropAction(translated.dropAction()); } - event->setDropAction(translated.dropAction()); } void QWidgetWindow::handleDragLeaveEvent(QDragLeaveEvent *event) { if (m_dragTarget) - QGuiApplication::forwardEvent(m_dragTarget.data(), event); - m_dragTarget = 0; + QGuiApplication::forwardEvent(m_dragTarget, event); + m_dragTarget = nullptr; } void QWidgetWindow::handleDropEvent(QDropEvent *event) @@ -913,13 +934,12 @@ void QWidgetWindow::handleDropEvent(QDropEvent *event) event->ignore(); return; } - const QPoint mapped = m_dragTarget.data()->mapFromGlobal(m_widget->mapToGlobal(event->pos())); + const QPoint mapped = m_dragTarget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); QDropEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers()); - QGuiApplication::forwardEvent(m_dragTarget.data(), &translated, event); - if (translated.isAccepted()) - event->accept(); + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + event->setAccepted(translated.isAccepted()); event->setDropAction(translated.dropAction()); - m_dragTarget = 0; + m_dragTarget = nullptr; } #endif // QT_CONFIG(draganddrop) @@ -957,7 +977,10 @@ void QWidgetWindow::handleExposeEvent(QExposeEvent *event) } if (exposed) { + // QTBUG-39220, QTBUG-58575: set all (potentially fully obscured parent widgets) mapped. m_widget->setAttribute(Qt::WA_Mapped); + for (QWidget *p = m_widget->parentWidget(); p && !p->testAttribute(Qt::WA_Mapped); p = p->parentWidget()) + p->setAttribute(Qt::WA_Mapped); if (!event->region().isNull()) wPriv->syncBackingStore(event->region()); } else { diff --git a/src/widgets/kernel/qwidgetwindow_p.h b/src/widgets/kernel/qwidgetwindow_p.h index ead099390e..0728135467 100644 --- a/src/widgets/kernel/qwidgetwindow_p.h +++ b/src/widgets/kernel/qwidgetwindow_p.h @@ -96,7 +96,8 @@ protected: void handleWheelEvent(QWheelEvent *); #endif #if QT_CONFIG(draganddrop) - void handleDragEnterMoveEvent(QDragMoveEvent *); + void handleDragEnterEvent(QDragEnterEvent *, QWidget *widget = nullptr); + void handleDragMoveEvent(QDragMoveEvent *); void handleDragLeaveEvent(QDragLeaveEvent *); void handleDropEvent(QDropEvent *); #endif |