diff options
Diffstat (limited to 'src/widgets/kernel/qwidgetwindow.cpp')
-rw-r--r-- | src/widgets/kernel/qwidgetwindow.cpp | 528 |
1 files changed, 307 insertions, 221 deletions
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index fbc71cd0ea..03dde9ca69 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets 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$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "private/qwindow_p.h" #include "qwidgetwindow_p.h" @@ -43,10 +7,10 @@ #include "private/qwidget_p.h" #include "private/qapplication_p.h" -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) #include <QtGui/qaccessible.h> #endif -#include <private/qwidgetbackingstore_p.h> +#include <private/qwidgetrepaintmanager_p.h> #include <qpa/qwindowsysteminterface_p.h> #include <qpa/qplatformtheme.h> #include <qpa/qplatformwindow.h> @@ -55,13 +19,16 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets(); -Q_WIDGETS_EXPORT QWidget *qt_button_down = 0; // widget got last button-down +Q_WIDGETS_EXPORT QWidget *qt_button_down = nullptr; // widget got last button-down // popup control -QWidget *qt_popup_down = 0; // popup that contains the pressed widget +QWidget *qt_popup_down = nullptr; // popup that contains the pressed widget extern int openPopupCount; +bool qt_popup_down_closed = false; // qt_popup_down has been closed bool qt_replay_popup_mouse_event = false; extern bool qt_try_modal(QWidget *widget, QEvent::Type type); @@ -72,9 +39,24 @@ public: void setVisible(bool visible) override { Q_Q(QWidgetWindow); - if (QWidget *widget = q->widget()) - QWidgetPrivate::get(widget)->setVisible(visible); - else + qCDebug(lcWidgetShowHide) << "Setting visibility of" << q->widget() + << "to" << visible << "via QWidgetWindowPrivate"; + + if (QWidget *widget = q->widget()) { + // If the widget's visible state is already matching the new QWindow + // visible state we assume the widget has already synced up. + if (visible != widget->isVisible()) + QWidgetPrivate::get(widget)->setVisible(visible); + } + + // If we end up calling QWidgetPrivate::setVisible() above, we will + // in most cases recurse back into setNativeWindowVisibility() to + // update the QWindow state. But during QWidget::destroy() this is + // not the case, as Qt::WA_WState_Created has been unset by the time + // we check if we should call hide_helper(). We handle this case, as + // well as the cases where we don't call QWidgetPrivate::setVisible(), + // by syncing up the QWindow state here if needed. + if (q->isVisible() != visible) QWindowPrivate::setVisible(visible); } @@ -95,10 +77,40 @@ public: widget->focusWidget()->clearFocus(); } + void setFocusToTarget(FocusTarget target, Qt::FocusReason reason) override + { + Q_Q(QWidgetWindow); + QWidget *widget = q->widget(); + if (!widget) + return; + QWidget *newFocusWidget = nullptr; + + switch (target) { + case FocusTarget::First: + newFocusWidget = q->getFocusWidget(QWidgetWindow::FirstFocusWidget); + break; + case FocusTarget::Last: + newFocusWidget = q->getFocusWidget(QWidgetWindow::LastFocusWidget); + break; + case FocusTarget::Next: { + QWidget *focusWidget = widget->focusWidget() ? widget->focusWidget() : widget; + newFocusWidget = focusWidget->nextInFocusChain() ? focusWidget->nextInFocusChain() : focusWidget; + break; + } + case FocusTarget::Prev: { + QWidget *focusWidget = widget->focusWidget() ? widget->focusWidget() : widget; + newFocusWidget = focusWidget->previousInFocusChain() ? focusWidget->previousInFocusChain() : focusWidget; + break; + } + default: + break; + } + + if (newFocusWidget) + newFocusWidget->setFocus(reason); + } + QRectF closestAcceptableGeometry(const QRectF &rect) const override; -#if QT_CONFIG(opengl) - QOpenGLContext *shareContext() const override; -#endif void processSafeAreaMarginsChanged() override { @@ -106,6 +118,9 @@ public: if (QWidget *widget = q->widget()) QWidgetPrivate::get(widget)->updateContentsRect(); } + + bool participatesInLastWindowClosed() const override; + bool treatAsVisible() const override; }; QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const @@ -137,40 +152,48 @@ QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const return result; } -#if QT_CONFIG(opengl) -QOpenGLContext *QWidgetWindowPrivate::shareContext() const -{ - Q_Q(const QWidgetWindow); - const QWidgetPrivate *widgetPrivate = QWidgetPrivate::get(q->widget()); - return widgetPrivate->shareContext(); -} -#endif // opengl +bool q_evaluateRhiConfig(const QWidget *w, QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType); QWidgetWindow::QWidgetWindow(QWidget *widget) - : QWindow(*new QWidgetWindowPrivate(), 0) + : QWindow(*new QWidgetWindowPrivate(), nullptr) , m_widget(widget) { updateObjectName(); - // Enable QOpenGLWidget/QQuickWidget children if the platform plugin supports it, - // and the application developer has not explicitly disabled it. - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface) - && !QApplication::testAttribute(Qt::AA_ForceRasterWidgets)) { - setSurfaceType(QSurface::RasterGLSurface); + if (!QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets)) { + QSurface::SurfaceType type = QSurface::RasterSurface; + q_evaluateRhiConfig(m_widget, nullptr, &type); + setSurfaceType(type); } + connect(widget, &QObject::objectNameChanged, this, &QWidgetWindow::updateObjectName); - connect(this, SIGNAL(screenChanged(QScreen*)), this, SLOT(handleScreenChange())); + connect(this, &QWidgetWindow::screenChanged, this, &QWidgetWindow::handleScreenChange); } QWidgetWindow::~QWidgetWindow() { + if (!m_widget) + return; + + QTLWExtra *topData = QWidgetPrivate::get(m_widget)->topData(); + Q_ASSERT(topData); + + // The QPlaformBackingStore may hold a reference to the window, + // so the backingstore needs to be deleted first. + topData->repaintManager.reset(nullptr); + delete topData->backingStore; + topData->backingStore = nullptr; + topData->widgetTextures.clear(); + + // Too late to do anything beyond this point + topData->window = nullptr; } -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QAccessibleInterface *QWidgetWindow::accessibleRoot() const { if (m_widget) return QAccessible::queryAccessibleInterface(m_widget); - return 0; + return nullptr; } #endif @@ -199,6 +222,9 @@ QObject *QWidgetWindow::focusObject() const void QWidgetWindow::setNativeWindowVisibility(bool visible) { Q_D(QWidgetWindow); + qCDebug(lcWidgetShowHide) << "Setting visibility of" << this + << "to" << visible << "via QWidgetWindow::setNativeWindowVisibility"; + // Call base class setVisible() implementation to run the QWindow // visibility logic. Don't call QWidgetWindowPrivate::setVisible() // since that will recurse back into QWidget code. @@ -215,6 +241,8 @@ static inline bool shouldBePropagatedToWidget(QEvent *event) case QEvent::DynamicPropertyChange: case QEvent::ChildAdded: case QEvent::ChildRemoved: + case QEvent::Paint: + case QEvent::Close: // Propagated manually in closeEvent return false; default: return true; @@ -226,20 +254,7 @@ bool QWidgetWindow::event(QEvent *event) if (!m_widget) return QWindow::event(event); - if (m_widget->testAttribute(Qt::WA_DontShowOnScreen)) { - // \a event is uninteresting for QWidgetWindow, the event was probably - // generated before WA_DontShowOnScreen was set - if (!shouldBePropagatedToWidget(event)) - return true; - return QCoreApplication::forwardEvent(m_widget, event); - } - switch (event->type()) { - case QEvent::Close: - handleCloseEvent(static_cast<QCloseEvent *>(event)); - QWindow::event(event); - return true; - case QEvent::Enter: case QEvent::Leave: handleEnterLeaveEvent(event); @@ -251,7 +266,7 @@ bool QWidgetWindow::event(QEvent *event) handleFocusInEvent(static_cast<QFocusEvent *>(event)); Q_FALLTHROUGH(); case QEvent::FocusOut: { -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QAccessible::State state; state.active = true; QAccessibleStateChangeEvent ev(m_widget, state); @@ -360,7 +375,7 @@ bool QWidgetWindow::event(QEvent *event) #endif // QT_NO_CONTEXTMENU case QEvent::WindowBlocked: - qt_button_down = 0; + qt_button_down = nullptr; break; case QEvent::UpdateRequest: @@ -369,6 +384,10 @@ bool QWidgetWindow::event(QEvent *event) m_widget->repaint(); return true; + case QEvent::DevicePixelRatioChange: + handleDevicePixelRatioChange(); + break; + default: break; } @@ -379,27 +398,26 @@ bool QWidgetWindow::event(QEvent *event) return QWindow::event(event); } -QPointer<QWidget> qt_last_mouse_receiver = 0; +QPointer<QWidget> qt_last_mouse_receiver = nullptr; void QWidgetWindow::handleEnterLeaveEvent(QEvent *event) { -#if !defined(Q_OS_OSX) && !defined(Q_OS_IOS) // Cocoa tracks popups // Ignore all enter/leave events from QPA if we are not on the first-level context menu. // This prevents duplicated events on most platforms. Fake events will be delivered in // QWidgetWindow::handleMouseEvent(QMouseEvent *). Make an exception whether the widget // is already under mouse - let the mouse leave. if (QApplicationPrivate::inPopupMode() && m_widget != QApplication::activePopupWidget() && !m_widget->underMouse()) return; -#endif + if (event->type() == QEvent::Leave) { - QWidget *enter = 0; + QWidget *enter = nullptr; // Check from window system event queue if the next queued enter targets a window // in the same window hierarchy (e.g. enter a child of this window). If so, // remove the enter event from queue and handle both in single dispatch. QWindowSystemInterfacePrivate::EnterEvent *systemEvent = static_cast<QWindowSystemInterfacePrivate::EnterEvent *> (QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::Enter)); - const QPointF globalPosF = systemEvent ? systemEvent->globalPos : QGuiApplicationPrivate::lastCursorPosition; + const QPointF globalPosF = systemEvent ? systemEvent->globalPos : QPointF(QGuiApplicationPrivate::lastCursorPosition); if (systemEvent) { if (QWidgetWindow *enterWindow = qobject_cast<QWidgetWindow *>(systemEvent->enter)) { @@ -430,7 +448,7 @@ void QWidgetWindow::handleEnterLeaveEvent(QEvent *event) } } else { const QEnterEvent *ee = static_cast<QEnterEvent *>(event); - QWidget *child = m_widget->childAt(ee->pos()); + QWidget *child = m_widget->childAt(ee->position().toPoint()); QWidget *receiver = child ? child : m_widget.data(); QWidget *leave = nullptr; if (QApplicationPrivate::inPopupMode() && receiver == m_widget @@ -439,7 +457,7 @@ void QWidgetWindow::handleEnterLeaveEvent(QEvent *event) // action on first-level menu. leave = qt_last_mouse_receiver; } - QApplicationPrivate::dispatchEnterLeave(receiver, leave, ee->screenPos()); + QApplicationPrivate::dispatchEnterLeave(receiver, leave, ee->globalPosition()); qt_last_mouse_receiver = receiver; } } @@ -470,13 +488,13 @@ QWidget *QWidgetWindow::getFocusWidget(FocusWidgets fw) void QWidgetWindow::handleFocusInEvent(QFocusEvent *e) { - QWidget *focusWidget = 0; + QWidget *focusWidget = nullptr; if (e->reason() == Qt::BacktabFocusReason) focusWidget = getFocusWidget(LastFocusWidget); else if (e->reason() == Qt::TabFocusReason) focusWidget = getFocusWidget(FirstFocusWidget); - if (focusWidget != 0) + if (focusWidget != nullptr) focusWidget->setFocus(); } @@ -487,20 +505,17 @@ void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e) void QWidgetWindow::handleMouseEvent(QMouseEvent *event) { - static const QEvent::Type contextMenuTrigger = - QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ? - QEvent::MouseButtonRelease : QEvent::MouseButtonPress; - if (qApp->d_func()->inPopupMode()) { - QWidget *activePopupWidget = qApp->activePopupWidget(); - QPoint mapped = event->pos(); + if (QApplicationPrivate::inPopupMode()) { + QPointer<QWidget> activePopupWidget = QApplication::activePopupWidget(); + QPointF mapped = event->position(); if (activePopupWidget != m_widget) - mapped = activePopupWidget->mapFromGlobal(event->globalPos()); + mapped = activePopupWidget->mapFromGlobal(event->globalPosition()); bool releaseAfter = false; - QWidget *popupChild = activePopupWidget->childAt(mapped); + QWidget *popupChild = activePopupWidget->childAt(mapped.toPoint()); if (activePopupWidget != qt_popup_down) { - qt_button_down = 0; - qt_popup_down = 0; + qt_button_down = nullptr; + qt_popup_down = nullptr; } switch (event->type()) { @@ -508,6 +523,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonDblClick: qt_button_down = popupChild; qt_popup_down = activePopupWidget; + qt_popup_down_closed = false; break; case QEvent::MouseButtonRelease: releaseAfter = true; @@ -522,38 +538,39 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) // deliver event qt_replay_popup_mouse_event = false; QPointer<QWidget> receiver = activePopupWidget; - QPoint widgetPos = mapped; + QPointF widgetPos = mapped; if (qt_button_down) receiver = qt_button_down; else if (popupChild) receiver = popupChild; if (receiver != activePopupWidget) - widgetPos = receiver->mapFromGlobal(event->globalPos()); + widgetPos = receiver->mapFromGlobal(event->globalPosition()); -#if !defined(Q_OS_OSX) && !defined(Q_OS_IOS) // Cocoa tracks popups - const bool reallyUnderMouse = activePopupWidget->rect().contains(mapped); + const bool reallyUnderMouse = activePopupWidget->rect().contains(mapped.toPoint()); const bool underMouse = activePopupWidget->underMouse(); if (underMouse != reallyUnderMouse) { if (reallyUnderMouse) { - const QPoint receiverMapped = receiver->mapFromGlobal(event->screenPos().toPoint()); + const QPoint receiverMapped = receiver->mapFromGlobal(event->globalPosition().toPoint()); // Prevent negative mouse position on enter event - this event // should be properly handled in "handleEnterLeaveEvent()". if (receiverMapped.x() >= 0 && receiverMapped.y() >= 0) { - QApplicationPrivate::dispatchEnterLeave(receiver, nullptr, event->screenPos()); + QApplicationPrivate::dispatchEnterLeave(receiver, nullptr, event->globalPosition()); qt_last_mouse_receiver = receiver; } } else { - QApplicationPrivate::dispatchEnterLeave(nullptr, qt_last_mouse_receiver, event->screenPos()); + QApplicationPrivate::dispatchEnterLeave(nullptr, qt_last_mouse_receiver, event->globalPosition()); qt_last_mouse_receiver = receiver; receiver = activePopupWidget; } } -#endif - if ((event->type() != QEvent::MouseButtonPress) - || !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) { - QMouseEvent e(event->type(), widgetPos, event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers(), event->source()); + if ((event->type() != QEvent::MouseButtonPress) || !(QMutableSinglePointEvent::from(event)->isDoubleClick())) { + // if the widget that was pressed is gone, then deliver move events without buttons + const auto buttons = event->type() == QEvent::MouseMove && qt_popup_down_closed + ? Qt::NoButton : event->buttons(); + QMouseEvent e(event->type(), widgetPos, event->scenePosition(), event->globalPosition(), + event->button(), buttons, event->modifiers(), + event->source(), event->pointingDevice()); e.setTimestamp(event->timestamp()); QApplicationPrivate::sendMouseEvent(receiver, &e, receiver, receiver->window(), &qt_button_down, qt_last_mouse_receiver); qt_last_mouse_receiver = receiver; @@ -571,14 +588,14 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) } } - if (qApp->activePopupWidget() != activePopupWidget + if (QApplication::activePopupWidget() != activePopupWidget && qt_replay_popup_mouse_event && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) { if (m_widget->windowType() != Qt::Popup) - qt_button_down = 0; + qt_button_down = nullptr; if (event->type() == QEvent::MouseButtonPress) { // the popup disappeared, replay the mouse press event - QWidget *w = QApplication::widgetAt(event->globalPos()); + QWidget *w = QApplication::widgetAt(event->globalPosition().toPoint()); if (w && !QApplicationPrivate::isBlockedByModal(w)) { // activate window of the widget under mouse pointer if (!w->isActiveWindow()) { @@ -586,17 +603,14 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) w->window()->raise(); } - QWindow *win = w->windowHandle(); - if (!win) - win = w->nativeParentWidget()->windowHandle(); - if (win) { + if (auto win = qt_widget_private(w)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) { const QRect globalGeometry = win->isTopLevel() ? win->geometry() : QRect(win->mapToGlobal(QPoint(0, 0)), win->size()); - if (globalGeometry.contains(event->globalPos())) { + if (globalGeometry.contains(event->globalPosition().toPoint())) { // Use postEvent() to ensure the local QEventLoop terminates when called from QMenu::exec() - const QPoint localPos = win->mapFromGlobal(event->globalPos()); - QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, localPos, localPos, event->globalPos(), + const QPoint localPos = win->mapFromGlobal(event->globalPosition().toPoint()); + QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, localPos, localPos, event->globalPosition().toPoint(), event->button(), event->buttons(), event->modifiers(), event->source()); QCoreApplicationPrivate::setEventSpontaneous(e, true); e->setTimestamp(event->timestamp()); @@ -607,37 +621,39 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) } qt_replay_popup_mouse_event = false; #ifndef QT_NO_CONTEXTMENU - } else if (event->type() == contextMenuTrigger + } else if (event->type() == QGuiApplicationPrivate::contextMenuEventType() && event->button() == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { QWidget *receiver = activePopupWidget; if (qt_button_down) receiver = qt_button_down; - else if(popupChild) + else if (popupChild) receiver = popupChild; - QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPos(), event->modifiers()); + const QPoint localPos = receiver->mapFromGlobal(event->globalPosition().toPoint()); + QContextMenuEvent e(QContextMenuEvent::Mouse, localPos, event->globalPosition().toPoint(), event->modifiers()); QApplication::forwardEvent(receiver, &e, event); } #else - Q_UNUSED(contextMenuTrigger) - Q_UNUSED(oldOpenPopupCount) + Q_UNUSED(oldOpenPopupCount); } #endif if (releaseAfter) { - qt_button_down = 0; - qt_popup_down = 0; + qt_button_down = nullptr; + qt_popup_down_closed = false; + qt_popup_down = nullptr; } return; } + qt_popup_down_closed = false; // modal event handling if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type())) return; // which child should have it? - QWidget *widget = m_widget->childAt(event->pos()); - QPoint mapped = event->pos(); + QWidget *widget = m_widget->childAt(event->position().toPoint()); + QPoint mapped = event->position().toPoint(); if (!widget) widget = m_widget; @@ -646,28 +662,31 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) if (event->type() == QEvent::MouseButtonPress && initialPress) qt_button_down = widget; - QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->windowPos().toPoint(), &mapped, event->type(), event->buttons(), + QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->scenePosition().toPoint(), &mapped, event->type(), event->buttons(), qt_button_down, widget); if (!receiver) return; - if ((event->type() != QEvent::MouseButtonPress) - || !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) { + if ((event->type() != QEvent::MouseButtonPress) || !QMutableSinglePointEvent::from(event)->isDoubleClick()) { // The preceding statement excludes MouseButtonPress events which caused // creation of a MouseButtonDblClick event. QTBUG-25831 - QMouseEvent translated(event->type(), mapped, event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers(), event->source()); + QMouseEvent translated(event->type(), mapped, event->scenePosition(), event->globalPosition(), + event->button(), event->buttons(), event->modifiers(), + event->source(), event->pointingDevice()); translated.setTimestamp(event->timestamp()); QApplicationPrivate::sendMouseEvent(receiver, &translated, widget, m_widget, &qt_button_down, qt_last_mouse_receiver); event->setAccepted(translated.isAccepted()); } #ifndef QT_NO_CONTEXTMENU - if (event->type() == contextMenuTrigger && event->button() == Qt::RightButton - && m_widget->rect().contains(event->pos())) { - QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPos(), event->modifiers()); + if (event->type() == QGuiApplicationPrivate::contextMenuEventType() + && event->button() == Qt::RightButton + && m_widget->rect().contains(event->position().toPoint())) { + QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPosition().toPoint(), event->modifiers()); QGuiApplication::forwardEvent(receiver, &e, event); + if (e.isAccepted()) + event->accept(); } #endif } @@ -675,14 +694,14 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) void QWidgetWindow::handleTouchEvent(QTouchEvent *event) { if (event->type() == QEvent::TouchCancel) { - QApplicationPrivate::translateTouchCancel(event->device(), event->timestamp()); + QApplicationPrivate::translateTouchCancel(event->pointingDevice(), event->timestamp()); event->accept(); - } else if (qApp->d_func()->inPopupMode()) { + } else if (QApplicationPrivate::inPopupMode()) { // Ignore touch events for popups. This will cause QGuiApplication to synthesise mouse // events instead, which QWidgetWindow::handleMouseEvent will forward correctly: event->ignore(); } else { - event->setAccepted(QApplicationPrivate::translateRawTouchEvent(m_widget, event->device(), event->touchPoints(), event->timestamp())); + event->setAccepted(QApplicationPrivate::translateRawTouchEvent(m_widget, event)); } } @@ -707,6 +726,9 @@ bool QWidgetWindow::updateSize() bool changed = false; if (m_widget->testAttribute(Qt::WA_OutsideWSRange)) return changed; + if (m_widget->testAttribute(Qt::WA_DontShowOnScreen)) + return changed; + if (m_widget->data->crect.size() != geometry().size()) { changed = true; m_widget->data->crect.setSize(geometry().size()); @@ -716,44 +738,45 @@ bool QWidgetWindow::updateSize() return changed; } -bool QWidgetWindow::updatePos() -{ - bool changed = false; - if (m_widget->testAttribute(Qt::WA_OutsideWSRange)) - return changed; - if (m_widget->data->crect.topLeft() != geometry().topLeft()) { - changed = true; - m_widget->data->crect.moveTopLeft(geometry().topLeft()); - } - updateMargins(); - return changed; -} - void QWidgetWindow::updateMargins() { - const QMargins margins = frameMargins(); + // QTBUG-79147 (Windows): Bail out on resize events after closing a dialog + // and destroying the platform window which would clear the margins. QTLWExtra *te = m_widget->d_func()->topData(); + if (te->window == nullptr || te->window->handle() == nullptr) + return; + const QMargins margins = frameMargins(); te->posIncludesFrame= false; te->frameStrut.setCoords(margins.left(), margins.top(), margins.right(), margins.bottom()); m_widget->data->fstrut_dirty = false; } -static void sendScreenChangeRecursively(QWidget *widget) +static void sendChangeRecursively(QWidget *widget, QEvent::Type type) { - QEvent e(QEvent::ScreenChangeInternal); - QApplication::sendEvent(widget, &e); + QEvent e(type); + QCoreApplication::sendEvent(widget, &e); QWidgetPrivate *d = QWidgetPrivate::get(widget); for (int i = 0; i < d->children.size(); ++i) { QWidget *w = qobject_cast<QWidget *>(d->children.at(i)); if (w) - sendScreenChangeRecursively(w); + sendChangeRecursively(w, type); } } void QWidgetWindow::handleScreenChange() { // Send an event recursively to the widget and its children. - sendScreenChangeRecursively(m_widget); + sendChangeRecursively(m_widget, QEvent::ScreenChangeInternal); + + // Invalidate the backing store buffer and repaint immediately. + if (screen()) + repaintWindow(); +} + +void QWidgetWindow::handleDevicePixelRatioChange() +{ + // Send an event recursively to the widget and its children. + sendChangeRecursively(m_widget, QEvent::DevicePixelRatioChange); // Invalidate the backing store buffer and repaint immediately. if (screen()) @@ -766,9 +789,9 @@ void QWidgetWindow::repaintWindow() return; QTLWExtra *tlwExtra = m_widget->window()->d_func()->maybeTopData(); - if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->backingStoreTracker->markDirty(m_widget->rect(), m_widget, - QWidgetBackingStore::UpdateNow, QWidgetBackingStore::BufferInvalid); + if (tlwExtra && tlwExtra->backingStore) + tlwExtra->repaintManager->markDirty(m_widget->rect(), m_widget, + QWidgetRepaintManager::UpdateNow, QWidgetRepaintManager::BufferInvalid); } // Store normal geometry used for saving application settings. @@ -789,32 +812,82 @@ void QWidgetWindow::updateNormalGeometry() void QWidgetWindow::handleMoveEvent(QMoveEvent *event) { - if (updatePos()) - QGuiApplication::forwardEvent(m_widget, event); + if (m_widget->testAttribute(Qt::WA_OutsideWSRange)) + return; + if (m_widget->testAttribute(Qt::WA_DontShowOnScreen)) + return; + + auto oldPosition = m_widget->data->crect.topLeft(); + auto newPosition = geometry().topLeft(); + + if (!m_widget->isWindow()) { + if (auto *nativeParent = m_widget->nativeParentWidget()) + newPosition = m_widget->parentWidget()->mapFrom(nativeParent, newPosition); + } + + bool changed = newPosition != oldPosition; + + if (changed) + m_widget->data->crect.moveTopLeft(newPosition); + + updateMargins(); // FIXME: Only do when changed? + + if (changed) { + QMoveEvent widgetEvent(newPosition, oldPosition); + QGuiApplication::forwardEvent(m_widget, &widgetEvent, event); + } } void QWidgetWindow::handleResizeEvent(QResizeEvent *event) { - QSize oldSize = m_widget->data->crect.size(); + auto oldRect = m_widget->rect(); if (updateSize()) { QGuiApplication::forwardEvent(m_widget, event); - if (m_widget->d_func()->paintOnScreen()) { - QRegion updateRegion(geometry()); + if (m_widget->d_func()->shouldPaintOnScreen()) { + QRegion dirtyRegion = m_widget->rect(); if (m_widget->testAttribute(Qt::WA_StaticContents)) - updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height()); - m_widget->d_func()->syncBackingStore(updateRegion); + dirtyRegion -= oldRect; + m_widget->d_func()->syncBackingStore(dirtyRegion); } else { m_widget->d_func()->syncBackingStore(); } } } -void QWidgetWindow::handleCloseEvent(QCloseEvent *event) +void QWidgetWindow::closeEvent(QCloseEvent *event) { - bool is_closing = m_widget->d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent); - event->setAccepted(is_closing); + Q_D(QWidgetWindow); + bool accepted = m_widget->d_func()->handleClose(d->inClose ? QWidgetPrivate::CloseWithEvent + : QWidgetPrivate::CloseWithSpontaneousEvent); + event->setAccepted(accepted); +} + +bool QWidgetWindowPrivate::participatesInLastWindowClosed() const +{ + Q_Q(const QWidgetWindow); + + // For historical reasons WA_QuitOnClose has been closely tied + // to the lastWindowClosed signal, since the default behavior + // is to quit the application after emitting lastWindowClosed. + // ### Qt 7: Rename this attribute, or decouple behavior. + if (!q->widget()->testAttribute(Qt::WA_QuitOnClose)) + return false; + + return QWindowPrivate::participatesInLastWindowClosed(); +} + +bool QWidgetWindowPrivate::treatAsVisible() const +{ + Q_Q(const QWidgetWindow); + + // Widget windows may have Qt::WA_DontShowOnScreen, in which case the + // QQWidget will be visible, but the corresponding QWindow will not. + // Since the lastWindowClosed logic relies on checking whether the + // closed window was visible, and if there are any remaining visible + // windows, we need to reflect the QWidget state, not the QWindow one. + return q->widget()->isVisible(); } #if QT_CONFIG(wheelevent) @@ -825,25 +898,27 @@ void QWidgetWindow::handleWheelEvent(QWheelEvent *event) return; QWidget *rootWidget = m_widget; - QPoint pos = event->pos(); + QPointF pos = event->position(); // Use proper popup window for wheel event. Some QPA sends the wheel // event to the root menu, so redirect it to the proper popup window. QWidget *activePopupWidget = QApplication::activePopupWidget(); if (activePopupWidget && activePopupWidget != m_widget) { rootWidget = activePopupWidget; - pos = rootWidget->mapFromGlobal(event->globalPos()); + pos = rootWidget->mapFromGlobal(event->globalPosition()); } // which child should have it? - QWidget *widget = rootWidget->childAt(pos); + QWidget *widget = rootWidget->childAt(pos.toPoint()); if (!widget) widget = rootWidget; - QPoint mapped = widget->mapFrom(rootWidget, pos); + QPointF 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()); + QWheelEvent translated(mapped, event->globalPosition(), event->pixelDelta(), event->angleDelta(), + event->buttons(), event->modifiers(), event->phase(), event->inverted(), + event->source(), event->pointingDevice()); translated.setTimestamp(event->timestamp()); QGuiApplication::forwardEvent(widget, &translated, event); } @@ -868,16 +943,16 @@ void QWidgetWindow::handleDragEnterEvent(QDragEnterEvent *event, QWidget *widget { Q_ASSERT(m_dragTarget == nullptr); if (!widget) - widget = findDnDTarget(m_widget, event->pos()); + widget = findDnDTarget(m_widget, event->position().toPoint()); if (!widget) { event->ignore(); return; } m_dragTarget = widget; - const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); + const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint())); QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(), - event->mouseButtons(), event->keyboardModifiers()); + event->buttons(), event->modifiers()); QGuiApplication::forwardEvent(m_dragTarget, &translated, event); event->setAccepted(translated.isAccepted()); event->setDropAction(translated.dropAction()); @@ -885,18 +960,19 @@ void QWidgetWindow::handleDragEnterEvent(QDragEnterEvent *event, QWidget *widget void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) { - auto *widget = findDnDTarget(m_widget, event->pos()); + QPointer<QWidget> widget = findDnDTarget(m_widget, event->position().toPoint()); if (!widget) { event->ignore(); if (m_dragTarget) { // Send DragLeave to previous QDragLeaveEvent leaveEvent; - QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); + QWidget *dragTarget = m_dragTarget; m_dragTarget = nullptr; + QGuiApplication::forwardEvent(dragTarget, &leaveEvent, event); } } else { - const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); + const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint())); QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(), - event->mouseButtons(), event->keyboardModifiers()); + event->buttons(), event->modifiers()); if (widget == m_dragTarget) { // Target widget unchanged: Send DragMove translated.setDropAction(event->dropAction()); @@ -905,17 +981,22 @@ void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) } else { if (m_dragTarget) { // Send DragLeave to previous QDragLeaveEvent leaveEvent; - QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); + QWidget *dragTarget = m_dragTarget; m_dragTarget = nullptr; + QGuiApplication::forwardEvent(dragTarget, &leaveEvent, event); + } + // widget might have been deleted when handling the leaveEvent + if (widget) { + // Send DragEnter to new widget. + handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget); + // Handling 'DragEnter' should suffice for the application. + translated.setDropAction(event->dropAction()); + translated.setAccepted(event->isAccepted()); + // The drag enter event is always immediately followed by a drag move event, + // see QDragEnterEvent documentation. + if (m_dragTarget) + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); } - // Send DragEnter to new widget. - handleDragEnterEvent(static_cast<QDragEnterEvent*>(event), widget); - // Handling 'DragEnter' should suffice for the application. - translated.setDropAction(event->dropAction()); - translated.setAccepted(event->isAccepted()); - // 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()); @@ -924,9 +1005,11 @@ void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) void QWidgetWindow::handleDragLeaveEvent(QDragLeaveEvent *event) { - if (m_dragTarget) - QGuiApplication::forwardEvent(m_dragTarget, event); - m_dragTarget = nullptr; + if (m_dragTarget) { + QWidget *dragTarget = m_dragTarget; + m_dragTarget = nullptr; + QGuiApplication::forwardEvent(dragTarget, event); + } } void QWidgetWindow::handleDropEvent(QDropEvent *event) @@ -936,18 +1019,22 @@ void QWidgetWindow::handleDropEvent(QDropEvent *event) event->ignore(); return; } - 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, &translated, event); + const QPoint mapped = m_dragTarget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint())); + QDropEvent translated(mapped, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); + QWidget *dragTarget = m_dragTarget; + m_dragTarget = nullptr; + QGuiApplication::forwardEvent(dragTarget, &translated, event); event->setAccepted(translated.isAccepted()); event->setDropAction(translated.dropAction()); - m_dragTarget = nullptr; } #endif // QT_CONFIG(draganddrop) void QWidgetWindow::handleExposeEvent(QExposeEvent *event) { + if (m_widget->testAttribute(Qt::WA_DontShowOnScreen)) + return; // Ignore for widgets that fake exposure + QWidgetPrivate *wPriv = m_widget->d_func(); const bool exposed = isExposed(); @@ -967,7 +1054,7 @@ void QWidgetWindow::handleExposeEvent(QExposeEvent *event) // If the window becomes not exposed... if (wPriv->childrenShownByExpose) { // ... and child widgets was previously shown by the expose event - hide widgets again. - // This is a workaround, because sometimes when window is minimized programatically, + // This is a workaround, because sometimes when window is minimized programmatically, // the QPA can notify that the window is exposed after changing window state to minimized // and then, the QPA can send next expose event with null exposed region (not exposed). wPriv->hideChildren(true); @@ -983,8 +1070,8 @@ void QWidgetWindow::handleExposeEvent(QExposeEvent *event) 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()); + if (!event->m_region.isNull()) + wPriv->syncBackingStore(event->m_region); } else { m_widget->setAttribute(Qt::WA_Mapped, false); } @@ -1018,7 +1105,7 @@ void QWidgetWindow::handleWindowStateChangedEvent(QWindowStateChangeEvent *event } } -bool QWidgetWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) +bool QWidgetWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) { return m_widget->nativeEvent(eventType, message, result); } @@ -1026,25 +1113,24 @@ bool QWidgetWindow::nativeEvent(const QByteArray &eventType, void *message, long #if QT_CONFIG(tabletevent) void QWidgetWindow::handleTabletEvent(QTabletEvent *event) { - static QPointer<QWidget> qt_tablet_target = 0; + static QPointer<QWidget> qt_tablet_target = nullptr; QWidget *widget = qt_tablet_target; if (!widget) { - widget = m_widget->childAt(event->pos()); - if (event->type() == QEvent::TabletPress) { - if (!widget) - widget = m_widget; + widget = m_widget->childAt(event->position().toPoint()); + if (!widget) + widget = m_widget; + if (event->type() == QEvent::TabletPress) qt_tablet_target = widget; - } } if (widget) { - QPointF delta = event->globalPosF() - event->globalPos(); - QPointF mapped = widget->mapFromGlobal(event->globalPos()) + delta; - QTabletEvent ev(event->type(), mapped, event->globalPosF(), event->device(), event->pointerType(), + QPointF delta = event->globalPosition() - event->globalPosition().toPoint(); + QPointF mapped = widget->mapFromGlobal(event->globalPosition().toPoint()) + delta; + QTabletEvent ev(event->type(), event->pointingDevice(), mapped, event->globalPosition(), event->pressure(), event->xTilt(), event->yTilt(), event->tangentialPressure(), - event->rotation(), event->z(), event->modifiers(), event->uniqueId(), event->button(), event->buttons()); + event->rotation(), event->z(), event->modifiers(), event->button(), event->buttons()); ev.setTimestamp(event->timestamp()); ev.setAccepted(false); QGuiApplication::forwardEvent(widget, &ev, event); @@ -1052,7 +1138,7 @@ void QWidgetWindow::handleTabletEvent(QTabletEvent *event) } if (event->type() == QEvent::TabletRelease && event->buttons() == Qt::NoButton) - qt_tablet_target = 0; + qt_tablet_target = nullptr; } #endif // QT_CONFIG(tabletevent) @@ -1060,14 +1146,14 @@ void QWidgetWindow::handleTabletEvent(QTabletEvent *event) void QWidgetWindow::handleGestureEvent(QNativeGestureEvent *e) { // copy-pasted code to find correct widget follows: - QObject *receiver = 0; + QObject *receiver = nullptr; if (QApplicationPrivate::inPopupMode()) { QWidget *popup = QApplication::activePopupWidget(); QWidget *popupFocusWidget = popup->focusWidget(); receiver = popupFocusWidget ? popupFocusWidget : popup; } if (!receiver) - receiver = QApplication::widgetAt(e->globalPos()); + receiver = QApplication::widgetAt(e->globalPosition().toPoint()); if (!receiver) receiver = m_widget; // last resort @@ -1096,7 +1182,7 @@ void QWidgetWindow::handleContextMenuEvent(QContextMenuEvent *e) } } if (fw && fw->isEnabled()) { - QPoint pos = fw->inputMethodQuery(Qt::ImMicroFocus).toRect().center(); + QPoint pos = fw->inputMethodQuery(Qt::ImCursorRectangle).toRect().center(); QContextMenuEvent widgetEvent(QContextMenuEvent::Keyboard, pos, fw->mapToGlobal(pos), e->modifiers()); QGuiApplication::forwardEvent(fw, &widgetEvent, e); @@ -1108,8 +1194,8 @@ void QWidgetWindow::updateObjectName() { QString name = m_widget->objectName(); if (name.isEmpty()) - name = QString::fromUtf8(m_widget->metaObject()->className()) + QLatin1String("Class"); - name += QLatin1String("Window"); + name = QString::fromUtf8(m_widget->metaObject()->className()) + "Class"_L1; + name += "Window"_L1; setObjectName(name); } |