summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/kernel/qwindow.cpp')
-rw-r--r--src/gui/kernel/qwindow.cpp551
1 files changed, 360 insertions, 191 deletions
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index d93f43a310..46a787e706 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -1,46 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui 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) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwindow.h"
#include <qpa/qplatformwindow.h>
#include <qpa/qplatformintegration.h>
+#ifndef QT_NO_CONTEXTMENU
+#include <qpa/qplatformtheme.h>
+#endif
#include "qsurfaceformat.h"
#ifndef QT_NO_OPENGL
#include <qpa/qplatformopenglcontext.h>
@@ -51,7 +18,7 @@
#include "qwindow_p.h"
#include "qguiapplication_p.h"
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
# include "qaccessible.h"
#endif
#include "qhighdpiscaling_p.h"
@@ -66,6 +33,7 @@
#include <QStyleHints>
#include <qpa/qplatformcursor.h>
+#include <qpa/qplatformwindow_p.h>
QT_BEGIN_NAMESPACE
@@ -160,7 +128,7 @@ QWindow::QWindow(QScreen *targetScreen)
, QSurface(QSurface::Window)
{
Q_D(QWindow);
- d->init(targetScreen);
+ d->init(nullptr, targetScreen);
}
static QWindow *nonDesktopParent(QWindow *parent)
@@ -201,11 +169,11 @@ QWindow::QWindow(QWindow *parent)
\sa setParent()
*/
QWindow::QWindow(QWindowPrivate &dd, QWindow *parent)
- : QObject(dd, nonDesktopParent(parent))
+ : QObject(dd, nullptr)
, QSurface(QSurface::Window)
{
Q_D(QWindow);
- d->init();
+ d->init(nonDesktopParent(parent));
}
/*!
@@ -215,35 +183,74 @@ QWindow::~QWindow()
{
Q_D(QWindow);
d->destroy();
+ // Decouple from parent before window goes under
+ setParent(nullptr);
QGuiApplicationPrivate::window_list.removeAll(this);
if (!QGuiApplicationPrivate::is_app_closing)
QGuiApplicationPrivate::instance()->modalWindowList.removeOne(this);
- // focus_window is normally cleared in destroy(), but the window may in
- // some cases end up becoming the focus window again. Clear it again
- // here as a workaround. See QTBUG-75326.
+ // thse are normally cleared in destroy(), but the window may in
+ // some cases end up becoming the focus window again, or receive an enter
+ // event. Clear it again here as a workaround. See QTBUG-75326.
if (QGuiApplicationPrivate::focus_window == this)
QGuiApplicationPrivate::focus_window = nullptr;
+ if (QGuiApplicationPrivate::currentMouseWindow == this)
+ QGuiApplicationPrivate::currentMouseWindow = nullptr;
+ if (QGuiApplicationPrivate::currentMousePressWindow == this)
+ QGuiApplicationPrivate::currentMousePressWindow = nullptr;
+
+ d->isWindow = false;
}
-void QWindowPrivate::init(QScreen *targetScreen)
+QWindowPrivate::QWindowPrivate()
+ = default;
+
+QWindowPrivate::~QWindowPrivate()
+ = default;
+
+void QWindowPrivate::init(QWindow *parent, QScreen *targetScreen)
{
Q_Q(QWindow);
+ q->QObject::setParent(parent);
+
+ isWindow = true;
parentWindow = static_cast<QWindow *>(q->QObject::parent());
+ QScreen *connectScreen = targetScreen ? targetScreen : QGuiApplication::primaryScreen();
+
if (!parentWindow)
- connectToScreen(targetScreen ? targetScreen : QGuiApplication::primaryScreen());
+ connectToScreen(connectScreen);
// If your application aborts here, you are probably creating a QWindow
// before the screen list is populated.
if (Q_UNLIKELY(!parentWindow && !topLevelScreen)) {
qFatal("Cannot create window: no screens available");
- exit(1);
}
QGuiApplicationPrivate::window_list.prepend(q);
requestedFormat = QSurfaceFormat::defaultFormat();
+ devicePixelRatio = connectScreen->devicePixelRatio();
+
+ QObject::connect(q, &QWindow::screenChanged, q, [q, this](QScreen *){
+ // We may have changed scaling; trigger resize event if needed,
+ // except on Windows, where we send resize events during WM_DPICHANGED
+ // event handling. FIXME: unify DPI change handling across all platforms.
+#ifndef Q_OS_WIN
+ if (q->handle()) {
+ QWindowSystemInterfacePrivate::GeometryChangeEvent gce(q, QHighDpi::fromNativePixels(q->handle()->geometry(), q));
+ QGuiApplicationPrivate::processGeometryChangeEvent(&gce);
+ }
+#else
+ Q_UNUSED(q);
+#endif
+ updateDevicePixelRatio();
+ });
+
+ if (parentWindow) {
+ QChildWindowEvent childAddedEvent(QEvent::ChildWindowAdded, q);
+ QCoreApplication::sendEvent(parentWindow, &childAddedEvent);
+ }
}
/*!
@@ -324,7 +331,6 @@ void QWindow::setVisibility(Visibility v)
break;
default:
Q_ASSERT(false);
- break;
}
}
@@ -449,14 +455,14 @@ void QWindowPrivate::updateSiblingPosition(SiblingPosition position)
QObjectList &siblings = q->parent()->d_ptr->children;
- const int siblingCount = siblings.size() - 1;
+ const qsizetype siblingCount = siblings.size() - 1;
if (siblingCount == 0)
return;
- const int currentPosition = siblings.indexOf(q);
+ const qsizetype currentPosition = siblings.indexOf(q);
Q_ASSERT(currentPosition >= 0);
- const int targetPosition = position == PositionTop ? siblingCount : 0;
+ const qsizetype targetPosition = position == PositionTop ? siblingCount : 0;
if (currentPosition == targetPosition)
return;
@@ -464,7 +470,7 @@ void QWindowPrivate::updateSiblingPosition(SiblingPosition position)
siblings.move(currentPosition, targetPosition);
}
-inline bool QWindowPrivate::windowRecreationRequired(QScreen *newScreen) const
+bool QWindowPrivate::windowRecreationRequired(QScreen *newScreen) const
{
Q_Q(const QWindow);
const QScreen *oldScreen = q->screen();
@@ -472,7 +478,7 @@ inline bool QWindowPrivate::windowRecreationRequired(QScreen *newScreen) const
&& !(oldScreen && oldScreen->virtualSiblings().contains(newScreen));
}
-inline void QWindowPrivate::disconnectFromScreen()
+void QWindowPrivate::disconnectFromScreen()
{
if (topLevelScreen)
topLevelScreen = nullptr;
@@ -529,6 +535,13 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
if (q->parent())
q->parent()->create();
+ if (platformWindow) {
+ // Creating the parent window will end up creating any child window
+ // that was already visible, via setVisible. If this applies to us,
+ // we will already have a platform window at this point.
+ return;
+ }
+
// QPlatformWindow will poll geometry() during construction below. Set the
// screen here so that high-dpi scaling will use the correct scale factor.
if (q->isTopLevel()) {
@@ -571,6 +584,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(q, &e);
+ updateDevicePixelRatio();
+
if (needsUpdate)
q->requestUpdate();
}
@@ -588,6 +603,37 @@ QRectF QWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
return QRectF();
}
+void QWindowPrivate::setMinOrMaxSize(QSize *oldSizeMember, const QSize &size,
+ qxp::function_ref<void()> funcWidthChanged,
+ qxp::function_ref<void()> funcHeightChanged)
+{
+ Q_Q(QWindow);
+ Q_ASSERT(oldSizeMember);
+ const QSize adjustedSize =
+ size.expandedTo(QSize(0, 0)).boundedTo(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX));
+ if (*oldSizeMember == adjustedSize)
+ return;
+ const bool widthChanged = adjustedSize.width() != oldSizeMember->width();
+ const bool heightChanged = adjustedSize.height() != oldSizeMember->height();
+ *oldSizeMember = adjustedSize;
+
+ if (platformWindow && q->isTopLevel())
+ platformWindow->propagateSizeHints();
+
+ if (widthChanged)
+ funcWidthChanged();
+ if (heightChanged)
+ funcHeightChanged();
+
+ // resize window if current size is outside of min and max limits
+ if (minimumSize.width() <= maximumSize.width()
+ || minimumSize.height() <= maximumSize.height()) {
+ const QSize currentSize = q->size();
+ const QSize boundedSize = currentSize.expandedTo(minimumSize).boundedTo(maximumSize);
+ q->resize(boundedSize);
+ }
+}
+
/*!
Sets the \a surfaceType of the window.
@@ -627,6 +673,13 @@ QWindow::SurfaceType QWindow::surfaceType() const
By default, the window is not visible, you must call setVisible(true), or
show() or similar to make it visible.
+ \note Hiding a window does not remove the window from the windowing system,
+ it only hides it. On windowing systems that give full screen applications a
+ dedicated desktop (such as macOS), hiding a full screen window will not remove
+ that desktop, but leave it blank. Another window from the same application
+ might be shown full screen, and will fill that desktop. Use QWindow::close to
+ completely remove a window from the windowing system.
+
\sa show()
*/
void QWindow::setVisible(bool visible)
@@ -650,7 +703,7 @@ bool QWindow::isVisible() const
into an actual native surface. However, the window remains hidden until setVisible() is called.
Note that it is not usually necessary to call this function directly, as it will be implicitly
- called by show(), setVisible(), and other functions that require access to the platform
+ called by show(), setVisible(), winId(), and other functions that require access to the platform
resources.
Call destroy() to free the platform resources if necessary.
@@ -666,6 +719,9 @@ void QWindow::create()
/*!
Returns the window's platform id.
+ \note This function will cause the platform window to be created if it is not already.
+ Returns 0, if the platform window creation failed.
+
For platforms where this id might be useful, the value returned
will uniquely represent the window inside the corresponding screen.
@@ -678,6 +734,9 @@ WId QWindow::winId() const
if (!d->platformWindow)
const_cast<QWindow *>(this)->create();
+ if (!d->platformWindow)
+ return 0;
+
return d->platformWindow->winId();
}
@@ -721,6 +780,10 @@ void QWindow::setParent(QWindow *parent)
return;
}
+ QEvent parentAboutToChangeEvent(QEvent::ParentWindowAboutToChange);
+ QCoreApplication::sendEvent(this, &parentAboutToChangeEvent);
+
+ const auto previousParent = d->parentWindow;
QObject::setParent(parent);
d->parentWindow = parent;
@@ -743,6 +806,19 @@ void QWindow::setParent(QWindow *parent)
}
QGuiApplicationPrivate::updateBlockedStatus(this);
+
+ if (previousParent) {
+ QChildWindowEvent childRemovedEvent(QEvent::ChildWindowRemoved, this);
+ QCoreApplication::sendEvent(previousParent, &childRemovedEvent);
+ }
+
+ if (parent) {
+ QChildWindowEvent childAddedEvent(QEvent::ChildWindowAdded, this);
+ QCoreApplication::sendEvent(parent, &childAddedEvent);
+ }
+
+ QEvent parentChangedEvent(QEvent::ParentWindowChange);
+ QCoreApplication::sendEvent(this, &parentChangedEvent);
}
/*!
@@ -1226,12 +1302,16 @@ bool QWindow::isExposed() const
*/
/*!
- Returns \c true if the window should appear active from a style perspective.
+ Returns \c true if the window is active.
This is the case for the window that has input focus as well as windows
that are in the same parent / transient parent chain as the focus window.
+ Typically active windows should appear active from a style perspective.
+
To get the window that currently has focus, use QGuiApplication::focusWindow().
+
+ \sa requestActivate()
*/
bool QWindow::isActive() const
{
@@ -1305,14 +1385,31 @@ Qt::ScreenOrientation QWindow::contentOrientation() const
qreal QWindow::devicePixelRatio() const
{
Q_D(const QWindow);
+ return d->devicePixelRatio;
+}
+
+/*
+ Updates the cached devicePixelRatio value by polling for a new value.
+ Sends QEvent::DevicePixelRatioChange to the window if the DPR has changed.
+ Returns true if the DPR was changed.
+*/
+bool QWindowPrivate::updateDevicePixelRatio()
+{
+ Q_Q(QWindow);
// If there is no platform window use the associated screen's devicePixelRatio,
// which typically is the primary screen and will be correct for single-display
// systems (a very common case).
- if (!d->platformWindow)
- return screen()->devicePixelRatio();
+ const qreal newDevicePixelRatio = platformWindow ?
+ platformWindow->devicePixelRatio() * QHighDpiScaling::factor(q) : q->screen()->devicePixelRatio();
+
+ if (newDevicePixelRatio == devicePixelRatio)
+ return false;
- return d->platformWindow->devicePixelRatio() * QHighDpiScaling::factor(this);
+ devicePixelRatio = newDevicePixelRatio;
+ QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
+ QGuiApplication::sendEvent(q, &dprChangeEvent);
+ return true;
}
Qt::WindowState QWindowPrivate::effectiveState(Qt::WindowStates state)
@@ -1367,8 +1464,13 @@ void QWindow::setWindowStates(Qt::WindowStates state)
if (d->platformWindow)
d->platformWindow->setWindowState(state);
+
+ auto originalEffectiveState = QWindowPrivate::effectiveState(d->windowState);
d->windowState = state;
- emit windowStateChanged(QWindowPrivate::effectiveState(d->windowState));
+ auto newEffectiveState = QWindowPrivate::effectiveState(d->windowState);
+ if (newEffectiveState != originalEffectiveState)
+ emit windowStateChanged(newEffectiveState);
+
d->updateVisibility();
}
@@ -1543,17 +1645,9 @@ QSize QWindow::sizeIncrement() const
void QWindow::setMinimumSize(const QSize &size)
{
Q_D(QWindow);
- QSize adjustedSize = QSize(qBound(0, size.width(), QWINDOWSIZE_MAX), qBound(0, size.height(), QWINDOWSIZE_MAX));
- if (d->minimumSize == adjustedSize)
- return;
- QSize oldSize = d->minimumSize;
- d->minimumSize = adjustedSize;
- if (d->platformWindow && isTopLevel())
- d->platformWindow->propagateSizeHints();
- if (d->minimumSize.width() != oldSize.width())
- emit minimumWidthChanged(d->minimumSize.width());
- if (d->minimumSize.height() != oldSize.height())
- emit minimumHeightChanged(d->minimumSize.height());
+ d->setMinOrMaxSize(
+ &d->minimumSize, size, [this, d]() { emit minimumWidthChanged(d->minimumSize.width()); },
+ [this, d]() { emit minimumHeightChanged(d->minimumSize.height()); });
}
/*!
@@ -1586,20 +1680,18 @@ void QWindow::setY(int arg)
\property QWindow::width
\brief the width of the window's geometry
*/
-void QWindow::setWidth(int arg)
+void QWindow::setWidth(int w)
{
- if (width() != arg)
- resize(arg, height());
+ resize(w, height());
}
/*!
\property QWindow::height
\brief the height of the window's geometry
*/
-void QWindow::setHeight(int arg)
+void QWindow::setHeight(int h)
{
- if (height() != arg)
- resize(width(), arg);
+ resize(width(), h);
}
/*!
@@ -1630,17 +1722,9 @@ void QWindow::setMinimumHeight(int h)
void QWindow::setMaximumSize(const QSize &size)
{
Q_D(QWindow);
- QSize adjustedSize = QSize(qBound(0, size.width(), QWINDOWSIZE_MAX), qBound(0, size.height(), QWINDOWSIZE_MAX));
- if (d->maximumSize == adjustedSize)
- return;
- QSize oldSize = d->maximumSize;
- d->maximumSize = adjustedSize;
- if (d->platformWindow && isTopLevel())
- d->platformWindow->propagateSizeHints();
- if (d->maximumSize.width() != oldSize.width())
- emit maximumWidthChanged(d->maximumSize.width());
- if (d->maximumSize.height() != oldSize.height())
- emit maximumHeightChanged(d->maximumSize.height());
+ d->setMinOrMaxSize(
+ &d->maximumSize, size, [this, d]() { emit maximumWidthChanged(d->maximumSize.width()); },
+ [this, d]() { emit maximumHeightChanged(d->maximumSize.height()); });
}
/*!
@@ -1736,13 +1820,7 @@ void QWindow::setGeometry(const QRect &rect)
QScreen *newScreen = d->screenForGeometry(rect);
if (newScreen && isTopLevel())
d->setTopLevelScreen(newScreen, true);
-
- QRect nativeRect;
- if (newScreen && isTopLevel())
- nativeRect = QHighDpi::toNativePixels(rect, newScreen);
- else
- nativeRect = QHighDpi::toNativeLocalPosition(rect, newScreen);
- d->platformWindow->setGeometry(nativeRect);
+ d->platformWindow->setGeometry(QHighDpi::toNativeWindowGeometry(rect, this));
} else {
d->geometry = rect;
@@ -1873,6 +1951,10 @@ void QWindow::setFramePosition(const QPoint &point)
For interactively moving windows, see startSystemMove(). For interactively
resizing windows, see startSystemResize().
+ \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 position(), startSystemMove()
*/
void QWindow::setPosition(const QPoint &pt)
@@ -1896,6 +1978,10 @@ void QWindow::setPosition(int posx, int posy)
\fn QPoint QWindow::position() const
\brief Returns the position of the window on the desktop excluding any window frame
+ \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 setPosition()
*/
@@ -1927,11 +2013,16 @@ void QWindow::resize(int w, int h)
void QWindow::resize(const QSize &newSize)
{
Q_D(QWindow);
+
+ const QSize oldSize = size();
+ if (newSize == oldSize)
+ return;
+
d->positionPolicy = QWindowPrivate::WindowFrameExclusive;
if (d->platformWindow) {
- d->platformWindow->setGeometry(QHighDpi::toNativePixels(QRect(position(), newSize), this));
+ d->platformWindow->setGeometry(
+ QHighDpi::toNativeWindowGeometry(QRect(position(), newSize), this));
} else {
- const QSize oldSize = d->geometry.size();
d->geometry.setSize(newSize);
if (newSize.width() != oldSize.width())
emit widthChanged(newSize.width());
@@ -1972,17 +2063,6 @@ void QWindowPrivate::destroy()
}
}
- if (QGuiApplicationPrivate::focus_window == q)
- QGuiApplicationPrivate::focus_window = q->parent();
- if (QGuiApplicationPrivate::currentMouseWindow == q)
- QGuiApplicationPrivate::currentMouseWindow = q->parent();
- if (QGuiApplicationPrivate::currentMousePressWindow == q)
- QGuiApplicationPrivate::currentMousePressWindow = q->parent();
-
- for (int i = 0; i < QGuiApplicationPrivate::tabletDevicePoints.size(); ++i)
- if (QGuiApplicationPrivate::tabletDevicePoints.at(i).target == q)
- QGuiApplicationPrivate::tabletDevicePoints[i].target = q->parent();
-
bool wasVisible = q->isVisible();
visibilityOnDestroy = wasVisible && platformWindow;
@@ -1991,7 +2071,7 @@ void QWindowPrivate::destroy()
// Let subclasses act, typically by doing graphics resource cleaup, when
// the window, to which graphics resource may be tied, is going away.
//
- // NB! This is disfunctional when destroy() is invoked from the dtor since
+ // NB! This is dysfunctional when destroy() is invoked from the dtor since
// a reimplemented event() will not get called in the subclasses at that
// stage. However, the typical QWindow cleanup involves either close() or
// going through QWindowContainer, both of which will do an explicit, early
@@ -2003,9 +2083,18 @@ void QWindowPrivate::destroy()
// Unset platformWindow before deleting, so that the destructor of the
// platform window does not recurse back into the platform window via
// this window during destruction (e.g. as a result of platform events).
- QPlatformWindow *pw = platformWindow;
- platformWindow = nullptr;
- delete pw;
+ delete std::exchange(platformWindow, nullptr);
+
+ if (QGuiApplicationPrivate::focus_window == q)
+ QGuiApplicationPrivate::focus_window = q->parent();
+ if (QGuiApplicationPrivate::currentMouseWindow == q)
+ QGuiApplicationPrivate::currentMouseWindow = q->parent();
+ if (QGuiApplicationPrivate::currentMousePressWindow == q)
+ QGuiApplicationPrivate::currentMousePressWindow = q->parent();
+
+ for (int i = 0; i < QGuiApplicationPrivate::tabletDevicePoints.size(); ++i)
+ if (QGuiApplicationPrivate::tabletDevicePoints.at(i).target == q)
+ QGuiApplicationPrivate::tabletDevicePoints[i].target = q->parent();
resizeEventPending = true;
receivedExpose = false;
@@ -2142,20 +2231,26 @@ QObject *QWindow::focusObject() const
/*!
Shows the window.
- This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(),
+ For child windows, this is equivalent to calling showNormal().
+ Otherwise, it is equivalent to calling showFullScreen(), showMaximized(), or showNormal(),
depending on the platform's default behavior for the window type and flags.
\sa showFullScreen(), showMaximized(), showNormal(), hide(), QStyleHints::showIsFullScreen(), flags()
*/
void QWindow::show()
{
- Qt::WindowState defaultState = QGuiApplicationPrivate::platformIntegration()->defaultWindowState(d_func()->windowFlags);
- if (defaultState == Qt::WindowFullScreen)
- showFullScreen();
- else if (defaultState == Qt::WindowMaximized)
- showMaximized();
- else
+ if (parent()) {
showNormal();
+ } else {
+ const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
+ Qt::WindowState defaultState = platformIntegration->defaultWindowState(d_func()->windowFlags);
+ if (defaultState == Qt::WindowFullScreen)
+ showFullScreen();
+ else if (defaultState == Qt::WindowMaximized)
+ showMaximized();
+ else
+ showNormal();
+ }
}
/*!
@@ -2204,6 +2299,9 @@ void QWindow::showMaximized()
Equivalent to calling setWindowStates(Qt::WindowFullScreen) and then
setVisible(true).
+ See the \l{QWidget::showFullScreen()} documentation for platform-specific
+ considerations and limitations.
+
\sa setWindowStates(), setVisible()
*/
void QWindow::showFullScreen()
@@ -2242,15 +2340,52 @@ void QWindow::showNormal()
bool QWindow::close()
{
Q_D(QWindow);
+ if (d->inClose)
+ return true;
// Do not close non top level windows
- if (parent())
+ if (!isTopLevel())
return false;
if (!d->platformWindow)
return true;
- return d->platformWindow->close();
+ // The window might be deleted during close,
+ // as a result of delivering the close event.
+ QPointer guard(this);
+ d->inClose = true;
+ bool success = d->platformWindow->close();
+ if (guard)
+ d->inClose = false;
+
+ return success;
+}
+
+bool QWindowPrivate::participatesInLastWindowClosed() const
+{
+ Q_Q(const QWindow);
+
+ if (!q->isTopLevel())
+ return false;
+
+ // Tool-tip widgets do not normally have Qt::WA_QuitOnClose,
+ // but since we do not have a similar flag for non-widget
+ // windows we need an explicit exclusion here as well.
+ if (q->type() == Qt::ToolTip)
+ return false;
+
+ // A window with a transient parent is not a primary window,
+ // it's a secondary window.
+ if (q->transientParent())
+ return false;
+
+ return true;
+}
+
+bool QWindowPrivate::treatAsVisible() const
+{
+ Q_Q(const QWindow);
+ return q->isVisible();
}
/*!
@@ -2403,7 +2538,7 @@ bool QWindow::event(QEvent *ev)
case QEvent::FocusIn: {
focusInEvent(static_cast<QFocusEvent *>(ev));
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
QAccessible::State state;
state.active = true;
QAccessibleStateChangeEvent event(this, state);
@@ -2413,7 +2548,7 @@ bool QWindow::event(QEvent *ev)
case QEvent::FocusOut: {
focusOutEvent(static_cast<QFocusEvent *>(ev));
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
QAccessible::State state;
state.active = true;
QAccessibleStateChangeEvent event(this, state);
@@ -2427,20 +2562,25 @@ bool QWindow::event(QEvent *ev)
break;
#endif
- case QEvent::Close:
+ case QEvent::Close: {
+
+ Q_D(QWindow);
+ const bool wasVisible = d->treatAsVisible();
+ const bool participatesInLastWindowClosed = d->participatesInLastWindowClosed();
+
+ // The window might be deleted in the close event handler
+ QPointer<QWindow> deletionGuard(this);
closeEvent(static_cast<QCloseEvent*>(ev));
+
if (ev->isAccepted()) {
- Q_D(QWindow);
- bool wasVisible = isVisible();
- destroy();
- if (wasVisible) {
- // FIXME: This check for visibility is a workaround for both QWidgetWindow
- // and QWindow having logic to emit lastWindowClosed, and possibly quit the
- // application. We should find a better way to handle this.
- d->maybeQuitOnLastWindowClosed();
- }
+ if (deletionGuard)
+ destroy();
+ if (wasVisible && participatesInLastWindowClosed)
+ QGuiApplicationPrivate::instance()->maybeLastWindowClosed();
}
+
break;
+ }
case QEvent::Expose:
exposeEvent(static_cast<QExposeEvent *>(ev));
@@ -2462,13 +2602,6 @@ bool QWindow::event(QEvent *ev)
setIcon(icon());
break;
- case QEvent::WindowStateChange: {
- Q_D(QWindow);
- emit windowStateChanged(QWindowPrivate::effectiveState(d->windowState));
- d->updateVisibility();
- break;
- }
-
#if QT_CONFIG(tabletevent)
case QEvent::TabletPress:
case QEvent::TabletMove:
@@ -2491,17 +2624,50 @@ bool QWindow::event(QEvent *ev)
default:
return QObject::event(ev);
}
+
+#ifndef QT_NO_CONTEXTMENU
+ /*
+ QGuiApplicationPrivate::processContextMenuEvent blocks mouse-triggered
+ context menu events that the QPA plugin might generate. In practice that
+ never happens, as even on Windows WM_CONTEXTMENU is never generated by
+ the OS (we never call the default window procedure that would do that in
+ response to unhandled WM_RBUTTONUP).
+
+ So, we always have to syntheize QContextMenuEvent for mouse events anyway.
+ QWidgetWindow synthesizes QContextMenuEvent similar to this code, and
+ never calls QWindow::event, so we have to do it here as well.
+
+ This logic could be simplified by always synthesizing events in
+ QGuiApplicationPrivate, or perhaps even in each QPA plugin. See QTBUG-93486.
+ */
+ static const QEvent::Type contextMenuTrigger =
+ QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ?
+ QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
+ auto asMouseEvent = [](QEvent *ev) {
+ const auto t = ev->type();
+ return t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease
+ ? static_cast<QMouseEvent *>(ev) : nullptr ;
+ };
+ if (QMouseEvent *me = asMouseEvent(ev); me &&
+ ev->type() == contextMenuTrigger && me->button() == Qt::RightButton) {
+ QContextMenuEvent e(QContextMenuEvent::Mouse, me->position().toPoint(),
+ me->globalPosition().toPoint(), me->modifiers());
+ QGuiApplication::sendEvent(this, &e);
+ }
+#endif
return true;
}
/*!
Schedules a QEvent::UpdateRequest event to be delivered to this window.
- The event is delivered in sync with the display vsync on platforms
- where this is possible. Otherwise, the event is delivered after a
- delay of 5 ms. The additional time is there to give the event loop
- a bit of idle time to gather system events, and can be overridden
- using the QT_QPA_UPDATE_IDLE_TIME environment variable.
+ The event is delivered in sync with the display vsync on platforms where
+ this is possible. Otherwise, the event is delivered after a delay of at
+ most 5 ms. If the window's associated screen reports a
+ \l{QScreen::refreshRate()}{refresh rate} higher than 60 Hz, the interval is
+ scaled down to a value smaller than 5. The additional time is there to give
+ the event loop a bit of idle time to gather system events, and can be
+ overridden using the QT_QPA_UPDATE_IDLE_TIME environment variable.
When driving animations, this function should be called once after drawing
has completed. Calling this function multiple times will result in a single
@@ -2653,11 +2819,7 @@ void QWindow::tabletEvent(QTabletEvent *ev)
Should return true only if the event was handled.
*/
-#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool QWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
-#else
-bool QWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
-#endif
{
Q_UNUSED(eventType);
Q_UNUSED(message);
@@ -2681,7 +2843,7 @@ QPointF QWindow::mapToGlobal(const QPointF &pos) const
// QTBUG-43252, prefer platform implementation for foreign windows.
if (d->platformWindow
&& (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) {
- return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapToGlobalF(QHighDpi::toNativeLocalPosition(pos, this)), this);
+ return QHighDpi::fromNativeGlobalPosition(d->platformWindow->mapToGlobalF(QHighDpi::toNativeLocalPosition(pos, this)), this);
}
if (!QHighDpiScaling::isActive())
@@ -2694,7 +2856,12 @@ QPointF QWindow::mapToGlobal(const QPointF &pos) const
// Map the position (and the window's global position) to native coordinates, perform
// the addition, and then map back to device independent coordinates.
QPointF nativeLocalPos = QHighDpi::toNativeLocalPosition(pos, this);
- QPointF nativeWindowGlobalPos = QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
+ // Get the native window position directly from the platform window
+ // if available (it can be null if the window hasn't been shown yet),
+ // or fall back to scaling the QWindow position.
+ QPointF nativeWindowGlobalPos = d->platformWindow
+ ? d->platformWindow->mapToGlobal(QPoint(0,0)).toPointF()
+ : QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
QPointF nativeGlobalPos = nativeLocalPos + nativeWindowGlobalPos;
QPointF deviceIndependentGlobalPos = QHighDpi::fromNativeGlobalPosition(nativeGlobalPos, this);
return deviceIndependentGlobalPos;
@@ -2723,16 +2890,21 @@ QPointF QWindow::mapFromGlobal(const QPointF &pos) const
// QTBUG-43252, prefer platform implementation for foreign windows.
if (d->platformWindow
&& (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) {
- return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapFromGlobalF(QHighDpi::toNativeLocalPosition(pos, this)), this);
+ return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapFromGlobalF(QHighDpi::toNativeGlobalPosition(pos, this)), this);
}
if (!QHighDpiScaling::isActive())
return pos - d->globalPosition();
// Calculate local position in the native coordinate system. (See comment for the
- // correspinding mapToGlobal() code above).
+ // corresponding mapToGlobal() code above).
QPointF nativeGlobalPos = QHighDpi::toNativeGlobalPosition(pos, this);
- QPointF nativeWindowGlobalPos = QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
+ // Get the native window position directly from the platform window
+ // if available (it can be null if the window hasn't been shown yet),
+ // or fall back to scaling the QWindow position.
+ QPointF nativeWindowGlobalPos = d->platformWindow
+ ? d->platformWindow->mapToGlobal(QPoint(0,0)).toPointF()
+ : QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this);
QPointF nativeLocalPos = nativeGlobalPos - nativeWindowGlobalPos;
QPointF deviceIndependentLocalPos = QHighDpi::fromNativeLocalPosition(nativeLocalPos, this);
return deviceIndependentLocalPos;
@@ -2768,34 +2940,6 @@ Q_GUI_EXPORT QWindowPrivate *qt_window_private(QWindow *window)
return window->d_func();
}
-void QWindowPrivate::maybeQuitOnLastWindowClosed()
-{
- if (!QCoreApplication::instance())
- return;
-
- Q_Q(QWindow);
- if (!q->isTopLevel())
- return;
-
- QWindowList list = QGuiApplication::topLevelWindows();
- bool lastWindowClosed = true;
- for (int i = 0; i < list.size(); ++i) {
- QWindow *w = list.at(i);
- if (!w->isVisible() || w->transientParent() || w->type() == Qt::ToolTip)
- continue;
- lastWindowClosed = false;
- break;
- }
- if (lastWindowClosed) {
- QGuiApplicationPrivate::emitLastWindowClosed();
-
- if (QGuiApplication::quitOnLastWindowClosed()) {
- QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance()));
- applicationPrivate->maybeQuit();
- }
- }
-}
-
QWindow *QWindowPrivate::topLevelWindow(QWindow::AncestorMode mode) const
{
Q_Q(const QWindow);
@@ -2813,13 +2957,6 @@ QWindow *QWindowPrivate::topLevelWindow(QWindow::AncestorMode mode) const
return window;
}
-#if QT_CONFIG(opengl)
-QOpenGLContext *QWindowPrivate::shareContext() const
-{
- return qt_gl_global_share_context();
-};
-#endif
-
/*!
Creates a local representation of a window created by another process or by
using native libraries below Qt.
@@ -2860,7 +2997,7 @@ QWindow *QWindow::fromWinId(WId id)
}
/*!
- Causes an alert to be shown for \a msec miliseconds. If \a msec is \c 0 (the
+ Causes an alert to be shown for \a msec milliseconds. If \a msec is \c 0 (the
default), then the alert is shown indefinitely until the window becomes
active again. This function has no effect on an active window.
@@ -2977,6 +3114,38 @@ bool QWindowPrivate::applyCursor()
}
#endif // QT_NO_CURSOR
+void *QWindow::resolveInterface(const char *name, int revision) const
+{
+ using namespace QNativeInterface::Private;
+
+ auto *platformWindow = handle();
+ Q_UNUSED(platformWindow);
+ Q_UNUSED(name);
+ Q_UNUSED(revision);
+
+#if defined(Q_OS_WIN)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWindowsWindow, platformWindow);
+#endif
+
+#if QT_CONFIG(xcb)
+ QT_NATIVE_INTERFACE_RETURN_IF(QXcbWindow, platformWindow);
+#endif
+
+#if defined(Q_OS_MACOS)
+ QT_NATIVE_INTERFACE_RETURN_IF(QCocoaWindow, platformWindow);
+#endif
+
+#if QT_CONFIG(wayland)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWaylandWindow, platformWindow);
+#endif
+
+#if defined(Q_OS_WASM)
+ QT_NATIVE_INTERFACE_RETURN_IF(QWasmWindow, platformWindow);
+#endif
+
+ return nullptr;
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QWindow *window)
{
@@ -3016,7 +3185,7 @@ QDebug operator<<(QDebug debug, const QWindow *window)
}
#endif // !QT_NO_DEBUG_STREAM
-#if QT_CONFIG(vulkan) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(vulkan) || defined(Q_QDOC)
/*!
Associates this window with the specified Vulkan \a instance.