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.cpp283
1 files changed, 214 insertions, 69 deletions
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index 4d863d088e..b40fd7e8e8 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -18,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"
@@ -128,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)
@@ -169,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));
}
/*!
@@ -183,6 +183,8 @@ 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);
@@ -206,15 +208,19 @@ QWindowPrivate::QWindowPrivate()
QWindowPrivate::~QWindowPrivate()
= default;
-void QWindowPrivate::init(QScreen *targetScreen)
+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.
@@ -224,6 +230,27 @@ void QWindowPrivate::init(QScreen *targetScreen)
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);
+ }
}
/*!
@@ -428,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;
@@ -494,7 +521,9 @@ void QWindowPrivate::setTopLevelScreen(QScreen *newScreen, bool recreate)
}
}
-void QWindowPrivate::create(bool recursive, WId nativeHandle)
+static constexpr auto kForeignWindowId = "_q_foreignWinId";
+
+void QWindowPrivate::create(bool recursive)
{
Q_Q(QWindow);
if (platformWindow)
@@ -508,6 +537,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()) {
@@ -515,6 +551,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
setTopLevelScreen(screen, false);
}
+ const WId nativeHandle = q->property(kForeignWindowId).value<WId>();
+
QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
platformWindow = nativeHandle ? platformIntegration->createForeignWindow(q, nativeHandle)
: platformIntegration->createPlatformWindow(q);
@@ -550,6 +588,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(q, &e);
+ updateDevicePixelRatio();
+
if (needsUpdate)
q->requestUpdate();
}
@@ -567,6 +607,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.
@@ -636,7 +707,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.
@@ -652,6 +723,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.
@@ -664,6 +738,9 @@ WId QWindow::winId() const
if (!d->platformWindow)
const_cast<QWindow *>(this)->create();
+ if (!d->platformWindow)
+ return 0;
+
return d->platformWindow->winId();
}
@@ -707,6 +784,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;
@@ -729,6 +810,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);
}
/*!
@@ -1220,6 +1314,8 @@ bool QWindow::isExposed() const
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
{
@@ -1293,14 +1389,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();
- return d->platformWindow->devicePixelRatio() * QHighDpiScaling::factor(this);
+ if (newDevicePixelRatio == devicePixelRatio)
+ return false;
+
+ devicePixelRatio = newDevicePixelRatio;
+ QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
+ QGuiApplication::sendEvent(q, &dprChangeEvent);
+ return true;
}
Qt::WindowState QWindowPrivate::effectiveState(Qt::WindowStates state)
@@ -1355,8 +1468,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();
}
@@ -1531,17 +1649,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()); });
}
/*!
@@ -1574,20 +1684,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);
}
/*!
@@ -1618,17 +1726,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()); });
}
/*!
@@ -1855,6 +1955,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)
@@ -1878,6 +1982,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()
*/
@@ -1909,12 +2017,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::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());
@@ -1951,6 +2063,16 @@ void QWindowPrivate::destroy()
QObject *object = childrenWindows.at(i);
if (object->isWindowType()) {
QWindow *w = static_cast<QWindow*>(object);
+ auto *childPlatformWindow = w->handle();
+ if (!childPlatformWindow)
+ continue;
+
+ // Decouple the foreign window from this window,
+ // so that destroying our native handle doesn't
+ // bring down the foreign window as well.
+ if (childPlatformWindow->isForeignWindow())
+ childPlatformWindow->setParent(nullptr);
+
qt_window_private(w)->destroy();
}
}
@@ -2123,20 +2245,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();
+ }
}
/*!
@@ -2424,7 +2552,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);
@@ -2434,7 +2562,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);
@@ -2488,13 +2616,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:
@@ -2554,11 +2675,13 @@ bool QWindow::event(QEvent *ev)
/*!
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
@@ -2747,7 +2870,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;
@@ -2785,7 +2913,12 @@ QPointF QWindow::mapFromGlobal(const QPointF &pos) const
// Calculate local position in the native coordinate system. (See comment for the
// 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;
@@ -2867,7 +3000,11 @@ QWindow *QWindow::fromWinId(WId id)
}
QWindow *window = new QWindow;
- qt_window_private(window)->create(false, id);
+
+ // Persist the winId in a private property so that we
+ // can recreate the window after being destroyed.
+ window->setProperty(kForeignWindowId, id);
+ window->create();
if (!window->handle()) {
delete window;
@@ -3016,6 +3153,14 @@ void *QWindow::resolveInterface(const char *name, int revision) const
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;
}
@@ -3058,7 +3203,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.