• Błażej Szczygieł's avatar
    QtWidgets: Proper delivery of enter/leave event to context menus · 57ecd5ae
    Błażej Szczygieł authored
    
    First-level context menu grabs the mouse, so all mouse events are
    delivered to it. This menu passes the mouse events to submenus. Any
    platform delivers mouse enter/leave event differently when window is
    grabbed. This patch unifies event delivery to context menus - it can
    block some unwanted events and it emulates fake events if necessary.
    
    This patch can reduce duplicated events and can provide proper enter
    or leave event to additional widgets in the context menu. It can also
    prevent submenu from unwanted close on Windows and X11.
    
    Added autotest.
    
    Task-number: QTBUG-45565
    Task-number: QTBUG-45893
    Task-number: QTBUG-47515
    Change-Id: I7dd476d0be23afa34e947e54aef235012d173dcf
    Reviewed-by: default avatarShawn Rutledge <shawn.rutledge@theqtcompany.com>
    57ecd5ae
qwidgetwindow.cpp 36.48 KiB
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtWidgets module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 http://www.qt.io/terms-conditions. For further
** information use the contact form at http://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 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
** $QT_END_LICENSE$
****************************************************************************/
#include "private/qwindow_p.h"
#include "qwidgetwindow_p.h"
#include "qlayout.h"
#include "private/qwidget_p.h"
#include "private/qapplication_p.h"
#ifndef QT_NO_ACCESSIBILITY
#include <QtGui/qaccessible.h>
#endif
#include <private/qwidgetbackingstore_p.h>
#include <qpa/qwindowsysteminterface_p.h>
#include <qpa/qplatformtheme.h>
#include <qpa/qplatformwindow.h>
#include <private/qgesturemanager_p.h>
#include <private/qhighdpiscaling_p.h>
QT_BEGIN_NAMESPACE
Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets();
QWidget *qt_button_down = 0; // widget got last button-down
// popup control
QWidget *qt_popup_down = 0; // popup that contains the pressed widget
extern int openPopupCount;
bool qt_replay_popup_mouse_event = false;
extern bool qt_try_modal(QWidget *widget, QEvent::Type type);
class QWidgetWindowPrivate : public QWindowPrivate
    Q_DECLARE_PUBLIC(QWidgetWindow)
public:
    QWindow *eventReceiver() Q_DECL_OVERRIDE {
        Q_Q(QWidgetWindow);
        QWindow *w = q;
        while (w->parent() && qobject_cast<QWidgetWindow *>(w) && qobject_cast<QWidgetWindow *>(w->parent())) {
            w = w->parent();
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
} return w; } void clearFocusObject() Q_DECL_OVERRIDE { Q_Q(QWidgetWindow); QWidget *widget = q->widget(); if (widget && widget->focusWidget()) widget->focusWidget()->clearFocus(); } QRectF closestAcceptableGeometry(const QRectF &rect) const Q_DECL_OVERRIDE; }; QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const { Q_Q(const QWidgetWindow); const QWidget *widget = q->widget(); if (!widget->isWindow() || !widget->hasHeightForWidth()) return QRect(); const QSize oldSize = rect.size().toSize(); const QSize newSize = QLayout::closestAcceptableSize(widget, oldSize); if (newSize == oldSize) return QRectF(); const int dw = newSize.width() - oldSize.width(); const int dh = newSize.height() - oldSize.height(); QRectF result = rect; const QRectF currentGeometry(widget->geometry()); const qreal topOffset = result.top() - currentGeometry.top(); const qreal bottomOffset = result.bottom() - currentGeometry.bottom(); if (qAbs(topOffset) > qAbs(bottomOffset)) result.setTop(result.top() - dh); // top edge drag else result.setBottom(result.bottom() + dh); // bottom edge drag const qreal leftOffset = result.left() - currentGeometry.left(); const qreal rightOffset = result.right() - currentGeometry.right(); if (qAbs(leftOffset) > qAbs(rightOffset)) result.setLeft(result.left() - dw); // left edge drag else result.setRight(result.right() + dw); // right edge drag return result; } QWidgetWindow::QWidgetWindow(QWidget *widget) : QWindow(*new QWidgetWindowPrivate(), 0) , 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); } connect(m_widget, &QObject::objectNameChanged, this, &QWidgetWindow::updateObjectName); connect(this, SIGNAL(screenChanged(QScreen*)), this, SLOT(handleScreenChange())); } QWidgetWindow::~QWidgetWindow() { } #ifndef QT_NO_ACCESSIBILITY QAccessibleInterface *QWidgetWindow::accessibleRoot() const { if (m_widget) return QAccessible::queryAccessibleInterface(m_widget); return 0; }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
#endif QObject *QWidgetWindow::focusObject() const { QWidget *widget = m_widget->focusWidget(); if (!widget) widget = m_widget; if (widget) { QObject *focusObj = QWidgetPrivate::get(widget)->focusObject(); if (focusObj) return focusObj; } return widget; } bool QWidgetWindow::event(QEvent *event) { if (m_widget->testAttribute(Qt::WA_DontShowOnScreen)) { // \a event is uninteresting for QWidgetWindow, the event was probably // generated before WA_DontShowOnScreen was set return QCoreApplication::sendEvent(m_widget, event); } switch (event->type()) { case QEvent::Close: handleCloseEvent(static_cast<QCloseEvent *>(event)); return true; case QEvent::Enter: case QEvent::Leave: handleEnterLeaveEvent(event); return true; // these should not be sent to QWidget, the corresponding events // are sent by QApplicationPrivate::notifyActiveWindowChange() case QEvent::FocusIn: handleFocusInEvent(static_cast<QFocusEvent *>(event)); // Fallthrough case QEvent::FocusOut: { #ifndef QT_NO_ACCESSIBILITY QAccessible::State state; state.active = true; QAccessibleStateChangeEvent ev(widget(), state); QAccessible::updateAccessibility(&ev); #endif return false; } case QEvent::FocusAboutToChange: if (QApplicationPrivate::focus_widget) { if (QApplicationPrivate::focus_widget->testAttribute(Qt::WA_InputMethodEnabled)) QGuiApplication::inputMethod()->commit(); QGuiApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, event); } return true; case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::ShortcutOverride: handleKeyEvent(static_cast<QKeyEvent *>(event)); return true; case QEvent::MouseMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: handleMouseEvent(static_cast<QMouseEvent *>(event));
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
return true; case QEvent::NonClientAreaMouseMove: case QEvent::NonClientAreaMouseButtonPress: case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonDblClick: handleNonClientAreaMouseEvent(static_cast<QMouseEvent *>(event)); return true; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: handleTouchEvent(static_cast<QTouchEvent *>(event)); return true; case QEvent::Move: handleMoveEvent(static_cast<QMoveEvent *>(event)); return true; case QEvent::Resize: handleResizeEvent(static_cast<QResizeEvent *>(event)); return true; #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: handleWheelEvent(static_cast<QWheelEvent *>(event)); return true; #endif #ifndef QT_NO_DRAGANDDROP case QEvent::DragEnter: case QEvent::DragMove: handleDragEnterMoveEvent(static_cast<QDragMoveEvent *>(event)); return true; case QEvent::DragLeave: handleDragLeaveEvent(static_cast<QDragLeaveEvent *>(event)); return true; case QEvent::Drop: handleDropEvent(static_cast<QDropEvent *>(event)); return true; #endif case QEvent::Expose: handleExposeEvent(static_cast<QExposeEvent *>(event)); return true; case QEvent::WindowStateChange: handleWindowStateChangedEvent(static_cast<QWindowStateChangeEvent *>(event)); return true; case QEvent::ThemeChange: { QEvent widgetEvent(QEvent::ThemeChange); QGuiApplication::sendSpontaneousEvent(m_widget, &widgetEvent); } return true; #ifndef QT_NO_TABLETEVENT case QEvent::TabletPress: case QEvent::TabletMove: case QEvent::TabletRelease: handleTabletEvent(static_cast<QTabletEvent *>(event)); return true; #endif #ifndef QT_NO_GESTURES case QEvent::NativeGesture: handleGestureEvent(static_cast<QNativeGestureEvent *>(event)); return true; #endif
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
#ifndef QT_NO_CONTEXTMENU case QEvent::ContextMenu: handleContextMenuEvent(static_cast<QContextMenuEvent *>(event)); return true; #endif // Handing show events to widgets (see below) here would cause them to be triggered twice case QEvent::Show: case QEvent::Hide: return QWindow::event(event); case QEvent::WindowBlocked: qt_button_down = 0; break; case QEvent::UpdateRequest: // This is not the same as an UpdateRequest for a QWidget. That just // syncs the backing store while here we also must mark as dirty. m_widget->repaint(); return true; default: break; } if (QCoreApplication::sendEvent(m_widget, event) && event->type() != QEvent::Timer) return true; return QWindow::event(event); } QPointer<QWidget> qt_last_mouse_receiver = 0; 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; // 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; if (systemEvent) { if (QWidgetWindow *enterWindow = qobject_cast<QWidgetWindow *>(systemEvent->enter)) { QWindow *thisParent = this; QWindow *enterParent = enterWindow; while (thisParent->parent()) thisParent = thisParent->parent(); while (enterParent->parent()) enterParent = enterParent->parent(); if (thisParent == enterParent) { QGuiApplicationPrivate::currentMouseWindow = enterWindow; enter = enterWindow->widget(); QWindowSystemInterfacePrivate::removeWindowSystemEvent(systemEvent); } } } // Enter-leave between sibling widgets is ignored when there is a mousegrabber - this makes // both native and non-native widgets work similarly.
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
// When mousegrabbing, leaves are only generated if leaving the parent window. if (!enter || !QWidget::mouseGrabber()) { // Preferred leave target is the last mouse receiver, unless it has native window, // in which case it is assumed to receive it's own leave event when relevant. QWidget *leave = m_widget; if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId()) leave = qt_last_mouse_receiver.data(); QApplicationPrivate::dispatchEnterLeave(enter, leave, globalPosF); qt_last_mouse_receiver = enter; } } else { const QEnterEvent *ee = static_cast<QEnterEvent *>(event); QWidget *child = m_widget->childAt(ee->pos()); QWidget *receiver = child ? child : m_widget; QApplicationPrivate::dispatchEnterLeave(receiver, 0, ee->screenPos()); qt_last_mouse_receiver = receiver; } } QWidget *QWidgetWindow::getFocusWidget(FocusWidgets fw) { QWidget *tlw = m_widget; QWidget *w = tlw->nextInFocusChain(); QWidget *last = tlw; uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus; while (w != tlw) { if (((w->focusPolicy() & focus_flag) == focus_flag) && w->isVisibleTo(m_widget) && w->isEnabled()) { last = w; if (fw == FirstFocusWidget) break; } w = w->nextInFocusChain(); } return last; } void QWidgetWindow::handleFocusInEvent(QFocusEvent *e) { QWidget *focusWidget = 0; if (e->reason() == Qt::BacktabFocusReason) focusWidget = getFocusWidget(LastFocusWidget); else if (e->reason() == Qt::TabFocusReason) focusWidget = getFocusWidget(FirstFocusWidget); if (focusWidget != 0) focusWidget->setFocus(); } void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e) { QApplication::sendSpontaneousEvent(m_widget, 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 (activePopupWidget != m_widget) mapped = activePopupWidget->mapFromGlobal(event->globalPos());
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
bool releaseAfter = false; QWidget *popupChild = activePopupWidget->childAt(mapped); if (activePopupWidget != qt_popup_down) { qt_button_down = 0; qt_popup_down = 0; } switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: qt_button_down = popupChild; qt_popup_down = activePopupWidget; break; case QEvent::MouseButtonRelease: releaseAfter = true; break; default: break; // nothing for mouse move } int oldOpenPopupCount = openPopupCount; if (activePopupWidget->isEnabled()) { // deliver event qt_replay_popup_mouse_event = false; QWidget *receiver = activePopupWidget; QPoint widgetPos = mapped; if (qt_button_down) receiver = qt_button_down; else if (popupChild) receiver = popupChild; if (receiver != activePopupWidget) widgetPos = receiver->mapFromGlobal(event->globalPos()); QWidget *alien = receiver; #if !defined(Q_OS_OSX) && !defined(Q_OS_IOS) // Cocoa tracks popups const bool reallyUnderMouse = activePopupWidget->rect().contains(mapped); const bool underMouse = activePopupWidget->underMouse(); if (activePopupWidget != m_widget || (!underMouse && qt_button_down)) { // If active popup menu is not the first-level popup menu then we must emulate enter/leave events, // because first-level popup menu grabs the mouse and enter/leave events are delivered only to it // by QPA. Make an exception for first-level popup menu when the mouse button is pressed on widget. if (underMouse != reallyUnderMouse) { if (reallyUnderMouse) { QApplicationPrivate::dispatchEnterLeave(receiver, Q_NULLPTR, event->screenPos()); qt_last_mouse_receiver = receiver; } else { QApplicationPrivate::dispatchEnterLeave(Q_NULLPTR, qt_last_mouse_receiver, event->screenPos()); qt_last_mouse_receiver = receiver; receiver = activePopupWidget; } } } else if (!reallyUnderMouse) { alien = Q_NULLPTR; } #endif QMouseEvent e(event->type(), widgetPos, event->windowPos(), event->screenPos(), event->button(), event->buttons(), event->modifiers(), event->source()); e.setTimestamp(event->timestamp()); QApplicationPrivate::sendMouseEvent(receiver, &e, alien, receiver->window(), &qt_button_down, qt_last_mouse_receiver); qt_last_mouse_receiver = receiver; } else { // close disabled popups when a mouse button is pressed or released switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: case QEvent::MouseButtonRelease: activePopupWidget->close();
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
break; default: break; } } if (qApp->activePopupWidget() != activePopupWidget && qt_replay_popup_mouse_event && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) { if (m_widget->windowType() != Qt::Popup) qt_button_down = 0; if (event->type() == QEvent::MouseButtonPress) { // the popup disappeared, replay the mouse press event QWidget *w = QApplication::widgetAt(event->globalPos()); if (w && !QApplicationPrivate::isBlockedByModal(w)) { // activate window of the widget under mouse pointer if (!w->isActiveWindow()) { w->activateWindow(); w->raise(); } QWindow *win = w->windowHandle(); if (!win) win = w->nativeParentWidget()->windowHandle(); if (win) { const QRect globalGeometry = win->isTopLevel() ? win->geometry() : QRect(win->mapToGlobal(QPoint(0, 0)), win->size()); if (globalGeometry.contains(event->globalPos())) { // 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(), event->button(), event->buttons(), event->modifiers(), event->source()); QCoreApplicationPrivate::setEventSpontaneous(e, true); e->setTimestamp(event->timestamp()); QCoreApplication::postEvent(win, e); } } } } qt_replay_popup_mouse_event = false; #ifndef QT_NO_CONTEXTMENU } else if (event->type() == contextMenuTrigger && event->button() == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { QWidget *popupEvent = activePopupWidget; if (qt_button_down) popupEvent = qt_button_down; else if(popupChild) popupEvent = popupChild; QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPos(), event->modifiers()); QApplication::sendSpontaneousEvent(popupEvent, &e); #endif } if (releaseAfter) { qt_button_down = 0; qt_popup_down = 0; } return; } // 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();
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
if (!widget) widget = m_widget; if (event->type() == QEvent::MouseButtonPress) qt_button_down = widget; QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->windowPos().toPoint(), &mapped, event->type(), event->buttons(), qt_button_down, widget); if (!receiver) { if (event->type() == QEvent::MouseButtonRelease) QApplicationPrivate::mouse_buttons &= ~event->button(); return; } if ((event->type() != QEvent::MouseButtonPress) || !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) { // 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()); 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()); QGuiApplication::sendSpontaneousEvent(receiver, &e); } #endif } void QWidgetWindow::handleTouchEvent(QTouchEvent *event) { if (event->type() == QEvent::TouchCancel) { QApplicationPrivate::translateTouchCancel(event->device(), event->timestamp()); event->accept(); } else if (qApp->d_func()->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())); } } void QWidgetWindow::handleKeyEvent(QKeyEvent *event) { if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type())) return; QObject *receiver = QWidget::keyboardGrabber(); if (!receiver && QApplicationPrivate::inPopupMode()) { QWidget *popup = QApplication::activePopupWidget(); QWidget *popupFocusWidget = popup->focusWidget(); receiver = popupFocusWidget ? popupFocusWidget : popup; } if (!receiver) receiver = focusObject(); QGuiApplication::sendSpontaneousEvent(receiver, event); } bool QWidgetWindow::updateSize() { bool changed = false; if (m_widget->testAttribute(Qt::WA_OutsideWSRange)) return changed;
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
if (m_widget->data->crect.size() != geometry().size()) { changed = true; m_widget->data->crect.setSize(geometry().size()); } updateMargins(); 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(); QTLWExtra *te = m_widget->d_func()->topData(); 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) { QEvent e(QEvent::ScreenChangeInternal); QApplication::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); } } void QWidgetWindow::handleScreenChange() { // Send an event recursively to the widget and its children. sendScreenChangeRecursively(m_widget); // Invalidate the backing store buffer and repaint immediately. if (screen()) repaintWindow(); } void QWidgetWindow::repaintWindow() { if (!m_widget->isVisible() || !m_widget->updatesEnabled() || !m_widget->rect().isValid()) 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); } Qt::WindowState effectiveState(Qt::WindowStates state); // Store normal geometry used for saving application settings. void QWidgetWindow::updateNormalGeometry() { QTLWExtra *tle = m_widget->d_func()->maybeTopData();
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
if (!tle) return; // Ask platform window, default to widget geometry. QRect normalGeometry; if (const QPlatformWindow *pw = handle()) normalGeometry = QHighDpi::fromNativePixels(pw->normalGeometry(), this); if (!normalGeometry.isValid() && effectiveState(m_widget->windowState()) == Qt::WindowNoState) normalGeometry = m_widget->geometry(); if (normalGeometry.isValid()) tle->normalGeometry = normalGeometry; } void QWidgetWindow::handleMoveEvent(QMoveEvent *event) { if (updatePos()) QGuiApplication::sendSpontaneousEvent(m_widget, event); } void QWidgetWindow::handleResizeEvent(QResizeEvent *event) { QSize oldSize = m_widget->data->crect.size(); if (updateSize()) { QGuiApplication::sendSpontaneousEvent(m_widget, event); if (m_widget->d_func()->paintOnScreen()) { QRegion updateRegion(geometry()); if (m_widget->testAttribute(Qt::WA_StaticContents)) updateRegion -= QRect(0, 0, oldSize.width(), oldSize.height()); m_widget->d_func()->syncBackingStore(updateRegion); } else { m_widget->d_func()->syncBackingStore(); } } } void QWidgetWindow::handleCloseEvent(QCloseEvent *event) { bool is_closing = m_widget->d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent); event->setAccepted(is_closing); } #ifndef QT_NO_WHEELEVENT void QWidgetWindow::handleWheelEvent(QWheelEvent *event) { if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type())) return; // which child should have it? QWidget *widget = m_widget->childAt(event->pos()); if (!widget) widget = m_widget; QPoint mapped = widget->mapFrom(m_widget, event->pos()); QWheelEvent translated(mapped, event->globalPos(), event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source()); QGuiApplication::sendSpontaneousEvent(widget, &translated); } #endif // QT_NO_WHEELEVENT #ifndef QT_NO_DRAGANDDROP void QWidgetWindow::handleDragEnterMoveEvent(QDragMoveEvent *event) { 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());
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
if (!widget) widget = m_widget; 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::sendSpontaneousEvent(widget, &translated); 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::sendSpontaneousEvent(m_dragTarget.data(), &le); m_dragTarget = 0; } if (!widget) { 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::sendSpontaneousEvent(widget, &translated); if (translated.isAccepted()) { event->accept(); } else { event->ignore(); } event->setDropAction(translated.dropAction()); } void QWidgetWindow::handleDragLeaveEvent(QDragLeaveEvent *event) { if (m_dragTarget) QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), event); m_dragTarget = 0; } void QWidgetWindow::handleDropEvent(QDropEvent *event) { if (m_dragTarget.isNull()) { qWarning() << m_widget << ": No drag target set."; event->ignore(); return; } const QPoint mapped = m_dragTarget.data()->mapFromGlobal(m_widget->mapToGlobal(event->pos())); QDropEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers()); QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), &translated); if (translated.isAccepted()) event->accept(); event->setDropAction(translated.dropAction()); m_dragTarget = 0; } #endif // QT_NO_DRAGANDDROP
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
void QWidgetWindow::handleExposeEvent(QExposeEvent *event) { if (isExposed()) { m_widget->setAttribute(Qt::WA_Mapped); if (!event->region().isNull()) { // Exposed native widgets need to be marked dirty to get them repainted correctly. if (m_widget->internalWinId() && !m_widget->isWindow() && m_widget->isVisible() && m_widget->updatesEnabled()) { if (QWidgetBackingStore *bs = m_widget->d_func()->maybeBackingStore()) bs->markDirty(event->region(), m_widget); } m_widget->d_func()->syncBackingStore(event->region()); } } else { m_widget->setAttribute(Qt::WA_Mapped, false); } } void QWidgetWindow::handleWindowStateChangedEvent(QWindowStateChangeEvent *event) { // QWindow does currently not know 'active'. Qt::WindowStates eventState = event->oldState(); Qt::WindowStates widgetState = m_widget->windowState(); if (widgetState & Qt::WindowActive) eventState |= Qt::WindowActive; // Determine the new widget state, remember maximized/full screen // during minimized. switch (windowState()) { case Qt::WindowNoState: widgetState &= ~(Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen); break; case Qt::WindowMinimized: widgetState |= Qt::WindowMinimized; break; case Qt::WindowMaximized: updateNormalGeometry(); widgetState |= Qt::WindowMaximized; widgetState &= ~(Qt::WindowMinimized | Qt::WindowFullScreen); break; case Qt::WindowFullScreen: updateNormalGeometry(); widgetState |= Qt::WindowFullScreen; widgetState &= ~(Qt::WindowMinimized); break; case Qt::WindowActive: // Not handled by QWindow break; } // Sent event if the state changed (that is, it is not triggered by // QWidget::setWindowState(), which also sends an event to the widget). if (widgetState != int(m_widget->data->window_state)) { m_widget->data->window_state = widgetState; QWindowStateChangeEvent widgetEvent(eventState); QGuiApplication::sendSpontaneousEvent(m_widget, &widgetEvent); } } bool QWidgetWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) { return m_widget->nativeEvent(eventType, message, result); } #ifndef QT_NO_TABLETEVENT void QWidgetWindow::handleTabletEvent(QTabletEvent *event) { static QPointer<QWidget> qt_tablet_target = 0; if (event->type() == QEvent::TabletPress) { QWidget *widget = m_widget->childAt(event->pos()); if (!widget)
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
widget = m_widget; qt_tablet_target = widget; } if (qt_tablet_target) { QPointF delta = event->globalPosF() - event->globalPos(); QPointF mapped = qt_tablet_target->mapFromGlobal(event->globalPos()) + delta; QTabletEvent ev(event->type(), mapped, event->globalPosF(), event->device(), event->pointerType(), event->pressure(), event->xTilt(), event->yTilt(), event->tangentialPressure(), event->rotation(), event->z(), event->modifiers(), event->uniqueId(), event->button(), event->buttons()); ev.setTimestamp(event->timestamp()); QGuiApplication::sendSpontaneousEvent(qt_tablet_target, &ev); } if (event->type() == QEvent::TabletRelease && event->buttons() == Qt::NoButton) qt_tablet_target = 0; } #endif // QT_NO_TABLETEVENT #ifndef QT_NO_GESTURES void QWidgetWindow::handleGestureEvent(QNativeGestureEvent *e) { // copy-pasted code to find correct widget follows: QObject *receiver = 0; if (QApplicationPrivate::inPopupMode()) { QWidget *popup = QApplication::activePopupWidget(); QWidget *popupFocusWidget = popup->focusWidget(); receiver = popupFocusWidget ? popupFocusWidget : popup; } if (!receiver) receiver = QApplication::widgetAt(e->globalPos()); if (!receiver) receiver = m_widget; // last resort QApplication::sendSpontaneousEvent(receiver, e); } #endif // QT_NO_GESTURES #ifndef QT_NO_CONTEXTMENU void QWidgetWindow::handleContextMenuEvent(QContextMenuEvent *e) { // We are only interested in keyboard originating context menu events here, // mouse originated context menu events for widgets are generated in mouse handling methods. if (e->reason() != QContextMenuEvent::Keyboard) return; QWidget *fw = QWidget::keyboardGrabber(); if (!fw) { if (QApplication::activePopupWidget()) { fw = (QApplication::activePopupWidget()->focusWidget() ? QApplication::activePopupWidget()->focusWidget() : QApplication::activePopupWidget()); } else if (QApplication::focusWidget()) { fw = QApplication::focusWidget(); } else { fw = m_widget; } } if (fw && fw->isEnabled()) { QPoint pos = fw->inputMethodQuery(Qt::ImMicroFocus).toRect().center(); QContextMenuEvent widgetEvent(QContextMenuEvent::Keyboard, pos, fw->mapToGlobal(pos), e->modifiers()); QGuiApplication::sendSpontaneousEvent(fw, &widgetEvent); } } #endif // QT_NO_CONTEXTMENU void QWidgetWindow::updateObjectName() {
981982983984985986987988989
QString name = m_widget->objectName(); if (name.isEmpty()) name = QString::fromUtf8(m_widget->metaObject()->className()) + QStringLiteral("Class"); name += QStringLiteral("Window"); setObjectName(name); } QT_END_NAMESPACE