diff options
Diffstat (limited to 'src/widgets/kernel/qwidget.cpp')
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 3889 |
1 files changed, 2201 insertions, 1688 deletions
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index bf339ca5c5..365323933b 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1,48 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qapplication.h" #include "qapplication_p.h" #include "qbrush.h" #include "qcursor.h" -#include "qdesktopwidget_p.h" +#include "private/qduplicatetracker_p.h" #include "qevent.h" #include "qlayout.h" #if QT_CONFIG(menu) @@ -58,20 +22,16 @@ #include "qwidget.h" #include "qstyleoption.h" #include "qstylehints.h" -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) # include "qaccessible.h" #endif -#if 0 // Used to be included in Qt4 for Q_WS_MAC -# include "qt_mac_p.h" -# include "qt_cocoa_helpers_mac_p.h" -# include "qmainwindow.h" -# include "qtoolbar.h" -# include <private/qmainwindowlayout_p.h> -#endif #include <qpa/qplatformwindow.h> +#include <qpa/qplatformwindow_p.h> #include "private/qwidgetwindow_p.h" #include "qpainter.h" +#if QT_CONFIG(tooltip) #include "qtooltip.h" +#endif #if QT_CONFIG(whatsthis) #include "qwhatsthis.h" #endif @@ -79,25 +39,22 @@ #include "private/qstylesheetstyle_p.h" #include "private/qstyle_p.h" #include "qfileinfo.h" +#include "qscopeguard.h" #include <QtGui/private/qhighdpiscaling_p.h> #include <QtGui/qinputmethod.h> -#include <QtGui/qopenglcontext.h> -#include <QtGui/private/qopenglcontext_p.h> -#include <QtGui/qoffscreensurface.h> #if QT_CONFIG(graphicseffect) #include <private/qgraphicseffect_p.h> #endif #include <qbackingstore.h> -#include <private/qwidgetbackingstore_p.h> -#if 0 // Used to be included in Qt4 for Q_WS_MAC -# include <private/qpaintengine_mac_p.h> -#endif +#include <private/qwidgetrepaintmanager_p.h> #include <private/qpaintengine_raster_p.h> #include "qwidget_p.h" #include <QtGui/private/qwindow_p.h> -#include "qaction_p.h" +#if QT_CONFIG(action) +# include "QtGui/private/qaction_p.h" +#endif #include "qlayout_p.h" #if QT_CONFIG(graphicsview) #include "QtWidgets/qgraphicsproxywidget.h" @@ -118,16 +75,23 @@ #include "qwindowcontainer_p.h" -#include <QtPlatformHeaders/qxcbwindowfunctions.h> - -// widget/widget data creation count -//#define QWIDGET_EXTRA_DEBUG -//#define ALIEN_DEBUG +#include <sstream> QT_BEGIN_NAMESPACE -#if 0 // Used to be included in Qt4 for Q_WS_MAC -bool qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; +using namespace QNativeInterface::Private; +using namespace Qt::StringLiterals; + +Q_LOGGING_CATEGORY(lcWidgetPainting, "qt.widgets.painting", QtWarningMsg); +Q_LOGGING_CATEGORY(lcWidgetShowHide, "qt.widgets.showhide", QtWarningMsg); +Q_LOGGING_CATEGORY(lcWidgetWindow, "qt.widgets.window", QtWarningMsg); +Q_LOGGING_CATEGORY(lcWidgetFocus, "qt.widgets.focus") + +#ifndef QT_NO_DEBUG_STREAM +namespace { + struct WidgetAttributes { const QWidget *widget; }; + QDebug operator<<(QDebug debug, const WidgetAttributes &attributes); +} #endif static inline bool qRectIntersects(const QRect &r1, const QRect &r2) @@ -136,119 +100,29 @@ static inline bool qRectIntersects(const QRect &r1, const QRect &r2) qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom())); } -#if 0 // Used to be included in Qt4 for Q_WS_MAC -# define QT_NO_PAINT_DEBUG -#endif - extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); // qapplication.cpp -extern QDesktopWidget *qt_desktopWidget; // qapplication.cpp - -/*! - \internal - \class QWidgetBackingStoreTracker - \brief Class which allows tracking of which widgets are using a given backing store - - QWidgetBackingStoreTracker is a thin wrapper around a QWidgetBackingStore pointer, - which maintains a list of the QWidgets which are currently using the backing - store. This list is modified via the registerWidget and unregisterWidget functions. - */ - -QWidgetBackingStoreTracker::QWidgetBackingStoreTracker() - : m_ptr(0) -{ - -} - -QWidgetBackingStoreTracker::~QWidgetBackingStoreTracker() -{ - delete m_ptr; -} - -/*! - \internal - Destroy the contained QWidgetBackingStore, if not null, and clear the list of - widgets using the backing store, then create a new QWidgetBackingStore, providing - the QWidget. - */ -void QWidgetBackingStoreTracker::create(QWidget *widget) -{ - destroy(); - m_ptr = new QWidgetBackingStore(widget); -} - -/*! - \internal - Destroy the contained QWidgetBackingStore, if not null, and clear the list of - widgets using the backing store. - */ -void QWidgetBackingStoreTracker::destroy() -{ - delete m_ptr; - m_ptr = 0; - m_widgets.clear(); -} - -/*! - \internal - Add the widget to the list of widgets currently using the backing store. - If the widget was already in the list, this function is a no-op. - */ -void QWidgetBackingStoreTracker::registerWidget(QWidget *w) -{ - Q_ASSERT(m_ptr); - Q_ASSERT(w->internalWinId()); - Q_ASSERT(qt_widget_private(w)->maybeBackingStore() == m_ptr); - m_widgets.insert(w); -} - -/*! - \internal - Remove the widget from the list of widgets currently using the backing store. - If the widget was in the list, and removing it causes the list to be empty, - the backing store is deleted. - If the widget was not in the list, this function is a no-op. - */ -void QWidgetBackingStoreTracker::unregisterWidget(QWidget *w) -{ - if (m_widgets.remove(w) && m_widgets.isEmpty()) { - delete m_ptr; - m_ptr = 0; - } -} - -/*! - \internal - Recursively remove widget and all of its descendents. - */ -void QWidgetBackingStoreTracker::unregisterWidgetSubtree(QWidget *widget) -{ - unregisterWidget(widget); - foreach (QObject *child, widget->children()) - if (QWidget *childWidget = qobject_cast<QWidget *>(child)) - unregisterWidgetSubtree(childWidget); -} QWidgetPrivate::QWidgetPrivate(int version) : QObjectPrivate(version) - , extra(0) - , focus_next(0) - , focus_prev(0) - , focus_child(0) - , layout(0) - , needsFlush(0) - , redirectDev(0) - , widgetItem(0) - , extraPaintEngine(0) - , polished(0) - , graphicsEffect(0) + , focus_next(nullptr) + , focus_prev(nullptr) + , focus_child(nullptr) + , layout(nullptr) + , needsFlush(nullptr) + , redirectDev(nullptr) + , widgetItem(nullptr) + , extraPaintEngine(nullptr) + , polished(nullptr) + , graphicsEffect(nullptr) #if !defined(QT_NO_IM) , imHints(Qt::ImhNone) #endif -#ifndef QT_NO_TOOLTIP +#if QT_CONFIG(tooltip) , toolTipDuration(-1) #endif , directFontResolveMask(0) , inheritedFontResolveMask(0) + , directPaletteResolveMask(0) , inheritedPaletteResolveMask(0) , leftmargin(0) , topmargin(0) @@ -258,7 +132,7 @@ QWidgetPrivate::QWidgetPrivate(int version) , topLayoutItemMargin(0) , rightLayoutItemMargin(0) , bottomLayoutItemMargin(0) - , hd(0) + , hd(nullptr) , size_policy(QSizePolicy::Preferred, QSizePolicy::Preferred) , fg_role(QPalette::NoRole) , bg_role(QPalette::NoRole) @@ -275,26 +149,13 @@ QWidgetPrivate::QWidgetPrivate(int version) #ifndef QT_NO_IM , inheritsInputMethodHints(0) #endif -#ifndef QT_NO_OPENGL , renderToTextureReallyDirty(1) - , renderToTextureComposeActive(0) -#endif + , usesRhiFlush(0) , childrenHiddenByWState(0) , childrenShownByExpose(0) #if defined(Q_OS_WIN) , noPaintOnScreen(0) #endif -#if 0 // Used to be included in Qt4 for Q_WS_X11 - , picture(0) -#elif 0 // Used to be included in Qt4 for Q_WS_WIN - #ifndef QT_NO_GESTURES - , nativeGesturePanEnabled(0) - #endif -#elif 0 // Used to be included in Qt4 for Q_WS_MAC - , needWindowChange(0) - , window_event(0) - , qd_hd(0) -#endif { if (Q_UNLIKELY(!qApp)) { qFatal("QWidget: Must construct a QApplication before a QWidget"); @@ -311,18 +172,9 @@ QWidgetPrivate::QWidgetPrivate(int version) version, QObjectPrivateVersion); #endif - isWidget = true; + willBeWidget = true; // used in QObject's ctor memset(high_attributes, 0, sizeof(high_attributes)); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - drawRectOriginalAdded = false; - originalDrawMethod = true; - changeMethods = false; - isInUnifiedToolbar = false; - unifiedSurface = 0; - toolbar_ancestor = 0; - flushRequested = false; - touchEventsEnabled = false; -#endif + #ifdef QWIDGET_EXTRA_DEBUG static int count = 0; qDebug() << "widgets" << ++count; @@ -333,7 +185,7 @@ QWidgetPrivate::QWidgetPrivate(int version) QWidgetPrivate::~QWidgetPrivate() { if (widgetItem) - widgetItem->wid = 0; + widgetItem->wid = nullptr; if (extra) deleteExtra(); @@ -358,7 +210,7 @@ void QWidgetPrivate::scrollChildren(int dx, int dy) w->d_func()->setWSGeometry(); w->d_func()->setDirtyOpaqueRegion(); QMoveEvent e(r.topLeft(), oldp); - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } } } @@ -392,7 +244,7 @@ QPointer<QWidget> QWidgetPrivate::editingWidget; This feature is only available in Qt for Embedded Linux. - \sa setEditFocus(), QApplication::keypadNavigationEnabled() + \sa setEditFocus(), QApplication::navigationMode() */ bool QWidget::hasEditFocus() const { @@ -412,7 +264,7 @@ bool QWidget::hasEditFocus() const This feature is only available in Qt for Embedded Linux. - \sa hasEditFocus(), QApplication::keypadNavigationEnabled() + \sa hasEditFocus(), QApplication::navigationMode() */ void QWidget::setEditFocus(bool on) { @@ -434,13 +286,13 @@ void QWidget::setEditFocus(bool on) if (!on && QWidgetPrivate::editingWidget == f) { QWidgetPrivate::editingWidget = 0; QEvent event(QEvent::LeaveEditFocus); - QApplication::sendEvent(f, &event); - QApplication::sendEvent(f->style(), &event); + QCoreApplication::sendEvent(f, &event); + QCoreApplication::sendEvent(f->style(), &event); } else if (on) { QWidgetPrivate::editingWidget = f; QEvent event(QEvent::EnterEditFocus); - QApplication::sendEvent(f, &event); - QApplication::sendEvent(f->style(), &event); + QCoreApplication::sendEvent(f, &event); + QCoreApplication::sendEvent(f->style(), &event); } } #endif @@ -505,25 +357,25 @@ void QWidget::setAutoFillBackground(bool enabled) A widget that is not embedded in a parent widget is called a window. Usually, windows have a frame and a title bar, although it is also possible to create windows without such decoration using suitable - \l{Qt::WindowFlags}{window flags}). In Qt, QMainWindow and the various + \l{Qt::WindowFlags}{window flags}. In Qt, QMainWindow and the various subclasses of QDialog are the most common window types. Every widget's constructor accepts one or two standard arguments: \list 1 - \li \c{QWidget *parent = 0} is the parent of the new widget. If it is 0 - (the default), the new widget will be a window. If not, it will be - a child of \e parent, and be constrained by \e parent's geometry - (unless you specify Qt::Window as window flag). - \li \c{Qt::WindowFlags f = 0} (where available) sets the window flags; - the default is suitable for almost all widgets, but to get, for + \li \c{QWidget *parent = nullptr} is the parent of the new widget. + If it is \nullptr (the default), the new widget will be a window. + If not, it will be a child of \e parent, and be constrained by + \e parent's geometry (unless you specify Qt::Window as window flag). + \li \c{Qt::WindowFlags f = { }} (where available) sets the window flags; + the default is suitable for most widgets, but to get, for example, a window without a window system frame, you must use special flags. \endlist QWidget has many member functions, but some of them have little direct functionality; for example, QWidget has a font property, but never uses - this itself. There are many subclasses which provide real functionality, + this itself. There are many subclasses that provide real functionality, such as QLabel, QPushButton, QListWidget, and QTabWidget. @@ -531,7 +383,7 @@ void QWidget::setAutoFillBackground(bool enabled) A widget without a parent widget is always an independent window (top-level widget). For these widgets, setWindowTitle() and setWindowIcon() set the - title bar and icon respectively. + title bar and icon, respectively. Non-window widgets are child widgets, displayed within their parent widgets. Most widgets in Qt are mainly useful as child widgets. For @@ -544,7 +396,7 @@ void QWidget::setAutoFillBackground(bool enabled) widgets in a layout provided by QGridLayout. The QLabel child widgets have been outlined to indicate their full sizes. - If you want to use a QWidget to hold child widgets you will usually want to + If you want to use a QWidget to hold child widgets, you will usually want to add a layout to the parent QWidget. See \l{Layout Management} for more information. @@ -554,14 +406,13 @@ void QWidget::setAutoFillBackground(bool enabled) When a widget is used as a container to group a number of child widgets, it is known as a composite widget. These can be created by constructing a widget with the required visual properties - a QFrame, for example - and - adding child widgets to it, usually managed by a layout. The above diagram - shows such a composite widget that was created using Qt Designer. + adding child widgets to it, usually managed by a layout. Composite widgets can also be created by subclassing a standard widget, such as QWidget or QFrame, and adding the necessary layout and child widgets in the constructor of the subclass. Many of the \l{Qt Widgets Examples} {examples provided with Qt} use this approach, and it is also covered in - the Qt \l{Tutorials}. + the Qt \l{Widgets Tutorial}. \section1 Custom Widgets and Painting @@ -575,7 +426,7 @@ void QWidget::setAutoFillBackground(bool enabled) Each widget performs all painting operations from within its paintEvent() function. This is called whenever the widget needs to be redrawn, either - as a result of some external change or when requested by the application. + because of some external change or when requested by the application. The \l{widgets/analogclock}{Analog Clock example} shows how a simple widget can handle paint events. @@ -587,7 +438,7 @@ void QWidget::setAutoFillBackground(bool enabled) sizeHint() to provide a reasonable default size for the widget and to set the correct size policy with setSizePolicy(). - By default, composite widgets which do not provide a size hint will be + By default, composite widgets that do not provide a size hint will be sized according to the space requirements of their child widgets. The size policy lets you supply good default behavior for the layout @@ -607,9 +458,9 @@ void QWidget::setAutoFillBackground(bool enabled) delivers events to widgets by calling specific event handler functions with instances of QEvent subclasses containing information about each event. - If your widget only contains child widgets, you probably do not need to + If your widget only contains child widgets, you probably don't need to implement any event handlers. If you want to detect a mouse click in a - child widget call the child's underMouse() function inside the widget's + child widget, call the child's underMouse() function inside the widget's mousePressEvent(). The \l{widgets/scribble}{Scribble example} implements a wider set of @@ -674,7 +525,7 @@ void QWidget::setAutoFillBackground(bool enabled) button is held down. This can be useful during drag and drop operations. If you call \l{setMouseTracking()}{setMouseTracking}(true), you get mouse move events even when no buttons are held down. - (See also the \l{Drag and Drop} guide.) + (See also the \l{Drag and Drop in Qt}{Drag and Drop} guide.) \li keyReleaseEvent() is called whenever a key is released and while it is held down (if the key is auto-repeating). In that case, the widget will receive a pair of key release and key press event for @@ -688,7 +539,7 @@ void QWidget::setAutoFillBackground(bool enabled) space. (This excludes screen space owned by any of the widget's children.) \li leaveEvent() is called when the mouse leaves the widget's screen - space. If the mouse enters a child widget it will not cause a + space. If the mouse enters a child widget, it will not cause a leaveEvent(). \li moveEvent() is called when the widget has been moved relative to its parent. @@ -826,12 +677,11 @@ void QWidget::setAutoFillBackground(bool enabled) \section1 Transparency and Double Buffering - Since Qt 4.0, QWidget automatically double-buffers its painting, so there + QWidget automatically double-buffers its painting, so there is no need to write double-buffering code in paintEvent() to avoid flicker. - Since Qt 4.1, the Qt::WA_ContentsPropagated widget attribute has been - deprecated. Instead, the contents of parent widgets are propagated by + The contents of parent widgets are propagated by default to each of their children as long as Qt::WA_PaintOnScreen is not set. Custom widgets can be written to take advantage of this feature by updating irregular regions (to create non-rectangular child widgets), or @@ -848,7 +698,7 @@ void QWidget::setAutoFillBackground(bool enabled) \list \li The left widget has no additional properties or widget attributes - set. This default state suits most custom widgets using + set. This default state suits most custom widgets that have transparency, are irregularly-shaped, or do not paint over their entire area with an opaque brush. \li The center widget has the \l autoFillBackground property set. This @@ -859,10 +709,7 @@ void QWidget::setAutoFillBackground(bool enabled) set. This indicates that the widget will paint over its entire area with opaque colors. The widget's area will initially be \e{uninitialized}, represented in the diagram with a red diagonal - grid pattern that shines through the overpainted area. The - Qt::WA_OpaquePaintArea attribute is useful for widgets that need to - paint their own specialized contents quickly and do not need a - default filled background. + grid pattern that shines through the overpainted area. \endlist To rapidly update custom widgets with simple background colors, such as @@ -872,19 +719,18 @@ void QWidget::setAutoFillBackground(bool enabled) implement the necessary drawing functionality in the widget's paintEvent(). To rapidly update custom widgets that constantly paint over their entire - areas with opaque content, e.g., video streaming widgets, it is better to - set the widget's Qt::WA_OpaquePaintEvent, avoiding any unnecessary overhead - associated with repainting the widget's background. + areas with opaque content, for example, video streaming widgets, it is + better to set the widget's Qt::WA_OpaquePaintEvent, avoiding any unnecessary + overhead associated with repainting the widget's background. If a widget has both the Qt::WA_OpaquePaintEvent widget attribute \e{and} the \l autoFillBackground property set, the Qt::WA_OpaquePaintEvent attribute takes precedence. Depending on your requirements, you should choose either one of them. - Since Qt 4.1, the contents of parent widgets are also propagated to - standard Qt widgets. This can lead to some unexpected results if the - parent widget is decorated in a non-standard way, as shown in the diagram - below. + The contents of parent widgets are also propagated to standard Qt widgets. + This can lead to some unexpected results if the parent widget is decorated + in a non-standard way, as shown in the diagram below. \image propagation-standard.png @@ -896,8 +742,8 @@ void QWidget::setAutoFillBackground(bool enabled) \section1 Creating Translucent Windows - Since Qt 4.5, it has been possible to create windows with translucent regions - on window systems that support compositing. + You can create windows with translucent regions on window systems that + support compositing. To enable this feature in a top-level widget, set its Qt::WA_TranslucentBackground attribute with setAttribute() and ensure that its background is painted with @@ -910,24 +756,26 @@ void QWidget::setAutoFillBackground(bool enabled) and a compositing window manager. \li Windows: The widget needs to have the Qt::FramelessWindowHint window flag set for the translucency to work. + \li \macos: The widget needs to have the Qt::FramelessWindowHint window flag set + for the translucency to work. \endlist \section1 Native Widgets vs Alien Widgets - Introduced in Qt 4.4, alien widgets are widgets unknown to the windowing - system. They do not have a native window handle associated with them. This - feature significantly speeds up widget painting, resizing, and removes flicker. + Alien widgets are widgets unknown to the windowing system. They do not have + a native window handle associated with them. This feature significantly + speeds up widget painting, resizing, and removes flicker. - Should you require the old behavior with native windows, you can choose - one of the following options: + Should you require the old behavior with native windows, choose one of the + following options: \list 1 \li Use the \c{QT_USE_NATIVE_WINDOWS=1} in your environment. \li Set the Qt::AA_NativeWindows attribute on your application. All widgets will be native widgets. \li Set the Qt::WA_NativeWindow attribute on widgets: The widget itself - and all of its ancestors will become native (unless + and all its ancestors will become native (unless Qt::WA_DontCreateNativeAncestors is set). \li Call QWidget::winId to enforce a native window (this implies 3). \li Set the Qt::WA_PaintOnScreen attribute to enforce a native window @@ -938,25 +786,9 @@ void QWidget::setAutoFillBackground(bool enabled) */ -QWidgetMapper *QWidgetPrivate::mapper = 0; // widget with wid -QWidgetSet *QWidgetPrivate::allWidgets = 0; // widgets with no wid - - -/***************************************************************************** - QWidget utility functions - *****************************************************************************/ - -QRegion qt_dirtyRegion(QWidget *widget) -{ - if (!widget) - return QRegion(); +QWidgetMapper *QWidgetPrivate::mapper = nullptr; // widget with wid +QWidgetSet *QWidgetPrivate::allWidgets = nullptr; // widgets with no wid - QWidgetBackingStore *bs = qt_widget_private(widget)->maybeBackingStore(); - if (!bs) - return QRegion(); - - return bs->dirtyRegion(widget); -} /***************************************************************************** QWidget member functions @@ -975,7 +807,6 @@ QRegion qt_dirtyRegion(QWidget *widget) \li Qt::WA_WState_InPaintEvent Currently processing a paint event. \li Qt::WA_WState_Reparented The widget has been reparented. \li Qt::WA_WState_ConfigPending A configuration (resize/move) event is pending. - \li Qt::WA_WState_DND (Deprecated) The widget supports drag and drop, see setAcceptDrops(). \endlist */ @@ -989,12 +820,7 @@ struct QWidgetExceptionCleaner Q_UNUSED(d); #else QWidgetPrivate::allWidgets->remove(that); - if (d->focus_next != that) { - if (d->focus_next) - d->focus_next->d_func()->focus_prev = d->focus_prev; - if (d->focus_prev) - d->focus_prev->d_func()->focus_next = d->focus_next; - } + d->removeFromFocusChain(); #endif } }; @@ -1003,14 +829,14 @@ struct QWidgetExceptionCleaner Constructs a widget which is a child of \a parent, with widget flags set to \a f. - If \a parent is 0, the new widget becomes a window. If + If \a parent is \nullptr, the new widget becomes a window. If \a parent is another widget, this widget becomes a child window inside \a parent. The new widget is deleted when its \a parent is deleted. The widget flags argument, \a f, is normally 0, but it can be set - to customize the frame of a window (i.e. \a - parent must be 0). To customize the frame, use a value composed + to customize the frame of a window (i.e. \a parent must be + \nullptr). To customize the frame, use a value composed from the bitwise OR of any of the \l{Qt::WindowFlags}{window flags}. If you add a child widget to an already visible widget you must @@ -1025,7 +851,7 @@ struct QWidgetExceptionCleaner \sa windowFlags */ QWidget::QWidget(QWidget *parent, Qt::WindowFlags f) - : QObject(*new QWidgetPrivate, 0), QPaintDevice() + : QObject(*new QWidgetPrivate, nullptr), QPaintDevice() { QT_TRY { d_func()->init(parent, f); @@ -1039,7 +865,7 @@ QWidget::QWidget(QWidget *parent, Qt::WindowFlags f) /*! \internal */ QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f) - : QObject(dd, 0), QPaintDevice() + : QObject(dd, nullptr), QPaintDevice() { Q_D(QWidget); QT_TRY { @@ -1083,17 +909,12 @@ void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w) // Only enable this on non-Mac platforms. Since the old way of doing this would // interpret WindowSystemMenuHint as a close button and we can't change that behavior // we can't just add this in. -#if 1 // Used to be excluded in Qt4 for Q_WS_MAC if ((flags & (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint)) # ifdef Q_OS_WIN && type != Qt::Dialog // QTBUG-2027, allow for menu-less dialogs. # endif ) { flags |= Qt::WindowSystemMenuHint; -#else - if (flags & (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint - | Qt::WindowSystemMenuHint)) { -#endif flags |= Qt::WindowTitleHint; flags &= ~Qt::FramelessWindowHint; } @@ -1104,18 +925,11 @@ void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w) flags |= Qt::WindowSystemMenuHint; flags |= Qt::WindowTitleHint; } - if (customize) - ; // don't modify window flags if the user explicitly set them. - else if (type == Qt::Dialog || type == Qt::Sheet) { + if (!customize) { // don't modify window flags if the user explicitly set them. flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; - // ### fixme: Qt 6: Never set Qt::WindowContextHelpButtonHint flag automatically - if (!QApplicationPrivate::testAttribute(Qt::AA_DisableWindowContextHelpButton)) - flags |= Qt::WindowContextHelpButtonHint; - } else if (type == Qt::Tool) - flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; - else - flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint | - Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint | Qt::WindowFullscreenButtonHint; + if (type != Qt::Dialog && type != Qt::Sheet && type != Qt::Tool) + flags |= Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint; + } if (w->testAttribute(Qt::WA_TransparentForMouseEvents)) flags |= Qt::WindowTransparentForInput; } @@ -1123,6 +937,11 @@ void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w) void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) { Q_Q(QWidget); + isWidget = true; + wasWidget = true; + + Q_ASSERT_X(q != parentWidget, Q_FUNC_INFO, "Cannot parent a QWidget to itself"); + if (Q_UNLIKELY(!qobject_cast<QApplication *>(QCoreApplication::instance()))) qFatal("QWidget: Cannot create a QWidget without QApplication"); @@ -1130,13 +949,6 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (allWidgets) allWidgets->insert(q); - int targetScreen = -1; - if (parentWidget && parentWidget->windowType() == Qt::Desktop) { - const QDesktopScreenWidget *sw = qobject_cast<const QDesktopScreenWidget *>(parentWidget); - targetScreen = sw ? sw->screenNumber() : 0; - parentWidget = 0; - } - q->data = &data; #if QT_CONFIG(thread) @@ -1146,19 +958,6 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) } #endif -#if 0 // Used to be included in Qt4 for Q_WS_X11 - if (desktopWidget) { - // make sure the widget is created on the same screen as the - // programmer specified desktop widget - xinfo = desktopWidget->d_func()->xinfo; - } -#endif - if (targetScreen >= 0) { - topData()->initialScreenIndex = targetScreen; - if (QWindow *window = q->windowHandle()) - window->setScreen(QGuiApplication::screens().value(targetScreen, nullptr)); - } - data.fstrut_dirty = true; data.winid = 0; @@ -1170,7 +969,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) data.window_modality = Qt::NonModal; data.sizehint_forced = 0; - data.is_closing = 0; + data.is_closing = false; data.in_show = 0; data.in_set_window_state = 0; data.in_destructor = false; @@ -1180,9 +979,6 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) mustHaveWindowHandle = 1; q->setAttribute(Qt::WA_NativeWindow); } -//#if 0 // Used to be included in Qt4 for Q_WS_MAC -// q->setAttribute(Qt::WA_NativeWindow); -//#endif q->setAttribute(Qt::WA_QuitOnClose); // might be cleared in adjustQuitOnCloseAttribute() adjustQuitOnCloseAttribute(); @@ -1192,7 +988,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) //give potential windows a bigger "pre-initial" size; create() will give them a new size later data.crect = parentWidget ? QRect(0,0,100,30) : QRect(0,0,640,480); - focus_next = focus_prev = q; + initFocusChain(); if ((f & Qt::WindowType_Mask) == Qt::Desktop) q->create(); @@ -1206,9 +1002,6 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) setOpaque(q->isWindow() && background.style() != Qt::NoBrush && background.isOpaque()); } data.fnt = QFont(data.fnt, q); -#if 0 // Used to be included in Qt4 for Q_WS_X11 - data.fnt.x11SetScreen(xinfo.screen()); -#endif q->setAttribute(Qt::WA_PendingMoveEvent); q->setAttribute(Qt::WA_PendingResizeEvent); @@ -1216,28 +1009,13 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (++QWidgetPrivate::instanceCounter > QWidgetPrivate::maxInstances) QWidgetPrivate::maxInstances = QWidgetPrivate::instanceCounter; - if (QApplicationPrivate::testAttribute(Qt::AA_ImmediateWidgetCreation)) // ### fixme: Qt 6: Remove AA_ImmediateWidgetCreation. - q->create(); - QEvent e(QEvent::Create); - QApplication::sendEvent(q, &e); - QApplication::postEvent(q, new QEvent(QEvent::PolishRequest)); - - extraPaintEngine = 0; + QCoreApplication::sendEvent(q, &e); + QCoreApplication::postEvent(q, new QEvent(QEvent::PolishRequest)); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // If we add a child to the unified toolbar, we have to redirect the painting. - if (parentWidget && parentWidget->d_func() && parentWidget->d_func()->isInUnifiedToolbar) { - if (parentWidget->d_func()->unifiedSurface) { - QWidget *toolbar = parentWidget->d_func()->toolbar_ancestor; - parentWidget->d_func()->unifiedSurface->recursiveRedirect(toolbar, toolbar, toolbar->d_func()->toolbar_offset); - } - } -#endif + extraPaintEngine = nullptr; } - - void QWidgetPrivate::createRecursively() { Q_Q(QWidget); @@ -1249,6 +1027,118 @@ void QWidgetPrivate::createRecursively() } } +QRhi *QWidgetPrivate::rhi() const +{ + if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) + return repaintManager->rhi(); + else + return nullptr; +} + +/*! + \internal + Returns the closest parent widget that has a QWindow window handle + + \note This behavior is different from nativeParentWidget(), which + returns the closest parent that has a QWindow window handle with + a created QPlatformWindow, and hence native window (winId). +*/ +QWidget *QWidgetPrivate::closestParentWidgetWithWindowHandle() const +{ + Q_Q(const QWidget); + QWidget *parent = q->parentWidget(); + while (parent && !parent->windowHandle()) + parent = parent->parentWidget(); + return parent; +} + +QWindow *QWidgetPrivate::windowHandle(WindowHandleMode mode) const +{ + if (mode == WindowHandleMode::Direct || mode == WindowHandleMode::Closest) { + if (QTLWExtra *x = maybeTopData()) { + if (x->window != nullptr || mode == WindowHandleMode::Direct) + return x->window; + } + } + if (mode == WindowHandleMode::Closest) { + // FIXME: Use closestParentWidgetWithWindowHandle instead + if (auto nativeParent = q_func()->nativeParentWidget()) { + if (auto window = nativeParent->windowHandle()) + return window; + } + } + if (mode == WindowHandleMode::TopLevel || mode == WindowHandleMode::Closest) { + if (auto topLevel = q_func()->topLevelWidget()) { + if (auto window = topLevel ->windowHandle()) + return window; + } + } + return nullptr; +} + +/*! + \internal + + Used by clients outside of widgets to get a handle to the + closest QWindow without having to link to widgets. +*/ +QWindow *QWidgetPrivate::_q_closestWindowHandle() const +{ + return windowHandle(QWidgetPrivate::WindowHandleMode::Closest); +} + +QScreen *QWidgetPrivate::associatedScreen() const +{ +#if QT_CONFIG(graphicsview) + // embedded widgets never have a screen associated, let QWidget::screen fall back to toplevel + if (nearestGraphicsProxyWidget(q_func())) + return nullptr; +#endif + if (auto window = windowHandle(WindowHandleMode::Closest)) + return window->screen(); + return nullptr; +} + +// finds the first rhiconfig in the hierarchy that has enable==true +static bool q_evaluateRhiConfigRecursive(const QWidget *w, QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType) +{ + QPlatformBackingStoreRhiConfig config = QWidgetPrivate::get(w)->rhiConfig(); + if (config.isEnabled()) { + if (outConfig) + *outConfig = config; + if (outType) + *outType = QBackingStoreRhiSupport::surfaceTypeForConfig(config); + return true; + } + for (const QObject *child : w->children()) { + if (const QWidget *childWidget = qobject_cast<const QWidget *>(child)) { + if (q_evaluateRhiConfigRecursive(childWidget, outConfig, outType)) + return true; + } + } + return false; +} + +bool q_evaluateRhiConfig(const QWidget *w, QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType) +{ + // First, check env.vars. or other means that force the usage of rhi-based + // flushing with a specific graphics API. This takes precedence over what + // the widgets themselves declare. This is global, applying to all + // top-levels. + if (QBackingStoreRhiSupport::checkForceRhi(outConfig, outType)) { + qCDebug(lcWidgetPainting) << "Tree with root" << w << "evaluated to forced flushing with QRhi"; + return true; + } + + // Otherwise, check the widget hierarchy to see if there is a child (or + // ourselves) that declare the need for rhi-based composition. + if (q_evaluateRhiConfigRecursive(w, outConfig, outType)) { + qCDebug(lcWidgetPainting) << "Tree with root" << w << "evaluates to flushing with QRhi"; + return true; + } + + return false; +} // ### fixme: Qt 6: Remove parameter window from QWidget::create() @@ -1310,41 +1200,20 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) if (QApplicationPrivate::testAttribute(Qt::AA_NativeWindows)) setAttribute(Qt::WA_NativeWindow); -#ifdef ALIEN_DEBUG - qDebug() << "QWidget::create:" << this << "parent:" << parentWidget() - << "Alien?" << !testAttribute(Qt::WA_NativeWindow); -#endif - -#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ && QT_CONFIG(draganddrop) - // Unregister the dropsite (if already registered) before we - // re-create the widget with a native window. - if (testAttribute(Qt::WA_WState_Created) && !internalWinId() && testAttribute(Qt::WA_NativeWindow) - && d->extra && d->extra->dropTarget) { - d->registerDropSite(false); - } -#endif - d->updateIsOpaque(); setAttribute(Qt::WA_WState_Created); // set created flag d->create(); - // a real toplevel window needs a backing store - if (isWindow() && windowType() != Qt::Desktop) { - d->topData()->backingStoreTracker.destroy(); - d->topData()->backingStoreTracker.create(this); - } + // A real toplevel window needs a paint manager + if (isWindow() && windowType() != Qt::Desktop) + d->topData()->repaintManager.reset(new QWidgetRepaintManager(this)); d->setModal_sys(); if (!isWindow() && parentWidget() && parentWidget()->testAttribute(Qt::WA_DropSiteRegistered)) setAttribute(Qt::WA_DropSiteRegistered, true); -#ifdef QT_EVAL - extern void qt_eval_init_widget(QWidget *w); - qt_eval_init_widget(this); -#endif - // need to force the resting of the icon after changing parents if (testAttribute(Qt::WA_SetWindowIcon)) d->setWindowIcon_sys(); @@ -1408,6 +1277,7 @@ void QWidgetPrivate::create() // in case the extra was already valid. if (!win) { createTLSysExtra(); + Q_ASSERT(topData()->window); win = topData()->window; } @@ -1419,7 +1289,7 @@ void QWidgetPrivate::create() Qt::WindowFlags &flags = data.window_flags; -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) +#if defined(QT_PLATFORM_UIKIT) if (q->testAttribute(Qt::WA_ContentsMarginsRespectsSafeArea)) flags |= Qt::MaximizeUsingFullscreenGeometryHint; #endif @@ -1428,7 +1298,6 @@ void QWidgetPrivate::create() win->setProperty("_q_showWithoutActivating", QVariant(true)); if (q->testAttribute(Qt::WA_MacAlwaysShowToolWindow)) win->setProperty("_q_macAlwaysShowToolWindow", QVariant(true)); - setNetWmWindowTypes(true); // do nothing if none of WA_X11NetWmWindowType* is set win->setFlags(flags); fixPosIncludesFrame(); if (q->testAttribute(Qt::WA_Moved) @@ -1437,13 +1306,13 @@ void QWidgetPrivate::create() else win->resize(q->size()); if (win->isTopLevel()) { - int screenNumber = topData()->initialScreenIndex; - topData()->initialScreenIndex = -1; - if (screenNumber < 0) { - screenNumber = q->windowType() != Qt::Desktop - ? QDesktopWidgetPrivate::screenNumber(q) : 0; + QScreen *targetScreen = topData()->initialScreen; + topData()->initialScreen = nullptr; + if (!targetScreen) { + targetScreen = q->windowType() != Qt::Desktop + ? q->screen() : nullptr; } - win->setScreen(QGuiApplication::screens().value(screenNumber, nullptr)); + win->setScreen(targetScreen); } QSurfaceFormat format = win->requestedFormat(); @@ -1457,9 +1326,9 @@ void QWidgetPrivate::create() if (nativeParent->windowHandle()) { if (flags & Qt::Window) { win->setTransientParent(nativeParent->window()->windowHandle()); - win->setParent(0); + win->setParent(nullptr); } else { - win->setTransientParent(0); + win->setTransientParent(nullptr); win->setParent(nativeParent->windowHandle()); } } @@ -1476,21 +1345,30 @@ void QWidgetPrivate::create() } data.window_flags = win->flags(); - if (!win->isTopLevel()) // In a Widget world foreign windows can only be top level - data.window_flags &= ~Qt::ForeignWindow; - if (!topData()->role.isNull()) - QXcbWindowFunctions::setWmWindowRole(win, topData()->role.toLatin1()); +#if QT_CONFIG(xcb) + if (!topData()->role.isNull()) { + if (auto *xcbWindow = dynamic_cast<QXcbWindow*>(win->handle())) + xcbWindow->setWindowRole(topData()->role); + } +#endif QBackingStore *store = q->backingStore(); + usesRhiFlush = false; if (!store) { if (q->windowType() != Qt::Desktop) { - if (q->isTopLevel()) + if (q->isWindow()) { q->setBackingStore(new QBackingStore(win)); + QPlatformBackingStoreRhiConfig rhiConfig; + usesRhiFlush = q_evaluateRhiConfig(q, &rhiConfig, nullptr); + topData()->backingStore->handle()->setRhiConfig(rhiConfig); + } } else { q->setAttribute(Qt::WA_PaintOnScreen, true); } + } else if (win->handle()) { + usesRhiFlush = q_evaluateRhiConfig(q, nullptr, nullptr); } setWindowModified_helper(); @@ -1501,6 +1379,7 @@ void QWidgetPrivate::create() Q_ASSERT(id != WId(0)); setWinId(id); } + setNetWmWindowTypes(true); // do nothing if none of WA_X11NetWmWindowType* is set // Check children and create windows for them if necessary q_createNativeChildrenAndSetParent(q); @@ -1544,6 +1423,8 @@ void QWidgetPrivate::createTLSysExtra() #endif if (isTipLabel || isAlphaWidget || q->inherits("QRollEffect")) qt_window_private(extra->topextra->window)->setAutomaticPositionAndResizeEnabled(false); + + updateIsTranslucent(); } } @@ -1576,9 +1457,9 @@ QWidget::~QWidget() #ifndef QT_NO_ACTION // remove all actions from this widget - for (int i = 0; i < d->actions.size(); ++i) { - QActionPrivate *apriv = d->actions.at(i)->d_func(); - apriv->widgets.removeAll(this); + for (auto action : std::as_const(d->actions)) { + QActionPrivate *apriv = action->d_func(); + apriv->associatedObjects.removeAll(this); } d->actions.clear(); #endif @@ -1587,23 +1468,15 @@ QWidget::~QWidget() // Remove all shortcuts grabbed by this // widget, unless application is closing if (!QApplicationPrivate::is_app_closing && testAttribute(Qt::WA_GrabbedShortcut)) - qApp->d_func()->shortcutMap.removeShortcut(0, this, QKeySequence()); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, this, QKeySequence()); #endif // delete layout while we still are a valid widget delete d->layout; - d->layout = 0; - // Remove myself from focus list - - Q_ASSERT(d->focus_next->d_func()->focus_prev == this); - Q_ASSERT(d->focus_prev->d_func()->focus_next == this); - - if (d->focus_next != this) { - d->focus_next->d_func()->focus_prev = d->focus_prev; - d->focus_prev->d_func()->focus_next = d->focus_next; - d->focus_next = d->focus_prev = 0; - } + d->layout = nullptr; + // Remove this from focus list + d->removeFromFocusChain(QWidgetPrivate::FocusChainRemovalRule::AssertConsistency); QT_TRY { #if QT_CONFIG(graphicsview) @@ -1611,7 +1484,7 @@ QWidget::~QWidget() while (w->d_func()->extra && w->d_func()->extra->focus_proxy) w = w->d_func()->extra->focus_proxy; QWidget *window = w->window(); - QWExtra *e = window ? window->d_func()->extra : 0; + QWExtra *e = window ? window->d_func()->extra.get() : nullptr ; if (!e || !e->proxyWidget || (w->parentWidget() && w->parentWidget()->d_func()->focus_child == this)) #endif clearFocus(); @@ -1623,7 +1496,7 @@ QWidget::~QWidget() if (isWindow() && isVisible() && internalWinId()) { QT_TRY { - d->close_helper(QWidgetPrivate::CloseNoEvent); + d->close(); } QT_CATCH(...) { // if we're out of memory, at least hide the window. QT_TRY { @@ -1632,25 +1505,18 @@ QWidget::~QWidget() // and if that also doesn't work, then give up } } - } - -#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ || 0 /* Used to be included in Qt4 for Q_WS_X11 */|| 0 /* Used to be included in Qt4 for Q_WS_MAC */ - else if (!internalWinId() && isVisible()) { - qApp->d_func()->sendSyntheticEnterLeave(this); - } -#endif - else if (isVisible()) { + } else if (isVisible()) { qApp->d_func()->sendSyntheticEnterLeave(this); } - if (QWidgetBackingStore *bs = d->maybeBackingStore()) { - bs->removeDirtyWidget(this); + if (QWidgetRepaintManager *repaintManager = d->maybeRepaintManager()) { + repaintManager->removeDirtyWidget(this); if (testAttribute(Qt::WA_StaticContents)) - bs->removeStaticWidget(this); + repaintManager->removeStaticWidget(this); } delete d->needsFlush; - d->needsFlush = 0; + d->needsFlush = nullptr; // The next 20 lines are duplicated from QObject, but required here // since QWidget deletes is children itself @@ -1670,32 +1536,18 @@ QWidget::~QWidget() if (d->declarativeData) { d->wasDeleted = true; // needed, so that destroying the declarative data does the right thing - if (static_cast<QAbstractDeclarativeDataImpl*>(d->declarativeData)->ownedByQml1) { - if (QAbstractDeclarativeData::destroyed_qml1) - QAbstractDeclarativeData::destroyed_qml1(d->declarativeData, this); - } else { - if (QAbstractDeclarativeData::destroyed) - QAbstractDeclarativeData::destroyed(d->declarativeData, this); - } - d->declarativeData = 0; // don't activate again in ~QObject + if (QAbstractDeclarativeData::destroyed) + QAbstractDeclarativeData::destroyed(d->declarativeData, this); + d->declarativeData = nullptr; // don't activate again in ~QObject d->wasDeleted = false; } d->blockSig = blocked; -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // QCocoaView holds a pointer back to this widget. Clear it now - // to make sure it's not followed later on. The lifetime of the - // QCocoaView might exceed the lifetime of this widget in cases - // where Cocoa itself holds references to it. - extern void qt_mac_clearCocoaViewQWidgetPointers(QWidget *); - qt_mac_clearCocoaViewQWidgetPointers(this); -#endif - if (!d->children.isEmpty()) d->deleteChildren(); - QApplication::removePostedEvents(this); + QCoreApplication::removePostedEvents(this); QT_TRY { destroy(); // platform-dependent cleanup @@ -1717,6 +1569,8 @@ QWidget::~QWidget() #if QT_CONFIG(graphicseffect) delete d->graphicsEffect; #endif + + d->isWidget = false; } int QWidgetPrivate::instanceCounter = 0; // Current number of widget instances @@ -1725,27 +1579,18 @@ int QWidgetPrivate::maxInstances = 0; // Maximum number of widget instances void QWidgetPrivate::setWinId(WId id) // set widget identifier { Q_Q(QWidget); - // the user might create a widget with Qt::Desktop window - // attribute (or create another QDesktopWidget instance), which - // will have the same windowid (the root window id) as the - // qt_desktopWidget. We should not add the second desktop widget - // to the mapper. - bool userDesktopWidget = qt_desktopWidget != 0 && qt_desktopWidget != q && q->windowType() == Qt::Desktop; - if (mapper && data.winid && !userDesktopWidget) { + if (mapper && data.winid) { mapper->remove(data.winid); } const WId oldWinId = data.winid; data.winid = id; -#if 0 // Used to be included in Qt4 for Q_WS_X11 - hd = id; // X11: hd == ident -#endif - if (mapper && id && !userDesktopWidget) { + if (mapper && id) { mapper->insert(data.winid, q); } - if(oldWinId != id) { + if (oldWinId != id) { QEvent e(QEvent::WinIdChange); QCoreApplication::sendEvent(q, &e); } @@ -1756,26 +1601,22 @@ void QWidgetPrivate::createTLExtra() if (!extra) createExtra(); if (!extra->topextra) { - QTLWExtra* x = extra->topextra = new QTLWExtra; - x->icon = 0; - x->backingStore = 0; - x->sharedPainter = 0; + extra->topextra = std::make_unique<QTLWExtra>(); + QTLWExtra* x = extra->topextra.get(); + x->backingStore = nullptr; + x->sharedPainter = nullptr; x->incw = x->inch = 0; x->basew = x->baseh = 0; x->frameStrut.setCoords(0, 0, 0, 0); x->normalGeometry = QRect(0,0,-1,-1); - x->savedFlags = 0; + x->savedFlags = { }; x->opacity = 255; x->posIncludesFrame = 0; x->sizeAdjusted = false; - x->inTopLevelResize = false; x->embedded = 0; - x->window = 0; - x->shareContext = 0; - x->initialScreenIndex = -1; -#if 0 // Used to be included in Qt4 for Q_WS_MAC - x->wasMaximized = false; -#endif + x->window = nullptr; + x->initialScreen = nullptr; + #ifdef QWIDGET_EXTRA_DEBUG static int count = 0; qDebug() << "tlextra" << ++count; @@ -1791,14 +1632,10 @@ void QWidgetPrivate::createTLExtra() void QWidgetPrivate::createExtra() { if (!extra) { // if not exists - extra = new QWExtra; - extra->glContext = 0; - extra->topextra = 0; + extra = std::make_unique<QWExtra>(); + extra->glContext = nullptr; #if QT_CONFIG(graphicsview) - extra->proxyWidget = 0; -#endif -#ifndef QT_NO_CURSOR - extra->curs = 0; + extra->proxyWidget = nullptr; #endif extra->minw = 0; extra->minh = 0; @@ -1833,24 +1670,17 @@ void QWidgetPrivate::createSysExtra() void QWidgetPrivate::deleteExtra() { if (extra) { // if exists -#ifndef QT_NO_CURSOR - delete extra->curs; -#endif deleteSysExtra(); #ifndef QT_NO_STYLE_STYLESHEET // dereference the stylesheet style if (QStyleSheetStyle *proxy = qt_styleSheet(extra->style)) proxy->deref(); #endif - if (extra->topextra) { + if (extra->topextra) deleteTLSysExtra(); - // extra->topextra->backingStore destroyed in QWidgetPrivate::deleteTLSysExtra() - delete extra->topextra->icon; - delete extra->topextra; - } - delete extra; + // extra->xic destroyed in QWidget::destroy() - extra = 0; + extra.reset(); } } @@ -1858,39 +1688,11 @@ void QWidgetPrivate::deleteSysExtra() { } -static void deleteBackingStore(QWidgetPrivate *d) -{ - QTLWExtra *topData = d->topData(); - - delete topData->backingStore; - topData->backingStore = 0; -} - void QWidgetPrivate::deleteTLSysExtra() { if (extra && extra->topextra) { - //the qplatformbackingstore may hold a reference to the window, so the backingstore - //needs to be deleted first. - - extra->topextra->backingStoreTracker.destroy(); - deleteBackingStore(this); -#ifndef QT_NO_OPENGL - qDeleteAll(extra->topextra->widgetTextures); - extra->topextra->widgetTextures.clear(); - delete extra->topextra->shareContext; - extra->topextra->shareContext = 0; -#endif - - //the toplevel might have a context with a "qglcontext associated with it. We need to - //delete the qglcontext before we delete the qplatformopenglcontext. - //One unfortunate thing about this is that we potentially create a glContext just to - //delete it straight afterwards. - if (extra->topextra->window) { - extra->topextra->window->destroy(); - } delete extra->topextra->window; - extra->topextra->window = 0; - + extra->topextra->window = nullptr; } } @@ -1923,7 +1725,7 @@ QRegion QWidgetPrivate::overlappedRegion(const QRect &rect, bool breakAfterFirst const QRect siblingRect = sibling->d_func()->effectiveRectFor(sibling->data->crect); if (qRectIntersects(siblingRect, r)) { - const QWExtra *siblingExtra = sibling->d_func()->extra; + const auto &siblingExtra = sibling->d_func()->extra; if (siblingExtra && siblingExtra->hasMask && !sibling->d_func()->graphicsEffect && !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) { continue; @@ -1942,21 +1744,57 @@ QRegion QWidgetPrivate::overlappedRegion(const QRect &rect, bool breakAfterFirst void QWidgetPrivate::syncBackingStore() { - if (paintOnScreen()) { - repaint_sys(dirty); + if (shouldPaintOnScreen()) { + paintOnScreen(dirty); dirty = QRegion(); - } else if (QWidgetBackingStore *bs = maybeBackingStore()) { - bs->sync(); + } else if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) { + repaintManager->sync(); } } void QWidgetPrivate::syncBackingStore(const QRegion ®ion) { - if (paintOnScreen()) - repaint_sys(region); - else if (QWidgetBackingStore *bs = maybeBackingStore()) { - bs->sync(q_func(), region); + if (shouldPaintOnScreen()) + paintOnScreen(region); + else if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) { + repaintManager->sync(q_func(), region); + } +} + +void QWidgetPrivate::paintOnScreen(const QRegion &rgn) +{ + if (data.in_destructor) + return; + + if (shouldDiscardSyncRequest()) + return; + + Q_Q(QWidget); + if (q->testAttribute(Qt::WA_StaticContents)) { + if (!extra) + createExtra(); + extra->staticContentsSize = data.crect.size(); } + + QPaintEngine *engine = q->paintEngine(); + + // QGLWidget does not support partial updates if: + // 1) The context is double buffered + // 2) The context is single buffered and auto-fill background is enabled. + const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL + || engine->type() == QPaintEngine::OpenGL2)) + && (usesDoubleBufferedGLContext || q->autoFillBackground()); + QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn); + + toBePainted &= clipRect(); + clipToEffectiveMask(toBePainted); + if (toBePainted.isEmpty()) + return; // Nothing to repaint. + + drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, nullptr); + + if (Q_UNLIKELY(q->paintingActive())) + qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); } void QWidgetPrivate::setUpdatesEnabled_helper(bool enable) @@ -1995,19 +1833,21 @@ void QWidgetPrivate::propagatePaletteChange() #if QT_CONFIG(graphicsview) if (!q->parentWidget() && extra && extra->proxyWidget) { QGraphicsProxyWidget *p = extra->proxyWidget; - inheritedPaletteResolveMask = p->d_func()->inheritedPaletteResolveMask | p->palette().resolve(); + inheritedPaletteResolveMask = p->d_func()->inheritedPaletteResolveMask | p->palette().resolveMask(); } else #endif // QT_CONFIG(graphicsview) if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation)) { inheritedPaletteResolveMask = 0; } - int mask = data.pal.resolve() | inheritedPaletteResolveMask; + + directPaletteResolveMask = data.pal.resolveMask(); + auto mask = directPaletteResolveMask | inheritedPaletteResolveMask; const bool useStyleSheetPropagationInWidgetStyles = QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); QEvent pc(QEvent::PaletteChange); - QApplication::sendEvent(q, &pc); + QCoreApplication::sendEvent(q, &pc); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast<QWidget*>(children.at(i)); if (w && (!w->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) @@ -2070,8 +1910,8 @@ QRegion QWidgetPrivate::clipRegion() const while(w->d_func()->children.at(i++) != static_cast<const QObject *>(ignoreUpTo)) ; for ( ; i < w->d_func()->children.size(); ++i) { - if(QWidget *sibling = qobject_cast<QWidget *>(w->d_func()->children.at(i))) { - if(sibling->isVisible() && !sibling->isWindow()) { + if (QWidget *sibling = qobject_cast<QWidget *>(w->d_func()->children.at(i))) { + if (sibling->isVisible() && !sibling->isWindow()) { QRect siblingRect(ox+sibling->x(), oy+sibling->y(), sibling->width(), sibling->height()); if (qRectIntersects(siblingRect, q->rect())) @@ -2183,11 +2023,6 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt if (disableSubtractOpaqueSiblings || q->isWindow()) return; -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (q->d_func()->isInUnifiedToolbar) - return; -#endif - QRect clipBoundingRect; bool dirtyClipBoundingRect = true; @@ -2291,7 +2126,7 @@ void QWidgetPrivate::clipToEffectiveMask(QRegion ®ion) const } } -bool QWidgetPrivate::paintOnScreen() const +bool QWidgetPrivate::shouldPaintOnScreen() const { #if defined(QT_NO_BACKINGSTORE) return true; @@ -2320,13 +2155,6 @@ void QWidgetPrivate::updateIsOpaque() #endif // QT_CONFIG(graphicseffect) Q_Q(QWidget); -#if 0 // Used to be included in Qt4 for Q_WS_X11 - if (q->testAttribute(Qt::WA_X11OpenGLOverlay)) { - setOpaque(false); - return; - } -#endif - if (q->testAttribute(Qt::WA_OpaquePaintEvent) || q->testAttribute(Qt::WA_PaintOnScreen)) { setOpaque(true); return; @@ -2366,10 +2194,28 @@ void QWidgetPrivate::updateIsTranslucent() if (QWindow *window = q->windowHandle()) { QSurfaceFormat format = window->format(); const int oldAlpha = format.alphaBufferSize(); - const int newAlpha = q->testAttribute(Qt::WA_TranslucentBackground)? 8 : 0; + const int newAlpha = q->testAttribute(Qt::WA_TranslucentBackground) ? 8 : -1; if (oldAlpha != newAlpha) { - format.setAlphaBufferSize(newAlpha); - window->setFormat(format); + // QTBUG-85714: Do this only when the QWindow has not yet been create()'ed yet. + // + // If that is not the case, then the setFormat() is not just futile + // but downright dangerous. Futile because the format matters only + // when creating the native window, no point in changing it + // afterwards. Dangerous because a QOpenGLContext or something else + // may eventually query the QWindow's format(), in order to ensure + // compatibility (in terms of native concepts such as pixel format, + // EGLConfig, etc.), and if we change it here, then the returned + // format does not describe reality anymore. (reality being the + // settings with which the native resource was created). + // + // Whereas if one does a destroy()-create() then this all here + // won't matter because the format is updated in + // QWidgetPrivate::create() again. + // + if (!window->handle()) { + format.setAlphaBufferSize(newAlpha); + window->setFormat(format); + } } } } @@ -2379,20 +2225,9 @@ static inline void fillRegion(QPainter *painter, const QRegion &rgn, const QBrus Q_ASSERT(painter); if (brush.style() == Qt::TexturePattern) { -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // Optimize pattern filling on mac by using HITheme directly - // when filling with the standard widget background. - // Defined in qmacstyle_mac.cpp - extern void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush); - qt_mac_fill_background(painter, rgn, brush); -#else - { - const QRect rect(rgn.boundingRect()); - painter->setClipRegion(rgn); - painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); - } -#endif - + const QRect rect(rgn.boundingRect()); + painter->setClipRegion(rgn); + painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); } else if (brush.gradient() && (brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode || brush.gradient()->coordinateMode() == QGradient::ObjectMode)) { @@ -2406,29 +2241,34 @@ static inline void fillRegion(QPainter *painter, const QRegion &rgn, const QBrus } } -void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int flags) const +bool QWidgetPrivate::updateBrushOrigin(QPainter *painter, const QBrush &brush) const { - Q_Q(const QWidget); - #if QT_CONFIG(scrollarea) - bool resetBrushOrigin = false; - QPointF oldBrushOrigin; + Q_Q(const QWidget); //If we are painting the viewport of a scrollarea, we must apply an offset to the brush in case we are drawing a texture + if (brush.style() == Qt::NoBrush || brush.style() == Qt::SolidPattern) + return false; QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea *>(parent); if (scrollArea && scrollArea->viewport() == q) { QObjectData *scrollPrivate = static_cast<QWidget *>(scrollArea)->d_ptr.data(); QAbstractScrollAreaPrivate *priv = static_cast<QAbstractScrollAreaPrivate *>(scrollPrivate); - oldBrushOrigin = painter->brushOrigin(); - resetBrushOrigin = true; painter->setBrushOrigin(-priv->contentsOffset()); - } #endif // QT_CONFIG(scrollarea) + return true; +} + +void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, DrawWidgetFlags flags) const +{ + Q_Q(const QWidget); + bool brushOriginSet = false; const QBrush autoFillBrush = q->palette().brush(q->backgroundRole()); if ((flags & DrawAsRoot) && !(q->autoFillBackground() && autoFillBrush.isOpaque())) { const QBrush bg = q->palette().brush(QPalette::Window); + if (!brushOriginSet) + brushOriginSet = updateBrushOrigin(painter, bg); if (!(flags & DontSetCompositionMode)) { //copy alpha straight in QPainter::CompositionMode oldMode = painter->compositionMode(); @@ -2440,8 +2280,11 @@ void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int } } - if (q->autoFillBackground()) + if (q->autoFillBackground()) { + if (!brushOriginSet) + brushOriginSet = updateBrushOrigin(painter, autoFillBrush); fillRegion(painter, rgn, autoFillBrush); + } if (q->testAttribute(Qt::WA_StyledBackground)) { painter->setClipRegion(rgn); @@ -2449,11 +2292,6 @@ void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int opt.initFrom(q); q->style()->drawPrimitive(QStyle::PE_Widget, &opt, painter, q); } - -#if QT_CONFIG(scrollarea) - if (resetBrushOrigin) - painter->setBrushOrigin(oldBrushOrigin); -#endif // QT_CONFIG(scrollarea) } /* @@ -2463,36 +2301,32 @@ void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int visible widgets. */ -#if 0 // Used to be included in Qt4 for Q_WS_MAC - extern QPointer<QWidget> qt_button_down; -#else - extern QWidget *qt_button_down; -#endif +extern QWidget *qt_button_down; void QWidgetPrivate::deactivateWidgetCleanup() { Q_Q(QWidget); // If this was the active application window, reset it if (QApplication::activeWindow() == q) - QApplication::setActiveWindow(0); + QApplicationPrivate::setActiveWindow(nullptr); // If the is the active mouse press widget, reset it if (q == qt_button_down) - qt_button_down = 0; + qt_button_down = nullptr; } /*! - Returns a pointer to the widget with window identifer/handle \a + Returns a pointer to the widget with window identifier/handle \a id. The window identifier type depends on the underlying window system, see \c qwindowdefs.h for the actual definition. If there - is no widget with this identifier, 0 is returned. + is no widget with this identifier, \nullptr is returned. */ QWidget *QWidget::find(WId id) { - return QWidgetPrivate::mapper ? QWidgetPrivate::mapper->value(id, 0) : 0; + return QWidgetPrivate::mapper ? QWidgetPrivate::mapper->value(id, 0) : nullptr; } @@ -2522,10 +2356,9 @@ QWidget *QWidget::find(WId id) */ WId QWidget::winId() const { - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { -#ifdef ALIEN_DEBUG - qDebug() << "QWidget::winId: creating native window for" << this; -#endif + if (!data->in_destructor + && (!testAttribute(Qt::WA_WState_Created) || !internalWinId())) + { QWidget *that = const_cast<QWidget*>(this); that->setAttribute(Qt::WA_NativeWindow); that->d_func()->createWinId(); @@ -2538,9 +2371,6 @@ void QWidgetPrivate::createWinId() { Q_Q(QWidget); -#ifdef ALIEN_DEBUG - qDebug() << "QWidgetPrivate::createWinId for" << q; -#endif const bool forceNativeWindow = q->testAttribute(Qt::WA_NativeWindow); if (!q->testAttribute(Qt::WA_WState_Created) || (forceNativeWindow && !q->internalWinId())) { if (!q->isWindow()) { @@ -2594,8 +2424,7 @@ bool QWidgetPrivate::setScreen(QScreen *screen) return false; const QScreen *currentScreen = windowHandle() ? windowHandle()->screen() : nullptr; if (currentScreen != screen) { - if (!windowHandle()) // Try to create a window handle if not created. - createWinId(); + topData()->initialScreen = screen; if (windowHandle()) windowHandle()->setScreen(screen); return true; @@ -2612,10 +2441,6 @@ Ensures that the widget has a window system identifier, i.e. that it is known to void QWidget::createWinId() { Q_D(QWidget); -#ifdef ALIEN_DEBUG - qDebug() << "QWidget::createWinId" << this; -#endif -// qWarning("QWidget::createWinId is obsolete, please fix your code."); d->createWinId(); } @@ -2653,7 +2478,7 @@ WId QWidget::effectiveWinId() const \since 5.0 - \sa winId() + \sa winId(), screen() */ QWindow *QWidget::windowHandle() const { @@ -2661,6 +2486,47 @@ QWindow *QWidget::windowHandle() const return d->windowHandle(); } +/*! + Returns the screen the widget is on. + + \since 5.14 + + \sa windowHandle() +*/ +QScreen *QWidget::screen() const +{ + Q_D(const QWidget); + if (auto associatedScreen = d->associatedScreen()) + return associatedScreen; + if (auto topLevel = window()) { + if (auto topData = qt_widget_private(topLevel)->topData()) { + if (topData->initialScreen) + return topData->initialScreen; + } + if (auto screenByPos = QGuiApplication::screenAt(topLevel->geometry().center())) + return screenByPos; + } + return QGuiApplication::primaryScreen(); +} + +/*! + Sets the screen on which the widget should be shown to \a screen. + + Setting the screen only makes sense for windows. If necessary, the widget's + window will get recreated on \a screen. + + \note If the screen is part of a virtual desktop of multiple screens, + the window will not move automatically to \a screen. To place the + window relative to the screen, use the screen's topLeft() position. + + \sa QWindow::setScreen() +*/ +void QWidget::setScreen(QScreen *screen) +{ + Q_D(QWidget); + d->setScreen(screen); +} + #ifndef QT_NO_STYLE_STYLESHEET /*! @@ -2704,7 +2570,16 @@ void QWidget::setStyleSheet(const QString& styleSheet) } if (proxy) { // style sheet update - if (d->polished) + bool repolish = d->polished; + if (!repolish) { + const auto childWidgets = findChildren<QWidget*>(); + for (auto child : childWidgets) { + repolish = child->d_func()->polished; + if (repolish) + break; + } + } + if (repolish) proxy->repolish(this); return; } @@ -2712,7 +2587,7 @@ void QWidget::setStyleSheet(const QString& styleSheet) if (testAttribute(Qt::WA_SetStyle)) { d->setStyle_helper(new QStyleSheetStyle(d->extra->style), true); } else { - d->setStyle_helper(new QStyleSheetStyle(0), true); + d->setStyle_helper(new QStyleSheetStyle(nullptr), true); } } @@ -2755,12 +2630,12 @@ QStyle *QWidget::style() const void QWidget::setStyle(QStyle *style) { Q_D(QWidget); - setAttribute(Qt::WA_SetStyle, style != 0); + setAttribute(Qt::WA_SetStyle, style != nullptr); d->createExtra(); #ifndef QT_NO_STYLE_STYLESHEET if (QStyleSheetStyle *styleSheetStyle = qt_styleSheet(style)) { //if for some reason someone try to set a QStyleSheetStyle, ref it - //(this may happen for exemple in QButtonDialogBox which propagates its style) + //(this may happen for example in QButtonDialogBox which propagates its style) styleSheetStyle->ref(); d->setStyle_helper(style, false); } else if (qt_styleSheet(d->extra->style) || !qApp->styleSheet().isEmpty()) { @@ -2808,7 +2683,7 @@ void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate) #endif QEvent e(QEvent::StyleChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); #ifndef QT_NO_STYLE_STYLESHEET // dereference the old stylesheet style @@ -2835,7 +2710,7 @@ void QWidgetPrivate::inheritStyle() QStyle *origStyle = proxy ? proxy->base : extraStyle; QWidget *parent = q->parentWidget(); - QStyle *parentStyle = (parent && parent->d_func()->extra) ? (QStyle*)parent->d_func()->extra->style : 0; + QStyle *parentStyle = (parent && parent->d_func()->extra) ? (QStyle*)parent->d_func()->extra->style : nullptr; // If we have stylesheet on app or parent has stylesheet style, we need // to be running a proxy if (!qApp->styleSheet().isEmpty() || qt_styleSheet(parentStyle)) { @@ -2858,7 +2733,7 @@ void QWidgetPrivate::inheritStyle() // In such a case we need to start following the application style (i.e revert // the propagation behavior of QStyleSheetStyle) if (!q->testAttribute(Qt::WA_SetStyle)) - origStyle = 0; + origStyle = nullptr; setStyle_helper(origStyle, true); #endif // QT_NO_STYLE_STYLESHEET @@ -3026,7 +2901,7 @@ void QWidget::overrideWindowState(Qt::WindowStates newstate) { QWindowStateChangeEvent e(Qt::WindowStates(data->window_state), true); data->window_state = newstate; - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! @@ -3043,7 +2918,7 @@ void QWidget::overrideWindowState(Qt::WindowStates newstate) \snippet code/src_gui_kernel_qwidget.cpp 0 - In order to restore and activate a minimized window (while + To restore and activate a minimized window (while preserving its maximized and/or full-screen state), use the following: \snippet code/src_gui_kernel_qwidget.cpp 1 @@ -3063,6 +2938,8 @@ void QWidget::setWindowState(Qt::WindowStates newstate) { Q_D(QWidget); Qt::WindowStates oldstate = windowState(); + if (newstate.testFlag(Qt::WindowMinimized)) // QTBUG-46763 + newstate.setFlag(Qt::WindowActive, false); if (oldstate == newstate) return; if (isWindow() && !testAttribute(Qt::WA_WState_Created)) @@ -3088,7 +2965,7 @@ void QWidget::setWindowState(Qt::WindowStates newstate) activateWindow(); QWindowStateChangeEvent e(oldstate); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! @@ -3110,14 +2987,14 @@ bool QWidget::isFullScreen() const Calling this function only affects \l{isWindow()}{windows}. - To return from full-screen mode, call showNormal(). + To return from full-screen mode, call showNormal() or close(). - Full-screen mode works fine under Windows, but has certain + \note Full-screen mode works fine under Windows, but has certain problems under X. These problems are due to limitations of the ICCCM protocol that specifies the communication between X11 clients and the window manager. ICCCM simply does not understand the concept of non-decorated full-screen windows. Therefore, the - best we can do is to request a borderless window and place and + best you can do is to request a borderless window and place and resize it to fill the entire screen. Depending on the window manager, this may or may not work. The borderless window is requested using MOTIF hints, which are at least partially @@ -3125,26 +3002,24 @@ bool QWidget::isFullScreen() const An alternative would be to bypass the window manager entirely and create a window with the Qt::X11BypassWindowManagerHint flag. This - has other severe problems though, like totally broken keyboard focus + has other severe problems though, like broken keyboard focus and very strange effects on desktop changes or when the user raises other windows. X11 window managers that follow modern post-ICCCM specifications support full-screen mode properly. - \sa showNormal(), showMaximized(), show(), hide(), isVisible() + On macOS, showing a window full screen puts the entire application in + full-screen mode, providing it with a dedicated desktop. Showing another + window while the application runs in full-screen mode might automatically + make that window full screen as well. To prevent that, exit full-screen + mode by calling showNormal() or by close() on the full screen window + before showing another window. + + \sa showNormal(), showMaximized(), show(), isVisible(), close() */ void QWidget::showFullScreen() { -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // If the unified toolbar is enabled, we have to disable it before going fullscreen. - QMainWindow *mainWindow = qobject_cast<QMainWindow*>(this); - if (mainWindow && mainWindow->unifiedTitleAndToolBarOnMac()) { - mainWindow->setUnifiedTitleAndToolBarOnMac(false); - QMainWindowLayout *mainLayout = qobject_cast<QMainWindowLayout*>(mainWindow->layout()); - mainLayout->activateUnifiedToolbarAfterFullScreen = true; - } -#endif ensurePolished(); setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowMaximized)) @@ -3172,18 +3047,6 @@ void QWidget::showMaximized() setWindowState((windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowMaximized); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // If the unified toolbar was enabled before going fullscreen, we have to enable it back. - QMainWindow *mainWindow = qobject_cast<QMainWindow*>(this); - if (mainWindow) - { - QMainWindowLayout *mainLayout = qobject_cast<QMainWindowLayout*>(mainWindow->layout()); - if (mainLayout->activateUnifiedToolbarAfterFullScreen) { - mainWindow->setUnifiedTitleAndToolBarOnMac(true); - mainLayout->activateUnifiedToolbarAfterFullScreen = false; - } - } -#endif setVisible(true); } @@ -3201,18 +3064,6 @@ void QWidget::showNormal() setWindowState(windowState() & ~(Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // If the unified toolbar was enabled before going fullscreen, we have to enable it back. - QMainWindow *mainWindow = qobject_cast<QMainWindow*>(this); - if (mainWindow) - { - QMainWindowLayout *mainLayout = qobject_cast<QMainWindowLayout*>(mainWindow->layout()); - if (mainLayout->activateUnifiedToolbarAfterFullScreen) { - mainWindow->setUnifiedTitleAndToolBarOnMac(true); - mainLayout->activateUnifiedToolbarAfterFullScreen = false; - } - } -#endif setVisible(true); } @@ -3251,7 +3102,7 @@ bool QWidget::isEnabledTo(const QWidget *ancestor) const /*! Appends the action \a action to this widget's list of actions. - All QWidgets have a list of \l{QAction}s, however they can be + All QWidgets have a list of \l{QAction}s. However, they can be represented graphically in many different ways. The default use of the QAction list (as returned by actions()) is to create a context QMenu. @@ -3265,7 +3116,7 @@ bool QWidget::isEnabledTo(const QWidget *ancestor) const */ void QWidget::addAction(QAction *action) { - insertAction(0, action); + insertAction(nullptr, action); } /*! @@ -3273,19 +3124,15 @@ void QWidget::addAction(QAction *action) \sa removeAction(), QMenu, addAction() */ -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) void QWidget::addActions(const QList<QAction *> &actions) -#else -void QWidget::addActions(QList<QAction*> actions) -#endif { - for(int i = 0; i < actions.count(); i++) - insertAction(0, actions.at(i)); + for(int i = 0; i < actions.size(); i++) + insertAction(nullptr, actions.at(i)); } /*! Inserts the action \a action to this widget's list of actions, - before the action \a before. It appends the action if \a before is 0 or + before the action \a before. It appends the action if \a before is \nullptr or \a before is not a valid action for this widget. A QWidget should only have one of each action. @@ -3300,39 +3147,35 @@ void QWidget::insertAction(QAction *before, QAction *action) } Q_D(QWidget); - if(d->actions.contains(action)) + if (d->actions.contains(action)) removeAction(action); int pos = d->actions.indexOf(before); if (pos < 0) { - before = 0; + before = nullptr; pos = d->actions.size(); } d->actions.insert(pos, action); QActionPrivate *apriv = action->d_func(); - apriv->widgets.append(this); + apriv->associatedObjects.append(this); QActionEvent e(QEvent::ActionAdded, action, before); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } /*! Inserts the actions \a actions to this widget's list of actions, - before the action \a before. It appends the action if \a before is 0 or + before the action \a before. It appends the action if \a before is \nullptr or \a before is not a valid action for this widget. A QWidget can have at most one of each action. \sa removeAction(), QMenu, insertAction(), contextMenuPolicy */ -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) void QWidget::insertActions(QAction *before, const QList<QAction*> &actions) -#else -void QWidget::insertActions(QAction *before, QList<QAction*> actions) -#endif { - for(int i = 0; i < actions.count(); ++i) + for(int i = 0; i < actions.size(); ++i) insertAction(before, actions.at(i)); } @@ -3348,11 +3191,11 @@ void QWidget::removeAction(QAction *action) Q_D(QWidget); QActionPrivate *apriv = action->d_func(); - apriv->widgets.removeAll(this); + apriv->associatedObjects.removeAll(this); if (d->actions.removeAll(action)) { QActionEvent e(QEvent::ActionRemoved, action); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } } @@ -3366,14 +3209,130 @@ QList<QAction*> QWidget::actions() const Q_D(const QWidget); return d->actions; } -#endif // QT_NO_ACTION /*! - \fn bool QWidget::isEnabledToTLW() const - \obsolete + \fn QAction *QWidget::addAction(const QString &text); + \fn QAction *QWidget::addAction(const QString &text, const QKeySequence &shortcut); + \fn QAction *QWidget::addAction(const QIcon &icon, const QString &text); + \fn QAction *QWidget::addAction(const QIcon &icon, const QString &text, const QKeySequence &shortcut); + + \since 6.3 + + These convenience functions create a new action with text \a text, + icon \a icon and shortcut \a shortcut, if any. + + The functions add the newly created action to the widget's + list of actions, and return it. + + QWidget takes ownership of the returned QAction. +*/ +QAction *QWidget::addAction(const QString &text) +{ + QAction *ret = new QAction(text, this); + addAction(ret); + return ret; +} + +QAction *QWidget::addAction(const QIcon &icon, const QString &text) +{ + QAction *ret = new QAction(icon, text, this); + addAction(ret); + return ret; +} + +#if QT_CONFIG(shortcut) +QAction *QWidget::addAction(const QString &text, const QKeySequence &shortcut) +{ + QAction *ret = addAction(text); + ret->setShortcut(shortcut); + return ret; +} + +QAction *QWidget::addAction(const QIcon &icon, const QString &text, const QKeySequence &shortcut) +{ + QAction *ret = addAction(icon, text); + ret->setShortcut(shortcut); + return ret; +} +#endif + +/*! + \fn QAction *QWidget::addAction(const QString &text, const QObject *receiver, const char* member, Qt::ConnectionType type) + \fn QAction *QWidget::addAction(const QIcon &icon, const QString &text, const QObject *receiver, const char* member, Qt::ConnectionType type) + \fn QAction *QWidget::addAction(const QString &text, const QKeySequence &shortcut, const QObject *receiver, const char* member, Qt::ConnectionType type) + \fn QAction *QWidget::addAction(const QIcon &icon, const QString &text, const QKeySequence &shortcut, const QObject *receiver, const char* member, Qt::ConnectionType type) + + \overload + \since 6.3 + + This convenience function creates a new action with the text \a + text, icon \a icon, and shortcut \a shortcut, if any. + + The action's \l{QAction::triggered()}{triggered()} signal is connected + to the \a receiver's \a member slot. The function adds the newly created + action to the widget's list of actions and returns it. + + QWidget takes ownership of the returned QAction. +*/ +QAction *QWidget::addAction(const QString &text, const QObject *receiver, const char* member, + Qt::ConnectionType type) +{ + QAction *action = addAction(text); + QObject::connect(action, SIGNAL(triggered(bool)), receiver, member, type); + return action; +} + +QAction *QWidget::addAction(const QIcon &icon, const QString &text, + const QObject *receiver, const char* member, + Qt::ConnectionType type) +{ + QAction *action = addAction(icon, text); + QObject::connect(action, SIGNAL(triggered(bool)), receiver, member, type); + return action; +} + +#if QT_CONFIG(shortcut) +QAction *QWidget::addAction(const QString &text, const QKeySequence &shortcut, + const QObject *receiver, const char* member, + Qt::ConnectionType type) +{ + QAction *action = addAction(text, receiver, member, type); + action->setShortcut(shortcut); + return action; +} + +QAction *QWidget::addAction(const QIcon &icon, const QString &text, const QKeySequence &shortcut, + const QObject *receiver, const char* member, + Qt::ConnectionType type) +{ + QAction *action = addAction(icon, text, receiver, member, type); + action->setShortcut(shortcut); + return action; +} +#endif // QT_CONFIG(shortcut) + +/*! + \fn template<typename...Args, typename = compatible_action_slot_args<Args...>> QAction *QWidget::addAction(const QString &text, Args&&...args) + \fn template<typename...Args, typename = compatible_action_slot_args<Args...>> QAction *QWidget::addAction(const QString &text, const QKeySequence &shortcut, Args&&...args) + \fn template<typename...Args, typename = compatible_action_slot_args<Args...>> QAction *QWidget::addAction(const QIcon &icon, const QString &text, Args&&...args) + \fn template<typename...Args, typename = compatible_action_slot_args<Args...>> QAction *QWidget::addAction(const QIcon &icon, const QString &text, const QKeySequence &shortcut, Args&&...args) + + \since 6.3 + \overload - This function is deprecated. It is equivalent to isEnabled() + These convenience functions create a new action with the text \a text, + icon \a icon, and shortcut \a shortcut, if any. + + The action's \l{QAction::triggered()}{triggered()} signal is connected + as if by a call to QObject::connect(action, &QAction::triggered, args...), + perfectly forwarding \a args, including a possible Qt::ConnectionType. + + The function adds the newly created action to the widget's list of + actions and returns it. + + QWidget takes ownership of the returned QAction. */ +#endif // QT_NO_ACTION /*! \property QWidget::enabled @@ -3428,13 +3387,6 @@ void QWidgetPrivate::setEnabled_helper(bool enable) if (w && !w->testAttribute(attribute)) w->d_func()->setEnabled_helper(enable); } -#if 0 // Used to be included in Qt4 for Q_WS_X11 - if (q->testAttribute(Qt::WA_SetCursor) || q->isWindow()) { - // enforce the windows behavior of clearing the cursor on - // disabled widgets - qt_x11_enforce_cursor(q); - } -#endif #ifndef QT_NO_CURSOR if (q->testAttribute(Qt::WA_SetCursor) || q->isWindow()) { // enforce the windows behavior of clearing the cursor on @@ -3442,9 +3394,6 @@ void QWidgetPrivate::setEnabled_helper(bool enable) qt_qpa_set_cursor(q, false); } #endif -#if 0 // Used to be included in Qt4 for Q_WS_MAC - setEnabled_helper_sys(enable); -#endif #ifndef QT_NO_IM if (q->testAttribute(Qt::WA_InputMethodEnabled) && q->hasFocus()) { QWidget *focusWidget = effectiveFocusWidget(); @@ -3459,7 +3408,7 @@ void QWidgetPrivate::setEnabled_helper(bool enable) } #endif //QT_NO_IM QEvent e(QEvent::EnabledChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } /*! @@ -3477,7 +3426,7 @@ void QWidgetPrivate::setEnabled_helper(bool enable) By default, this property is \c false. - \sa {Drag and Drop} + \sa {Drag and Drop in Qt}{Drag and Drop} */ bool QWidget::acceptDrops() const { @@ -3591,6 +3540,10 @@ int QWidget::y() const See the \l{Window Geometry} documentation for an overview of geometry issues with windows. + \note Not all windowing systems support setting or querying top level window positions. + On such a system, programmatically moving windows may not have any effect, and artificial + values may be returned for the current positions, such as \c QPoint(0, 0). + \sa frameGeometry, size, x(), y() */ QPoint QWidget::pos() const @@ -3630,19 +3583,6 @@ QPoint QWidget::pos() const */ /*! - \property QWidget::normalGeometry - - \brief the geometry of the widget as it will appear when shown as - a normal (not maximized or full screen) top-level widget - - For child widgets this property always holds an empty rectangle. - - By default, this property contains an empty rectangle. - - \sa QWidget::windowState(), QWidget::geometry -*/ - -/*! \property QWidget::size \brief the size of the widget excluding any window frame @@ -3673,8 +3613,7 @@ QPoint QWidget::pos() const issues with windows. \note Do not use this function to find the width of a screen on - a \l{QDesktopWidget}{multiple screen desktop}. Read - \l{QDesktopWidget#Screen Geometry}{this note} for details. + a multi-screen desktop. See QScreen for details. By default, this property contains a value that depends on the user's platform and screen geometry. @@ -3689,12 +3628,8 @@ QPoint QWidget::pos() const See the \l{Window Geometry} documentation for an overview of geometry issues with windows. - \note Do not use this function to find the height of a screen - on a \l{QDesktopWidget}{multiple screen desktop}. Read - \l{QDesktopWidget#Screen Geometry}{this note} for details. - By default, this property contains a value that depends on the user's - platform and screen geometry. + platform and \l{QScreen::geometry}{screen geometry}. \sa geometry, width, size */ @@ -3715,11 +3650,25 @@ QPoint QWidget::pos() const \sa size */ +/*! + \property QWidget::normalGeometry + + \brief the geometry of the widget as it will appear when shown as + a normal (not maximized or full screen) top-level widget + + If the widget is already in this state the normal geometry will + reflect the widget's current geometry(). + For child widgets this property always holds an empty rectangle. + + By default, this property contains an empty rectangle. + + \sa QWidget::windowState(), QWidget::geometry +*/ QRect QWidget::normalGeometry() const { Q_D(const QWidget); - if (!d->extra || !d->extra->topextra) + if (!isWindow()) return QRect(); if (!isMaximized() && !isFullScreen()) @@ -3792,7 +3741,7 @@ QRegion QWidget::childrenRegion() const the current size is smaller. The minimum size set by this function will override the minimum size - defined by QLayout. In order to unset the minimum size, use a + defined by QLayout. To unset the minimum size, use a value of \c{QSize(0, 0)}. By default, this property contains a size with zero width and height. @@ -3925,7 +3874,7 @@ QSize QWidget::sizeIncrement() const QSize QWidget::baseSize() const { Q_D(const QWidget); - return (d->extra != 0 && d->extra->topextra != 0) + return (d->extra && d->extra->topextra) ? QSize(d->extra->topextra->basew, d->extra->topextra->baseh) : QSize(0, 0); } @@ -4238,19 +4187,20 @@ void QWidget::setFixedHeight(int h) /*! Translates the widget coordinate \a pos to the coordinate system - of \a parent. The \a parent must not be 0 and must be a parent + of \a parent. The \a parent must not be \nullptr and must be a parent of the calling widget. \sa mapFrom(), mapToParent(), mapToGlobal(), underMouse() + \since 6.0 */ -QPoint QWidget::mapTo(const QWidget * parent, const QPoint & pos) const +QPointF QWidget::mapTo(const QWidget *parent, const QPointF &pos) const { - QPoint p = pos; + QPointF p = pos; if (parent) { const QWidget * w = this; while (w != parent) { - Q_ASSERT_X(w, "QWidget::mapTo(const QWidget *parent, const QPoint &pos)", + Q_ASSERT_X(w, "QWidget::mapTo(const QWidget *parent, const QPointF &pos)", "parent must be in parent hierarchy"); p = w->mapToParent(p); w = w->parentWidget(); @@ -4259,18 +4209,26 @@ QPoint QWidget::mapTo(const QWidget * parent, const QPoint & pos) const return p; } +/*! + \overload +*/ +QPoint QWidget::mapTo(const QWidget *parent, const QPoint &pos) const +{ + return mapTo(parent, QPointF(pos)).toPoint(); +} /*! Translates the widget coordinate \a pos from the coordinate system of \a parent to this widget's coordinate system. The \a parent - must not be 0 and must be a parent of the calling widget. + must not be \nullptr and must be a parent of the calling widget. \sa mapTo(), mapFromParent(), mapFromGlobal(), underMouse() + \since 6.0 */ -QPoint QWidget::mapFrom(const QWidget * parent, const QPoint & pos) const +QPointF QWidget::mapFrom(const QWidget *parent, const QPointF &pos) const { - QPoint p(pos); + QPointF p(pos); if (parent) { const QWidget * w = this; while (w != parent) { @@ -4284,6 +4242,13 @@ QPoint QWidget::mapFrom(const QWidget * parent, const QPoint & pos) const return p; } +/*! + \overload +*/ +QPoint QWidget::mapFrom(const QWidget *parent, const QPoint &pos) const +{ + return mapFrom(parent, QPointF(pos)).toPoint(); +} /*! Translates the widget coordinate \a pos to a coordinate in the @@ -4292,8 +4257,17 @@ QPoint QWidget::mapFrom(const QWidget * parent, const QPoint & pos) const Same as mapToGlobal() if the widget has no parent. \sa mapFromParent(), mapTo(), mapToGlobal(), underMouse() + \since 6.0 */ +QPointF QWidget::mapToParent(const QPointF &pos) const +{ + return pos + QPointF(data->crect.topLeft()); +} + +/*! + \overload +*/ QPoint QWidget::mapToParent(const QPoint &pos) const { return pos + data->crect.topLeft(); @@ -4306,8 +4280,17 @@ QPoint QWidget::mapToParent(const QPoint &pos) const Same as mapFromGlobal() if the widget has no parent. \sa mapToParent(), mapFrom(), mapFromGlobal(), underMouse() + \since 6.0 */ +QPointF QWidget::mapFromParent(const QPointF &pos) const +{ + return pos - QPointF(data->crect.topLeft()); +} + +/*! + \overload +*/ QPoint QWidget::mapFromParent(const QPoint &pos) const { return pos - data->crect.topLeft(); @@ -4342,7 +4325,8 @@ QWidget *QWidget::window() const \since 4.4 Returns the native parent for this widget, i.e. the next ancestor widget - that has a system identifier, or 0 if it does not have any native parent. + that has a system identifier, or \nullptr if it does not have any native + parent. \sa effectiveWinId() */ @@ -4355,7 +4339,7 @@ QWidget *QWidget::nativeParentWidget() const } /*! \fn QWidget *QWidget::topLevelWidget() const - \obsolete + \deprecated Use window() instead. */ @@ -4368,7 +4352,7 @@ QWidget *QWidget::nativeParentWidget() const The background role defines the brush from the widget's \l palette that is used to render the background. - If no explicit background role is set, the widget inherts its parent + If no explicit background role is set, the widget inherits its parent widget's background role. \sa setBackgroundRole(), foregroundRole() @@ -4512,7 +4496,7 @@ void QWidget::setForegroundRole(QPalette::ColorRole role) QWidget's palette propagation is similar to its font propagation. The current style, which is used to render the content of all standard Qt - widgets, is free to choose colors and brushes from the widget palette, or + widgets, is free to choose colors and brushes from the widget palette, or, in some cases, to ignore the palette (partially, or completely). In particular, certain styles like GTK style, Mac style, and Windows Vista style, depend on third party APIs to render the content of widgets, @@ -4525,26 +4509,20 @@ void QWidget::setForegroundRole(QPalette::ColorRole role) the "color", "background-color", "selection-color", "selection-background-color" and "alternate-background-color". - \sa QApplication::palette(), QWidget::font(), {Qt Style Sheets} + \sa QGuiApplication::palette(), QWidget::font(), {Qt Style Sheets} */ const QPalette &QWidget::palette() const { if (!isEnabled()) { data->pal.setCurrentColorGroup(QPalette::Disabled); } else if ((!isVisible() || isActiveWindow()) -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +#if defined(Q_OS_WIN) && !QApplicationPrivate::isBlockedByModal(const_cast<QWidget *>(this)) #endif ) { data->pal.setCurrentColorGroup(QPalette::Active); } else { -#if 0 // Used to be included in Qt4 for Q_WS_MAC - extern bool qt_mac_can_clickThrough(const QWidget *); //qwidget_mac.cpp - if (qt_mac_can_clickThrough(this)) - data->pal.setCurrentColorGroup(QPalette::Active); - else -#endif - data->pal.setCurrentColorGroup(QPalette::Inactive); + data->pal.setCurrentColorGroup(QPalette::Inactive); } return data->pal; } @@ -4552,7 +4530,7 @@ const QPalette &QWidget::palette() const void QWidget::setPalette(const QPalette &palette) { Q_D(QWidget); - setAttribute(Qt::WA_SetPalette, palette.resolve() != 0); + setAttribute(Qt::WA_SetPalette, palette.resolveMask() != 0); // Determine which palette is inherited from this widget's ancestors and // QApplication::palette, resolve this against \a palette (attributes from @@ -4572,7 +4550,7 @@ void QWidget::setPalette(const QPalette &palette) widget's palette are implicitly imposed on this widget by the user). Note that this font does not take into account the palette set on \a w itself. */ -QPalette QWidgetPrivate::naturalWidgetPalette(uint inheritedMask) const +QPalette QWidgetPrivate::naturalWidgetPalette(QPalette::ResolveMask inheritedMask) const { Q_Q(const QWidget); @@ -4588,9 +4566,9 @@ QPalette QWidgetPrivate::naturalWidgetPalette(uint inheritedMask) const )) { if (QWidget *p = q->parentWidget()) { if (!p->testAttribute(Qt::WA_StyleSheet) || useStyleSheetPropagationInWidgetStyles) { - if (!naturalPalette.isCopyOf(QApplication::palette())) { + if (!naturalPalette.isCopyOf(QGuiApplication::palette())) { QPalette inheritedPalette = p->palette(); - inheritedPalette.resolve(inheritedMask); + inheritedPalette.setResolveMask(inheritedMask); naturalPalette = inheritedPalette.resolve(naturalPalette); } else { naturalPalette = p->palette(); @@ -4600,12 +4578,12 @@ QPalette QWidgetPrivate::naturalWidgetPalette(uint inheritedMask) const #if QT_CONFIG(graphicsview) else if (extra && extra->proxyWidget) { QPalette inheritedPalette = extra->proxyWidget->palette(); - inheritedPalette.resolve(inheritedMask); + inheritedPalette.setResolveMask(inheritedMask); naturalPalette = inheritedPalette.resolve(naturalPalette); } #endif // QT_CONFIG(graphicsview) } - naturalPalette.resolve(0); + naturalPalette.setResolveMask(0); return naturalPalette; } /*! @@ -4626,7 +4604,7 @@ void QWidgetPrivate::resolvePalette() void QWidgetPrivate::setPalette_helper(const QPalette &palette) { Q_Q(QWidget); - if (data.pal == palette && data.pal.resolve() == palette.resolve()) + if (data.pal == palette && data.pal.resolveMask() == palette.resolveMask()) return; data.pal = palette; updateSystemBackground(); @@ -4696,7 +4674,7 @@ void QWidget::setFont(const QFont &font) style->saveWidgetFont(this, font); #endif - setAttribute(Qt::WA_SetFont, font.resolve() != 0); + setAttribute(Qt::WA_SetFont, font.resolveMask() != 0); // Determine which font is inherited from this widget's ancestors and // QApplication::font, resolve this against \a font (attributes from the @@ -4738,7 +4716,7 @@ QFont QWidgetPrivate::naturalWidgetFont(uint inheritedMask) const if (!naturalFont.isCopyOf(QApplication::font())) { if (inheritedMask != 0) { QFont inheritedFont = p->font(); - inheritedFont.resolve(inheritedMask); + inheritedFont.setResolveMask(inheritedMask); naturalFont = inheritedFont.resolve(naturalFont); } // else nothing to do (naturalFont = naturalFont) } else { @@ -4750,13 +4728,13 @@ QFont QWidgetPrivate::naturalWidgetFont(uint inheritedMask) const else if (extra && extra->proxyWidget) { if (inheritedMask != 0) { QFont inheritedFont = extra->proxyWidget->font(); - inheritedFont.resolve(inheritedMask); + inheritedFont.setResolveMask(inheritedMask); naturalFont = inheritedFont.resolve(naturalFont); } // else nothing to do (naturalFont = naturalFont) } #endif // QT_CONFIG(graphicsview) } - naturalFont.resolve(0); + naturalFont.setResolveMask(0); return naturalFont; } @@ -4768,7 +4746,7 @@ QFont QWidgetPrivate::naturalWidgetFont(uint inheritedMask) const QFont QWidgetPrivate::localFont() const { QFont localfont = data.fnt; - localfont.resolve(directFontResolveMask); + localfont.setResolveMask(directFontResolveMask); return localfont; } @@ -4801,32 +4779,29 @@ void QWidgetPrivate::updateFont(const QFont &font) Q_Q(QWidget); #ifndef QT_NO_STYLE_STYLESHEET const QStyleSheetStyle* cssStyle; - cssStyle = extra ? qt_styleSheet(extra->style) : 0; + cssStyle = extra ? qt_styleSheet(extra->style) : nullptr; const bool useStyleSheetPropagationInWidgetStyles = QCoreApplication::testAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles); #endif data.fnt = QFont(font, q); -#if 0 // Used to be included in Qt4 for Q_WS_X11 - // make sure the font set on this widget is associated with the correct screen - data.fnt.x11SetScreen(xinfo.screen()); -#endif + // Combine new mask with natural mask and propagate to children. #if QT_CONFIG(graphicsview) if (!q->parentWidget() && extra && extra->proxyWidget) { QGraphicsProxyWidget *p = extra->proxyWidget; - inheritedFontResolveMask = p->d_func()->inheritedFontResolveMask | p->font().resolve(); + inheritedFontResolveMask = p->d_func()->inheritedFontResolveMask | p->font().resolveMask(); } else #endif // QT_CONFIG(graphicsview) if (q->isWindow() && !q->testAttribute(Qt::WA_WindowPropagation)) { inheritedFontResolveMask = 0; } - uint newMask = data.fnt.resolve() | inheritedFontResolveMask; + uint newMask = data.fnt.resolveMask() | inheritedFontResolveMask; // Set the font as also having resolved inherited traits, so the result of reading QWidget::font() // isn't all weak information, but save the original mask to be able to let new changes on the // parent widget font propagate correctly. - directFontResolveMask = data.fnt.resolve(); - data.fnt.resolve(newMask); + directFontResolveMask = data.fnt.resolveMask(); + data.fnt.setResolveMask(newMask); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast<QWidget*>(children.at(i)); @@ -4854,7 +4829,7 @@ void QWidgetPrivate::updateFont(const QFont &font) #endif QEvent e(QEvent::FontChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } void QWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction) @@ -4872,20 +4847,22 @@ void QWidgetPrivate::setLayoutDirection_helper(Qt::LayoutDirection direction) } } QEvent e(QEvent::LayoutDirectionChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } void QWidgetPrivate::resolveLayoutDirection() { Q_Q(const QWidget); if (!q->testAttribute(Qt::WA_SetLayoutDirection)) - setLayoutDirection_helper(q->isWindow() ? QApplication::layoutDirection() : q->parentWidget()->layoutDirection()); + setLayoutDirection_helper(q->isWindow() ? QGuiApplication::layoutDirection() : q->parentWidget()->layoutDirection()); } /*! \property QWidget::layoutDirection - \brief the layout direction for this widget + \brief the layout direction for this widget. + + \note This method no longer affects text layout direction since Qt 4.7. By default, this property is set to Qt::LeftToRight. @@ -4896,7 +4873,6 @@ void QWidgetPrivate::resolveLayoutDirection() has been called for the parent do not inherit the parent's layout direction. - This method no longer affects text layout direction since Qt 4.7. \sa QApplication::layoutDirection */ @@ -4963,9 +4939,9 @@ void QWidget::unsetLayoutDirection() Some underlying window implementations will reset the cursor if it leaves a widget even if the mouse is grabbed. If you want to have a cursor set for all widgets, even when outside the window, consider - QApplication::setOverrideCursor(). + QGuiApplication::setOverrideCursor(). - \sa QApplication::setOverrideCursor() + \sa QGuiApplication::setOverrideCursor() */ #ifndef QT_NO_CURSOR @@ -4984,22 +4960,17 @@ QCursor QWidget::cursor() const void QWidget::setCursor(const QCursor &cursor) { Q_D(QWidget); -// On Mac we must set the cursor even if it is the ArrowCursor. -#if 1 // Used to be excluded in Qt4 for Q_WS_MAC if (cursor.shape() != Qt::ArrowCursor || (d->extra && d->extra->curs)) -#endif { d->createExtra(); - QCursor *newCursor = new QCursor(cursor); - delete d->extra->curs; - d->extra->curs = newCursor; + d->extra->curs = std::make_unique<QCursor>(cursor); } setAttribute(Qt::WA_SetCursor); d->setCursor_sys(cursor); QEvent event(QEvent::CursorChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } void QWidgetPrivate::setCursor_sys(const QCursor &cursor) @@ -5012,16 +4983,14 @@ void QWidgetPrivate::setCursor_sys(const QCursor &cursor) void QWidget::unsetCursor() { Q_D(QWidget); - if (d->extra) { - delete d->extra->curs; - d->extra->curs = 0; - } + if (d->extra) + d->extra->curs.reset(); if (!isWindow()) setAttribute(Qt::WA_SetCursor, false); d->unsetCursor_sys(); QEvent event(QEvent::CursorChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } void QWidgetPrivate::unsetCursor_sys() @@ -5047,7 +5016,7 @@ void qt_qpa_set_cursor(QWidget *w, bool force) if (!w->testAttribute(Qt::WA_WState_Created)) return; - static QPointer<QWidget> lastUnderMouse = 0; + static QPointer<QWidget> lastUnderMouse = nullptr; if (force) { lastUnderMouse = w; } else if (lastUnderMouse) { @@ -5119,9 +5088,6 @@ void qt_qpa_set_cursor(QWidget *w, bool force) \note To obtain the contents of a QOpenGLWidget, use QOpenGLWidget::grabFramebuffer() instead. - - \note To obtain the contents of a QGLWidget (deprecated), use - QGLWidget::grabFrameBuffer() or QGLWidget::renderPixmap() instead. */ void QWidget::render(QPaintDevice *target, const QPoint &targetOffset, const QRegion &sourceRegion, RenderFlags renderFlags) @@ -5194,6 +5160,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, const QRegion oldSystemClip = enginePriv->systemClip; const QRegion oldBaseClip = enginePriv->baseSystemClip; const QRegion oldSystemViewport = enginePriv->systemViewport; + const Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection(); // This ensures that all painting triggered by render() is clipped to the current engine clip. if (painter->hasClipping()) { @@ -5202,6 +5169,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, } else { enginePriv->setSystemViewport(oldSystemClip); } + painter->setLayoutDirection(layoutDirection()); d->render(target, targetOffset, toBePainted, renderFlags); @@ -5209,6 +5177,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, enginePriv->baseSystemClip = oldBaseClip; enginePriv->setSystemTransformAndViewport(oldTransform, oldSystemViewport); enginePriv->systemStateChanged(); + painter->setLayoutDirection(oldLayoutDirection); // Restore shared painter. d->setSharedPainter(oldPainter); @@ -5219,7 +5188,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, static void sendResizeEvents(QWidget *target) { QResizeEvent e(target->size(), QSize()); - QApplication::sendEvent(target, &e); + QCoreApplication::sendEvent(target, &e); const QObjectList children = target->children(); for (int i = 0; i < children.size(); ++i) { @@ -5263,7 +5232,7 @@ QPixmap QWidget::grab(const QRect &rectangle) if (!r.intersects(rect())) return QPixmap(); - const qreal dpr = devicePixelRatioF(); + const qreal dpr = devicePixelRatio(); QPixmap res((QSizeF(r.size()) * dpr).toSize()); res.setDevicePixelRatio(dpr); if (!d->isOpaque) @@ -5278,7 +5247,7 @@ QPixmap QWidget::grab(const QRect &rectangle) \brief The graphicsEffect function returns a pointer to the widget's graphics effect. - If the widget has no graphics effect, 0 is returned. + If the widget has no graphics effect, \nullptr is returned. \since 4.6 @@ -5322,9 +5291,9 @@ void QWidget::setGraphicsEffect(QGraphicsEffect *effect) return; if (d->graphicsEffect) { - d->invalidateBuffer(rect()); + d->invalidateBackingStore(rect()); delete d->graphicsEffect; - d->graphicsEffect = 0; + d->graphicsEffect = nullptr; } if (effect) { @@ -5419,18 +5388,16 @@ void QWidgetPrivate::render_helper(QPainter *painter, const QPoint &targetOffset Q_ASSERT(!toBePainted.isEmpty()); Q_Q(QWidget); -#if 1 // Used to be excluded in Qt4 for Q_WS_MAC const QTransform originalTransform = painter->worldTransform(); const bool useDeviceCoordinates = originalTransform.isScaling(); if (!useDeviceCoordinates) { -#endif // Render via a pixmap. const QRect rect = toBePainted.boundingRect(); const QSize size = rect.size(); if (size.isNull()) return; - const qreal pixmapDevicePixelRatio = painter->device()->devicePixelRatioF(); + const qreal pixmapDevicePixelRatio = painter->device()->devicePixelRatio(); QPixmap pixmap(size * pixmapDevicePixelRatio); pixmap.setDevicePixelRatio(pixmapDevicePixelRatio); @@ -5446,7 +5413,6 @@ void QWidgetPrivate::render_helper(QPainter *painter, const QPoint &targetOffset if (restore) painter->setRenderHints(QPainter::SmoothPixmapTransform, false); -#if 1 // Used to be excluded in Qt4 for Q_WS_MAC } else { // Render via a pixmap in device coordinates (to avoid pixmap scaling). QTransform transform = originalTransform; @@ -5477,29 +5443,33 @@ void QWidgetPrivate::render_helper(QPainter *painter, const QPoint &targetOffset painter->drawPixmap(deviceRect.topLeft(), pixmap); painter->setTransform(originalTransform); } -#endif } -void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags, - QPainter *sharedPainter, QWidgetBackingStore *backingStore) +void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, DrawWidgetFlags flags, + QPainter *sharedPainter, QWidgetRepaintManager *repaintManager) { if (rgn.isEmpty()) return; + Q_Q(QWidget); + + qCInfo(lcWidgetPainting) << "Drawing" << rgn << "of" << q << "at" << offset + << "into paint device" << pdev << "with" << flags; + const bool asRoot = flags & DrawAsRoot; - bool onScreen = paintOnScreen(); + bool onScreen = shouldPaintOnScreen(); - Q_Q(QWidget); #if QT_CONFIG(graphicseffect) if (graphicsEffect && graphicsEffect->isEnabled()) { QGraphicsEffectSource *source = graphicsEffect->d_func()->source; QWidgetEffectSourcePrivate *sourced = static_cast<QWidgetEffectSourcePrivate *> (source->d_func()); if (!sourced->context) { - QWidgetPaintContext context(pdev, rgn, offset, flags, sharedPainter, backingStore); + const QRegion effectRgn((flags & UseEffectRegionBounds) ? rgn.boundingRect() : rgn); + QWidgetPaintContext context(pdev, effectRgn, offset, flags, sharedPainter, repaintManager); sourced->context = &context; if (!sharedPainter) { - setSystemClip(pdev->paintEngine(), pdev->devicePixelRatioF(), rgn.translated(offset)); + setSystemClip(pdev->paintEngine(), pdev->devicePixelRatio(), effectRgn.translated(offset)); QPainter p(pdev); p.translate(offset); context.painter = &p; @@ -5513,22 +5483,21 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } sharedPainter->save(); sharedPainter->translate(offset); - setSystemClip(sharedPainter->paintEngine(), sharedPainter->device()->devicePixelRatioF(), rgn.translated(offset)); + setSystemClip(sharedPainter->paintEngine(), sharedPainter->device()->devicePixelRatio(), effectRgn.translated(offset)); graphicsEffect->draw(sharedPainter); setSystemClip(sharedPainter->paintEngine(), 1, QRegion()); sharedPainter->restore(); } - sourced->context = 0; + sourced->context = nullptr; - // Native widgets need to be marked dirty on screen so painting will be done in correct context - // Same check as in the no effects case below. - if (backingStore && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow())) - backingStore->markDirtyOnScreen(rgn, q, offset); + if (repaintManager) + repaintManager->markNeedsFlush(q, effectRgn, offset); return; } } #endif // QT_CONFIG(graphicseffect) + flags = flags & ~UseEffectRegionBounds; const bool alsoOnScreen = flags & DrawPaintOnScreen; const bool recursive = flags & DrawRecursive; @@ -5550,54 +5519,35 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP q->setAttribute(Qt::WA_WState_InPaintEvent); //clip away the new area -#ifndef QT_NO_PAINT_DEBUG - bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted); -#endif QPaintEngine *paintEngine = pdev->paintEngine(); if (paintEngine) { setRedirected(pdev, -offset); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // (Alien support) Special case for Mac when redirecting: If the paint device - // is of the Widget type we need to set WA_WState_InPaintEvent since painting - // outside the paint event is not supported on QWidgets. The attributeis - // restored further down. - if (pdev->devType() == QInternal::Widget) - static_cast<QWidget *>(pdev)->setAttribute(Qt::WA_WState_InPaintEvent); - -#endif if (sharedPainter) - setSystemClip(pdev->paintEngine(), pdev->devicePixelRatioF(), toBePainted); + setSystemClip(pdev->paintEngine(), pdev->devicePixelRatio(), toBePainted); else paintEngine->d_func()->systemRect = q->data->crect; //paint the background if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground)) && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) { -#ifndef QT_NO_OPENGL beginBackingStorePainting(); -#endif QPainter p(q); - paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0); -#ifndef QT_NO_OPENGL + p.setRenderHint(QPainter::SmoothPixmapTransform); + paintBackground(&p, toBePainted, (asRoot || onScreen) ? (flags | DrawAsRoot) : DrawWidgetFlags()); endBackingStorePainting(); -#endif } if (!sharedPainter) - setSystemClip(pdev->paintEngine(), pdev->devicePixelRatioF(), toBePainted.translated(offset)); + setSystemClip(pdev->paintEngine(), pdev->devicePixelRatio(), toBePainted.translated(offset)); if (!onScreen && !asRoot && !isOpaque && q->testAttribute(Qt::WA_TintedBackground)) { -#ifndef QT_NO_OPENGL beginBackingStorePainting(); -#endif QPainter p(q); QColor tint = q->palette().window().color(); - tint.setAlphaF(qreal(.6)); + tint.setAlphaF(.6f); p.fillRect(toBePainted.boundingRect(), tint); -#ifndef QT_NO_OPENGL endBackingStorePainting(); -#endif } } @@ -5608,16 +5558,15 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP #endif bool skipPaintEvent = false; -#ifndef QT_NO_OPENGL if (renderToTexture) { // This widget renders into a texture which is composed later. We just need to // punch a hole in the backingstore, so the texture will be visible. beginBackingStorePainting(); - if (!q->testAttribute(Qt::WA_AlwaysStackOnTop) && backingStore) { + if (!q->testAttribute(Qt::WA_AlwaysStackOnTop) && repaintManager) { QPainter p(q); p.setCompositionMode(QPainter::CompositionMode_Source); p.fillRect(q->rect(), Qt::transparent); - } else if (!backingStore) { + } else if (!repaintManager) { // We are not drawing to a backingstore: fall back to QImage QImage img = grabFramebuffer(); // grabFramebuffer() always sets the format to RGB32 @@ -5634,28 +5583,22 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP else skipPaintEvent = true; } -#endif // QT_NO_OPENGL if (!skipPaintEvent) { //actually send the paint event sendPaintEvent(toBePainted); } - // Native widgets need to be marked dirty on screen so painting will be done in correct context - if (backingStore && !onScreen && !asRoot && (q->internalWinId() || (q->nativeParentWidget() && !q->nativeParentWidget()->isWindow()))) - backingStore->markDirtyOnScreen(toBePainted, q, offset); + if (repaintManager) + repaintManager->markNeedsFlush(q, toBePainted, offset); //restore if (paintEngine) { -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (pdev->devType() == QInternal::Widget) - static_cast<QWidget *>(pdev)->setAttribute(Qt::WA_WState_InPaintEvent, false); -#endif restoreRedirected(); if (!sharedPainter) paintEngine->d_func()->systemRect = QRect(); else - paintEngine->d_func()->currentClipDevice = 0; + paintEngine->d_func()->currentClipDevice = nullptr; setSystemClip(pdev->paintEngine(), 1, QRegion()); } @@ -5666,11 +5609,6 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP if (paintEngine && paintEngine->autoDestruct()) { delete paintEngine; } - -#ifndef QT_NO_PAINT_DEBUG - if (flushed) - QWidgetBackingStore::unflushPaint(q, toBePainted); -#endif } else if (q->isWindow()) { QPaintEngine *engine = pdev->paintEngine(); if (engine) { @@ -5689,8 +5627,8 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } if (recursive && !children.isEmpty()) { - paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot - , sharedPainter, backingStore); + paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot, + sharedPainter, repaintManager); } } @@ -5700,10 +5638,8 @@ void QWidgetPrivate::sendPaintEvent(const QRegion &toBePainted) QPaintEvent e(toBePainted); QCoreApplication::sendSpontaneousEvent(q, &e); -#ifndef QT_NO_OPENGL if (renderToTexture) resolveSamples(); -#endif // QT_NO_OPENGL } void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, @@ -5721,8 +5657,7 @@ void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, if (paintRegion.isEmpty()) return; -#if 1 // Used to be excluded in Qt4 for Q_WS_MAC - QPainter *oldSharedPainter = inRenderWithPainter ? sharedPainter() : 0; + QPainter *oldSharedPainter = inRenderWithPainter ? sharedPainter() : nullptr; // Use the target's shared painter if set (typically set when doing // "other->render(widget);" in the widget's paintEvent. @@ -5734,7 +5669,6 @@ void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, setSharedPainter(targetPainter); } } -#endif // Use the target's redirected device if set and adjust offset and paint // region accordingly. This is typically the case when people call render @@ -5742,12 +5676,10 @@ void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, QPoint offset = targetOffset; offset -= paintRegion.boundingRect().topLeft(); QPoint redirectionOffset; - QPaintDevice *redirected = 0; + QPaintDevice *redirected = nullptr; if (target->devType() == QInternal::Widget) redirected = static_cast<QWidget *>(target)->d_func()->redirected(&redirectionOffset); - if (!redirected) - redirected = QPainter::redirected(target, &redirectionOffset); if (redirected) { target = redirected; @@ -5763,7 +5695,7 @@ void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, } // Set backingstore flags. - int flags = DrawPaintOnScreen | DrawInvisible; + DrawWidgetFlags flags = DrawPaintOnScreen | DrawInvisible; if (renderFlags & QWidget::DrawWindowBackground) flags |= DrawAsRoot; @@ -5783,10 +5715,10 @@ void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, } void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& siblings, int index, const QRegion &rgn, - const QPoint &offset, int flags - , QPainter *sharedPainter, QWidgetBackingStore *backingStore) + const QPoint &offset, DrawWidgetFlags flags + , QPainter *sharedPainter, QWidgetRepaintManager *repaintManager) { - QWidget *w = 0; + QWidget *w = nullptr; QRect boundingRect; bool dirtyBoundingRect = true; const bool exludeOpaqueChildren = (flags & DontDrawOpaqueChildren); @@ -5819,8 +5751,8 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis QRegion wr(rgn); if (wd->isOpaque) wr -= hasMask ? wd->extra->mask.translated(widgetPos) : w->data->crect; - paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flags - , sharedPainter, backingStore); + paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flags, + sharedPainter, repaintManager); } if (w->updatesEnabled() @@ -5833,7 +5765,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis wRegion.translate(-widgetPos); if (hasMask) wRegion &= wd->extra->mask; - wd->drawWidget(pdev, wRegion, offset + widgetPos, flags, sharedPainter, backingStore); + wd->drawWidget(pdev, wRegion, offset + widgetPos, flags, sharedPainter, repaintManager); } } @@ -5868,7 +5800,7 @@ void QWidgetEffectSourcePrivate::draw(QPainter *painter) toBePainted &= wd->extra->mask; wd->drawWidget(context->pdev, toBePainted, context->offset, context->flags, - context->sharedPainter, context->backingStore); + context->sharedPainter, context->repaintManager); } QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset, @@ -5906,7 +5838,7 @@ QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint * qreal dpr(1.0); if (const auto *paintDevice = context->painter->device()) - dpr = paintDevice->devicePixelRatioF(); + dpr = paintDevice->devicePixelRatio(); else qWarning("QWidgetEffectSourcePrivate::pixmap: Painter not active"); QPixmap pixmap(effectRect.size() * dpr); @@ -5924,18 +5856,18 @@ QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint * Finds the nearest widget embedded in a graphics proxy widget along the chain formed by this widget and its ancestors. The search starts at \a origin (inclusive). - If successful, the function returns the proxy that embeds the widget, or 0 if no embedded - widget was found. + If successful, the function returns the proxy that embeds the widget, or \nullptr if no + embedded widget was found. */ -QGraphicsProxyWidget * QWidgetPrivate::nearestGraphicsProxyWidget(const QWidget *origin) +QGraphicsProxyWidget *QWidgetPrivate::nearestGraphicsProxyWidget(const QWidget *origin) { if (origin) { - QWExtra *extra = origin->d_func()->extra; + const auto &extra = origin->d_func()->extra; if (extra && extra->proxyWidget) return extra->proxyWidget; return nearestGraphicsProxyWidget(origin->parentWidget()); } - return 0; + return nullptr; } #endif @@ -5975,7 +5907,7 @@ void QWidgetPrivate::setLocale_helper(const QLocale &loc, bool forceUpdate) } } QEvent e(QEvent::LocaleChange); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } void QWidget::setLocale(const QLocale &locale) @@ -6041,7 +5973,7 @@ QString QWidget::windowTitle() const if (!d->extra->topextra->caption.isEmpty()) return d->extra->topextra->caption; if (!d->extra->topextra->filePath.isEmpty()) - return QFileInfo(d->extra->topextra->filePath).fileName() + QLatin1String("[*]"); + return QFileInfo(d->extra->topextra->filePath).fileName() + "[*]"_L1; } return QString(); } @@ -6061,17 +5993,11 @@ QString qt_setWindowTitle_helperHelper(const QString &title, const QWidget *widg { Q_ASSERT(widget); -#ifdef QT_EVAL - extern QString qt_eval_adapt_window_title(const QString &title); - QString cap = qt_eval_adapt_window_title(title); -#else QString cap = title; -#endif - if (cap.isEmpty()) return cap; - QLatin1String placeHolder("[*]"); + const auto placeHolder = "[*]"_L1; int index = cap.indexOf(placeHolder); // here the magic begins @@ -6086,7 +6012,7 @@ QString qt_setWindowTitle_helperHelper(const QString &title, const QWidget *widg if (count%2) { // odd number of [*] -> replace last one int lastIndex = cap.lastIndexOf(placeHolder, index - 1); if (widget->isWindowModified() - && widget->style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, 0, widget)) + && widget->style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, nullptr, widget)) cap.replace(lastIndex, 3, QWidget::tr("*")); else cap.remove(lastIndex, 3); @@ -6095,7 +6021,7 @@ QString qt_setWindowTitle_helperHelper(const QString &title, const QWidget *widg index = cap.indexOf(placeHolder, index); } - cap.replace(QLatin1String("[*][*]"), placeHolder); + cap.replace("[*][*]"_L1, placeHolder); return cap; } @@ -6127,11 +6053,17 @@ void QWidgetPrivate::setWindowIconText_helper(const QString &title) void QWidgetPrivate::setWindowIconText_sys(const QString &iconText) { +#if QT_CONFIG(xcb) Q_Q(QWidget); // ### The QWidget property is deprecated, but the XCB window function is not. // It should remain available for the rare application that needs it. - if (QWindow *window = q->windowHandle()) - QXcbWindowFunctions::setWmWindowIconText(window, iconText); + if (QWindow *window = q->windowHandle()) { + if (auto *xcbWindow = dynamic_cast<QXcbWindow*>(window->handle())) + xcbWindow->setWindowIconText(iconText); + } +#else + Q_UNUSED(iconText); +#endif } /*! @@ -6141,7 +6073,7 @@ void QWidgetPrivate::setWindowIconText_sys(const QString &iconText) new \a iconText as an argument. \since 5.2 - \obsolete + \deprecated This signal is deprecated. */ @@ -6156,7 +6088,7 @@ void QWidget::setWindowIconText(const QString &iconText) d->setWindowIconText_helper(iconText); QEvent e(QEvent::IconTextChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); emit windowIconTextChanged(iconText); } @@ -6175,14 +6107,28 @@ void QWidget::setWindowTitle(const QString &title) if (QWidget::windowTitle() == title && !title.isEmpty() && !title.isNull()) return; +#if QT_CONFIG(accessibility) + QString oldAccessibleName; + const QAccessibleInterface *accessible = QAccessible::queryAccessibleInterface(this); + if (accessible) + oldAccessibleName = accessible->text(QAccessible::Name); +#endif + Q_D(QWidget); d->topData()->caption = title; d->setWindowTitle_helper(title); QEvent e(QEvent::WindowTitleChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); emit windowTitleChanged(title); + +#if QT_CONFIG(accessibility) + if (accessible && accessible->text(QAccessible::Name) != oldAccessibleName) { + QAccessibleEvent event(this, QAccessible::NameChanged); + QAccessible::updateAccessibility(&event); + } +#endif } @@ -6194,7 +6140,11 @@ void QWidget::setWindowTitle(const QString &title) has been set, windowIcon() returns the application icon (QApplication::windowIcon()). - \sa windowTitle + \note On \macos, window icons represent the active document, + and will not be displayed unless a file path has also been + set using setWindowFilePath. + + \sa windowTitle, setWindowFilePath */ QIcon QWidget::windowIcon() const { @@ -6218,11 +6168,11 @@ void QWidgetPrivate::setWindowIcon_helper() // QWidgetWindow to the top level QWidget ensures that the event reaches // the top level anyhow if (!q->windowHandle()) - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast<QWidget *>(children.at(i)); if (w && !w->isWindow()) - QApplication::sendEvent(w, &e); + QCoreApplication::sendEvent(w, &e); } } @@ -6243,8 +6193,9 @@ void QWidget::setWindowIcon(const QIcon &icon) d->createTLExtra(); if (!d->extra->topextra->icon) - d->extra->topextra->icon = new QIcon(); - *d->extra->topextra->icon = icon; + d->extra->topextra->icon = std::make_unique<QIcon>(icon); + else + *d->extra->topextra->icon = icon; d->setWindowIcon_sys(); d->setWindowIcon_helper(); @@ -6268,7 +6219,7 @@ void QWidgetPrivate::setWindowIcon_sys() It is only implemented on the X11 platform, and only certain window managers use this window property. - \obsolete + \deprecated This property is deprecated. \sa windowIcon, windowTitle @@ -6366,11 +6317,17 @@ QString QWidget::windowRole() const */ void QWidget::setWindowRole(const QString &role) { +#if QT_CONFIG(xcb) Q_D(QWidget); d->createTLExtra(); d->topData()->role = role; - if (windowHandle()) - QXcbWindowFunctions::setWmWindowRole(windowHandle(), role.toLatin1()); + if (windowHandle()) { + if (auto *xcbWindow = dynamic_cast<QXcbWindow*>(windowHandle()->handle())) + xcbWindow->setWindowRole(role); + } +#else + Q_UNUSED(role); +#endif } /*! @@ -6407,7 +6364,7 @@ void QWidget::setWindowRole(const QString &role) /*! - Sets the widget's focus proxy to widget \a w. If \a w is 0, the + Sets the widget's focus proxy to widget \a w. If \a w is \nullptr, the function resets this widget to have no focus proxy. Some widgets can "have focus", but create a child widget, such as @@ -6416,7 +6373,8 @@ void QWidget::setWindowRole(const QString &role) setFocusProxy() sets the widget which will actually get focus when "this widget" gets it. If there is a focus proxy, setFocus() and - hasFocus() operate on the focus proxy. + hasFocus() operate on the focus proxy. If "this widget" is the focus + widget, then setFocusProxy() moves focus to the new focus proxy. \sa focusProxy() */ @@ -6434,21 +6392,56 @@ void QWidget::setFocusProxy(QWidget * w) } } + const bool moveFocusToProxy = (QApplicationPrivate::focus_widget == this); + d->createExtra(); d->extra->focus_proxy = w; + + if (w && isAncestorOf(w)) { + // If the focus proxy is a child of this (so this is a compound widget), then + // we need to make sure that this widget is immediately in front of its own children + // in the focus chain. Otherwise focusNextPrev_helper might jump over unrelated + // widgets that are positioned between this compound widget, and its proxy in + // the focus chain. + const QWidget *parentOfW = w->parentWidget(); + Q_ASSERT(parentOfW); // can't be nullptr since we are an ancestor of w + QWidget *firstChild = nullptr; + const auto childList = children(); + for (QObject *child : childList) { + if ((firstChild = qobject_cast<QWidget *>(child))) + break; + } + Q_ASSERT(firstChild); // can't be nullptr since w is a child + d->insertIntoFocusChainBefore(firstChild); + } else if (w && w->isAncestorOf(this)) { + // If the focus proxy is a parent, 'this' has to be inserted directly after its parent in the focus chain + // remove it from the chain and insert this into the focus chain after its parent + + // is this the case already? + QWidget *parentsNext = w->nextInFocusChain(); + if (parentsNext == this) { + // nothing to do. + Q_ASSERT(previousInFocusChain() == w); + } else { + d->QWidgetPrivate::insertIntoFocusChainAfter(w); + } + } + + if (moveFocusToProxy) + setFocus(Qt::OtherFocusReason); } /*! - Returns the focus proxy, or 0 if there is no focus proxy. + Returns the focus proxy, or \nullptr if there is no focus proxy. \sa setFocusProxy() */ -QWidget * QWidget::focusProxy() const +QWidget *QWidget::focusProxy() const { Q_D(const QWidget); - return d->extra ? (QWidget *)d->extra->focus_proxy : 0; + return d->extra ? d->extra->focus_proxy.data() : nullptr; } @@ -6471,7 +6464,7 @@ bool QWidget::hasFocus() const w = w->d_func()->extra->focus_proxy; #if QT_CONFIG(graphicsview) if (QWidget *window = w->window()) { - QWExtra *e = window->d_func()->extra; + const auto &e = window->d_func()->extra; if (e && e->proxyWidget && e->proxyWidget->hasFocus() && window->focusWidget() == w) return true; } @@ -6523,21 +6516,17 @@ void QWidget::setFocus(Qt::FocusReason reason) if (!f) f = this; - if (QApplication::focusWidget() == f -#if 0 // Used to be included in Qt4 for Q_WS_WIN - && GetFocus() == f->internalWinId() -#endif - ) + if (QApplication::focusWidget() == f) return; #if QT_CONFIG(graphicsview) - QWidget *previousProxyFocus = 0; - if (QWExtra *topData = window()->d_func()->extra) { + QWidget *previousProxyFocus = nullptr; + if (const auto &topData = window()->d_func()->extra) { if (topData->proxyWidget && topData->proxyWidget->hasFocus()) { previousProxyFocus = topData->proxyWidget->widget()->focusWidget(); if (previousProxyFocus && previousProxyFocus->focusProxy()) previousProxyFocus = previousProxyFocus->focusProxy(); - if (previousProxyFocus == this && !topData->proxyWidget->d_func()->proxyIsGivingFocus) + if (previousProxyFocus == f && !topData->proxyWidget->d_func()->proxyIsGivingFocus) return; } } @@ -6545,7 +6534,7 @@ void QWidget::setFocus(Qt::FocusReason reason) #if QT_CONFIG(graphicsview) // Update proxy state - if (QWExtra *topData = window()->d_func()->extra) { + if (const auto &topData = window()->d_func()->extra) { if (topData->proxyWidget && !topData->proxyWidget->hasFocus()) { f->d_func()->updateFocusChild(); topData->proxyWidget->d_func()->focusFromWidgetToProxy = 1; @@ -6565,19 +6554,14 @@ void QWidget::setFocus(Qt::FocusReason reason) if (reason != Qt::NoFocusReason) { QFocusEvent focusAboutToChange(QEvent::FocusAboutToChange, reason); - QApplication::sendEvent(prev, &focusAboutToChange); + QCoreApplication::sendEvent(prev, &focusAboutToChange); } } f->d_func()->updateFocusChild(); QApplicationPrivate::setFocusWidget(f, reason); -#ifndef QT_NO_ACCESSIBILITY -# ifdef Q_OS_WIN - // The negation of the condition in setFocus_sys - if (!(testAttribute(Qt::WA_WState_Created) && window()->windowType() != Qt::Popup && internalWinId())) - //setFocusWidget will already post a focus event for us (that the AT client receives) on Windows -# endif +#if QT_CONFIG(accessibility) // menus update the focus manually and this would create bogus events if (!(f->inherits("QMenuBar") || f->inherits("QMenu") || f->inherits("QMenuItem"))) { @@ -6586,29 +6570,29 @@ void QWidget::setFocus(Qt::FocusReason reason) } #endif #if QT_CONFIG(graphicsview) - if (QWExtra *topData = window()->d_func()->extra) { + if (const auto &topData = window()->d_func()->extra) { if (topData->proxyWidget) { if (previousProxyFocus && previousProxyFocus != f) { // Send event to self QFocusEvent event(QEvent::FocusOut, reason); QPointer<QWidget> that = previousProxyFocus; - QApplication::sendEvent(previousProxyFocus, &event); + QCoreApplication::sendEvent(previousProxyFocus, &event); if (that) - QApplication::sendEvent(that->style(), &event); + QCoreApplication::sendEvent(that->style(), &event); } if (!isHidden()) { #if QT_CONFIG(graphicsview) // Update proxy state - if (QWExtra *topData = window()->d_func()->extra) + if (const auto &topData = window()->d_func()->extra) if (topData->proxyWidget && topData->proxyWidget->hasFocus()) topData->proxyWidget->d_func()->updateProxyInputMethodAcceptanceFromWidget(); #endif // Send event to self QFocusEvent event(QEvent::FocusIn, reason); QPointer<QWidget> that = f; - QApplication::sendEvent(f, &event); + QCoreApplication::sendEvent(f, &event); if (that) - QApplication::sendEvent(that->style(), &event); + QCoreApplication::sendEvent(that->style(), &event); } } } @@ -6649,7 +6633,9 @@ void QWidgetPrivate::setFocus_sys() { Q_Q(QWidget); // Embedded native widget may have taken the focus; get it back to toplevel - // if that is the case (QTBUG-25852) + // if that is the case (QTBUG-25852), unless widget is a window container. + if (extra && extra->hasWindowContainer) + return; // Do not activate in case the popup menu opens another application (QTBUG-70810) // unless the application is embedded (QTBUG-71991). if (QWindow *nativeWindow = q->testAttribute(Qt::WA_WState_Created) ? q->window()->windowHandle() : nullptr) { @@ -6671,12 +6657,12 @@ void QWidgetPrivate::updateFocusChild() if (q->isHidden()) { while (w && w->isHidden()) { w->d_func()->focus_child = q; - w = w->isWindow() ? 0 : w->parentWidget(); + w = w->isWindow() ? nullptr : w->parentWidget(); } } else { while (w) { w->d_func()->focus_child = q; - w = w->isWindow() ? 0 : w->parentWidget(); + w = w->isWindow() ? nullptr : w->parentWidget(); } } @@ -6701,8 +6687,8 @@ void QWidgetPrivate::updateFocusChild() If the widget has active focus, a \l{focusOutEvent()}{focus out event} is sent to this widget to tell it that it has lost the focus. - This widget must enable focus setting in order to get the keyboard - input focus, i.e. it must call setFocusPolicy(). + This widget must enable focus setting to get the keyboard + input focus; that is, it must call setFocusPolicy(). \sa hasFocus(), setFocus(), focusInEvent(), focusOutEvent(), setFocusPolicy(), QApplication::focusWidget() @@ -6715,46 +6701,47 @@ void QWidget::clearFocus() QGuiApplication::inputMethod()->commit(); QFocusEvent focusAboutToChange(QEvent::FocusAboutToChange); - QApplication::sendEvent(this, &focusAboutToChange); + QCoreApplication::sendEvent(this, &focusAboutToChange); + } + + QTLWExtra *extra = window()->d_func()->maybeTopData(); + QObject *originalFocusObject = nullptr; + if (extra && extra->window) { + originalFocusObject = extra->window->focusObject(); + // the window's focus object might already be nullptr if we are in the destructor, but we still + // need to update QGuiApplication and input context if we have a focus widget. + if (!originalFocusObject) + originalFocusObject = focusWidget(); } QWidget *w = this; while (w) { // Just like setFocus(), we update (clear) the focus_child of our parents if (w->d_func()->focus_child == this) - w->d_func()->focus_child = 0; + w->d_func()->focus_child = nullptr; w = w->parentWidget(); } - // Since we've unconditionally cleared the focus_child of our parents, we need + // We've potentially cleared the focus_child of our parents, so we need // to report this to the rest of Qt. Note that the focus_child is not the same // thing as the application's focusWidget, which is why this piece of code is - // not inside the hasFocus() block below. - if (QTLWExtra *extra = window()->d_func()->maybeTopData()) { - if (extra->window) - emit extra->window->focusObjectChanged(extra->window->focusObject()); - } + // not inside a hasFocus() block. + if (originalFocusObject && originalFocusObject != extra->window->focusObject()) + emit extra->window->focusObjectChanged(extra->window->focusObject()); #if QT_CONFIG(graphicsview) - QWExtra *topData = d_func()->extra; + const auto &topData = d_func()->extra; if (topData && topData->proxyWidget) topData->proxyWidget->clearFocus(); #endif if (hasFocus()) { // Update proxy state - QApplicationPrivate::setFocusWidget(0, Qt::OtherFocusReason); -#if 0 // Used to be included in Qt4 for Q_WS_WIN - if (!(windowType() == Qt::Popup) && GetFocus() == internalWinId()) - SetFocus(0); - else -#endif - { -#ifndef QT_NO_ACCESSIBILITY - QAccessibleEvent event(this, QAccessible::Focus); - QAccessible::updateAccessibility(&event); + QApplicationPrivate::setFocusWidget(nullptr, Qt::OtherFocusReason); +#if QT_CONFIG(accessibility) + QAccessibleEvent event(this, QAccessible::Focus); + QAccessible::updateAccessibility(&event); #endif - } } } @@ -6827,10 +6814,10 @@ bool QWidget::focusNextPrevChild(bool next) */ if (wrappingOccurred) { QWindow *window = windowHandle(); - if (window != 0) { + if (window != nullptr) { QWindowPrivate *winp = qt_window_private(window); - if (winp->platformWindow != 0) { + if (winp->platformWindow != nullptr) { QFocusEvent event(QEvent::FocusIn, reason); event.ignore(); winp->platformWindow->windowEvent(&event); @@ -6857,6 +6844,13 @@ QWidget *QWidget::focusWidget() const return const_cast<QWidget *>(d_func()->focus_child); } +QObject *QWidgetPrivate::focusObject() +{ + Q_Q(QWidget); + QWidget *proxy = deepestFocusProxy(); + return proxy ? proxy : q; +} + /*! Returns the next widget in this widget's focus chain. @@ -6864,7 +6858,8 @@ QWidget *QWidget::focusWidget() const */ QWidget *QWidget::nextInFocusChain() const { - return const_cast<QWidget *>(d_func()->focus_next); + Q_D(const QWidget); + return d->nextPrevElementInFocusChain(QWidgetPrivate::FocusDirection::Next); } /*! @@ -6877,7 +6872,8 @@ QWidget *QWidget::nextInFocusChain() const */ QWidget *QWidget::previousInFocusChain() const { - return const_cast<QWidget *>(d_func()->focus_prev); + Q_D(const QWidget); + return d->nextPrevElementInFocusChain(QWidgetPrivate::FocusDirection::Previous); } /*! @@ -6898,18 +6894,18 @@ QWidget *QWidget::previousInFocusChain() const bool QWidget::isActiveWindow() const { QWidget *tlw = window(); - if(tlw == QApplication::activeWindow() || (isVisible() && (tlw->windowType() == Qt::Popup))) + if (tlw == QApplication::activeWindow() || (isVisible() && (tlw->windowType() == Qt::Popup))) return true; #if QT_CONFIG(graphicsview) - if (QWExtra *tlwExtra = tlw->d_func()->extra) { + if (const auto &tlwExtra = tlw->d_func()->extra) { if (isVisible() && tlwExtra->proxyWidget) return tlwExtra->proxyWidget->isActiveWindow(); } #endif - if(style()->styleHint(QStyle::SH_Widget_ShareActivation, 0, this)) { - if(tlw->windowType() == Qt::Tool && + if (style()->styleHint(QStyle::SH_Widget_ShareActivation, nullptr, this)) { + if (tlw->windowType() == Qt::Tool && !tlw->isModal() && (!tlw->parentWidget() || tlw->parentWidget()->isActiveWindow())) return true; @@ -6917,7 +6913,7 @@ bool QWidget::isActiveWindow() const while(w && tlw->windowType() == Qt::Tool && !w->isModal() && w->parentWidget()) { w = w->parentWidget()->window(); - if(w == tlw) + if (w == tlw) return true; } } @@ -6947,6 +6943,30 @@ bool QWidget::isActiveWindow() const } /*! + \fn void QWidget::setTabOrder(std::initializer_list<QWidget *> widgets) + \overload + \since 6.6 + + Sets the tab order for the widgets in the \a widgets list by calling + \l{QWidget::setTabOrder(QWidget *, QWidget *)} for each consecutive + pair of widgets. + + Instead of setting up each pair manually like this: + + \snippet code/src_gui_kernel_qwidget.cpp 9 + + you can call: + + \snippet code/src_gui_kernel_qwidget.cpp 9.list + + The call does not create a closed tab focus loop. If there are more widgets + with \l{Qt::TabFocus} focus policy, tabbing on \c{d} will move focus to one + of those widgets, not back to \c{a}. + + \sa setFocusPolicy(), setFocusProxy(), {Keyboard Focus in Widgets} +*/ + +/*! Puts the \a second widget after the \a first widget in the focus order. It effectively removes the \a second widget from its focus chain and @@ -6984,64 +7004,74 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second) return; } - auto determineLastFocusChild = [](QWidget *target, QWidget *&lastFocusChild) + const auto determineLastFocusChild = [](QWidget *target, QWidget *noFurtherThan) { // Since we need to repeat the same logic for both 'first' and 'second', we add a function that // determines the last focus child for a widget, taking proxies and compound widgets into account. // If the target is not a compound widget (it doesn't have a focus proxy that points to a child), // 'lastFocusChild' will be set to the target itself. - lastFocusChild = target; + QWidget *lastFocusChild = target; QWidget *focusProxy = target->d_func()->deepestFocusProxy(); - if (!focusProxy || !target->isAncestorOf(focusProxy)) - return; - - lastFocusChild = focusProxy; - - for (QWidget *focusNext = lastFocusChild->d_func()->focus_next; - focusNext != focusProxy && target->isAncestorOf(focusNext) && focusNext->window() == focusProxy->window(); - focusNext = focusNext->d_func()->focus_next) { - if (focusNext->focusPolicy() != Qt::NoFocus) - lastFocusChild = focusNext; + if (!focusProxy) { + // QTBUG-81097: Another case is possible here. We can have a child + // widget, that sets its focusProxy() to the parent (target). + // An example of such widget is a QLineEdit, nested into + // a QAbstractSpinBox. In this case such widget should be considered + // the last focus child. + for (auto *object : target->children()) { + QWidget *w = qobject_cast<QWidget*>(object); + if (w && w->focusProxy() == target) { + lastFocusChild = w; + break; + } + } + } else if (target->isAncestorOf(focusProxy)) { + lastFocusChild = focusProxy; + for (QWidget *focusNext = lastFocusChild->nextInFocusChain(); + focusNext != focusProxy && target->isAncestorOf(focusNext) && focusNext->window() == focusProxy->window(); + focusNext = focusNext->nextInFocusChain()) { + if (focusNext == noFurtherThan) + break; + if (focusNext->focusPolicy() != Qt::NoFocus) + lastFocusChild = focusNext; + } } + return lastFocusChild; }; - auto setPrev = [](QWidget *w, QWidget *prev) - { - w->d_func()->focus_prev = prev; - }; - auto setNext = [](QWidget *w, QWidget *next) - { - w->d_func()->focus_next = next; - }; - - // remove the second widget from the chain - QWidget *lastFocusChildOfSecond; - determineLastFocusChild(second, lastFocusChildOfSecond); - { - QWidget *oldPrev = second->d_func()->focus_prev; - QWidget *prevWithFocus = oldPrev; - while (prevWithFocus->focusPolicy() == Qt::NoFocus) - prevWithFocus = prevWithFocus->d_func()->focus_prev; - // only widgets between first and second -> all is fine - if (prevWithFocus == first) - return; - QWidget *oldNext = lastFocusChildOfSecond->d_func()->focus_next; - setPrev(oldNext, oldPrev); - setNext(oldPrev, oldNext); - } + // detect inflection in case we have compound widgets + QWidget *lastFocusChildOfFirst = determineLastFocusChild(first, second); + if (lastFocusChildOfFirst == second) + lastFocusChildOfFirst = first; + QWidget *lastFocusChildOfSecond = determineLastFocusChild(second, first); + if (lastFocusChildOfSecond == first) + lastFocusChildOfSecond = second; + + // Return if only NoFocus widgets are between first and second + QWidget *oldPrev = second->previousInFocusChain(); + QWidget *prevWithFocus = oldPrev; + while (prevWithFocus->focusPolicy() == Qt::NoFocus) + prevWithFocus = prevWithFocus->previousInFocusChain(); + if (prevWithFocus == first) + return; + const QWidgetList chain = QWidgetPrivate::takeFromFocusChain(second, lastFocusChildOfSecond); + QWidgetPrivate::insertIntoFocusChain(chain, QWidgetPrivate::FocusDirection::Next, lastFocusChildOfFirst); +} - // insert the second widget into the chain - QWidget *lastFocusChildOfFirst; - determineLastFocusChild(first, lastFocusChildOfFirst); - { - QWidget *oldNext = lastFocusChildOfFirst->d_func()->focus_next; - setPrev(second, lastFocusChildOfFirst); - setNext(lastFocusChildOfFirst, second); - setPrev(oldNext, lastFocusChildOfSecond); - setNext(lastFocusChildOfSecond, oldNext); +void QWidget::setTabOrder(std::initializer_list<QWidget *> widgets) +{ + QWidget *prev = nullptr; + for (const auto &widget : widgets) { + if (!prev) { + prev = widget; + } else { + QWidget::setTabOrder(prev, widget); + prev = widget; + } } } + /*!\internal Moves the relevant subwidgets of this widget from the \a oldtlw's @@ -7060,94 +7090,10 @@ void QWidgetPrivate::reparentFocusWidgets(QWidget * oldtlw) if (oldtlw == q->window()) return; // nothing to do - if(focus_child) + if (focus_child) focus_child->clearFocus(); - // separate the focus chain into new (children of myself) and old (the rest) - QWidget *firstOld = 0; - //QWidget *firstNew = q; //invariant - QWidget *o = 0; // last in the old list - QWidget *n = q; // last in the new list - - bool prevWasNew = true; - QWidget *w = focus_next; - - //Note: for efficiency, we do not maintain the list invariant inside the loop - //we append items to the relevant list, and we optimize by not changing pointers - //when subsequent items are going into the same list. - while (w != q) { - bool currentIsNew = q->isAncestorOf(w); - if (currentIsNew) { - if (!prevWasNew) { - //prev was old -- append to new list - n->d_func()->focus_next = w; - w->d_func()->focus_prev = n; - } - n = w; - } else { - if (prevWasNew) { - //prev was new -- append to old list, if there is one - if (o) { - o->d_func()->focus_next = w; - w->d_func()->focus_prev = o; - } else { - // "create" the old list - firstOld = w; - } - } - o = w; - } - w = w->d_func()->focus_next; - prevWasNew = currentIsNew; - } - - //repair the old list: - if (firstOld) { - o->d_func()->focus_next = firstOld; - firstOld->d_func()->focus_prev = o; - } - - if (!q->isWindow()) { - QWidget *topLevel = q->window(); - //insert new chain into toplevel's chain - - QWidget *prev = topLevel->d_func()->focus_prev; - - topLevel->d_func()->focus_prev = n; - prev->d_func()->focus_next = q; - - focus_prev = prev; - n->d_func()->focus_next = topLevel; - } else { - //repair the new list - n->d_func()->focus_next = q; - focus_prev = n; - } - -} - -/*!\internal - - Measures the shortest distance from a point to a rect. - - This function is called from QDesktopwidget::screen(QPoint) to find the - closest screen for a point. - In directional KeypadNavigation, it is called to find the closest - widget to the current focus widget center. -*/ -int QWidgetPrivate::pointToRect(const QPoint &p, const QRect &r) -{ - int dx = 0; - int dy = 0; - if (p.x() < r.left()) - dx = r.left() - p.x(); - else if (p.x() > r.right()) - dx = p.x() - r.right(); - if (p.y() < r.top()) - dy = r.top() - p.y(); - else if (p.y() > r.bottom()) - dy = p.y() - r.bottom(); - return dx + dy; + reparentFocusChildren(QWidgetPrivate::FocusDirection::Next); } /*! @@ -7244,8 +7190,10 @@ void QWidget::resize(const QSize &s) d->setGeometry_sys(geometry().x(), geometry().y(), s.width(), s.height(), false); d->setDirtyOpaqueRegion(); } else { + const auto oldRect = data->crect; data->crect.setSize(s.boundedTo(maximumSize()).expandedTo(minimumSize())); - setAttribute(Qt::WA_PendingResizeEvent); + if (oldRect != data->crect) + setAttribute(Qt::WA_PendingResizeEvent); } } @@ -7260,10 +7208,13 @@ void QWidget::setGeometry(const QRect &r) d->setGeometry_sys(r.x(), r.y(), r.width(), r.height(), true); d->setDirtyOpaqueRegion(); } else { + const auto oldRect = data->crect; data->crect.setTopLeft(r.topLeft()); data->crect.setSize(r.size().boundedTo(maximumSize()).expandedTo(minimumSize())); - setAttribute(Qt::WA_PendingMoveEvent); - setAttribute(Qt::WA_PendingResizeEvent); + if (oldRect != data->crect) { + setAttribute(Qt::WA_PendingMoveEvent); + setAttribute(Qt::WA_PendingResizeEvent); + } } if (d->extra && d->extra->hasWindowContainer) @@ -7351,22 +7302,22 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (renderToTexture) { QRegion updateRegion(q->geometry()); updateRegion += QRect(oldPos, olds); - q->parentWidget()->d_func()->invalidateBuffer(updateRegion); + q->parentWidget()->d_func()->invalidateBackingStore(updateRegion); } else if (isMove && !isResize) { moveRect(QRect(oldPos, olds), x - oldPos.x(), y - oldPos.y()); } else { - invalidateBuffer_resizeHelper(oldPos, olds); + invalidateBackingStore_resizeHelper(oldPos, olds); } } } if (isMove) { QMoveEvent e(q->pos(), oldPos); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } if (isResize) { QResizeEvent e(r.size(), olds); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); if (q->windowHandle()) q->update(); } @@ -7398,18 +7349,6 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) */ QByteArray QWidget::saveGeometry() const { -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // We check if the window was maximized during this invocation. If so, we need to record the - // starting position as 0,0. - Q_D(const QWidget); - QRect newFramePosition = frameGeometry(); - QRect newNormalPosition = normalGeometry(); - if(d->topData()->wasMaximized && !(windowState() & Qt::WindowMaximized)) { - // Change the starting position - newFramePosition.moveTo(0, 0); - newNormalPosition.moveTo(0, 0); - } -#endif QByteArray array; QDataStream stream(&array, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_4_0); @@ -7420,34 +7359,81 @@ QByteArray QWidget::saveGeometry() const // - Qt 5.12 - today : Version 3.0, save QWidget::geometry() quint16 majorVersion = 3; quint16 minorVersion = 0; - const int screenNumber = QDesktopWidgetPrivate::screenNumber(this); + const int screenNumber = QGuiApplication::screens().indexOf(screen()); stream << magicNumber << majorVersion << minorVersion -#if 0 // Used to be included in Qt4 for Q_WS_MAC - << newFramePosition - << newNormalPosition -#else << frameGeometry() << normalGeometry() -#endif << qint32(screenNumber) << quint8(windowState() & Qt::WindowMaximized) << quint8(windowState() & Qt::WindowFullScreen) - << qint32(QDesktopWidgetPrivate::screenGeometry(screenNumber).width()) // added in 2.0 + << qint32(screen()->geometry().width()) // added in 2.0 << geometry(); // added in 3.0 return array; } -static void checkRestoredGeometry(const QRect &availableGeometry, QRect *restoredGeometry, +/*! + \internal + + Check a if \a restoredGeometry fits into \a availableGeometry + This method is used to verify that a widget is restored to a geometry, which + fits into the target screen. + + \param frameHeight represents the height of the widget's title bar, which is expected + to be on its top. + + If the size of \a restoredGeometry exceeds \a availableGeometry, its height and width + will be resized to be two pixels smaller than \a availableGeometry. An exact match would + be full screen. + + If at least one edge of \a restoredGeometry is outside \a availableGeometry, + \a restoredGeometry will be moved + \list + \li down if its top is off screen + \li up if its bottom is off screen + \li right if its left edge is off screen + \li left if its right edge is off screen + \endlist + */ +void QWidgetPrivate::checkRestoredGeometry(const QRect &availableGeometry, QRect *restoredGeometry, int frameHeight) { - if (!restoredGeometry->intersects(availableGeometry)) { - restoredGeometry->moveBottom(qMin(restoredGeometry->bottom(), availableGeometry.bottom())); - restoredGeometry->moveLeft(qMax(restoredGeometry->left(), availableGeometry.left())); - restoredGeometry->moveRight(qMin(restoredGeometry->right(), availableGeometry.right())); + // compare with restored geometry's height increased by frameHeight + const int height = restoredGeometry->height() + frameHeight; + + // Step 1: Resize if necessary: + // make height / width 2px smaller than screen, because an exact match would be fullscreen + if (availableGeometry.height() <= height) + restoredGeometry->setHeight(availableGeometry.height() - 2 - frameHeight); + if (availableGeometry.width() <= restoredGeometry->width()) + restoredGeometry->setWidth(availableGeometry.width() - 2); + + // Step 2: Move if necessary: + // Construct a rectangle from restored Geometry adjusted by frameHeight + const QRect restored = restoredGeometry->adjusted(0, -frameHeight, 0, 0); + + // Return if restoredGeometry (including frame) fits into screen + if (availableGeometry.contains(restored)) + return; + + // (size is correct, but at least one edge is off screen) + + // Top out of bounds => move down + if (restored.top() <= availableGeometry.top()) { + restoredGeometry->moveTop(availableGeometry.top() + 1 + frameHeight); + } else if (restored.bottom() >= availableGeometry.bottom()) { + // Bottom out of bounds => move up + restoredGeometry->moveBottom(availableGeometry.bottom() - 1); + } + + // Left edge out of bounds => move right + if (restored.left() <= availableGeometry.left()) { + restoredGeometry->moveLeft(availableGeometry.left() + 1); + } else if (restored.right() >= availableGeometry.right()) { + // Right edge out of bounds => move left + restoredGeometry->moveRight(availableGeometry.right() - 1); } - restoredGeometry->moveTop(qMax(restoredGeometry->top(), availableGeometry.top() + frameHeight)); } /*! @@ -7517,9 +7503,10 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) // ### Qt 6 - Perhaps it makes sense to dumb down the restoreGeometry() logic, see QTBUG-69104 - if (restoredScreenNumber >= QDesktopWidgetPrivate::numScreens()) - restoredScreenNumber = QDesktopWidgetPrivate::primaryScreen(); - const qreal screenWidthF = qreal(QDesktopWidgetPrivate::screenGeometry(restoredScreenNumber).width()); + if (restoredScreenNumber >= qMax(QGuiApplication::screens().size(), 1)) + restoredScreenNumber = 0; + const QScreen *restoredScreen = QGuiApplication::screens().value(restoredScreenNumber, nullptr); + const qreal screenWidthF = restoredScreen ? qreal(restoredScreen->geometry().width()) : 0; // Sanity check bailing out when large variations of screen sizes occur due to // high DPI scaling or different levels of DPI awareness. if (restoredScreenWidth) { @@ -7533,7 +7520,9 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) return false; } - const int frameHeight = 20; + const int frameHeight = QApplication::style() + ? QApplication::style()->pixelMetric(QStyle::PM_TitleBarHeight, nullptr, this) + : 20; if (!restoredNormalGeometry.isValid()) restoredNormalGeometry = QRect(QPoint(0, frameHeight), sizeHint()); @@ -7544,20 +7533,16 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) .expandedTo(d_func()->adjustedSize())); } - const QRect availableGeometry = QDesktopWidgetPrivate::availableGeometry(restoredScreenNumber); + const QRect availableGeometry = restoredScreen ? restoredScreen->availableGeometry() + : QRect(); // Modify the restored geometry if we are about to restore to coordinates // that would make the window "lost". This happens if: - // - The restored geometry is completely oustside the available geometry + // - The restored geometry is completely or partly oustside the available geometry // - The title bar is outside the available geometry. - // - (Mac only) The window is higher than the available geometry. It must - // be possible to bring the size grip on screen by moving the window. -#if 0 // Used to be included in Qt4 for Q_WS_MAC - restoredNormalGeometry.setHeight(qMin(restoredNormalGeometry.height(), availableGeometry.height() - frameHeight)); -#endif - checkRestoredGeometry(availableGeometry, &restoredGeometry, frameHeight); - checkRestoredGeometry(availableGeometry, &restoredNormalGeometry, frameHeight); + QWidgetPrivate::checkRestoredGeometry(availableGeometry, &restoredGeometry, frameHeight); + QWidgetPrivate::checkRestoredGeometry(availableGeometry, &restoredNormalGeometry, frameHeight); if (maximized || fullScreen) { // set geometry before setting the window state to make @@ -7573,7 +7558,7 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) // Setting a geometry on an already maximized window causes this to be // restored into a broken, half-maximized state, non-resizable state (QTBUG-4397). // Move the window in normal state if needed. - if (restoredScreenNumber != QDesktopWidgetPrivate::screenNumber(this)) { + if (restoredScreen != screen()) { setWindowState(Qt::WindowNoState); setGeometry(restoredNormalGeometry); } @@ -7589,6 +7574,8 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) d_func()->topData()->normalGeometry = restoredNormalGeometry; } else { setWindowState(windowState() & ~(Qt::WindowMaximized | Qt::WindowFullScreen)); + + // FIXME: Why fall back to restoredNormalGeometry if majorVersion <= 2? if (majorVersion > 2) setGeometry(restoredGeometry); else @@ -7611,7 +7598,7 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) Changing the margins will trigger a resizeEvent(). - \sa contentsRect(), getContentsMargins() + \sa contentsRect(), contentsMargins() */ void QWidget::setContentsMargins(int left, int top, int right, int bottom) { @@ -7641,7 +7628,7 @@ void QWidget::setContentsMargins(int left, int top, int right, int bottom) Changing the margins will trigger a resizeEvent(). - \sa contentsRect(), getContentsMargins() + \sa contentsRect(), contentsMargins() */ void QWidget::setContentsMargins(const QMargins &margins) { @@ -7661,39 +7648,13 @@ void QWidgetPrivate::updateContentsRect() if (q->isVisible()) { q->update(); QResizeEvent e(q->data->crect.size(), q->data->crect.size()); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); } else { q->setAttribute(Qt::WA_PendingResizeEvent, true); } QEvent e(QEvent::ContentsRectChange); - QApplication::sendEvent(q, &e); -} - -/*! - Returns the widget's contents margins for \a left, \a top, \a - right, and \a bottom. - - \sa setContentsMargins(), contentsRect() - */ -void QWidget::getContentsMargins(int *left, int *top, int *right, int *bottom) const -{ - QMargins m = contentsMargins(); - if (left) - *left = m.left(); - if (top) - *top = m.top(); - if (right) - *right = m.right(); - if (bottom) - *bottom = m.bottom(); -} - -// FIXME: Move to qmargins.h for next minor Qt release -QMargins operator|(const QMargins &m1, const QMargins &m2) -{ - return QMargins(qMax(m1.left(), m2.left()), qMax(m1.top(), m2.top()), - qMax(m1.right(), m2.right()), qMax(m1.bottom(), m2.bottom())); + QCoreApplication::sendEvent(q, &e); } /*! @@ -7701,7 +7662,7 @@ QMargins operator|(const QMargins &m1, const QMargins &m2) \brief The contentsMargins function returns the widget's contents margins. - \sa getContentsMargins(), setContentsMargins(), contentsRect() + \sa setContentsMargins(), contentsRect() */ QMargins QWidget::contentsMargins() const { @@ -7714,7 +7675,7 @@ QMargins QWidget::contentsMargins() const /*! Returns the area inside the widget's margins. - \sa setContentsMargins(), getContentsMargins() + \sa setContentsMargins(), contentsMargins() */ QRect QWidget::contentsRect() const { @@ -7746,11 +7707,15 @@ QMargins QWidgetPrivate::safeAreaMargins() const return QMargins(); // Or, if one of our ancestors are in a layout that does not have WA_LayoutOnEntireRect - // set, then we know that the layout has already taken care of placing us inside the - // safe area, by taking the contents rect of its parent widget into account. + // set, and the widget respects the safe area, then we know that the layout has already + // taken care of placing us inside the safe area, by taking the contents rect of its + // parent widget into account. const QWidget *assumedSafeWidget = nullptr; for (const QWidget *w = q; w != nativeWidget; w = w->parentWidget()) { QWidget *parentWidget = w->parentWidget(); + if (!parentWidget->testAttribute(Qt::WA_ContentsMarginsRespectsSafeArea)) + continue; // Layout can't help us + if (parentWidget->testAttribute(Qt::WA_LayoutOnEntireRect)) continue; // Layout not going to help us @@ -7761,7 +7726,7 @@ QMargins QWidgetPrivate::safeAreaMargins() const if (layout->geometry().isNull()) continue; // Layout hasn't been activated yet - if (layout->indexOf(const_cast<QWidget *>(w)) < 0) + if (layout->indexOf(w) < 0) continue; // Widget is not in layout assumedSafeWidget = w; @@ -7913,21 +7878,29 @@ void QWidget::setUpdatesEnabled(bool enable) /*! Shows the widget and its child widgets. - This is equivalent to calling showFullScreen(), showMaximized(), or setVisible(true), - depending on the platform's default behavior for the window flags. + For child windows, this is equivalent to calling setVisible(true). + Otherwise, it is equivalent to calling showFullScreen(), showMaximized(), + or setVisible(true), depending on the platform's default behavior for the window flags. - \sa raise(), showEvent(), hide(), setVisible(), showMinimized(), showMaximized(), + \sa raise(), showEvent(), hide(), setVisible(), showMinimized(), showMaximized(), showNormal(), isVisible(), windowFlags() */ void QWidget::show() { - Qt::WindowState defaultState = QGuiApplicationPrivate::platformIntegration()->defaultWindowState(data->window_flags); - if (defaultState == Qt::WindowFullScreen) - showFullScreen(); - else if (defaultState == Qt::WindowMaximized) - showMaximized(); - else - setVisible(true); // FIXME: Why not showNormal(), like QWindow::show()? + // Note: We don't call showNormal() as not to clobber Qt::Window(Max/Min)imized + + if (!isWindow()) { + setVisible(true); + } else { + const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration(); + Qt::WindowState defaultState = platformIntegration->defaultWindowState(data->window_flags); + if (defaultState == Qt::WindowFullScreen) + showFullScreen(); + else if (defaultState == Qt::WindowMaximized) + showMaximized(); + else + setVisible(true); + } } /*! \internal @@ -7963,13 +7936,13 @@ void QWidgetPrivate::sendPendingMoveAndResizeEvents(bool recursive, bool disable if (q->testAttribute(Qt::WA_PendingMoveEvent)) { QMoveEvent e(data.crect.topLeft(), data.crect.topLeft()); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); q->setAttribute(Qt::WA_PendingMoveEvent, false); } if (q->testAttribute(Qt::WA_PendingResizeEvent)) { QResizeEvent e(data.crect.size(), QSize()); - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); q->setAttribute(Qt::WA_PendingResizeEvent, false); } @@ -8069,24 +8042,16 @@ void QWidgetPrivate::show_helper() Q_UNUSED(isEmbedded); #endif - // On Windows, show the popup now so that our own focus handling - // stores the correct old focus widget even if it's stolen in the - // showevent -#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ || 0 /* Used to be included in Qt4 for Q_WS_MAC */ - if (!isEmbedded && q->windowType() == Qt::Popup) - qApp->d_func()->openPopup(q); -#endif - // send the show event before showing the window QShowEvent showEvent; - QApplication::sendEvent(q, &showEvent); + QCoreApplication::sendEvent(q, &showEvent); show_sys(); if (!isEmbedded && q->windowType() == Qt::Popup) qApp->d_func()->openPopup(q); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (q->windowType() != Qt::ToolTip) { // Tooltips are read aloud twice in MS narrator. QAccessibleEvent event(q, QAccessible::ObjectShow); QAccessible::updateAccessibility(&event); @@ -8094,7 +8059,7 @@ void QWidgetPrivate::show_helper() #endif if (QApplicationPrivate::hidden_focus_widget == q) { - QApplicationPrivate::hidden_focus_widget = 0; + QApplicationPrivate::hidden_focus_widget = nullptr; q->setFocus(Qt::OtherFocusReason); } @@ -8102,7 +8067,7 @@ void QWidgetPrivate::show_helper() // is spinnning; otherwise it might not show up on particular platforms. // This makes QSplashScreen behave the same on all platforms. if (!qApp->d_func()->in_exec && q->windowType() == Qt::SplashScreen) - QApplication::processEvents(); + QCoreApplication::processEvents(); data.in_show = false; // reset qws optimization } @@ -8111,10 +8076,10 @@ void QWidgetPrivate::show_sys() { Q_Q(QWidget); - QWidgetWindow *window = windowHandle(); + auto window = qobject_cast<QWidgetWindow *>(windowHandle()); if (q->testAttribute(Qt::WA_DontShowOnScreen)) { - invalidateBuffer(q->rect()); + invalidateBackingStore(q->rect()); q->setAttribute(Qt::WA_Mapped); // add our window the modal window list (native dialogs) if (window && q->isWindow() @@ -8128,9 +8093,9 @@ void QWidgetPrivate::show_sys() } if (renderToTexture && !q->isWindow()) - QApplication::postEvent(q->parentWidget(), new QUpdateLaterEvent(q->geometry())); + QCoreApplication::postEvent(q->parentWidget(), new QUpdateLaterEvent(q->geometry())); else - QApplication::postEvent(q, new QUpdateLaterEvent(q->rect())); + QCoreApplication::postEvent(q, new QUpdateLaterEvent(q->rect())); if ((!q->isWindow() && !q->testAttribute(Qt::WA_NativeWindow)) || q->testAttribute(Qt::WA_OutsideWSRange)) { @@ -8157,7 +8122,7 @@ void QWidgetPrivate::show_sys() #ifndef QT_NO_CURSOR qt_qpa_set_cursor(q, false); // Needed in case cursor was set before show #endif - invalidateBuffer(q->rect()); + invalidateBackingStore(q->rect()); window->setNativeWindowVisibility(true); // Was the window moved by the Window system or QPlatformWindow::initialGeometry() ? if (window->isTopLevel()) { @@ -8193,7 +8158,7 @@ void QWidgetPrivate::hide_helper() bool isEmbedded = false; #if QT_CONFIG(graphicsview) - isEmbedded = q->isWindow() && !bypassGraphicsProxyWidget(q) && nearestGraphicsProxyWidget(q->parentWidget()) != 0; + isEmbedded = q->isWindow() && !bypassGraphicsProxyWidget(q) && nearestGraphicsProxyWidget(q->parentWidget()) != nullptr; #else Q_UNUSED(isEmbedded); #endif @@ -8201,12 +8166,6 @@ void QWidgetPrivate::hide_helper() if (!isEmbedded && (q->windowType() == Qt::Popup)) qApp->d_func()->closePopup(q); -#if 0 // Used to be included in Qt4 for Q_WS_WIN - if (q->isWindow() && !(q->windowType() == Qt::Popup) && q->parentWidget() - && !q->parentWidget()->isHidden() && q->isActiveWindow()) - q->parentWidget()->activateWindow(); // Activate parent -#endif - q->setAttribute(Qt::WA_Mapped, false); hide_sys(); @@ -8218,7 +8177,7 @@ void QWidgetPrivate::hide_helper() } QHideEvent hideEvent; - QApplication::sendEvent(q, &hideEvent); + QCoreApplication::sendEvent(q, &hideEvent); hideChildren(false); // next bit tries to move the focus if the focus widget is now @@ -8235,10 +8194,10 @@ void QWidgetPrivate::hide_helper() } } - if (QWidgetBackingStore *bs = maybeBackingStore()) - bs->removeDirtyWidget(q); + if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) + repaintManager->removeDirtyWidget(q); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (wasVisible) { QAccessibleEvent event(q, QAccessible::ObjectHide); QAccessible::updateAccessibility(&event); @@ -8250,7 +8209,7 @@ void QWidgetPrivate::hide_sys() { Q_Q(QWidget); - QWidgetWindow *window = windowHandle(); + auto window = qobject_cast<QWidgetWindow *>(windowHandle()); if (q->testAttribute(Qt::WA_DontShowOnScreen)) { q->setAttribute(Qt::WA_Mapped, false); @@ -8271,12 +8230,12 @@ void QWidgetPrivate::hide_sys() QWidget *p = q->parentWidget(); if (p &&p->isVisible()) { if (renderToTexture) - p->d_func()->invalidateBuffer(q->geometry()); + p->d_func()->invalidateBackingStore(q->geometry()); else - invalidateBuffer(q->rect()); + invalidateBackingStore(q->rect()); } } else { - invalidateBuffer(q->rect()); + invalidateBackingStore(q->rect()); } if (window) @@ -8308,13 +8267,17 @@ void QWidgetPrivate::hide_sys() void QWidget::setVisible(bool visible) { + Q_D(QWidget); + qCDebug(lcWidgetShowHide) << "Setting visibility of" << this + << "with attributes" << WidgetAttributes{this} + << "to" << visible << "via QWidget"; + if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden) == !visible) return; // Remember that setVisible was called explicitly setAttribute(Qt::WA_WState_ExplicitShowHide); - Q_D(QWidget); d->setVisible(visible); } @@ -8324,6 +8287,10 @@ void QWidget::setVisible(bool visible) void QWidgetPrivate::setVisible(bool visible) { Q_Q(QWidget); + qCDebug(lcWidgetShowHide) << "Setting visibility of" << q + << "with attributes" << WidgetAttributes{q} + << "to" << visible << "via QWidgetPrivate"; + if (visible) { // show // Designer uses a trick to make grabWidget work without showing if (!q->isWindow() && q->parentWidget() && q->parentWidget()->isVisible() @@ -8389,20 +8356,10 @@ void QWidgetPrivate::setVisible(bool visible) } QEvent showToParentEvent(QEvent::ShowToParent); - QApplication::sendEvent(q, &showToParentEvent); + QCoreApplication::sendEvent(q, &showToParentEvent); } else { // hide -#if 0 // Used to be included in Qt4 for Q_WS_WIN - // reset WS_DISABLED style in a Blocked window - if(isWindow() && testAttribute(Qt::WA_WState_Created) - && QApplicationPrivate::isBlockedByModal(this)) - { - LONG dwStyle = GetWindowLong(winId(), GWL_STYLE); - dwStyle &= ~WS_DISABLED; - SetWindowLong(winId(), GWL_STYLE, dwStyle); - } -#endif if (QApplicationPrivate::hidden_focus_widget == q) - QApplicationPrivate::hidden_focus_widget = 0; + QApplicationPrivate::hidden_focus_widget = nullptr; // hw: The test on getOpaqueRegion() needs to be more intelligent // currently it doesn't work if the widget is hidden (the region will @@ -8411,20 +8368,21 @@ void QWidgetPrivate::setVisible(bool visible) if (!q->isWindow() && q->parentWidget()) // && !d->getOpaqueRegion().isEmpty()) q->parentWidget()->d_func()->setDirtyOpaqueRegion(); - q->setAttribute(Qt::WA_WState_Hidden); - if (q->testAttribute(Qt::WA_WState_Created)) + if (!q->testAttribute(Qt::WA_WState_Hidden)) { + q->setAttribute(Qt::WA_WState_Hidden); hide_helper(); + } // invalidate layout similar to updateGeometry() if (!q->isWindow() && q->parentWidget()) { if (q->parentWidget()->d_func()->layout) q->parentWidget()->d_func()->layout->invalidate(); else if (q->parentWidget()->isVisible()) - QApplication::postEvent(q->parentWidget(), new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(q->parentWidget(), new QEvent(QEvent::LayoutRequest)); } QEvent hideToParentEvent(QEvent::HideToParent); - QApplication::sendEvent(q, &hideToParentEvent); + QCoreApplication::sendEvent(q, &hideToParentEvent); } } @@ -8436,21 +8394,35 @@ void QWidget::setHidden(bool hidden) setVisible(!hidden); } +bool QWidgetPrivate::isExplicitlyHidden() const +{ + Q_Q(const QWidget); + return q->isHidden() && q->testAttribute(Qt::WA_WState_ExplicitShowHide); +} + void QWidgetPrivate::_q_showIfNotHidden() { Q_Q(QWidget); - if ( !(q->isHidden() && q->testAttribute(Qt::WA_WState_ExplicitShowHide)) ) + if (!isExplicitlyHidden()) q->setVisible(true); } void QWidgetPrivate::showChildren(bool spontaneous) { + Q_Q(QWidget); + qCDebug(lcWidgetShowHide) << "Showing children of" << q + << "spontaneously" << spontaneous; + QList<QObject*> childList = children; for (int i = 0; i < childList.size(); ++i) { QWidget *widget = qobject_cast<QWidget*>(childList.at(i)); - if (!widget - || widget->isWindow() - || widget->testAttribute(Qt::WA_WState_Hidden)) + if (!widget) + continue; + qCDebug(lcWidgetShowHide) << "Considering" << widget + << "with attributes" << WidgetAttributes{widget}; + if (widget->windowHandle() && !widget->testAttribute(Qt::WA_WState_ExplicitShowHide)) + widget->setAttribute(Qt::WA_WState_Hidden, false); + if (widget->isWindow() || widget->testAttribute(Qt::WA_WState_Hidden)) continue; if (spontaneous) { widget->setAttribute(Qt::WA_Mapped); @@ -8461,35 +8433,27 @@ void QWidgetPrivate::showChildren(bool spontaneous) if (widget->testAttribute(Qt::WA_WState_ExplicitShowHide)) widget->d_func()->show_recursive(); else - widget->show(); + widget->d_func()->setVisible(true); } } } void QWidgetPrivate::hideChildren(bool spontaneous) { + Q_Q(QWidget); + qCDebug(lcWidgetShowHide) << "Hiding children of" << q + << "spontaneously" << spontaneous; + QList<QObject*> childList = children; for (int i = 0; i < childList.size(); ++i) { QWidget *widget = qobject_cast<QWidget*>(childList.at(i)); - if (!widget || widget->isWindow() || widget->testAttribute(Qt::WA_WState_Hidden)) + if (!widget) continue; -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // Before doing anything we need to make sure that we don't leave anything in a non-consistent state. - // When hiding a widget we need to make sure that no mouse_down events are active, because - // the mouse_up event will never be received by a hidden widget or one of its descendants. - // The solution is simple, before going through with this we check if there are any mouse_down events in - // progress, if so we check if it is related to this widget or not. If so, we just reset the mouse_down and - // then we continue. - // In X11 and Windows we send a mouse_release event, however we don't do that here because we were already - // ignoring that from before. I.e. Carbon did not send the mouse release event, so we will not send the - // mouse release event. There are two ways to interpret this: - // 1. If we don't send the mouse release event, the widget might get into an inconsistent state, i.e. it - // might be waiting for a release event that will never arrive. - // 2. If we send the mouse release event, then the widget might decide to trigger an action that is not - // supposed to trigger because it is not visible. - if(widget == qt_button_down) - qt_button_down = 0; -#endif + qCDebug(lcWidgetShowHide) << "Considering" << widget + << "with attributes" << WidgetAttributes{widget}; + if (widget->isWindow() || widget->testAttribute(Qt::WA_WState_Hidden)) + continue; + if (spontaneous) widget->setAttribute(Qt::WA_Mapped, false); else @@ -8499,7 +8463,7 @@ void QWidgetPrivate::hideChildren(bool spontaneous) if (spontaneous) { QApplication::sendSpontaneousEvent(widget, &e); } else { - QApplication::sendEvent(widget, &e); + QCoreApplication::sendEvent(widget, &e); if (widget->internalWinId() && widget->testAttribute(Qt::WA_DontCreateNativeAncestors)) { // hide_sys() on an ancestor won't have any affect on this @@ -8508,68 +8472,75 @@ void QWidgetPrivate::hideChildren(bool spontaneous) } } qApp->d_func()->sendSyntheticEnterLeave(widget); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (!spontaneous) { QAccessibleEvent event(widget, QAccessible::ObjectHide); QAccessible::updateAccessibility(&event); } #endif } + + // If the window of this widget is not closed, then the leave event + // will eventually handle the widget under mouse use case. + // Otherwise, we need to explicitly handle it here. + if (QWidget* widgetWindow = q->window(); + widgetWindow && widgetWindow->data->is_closing) { + q->setAttribute(Qt::WA_UnderMouse, false); + } } -bool QWidgetPrivate::close_helper(CloseMode mode) +/*! + \internal + + For windows, this is called from the QWidgetWindow::handleCloseEvent implementation, + which QWidget::close indirectly calls by closing the QWindow. \a mode will be + CloseWithEvent if QWidgetWindow::handleCloseEvent is called indirectly by + QWindow::close, and CloseWithSpontaneousEvent if the close event originates from the + system (i.e. the user clicked the close button in the title bar). + + QDialog calls this method directly in its hide() implementation, which might be + called from the QDialog::closeEvent override. \a mode will be set to CloseNoEvent + to prevent recursion. + + For non-windows, this is called directly by QWidget::close, and \a mode will be + CloseWithEvent. + + The function is also called by the QWidget destructor, with \a mode set to CloseNoEvent. +*/ +bool QWidgetPrivate::handleClose(CloseMode mode) { + Q_Q(QWidget); + qCDebug(lcWidgetShowHide) << "Handling close event for" << q; + if (data.is_closing) return true; - Q_Q(QWidget); - data.is_closing = 1; + // We might not have initiated the close, so update the state now that we know + data.is_closing = true; QPointer<QWidget> that = q; - QPointer<QWidget> parentWidget = (q->parentWidget() && !QObjectPrivate::get(q->parentWidget())->wasDeleted) ? q->parentWidget() : nullptr; - bool quitOnClose = q->testAttribute(Qt::WA_QuitOnClose); + if (data.in_destructor) + mode = CloseNoEvent; + if (mode != CloseNoEvent) { QCloseEvent e; if (mode == CloseWithSpontaneousEvent) QApplication::sendSpontaneousEvent(q, &e); else - QApplication::sendEvent(q, &e); + QCoreApplication::sendEvent(q, &e); if (!that.isNull() && !e.isAccepted()) { - data.is_closing = 0; + data.is_closing = false; return false; } } + // even for windows, make sure we deliver a hide event and that all children get hidden if (!that.isNull() && !q->isHidden()) q->hide(); - // Attempt to close the application only if this has WA_QuitOnClose set and a non-visible parent - quitOnClose = quitOnClose && (parentWidget.isNull() || !parentWidget->isVisible()); - - if (quitOnClose) { - /* if there is no non-withdrawn primary window left (except - the ones without QuitOnClose), we emit the lastWindowClosed - signal */ - QWidgetList list = QApplication::topLevelWidgets(); - bool lastWindowClosed = true; - for (int i = 0; i < list.size(); ++i) { - QWidget *w = list.at(i); - if (!w->isVisible() || w->parentWidget() || !w->testAttribute(Qt::WA_QuitOnClose)) - continue; - lastWindowClosed = false; - break; - } - if (lastWindowClosed) { - QGuiApplicationPrivate::emitLastWindowClosed(); - QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance())); - applicationPrivate->maybeQuit(); - } - } - - if (!that.isNull()) { - data.is_closing = 0; + data.is_closing = false; if (q->testAttribute(Qt::WA_DeleteOnClose)) { q->setAttribute(Qt::WA_DeleteOnClose, false); q->deleteLater(); @@ -8593,7 +8564,7 @@ bool QWidgetPrivate::close_helper(CloseMode mode) is also deleted. A close events is delivered to the widget no matter if the widget is visible or not. - The \l QApplication::lastWindowClosed() signal is emitted when the + The \l QGuiApplication::lastWindowClosed() signal is emitted when the last visible primary window (i.e. window with no parent) with the Qt::WA_QuitOnClose attribute set is closed. By default this attribute is set for all widgets except transient windows such as @@ -8603,7 +8574,25 @@ bool QWidgetPrivate::close_helper(CloseMode mode) bool QWidget::close() { - return d_func()->close_helper(QWidgetPrivate::CloseWithEvent); + return d_func()->close(); +} + +bool QWidgetPrivate::close() +{ + // FIXME: We're not setting is_closing here, even though that would + // make sense, as the code below will not end up in handleClose to + // reset is_closing when there's a QWindow, but no QPlatformWindow, + // and we can't assume close is synchronous so we can't reset it here. + + // Close native widgets via QWindow::close() in order to run QWindow + // close code. The QWidget-specific close code in handleClose() will + // in this case be called from the Close event handler in QWidgetWindow. + if (QWindow *widgetWindow = windowHandle()) { + if (widgetWindow->isTopLevel()) + return widgetWindow->close(); + } + + return handleClose(QWidgetPrivate::CloseWithEvent); } /*! @@ -8638,7 +8627,7 @@ bool QWidget::close() when the user minimizes the window, and a spontaneous show event when the window is restored again. - You almost never have to reimplement the setVisible() function. If + You seldom have to reimplement the setVisible() function. If you need to change some settings before a widget is shown, use showEvent() instead. If you need to do some delayed initialization use the Polish event delivered to the event() function. @@ -8724,11 +8713,13 @@ QSize QWidgetPrivate::adjustedSize() const s.setWidth(qMax(s.width(), 200)); if (exp & Qt::Vertical) s.setHeight(qMax(s.height(), 100)); -#if 0 // Used to be included in Qt4 for Q_WS_X11 - QRect screen = QDesktopWidgetPrivate::screenGeometry(q->x11Info().screen()); -#else // all others - QRect screen = QDesktopWidgetPrivate::screenGeometry(q->pos()); -#endif + + QRect screen; + if (const QScreen *screenAtPoint = QGuiApplication::screenAt(q->pos())) + screen = screenAtPoint->geometry(); + else + screen = QGuiApplication::primaryScreen()->geometry(); + s.setWidth(qMin(s.width(), screen.width()*2/3)); s.setHeight(qMin(s.height(), screen.height()*2/3)); @@ -8830,7 +8821,7 @@ QSize QWidget::minimumSizeHint() const /*! \fn QWidget *QWidget::parentWidget() const - Returns the parent of this widget, or 0 if it does not have any + Returns the parent of this widget, or \nullptr if it does not have any parent widget. */ @@ -8853,26 +8844,6 @@ bool QWidget::isAncestorOf(const QWidget *child) const return false; } -#if 0 // Used to be included in Qt4 for Q_WS_WIN -inline void setDisabledStyle(QWidget *w, bool setStyle) -{ - // set/reset WS_DISABLED style. - if(w && w->isWindow() && w->isVisible() && w->isEnabled()) { - LONG dwStyle = GetWindowLong(w->winId(), GWL_STYLE); - LONG newStyle = dwStyle; - if (setStyle) - newStyle |= WS_DISABLED; - else - newStyle &= ~WS_DISABLED; - if (newStyle != dwStyle) { - SetWindowLong(w->winId(), GWL_STYLE, newStyle); - // we might need to repaint in some situations (eg. menu) - w->repaint(); - } - } -} -#endif - /***************************************************************************** QWidget event handling *****************************************************************************/ @@ -8935,6 +8906,23 @@ bool QWidget::event(QEvent *event) } } switch (event->type()) { + case QEvent::PlatformSurface: { + // Sync up QWidget's view of whether or not the widget has been created + switch (static_cast<QPlatformSurfaceEvent*>(event)->surfaceEventType()) { + case QPlatformSurfaceEvent::SurfaceCreated: + if (!testAttribute(Qt::WA_WState_Created)) + create(); + break; + case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: + if (testAttribute(Qt::WA_WState_Created)) { + // Child windows have already been destroyed by QWindow, + // so we skip them here. + destroy(false, false); + } + break; + } + break; + } case QEvent::MouseMove: mouseMoveEvent((QMouseEvent*)event); break; @@ -8966,7 +8954,7 @@ bool QWidget::event(QEvent *event) break; #endif case QEvent::KeyPress: { - QKeyEvent *k = (QKeyEvent *)event; + QKeyEvent *k = static_cast<QKeyEvent *>(event); bool res = false; if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? if (k->key() == Qt::Key_Backtab @@ -9023,16 +9011,22 @@ bool QWidget::event(QEvent *event) inputMethodEvent((QInputMethodEvent *) event); break; - case QEvent::InputMethodQuery: - if (testAttribute(Qt::WA_InputMethodEnabled)) { + case QEvent::InputMethodQuery: { QInputMethodQueryEvent *query = static_cast<QInputMethodQueryEvent *>(event); Qt::InputMethodQueries queries = query->queries(); for (uint i = 0; i < 32; ++i) { Qt::InputMethodQuery q = (Qt::InputMethodQuery)(int)(queries & (1<<i)); if (q) { QVariant v = inputMethodQuery(q); - if (q == Qt::ImEnabled && !v.isValid() && isEnabled()) - v = QVariant(true); // special case for Qt4 compatibility + if (q == Qt::ImEnabled && !v.isValid() && isEnabled()) { + // Qt:ImEnabled was added in Qt 5.3. So not all widgets support it, even + // if they implement IM otherwise (by overriding inputMethodQuery()). Instead + // they set the widget attribute Qt::WA_InputMethodEnabled. But this attribute + // will only be set if the widget supports IM _and_ is not read-only. So for + // read-only widgets, not all IM features will be supported when ImEnabled is + // not implemented explicitly (e.g selection handles for read-only widgets on iOS). + v = QVariant(testAttribute(Qt::WA_InputMethodEnabled)); + } query->setValue(q, v); } } @@ -9049,7 +9043,7 @@ bool QWidget::event(QEvent *event) setAttribute(Qt::WA_WState_Polished); if (!QApplication::font(this).isCopyOf(QApplication::font())) d->resolveFont(); - if (!QApplication::palette(this).isCopyOf(QApplication::palette())) + if (!QApplication::palette(this).isCopyOf(QGuiApplication::palette())) d->resolvePalette(); } break; @@ -9073,10 +9067,10 @@ bool QWidget::event(QEvent *event) #if QT_CONFIG(statustip) if (d->statusTip.size()) { QStatusTipEvent tip(d->statusTip); - QApplication::sendEvent(const_cast<QWidget *>(this), &tip); + QCoreApplication::sendEvent(const_cast<QWidget *>(this), &tip); } #endif - enterEvent(event); + enterEvent(static_cast<QEnterEvent*>(event)); break; case QEvent::Leave: @@ -9084,7 +9078,7 @@ bool QWidget::event(QEvent *event) if (d->statusTip.size()) { QString empty; QStatusTipEvent tip(empty); - QApplication::sendEvent(const_cast<QWidget *>(this), &tip); + QCoreApplication::sendEvent(const_cast<QWidget *>(this), &tip); } #endif leaveEvent(event); @@ -9129,9 +9123,9 @@ bool QWidget::event(QEvent *event) break; #if QT_CONFIG(menu) case Qt::ActionsContextMenu: - if (d->actions.count()) { + if (d->actions.size()) { QMenu::exec(d->actions, static_cast<QContextMenuEvent *>(event)->globalPos(), - 0, this); + nullptr, this); break; } Q_FALLTHROUGH(); @@ -9235,7 +9229,7 @@ bool QWidget::event(QEvent *event) for (int i = 0; i < childList.size(); ++i) { QWidget *w = qobject_cast<QWidget *>(childList.at(i)); if (w && w->isVisible() && !w->isWindow()) - QApplication::sendEvent(w, event); + QCoreApplication::sendEvent(w, event); } break; } @@ -9246,7 +9240,7 @@ bool QWidget::event(QEvent *event) for (int i = 0; i < childList.size(); ++i) { QObject *o = childList.at(i); if (o) - QApplication::sendEvent(o, event); + QCoreApplication::sendEvent(o, event); } } update(); @@ -9285,15 +9279,12 @@ bool QWidget::event(QEvent *event) QWidget *w = static_cast<QWidget *>(o); // do not forward the event to child windows; QApplication does this for us if (!w->isWindow()) - QApplication::sendEvent(w, event); + QCoreApplication::sendEvent(w, event); } } } -#if 0 // Used to be included in Qt4 for Q_WS_WIN - setDisabledStyle(this, (event->type() == QEvent::WindowBlocked)); -#endif break; -#ifndef QT_NO_TOOLTIP +#if QT_CONFIG(tooltip) case QEvent::ToolTip: if (!d->toolTip.isEmpty()) QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), d->toolTip, this, QRect(), d->toolTipDuration); @@ -9316,9 +9307,6 @@ bool QWidget::event(QEvent *event) case QEvent::EmbeddingControl: d->topData()->frameStrut.setCoords(0 ,0, 0, 0); data->fstrut_dirty = false; -#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ || 0 /* Used to be included in Qt4 for Q_WS_X11 */ - d->topData()->embedded = 1; -#endif break; #ifndef QT_NO_ACTION case QEvent::ActionAdded: @@ -9337,15 +9325,10 @@ bool QWidget::event(QEvent *event) for (int i = 0; i < childList.size(); ++i) { QWidget *w = qobject_cast<QWidget *>(childList.at(i)); if (w && w->isVisible() && !w->isWindow()) - QApplication::sendEvent(w, event); + QCoreApplication::sendEvent(w, event); } break; } -#if 0 // Used to be included in Qt4 for Q_WS_MAC - case QEvent::MacGLWindowChange: - d->needWindowChange = false; - break; -#endif case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: @@ -9364,16 +9347,15 @@ bool QWidget::event(QEvent *event) const QWindow *win = te->window; d->setWinId((win && win->handle()) ? win->handle()->winId() : 0); } + break; + case QEvent::DevicePixelRatioChange: if (d->data.fnt.d->dpi != logicalDpiY()) d->updateFont(d->data.fnt); -#ifndef QT_NO_OPENGL d->renderToTextureReallyDirty = 1; -#endif break; -#ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName(); - if (propName.length() == 13 && !qstrncmp(propName, "_q_customDpi", 12)) { + if (propName.size() == 13 && !qstrncmp(propName, "_q_customDpi", 12)) { uint value = property(propName.constData()).toUInt(); if (!d->extra) d->createExtra(); @@ -9388,7 +9370,6 @@ bool QWidget::event(QEvent *event) windowHandle()->setProperty(propName, property(propName)); Q_FALLTHROUGH(); } -#endif default: return QObject::event(event); } @@ -9416,7 +9397,7 @@ void QWidget::changeEvent(QEvent * event) switch(event->type()) { case QEvent::EnabledChange: { update(); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QAccessible::State s; s.disabled = true; QAccessibleStateChangeEvent event(this, s); @@ -9440,8 +9421,8 @@ void QWidget::changeEvent(QEvent * event) break; case QEvent::ThemeChange: - if (QApplication::desktopSettingsAware() && windowType() != Qt::Desktop - && qApp && !QApplication::closingDown()) { + if (QGuiApplication::desktopSettingsAware() && windowType() != Qt::Desktop + && qApp && !QCoreApplication::closingDown()) { if (testAttribute(Qt::WA_WState_Polished)) QApplication::style()->unpolish(this); if (testAttribute(Qt::WA_WState_Polished)) @@ -9457,11 +9438,6 @@ void QWidget::changeEvent(QEvent * event) case QEvent::MacSizeChange: updateGeometry(); break; -#elif 0 // Used to be included in Qt4 for Q_WS_MAC - case QEvent::ToolTipChange: - case QEvent::MouseTrackingChange: - qt_mac_update_mouseTracking(this); - break; #endif default: @@ -9528,7 +9504,7 @@ void QWidget::mousePressEvent(QMouseEvent *event) if (QApplication::activePopupWidget() == w) // widget does not want to disappear w->hide(); // hide at least } - if (!rect().contains(event->pos())){ + if (!rect().contains(event->position().toPoint())){ close(); } } @@ -9554,9 +9530,11 @@ void QWidget::mouseReleaseEvent(QMouseEvent *event) The default implementation calls mousePressEvent(). \note The widget will also receive mouse press and mouse release - events in addition to the double click event. It is up to the - developer to ensure that the application interprets these events - correctly. + events in addition to the double click event. And if another widget + that overlaps this widget disappears in response to press or + release events, then this widget will only receive the double click + event. It is up to the developer to ensure that the application + interprets these events correctly. \sa mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(), event(), QMouseEvent @@ -9635,7 +9613,7 @@ void QWidget::tabletEvent(QTabletEvent *event) implementation if you act upon the key. \sa keyReleaseEvent(), setFocusPolicy(), - focusInEvent(), focusOutEvent(), event(), QKeyEvent, {Tetrix Example} + focusInEvent(), focusOutEvent(), event(), QKeyEvent */ void QWidget::keyPressEvent(QKeyEvent *event) @@ -9686,7 +9664,7 @@ void QWidget::keyReleaseEvent(QKeyEvent *event) is passed in the \a event parameter A widget normally must setFocusPolicy() to something other than - Qt::NoFocus in order to receive focus events. (Note that the + Qt::NoFocus to receive focus events. (Note that the application programmer can call setFocus() on any widget, even those that do not normally accept focus.) @@ -9712,7 +9690,7 @@ void QWidget::focusInEvent(QFocusEvent *) passed in the \a event parameter. A widget normally must setFocusPolicy() to something other than - Qt::NoFocus in order to receive focus events. (Note that the + Qt::NoFocus to receive focus events. (Note that the application programmer can call setFocus() on any widget, even those that do not normally accept focus.) @@ -9736,7 +9714,7 @@ void QWidget::focusOutEvent(QFocusEvent *) } /*! - \fn void QWidget::enterEvent(QEvent *event) + \fn void QWidget::enterEvent(QEnterEvent *event) This event handler can be reimplemented in a subclass to receive widget enter events which are passed in the \a event parameter. @@ -9747,12 +9725,10 @@ void QWidget::focusOutEvent(QFocusEvent *) \sa leaveEvent(), mouseMoveEvent(), event() */ -void QWidget::enterEvent(QEvent *) +void QWidget::enterEvent(QEnterEvent *) { } -// ### Qt 6: void QWidget::enterEvent(QEnterEvent *). - /*! \fn void QWidget::leaveEvent(QEvent *event) @@ -9814,7 +9790,7 @@ void QWidget::leaveEvent(QEvent *) never be called; the backingstore will be used instead. \sa event(), repaint(), update(), QPainter, QPixmap, QPaintEvent, - {Analog Clock Example} + {Analog Clock} */ void QWidget::paintEvent(QPaintEvent *) @@ -9886,13 +9862,8 @@ void QWidget::actionEvent(QActionEvent *) Main window applications typically use reimplementations of this function to check whether the user's work has been saved and ask for permission before closing. - For example, the \l{Application Example} uses a helper function to determine whether - or not to close the window: - \snippet mainwindows/application/mainwindow.cpp 3 - \snippet mainwindows/application/mainwindow.cpp 4 - - \sa event(), hide(), close(), QCloseEvent, {Application Example} + \sa event(), hide(), close(), QCloseEvent */ void QWidget::closeEvent(QCloseEvent *event) @@ -9981,7 +9952,7 @@ QVariant QWidget::inputMethodQuery(Qt::InputMethodQuery query) const is set, the input method may change its visual components to reflect that only numbers can be entered. - \warning Some widgets require certain flags in order to work as + \warning Some widgets require certain flags to work as intended. To set a flag, do \c{w->setInputMethodHints(w->inputMethodHints()|f)} instead of \c{w->setInputMethodHints(f)}. @@ -9998,16 +9969,16 @@ QVariant QWidget::inputMethodQuery(Qt::InputMethodQuery query) const */ Qt::InputMethodHints QWidget::inputMethodHints() const { -#ifndef QT_NO_IM +#if QT_CONFIG(im) const QWidgetPrivate *priv = d_func(); while (priv->inheritsInputMethodHints) { priv = priv->q_func()->parentWidget()->d_func(); Q_ASSERT(priv); } return priv->imHints; -#else //QT_NO_IM - return 0; -#endif //QT_NO_IM +#else + return Qt::ImhNone; +#endif } void QWidget::setInputMethodHints(Qt::InputMethodHints hints) @@ -10168,7 +10139,7 @@ void QWidget::hideEvent(QHideEvent *) \endtable */ -bool QWidget::nativeEvent(const QByteArray &eventType, void *message, long *result) +bool QWidget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) { Q_UNUSED(eventType); Q_UNUSED(message); @@ -10215,7 +10186,7 @@ void QWidget::ensurePolished() const QList<QObject*> children = d->children; for (int i = 0; i < children.size(); ++i) { QObject *o = children.at(i); - if(!o->isWidgetType()) + if (!o->isWidgetType()) continue; if (QWidget *w = qobject_cast<QWidget *>(o)) w->ensurePolished(); @@ -10231,7 +10202,7 @@ void QWidget::ensurePolished() const Returns the mask currently set on a widget. If no mask is set the return value will be an empty region. - \sa setMask(), clearMask(), QRegion::isEmpty(), {Shaped Clock Example} + \sa setMask(), clearMask(), QRegion::isEmpty() */ QRegion QWidget::mask() const { @@ -10240,7 +10211,7 @@ QRegion QWidget::mask() const } /*! - Returns the layout manager that is installed on this widget, or 0 + Returns the layout manager that is installed on this widget, or \nullptr if no layout manager is installed. The layout manager sets the geometry of the widget's children @@ -10333,9 +10304,9 @@ QLayout *QWidget::takeLayout() Q_D(QWidget); QLayout *l = layout(); if (!l) - return 0; - d->layout = 0; - l->setParent(0); + return nullptr; + d->layout = nullptr; + l->setParent(nullptr); return l; } @@ -10383,7 +10354,7 @@ void QWidget::setSizePolicy(QSizePolicy policy) d->size_policy = policy; #if QT_CONFIG(graphicsview) - if (QWExtra *extra = d->extra) { + if (const auto &extra = d->extra) { if (extra->proxyWidget) extra->proxyWidget->setSizePolicy(policy); } @@ -10457,10 +10428,10 @@ QWidget *QWidget::childAt(const QPoint &p) const QWidget *QWidgetPrivate::childAt_helper(const QPoint &p, bool ignoreChildrenInDestructor) const { if (children.isEmpty()) - return 0; + return nullptr; if (!pointInsideRectAndMask(p)) - return 0; + return nullptr; return childAtRecursiveHelper(p, ignoreChildrenInDestructor); } @@ -10488,7 +10459,7 @@ QWidget *QWidgetPrivate::childAtRecursiveHelper(const QPoint &p, bool ignoreChil // We have found our target; namely the child at position 'p'. return child; } - return 0; + return nullptr; } void QWidgetPrivate::updateGeometry_helper(bool forceUpdate) @@ -10504,7 +10475,7 @@ void QWidgetPrivate::updateGeometry_helper(bool forceUpdate) if (parent->d_func()->layout) parent->d_func()->layout->invalidate(); else if (parent->isVisible()) - QApplication::postEvent(parent, new QEvent(QEvent::LayoutRequest)); + QCoreApplication::postEvent(parent, new QEvent(QEvent::LayoutRequest)); } } } @@ -10652,22 +10623,26 @@ void QWidget::setParent(QWidget *parent) setParent((QWidget*)parent, windowFlags() & ~Qt::WindowType_Mask); } -#ifndef QT_NO_OPENGL -static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget) +void qSendWindowChangeToTextureChildrenRecursively(QWidget *widget, QEvent::Type eventType) { QWidgetPrivate *d = QWidgetPrivate::get(widget); if (d->renderToTexture) { - QEvent e(QEvent::WindowChangeInternal); - QApplication::sendEvent(widget, &e); + QEvent e(eventType); + QCoreApplication::sendEvent(widget, &e); } for (int i = 0; i < d->children.size(); ++i) { QWidget *w = qobject_cast<QWidget *>(d->children.at(i)); - if (w && !w->isWindow() && QWidgetPrivate::get(w)->textureChildSeen) - sendWindowChangeToTextureChildrenRecursively(w); + if (w && !w->isWindow()) + qSendWindowChangeToTextureChildrenRecursively(w, eventType); + } + + // Notify QWidgetWindow after we've notified all child QWidgets + if (auto *window = d->windowHandle(QWidgetPrivate::WindowHandleMode::Direct)) { + QEvent e(eventType); + QCoreApplication::sendEvent(window, &e); } } -#endif /*! \overload @@ -10678,20 +10653,37 @@ static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget) void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) { Q_D(QWidget); - bool resized = testAttribute(Qt::WA_Resized); - bool wasCreated = testAttribute(Qt::WA_WState_Created); + Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QWidget to itself"); +#ifdef QT_DEBUG + const auto checkForParentChildLoops = qScopeGuard([&](){ + int depth = 0; + auto p = parentWidget(); + while (p) { + if (++depth == QObjectPrivate::CheckForParentChildLoopsWarnDepth) { + qWarning("QWidget %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; " + "this is undefined behavior", + this, metaObject()->className(), qPrintable(objectName())); + } + p = p->parentWidget(); + } + }); +#endif + + const bool resized = testAttribute(Qt::WA_Resized); + const bool wasCreated = testAttribute(Qt::WA_WState_Created); QWidget *oldtlw = window(); + Q_ASSERT(oldtlw); if (f & Qt::Window) // Frame geometry likely changes, refresh. d->data.fstrut_dirty = true; - QWidget *desktopWidget = 0; + QWidget *desktopWidget = nullptr; if (parent && parent->windowType() == Qt::Desktop) desktopWidget = parent; - bool newParent = (parent != parentWidget()) || !wasCreated || desktopWidget; + bool newParent = (parent != parentWidget()) || desktopWidget; if (newParent && parent && !desktopWidget) { - if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) + if (testAttribute(Qt::WA_NativeWindow) && !QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) parent->d_func()->enforceNativeChildren(); else if (parent->d_func()->nativeChildrenForced() || parent->testAttribute(Qt::WA_PaintOnScreen)) setAttribute(Qt::WA_NativeWindow); @@ -10699,49 +10691,56 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) if (wasCreated) { if (!testAttribute(Qt::WA_WState_Hidden)) { + // Hiding the widget will set WA_WState_Hidden as well, which would + // normally require the widget to be explicitly shown again to become + // visible, even as a child widget. But we refine this value later in + // setParent_sys(), applying WA_WState_Hidden based on whether the + // widget is a top level or not. hide(); + + // We reset WA_WState_ExplicitShowHide here, likely as a remnant of + // when we only had QWidget::setVisible(), which is treated as an + // explicit show/hide. Nowadays we have QWidgetPrivate::setVisible(), + // that allows us to hide a widget without affecting ExplicitShowHide. + // Though it can be argued that ExplicitShowHide should reflect the + // last update of the widget's state, so if we hide the widget as a + // side effect of changing parent, perhaps we _should_ reset it? setAttribute(Qt::WA_WState_ExplicitShowHide, false); } if (newParent) { QEvent e(QEvent::ParentAboutToChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } } - if (newParent && isAncestorOf(focusWidget())) - focusWidget()->clearFocus(); - QTLWExtra *oldTopExtra = window()->d_func()->maybeTopData(); - QWidgetBackingStoreTracker *oldBsTracker = oldTopExtra ? &oldTopExtra->backingStoreTracker : 0; + // texture-based widgets need a pre-notification when their associated top-level window changes + // This is not under the wasCreated/newParent conditions above in order to also play nice with QDockWidget. + if (oldtlw->d_func()->usesRhiFlush && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw))) + qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowAboutToChangeInternal); - d->setParent_sys(parent, f); + // If we get parented into another window, children will be folded + // into the new parent's focus chain, so clear focus now. + if (newParent && isAncestorOf(focusWidget()) && !(f & Qt::Window)) + focusWidget()->clearFocus(); - QTLWExtra *topExtra = window()->d_func()->maybeTopData(); - QWidgetBackingStoreTracker *bsTracker = topExtra ? &topExtra->backingStoreTracker : 0; - if (oldBsTracker && oldBsTracker != bsTracker) - oldBsTracker->unregisterWidgetSubtree(this); + d->setParent_sys(parent, f); if (desktopWidget) - parent = 0; + parent = nullptr; -#ifndef QT_NO_OPENGL if (d->textureChildSeen && parent) { // set the textureChildSeen flag up the whole parent chain QWidgetPrivate::get(parent)->setTextureChildSeen(); } -#endif - if (QWidgetBackingStore *oldBs = oldtlw->d_func()->maybeBackingStore()) { + if (QWidgetRepaintManager *oldPaintManager = oldtlw->d_func()->maybeRepaintManager()) { if (newParent) - oldBs->removeDirtyWidget(this); + oldPaintManager->removeDirtyWidget(this); // Move the widget and all its static children from // the old backing store to the new one. - oldBs->moveStaticWidgets(this); + oldPaintManager->moveStaticWidgets(this); } - // ### fixme: Qt 6: Remove AA_ImmediateWidgetCreation. - if (QApplicationPrivate::testAttribute(Qt::AA_ImmediateWidgetCreation) && !testAttribute(Qt::WA_WState_Created)) - create(); - d->reparentFocusWidgets(oldtlw); setAttribute(Qt::WA_Resized, resized); @@ -10750,6 +10749,12 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) if (!useStyleSheetPropagationInWidgetStyles && !testAttribute(Qt::WA_StyleSheet) && (!parent || !parent->testAttribute(Qt::WA_StyleSheet))) { + // if the parent has a font set or inherited, then propagate the mask to the new child + if (parent) { + const auto pd = parent->d_func(); + d->inheritedFontResolveMask = pd->directFontResolveMask | pd->inheritedFontResolveMask; + d->inheritedPaletteResolveMask = pd->directPaletteResolveMask | pd->inheritedPaletteResolveMask; + } d->resolveFont(); d->resolvePalette(); } @@ -10760,8 +10765,8 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) // event to handle recreation/rebinding of the GL context, hence the // (f & Qt::MSWindowsOwnDC) clause (which is set on QGLWidgets on all // platforms). - if (newParent -#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ || defined(QT_OPENGL_ES) + if (newParent || !wasCreated +#if QT_CONFIG(opengles2) || (f & Qt::MSWindowsOwnDC) #endif ) { @@ -10777,32 +10782,22 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) // send and post remaining QObject events if (parent && d->sendChildEvents) { QChildEvent e(QEvent::ChildAdded, this); - QApplication::sendEvent(parent, &e); + QCoreApplication::sendEvent(parent, &e); } -//### already hidden above ---> must probably do something smart on the mac -// #if 0 // Used to be included in Qt4 for Q_WS_MAC -// extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp -// if(!qt_mac_is_macdrawer(q)) //special case -// q->setAttribute(Qt::WA_WState_Hidden); -// #else -// q->setAttribute(Qt::WA_WState_Hidden); -//#endif - if (parent && d->sendChildEvents && d->polished) { QChildEvent e(QEvent::ChildPolished, this); QCoreApplication::sendEvent(parent, &e); } QEvent e(QEvent::ParentChange); - QApplication::sendEvent(this, &e); - } -#ifndef QT_NO_OPENGL - //renderToTexture widgets also need to know when their top-level window changes - if (d->textureChildSeen && oldtlw != window()) { - sendWindowChangeToTextureChildrenRecursively(this); + QCoreApplication::sendEvent(this, &e); } -#endif + + // texture-based widgets need another event when their top-level window + // changes (more precisely, has already changed at this point) + if (oldtlw->d_func()->usesRhiFlush && oldtlw != window()) + qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowChangeInternal); if (!wasCreated) { if (isWindow() || parentWidget()->isVisible()) @@ -10828,6 +10823,37 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) if (d->extra && d->extra->hasWindowContainer) QWindowContainer::parentWasChanged(this); + + QWidget *newtlw = window(); + if (oldtlw != newtlw) { + QSurface::SurfaceType surfaceType = QSurface::RasterSurface; + // Only evaluate the reparented subtree. While it might be tempting to + // do it on newtlw instead, the performance implications of that are + // problematic when it comes to large widget trees. + if (q_evaluateRhiConfig(this, nullptr, &surfaceType)) { + const bool wasUsingRhiFlush = newtlw->d_func()->usesRhiFlush; + newtlw->d_func()->usesRhiFlush = true; + bool recreate = false; + if (QWindow *w = newtlw->windowHandle()) { + if (w->surfaceType() != surfaceType || !wasUsingRhiFlush) + recreate = true; + } + // QTBUG-115652: Besides the toplevel the nativeParentWidget()'s QWindow must be checked as well. + if (QWindow *w = d->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) { + if (w->surfaceType() != surfaceType) + recreate = true; + } + if (recreate) { + const auto windowStateBeforeDestroy = newtlw->windowState(); + const auto visibilityBeforeDestroy = newtlw->isVisible(); + newtlw->destroy(); + newtlw->create(); + Q_ASSERT(newtlw->windowHandle()); + newtlw->windowHandle()->setWindowStates(windowStateBeforeDestroy); + QWidgetPrivate::get(newtlw)->setVisible(visibilityBeforeDestroy); + } + } + } } void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) @@ -10837,71 +10863,72 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) Qt::WindowFlags oldFlags = data.window_flags; bool wasCreated = q->testAttribute(Qt::WA_WState_Created); - int targetScreen = -1; + QScreen *targetScreen = nullptr; // Handle a request to move the widget to a particular screen if (newparent && newparent->windowType() == Qt::Desktop) { // make sure the widget is created on the same screen as the // programmer specified desktop widget - const QDesktopScreenWidget *sw = qobject_cast<const QDesktopScreenWidget *>(newparent); - targetScreen = sw ? sw->screenNumber() : 0; - newparent = 0; + targetScreen = newparent->screen(); + newparent = nullptr; } setWinId(0); + if (!newparent) { + f |= Qt::Window; + if (parent) + targetScreen = q->parentWidget()->window()->screen(); + } + + const bool destroyWindow = ( + // Reparenting top level to child + (oldFlags & Qt::Window) && !(f & Qt::Window) + // And we can dispose of the window + && wasCreated && !q->testAttribute(Qt::WA_NativeWindow) + ); + if (parent != newparent) { - QObjectPrivate::setParent_helper(newparent); //### why does this have to be done in the _sys function??? - if (q->windowHandle()) { + // Update object parent now, so we can resolve new parent window below + QObjectPrivate::setParent_helper(newparent); + + if (q->windowHandle()) q->windowHandle()->setFlags(f); - QWidget *parentWithWindow = - newparent ? (newparent->windowHandle() ? newparent : newparent->nativeParentWidget()) : 0; - if (parentWithWindow) { - QWidget *topLevel = parentWithWindow->window(); - if ((f & Qt::Window) && topLevel && topLevel->windowHandle()) { - q->windowHandle()->setTransientParent(topLevel->windowHandle()); - q->windowHandle()->setParent(0); - } else { - q->windowHandle()->setTransientParent(0); - q->windowHandle()->setParent(parentWithWindow->windowHandle()); - } - } else { - q->windowHandle()->setTransientParent(0); - q->windowHandle()->setParent(0); - } - } - } - if (!newparent) { - f |= Qt::Window; - if (targetScreen == -1) { - if (parent) - targetScreen = QDesktopWidgetPrivate::screenNumber(q->parentWidget()->window()); - } + // If the widget itself or any of its children have been created, + // we need to reparent their QWindows as well. + QWidget *parentWithWindow = closestParentWidgetWithWindowHandle(); + // But if the widget is about to be destroyed we must skip the + // widget itself, and only reparent children. + if (destroyWindow) + reparentWidgetWindowChildren(parentWithWindow); + else + reparentWidgetWindows(parentWithWindow, f); } - bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); + bool explicitlyHidden = isExplicitlyHidden(); - // Reparenting toplevel to child - if (wasCreated && !(f & Qt::Window) && (oldFlags & Qt::Window) && !q->testAttribute(Qt::WA_NativeWindow)) { + if (destroyWindow) { if (extra && extra->hasWindowContainer) QWindowContainer::toplevelAboutToBeDestroyed(q); - QWindow *newParentWindow = newparent->windowHandle(); - if (!newParentWindow) - if (QWidget *npw = newparent->nativeParentWidget()) - newParentWindow = npw->windowHandle(); - - Q_FOREACH (QObject *child, q->windowHandle()->children()) { - QWindow *childWindow = qobject_cast<QWindow *>(child); - if (!childWindow) - continue; - - QWidgetWindow *childWW = qobject_cast<QWidgetWindow *>(childWindow); - QWidget *childWidget = childWW ? childWW->widget() : 0; - if (!childWW || (childWidget && childWidget->testAttribute(Qt::WA_NativeWindow))) - childWindow->setParent(newParentWindow); + // There shouldn't be any QWindow children left, but if there + // are, re-parent them now, before we destroy. + if (!q->windowHandle()->children().isEmpty()) { + QWidget *parentWithWindow = closestParentWidgetWithWindowHandle(); + QWindow *newParentWindow = parentWithWindow ? parentWithWindow->windowHandle() : nullptr; + for (QObject *child : q->windowHandle()->children()) { + if (QWindow *childWindow = qobject_cast<QWindow *>(child)) { + qCWarning(lcWidgetWindow) << "Reparenting" << childWindow + << "before destroying" << this; + childWindow->setParent(newParentWindow); + } + } } - q->destroy(); + + // We have reparented any child windows of the widget we are + // about to destroy to the new parent window handle, so we can + // safely destroy this widget without destroying sub windows. + q->destroy(true, false); } adjustFlags(f, q); @@ -10918,12 +10945,59 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden); // move the window to the selected screen - if (!newparent && targetScreen != -1) { + if (!newparent && targetScreen) { // only if it is already created if (q->testAttribute(Qt::WA_WState_Created)) - q->windowHandle()->setScreen(QGuiApplication::screens().value(targetScreen, 0)); + q->windowHandle()->setScreen(targetScreen); else - topData()->initialScreenIndex = targetScreen; + topData()->initialScreen = targetScreen; + } +} + +void QWidgetPrivate::reparentWidgetWindows(QWidget *parentWithWindow, Qt::WindowFlags windowFlags) +{ + if (QWindow *window = windowHandle()) { + // Reparent this QWindow, and all QWindow children will follow + if (parentWithWindow) { + // The reparented widget has not updated its window flags yet, + // so we can't ask the widget directly. And we can't use the + // QWindow flags, as unlike QWidgets the QWindow flags always + // reflect Qt::Window, even for child windows. And we can't use + // QWindow::isTopLevel() either, as that depends on the parent, + // which we are in the process of updating. So we propagate the + // new flags of the reparented window from setParent_sys(). + if (windowFlags & Qt::Window) { + // Top level windows can only have transient parents, + // and the transient parent must be another top level. + QWidget *topLevel = parentWithWindow->window(); + auto *transientParent = topLevel->windowHandle(); + Q_ASSERT(transientParent); + qCDebug(lcWidgetWindow) << "Setting" << window << "transient parent to" << transientParent; + window->setTransientParent(transientParent); + window->setParent(nullptr); + } else { + auto *parentWindow = parentWithWindow->windowHandle(); + qCDebug(lcWidgetWindow) << "Reparenting" << window << "into" << parentWindow; + window->setTransientParent(nullptr); + window->setParent(parentWindow); + } + } else { + qCDebug(lcWidgetWindow) << "Making" << window << "top level window"; + window->setTransientParent(nullptr); + window->setParent(nullptr); + } + } else { + reparentWidgetWindowChildren(parentWithWindow); + } +} + +void QWidgetPrivate::reparentWidgetWindowChildren(QWidget *parentWithWindow) +{ + for (auto *child : std::as_const(children)) { + if (auto *childWidget = qobject_cast<QWidget*>(child)) { + auto *childPrivate = QWidgetPrivate::get(childWidget); + childPrivate->reparentWidgetWindows(parentWithWindow); + } } } @@ -11021,7 +11095,7 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) unless updates are disabled or the widget is hidden. We suggest only using repaint() if you need an immediate repaint, - for example during animation. In almost all circumstances update() + for example during animation. In most circumstances update() is better, as it permits Qt to optimize for speed and minimize flicker. @@ -11088,8 +11162,8 @@ void QWidgetPrivate::repaint(T r) return; QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->backingStoreTracker->markDirty(r, q, QWidgetBackingStore::UpdateNow); + if (tlwExtra && tlwExtra->backingStore && tlwExtra->repaintManager) + tlwExtra->repaintManager->markDirty(r, q, QWidgetRepaintManager::UpdateNow); } /*! @@ -11108,7 +11182,7 @@ void QWidgetPrivate::repaint(T r) If the Qt::WA_OpaquePaintEvent widget attribute is set, the widget is responsible for painting all its pixels with an opaque color. - \sa repaint(), paintEvent(), setUpdatesEnabled(), {Analog Clock Example} + \sa repaint(), paintEvent(), setUpdatesEnabled(), {Analog Clock} */ void QWidget::update() { @@ -11149,6 +11223,11 @@ void QWidgetPrivate::update(T r) { Q_Q(QWidget); + if (renderToTexture && !q->isVisible()) { + renderToTextureReallyDirty = 1; + return; + } + if (!q->isVisible() || !q->updatesEnabled()) return; @@ -11158,13 +11237,13 @@ void QWidgetPrivate::update(T r) return; if (q->testAttribute(Qt::WA_WState_InPaintEvent)) { - QApplication::postEvent(q, new QUpdateLaterEvent(clipped)); + QCoreApplication::postEvent(q, new QUpdateLaterEvent(clipped)); return; } QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->backingStoreTracker->markDirty(clipped, q); + if (tlwExtra && tlwExtra->backingStore && tlwExtra->repaintManager) + tlwExtra->repaintManager->markDirty(clipped, q); } /*! @@ -11195,7 +11274,7 @@ void QWidgetPrivate::macUpdateSizeAttribute() { Q_Q(QWidget); QEvent event(QEvent::MacSizeChange); - QApplication::sendEvent(q, &event); + QCoreApplication::sendEvent(q, &event); for (int i = 0; i < children.size(); ++i) { QWidget *w = qobject_cast<QWidget *>(children.at(i)); if (w && (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation)) @@ -11220,7 +11299,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) return; Q_D(QWidget); - Q_STATIC_ASSERT_X(sizeof(d->high_attributes)*8 >= (Qt::WA_AttributeCount - sizeof(uint)*8), + static_assert(sizeof(d->high_attributes)*8 >= (Qt::WA_AttributeCount - sizeof(uint)*8), "QWidget::setAttribute(WidgetAttribute, bool): " "QWidgetPrivate::high_attributes[] too small to contain all attributes in WidgetAttribute"); #ifdef Q_OS_WIN @@ -11251,7 +11330,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) else if (!on && (isWindow() || !parentWidget() || !parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))) setAttribute(Qt::WA_DropSiteRegistered, false); QEvent e(QEvent::AcceptDropsChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); break; } case Qt::WA_DropSiteRegistered: { @@ -11270,23 +11349,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) case Qt::WA_NoChildEventsFromChildren: d->receiveChildEvents = !on; break; -#if 0 // Used to be included in Qt4 for Q_WS_MAC - case Qt::WA_MacOpaqueSizeGrip: - d->macUpdateOpaqueSizeGrip(); - break; - case Qt::WA_MacShowFocusRect: - if (hasFocus()) { - clearFocus(); - setFocus(); - } - break; - case Qt::WA_Hover: - qt_mac_update_mouseTracking(this); - break; - case Qt::WA_MacAlwaysShowToolWindow: - d->macUpdateHideOnSuspend(); - break; -#endif case Qt::WA_MacNormalSize: case Qt::WA_MacSmallSize: case Qt::WA_MacMiniSize: @@ -11308,21 +11370,9 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) // reset modality type to NonModal when clearing WA_ShowModal data->window_modality = Qt::NonModal; } else if (data->window_modality == Qt::NonModal) { - // determine the modality type if it hasn't been set prior - // to setting WA_ShowModal. set the default to WindowModal - // if we are the child of a group leader; otherwise use + // If modality hasn't been set prior to setting WA_ShowModal, use // ApplicationModal. - QWidget *w = parentWidget(); - if (w) - w = w->window(); - while (w && !w->testAttribute(Qt::WA_GroupLeader)) { - w = w->parentWidget(); - if (w) - w = w->window(); - } - data->window_modality = (w && w->testAttribute(Qt::WA_GroupLeader)) - ? Qt::WindowModal - : Qt::ApplicationModal; + data->window_modality = Qt::ApplicationModal; // Some window managers do not allow us to enter modality after the // window is visible.The window must be hidden before changing the // windowModality property and then reshown. @@ -11334,11 +11384,11 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) break; case Qt::WA_MouseTracking: { QEvent e(QEvent::MouseTrackingChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); break; } case Qt::WA_TabletTracking: { QEvent e(QEvent::TabletTrackingChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); break; } case Qt::WA_NativeWindow: { d->createTLExtra(); @@ -11351,7 +11401,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) QGuiApplication::inputMethod()->commit(); QGuiApplication::inputMethod()->update(Qt::ImEnabled); } - if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget()) + if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget()) parentWidget()->d_func()->enforceNativeChildren(); if (on && !internalWinId() && testAttribute(Qt::WA_WState_Created)) d->createWinId(); @@ -11364,15 +11414,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) } case Qt::WA_PaintOnScreen: d->updateIsOpaque(); -#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ || 0 /* Used to be included in Qt4 for Q_WS_X11 */ || 0 /* Used to be included in Qt4 for Q_WS_MAC */ - // Recreate the widget if it's already created as an alien widget and - // WA_PaintOnScreen is enabled. Paint on screen widgets must have win id. - // So must their children. - if (on) { - setAttribute(Qt::WA_NativeWindow); - d->enforceNativeChildren(); - } -#endif Q_FALLTHROUGH(); case Qt::WA_OpaquePaintEvent: d->updateIsOpaque(); @@ -11384,9 +11425,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) d->updateSystemBackground(); break; case Qt::WA_TransparentForMouseEvents: -#if 0 // Used to be included in Qt4 for Q_WS_MAC - d->macUpdateIgnoreMouseEvents(); -#endif break; case Qt::WA_InputMethodEnabled: { #ifndef QT_NO_IM @@ -11403,20 +11441,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) d->resolveFont(); d->resolveLocale(); break; -#if 0 // Used to be included in Qt4 for Q_WS_X11 - case Qt::WA_NoX11EventCompression: - if (!d->extra) - d->createExtra(); - d->extra->compress_events = on; - break; - case Qt::WA_X11OpenGLOverlay: - d->updateIsOpaque(); - break; - case Qt::WA_X11DoNotAcceptFocus: - if (testAttribute(Qt::WA_WState_Created)) - d->updateX11AcceptFocus(); - break; -#endif case Qt::WA_DontShowOnScreen: { if (on && isVisible()) { // Make sure we keep the current state and only hide the widget @@ -11445,11 +11469,11 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) break; case Qt::WA_StaticContents: - if (QWidgetBackingStore *bs = d->maybeBackingStore()) { + if (QWidgetRepaintManager *repaintManager = d->maybeRepaintManager()) { if (on) - bs->addStaticWidget(this); + repaintManager->addStaticWidget(this); else - bs->removeStaticWidget(this); + repaintManager->removeStaticWidget(this); } break; case Qt::WA_TranslucentBackground: @@ -11459,10 +11483,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) break; case Qt::WA_AcceptTouchEvents: -#if 0 /* Used to be included in Qt4 for Q_WS_WIN */ || 0 /* Used to be included in Qt4 for Q_WS_MAC */ - if (on) - d->registerTouchWindow(); -#endif break; default: break; @@ -11503,7 +11523,7 @@ bool QWidget::testAttribute_helper(Qt::WidgetAttribute attribute) const \warning Changing this property from opaque to transparent might issue a paint event that needs to be processed before the window is displayed - correctly. This affects mainly the use of QPixmap::grabWindow(). Also note + correctly. This affects mainly the use of QScreen::grabWindow(). Also note that semi-transparent windows update and resize significantly slower than opaque windows. @@ -11571,7 +11591,7 @@ void QWidgetPrivate::setWindowOpacity_sys(qreal level) its parent because other children of the parent might have been modified. - \sa windowTitle, {Application Example}, {SDI Example}, {MDI Example} + \sa windowTitle */ bool QWidget::isWindowModified() const { @@ -11586,7 +11606,7 @@ void QWidget::setWindowModified(bool mod) d->setWindowModified_helper(); QEvent e(QEvent::ModifiedChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::setWindowModified_helper() @@ -11600,14 +11620,14 @@ void QWidgetPrivate::setWindowModified_helper() return; bool on = q->testAttribute(Qt::WA_WindowModified); if (!platformWindow->setWindowModified(on)) { - if (Q_UNLIKELY(on && !q->windowTitle().contains(QLatin1String("[*]")))) + if (Q_UNLIKELY(on && !q->windowTitle().contains("[*]"_L1))) qWarning("QWidget::setWindowModified: The window title does not contain a '[*]' placeholder"); setWindowTitle_helper(q->windowTitle()); setWindowIconText_helper(q->windowIconText()); } } -#ifndef QT_NO_TOOLTIP +#if QT_CONFIG(tooltip) /*! \property QWidget::toolTip @@ -11632,7 +11652,7 @@ void QWidget::setToolTip(const QString &s) d->toolTip = s; QEvent event(QEvent::ToolTipChange); - QApplication::sendEvent(this, &event); + QCoreApplication::sendEvent(this, &event); } QString QWidget::toolTip() const @@ -11664,7 +11684,7 @@ int QWidget::toolTipDuration() const return d->toolTipDuration; } -#endif // QT_NO_TOOLTIP +#endif // QT_CONFIG(tooltip) #if QT_CONFIG(statustip) @@ -11712,7 +11732,7 @@ QString QWidget::whatsThis() const } #endif // QT_CONFIG(whatsthis) -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) /*! \property QWidget::accessibleName @@ -11754,7 +11774,7 @@ QString QWidget::accessibleName() const \brief the widget's description as seen by assistive technologies The accessible description of a widget should convey what a widget does. - While the \l accessibleName should be a short and consise string (e.g. \gui{Save}), + While the \l accessibleName should be a short and concise string (e.g. \gui{Save}), the description should give more context, such as \gui{Saves the current document}. This property has to be \l{Internationalization with Qt}{localized}. @@ -11777,7 +11797,7 @@ QString QWidget::accessibleDescription() const Q_D(const QWidget); return d->accessibleDescription; } -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) #ifndef QT_NO_SHORTCUT /*! @@ -11809,7 +11829,7 @@ int QWidget::grabShortcut(const QKeySequence &key, Qt::ShortcutContext context) if (key.isEmpty()) return 0; setAttribute(Qt::WA_GrabbedShortcut); - return qApp->d_func()->shortcutMap.addShortcut(this, key, context, qWidgetShortcutContextMatcher); + return QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, key, context, qWidgetShortcutContextMatcher); } /*! @@ -11831,7 +11851,7 @@ void QWidget::releaseShortcut(int id) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.removeShortcut(id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(id, this, 0); } /*! @@ -11850,7 +11870,7 @@ void QWidget::setShortcutEnabled(int id, bool enable) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.setShortcutEnabled(enable, id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enable, id, this, 0); } /*! @@ -11865,18 +11885,18 @@ void QWidget::setShortcutAutoRepeat(int id, bool enable) { Q_ASSERT(qApp); if (id) - qApp->d_func()->shortcutMap.setShortcutAutoRepeat(enable, id, this, 0); + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(enable, id, this, 0); } #endif // QT_NO_SHORTCUT /*! - Updates the widget's micro focus. + Updates the widget's micro focus and informs input methods + that the state specified by \a query has changed. */ -void QWidget::updateMicroFocus() +void QWidget::updateMicroFocus(Qt::InputMethodQuery query) { - // updating everything since this is currently called for any kind of state change if (this == QGuiApplication::focusObject()) - QGuiApplication::inputMethod()->update(Qt::ImQueryAll); + QGuiApplication::inputMethod()->update(query); } /*! @@ -11911,7 +11931,7 @@ void QWidget::raise() QRegion region(rect()); d->subtractOpaqueSiblings(region); - d->invalidateBuffer(region); + d->invalidateBackingStore(region); } if (testAttribute(Qt::WA_WState_Created)) d->raise_sys(); @@ -11920,7 +11940,7 @@ void QWidget::raise() QWindowContainer::parentWasRaised(this); QEvent e(QEvent::ZOrderChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::raise_sys() @@ -11931,7 +11951,7 @@ void QWidgetPrivate::raise_sys() } else if (renderToTexture) { if (QWidget *p = q->parentWidget()) { setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + p->d_func()->invalidateBackingStore(effectiveRectFor(q->geometry())); } } } @@ -11970,7 +11990,7 @@ void QWidget::lower() QWindowContainer::parentWasLowered(this); QEvent e(QEvent::ZOrderChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::lower_sys() @@ -11981,7 +12001,7 @@ void QWidgetPrivate::lower_sys() q->windowHandle()->lower(); } else if (QWidget *p = q->parentWidget()) { setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + p->d_func()->invalidateBackingStore(effectiveRectFor(q->geometry())); } } @@ -12017,7 +12037,7 @@ void QWidget::stackUnder(QWidget* w) d->stackUnder_sys(w); QEvent e(QEvent::ZOrderChange); - QApplication::sendEvent(this, &e); + QCoreApplication::sendEvent(this, &e); } void QWidgetPrivate::stackUnder_sys(QWidget*) @@ -12025,13 +12045,13 @@ void QWidgetPrivate::stackUnder_sys(QWidget*) Q_Q(QWidget); if (QWidget *p = q->parentWidget()) { setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + p->d_func()->invalidateBackingStore(effectiveRectFor(q->geometry())); } } /*! \fn bool QWidget::isTopLevel() const - \obsolete + \deprecated Use isWindow() instead. */ @@ -12078,10 +12098,8 @@ QRect QWidgetPrivate::frameStrut() const } if (data.fstrut_dirty -#if 1 // Used to be excluded in Qt4 for Q_WS_WIN // ### Fix properly for 4.3 && q->isVisible() -#endif && q->testAttribute(Qt::WA_WState_Created)) const_cast<QWidgetPrivate *>(this)->updateFrameStrut(); @@ -12127,14 +12145,14 @@ bool QWidgetPrivate::navigateToDirection(Direction direction) Searches for a widget that is positioned in the \a direction, starting from the current focusWidget. - Returns the pointer to a found widget or 0, if there was no widget in - that direction. + Returns the pointer to a found widget or \nullptr, if there was no widget + in that direction. */ QWidget *QWidgetPrivate::widgetInNavigationDirection(Direction direction) { const QWidget *sourceWidget = QApplication::focusWidget(); if (!sourceWidget) - return 0; + return nullptr; const QRect sourceRect = sourceWidget->rect().translated(sourceWidget->mapToGlobal(QPoint())); const int sourceX = (direction == DirectionNorth || direction == DirectionSouth) ? @@ -12148,7 +12166,7 @@ QWidget *QWidgetPrivate::widgetInNavigationDirection(Direction direction) const QPoint sourceCenter = sourceRect.center(); const QWidget *sourceWindow = sourceWidget->window(); - QWidget *targetWidget = 0; + QWidget *targetWidget = nullptr; int shortestDistance = INT_MAX; const auto targetCandidates = QApplication::allWidgets(); @@ -12180,7 +12198,20 @@ QWidget *QWidgetPrivate::widgetInNavigationDirection(Direction direction) && targetCandidate->isVisible() // ...is in the same window, && targetCandidate->window() == sourceWindow) { - const int targetCandidateDistance = pointToRect(sourcePoint, targetCandidateRect); + const int targetCandidateDistance = [](const QPoint &sourcePoint, + const QRect &targetCandidateRect) { + int dx = 0; + int dy = 0; + if (p.x() < r.left()) + dx = r.left() - p.x(); + else if (p.x() > r.right()) + dx = p.x() - r.right(); + if (p.y() < r.top()) + dy = r.top() - p.y(); + else if (p.y() > r.bottom()) + dy = p.y() - r.bottom(); + return dx + dy; + }(); if (targetCandidateDistance < shortestDistance) { shortestDistance = targetCandidateDistance; targetWidget = targetCandidate; @@ -12241,7 +12272,7 @@ void QWidget::setBackingStore(QBackingStore *store) { // ### createWinId() ?? - if (!isTopLevel()) + if (!isWindow()) return; Q_D(QWidget); @@ -12251,17 +12282,17 @@ void QWidget::setBackingStore(QBackingStore *store) return; QBackingStore *oldStore = topData->backingStore; - deleteBackingStore(d); + delete topData->backingStore; topData->backingStore = store; - QWidgetBackingStore *bs = d->maybeBackingStore(); - if (!bs) + QWidgetRepaintManager *repaintManager = d->maybeRepaintManager(); + if (!repaintManager) return; - if (isTopLevel()) { - if (bs->store != oldStore && bs->store != store) - delete bs->store; - bs->store = store; + if (isWindow()) { + if (repaintManager->backingStore() != oldStore && repaintManager->backingStore() != store) + delete repaintManager->backingStore(); + repaintManager->setBackingStore(store); } } @@ -12277,9 +12308,8 @@ QBackingStore *QWidget::backingStore() const if (extra && extra->backingStore) return extra->backingStore; - QWidgetBackingStore *bs = d->maybeBackingStore(); - - return bs ? bs->store : 0; + QWidgetRepaintManager *repaintManager = d->maybeRepaintManager(); + return repaintManager ? repaintManager->backingStore() : nullptr; } void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const @@ -12347,28 +12377,6 @@ void QWidgetPrivate::adjustQuitOnCloseAttribute() } } -QOpenGLContext *QWidgetPrivate::shareContext() const -{ -#ifdef QT_NO_OPENGL - return 0; -#else - if (Q_UNLIKELY(!extra || !extra->topextra || !extra->topextra->window)) - return 0; - - QWidgetPrivate *that = const_cast<QWidgetPrivate *>(this); - if (!extra->topextra->shareContext) { - QOpenGLContext *ctx = new QOpenGLContext; - ctx->setShareContext(qt_gl_global_share_context()); - ctx->setFormat(extra->topextra->window->format()); - ctx->setScreen(extra->topextra->window->screen()); - ctx->create(); - that->extra->topextra->shareContext = ctx; - } - return that->extra->topextra->shareContext; -#endif // QT_NO_OPENGL -} - -#ifndef QT_NO_OPENGL void QWidgetPrivate::sendComposeStatus(QWidget *w, bool end) { QWidgetPrivate *wd = QWidgetPrivate::get(w); @@ -12384,7 +12392,6 @@ void QWidgetPrivate::sendComposeStatus(QWidget *w, bool end) sendComposeStatus(w, end); } } -#endif // QT_NO_OPENGL Q_WIDGETS_EXPORT QWidgetData *qt_qwidget_data(QWidget *widget) { @@ -12413,7 +12420,7 @@ QGraphicsProxyWidget *QWidget::graphicsProxyWidget() const if (d->extra) { return d->extra->proxyWidget; } - return 0; + return nullptr; } #endif @@ -12467,14 +12474,14 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) d->aboutToDestroy(); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); + parentWidget()->d_func()->invalidateBackingStore(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); if ((windowType() == Qt::Popup) && qApp) qApp->d_func()->closePopup(this); - if (this == QApplicationPrivate::active_window) - QApplication::setActiveWindow(0); + if (this == qApp->activeWindow()) + QApplicationPrivate::setActiveWindow(nullptr); if (QWidget::mouseGrabber() == this) releaseMouse(); if (QWidget::keyboardGrabber() == this) @@ -12538,7 +12545,7 @@ QPaintEngine *QWidget::paintEngine() const const_cast<QWidgetPrivate *>(d_func())->noPaintOnScreen = 1; #endif - return 0; //##### @@@ + return nullptr; //##### @@@ } // Do not call QWindow::mapToGlobal() until QPlatformWindow is properly showing. @@ -12595,40 +12602,58 @@ static MapToGlobalTransformResult mapToGlobalTransform(const QWidget *w) } /*! - \fn QPoint QWidget::mapToGlobal(const QPoint &pos) const + \fn QPointF QWidget::mapToGlobal(const QPointF &pos) const Translates the widget coordinate \a pos to global screen - coordinates. For example, \c{mapToGlobal(QPoint(0,0))} would give + coordinates. For example, \c{mapToGlobal(QPointF(0,0))} would give the global coordinates of the top-left pixel of the widget. \sa mapFromGlobal(), mapTo(), mapToParent() + \since 6.0 */ -QPoint QWidget::mapToGlobal(const QPoint &pos) const +QPointF QWidget::mapToGlobal(const QPointF &pos) const { const MapToGlobalTransformResult t = mapToGlobalTransform(this); - const QPoint g = t.transform.map(pos); + const QPointF g = t.transform.map(pos); return t.window ? t.window->mapToGlobal(g) : g; } /*! - \fn QPoint QWidget::mapFromGlobal(const QPoint &pos) const + \overload +*/ +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ + return mapToGlobal(QPointF(pos)).toPoint(); +} + +/*! + \fn QPointF QWidget::mapFromGlobal(const QPointF &pos) const Translates the global screen coordinate \a pos to widget coordinates. \sa mapToGlobal(), mapFrom(), mapFromParent() + \since 6.0 */ -QPoint QWidget::mapFromGlobal(const QPoint &pos) const +QPointF QWidget::mapFromGlobal(const QPointF &pos) const { const MapToGlobalTransformResult t = mapToGlobalTransform(this); - const QPoint windowLocal = t.window ? t.window->mapFromGlobal(pos) : pos; + const QPointF windowLocal = t.window ? t.window->mapFromGlobal(pos) : pos; return t.transform.inverted().map(windowLocal); } -QWidget *qt_pressGrab = 0; -QWidget *qt_mouseGrb = 0; +/*! + \overload +*/ +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ + return mapFromGlobal(QPointF(pos)).toPoint(); +} + +QWidget *qt_pressGrab = nullptr; +QWidget *qt_mouseGrb = nullptr; static bool mouseGrabWithCursor = false; -static QWidget *keyboardGrb = 0; +static QWidget *keyboardGrb = nullptr; static inline QWindow *grabberWindow(const QWidget *w) { @@ -12640,7 +12665,7 @@ static inline QWindow *grabberWindow(const QWidget *w) } #ifndef QT_NO_CURSOR -static void grabMouseForWidget(QWidget *widget, const QCursor *cursor = 0) +static void grabMouseForWidget(QWidget *widget, const QCursor *cursor = nullptr) #else static void grabMouseForWidget(QWidget *widget) #endif @@ -12660,7 +12685,7 @@ static void grabMouseForWidget(QWidget *widget) } qt_mouseGrb = widget; - qt_pressGrab = 0; + qt_pressGrab = nullptr; } static void releaseMouseGrabOfWidget(QWidget *widget) @@ -12676,7 +12701,7 @@ static void releaseMouseGrabOfWidget(QWidget *widget) window->setMouseGrabEnabled(false); } } - qt_mouseGrb = 0; + qt_mouseGrb = nullptr; } /*! @@ -12693,7 +12718,7 @@ static void releaseMouseGrabOfWidget(QWidget *widget) terminal. Use this function with extreme caution, and consider using the \c -nograb command line option while debugging. - It is almost never necessary to grab the mouse when using Qt, as + It is seldom necessary to grab the mouse when using Qt, as Qt grabs and releases it sensibly. In particular, Qt grabs the mouse when a mouse button is pressed and keeps it until the last button is released. @@ -12805,7 +12830,7 @@ void QWidget::releaseKeyboard() if (keyboardGrb == this) { if (QWindow *window = grabberWindow(this)) window->setKeyboardGrabEnabled(false); - keyboardGrb = 0; + keyboardGrb = nullptr; } } @@ -12815,7 +12840,7 @@ void QWidget::releaseKeyboard() Returns the widget that is currently grabbing the mouse input. If no widget in this application is currently grabbing the mouse, - 0 is returned. + \nullptr is returned. \sa grabMouse(), keyboardGrabber() */ @@ -12832,7 +12857,7 @@ QWidget *QWidget::mouseGrabber() Returns the widget that is currently grabbing the keyboard input. If no widget in this application is currently grabbing the - keyboard, 0 is returned. + keyboard, \nullptr is returned. \sa grabMouse(), mouseGrabber() */ @@ -12863,7 +12888,7 @@ QWidget *QWidget::keyboardGrabber() does not allow an application to interrupt what the user is currently doing in another application. - \sa isActiveWindow(), window(), show(), QWindowsWindowFunctions::setWindowActivationBehavior() + \sa isActiveWindow(), window(), show() */ void QWidget::activateWindow() { @@ -12882,57 +12907,63 @@ void QWidget::activateWindow() */ int QWidget::metric(PaintDeviceMetric m) const { - QWindow *topLevelWindow = 0; - QScreen *screen = 0; - if (QWidget *topLevel = window()) { - topLevelWindow = topLevel->windowHandle(); - if (topLevelWindow) - screen = topLevelWindow->screen(); - } - if (!screen && QGuiApplication::primaryScreen()) - screen = QGuiApplication::primaryScreen(); + QScreen *screen = this->screen(); if (!screen) { if (m == PdmDpiX || m == PdmDpiY) return 72; return QPaintDevice::metric(m); } - int val; - if (m == PdmWidth) { - val = data->crect.width(); - } else if (m == PdmWidthMM) { - val = data->crect.width() * screen->physicalSize().width() / screen->geometry().width(); - } else if (m == PdmHeight) { - val = data->crect.height(); - } else if (m == PdmHeightMM) { - val = data->crect.height() * screen->physicalSize().height() / screen->geometry().height(); - } else if (m == PdmDepth) { + + auto resolveDevicePixelRatio = [this, screen]() -> qreal { + + // Note: keep in sync with QBackingStorePrivate::backingStoreDevicePixelRatio()! + static bool downscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE") > 0; + QWindow *window = this->window()->windowHandle(); + if (window) + return downscale ? std::ceil(window->devicePixelRatio()) : window->devicePixelRatio(); + return screen->devicePixelRatio(); + }; + + switch (m) { + case PdmWidth: + return data->crect.width(); + case PdmWidthMM: + return data->crect.width() * screen->physicalSize().width() / screen->geometry().width(); + case PdmHeight: + return data->crect.height(); + case PdmHeightMM: + return data->crect.height() * screen->physicalSize().height() / screen->geometry().height(); + case PdmDepth: return screen->depth(); - } else if (m == PdmDpiX) { + case PdmDpiX: for (const QWidget *p = this; p; p = p->parentWidget()) { if (p->d_func()->extra && p->d_func()->extra->customDpiX) return p->d_func()->extra->customDpiX; } return qRound(screen->logicalDotsPerInchX()); - } else if (m == PdmDpiY) { + case PdmDpiY: for (const QWidget *p = this; p; p = p->parentWidget()) { if (p->d_func()->extra && p->d_func()->extra->customDpiY) return p->d_func()->extra->customDpiY; } return qRound(screen->logicalDotsPerInchY()); - } else if (m == PdmPhysicalDpiX) { + case PdmPhysicalDpiX: return qRound(screen->physicalDotsPerInchX()); - } else if (m == PdmPhysicalDpiY) { + case PdmPhysicalDpiY: return qRound(screen->physicalDotsPerInchY()); - } else if (m == PdmDevicePixelRatio) { - return topLevelWindow ? topLevelWindow->devicePixelRatio() : qApp->devicePixelRatio(); - } else if (m == PdmDevicePixelRatioScaled) { - return (QPaintDevice::devicePixelRatioFScale() * - (topLevelWindow ? topLevelWindow->devicePixelRatio() : qApp->devicePixelRatio())); - } else { - val = QPaintDevice::metric(m);// XXX + case PdmDevicePixelRatio: + return resolveDevicePixelRatio(); + case PdmDevicePixelRatioScaled: + return QPaintDevice::devicePixelRatioFScale() * resolveDevicePixelRatio(); + case PdmDevicePixelRatioF_EncodedA: + Q_FALLTHROUGH(); + case PdmDevicePixelRatioF_EncodedB: + return QPaintDevice::encodeMetricF(m, resolveDevicePixelRatio()); + default: + break; } - return val; + return QPaintDevice::metric(m); } /*! @@ -12969,14 +13000,14 @@ QPainter *QWidget::sharedPainter() const { // Someone sent a paint event directly to the widget if (!d_func()->redirectDev) - return 0; + return nullptr; QPainter *sp = d_func()->sharedPainter(); if (!sp || !sp->isActive()) - return 0; + return nullptr; if (sp->paintEngine()->paintDevice() != d_func()->redirectDev) - return 0; + return nullptr; return sp; } @@ -12990,8 +13021,16 @@ QPainter *QWidget::sharedPainter() const widget, window system controls in that area may or may not be visible, depending on the platform. - Note that this effect can be slow if the region is particularly - complex. + Since QRegion allows arbitrarily complex regions to be created, widget + masks can be made to suit the most unconventionally-shaped windows, and + even allow widgets to be displayed with holes in them. Note that this + effect can be slow if the region is particularly complex. + + Widget masks are used to hint to the window system that the application + does not want mouse events for areas outside the mask. On most systems, + they also result in coarse visual clipping. To get smooth window edges, use + translucent background and anti-aliased painting instead, as shown in the + \l{Translucent Background} example. \sa windowOpacity */ @@ -13010,10 +13049,8 @@ void QWidget::setMask(const QRegion &newMask) d->extra->mask = newMask; d->extra->hasMask = !newMask.isEmpty(); -#if 1 // Used to be excluded in Qt4 for Q_WS_MAC if (!testAttribute(Qt::WA_WState_Created)) return; -#endif d->setMask_sys(newMask); @@ -13079,7 +13116,7 @@ void QWidgetPrivate::setMask_sys(const QRegion ®ion) Masked widgets receive mouse events only on their visible portions. - \sa clearMask(), windowOpacity(), {Shaped Clock Example} + \sa clearMask(), windowOpacity() */ void QWidget::setMask(const QBitmap &bitmap) { @@ -13109,63 +13146,112 @@ void QWidgetPrivate::setWidgetParentHelper(QObject *widgetAsObject, QObject *new widget->setParent(static_cast<QWidget*>(newParent)); } +std::string QWidgetPrivate::flagsForDumping() const +{ + Q_Q(const QWidget); + std::string flags = QObjectPrivate::flagsForDumping(); + if (QApplication::focusWidget() == q) + flags += 'F'; + if (q->isVisible()) { + std::stringstream s; + s << '<' + << q->width() << 'x' << q->height() + << std::showpos << q->x() << q->y() + << '>'; + flags += s.str(); + } else { + flags += 'I'; + } + return flags; +} + void QWidgetPrivate::setNetWmWindowTypes(bool skipIfMissing) { +#if QT_CONFIG(xcb) Q_Q(QWidget); if (!q->windowHandle()) return; - int wmWindowType = 0; + QXcbWindow::WindowTypes wmWindowType = QXcbWindow::None; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDesktop)) - wmWindowType |= QXcbWindowFunctions::Desktop; + wmWindowType |= QXcbWindow::Desktop; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDock)) - wmWindowType |= QXcbWindowFunctions::Dock; + wmWindowType |= QXcbWindow::Dock; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeToolBar)) - wmWindowType |= QXcbWindowFunctions::Toolbar; + wmWindowType |= QXcbWindow::Toolbar; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeMenu)) - wmWindowType |= QXcbWindowFunctions::Menu; + wmWindowType |= QXcbWindow::Menu; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeUtility)) - wmWindowType |= QXcbWindowFunctions::Utility; + wmWindowType |= QXcbWindow::Utility; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeSplash)) - wmWindowType |= QXcbWindowFunctions::Splash; + wmWindowType |= QXcbWindow::Splash; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDialog)) - wmWindowType |= QXcbWindowFunctions::Dialog; + wmWindowType |= QXcbWindow::Dialog; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu)) - wmWindowType |= QXcbWindowFunctions::DropDownMenu; + wmWindowType |= QXcbWindow::DropDownMenu; if (q->testAttribute(Qt::WA_X11NetWmWindowTypePopupMenu)) - wmWindowType |= QXcbWindowFunctions::PopupMenu; + wmWindowType |= QXcbWindow::PopupMenu; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeToolTip)) - wmWindowType |= QXcbWindowFunctions::Tooltip; + wmWindowType |= QXcbWindow::Tooltip; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeNotification)) - wmWindowType |= QXcbWindowFunctions::Notification; + wmWindowType |= QXcbWindow::Notification; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeCombo)) - wmWindowType |= QXcbWindowFunctions::Combo; + wmWindowType |= QXcbWindow::Combo; if (q->testAttribute(Qt::WA_X11NetWmWindowTypeDND)) - wmWindowType |= QXcbWindowFunctions::Dnd; + wmWindowType |= QXcbWindow::Dnd; - if (wmWindowType == 0 && skipIfMissing) + if (wmWindowType == QXcbWindow::None && skipIfMissing) return; - QXcbWindowFunctions::setWmWindowType(q->windowHandle(), static_cast<QXcbWindowFunctions::WmWindowType>(wmWindowType)); + if (auto *xcbWindow = dynamic_cast<QXcbWindow*>(q->windowHandle()->handle())) + xcbWindow->setWindowType(wmWindowType); +#else + Q_UNUSED(skipIfMissing); +#endif +} + +/*! + \internal + \return \c true, if a child with \param policy exists and isn't a child of \param excludeChildrenOf. + Return false otherwise. + */ +bool QWidgetPrivate::hasChildWithFocusPolicy(Qt::FocusPolicy policy, const QWidget *excludeChildrenOf) const +{ + Q_Q(const QWidget); + const QWidgetList &children = q->findChildren<QWidget *>(Qt::FindChildrenRecursively); + for (const auto *child : children) { + if (child->focusPolicy() == policy && child->isEnabled() + && (!excludeChildrenOf || !excludeChildrenOf->isAncestorOf(child))) { + return true; + } + } + return false; } #ifndef QT_NO_DEBUG_STREAM -static inline void formatWidgetAttributes(QDebug debug, const QWidget *widget) +namespace { +QDebug operator<<(QDebug debug, const WidgetAttributes &attributes) { - const QMetaObject *qtMo = qt_getEnumMetaObject(Qt::WA_AttributeCount); - const QMetaEnum me = qtMo->enumerator(qtMo->indexOfEnumerator("WidgetAttribute")); - debug << ", attributes=["; - int count = 0; - for (int a = 0; a < Qt::WA_AttributeCount; ++a) { - if (widget->testAttribute(static_cast<Qt::WidgetAttribute>(a))) { - if (count++) - debug << ','; - debug << me.valueToKey(a); + const QDebugStateSaver saver(debug); + debug.nospace(); + debug << '['; + if (const QWidget *widget = attributes.widget) { + const QMetaObject *qtMo = qt_getEnumMetaObject(Qt::WA_AttributeCount); + const QMetaEnum me = qtMo->enumerator(qtMo->indexOfEnumerator("WidgetAttribute")); + int count = 0; + for (int a = 0; a < Qt::WA_AttributeCount; ++a) { + if (widget->testAttribute(static_cast<Qt::WidgetAttribute>(a))) { + if (count++) + debug << ','; + debug << me.valueToKey(a); + } } } debug << ']'; + return debug; +} } QDebug operator<<(QDebug debug, const QWidget *widget) @@ -13185,11 +13271,11 @@ QDebug operator<<(QDebug debug, const QWidget *widget) debug << ", disabled"; debug << ", states=" << widget->windowState() << ", type=" << widget->windowType() << ", flags=" << widget->windowFlags(); - formatWidgetAttributes(debug, widget); + debug << ", attributes=" << WidgetAttributes{widget}; if (widget->isWindow()) debug << ", window"; debug << ", " << geometry.width() << 'x' << geometry.height() - << forcesign << geometry.x() << geometry.y() << noforcesign; + << Qt::forcesign << geometry.x() << geometry.y() << Qt::noforcesign; if (frameGeometry != geometry) { const QMargins margins(geometry.x() - frameGeometry.x(), geometry.y() - frameGeometry.y(), @@ -13197,9 +13283,9 @@ QDebug operator<<(QDebug debug, const QWidget *widget) frameGeometry.bottom() - geometry.bottom()); debug << ", margins=" << margins; } - debug << ", devicePixelRatio=" << widget->devicePixelRatioF(); + debug << ", devicePixelRatio=" << widget->devicePixelRatio(); if (const WId wid = widget->internalWinId()) - debug << ", winId=0x" << hex << wid << dec; + debug << ", winId=0x" << Qt::hex << wid << Qt::dec; } debug << ')'; } else { @@ -13209,7 +13295,434 @@ QDebug operator<<(QDebug debug, const QWidget *widget) } #endif // !QT_NO_DEBUG_STREAM + +// *************************** Focus abstraction ************************************ + +#define FOCUS_NEXT(w) w->d_func()->focus_next +#define FOCUS_PREV(w) w->d_func()->focus_prev + +/*! + \internal + \return next or previous element in the focus chain, depending on + \param direction, irrespective of focus proxies or widgets with Qt::NoFocus. + */ +QWidget *QWidgetPrivate::nextPrevElementInFocusChain(FocusDirection direction) const +{ + Q_Q(const QWidget); + return direction == FocusDirection::Next ? FOCUS_NEXT(q) : FOCUS_PREV(q); +} + +/*! + \internal + Removes a widget from the focus chain, respecting the flags set in \param rules. + \list + \li EnsureFocusOut: If the widget has input focus, transfer focus to the next or previous widget + in the focus chain, depending on \param direction. + \li RemoveInconsistent: Remove the widget, even if its focus chain is inconsistent. + \li AssertConsistency: qFatal, if the focus chain is inconsistent. This is used in the QWidget destructor. + \endlist + \return \c true if the widget has been removed, otherwise \c false. + */ +bool QWidgetPrivate::removeFromFocusChain(FocusChainRemovalRules rules, FocusDirection direction) +{ + Q_Q(QWidget); + if (!isFocusChainConsistent()) { +#ifdef QT_DEBUG + if (rules.testFlag(FocusChainRemovalRule::AssertConsistency)) + qFatal() << q << "has inconsistent focus chain."; +#endif + qCDebug(lcWidgetFocus) << q << "wasn't removed, because of inconsistent focus chain."; + return false; + } + + if (!isInFocusChain()) { + qCDebug(lcWidgetFocus) << q << "wasn't removed, because it is not part of a focus chain."; + return false; + } + + if (rules.testFlag(FocusChainRemovalRule::EnsureFocusOut)) + q->focusNextPrevChild(direction == FocusDirection::Next); + + FOCUS_NEXT(FOCUS_PREV(q)) = FOCUS_NEXT(q); + FOCUS_PREV(FOCUS_NEXT(q)) = FOCUS_PREV(q); + initFocusChain(); + qCDebug(lcWidgetFocus) << q << "removed from focus chain."; + return true; +} + +/*! + \internal + Initialises the focus chain by making the widget point to itself. + */ +void QWidgetPrivate::initFocusChain() +{ + Q_Q(QWidget); + qCDebug(lcWidgetFocus) << "Initializing focus chain of" << q; + FOCUS_PREV(q) = q; + FOCUS_NEXT(q) = q; +} + +/*! + \internal + Reads QWidget children, which are not part of a focus chain yet. + Inserts them into the focus chain before or after the widget, + depending on \param direction and in the order of their creation. + This is used, when QWidget::setParent() causes a widget to change toplevel windows. + */ +void QWidgetPrivate::reparentFocusChildren(FocusDirection direction) +{ + Q_Q(QWidget); + + // separate the focus chain into new (children of myself) and old (the rest) + QWidget *firstOld = nullptr; + QWidget *lastOld = nullptr; // last in the old list + QWidget *lastNew = q; // last in the new list + bool prevWasNew = true; + QWidget *widget = nextPrevElementInFocusChain(direction); + + // For efficiency, do not maintain the list invariant inside the loop. + // Append items to the relevant list, and we optimize by not changing pointers, + // when subsequent items are going into the same list. + while (widget != q) { + bool currentIsNew = q->isAncestorOf(widget); + if (currentIsNew) { + if (!prevWasNew) { + // previous was old => append to new list + FOCUS_NEXT(lastNew) = widget; + FOCUS_PREV(widget) = lastNew; + } + lastNew = widget; + } else { + if (prevWasNew) { + // prev was new => append to old list, if it exists + if (lastOld) { + FOCUS_NEXT(lastOld) = widget; + FOCUS_PREV(widget) = lastOld; + } else { + // start the old list + firstOld = widget; + } + } + lastOld = widget; + } + widget = widget->d_func()->nextPrevElementInFocusChain(direction); + prevWasNew = currentIsNew; + } + + // repair old list: + if (firstOld) { + FOCUS_NEXT(lastOld) = firstOld; + FOCUS_PREV(firstOld) = lastOld; + } + + if (!q->isWindow()) { + QWidget *topLevel = q->window(); + // insert new chain into toplevel's chain + QWidget *prev = FOCUS_PREV(topLevel); + FOCUS_PREV(topLevel) = lastNew; + FOCUS_NEXT(prev) = q; + FOCUS_PREV(q) = prev; + FOCUS_NEXT(lastNew) = topLevel; + } else { + // repair new list + FOCUS_NEXT(lastNew) = q; + FOCUS_PREV(q) = lastNew; + } +} + +/*! + \internal + Inserts a widget into the focus chain before or after \param position, depending on + \param direction. + \return \c true, if the insertion has changed the focus chain, otherwise \c false. + */ +bool QWidgetPrivate::insertIntoFocusChain(FocusDirection direction, QWidget *position) +{ + Q_Q(QWidget); + Q_ASSERT(position); + QWidget *next = FOCUS_NEXT(q); + QWidget *previous = FOCUS_PREV(q); + + switch (direction) { + case FocusDirection::Next: + if (previous == position) { + qCDebug(lcWidgetFocus) << "No-op insertion." << q << "is already before" << position; + return false; + } + + removeFromFocusChain(FocusChainRemovalRule::AssertConsistency); + + FOCUS_NEXT(q) = FOCUS_NEXT(position); + FOCUS_PREV(FOCUS_NEXT(position)) = q; + FOCUS_NEXT(position) = q; + FOCUS_PREV(q) = position; + qCDebug(lcWidgetFocus) << q << "inserted after" << position; + break; + + case FocusDirection::Previous: + if (next == position) { + qCDebug(lcWidgetFocus) << "No-op insertion." << q << "is already after" << position; + return false; + } + + removeFromFocusChain(FocusChainRemovalRule::AssertConsistency); + + FOCUS_PREV(q) = FOCUS_PREV(position); + FOCUS_NEXT(FOCUS_PREV(position)) = q; + FOCUS_PREV(position) = q; + FOCUS_NEXT(q) = position; + qCDebug(lcWidgetFocus) << q << "inserted before" << position; + break; + } + + Q_ASSERT(isFocusChainConsistent()); + return true; +} + +/*! + \internal + Convenience override to insert a QWidgetList \param toBeInserted into the focus chain + before or after \param position, depending on \param direction. + \return \c true, if the insertion has changed the focus chain, otherwise \c false. + \note + \param toBeInserted must be a consistent focus chain. + */ +bool QWidgetPrivate::insertIntoFocusChain(const QWidgetList &toBeInserted, + FocusDirection direction, QWidget *position) +{ + if (toBeInserted.isEmpty()) { + qCDebug(lcWidgetFocus) << "No-op insertion of an empty list"; + return false; + } + + Q_ASSERT_X(!toBeInserted.contains(position), + Q_FUNC_INFO, + "Coding error: toBeInserted contains position"); + + QWidget *first = toBeInserted.constFirst(); + QWidget *last = toBeInserted.constLast(); + + // Call QWidget override to log accordingly + if (toBeInserted.count() == 1) + return first->d_func()->insertIntoFocusChain(direction, position); + + Q_ASSERT(first != last); + switch (direction) { + case FocusDirection::Previous: + if (FOCUS_PREV(position) == last) { + qCDebug(lcWidgetFocus) << "No-op insertion." << toBeInserted << "is already before" + << position; + return false; + } + FOCUS_NEXT(FOCUS_PREV(position)) = first; + FOCUS_PREV(first) = FOCUS_PREV(position); + FOCUS_NEXT(last) = position; + FOCUS_PREV(position) = last; + qCDebug(lcWidgetFocus) << toBeInserted << "inserted before" << position; + break; + case FocusDirection::Next: + if (FOCUS_PREV(position) == last) { + qCDebug(lcWidgetFocus) << "No-op insertion." << toBeInserted << "is already after" + << position; + return false; + } + FOCUS_PREV(FOCUS_NEXT(position)) = last; + FOCUS_NEXT(last) = FOCUS_NEXT(position); + FOCUS_PREV(first) = position; + FOCUS_NEXT(position) = first; + qCDebug(lcWidgetFocus) << toBeInserted << "inserted after" << position; + break; + } + + Q_ASSERT(position->d_func()->isFocusChainConsistent()); + return true; +} + +/*! + \internal + \return a QWidgetList, representing the part of the focus chain, + starting with \param from and ending with \param to, in \param direction. + */ +QWidgetList focusPath(QWidget *from, QWidget *to, QWidgetPrivate::FocusDirection direction) +{ + QWidgetList path({from}); + if (from == to) + return path; + + QWidget *current = from; + do { + switch (direction) { + case QWidgetPrivate::FocusDirection::Previous: + current = current->previousInFocusChain(); + break; + case QWidgetPrivate::FocusDirection::Next: + current = current->nextInFocusChain(); + break; + } + if (path.contains(current)) + return QWidgetList(); + path << current; + } while (current != to); + + return path; +} + +/*! + \internal + Removes the part from the focus chain starting with \param from and ending with \param to, + in \param direction. + \return removed part as a QWidgetList. + */ +QWidgetList QWidgetPrivate::takeFromFocusChain(QWidget *from, + QWidget *to, + FocusDirection direction) +{ + // Check if there is a path from->to in direction + const QWidgetList path = focusPath(from, to , direction); + if (path.isEmpty()) { + qCDebug(lcWidgetFocus) << "No-op removal. Focus chain from" << from << "doesn't lead to " << to; + return QWidgetList(); + } + + QWidget *first = path.constFirst(); + QWidget *last = path.constLast(); + if (first == last) { + first->d_func()->removeFromFocusChain(); + return QWidgetList({first}); + } + + FOCUS_NEXT(FOCUS_PREV(first)) = FOCUS_NEXT(last); + FOCUS_PREV(FOCUS_NEXT(last)) = FOCUS_PREV(first); + FOCUS_PREV(first) = last; + FOCUS_NEXT(last) = first; + qCDebug(lcWidgetFocus) << path << "removed from focus chain"; + return path; +} + +/*! + \internal + \return The last focus child of the widget, traversing the focus chain no further than + \param noFurtherThan. + */ +QWidget *QWidgetPrivate::determineLastFocusChild(QWidget *noFurtherThan) +{ + Q_Q(QWidget); + // Since we need to repeat the same logic for both 'first' and 'second', we add a function + // that determines the last focus child for a widget, taking proxies and compound widgets into + // account. If the target is not a compound widget (it doesn't have a focus proxy that points + // to a child), 'lastFocusChild' will be set to the target itself. + QWidget *lastFocusChild = q; + + QWidget *focusProxy = deepestFocusProxy(); + if (!focusProxy) { + // QTBUG-81097: Another case is possible here. We can have a child + // widget, that sets its focusProxy() to the parent (target). + // An example of such widget is a QLineEdit, nested into + // a QAbstractSpinBox. In this case such widget should be considered + // the last focus child. + for (auto *object : std::as_const(q->children())) { + QWidget *w = qobject_cast<QWidget *>(object); + if (w && w->focusProxy() == q) { + lastFocusChild = w; + break; + } + } + } else if (q->isAncestorOf(focusProxy)) { + lastFocusChild = focusProxy; + for (QWidget *focusNext = lastFocusChild->nextInFocusChain(); + focusNext != focusProxy && q->isAncestorOf(focusNext) + && focusNext->window() == focusProxy->window(); + focusNext = focusNext->nextInFocusChain()) { + if (focusNext == noFurtherThan) + break; + if (focusNext->focusPolicy() != Qt::NoFocus) + lastFocusChild = focusNext; + } + } + return lastFocusChild; +}; + +/*! + \internal + \return \c true, if the widget is part of a focus chain and \c false otherwise. + A widget is considered to be part of a focus chain, neither FOCUS_NEXT, nor FOCUS_PREV + are pointing to the widget itself. + + \note + This method doesn't check the consistency of the focus chain. + If multiple widgets have been removed from the focus chain by takeFromFocusChain(), + isInFocusChain() will return \c true for all of those widgets, even if they represent + an inconsistent focus chain. + */ +bool QWidgetPrivate::isInFocusChain() const +{ + Q_Q(const QWidget); + return !(FOCUS_NEXT(q) == q && FOCUS_PREV(q) == q); +} + +/*! + \internal + A focus chain is consistent, when it is circular: Following the chain in either direction + has to return to the beginning. This is why a newly constructed widget points to itself, + when the focus chain has been initialized. A newly constructed widget is considered to have + a consistent focus chain, while not being part of a focus chain. + + The method always returns \c true, when the logging category "qt.widgets.focus" is disabled. + When it is enabled, the method returns \c true early, if a widget is pointing to itself. + It returns \c false, if one of the following is detected: + \list + \li nullptr found in a previous/next pointer. + \li broken chain: widget A is B's previous, but B isn't A's next. + \li chain isn't closed: starting at A doesn't lead back to A. + \endlist + It return \c true, if none of the above is observed. + + \note + The focus chain is checked only in forward direction. + This is sufficient, because the check for a broken chain asserts consistent paths + in both directions. + */ +bool QWidgetPrivate::isFocusChainConsistent() const +{ + Q_Q(const QWidget); + const bool skip = !QLoggingCategory("qt.widgets.focus").isDebugEnabled(); + if (skip) + return true; + + if (!isInFocusChain()) + return true; + + const QWidget *position = q; + + for (int i = 0; i < QApplication::allWidgets().count(); ++i) { + if (!FOCUS_PREV(position) || !FOCUS_NEXT(position)) { + qCDebug(lcWidgetFocus) << "Nullptr found at:" << position + << "Previous pointing to" << FOCUS_PREV(position) + << "Next pointing to" << FOCUS_NEXT(position); + return false; + } + if (!(FOCUS_PREV(FOCUS_NEXT(position)) == position + && FOCUS_NEXT(FOCUS_PREV(position)) == position)) { + qCDebug(lcWidgetFocus) << "Inconsistent focus chain at:" << position + << "Previous pointing to" << FOCUS_PREV(FOCUS_NEXT(position)) + << "Next pointing to" << FOCUS_NEXT(FOCUS_PREV(position)); + return false; + } + position = FOCUS_NEXT(position); + if (position == q) + return true; + + } + + qCDebug(lcWidgetFocus) << "Focus chain leading from" << q << "to" << position << "is not closed."; + return false; +} + +#undef FOCUS_NEXT +#undef FOCUS_PREV + + QT_END_NAMESPACE #include "moc_qwidget.cpp" - +#include "moc_qwidget_p.cpp" |