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 if (QApplication::activePopupWidget() != activePopupWidget
595 && QApplicationPrivate::replayMousePress
596 && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) {
597 if (m_widget->windowType() != Qt::Popup)
598 qt_button_down = nullptr;
599 if (event->type() == QEvent::MouseButtonPress) {
600 // the popup disappeared: replay the mouse press event to whatever is behind it
601 QWidget *w = QApplication::widgetAt(event->globalPosition().toPoint());
602 if (w && !QApplicationPrivate::isBlockedByModal(w)) {
603 // activate window of the widget under mouse pointer
604 if (!w->isActiveWindow()) {
605 w->activateWindow();
606 w->window()->raise();
607 }
608
609 if (auto win = qt_widget_private(w)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) {
610 const QRect globalGeometry = win->isTopLevel()
611 ? win->geometry()
612 : QRect(win->mapToGlobal(QPoint(0, 0)), win->size());
613 if (globalGeometry.contains(event->globalPosition().toPoint())) {
614 // Use postEvent() to ensure the local QEventLoop terminates when called from QMenu::exec()
615 const QPoint localPos = win->mapFromGlobal(event->globalPosition().toPoint());
616 QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, localPos, localPos, event->globalPosition().toPoint(),
617 event->button(), event->buttons(), event->modifiers(), event->source());
618 QCoreApplicationPrivate::setEventSpontaneous(e, true);
619 e->setTimestamp(event->timestamp());
620 QCoreApplication::postEvent(win, e);
621 }
622 }
623 }
624 }
625 QApplicationPrivate::replayMousePress = false;
626#ifndef QT_NO_CONTEXTMENU
627 } else if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
628 && event->button() == Qt::RightButton) {
629 QWidget *receiver = activePopupWidget;
630 if (qt_button_down)
631 receiver = qt_button_down;
632 else if (popupChild)
633 receiver = popupChild;
634 const QPoint localPos = receiver->mapFromGlobal(event->globalPosition().toPoint());
635 QContextMenuEvent e(QContextMenuEvent::Mouse, localPos, event->globalPosition().toPoint(), event->modifiers());
636 QApplication::forwardEvent(receiver, &e, event);
637 }
638#else
639 }
640#endif
641
642 if (releaseAfter) {
643 qt_button_down = nullptr;
644 qt_popup_down_closed = false;
645 qt_popup_down = nullptr;
646 }
647 return;
648 }
649
650 qt_popup_down_closed = false;
651 // modal event handling
652 if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
653 return;
654
655 // which child should have it?
656 QWidget *widget = m_widget->childAt(event->position());
657 QPointF mapped = event->position();
658
659 if (!widget)
660 widget = m_widget;
661
662 const bool initialPress = event->buttons() == event->button();
663 if (event->type() == QEvent::MouseButtonPress && initialPress)
664 qt_button_down = widget;
665
666 QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->scenePosition(), &mapped, event->type(), event->buttons(),
667 qt_button_down, widget);
668 if (!receiver)
669 return;
670
671 if (d->isPopup() && receiver->window()->windowHandle() != this) {
672 receiver = widget;
673 mapped = event->position().toPoint();
674 }
675
676 if ((event->type() != QEvent::MouseButtonPress) || !QMutableSinglePointEvent::isDoubleClick(event)) {
677
678 // The preceding statement excludes MouseButtonPress events which caused
679 // creation of a MouseButtonDblClick event. QTBUG-25831
680 QMouseEvent translated(event->type(), mapped, event->scenePosition(), event->globalPosition(),
681 event->button(), event->buttons(), event->modifiers(),
682 event->source(), event->pointingDevice());
683 translated.setTimestamp(event->timestamp());
684 QApplicationPrivate::sendMouseEvent(receiver, &translated, widget, m_widget,
685 &qt_button_down, qt_last_mouse_receiver);
686 event->setAccepted(translated.isAccepted());
687 }
688
689 if (self.isNull())
690 return;
691
692#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
693 if (
694#else
695 if (event->isAccepted() &&
696#endif
697 (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease))
698 d->maybeSynthesizeContextMenuEvent(event);
699}
700
701void QWidgetWindow::handleTouchEvent(QTouchEvent *event)
702{
703 if (event->type() == QEvent::TouchCancel) {
704 QApplicationPrivate::translateTouchCancel(event->pointingDevice(), event->timestamp());
705 event->accept();
706 } else if (QApplication::activePopupWidget()) {
707 // Ignore touch events for popups. This will cause QGuiApplication to synthesise mouse
708 // events instead, which QWidgetWindow::handleMouseEvent will forward correctly:
709 event->ignore();
710 } else {
711 event->setAccepted(QApplicationPrivate::translateRawTouchEvent(m_widget, event));
712 }
713}
714
715void QWidgetWindow::handleKeyEvent(QKeyEvent *event)
716{
717 if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
718 return;
719
720 QObject *receiver = QWidget::keyboardGrabber();
721 if (auto *popup = QApplication::activePopupWidget(); !receiver && popup) {
722 QWidget *popupFocusWidget = popup->focusWidget();
723 receiver = popupFocusWidget ? popupFocusWidget : popup;
724 }
725 if (!receiver)
726 receiver = focusObject();
727 QGuiApplication::forwardEvent(receiver, event);
728}
729
730bool QWidgetWindow::updateSize()
731{
732 bool changed = false;
733 if (m_widget->testAttribute(Qt::WA_OutsideWSRange))
734 return changed;
735 if (m_widget->testAttribute(Qt::WA_DontShowOnScreen))
736 return changed;
737
738 if (m_widget->data->crect.size() != geometry().size()) {
739 changed = true;
740 m_widget->data->crect.setSize(geometry().size());
741 }
742
743 updateMargins();
744 return changed;
745}
746
747void QWidgetWindow::updateMargins()
748{
749 // QTBUG-79147 (Windows): Bail out on resize events after closing a dialog
750 // and destroying the platform window which would clear the margins.
751 QTLWExtra *te = m_widget->d_func()->topData();
752 if (te->window == nullptr || te->window->handle() == nullptr)
753 return;
754 const QMargins margins = frameMargins();
755 te->posIncludesFrame= false;
756 te->frameStrut.setCoords(margins.left(), margins.top(), margins.right(), margins.bottom());
757 m_widget->data->fstrut_dirty = false;
758}
759
760static void sendChangeRecursively(QWidget *widget, QEvent::Type type)
761{
762 QEvent e(type);
763 QCoreApplication::sendEvent(widget, &e);
764 QWidgetPrivate *d = QWidgetPrivate::get(widget);
765 for (int i = 0; i < d->children.size(); ++i) {
766 QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
767 if (w)
768 sendChangeRecursively(w, type);
769 }
770}
771
772void QWidgetWindow::handleScreenChange()
773{
774 // Send an event recursively to the widget and its children.
775 sendChangeRecursively(m_widget, QEvent::ScreenChangeInternal);
776
777 // Invalidate the backing store buffer and schedule repaint
778 scheduleRepaint();
779}
780
781void QWidgetWindow::handleDevicePixelRatioChange()
782{
783 // Send an event recursively to the widget and its children.
784 sendChangeRecursively(m_widget, QEvent::DevicePixelRatioChange);
785
786 // Invalidate the backing store buffer and schedule repaint
787 scheduleRepaint();
788}
789
790/*
791 Schedules a repaint in response to screen or DPR changes
792
793 Normally these changes will come with a corresponding expose
794 event following the change, but to guarantee that we refresh
795 the widget based on the new properties we also schedule our
796 own repaint.
797
798 Note that we do not do a synchronous repaint here, as the system
799 hasn't asked us to repaint just yet, it just informed us about
800 the new window state.
801*/
802void QWidgetWindow::scheduleRepaint()
803{
804 if (!screen())
805 return;
806
807 if (!m_widget->isVisible() || !m_widget->updatesEnabled() || !m_widget->rect().isValid())
808 return;
809
810 QTLWExtra *tlwExtra = m_widget->window()->d_func()->maybeTopData();
811 if (tlwExtra && tlwExtra->backingStore) {
812 tlwExtra->repaintManager->markDirty(m_widget->rect(), m_widget,
813 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
814 }
815}
816
817// Store normal geometry used for saving application settings.
818void QWidgetWindow::updateNormalGeometry()
819{
820 QTLWExtra *tle = m_widget->d_func()->maybeTopData();
821 if (!tle)
822 return;
823 // Ask platform window, default to widget geometry.
824 QRect normalGeometry;
825 if (const QPlatformWindow *pw = handle())
826 normalGeometry = QHighDpi::fromNativePixels(pw->normalGeometry(), this);
827 if (!normalGeometry.isValid() && !(m_widget->windowState() & ~Qt::WindowActive))
828 normalGeometry = m_widget->geometry();
829 if (normalGeometry.isValid())
830 tle->normalGeometry = normalGeometry;
831}
832
833void QWidgetWindow::handleMoveEvent(QMoveEvent *event)
834{
835 if (m_widget->testAttribute(Qt::WA_OutsideWSRange))
836 return;
837 if (m_widget->testAttribute(Qt::WA_DontShowOnScreen))
838 return;
839
840 auto oldPosition = m_widget->data->crect.topLeft();
841 auto newPosition = geometry().topLeft();
842
843 if (!m_widget->isWindow()) {
844 if (auto *nativeParent = m_widget->nativeParentWidget())
845 newPosition = m_widget->parentWidget()->mapFrom(nativeParent, newPosition);
846 }
847
848 bool changed = newPosition != oldPosition;
849
850 if (changed)
851 m_widget->data->crect.moveTopLeft(newPosition);
852
853 updateMargins(); // FIXME: Only do when changed?
854
855 if (changed) {
856 QMoveEvent widgetEvent(newPosition, oldPosition);
857 QGuiApplication::forwardEvent(m_widget, &widgetEvent, event);
858 }
859}
860
861void QWidgetWindow::handleResizeEvent(QResizeEvent *event)
862{
863 auto oldRect = m_widget->rect();
864
865 if (updateSize()) {
866 QGuiApplication::forwardEvent(m_widget, event);
867
868 if (m_widget->d_func()->shouldPaintOnScreen()) {
869 QRegion dirtyRegion = m_widget->rect();
870 if (m_widget->testAttribute(Qt::WA_StaticContents))
871 dirtyRegion -= oldRect;
872 m_widget->d_func()->syncBackingStore(dirtyRegion);
873 } else {
874 m_widget->d_func()->syncBackingStore();
875 }
876 }
877}
878
879void QWidgetWindow::closeEvent(QCloseEvent *event)
880{
881 Q_D(QWidgetWindow);
882 if (qt_popup_down == m_widget) {
883 qt_popup_down = nullptr;
885 }
886 bool accepted = m_widget->d_func()->handleClose(d->inClose ? QWidgetPrivate::CloseWithEvent
887 : QWidgetPrivate::CloseWithSpontaneousEvent);
888 event->setAccepted(accepted);
889}
890
892{
893 Q_Q(const QWidgetWindow);
894
895 // For historical reasons WA_QuitOnClose has been closely tied
896 // to the lastWindowClosed signal, since the default behavior
897 // is to quit the application after emitting lastWindowClosed.
898 // ### Qt 7: Rename this attribute, or decouple behavior.
899 if (!q->widget()->testAttribute(Qt::WA_QuitOnClose))
900 return false;
901
902 return QWindowPrivate::participatesInLastWindowClosed();
903}
904
906{
907 Q_Q(const QWidgetWindow);
908
909 // Widget windows may have Qt::WA_DontShowOnScreen, in which case the
910 // QQWidget will be visible, but the corresponding QWindow will not.
911 // Since the lastWindowClosed logic relies on checking whether the
912 // closed window was visible, and if there are any remaining visible
913 // windows, we need to reflect the QWidget state, not the QWindow one.
914 return q->widget()->isVisible();
915}
916
917#if QT_CONFIG(wheelevent)
918
919void QWidgetWindow::handleWheelEvent(QWheelEvent *event)
920{
921 if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
922 return;
923
924 QWidget *rootWidget = m_widget;
925 QPointF pos = event->position();
926
927 // Use proper popup window for wheel event. Some QPA sends the wheel
928 // event to the root menu, so redirect it to the proper popup window.
929 QWidget *activePopupWidget = QApplication::activePopupWidget();
930 if (activePopupWidget && activePopupWidget != m_widget) {
931 rootWidget = activePopupWidget;
932 pos = rootWidget->mapFromGlobal(event->globalPosition());
933 }
934
935 // which child should have it?
936 QWidget *widget = rootWidget->childAt(pos);
937
938 if (!widget)
939 widget = rootWidget;
940
941 QPointF mapped = widget->mapFrom(rootWidget, pos);
942
943 QWheelEvent translated(mapped, event->globalPosition(), event->pixelDelta(), event->angleDelta(),
944 event->buttons(), event->modifiers(), event->phase(), event->inverted(),
945 event->source(), event->pointingDevice());
946 translated.setTimestamp(event->timestamp());
947 QGuiApplication::forwardEvent(widget, &translated, event);
948}
949
950#endif // QT_CONFIG(wheelevent)
951
952#if QT_CONFIG(draganddrop)
953
954static QWidget *findDnDTarget(QWidget *parent, const QPoint &pos)
955{
956 // Find a target widget under mouse that accepts drops (QTBUG-22987).
957 QWidget *widget = parent->childAt(pos);
958 if (!widget)
959 widget = parent;
960 for ( ; widget && !widget->isWindow() && !widget->acceptDrops(); widget = widget->parentWidget()) ;
961 if (widget && !widget->acceptDrops())
962 widget = nullptr;
963 return widget;
964}
965
966/*!
967 \internal
968
969 Sends \a event to \a widget.
970
971 Also called from dragMoveEvent(), in which case \a event is-a
972 QDragMoveEvent only, not a full QDragEnterEvent, which is why this function
973 takes \a event as a QDragMoveEvent and not, as one would expect,
974 QDragEnterEvent (downcast would be UB).
975*/
976void QWidgetWindow::handleDragEnterEvent(QDragMoveEvent *event, QWidget *widget)
977{
978 Q_ASSERT(m_dragTarget == nullptr);
979 if (!widget)
980 widget = findDnDTarget(m_widget, event->position().toPoint());
981 if (!widget) {
982 event->ignore();
983 return;
984 }
985 m_dragTarget = widget;
986
987 const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint()));
988 QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(),
989 event->buttons(), event->modifiers());
990 QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
991 event->setAccepted(translated.isAccepted());
992 event->setDropAction(translated.dropAction());
993}
994
995void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event)
996{
997 QPointer<QWidget> widget = findDnDTarget(m_widget, event->position().toPoint());
998 if (!widget) {
999 event->ignore();
1000 if (m_dragTarget) { // Send DragLeave to previous
1001 QDragLeaveEvent leaveEvent;
1002 QWidget *dragTarget = m_dragTarget;
1003 m_dragTarget = nullptr;
1004 QGuiApplication::forwardEvent(dragTarget, &leaveEvent, event);
1005 }
1006 } else {
1007 const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint()));
1008 QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(),
1009 event->buttons(), event->modifiers());
1010
1011 if (widget == m_dragTarget) { // Target widget unchanged: Send DragMove
1012 translated.setDropAction(event->dropAction());
1013 translated.setAccepted(event->isAccepted());
1014 QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
1015 } else {
1016 if (m_dragTarget) { // Send DragLeave to previous
1017 QDragLeaveEvent leaveEvent;
1018 QWidget *dragTarget = m_dragTarget;
1019 m_dragTarget = nullptr;
1020 QGuiApplication::forwardEvent(dragTarget, &leaveEvent, event);
1021 }
1022 // widget might have been deleted when handling the leaveEvent
1023 if (widget) {
1024 // Send DragEnter to new widget.
1025 handleDragEnterEvent(event, widget);
1026 // Handling 'DragEnter' should suffice for the application.
1027 translated.setDropAction(event->dropAction());
1028 translated.setAccepted(event->isAccepted());
1029 // The drag enter event is always immediately followed by a drag move event,
1030 // see QDragEnterEvent documentation.
1031 if (m_dragTarget)
1032 QGuiApplication::forwardEvent(m_dragTarget, &translated, event);
1033 }
1034 }
1035 event->setAccepted(translated.isAccepted());
1036 event->setDropAction(translated.dropAction());
1037 }
1038}
1039
1040void QWidgetWindow::handleDragLeaveEvent(QDragLeaveEvent *event)
1041{
1042 if (m_dragTarget) {
1043 QWidget *dragTarget = m_dragTarget;
1044 m_dragTarget = nullptr;
1045 QGuiApplication::forwardEvent(dragTarget, event);
1046 }
1047}
1048
1049void QWidgetWindow::handleDropEvent(QDropEvent *event)
1050{
1051 if (Q_UNLIKELY(m_dragTarget.isNull())) {
1052 qWarning() << m_widget << ": No drag target set.";
1053 event->ignore();
1054 return;
1055 }
1056 const QPoint mapped = m_dragTarget->mapFromGlobal(m_widget->mapToGlobal(event->position().toPoint()));
1057 QDropEvent translated(mapped, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1058 QWidget *dragTarget = m_dragTarget;
1059 m_dragTarget = nullptr;
1060 QGuiApplication::forwardEvent(dragTarget, &translated, event);
1061 event->setAccepted(translated.isAccepted());
1062 event->setDropAction(translated.dropAction());
1063}
1064
1065#endif // QT_CONFIG(draganddrop)
1066
1067void QWidgetWindow::handleExposeEvent(QExposeEvent *event)
1068{
1069 if (m_widget->testAttribute(Qt::WA_DontShowOnScreen))
1070 return; // Ignore for widgets that fake exposure
1071
1072 QWidgetPrivate *wPriv = m_widget->d_func();
1073 const bool exposed = isExposed();
1074
1075 // We might get an expose event from the platform as part of
1076 // closing the window from ~QWidget, to support animated close
1077 // transitions. But at that point we no longer have a widget
1078 // subclass to draw a new frame, so skip the expose event.
1079 if (exposed && wPriv->data.in_destructor)
1080 return;
1081
1082 if (wPriv->childrenHiddenByWState) {
1083 // If widgets has been previously hidden by window state change event
1084 // and they aren't yet shown...
1085 if (exposed) {
1086 // If the window becomes exposed...
1087 if (!wPriv->childrenShownByExpose) {
1088 // ... and they haven't been shown by this function yet - show it.
1089 wPriv->showChildren(true);
1090 QShowEvent showEvent;
1091 QCoreApplication::forwardEvent(m_widget, &showEvent, event);
1092 wPriv->childrenShownByExpose = true;
1093 }
1094 } else {
1095 // If the window becomes not exposed...
1096 if (wPriv->childrenShownByExpose) {
1097 // ... and child widgets was previously shown by the expose event - hide widgets again.
1098 // This is a workaround, because sometimes when window is minimized programmatically,
1099 // the QPA can notify that the window is exposed after changing window state to minimized
1100 // and then, the QPA can send next expose event with null exposed region (not exposed).
1101 wPriv->hideChildren(true);
1102 QHideEvent hideEvent;
1103 QCoreApplication::forwardEvent(m_widget, &hideEvent, event);
1104 wPriv->childrenShownByExpose = false;
1105 }
1106 }
1107 }
1108
1109 if (exposed) {
1110 // QTBUG-39220, QTBUG-58575: set all (potentially fully obscured parent widgets) mapped.
1111 m_widget->setAttribute(Qt::WA_Mapped);
1112 for (QWidget *p = m_widget->parentWidget(); p && !p->testAttribute(Qt::WA_Mapped); p = p->parentWidget())
1113 p->setAttribute(Qt::WA_Mapped);
1114 if (!event->m_region.isNull())
1115 wPriv->syncBackingStore(event->m_region);
1116 } else {
1117 m_widget->setAttribute(Qt::WA_Mapped, false);
1118 }
1119}
1120
1121void QWidgetWindow::handleWindowStateChangedEvent(QWindowStateChangeEvent *event)
1122{
1123 // QWindow does currently not know 'active'.
1124 Qt::WindowStates eventState = event->oldState();
1125 Qt::WindowStates widgetState = m_widget->windowState();
1126 Qt::WindowStates windowState = windowStates();
1127 if (widgetState & Qt::WindowActive)
1128 eventState |= Qt::WindowActive;
1129
1130 // Determine the new widget state, remember maximized/full screen
1131 // during minimized.
1132 if (windowState & Qt::WindowMinimized) {
1133 widgetState |= Qt::WindowMinimized;
1134 } else {
1135 widgetState = windowState | (widgetState & Qt::WindowActive);
1136 if (windowState) // Maximized or FullScreen
1137 updateNormalGeometry();
1138 }
1139
1140 // Sent event if the state changed (that is, it is not triggered by
1141 // QWidget::setWindowState(), which also sends an event to the widget).
1142 if (widgetState != Qt::WindowStates::Int(m_widget->data->window_state)) {
1143 m_widget->data->window_state = uint(widgetState);
1144 QWindowStateChangeEvent widgetEvent(eventState);
1145 QGuiApplication::forwardEvent(m_widget, &widgetEvent, event);
1146 }
1147}
1148
1149bool QWidgetWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
1150{
1151 return m_widget->nativeEvent(eventType, message, result);
1152}
1153
1154#if QT_CONFIG(tabletevent)
1155void QWidgetWindow::handleTabletEvent(QTabletEvent *event)
1156{
1157 static QPointer<QWidget> qt_tablet_target = nullptr;
1158
1159 QWidget *widget = qt_tablet_target;
1160
1161 if (!widget) {
1162 widget = m_widget->childAt(event->position());
1163 if (!widget)
1164 widget = m_widget;
1165 if (event->type() == QEvent::TabletPress)
1166 qt_tablet_target = widget;
1167 }
1168
1169 if (widget) {
1170 QPointF delta = event->globalPosition() - event->globalPosition().toPoint();
1171 QPointF mapped = widget->mapFromGlobal(event->globalPosition().toPoint()) + delta;
1172 QTabletEvent ev(event->type(), event->pointingDevice(), mapped, event->globalPosition(),
1173 event->pressure(), event->xTilt(), event->yTilt(), event->tangentialPressure(),
1174 event->rotation(), event->z(), event->modifiers(), event->button(), event->buttons());
1175 ev.setTimestamp(event->timestamp());
1176 ev.setAccepted(false);
1177 QGuiApplication::forwardEvent(widget, &ev, event);
1178 event->setAccepted(ev.isAccepted());
1179 }
1180
1181 if (event->type() == QEvent::TabletRelease && event->buttons() == Qt::NoButton)
1182 qt_tablet_target = nullptr;
1183}
1184#endif // QT_CONFIG(tabletevent)
1185
1186#ifndef QT_NO_GESTURES
1187void QWidgetWindow::handleGestureEvent(QNativeGestureEvent *e)
1188{
1189 // copy-pasted code to find correct widget follows:
1190 QObject *receiver = nullptr;
1191 if (auto *popup = QApplication::activePopupWidget()) {
1192 QWidget *popupFocusWidget = popup->focusWidget();
1193 receiver = popupFocusWidget ? popupFocusWidget : popup;
1194 }
1195 if (!receiver)
1196 receiver = QApplication::widgetAt(e->globalPosition().toPoint());
1197 if (!receiver)
1198 receiver = m_widget; // last resort
1199
1200 QApplication::forwardEvent(receiver, e);
1201}
1202#endif // QT_NO_GESTURES
1203
1204#ifndef QT_NO_CONTEXTMENU
1205void QWidgetWindow::handleContextMenuEvent(QContextMenuEvent *e)
1206{
1207 QWidget *receiver = qt_last_mouse_receiver.get();
1208 QPoint pos = e->pos();
1209 QPoint globalPos = e->globalPos();
1210
1211 // Keyboard-originating context menu events are delivered to the focus widget,
1212 // independently of event position.
1213 if (e->reason() == QContextMenuEvent::Keyboard) {
1214 receiver = QWidget::keyboardGrabber();
1215 if (!receiver) {
1216 if (QApplication::activePopupWidget()) {
1217 receiver = (QApplication::activePopupWidget()->focusWidget()
1218 ? QApplication::activePopupWidget()->focusWidget()
1219 : QApplication::activePopupWidget());
1220 } else if (QApplication::focusWidget()) {
1221 receiver = QApplication::focusWidget();
1222 } else {
1223 receiver = m_widget;
1224 }
1225 }
1226 if (Q_LIKELY(receiver)) {
1227 pos = receiver->inputMethodQuery(Qt::ImCursorRectangle).toRect().center();
1228 globalPos = receiver->mapToGlobal(pos);
1229 }
1230 } else if (Q_LIKELY(receiver)) {
1231 pos = receiver->mapFromGlobal(e->globalPos());
1232 }
1233
1234 if (receiver && receiver->isEnabled()) {
1235 QContextMenuEvent widgetEvent(e->reason(), pos, globalPos, e->modifiers());
1236 QGuiApplication::forwardEvent(receiver, &widgetEvent, e);
1237 }
1238}
1239#endif // QT_NO_CONTEXTMENU
1240
1241void QWidgetWindow::updateObjectName()
1242{
1243 QString name = m_widget->objectName();
1244 if (name.isEmpty())
1245 name = QString::fromUtf8(m_widget->metaObject()->className()) + "Class"_L1;
1246 name += "Window"_L1;
1247 setObjectName(name);
1248}
1249
1250QT_END_NAMESPACE
1251
1252#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
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