Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwidgetwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "private/qwindow_p.h"
7#include "qlayout.h"
8
9#include "private/qwidget_p.h"
10#include "private/qapplication_p.h"
11#if QT_CONFIG(accessibility)
12#include <QtGui/qaccessible.h>
13#endif
14#include <private/qwidgetrepaintmanager_p.h>
15#include <qpa/qwindowsysteminterface_p.h>
16#include <qpa/qplatformtheme.h>
17#include <qpa/qplatformwindow.h>
18#include <private/qgesturemanager_p.h>
19#include <private/qhighdpiscaling_p.h>
20
22
23using namespace Qt::StringLiterals;
24
25Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets();
26
27Q_WIDGETS_EXPORT QWidget *qt_button_down = nullptr; // widget got last button-down
28
29// popup control
30QWidget *qt_popup_down = nullptr; // popup that contains the pressed widget
31bool qt_popup_down_closed = false; // qt_popup_down has been closed
32
33extern bool qt_try_modal(QWidget *widget, QEvent::Type type);
34
36{
37 Q_DECLARE_PUBLIC(QWidgetWindow)
38public:
40 {
42 qCDebug(lcWidgetShowHide) << "Setting visibility of" << q->widget()
43 << "to" << visible << "via QWidgetWindowPrivate";
44
45 if (QWidget *widget = q->widget()) {
46 // If the widget's visible state is already matching the new QWindow
47 // visible state we assume the widget has already synced up.
48 if (visible != widget->isVisible())
50 }
51
52 // If we end up calling QWidgetPrivate::setVisible() above, we will
53 // in most cases recurse back into setNativeWindowVisibility() to
54 // update the QWindow state. But during QWidget::destroy() this is
55 // not the case, as Qt::WA_WState_Created has been unset by the time
56 // we check if we should call hide_helper(). We handle this case, as
57 // well as the cases where we don't call QWidgetPrivate::setVisible(),
58 // by syncing up the QWindow state here if needed.
59 if (q->isVisible() != visible)
61 }
62
65 QWindow *w = q;
67 w = w->parent();
68 }
69 return w;
70 }
71
79
81 {
83 QWidget *widget = q->widget();
84 if (!widget)
85 return;
86
87 switch (target) {
88 case FocusTarget::Prev:
89 case FocusTarget::Next: {
92 return;
93 }
94 case FocusTarget::First:
95 case FocusTarget::Last: {
101 break;
102 }
103 default:
104 break;
105 }
106 }
107
109
112};
113
114QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
115{
116 Q_Q(const QWidgetWindow);
117 const QWidget *widget = q->widget();
118 if (!widget || !widget->isWindow() || !widget->hasHeightForWidth())
119 return QRect();
120 const QSize oldSize = rect.size().toSize();
121 const QSize newSize = QLayout::closestAcceptableSize(widget, oldSize);
122 if (newSize == oldSize)
123 return QRectF();
124 const int dw = newSize.width() - oldSize.width();
125 const int dh = newSize.height() - oldSize.height();
126 QRectF result = rect;
127 const QRectF currentGeometry(widget->geometry());
128 const qreal topOffset = result.top() - currentGeometry.top();
129 const qreal bottomOffset = result.bottom() - currentGeometry.bottom();
130 if (qAbs(topOffset) > qAbs(bottomOffset))
131 result.setTop(result.top() - dh); // top edge drag
132 else
133 result.setBottom(result.bottom() + dh); // bottom edge drag
134 const qreal leftOffset = result.left() - currentGeometry.left();
135 const qreal rightOffset = result.right() - currentGeometry.right();
136 if (qAbs(leftOffset) > qAbs(rightOffset))
137 result.setLeft(result.left() - dw); // left edge drag
138 else
139 result.setRight(result.right() + dw); // right edge drag
140 return result;
141}
142
143bool q_evaluateRhiConfig(const QWidget *w, QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType);
144
145QWidgetWindow::QWidgetWindow(QWidget *widget)
146 : QWindow(*new QWidgetWindowPrivate(), nullptr)
147 , m_widget(widget)
148{
149 updateObjectName();
150 if (!QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets)) {
151 QSurface::SurfaceType type = QSurface::RasterSurface;
152 if (q_evaluateRhiConfig(m_widget, nullptr, &type))
153 setSurfaceType(type);
154 }
155
156 connect(widget, &QObject::objectNameChanged, this, &QWidgetWindow::updateObjectName);
157 connect(this, &QWidgetWindow::screenChanged, this, &QWidgetWindow::handleScreenChange);
158}
159
161{
162 // destroy while we are still alive
163 destroy();
164
165 if (!m_widget)
166 return;
167
168 QTLWExtra *topData = QWidgetPrivate::get(m_widget)->topData();
169 Q_ASSERT(topData);
170
171 // The QPlaformBackingStore may hold a reference to the window,
172 // so the backingstore needs to be deleted first.
173 topData->repaintManager.reset(nullptr);
174 delete topData->backingStore;
175 topData->backingStore = nullptr;
176 topData->widgetTextures.clear();
177
178 // Too late to do anything beyond this point
179 topData->window = nullptr;
180}
181
182#if QT_CONFIG(accessibility)
183QAccessibleInterface *QWidgetWindow::accessibleRoot() const
184{
185 if (m_widget)
186 return QAccessible::queryAccessibleInterface(m_widget);
187 return nullptr;
188}
189#endif
190
192{
193 QWidget *windowWidget = m_widget;
194 if (!windowWidget)
195 return nullptr;
196
197 // A window can't have a focus object if it's being destroyed.
198 if (QWidgetPrivate::get(windowWidget)->data.in_destructor)
199 return nullptr;
200
201 QWidget *widget = windowWidget->focusWidget();
202
203 if (!widget)
204 widget = windowWidget;
205
206 QObject *focusObj = QWidgetPrivate::get(widget)->focusObject();
207 if (focusObj)
208 return focusObj;
209
210 return widget;
211}
212
214{
215 Q_D(QWidgetWindow);
216 qCDebug(lcWidgetShowHide) << "Setting visibility of" << this
217 << "to" << visible << "via QWidgetWindow::setNativeWindowVisibility";
218
219 // Call base class setVisible() implementation to run the QWindow
220 // visibility logic. Don't call QWidgetWindowPrivate::setVisible()
221 // since that will recurse back into QWidget code.
222 d->QWindowPrivate::setVisible(visible);
223}
224
225void QWidgetWindow::focusNextPrevChild(QWidget *widget, bool next)
226{
227 Q_ASSERT(widget);
228 widget->focusNextPrevChild(next);
229}
230
231static inline bool shouldBePropagatedToWidget(QEvent *event)
232{
233 switch (event->type()) {
234 // Handing show events to widgets would cause them to be triggered twice
235 case QEvent::Show:
236 case QEvent::Hide:
237 case QEvent::Timer:
238 case QEvent::DynamicPropertyChange:
239 case QEvent::ChildAdded:
240 case QEvent::ChildRemoved:
241 case QEvent::Paint:
242 case QEvent::Close: // Propagated manually in closeEvent
243 return false;
244 default:
245 return true;
246 }
247}
248
249bool QWidgetWindow::event(QEvent *event)
250{
251 if (!m_widget)
252 return QWindow::event(event);
253
254 switch (event->type()) {
255 case QEvent::Enter:
256 case QEvent::Leave:
257 handleEnterLeaveEvent(event);
258 return true;
259
260 // these should not be sent to QWidget, the corresponding events
261 // are sent by QApplicationPrivate::notifyActiveWindowChange()
262 case QEvent::FocusIn:
263 handleFocusInEvent(static_cast<QFocusEvent *>(event));
264 Q_FALLTHROUGH();
265 case QEvent::FocusOut: {
266#if QT_CONFIG(accessibility)
267 QAccessible::State state;
268 state.active = true;
269 QAccessibleStateChangeEvent ev(m_widget, state);
270 QAccessible::updateAccessibility(&ev);
271#endif
272 return false; }
273
274 case QEvent::FocusAboutToChange:
275 if (QApplicationPrivate::focus_widget) {
276 if (QApplicationPrivate::focus_widget->testAttribute(Qt::WA_InputMethodEnabled))
277 QGuiApplication::inputMethod()->commit();
278
279 QGuiApplication::forwardEvent(QApplicationPrivate::focus_widget, event);
280 }
281 return true;
282
283 case QEvent::KeyPress:
284 case QEvent::KeyRelease:
285 case QEvent::ShortcutOverride:
286 handleKeyEvent(static_cast<QKeyEvent *>(event));
287 return true;
288
289 case QEvent::MouseMove:
290 case QEvent::MouseButtonPress:
291 case QEvent::MouseButtonRelease:
292 case QEvent::MouseButtonDblClick:
293 handleMouseEvent(static_cast<QMouseEvent *>(event));
294 return true;
295
296 case QEvent::NonClientAreaMouseMove:
297 case QEvent::NonClientAreaMouseButtonPress:
298 case QEvent::NonClientAreaMouseButtonRelease:
299 case QEvent::NonClientAreaMouseButtonDblClick:
300 handleNonClientAreaMouseEvent(static_cast<QMouseEvent *>(event));
301 return true;
302
303 case QEvent::TouchBegin:
304 case QEvent::TouchUpdate:
305 case QEvent::TouchEnd:
306 case QEvent::TouchCancel:
307 handleTouchEvent(static_cast<QTouchEvent *>(event));
308 return true;
309
310 case QEvent::Move:
311 handleMoveEvent(static_cast<QMoveEvent *>(event));
312 return true;
313
314 case QEvent::Resize:
315 handleResizeEvent(static_cast<QResizeEvent *>(event));
316 return true;
317
318#if QT_CONFIG(wheelevent)
319 case QEvent::Wheel:
320 handleWheelEvent(static_cast<QWheelEvent *>(event));
321 return true;
322#endif
323
324#if QT_CONFIG(draganddrop)
325 case QEvent::DragEnter:
326 handleDragEnterEvent(static_cast<QDragEnterEvent *>(event));
327 return true;
328 case QEvent::DragMove:
329 handleDragMoveEvent(static_cast<QDragMoveEvent *>(event));
330 return true;
331 case QEvent::DragLeave:
332 handleDragLeaveEvent(static_cast<QDragLeaveEvent *>(event));
333 return true;
334 case QEvent::Drop:
335 handleDropEvent(static_cast<QDropEvent *>(event));
336 return true;
337#endif
338
339 case QEvent::Expose:
340 handleExposeEvent(static_cast<QExposeEvent *>(event));
341 return true;
342
343 case QEvent::WindowStateChange:
344 QWindow::event(event); // Update QWindow::Visibility and emit signals.
345 handleWindowStateChangedEvent(static_cast<QWindowStateChangeEvent *>(event));
346 return true;
347
348 case QEvent::ThemeChange: {
349 QEvent widgetEvent(QEvent::ThemeChange);
350 QCoreApplication::forwardEvent(m_widget, &widgetEvent, event);
351 }
352 return true;
353
354#if QT_CONFIG(tabletevent)
355 case QEvent::TabletPress:
356 case QEvent::TabletMove:
357 case QEvent::TabletRelease:
358 handleTabletEvent(static_cast<QTabletEvent *>(event));
359 return true;
360#endif
361
362#ifndef QT_NO_GESTURES
363 case QEvent::NativeGesture:
364 handleGestureEvent(static_cast<QNativeGestureEvent *>(event));
365 return true;
366#endif
367
368#ifndef QT_NO_CONTEXTMENU
369 case QEvent::ContextMenu:
370 handleContextMenuEvent(static_cast<QContextMenuEvent *>(event));
371 return true;
372#endif // QT_NO_CONTEXTMENU
373
374 case QEvent::WindowBlocked:
375 qt_button_down = nullptr;
376 break;
377
378 case QEvent::UpdateRequest:
379 // This is not the same as an UpdateRequest for a QWidget. That just
380 // syncs the backing store while here we also must mark as dirty.
381 m_widget->repaint();
382 return true;
383
384 case QEvent::DevicePixelRatioChange:
385 handleDevicePixelRatioChange();
386 break;
387
388 case QEvent::SafeAreaMarginsChange:
389 QWidgetPrivate::get(m_widget)->updateContentsRect();
390 break;
391
392 default:
393 break;
394 }
395
396 if (shouldBePropagatedToWidget(event) && QCoreApplication::forwardEvent(m_widget, event))
397 return true;
398
399 return QWindow::event(event);
400}
401
403
405{
406 // Ignore all enter/leave events from QPA if we are not on the first-level context menu.
407 // This prevents duplicated events on most platforms. Fake events will be delivered in
408 // QWidgetWindow::handleMouseEvent(QMouseEvent *). Make an exception whether the widget
409 // is already under mouse - let the mouse leave.
410 if (QApplicationPrivate::inPopupMode() && m_widget != QApplication::activePopupWidget() && !m_widget->underMouse())
411 return;
412
413 if (event->type() == QEvent::Leave) {
414 QWidget *enter = nullptr;
415 // Check from window system event queue if the next queued enter targets a window
416 // in the same window hierarchy (e.g. enter a child of this window). If so,
417 // remove the enter event from queue and handle both in single dispatch.
418 QWindowSystemInterfacePrivate::EnterEvent *systemEvent =
419 static_cast<QWindowSystemInterfacePrivate::EnterEvent *>
420 (QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::Enter));
421 const QPointF globalPosF = systemEvent ? systemEvent->globalPos : QPointF(QGuiApplicationPrivate::lastCursorPosition);
422 if (systemEvent) {
423 if (QWidgetWindow *enterWindow = qobject_cast<QWidgetWindow *>(systemEvent->enter))
424 {
425 QWindow *thisParent = this;
426 QWindow *enterParent = enterWindow;
427 while (thisParent->parent())
428 thisParent = thisParent->parent();
429 while (enterParent->parent())
430 enterParent = enterParent->parent();
431 if (thisParent == enterParent) {
432 QGuiApplicationPrivate::currentMouseWindow = enterWindow;
433 enter = enterWindow->widget();
434 QWindowSystemInterfacePrivate::removeWindowSystemEvent(systemEvent);
435 }
436 }
437 }
438 // Enter-leave between sibling widgets is ignored when there is a mousegrabber - this makes
439 // both native and non-native widgets work similarly.
440 // When mousegrabbing, leaves are only generated if leaving the parent window.
441 if (!enter || !QWidget::mouseGrabber()) {
442 // Preferred leave target is the last mouse receiver, unless it has native window,
443 // in which case it is assumed to receive it's own leave event when relevant.
444 QWidget *leave = m_widget;
445 if (qt_last_mouse_receiver && !qt_last_mouse_receiver->internalWinId())
446 leave = qt_last_mouse_receiver.data();
447 QApplicationPrivate::dispatchEnterLeave(enter, leave, globalPosF);
448 qt_last_mouse_receiver = enter;
449 }
450 } else {
451 const QEnterEvent *ee = static_cast<QEnterEvent *>(event);
452 QWidget *child = m_widget->childAt(ee->position());
453 QWidget *receiver = child ? child : m_widget.data();
454 QWidget *leave = nullptr;
455 if (QApplicationPrivate::inPopupMode() && receiver == m_widget
456 && qt_last_mouse_receiver != m_widget) {
457 // This allows to deliver the leave event to the native widget
458 // action on first-level menu.
459 leave = qt_last_mouse_receiver;
460 }
461 QApplicationPrivate::dispatchEnterLeave(receiver, leave, ee->globalPosition());
462 qt_last_mouse_receiver = receiver;
463 }
464}
465
466QWidget *QWidgetWindow::getFocusWidget(FocusWidgets fw)
467{
468 QWidget *tlw = m_widget;
469 QWidget *w = tlw->nextInFocusChain();
470
471 QWidget *last = tlw;
472
473 uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
474
475 while (w != tlw)
476 {
477 if (((w->focusPolicy() & focus_flag) == focus_flag)
478 && w->isVisibleTo(m_widget) && w->isEnabled())
479 {
480 last = w;
481 if (fw == FirstFocusWidget)
482 break;
483 }
484 w = w->nextInFocusChain();
485 }
486
487 return last;
488}
489
490void QWidgetWindow::handleFocusInEvent(QFocusEvent *e)
491{
492 QWidget *focusWidget = nullptr;
493 if (e->reason() == Qt::BacktabFocusReason)
494 focusWidget = getFocusWidget(LastFocusWidget);
495 else if (e->reason() == Qt::TabFocusReason)
496 focusWidget = getFocusWidget(FirstFocusWidget);
497
498 if (focusWidget != nullptr)
499 focusWidget->setFocus();
500}
501
502void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e)
503{
504 QApplication::forwardEvent(m_widget, e);
505}
506
507void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
508{
509 Q_D(QWidgetWindow);
510
511 // Event delivery can potentially result in window re-creation (QTBUG-132912)
512 // so we need QPointer to avoid a dangling d below
513 QPointer<QWidgetWindow> self = this;
514
515 if (auto *activePopupWidget = QApplication::activePopupWidget()) {
516 QPointF mapped = event->position();
517 if (activePopupWidget != m_widget)
518 mapped = activePopupWidget->mapFromGlobal(event->globalPosition());
519 bool releaseAfter = false;
520 QWidget *popupChild = activePopupWidget->childAt(mapped);
521
522 if (activePopupWidget != qt_popup_down) {
523 qt_button_down = nullptr;
524 qt_popup_down = nullptr;
525 }
526
527 switch (event->type()) {
528 case QEvent::MouseButtonPress:
529 case QEvent::MouseButtonDblClick:
530 qt_button_down = popupChild;
531 qt_popup_down = activePopupWidget;
532 qt_popup_down_closed = false;
533 break;
534 case QEvent::MouseButtonRelease:
535 releaseAfter = true;
536 break;
537 default:
538 break; // nothing for mouse move
539 }
540
541 if (activePopupWidget->isEnabled()) {
542 // deliver event
543 QPointer<QWidget> receiver = activePopupWidget;
544 QPointF widgetPos = mapped;
545 if (qt_button_down)
546 receiver = qt_button_down;
547 else if (popupChild)
548 receiver = popupChild;
549 if (receiver != activePopupWidget)
550 widgetPos = receiver->mapFromGlobal(event->globalPosition());
551
552 const bool reallyUnderMouse = activePopupWidget->rect().contains(mapped.toPoint());
553 const bool underMouse = activePopupWidget->underMouse();
554 if (underMouse != reallyUnderMouse) {
555 if (reallyUnderMouse) {
556 const QPoint receiverMapped = receiver->mapFromGlobal(event->globalPosition().toPoint());
557 // Prevent negative mouse position on enter event - this event
558 // should be properly handled in "handleEnterLeaveEvent()".
559 if (receiverMapped.x() >= 0 && receiverMapped.y() >= 0) {
560 QApplicationPrivate::dispatchEnterLeave(receiver, nullptr, event->globalPosition());
561 qt_last_mouse_receiver = receiver;
562 }
563 } else {
564 QApplicationPrivate::dispatchEnterLeave(nullptr, qt_last_mouse_receiver, event->globalPosition());
565 qt_last_mouse_receiver = receiver;
566 receiver = activePopupWidget;
567 }
568 }
569
570 if ((event->type() != QEvent::MouseButtonPress) || !(QMutableSinglePointEvent::isDoubleClick(event))) {
571 // if the widget that was pressed is gone, then deliver move events without buttons
572 const auto buttons = event->type() == QEvent::MouseMove && qt_popup_down_closed
573 ? Qt::NoButton : event->buttons();
574 QMouseEvent e(event->type(), widgetPos, event->scenePosition(), event->globalPosition(),
575 event->button(), buttons, event->modifiers(),
576 event->source(), event->pointingDevice());
577 e.setTimestamp(event->timestamp());
578 QApplicationPrivate::sendMouseEvent(receiver, &e, receiver, receiver->window(), &qt_button_down, qt_last_mouse_receiver);
579 qt_last_mouse_receiver = receiver;
580 }
581 } else {
582 // close disabled popups when a mouse button is pressed or released
583 switch (event->type()) {
584 case QEvent::MouseButtonPress:
585 case QEvent::MouseButtonDblClick:
586 case QEvent::MouseButtonRelease:
587 activePopupWidget->close();
588 break;
589 default:
590 break;
591 }
592 }
593
594 // Event delivery above might have destroyed this object. See QTBUG-138419.
595 if (self.isNull())
596 return;
597
598 if (QApplication::activePopupWidget() != activePopupWidget
599 && QApplicationPrivate::replayMousePress
600 && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) {
601 if (m_widget->windowType() != Qt::Popup)
602 qt_button_down = nullptr;
603 if (event->type() == QEvent::MouseButtonPress) {
604 // the popup disappeared: replay the mouse press event to whatever is behind it
605 QWidget *w = QApplication::widgetAt(event->globalPosition().toPoint());
606 if (w && !QApplicationPrivate::isBlockedByModal(w)) {
607 // activate window of the widget under mouse pointer
608 if (!w->isActiveWindow()) {
609 w->activateWindow();
610 w->window()->raise();
611 }
612
613 if (auto win = qt_widget_private(w)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) {
614 const QRect globalGeometry = win->isTopLevel()
615 ? win->geometry()
616 : QRect(win->mapToGlobal(QPoint(0, 0)), win->size());
617 if (globalGeometry.contains(event->globalPosition().toPoint())) {
618 // Use postEvent() to ensure the local QEventLoop terminates when called from QMenu::exec()
619 const QPoint localPos = win->mapFromGlobal(event->globalPosition().toPoint());
620 QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, localPos, localPos, event->globalPosition().toPoint(),
621 event->button(), event->buttons(), event->modifiers(), event->source());
622 QCoreApplicationPrivate::setEventSpontaneous(e, true);
623 e->setTimestamp(event->timestamp());
624 QCoreApplication::postEvent(win, e);
625 }
626 }
627 }
628 }
629 QApplicationPrivate::replayMousePress = false;
630#ifndef QT_NO_CONTEXTMENU
631 } else if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
632 && event->button() == Qt::RightButton) {
633 QWidget *receiver = activePopupWidget;
634 if (qt_button_down)
635 receiver = qt_button_down;
636 else if (popupChild)
637 receiver = popupChild;
638 const QPoint localPos = receiver->mapFromGlobal(event->globalPosition().toPoint());
639 QContextMenuEvent e(QContextMenuEvent::Mouse, localPos, event->globalPosition().toPoint(), event->modifiers());
640 QApplication::forwardEvent(receiver, &e, event);
641 }
642#else
643 }
644#endif
645
646 if (releaseAfter) {
647 qt_button_down = nullptr;
648 qt_popup_down_closed = false;
649 qt_popup_down = nullptr;
650 }
651 return;
652 }
653
654 qt_popup_down_closed = false;
655 // modal event handling
656 if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
657 return;
658
659 // which child should have it?
660 QWidget *widget = m_widget->childAt(event->position());
661 QPointF mapped = event->position();
662
663 if (!widget)
664 widget = m_widget;
665
666 const bool initialPress = event->buttons() == event->button();
667 if (event->type() == QEvent::MouseButtonPress && initialPress)
668 qt_button_down = widget;
669
670 QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->scenePosition(), &mapped, event->type(), event->buttons(),
671 qt_button_down, widget);
672 if (!receiver)
673 return;
674
675 if (d->isPopup() && receiver->window()->windowHandle() != this) {
676 receiver = widget;
677 mapped = event->position().toPoint();
678 }
679
680 if ((event->type() != QEvent::MouseButtonPress) || !QMutableSinglePointEvent::isDoubleClick(event)) {
681
682 // The preceding statement excludes MouseButtonPress events which caused
683 // creation of a MouseButtonDblClick event. QTBUG-25831
684 QMouseEvent translated(event->type(), mapped, event->scenePosition(), event->globalPosition(),
685 event->button(), event->buttons(), event->modifiers(),
686 event->source(), event->pointingDevice());
687 translated.setTimestamp(event->timestamp());
688 QApplicationPrivate::sendMouseEvent(receiver, &translated, widget, m_widget,
689 &qt_button_down, qt_last_mouse_receiver);
690 event->setAccepted(translated.isAccepted());
691 }
692
693 if (self.isNull())
694 return;
695
696#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
697 if (
698#else
699 if (event->isAccepted() &&
700#endif
701 (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease))
702 d->maybeSynthesizeContextMenuEvent(event);
703}
704
705void QWidgetWindow::handleTouchEvent(QTouchEvent *event)
706{
707 if (event->type() == QEvent::TouchCancel) {
708 QApplicationPrivate::translateTouchCancel(event->pointingDevice(), event->timestamp());
709 event->accept();
710 } else if (QApplication::activePopupWidget()) {
711 // Ignore touch events for popups. This will cause QGuiApplication to synthesise mouse
712 // events instead, which QWidgetWindow::handleMouseEvent will forward correctly:
713 event->ignore();
714 } else {
715 event->setAccepted(QApplicationPrivate::translateRawTouchEvent(m_widget, event));
716 }
717}
718
719void QWidgetWindow::handleKeyEvent(QKeyEvent *event)
720{
721 if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
722 return;
723
724 QObject *receiver = QWidget::keyboardGrabber();
725 if (auto *popup = QApplication::activePopupWidget(); !receiver && popup) {
726 QWidget *popupFocusWidget = popup->focusWidget();
727 receiver = popupFocusWidget ? popupFocusWidget : popup;
728 }
729 if (!receiver)
730 receiver = focusObject();
731 QGuiApplication::forwardEvent(receiver, event);
732}
733
734bool QWidgetWindow::updateSize()
735{
736 bool changed = false;
737 if (m_widget->testAttribute(Qt::WA_OutsideWSRange))
738 return changed;
739 if (m_widget->testAttribute(Qt::WA_DontShowOnScreen))
740 return changed;
741
742 if (m_widget->data->crect.size() != geometry().size()) {
743 changed = true;
744 m_widget->data->crect.setSize(geometry().size());
745 }
746
747 updateMargins();
748 return changed;
749}
750
751void QWidgetWindow::updateMargins()
752{
753 // QTBUG-79147 (Windows): Bail out on resize events after closing a dialog
754 // and destroying the platform window which would clear the margins.
755 QTLWExtra *te = m_widget->d_func()->topData();
756 if (te->window == nullptr || te->window->handle() == nullptr)
757 return;
758 const QMargins margins = frameMargins();
759 te->posIncludesFrame= false;
760 te->frameStrut.setCoords(margins.left(), margins.top(), margins.right(), margins.bottom());
761 m_widget->data->fstrut_dirty = false;
762}
763
764static void sendChangeRecursively(QWidget *widget, QEvent::Type type)
765{
766 QEvent e(type);
767 QCoreApplication::sendEvent(widget, &e);
768 QWidgetPrivate *d = QWidgetPrivate::get(widget);
769 for (int i = 0; i < d->children.size(); ++i) {
770 QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
771 if (w)
772 sendChangeRecursively(w, type);
773 }
774}
775
776void QWidgetWindow::handleScreenChange()
777{
778 // Send an event recursively to the widget and its children.
779 sendChangeRecursively(m_widget, QEvent::ScreenChangeInternal);
780
781 // Invalidate the backing store buffer and schedule repaint
782 scheduleRepaint();
783}
784
785void QWidgetWindow::handleDevicePixelRatioChange()
786{
787 // Send an event recursively to the widget and its children.
788 sendChangeRecursively(m_widget, QEvent::DevicePixelRatioChange);
789
790 // Invalidate the backing store buffer and schedule repaint
791 scheduleRepaint();
792}
793
794/*
795 Schedules a repaint in response to screen or DPR changes
796
797 Normally these changes will come with a corresponding expose
798 event following the change, but to guarantee that we refresh
799 the widget based on the new properties we also schedule our
800 own repaint.
801
802 Note that we do not do a synchronous repaint here, as the system
803 hasn't asked us to repaint just yet, it just informed us about
804 the new window state.
805*/
806void QWidgetWindow::scheduleRepaint()
807{
808 if (!screen())
809 return;
810
811 if (!m_widget->isVisible() || !m_widget->updatesEnabled() || !m_widget->rect().isValid())
812 return;
813
814 QTLWExtra *tlwExtra = m_widget->window()->d_func()->maybeTopData();
815 if (tlwExtra && tlwExtra->backingStore) {
816 tlwExtra->repaintManager->markDirty(m_widget->rect(), m_widget,
817 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
818 }
819}
820
821// Store normal geometry used for saving application settings.
822void QWidgetWindow::updateNormalGeometry()
823{
824 QTLWExtra *tle = m_widget->d_func()->maybeTopData();
825 if (!tle)
826 return;
827 // Ask platform window, default to widget geometry.
828 QRect normalGeometry;
829 if (const QPlatformWindow *pw = handle())
830 normalGeometry = QHighDpi::fromNativePixels(pw->normalGeometry(), this);
831 if (!normalGeometry.isValid() && !(m_widget->windowState() & ~Qt::WindowActive))
832 normalGeometry = m_widget->geometry();
833 if (normalGeometry.isValid())
834 tle->normalGeometry = normalGeometry;
835}
836
837void QWidgetWindow::handleMoveEvent(QMoveEvent *event)
838{
839 if (m_widget->testAttribute(Qt::WA_OutsideWSRange))
840 return;
841 if (m_widget->testAttribute(Qt::WA_DontShowOnScreen))
842 return;
843
844 auto oldPosition = m_widget->data->crect.topLeft();
845 auto newPosition = geometry().topLeft();
846
847 if (!m_widget->isWindow()) {
848 if (auto *nativeParent = m_widget->nativeParentWidget())
849 newPosition = m_widget->parentWidget()->mapFrom(nativeParent, newPosition);
850 }
851
852 bool changed = newPosition != oldPosition;
853
854 if (changed)
855 m_widget->data->crect.moveTopLeft(newPosition);
856
857 updateMargins(); // FIXME: Only do when changed?
858
859 if (changed) {
860 QMoveEvent widgetEvent(newPosition, oldPosition);
861 QGuiApplication::forwardEvent(m_widget, &widgetEvent, event);
862 }
863}
864
865void QWidgetWindow::handleResizeEvent(QResizeEvent *event)
866{
867 auto oldRect = m_widget->rect();
868
869 if (updateSize()) {
870 QGuiApplication::forwardEvent(m_widget, event);
871
872 if (m_widget->d_func()->shouldPaintOnScreen()) {
873 QRegion dirtyRegion = m_widget->rect();
874 if (m_widget->testAttribute(Qt::WA_StaticContents))
875 dirtyRegion -= oldRect;
876 m_widget->d_func()->syncBackingStore(dirtyRegion);
877 } else {
878 m_widget->d_func()->syncBackingStore();
879 }
880 }
881}
882
883void QWidgetWindow::closeEvent(QCloseEvent *event)
884{
885 Q_D(QWidgetWindow);
886 if (qt_popup_down == m_widget) {
887 qt_popup_down = nullptr;
889 }
890 bool accepted = m_widget->d_func()->handleClose(d->inClose ? QWidgetPrivate::CloseWithEvent
891 : QWidgetPrivate::CloseWithSpontaneousEvent);
892 event->setAccepted(accepted);
893}
894
896{
897 Q_Q(const QWidgetWindow);
898
899 // For historical reasons WA_QuitOnClose has been closely tied
900 // to the lastWindowClosed signal, since the default behavior
901 // is to quit the application after emitting lastWindowClosed.
902 // ### Qt 7: Rename this attribute, or decouple behavior.
903 if (!q->widget()->testAttribute(Qt::WA_QuitOnClose))
904 return false;
905
906 return QWindowPrivate::participatesInLastWindowClosed();
907}
908
910{
911 Q_Q(const QWidgetWindow);
912
913 // Widget windows may have Qt::WA_DontShowOnScreen, in which case the
914 // QQWidget will be visible, but the corresponding QWindow will not.
915 // Since the lastWindowClosed logic relies on checking whether the
916 // closed window was visible, and if there are any remaining visible
917 // windows, we need to reflect the QWidget state, not the QWindow one.
918 return q->widget()->isVisible();
919}
920
921#if QT_CONFIG(wheelevent)
922
923void QWidgetWindow::handleWheelEvent(QWheelEvent *event)
924{
925 if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
926 return;
927
928 QWidget *rootWidget = m_widget;
929 QPointF pos = event->position();
930
931 // Use proper popup window for wheel event. Some QPA sends the wheel
932 // event to the root menu, so redirect it to the proper popup window.
933 QWidget *activePopupWidget = QApplication::activePopupWidget();
934 if (activePopupWidget && activePopupWidget != m_widget) {
935 rootWidget = activePopupWidget;
936 pos = rootWidget->mapFromGlobal(event->globalPosition());
937 }
938
939 // which child should have it?
940 QWidget *widget = rootWidget->childAt(pos);
941
942 if (!widget)
943 widget = rootWidget;
944
945 QPointF mapped = widget->mapFrom(rootWidget, pos);
946
947 QWheelEvent translated(mapped, event->globalPosition(), event->pixelDelta(), event->angleDelta(),
948 event->buttons(), event->modifiers(), event->phase(), event->inverted(),
949 event->source(), event->pointingDevice());
950 translated.setTimestamp(event->timestamp());
951 QGuiApplication::forwardEvent(widget, &translated, event);
952}
953
954#endif // QT_CONFIG(wheelevent)
955
956#if QT_CONFIG(draganddrop)
957
958static QWidget *findDnDTarget(QWidget *parent, const QPoint &pos)
959{
960 // Find a target widget under mouse that accepts drops (QTBUG-22987).
961 QWidget *widget = parent->childAt(pos);
962 if (!widget)
963 widget = parent;
964 for ( ; widget && !widget->isWindow() && !widget->acceptDrops(); widget = widget->parentWidget()) ;
965 if (widget && !widget->acceptDrops())
966 widget = nullptr;
967 return widget;
968}
969
970/*!
971 \internal
972
973 Sends \a event to \a widget.
974
975 Also called from dragMoveEvent(), in which case \a event is-a
976 QDragMoveEvent only, not a full QDragEnterEvent, which is why this function
977 takes \a event as a QDragMoveEvent and not, as one would expect,
978 QDragEnterEvent (downcast would be UB).
979*/
980void QWidgetWindow::handleDragEnterEvent(QDragMoveEvent *event, QWidget *widget)
981{
982 Q_ASSERT(m_dragTarget == nullptr);
983 if (!widget)
984 widget = findDnDTarget(m_widget, event->position().toPoint());
985 if (!widget) {
986 event->ignore();
987 return;
988 }
989 m_dragTarget = widget;
990
991 const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint()));
992 QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(),
993 event->buttons(), event->modifiers());
994 QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
995 event->setAccepted(translated.isAccepted());
996 event->setDropAction(translated.dropAction());
997}
998
999void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
1000{
1001 QPointer<QWidget> widget = findDnDTarget(m_widget, event->position().toPoint());
1002 if (!widget) {
1003 event->ignore();
1004 if (m_dragTarget) { // Send DragLeave to previous
1005 QDragLeaveEvent leaveEvent;
1006 QWidget *dragTarget = m_dragTarget;
1007 m_dragTarget = nullptr;
1008 QGuiApplication::forwardEvent(dragTarget, &leaveEvent, event);
1009 }
1010 } else {
1011 const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint()));
1012 QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(),
1013 event->buttons(), event->modifiers());
1014
1015 if (widget == m_dragTarget) { // Target widget unchanged: Send DragMove
1016 translated.setDropAction(event->dropAction());
1017 translated.setAccepted(event->isAccepted());
1018 QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
1019 } else {
1020 if (m_dragTarget) { // Send DragLeave to previous
1021 QDragLeaveEvent leaveEvent;
1022 QWidget *dragTarget = m_dragTarget;
1023 m_dragTarget = nullptr;
1024 QGuiApplication::forwardEvent(dragTarget, &leaveEvent, event);
1025 }
1026 // widget might have been deleted when handling the leaveEvent
1027 if (widget) {
1028 // Send DragEnter to new widget.
1029 handleDragEnterEvent(event, widget);
1030 // Handling 'DragEnter' should suffice for the application.
1031 translated.setDropAction(event->dropAction());
1032 translated.setAccepted(event->isAccepted());
1033 // The drag enter event is always immediately followed by a drag move event,
1034 // see QDragEnterEvent documentation.
1035 if (m_dragTarget)
1036 QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
1037 }
1038 }
1039 event->setAccepted(translated.isAccepted());
1040 event->setDropAction(translated.dropAction());
1041 }
1042}
1043
1044void QWidgetWindow::handleDragLeaveEvent(QDragLeaveEvent *event)
1045{
1046 if (m_dragTarget) {
1047 QWidget *dragTarget = m_dragTarget;
1048 m_dragTarget = nullptr;
1049 QGuiApplication::forwardEvent(dragTarget, event);
1050 }
1051}
1052
1053void QWidgetWindow::handleDropEvent(QDropEvent *event)
1054{
1055 if (Q_UNLIKELY(m_dragTarget.isNull())) {
1056 qWarning() << m_widget << ": No drag target set.";
1057 event->ignore();
1058 return;
1059 }
1060 const QPoint mapped = m_dragTarget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint()));
1061 QDropEvent translated(mapped, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1062 QWidget *dragTarget = m_dragTarget;
1063 m_dragTarget = nullptr;
1064 QGuiApplication::forwardEvent(dragTarget, &translated, event);
1065 event->setAccepted(translated.isAccepted());
1066 event->setDropAction(translated.dropAction());
1067}
1068
1069#endif // QT_CONFIG(draganddrop)
1070
1071void QWidgetWindow::handleExposeEvent(QExposeEvent *event)
1072{
1073 if (m_widget->testAttribute(Qt::WA_DontShowOnScreen))
1074 return; // Ignore for widgets that fake exposure
1075
1076 QWidgetPrivate *wPriv = m_widget->d_func();
1077 const bool exposed = isExposed();
1078
1079 // We might get an expose event from the platform as part of
1080 // closing the window from ~QWidget, to support animated close
1081 // transitions. But at that point we no longer have a widget
1082 // subclass to draw a new frame, so skip the expose event.
1083 if (exposed && wPriv->data.in_destructor)
1084 return;
1085
1086 if (wPriv->childrenHiddenByWState) {
1087 // If widgets has been previously hidden by window state change event
1088 // and they aren't yet shown...
1089 if (exposed) {
1090 // If the window becomes exposed...
1091 if (!wPriv->childrenShownByExpose) {
1092 // ... and they haven't been shown by this function yet - show it.
1093 wPriv->showChildren(true);
1094 QShowEvent showEvent;
1095 QCoreApplication::forwardEvent(m_widget, &showEvent, event);
1096 wPriv->childrenShownByExpose = true;
1097 }
1098 } else {
1099 // If the window becomes not exposed...
1100 if (wPriv->childrenShownByExpose) {
1101 // ... and child widgets was previously shown by the expose event - hide widgets again.
1102 // This is a workaround, because sometimes when window is minimized programmatically,
1103 // the QPA can notify that the window is exposed after changing window state to minimized
1104 // and then, the QPA can send next expose event with null exposed region (not exposed).
1105 wPriv->hideChildren(true);
1106 QHideEvent hideEvent;
1107 QCoreApplication::forwardEvent(m_widget, &hideEvent, event);
1108 wPriv->childrenShownByExpose = false;
1109 }
1110 }
1111 }
1112
1113 if (exposed) {
1114 // QTBUG-39220, QTBUG-58575: set all (potentially fully obscured parent widgets) mapped.
1115 m_widget->setAttribute(Qt::WA_Mapped);
1116 for (QWidget *p = m_widget->parentWidget(); p && !p->testAttribute(Qt::WA_Mapped); p = p->parentWidget())
1117 p->setAttribute(Qt::WA_Mapped);
1118 if (!event->m_region.isNull())
1119 wPriv->syncBackingStore(event->m_region);
1120 } else {
1121 m_widget->setAttribute(Qt::WA_Mapped, false);
1122 }
1123}
1124
1125void QWidgetWindow::handleWindowStateChangedEvent(QWindowStateChangeEvent *event)
1126{
1127 // QWindow does currently not know 'active'.
1128 Qt::WindowStates eventState = event->oldState();
1129 Qt::WindowStates widgetState = m_widget->windowState();
1130 Qt::WindowStates windowState = windowStates();
1131 if (widgetState & Qt::WindowActive)
1132 eventState |= Qt::WindowActive;
1133
1134 // Determine the new widget state, remember maximized/full screen
1135 // during minimized.
1136 if (windowState & Qt::WindowMinimized) {
1137 widgetState |= Qt::WindowMinimized;
1138 } else {
1139 widgetState = windowState | (widgetState & Qt::WindowActive);
1140 if (windowState) // Maximized or FullScreen
1141 updateNormalGeometry();
1142 }
1143
1144 // Sent event if the state changed (that is, it is not triggered by
1145 // QWidget::setWindowState(), which also sends an event to the widget).
1146 if (widgetState != Qt::WindowStates::Int(m_widget->data->window_state)) {
1147 m_widget->data->window_state = uint(widgetState);
1148 QWindowStateChangeEvent widgetEvent(eventState);
1149 QGuiApplication::forwardEvent(m_widget, &widgetEvent, event);
1150 }
1151}
1152
1153bool QWidgetWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
1154{
1155 return m_widget->nativeEvent(eventType, message, result);
1156}
1157
1158#if QT_CONFIG(tabletevent)
1159void QWidgetWindow::handleTabletEvent(QTabletEvent *event)
1160{
1161 static QPointer<QWidget> qt_tablet_target = nullptr;
1162
1163 QWidget *widget = qt_tablet_target;
1164
1165 if (!widget) {
1166 widget = m_widget->childAt(event->position());
1167 if (!widget)
1168 widget = m_widget;
1169 if (event->type() == QEvent::TabletPress)
1170 qt_tablet_target = widget;
1171 }
1172
1173 if (widget) {
1174 QPointF delta = event->globalPosition() - event->globalPosition().toPoint();
1175 QPointF mapped = widget->mapFromGlobal(event->globalPosition().toPoint()) + delta;
1176 QTabletEvent ev(event->type(), event->pointingDevice(), mapped, event->globalPosition(),
1177 event->pressure(), event->xTilt(), event->yTilt(), event->tangentialPressure(),
1178 event->rotation(), event->z(), event->modifiers(), event->button(), event->buttons());
1179 ev.setTimestamp(event->timestamp());
1180 ev.setAccepted(false);
1181 QGuiApplication::forwardEvent(widget, &ev, event);
1182 event->setAccepted(ev.isAccepted());
1183 }
1184
1185 if (event->type() == QEvent::TabletRelease && event->buttons() == Qt::NoButton)
1186 qt_tablet_target = nullptr;
1187}
1188#endif // QT_CONFIG(tabletevent)
1189
1190#ifndef QT_NO_GESTURES
1191void QWidgetWindow::handleGestureEvent(QNativeGestureEvent *e)
1192{
1193 // copy-pasted code to find correct widget follows:
1194 QObject *receiver = nullptr;
1195 if (auto *popup = QApplication::activePopupWidget()) {
1196 QWidget *popupFocusWidget = popup->focusWidget();
1197 receiver = popupFocusWidget ? popupFocusWidget : popup;
1198 }
1199 if (!receiver)
1200 receiver = QApplication::widgetAt(e->globalPosition().toPoint());
1201 if (!receiver)
1202 receiver = m_widget; // last resort
1203
1204 QApplication::forwardEvent(receiver, e);
1205}
1206#endif // QT_NO_GESTURES
1207
1208#ifndef QT_NO_CONTEXTMENU
1209void QWidgetWindow::handleContextMenuEvent(QContextMenuEvent *e)
1210{
1211 QWidget *receiver = qt_last_mouse_receiver.get();
1212 QPoint pos = e->pos();
1213 QPoint globalPos = e->globalPos();
1214
1215 // Keyboard-originating context menu events are delivered to the focus widget,
1216 // independently of event position.
1217 if (e->reason() == QContextMenuEvent::Keyboard) {
1218 receiver = QWidget::keyboardGrabber();
1219 if (!receiver) {
1220 if (QApplication::activePopupWidget()) {
1221 receiver = (QApplication::activePopupWidget()->focusWidget()
1222 ? QApplication::activePopupWidget()->focusWidget()
1223 : QApplication::activePopupWidget());
1224 } else if (QApplication::focusWidget()) {
1225 receiver = QApplication::focusWidget();
1226 } else {
1227 receiver = m_widget;
1228 }
1229 }
1230 if (Q_LIKELY(receiver)) {
1231 pos = receiver->inputMethodQuery(Qt::ImCursorRectangle).toRect().center();
1232 globalPos = receiver->mapToGlobal(pos);
1233 }
1234 } else if (Q_LIKELY(receiver)) {
1235 pos = receiver->mapFromGlobal(e->globalPos());
1236 }
1237
1238 if (receiver && receiver->isEnabled()) {
1239 QContextMenuEvent widgetEvent(e->reason(), pos, globalPos, e->modifiers());
1240 QGuiApplication::forwardEvent(receiver, &widgetEvent, e);
1241 }
1242}
1243#endif // QT_NO_CONTEXTMENU
1244
1245void QWidgetWindow::updateObjectName()
1246{
1247 QString name = m_widget->objectName();
1248 if (name.isEmpty())
1249 name = QString::fromUtf8(m_widget->metaObject()->className()) + "Class"_L1;
1250 name += "Window"_L1;
1251 setObjectName(name);
1252}
1253
1254QT_END_NAMESPACE
1255
1256#include "moc_qwidgetwindow_p.cpp"
bool participatesInLastWindowClosed() const override
bool treatAsVisible() const override
void setNativeWindowVisibility(bool visible)
QObject * focusObject() const override
Returns the QObject that will be the final receiver of events tied focus, such as key events.
void handleMoveEvent(QMoveEvent *)
void handleEnterLeaveEvent(QEvent *)
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override
Override this to handle platform dependent events.
bool event(QEvent *) override
Override this to handle any event (ev) sent to the window.
void handleExposeEvent(QExposeEvent *)
void closeEvent(QCloseEvent *) override
Override this to handle close events (ev).
void handleGestureEvent(QNativeGestureEvent *)
void handleWindowStateChangedEvent(QWindowStateChangeEvent *event)
The QWidget class is the base class of all user interface objects.
Definition qwidget.h:99
Combined button and popup list for selecting options.
Q_WIDGETS_EXPORT bool qt_tab_all_widgets()
QWidget * qt_popup_down
static void sendChangeRecursively(QWidget *widget, QEvent::Type type)
bool qt_popup_down_closed
bool q_evaluateRhiConfig(const QWidget *w, QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType)
Definition qwidget.cpp:1134
static bool shouldBePropagatedToWidget(QEvent *event)
bool qt_try_modal(QWidget *widget, QEvent::Type type)
QPointer< QWindow > qt_last_mouse_receiver