summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/platforms/wasm/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp2
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.h2
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp166
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h18
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowstack.cpp203
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowstack.h63
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowstack.inc384
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowtreenode.cpp147
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowtreenode.h37
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowtreenode.inc128
-rw-r--r--tests/auto/wasm/qwasmwindowstack/tst_qwasmwindowstack.cpp318
-rw-r--r--tests/auto/wasm/qwasmwindowtreenode/tst_qwasmwindowtreenode.cpp43
13 files changed, 1078 insertions, 435 deletions
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt
index b00684a1378..03cb0d52ca1 100644
--- a/src/plugins/platforms/wasm/CMakeLists.txt
+++ b/src/plugins/platforms/wasm/CMakeLists.txt
@@ -32,7 +32,7 @@ qt_internal_add_plugin(QWasmIntegrationPlugin
qwasmwindowtreenode.cpp qwasmwindowtreenode.h
qwasmwindownonclientarea.cpp qwasmwindownonclientarea.h
qwasminputcontext.cpp qwasminputcontext.h
- qwasmwindowstack.cpp qwasmwindowstack.h
+ qwasmwindowstack.h
DEFINES
QT_EGL_NO_X11
QT_NO_FOREACH
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index badc5468c8b..a2c8306b13b 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -325,7 +325,7 @@ emscripten::val QWasmScreen::containerElement()
return m_shadowContainer;
}
-QWasmWindowTreeNode *QWasmScreen::parentNode()
+QWasmWindowTreeNode<> *QWasmScreen::parentNode()
{
return nullptr;
}
diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h
index 66e7e39d008..a19818af2ff 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.h
+++ b/src/plugins/platforms/wasm/qwasmscreen.h
@@ -25,7 +25,7 @@ class QWasmCompositor;
class QWasmDeadKeySupport;
class QOpenGLContext;
-class QWasmScreen : public QObject, public QPlatformScreen, public QWasmWindowTreeNode
+class QWasmScreen : public QObject, public QPlatformScreen, public QWasmWindowTreeNode<>
{
Q_OBJECT
public:
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 3337be184d6..3135bb6d655 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -37,17 +37,6 @@
QT_BEGIN_NAMESPACE
-namespace {
-QWasmWindowStack::PositionPreference positionPreferenceFromWindowFlags(Qt::WindowFlags flags)
-{
- if (flags.testFlag(Qt::WindowStaysOnTopHint))
- return QWasmWindowStack::PositionPreference::StayOnTop;
- if (flags.testFlag(Qt::WindowStaysOnBottomHint))
- return QWasmWindowStack::PositionPreference::StayOnBottom;
- return QWasmWindowStack::PositionPreference::Regular;
-}
-} // namespace
-
Q_GUI_EXPORT int qt_defaultDpiX();
QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
@@ -132,6 +121,16 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
registerEventHandlers();
+ m_transientWindowChangedConnection =
+ QObject::connect(
+ window(), &QWindow::transientParentChanged,
+ window(), [this](QWindow *tp) { onTransientParentChanged(tp); });
+
+ m_modalityChangedConnection =
+ QObject::connect(
+ window(), &QWindow::modalityChanged,
+ window(), [this](Qt::WindowModality) { onModalityChanged(); });
+
setParent(parent());
}
@@ -214,6 +213,9 @@ QWasmWindow::~QWasmWindow()
#if QT_CONFIG(accessibility)
QWasmAccessibility::onRemoveWindow(window());
#endif
+ QObject::disconnect(m_transientWindowChangedConnection);
+ QObject::disconnect(m_modalityChangedConnection);
+
shutdown();
emscripten::val::module_property("specialHTMLTargets").delete_(canvasSelector());
@@ -225,6 +227,37 @@ QWasmWindow::~QWasmWindow()
emscripten_cancel_animation_frame(m_requestAnimationFrameId);
}
+void QWasmWindow::shutdown()
+{
+ if (!window() ||
+ (QGuiApplication::focusWindow() && // Don't act if we have a focus window different from this
+ QGuiApplication::focusWindow() != window()))
+ return;
+
+ // Make a list of all windows sorted on active index.
+ // Skip windows with active index 0 as they have
+ // never been active.
+ std::map<uint64_t, QWasmWindow *> allWindows;
+ for (const auto &w : platformScreen()->allWindows()) {
+ if (w->getActiveIndex() > 0)
+ allWindows.insert({w->getActiveIndex(), w});
+ }
+
+ // window is not in all windows
+ if (getActiveIndex() > 0)
+ allWindows.insert({getActiveIndex(), this});
+
+ if (allWindows.size() >= 2) {
+ const auto lastIt = std::prev(allWindows.end());
+ const auto prevIt = std::prev(lastIt);
+ const auto lastW = lastIt->second;
+ const auto prevW = prevIt->second;
+
+ if (lastW == this) // Only act if window is last to be active
+ prevW->requestActivateWindow();
+ }
+}
+
QSurfaceFormat QWasmWindow::format() const
{
return window()->requestedFormat();
@@ -237,6 +270,24 @@ QWasmWindow *QWasmWindow::fromWindow(const QWindow *window)
return static_cast<QWasmWindow *>(window->handle());
}
+QWasmWindow *QWasmWindow::transientParent() const
+{
+ if (!window())
+ return nullptr;
+
+ return fromWindow(window()->transientParent());
+}
+
+Qt::WindowFlags QWasmWindow::windowFlags() const
+{
+ return window()->flags();
+}
+
+bool QWasmWindow::isModal() const
+{
+ return window()->isModal();
+}
+
void QWasmWindow::onRestoreClicked()
{
window()->setWindowState(Qt::WindowNoState);
@@ -471,30 +522,14 @@ void QWasmWindow::onActivationChanged(bool active)
dom::syncCSSClassWith(m_decoratedWindow, "inactive", !active);
}
-// Fix top level window flags in case only the type flags are passed.
-static inline Qt::WindowFlags fixTopLevelWindowFlags(Qt::WindowFlags flags)
-{
- if (!(flags.testFlag(Qt::CustomizeWindowHint))) {
- if (flags.testFlag(Qt::Window)) {
- flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint
- |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint;
- }
- if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Tool))
- flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
-
- if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen)
- flags |= Qt::FramelessWindowHint;
- }
- return flags;
-}
-
void QWasmWindow::setWindowFlags(Qt::WindowFlags flags)
{
flags = fixTopLevelWindowFlags(flags);
- if (flags.testFlag(Qt::WindowStaysOnTopHint) != m_flags.testFlag(Qt::WindowStaysOnTopHint)
- || flags.testFlag(Qt::WindowStaysOnBottomHint)
- != m_flags.testFlag(Qt::WindowStaysOnBottomHint)) {
+ if ((flags.testFlag(Qt::WindowStaysOnTopHint) != m_flags.testFlag(Qt::WindowStaysOnTopHint))
+ || (flags.testFlag(Qt::WindowStaysOnBottomHint)
+ != m_flags.testFlag(Qt::WindowStaysOnBottomHint))
+ || shouldBeAboveTransientParentFlags(flags) != shouldBeAboveTransientParentFlags(m_flags)) {
onPositionPreferenceChanged(positionPreferenceFromWindowFlags(flags));
}
m_flags = flags;
@@ -886,6 +921,55 @@ bool QWasmWindow::processWheel(const WheelEvent &event)
Qt::MouseEventNotSynthesized, event.webkitDirectionInvertedFromDevice);
}
+// Fix top level window flags in case only the type flags are passed.
+Qt::WindowFlags QWasmWindow::fixTopLevelWindowFlags(Qt::WindowFlags flags) const
+{
+ if (!(flags.testFlag(Qt::CustomizeWindowHint))) {
+ if (flags.testFlag(Qt::Window)) {
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint
+ |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint;
+ }
+ if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Tool))
+ flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+
+ if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen)
+ flags |= Qt::FramelessWindowHint;
+ }
+ return flags;
+}
+
+bool QWasmWindow::shouldBeAboveTransientParentFlags(Qt::WindowFlags flags) const
+{
+ if (!transientParent())
+ return false;
+
+ if (isModal())
+ return true;
+
+ if (flags.testFlag(Qt::Tool) ||
+ flags.testFlag(Qt::SplashScreen) ||
+ flags.testFlag(Qt::ToolTip) ||
+ flags.testFlag(Qt::Popup))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+QWasmWindowStack<>::PositionPreference QWasmWindow::positionPreferenceFromWindowFlags(Qt::WindowFlags flags) const
+{
+ flags = fixTopLevelWindowFlags(flags);
+
+ if (flags.testFlag(Qt::WindowStaysOnTopHint))
+ return QWasmWindowStack<>::PositionPreference::StayOnTop;
+ if (flags.testFlag(Qt::WindowStaysOnBottomHint))
+ return QWasmWindowStack<>::PositionPreference::StayOnBottom;
+ if (shouldBeAboveTransientParentFlags(flags))
+ return QWasmWindowStack<>::PositionPreference::StayAboveTransientParent;
+ return QWasmWindowStack<>::PositionPreference::Regular;
+}
+
QRect QWasmWindow::normalGeometry() const
{
return m_normalGeometry;
@@ -996,6 +1080,22 @@ void QWasmWindow::setMask(const QRegion &region)
m_decoratedWindow["style"].set("clipPath", emscripten::val(cssClipPath.str()));
}
+void QWasmWindow::onTransientParentChanged(QWindow *newTransientParent)
+{
+ Q_UNUSED(newTransientParent);
+
+ const auto positionPreference = positionPreferenceFromWindowFlags(window()->flags());
+ QWasmWindowTreeNode::onParentChanged(parentNode(), nullptr, positionPreference);
+ QWasmWindowTreeNode::onParentChanged(nullptr, parentNode(), positionPreference);
+}
+
+void QWasmWindow::onModalityChanged()
+{
+ const auto positionPreference = positionPreferenceFromWindowFlags(window()->flags());
+ QWasmWindowTreeNode::onParentChanged(parentNode(), nullptr, positionPreference);
+ QWasmWindowTreeNode::onParentChanged(nullptr, parentNode(), positionPreference);
+}
+
void QWasmWindow::setParent(const QPlatformWindow *)
{
// The window flags depend on whether we are a
@@ -1015,7 +1115,7 @@ emscripten::val QWasmWindow::containerElement()
return m_window;
}
-QWasmWindowTreeNode *QWasmWindow::parentNode()
+QWasmWindowTreeNode<> *QWasmWindow::parentNode()
{
if (parent())
return static_cast<QWasmWindow *>(parent());
@@ -1028,7 +1128,7 @@ QWasmWindow *QWasmWindow::asWasmWindow()
}
void QWasmWindow::onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current,
- QWasmWindowStack::PositionPreference positionPreference)
+ QWasmWindowStack<>::PositionPreference positionPreference)
{
if (previous)
previous->containerElement().call<void>("removeChild", m_decoratedWindow);
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index 67a3e8ea293..904e736a7e7 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -37,7 +37,7 @@ struct WheelEvent;
Q_DECLARE_LOGGING_CATEGORY(qLcQpaWasmInputContext)
class QWasmWindow final : public QPlatformWindow,
- public QWasmWindowTreeNode,
+ public QWasmWindowTreeNode<>,
public QNativeInterface::Private::QWasmWindow
{
public:
@@ -46,6 +46,9 @@ public:
~QWasmWindow() final;
static QWasmWindow *fromWindow(const QWindow *window);
+ QWasmWindow *transientParent() const;
+ Qt::WindowFlags windowFlags() const;
+ bool isModal() const;
QSurfaceFormat format() const override;
void registerEventHandlers();
@@ -106,15 +109,23 @@ public:
emscripten::val containerElement() final;
QWasmWindowTreeNode *parentNode() final;
+public slots:
+ void onTransientParentChanged(QWindow *newTransientParent);
+ void onModalityChanged();
+
private:
friend class QWasmScreen;
static constexpr auto defaultWindowSize = 160;
+ QMetaObject::Connection m_transientWindowChangedConnection;
+ QMetaObject::Connection m_modalityChangedConnection;
+
// QWasmWindowTreeNode:
QWasmWindow *asWasmWindow() final;
void onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current,
- QWasmWindowStack::PositionPreference positionPreference) final;
+ QWasmWindowStack<>::PositionPreference positionPreference) final;
+ void shutdown();
void invalidate();
bool hasFrame() const;
bool hasTitleBar() const;
@@ -134,6 +145,9 @@ private:
bool deliverPointerEvent(const PointerEvent &event);
void handleWheelEvent(const emscripten::val &event);
bool processWheel(const WheelEvent &event);
+ Qt::WindowFlags fixTopLevelWindowFlags(Qt::WindowFlags) const;
+ bool shouldBeAboveTransientParentFlags(Qt::WindowFlags flags) const;
+ QWasmWindowStack<>::PositionPreference positionPreferenceFromWindowFlags(Qt::WindowFlags) const;
QWasmCompositor *m_compositor = nullptr;
QWasmBackingStore *m_backingStore = nullptr;
diff --git a/src/plugins/platforms/wasm/qwasmwindowstack.cpp b/src/plugins/platforms/wasm/qwasmwindowstack.cpp
deleted file mode 100644
index d3769c7a1bb..00000000000
--- a/src/plugins/platforms/wasm/qwasmwindowstack.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-
-#include "qwasmwindowstack.h"
-
-QT_BEGIN_NAMESPACE
-
-QWasmWindowStack::QWasmWindowStack(WindowOrderChangedCallbackType windowOrderChangedCallback)
- : m_windowOrderChangedCallback(std::move(windowOrderChangedCallback)),
- m_regularWindowsBegin(m_windowStack.begin()),
- m_alwaysOnTopWindowsBegin(m_windowStack.begin())
-{
-}
-
-QWasmWindowStack::~QWasmWindowStack() = default;
-
-void QWasmWindowStack::pushWindow(QWasmWindow *window, PositionPreference position)
-{
- Q_ASSERT(m_windowStack.count(window) == 0);
-
- if (position == PositionPreference::StayOnTop) {
- const auto stayOnTopDistance =
- std::distance(m_windowStack.begin(), m_alwaysOnTopWindowsBegin);
- const auto regularDistance = std::distance(m_windowStack.begin(), m_regularWindowsBegin);
- m_windowStack.push_back(window);
- m_alwaysOnTopWindowsBegin = m_windowStack.begin() + stayOnTopDistance;
- m_regularWindowsBegin = m_windowStack.begin() + regularDistance;
- } else if (position == PositionPreference::Regular) {
- const auto regularDistance = std::distance(m_windowStack.begin(), m_regularWindowsBegin);
- m_alwaysOnTopWindowsBegin = m_windowStack.insert(m_alwaysOnTopWindowsBegin, window) + 1;
- m_regularWindowsBegin = m_windowStack.begin() + regularDistance;
- } else {
- const auto stayOnTopDistance =
- std::distance(m_windowStack.begin(), m_alwaysOnTopWindowsBegin);
- m_regularWindowsBegin = m_windowStack.insert(m_regularWindowsBegin, window) + 1;
- m_alwaysOnTopWindowsBegin = m_windowStack.begin() + stayOnTopDistance + 1;
- }
-
- m_windowOrderChangedCallback();
-}
-
-void QWasmWindowStack::removeWindow(QWasmWindow *window)
-{
- Q_ASSERT(m_windowStack.count(window) == 1);
-
- auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
- const auto position = getWindowPositionPreference(it);
- const auto stayOnTopDistance = std::distance(m_windowStack.begin(), m_alwaysOnTopWindowsBegin);
- const auto regularDistance = std::distance(m_windowStack.begin(), m_regularWindowsBegin);
-
- m_windowStack.erase(it);
-
- m_alwaysOnTopWindowsBegin = m_windowStack.begin() + stayOnTopDistance
- - (position != PositionPreference::StayOnTop ? 1 : 0);
- m_regularWindowsBegin = m_windowStack.begin() + regularDistance
- - (position == PositionPreference::StayOnBottom ? 1 : 0);
-
- m_windowOrderChangedCallback();
-}
-
-void QWasmWindowStack::raise(QWasmWindow *window)
-{
- Q_ASSERT(m_windowStack.count(window) == 1);
-
- if (window == topWindow())
- return;
-
- auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
- auto itEnd = ([this, position = getWindowPositionPreference(it)]() {
- switch (position) {
- case PositionPreference::StayOnTop:
- return m_windowStack.end();
- case PositionPreference::Regular:
- return m_alwaysOnTopWindowsBegin;
- case PositionPreference::StayOnBottom:
- return m_regularWindowsBegin;
- }
- })();
- std::rotate(it, it + 1, itEnd);
- m_windowOrderChangedCallback();
-}
-
-void QWasmWindowStack::lower(QWasmWindow *window)
-{
- Q_ASSERT(m_windowStack.count(window) == 1);
-
- if (window == *m_windowStack.begin())
- return;
-
- auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
- auto itBegin = ([this, position = getWindowPositionPreference(it)]() {
- switch (position) {
- case PositionPreference::StayOnTop:
- return m_alwaysOnTopWindowsBegin;
- case PositionPreference::Regular:
- return m_regularWindowsBegin;
- case PositionPreference::StayOnBottom:
- return m_windowStack.begin();
- }
- })();
-
- std::rotate(itBegin, it, it + 1);
- m_windowOrderChangedCallback();
-}
-
-void QWasmWindowStack::windowPositionPreferenceChanged(QWasmWindow *window,
- PositionPreference position)
-{
- auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
- const auto currentPosition = getWindowPositionPreference(it);
-
- const auto zones = static_cast<int>(position) - static_cast<int>(currentPosition);
- Q_ASSERT(zones != 0);
-
- if (zones < 0) {
- // Perform right rotation so that the window lands on top of regular windows
- const auto begin = std::make_reverse_iterator(it + 1);
- const auto end = position == PositionPreference::Regular
- ? std::make_reverse_iterator(m_alwaysOnTopWindowsBegin)
- : std::make_reverse_iterator(m_regularWindowsBegin);
- std::rotate(begin, begin + 1, end);
- if (zones == -2) {
- ++m_alwaysOnTopWindowsBegin;
- ++m_regularWindowsBegin;
- } else if (position == PositionPreference::Regular) {
- ++m_alwaysOnTopWindowsBegin;
- } else {
- ++m_regularWindowsBegin;
- }
- } else {
- // Perform left rotation so that the window lands at the bottom of always on top windows
- const auto begin = it;
- const auto end = position == PositionPreference::Regular ? m_regularWindowsBegin
- : m_alwaysOnTopWindowsBegin;
- std::rotate(begin, begin + 1, end);
- if (zones == 2) {
- --m_alwaysOnTopWindowsBegin;
- --m_regularWindowsBegin;
- } else if (position == PositionPreference::Regular) {
- --m_regularWindowsBegin;
- } else {
- --m_alwaysOnTopWindowsBegin;
- }
- }
- m_windowOrderChangedCallback();
-}
-
-QWasmWindowStack::iterator QWasmWindowStack::begin()
-{
- return m_windowStack.rbegin();
-}
-
-QWasmWindowStack::iterator QWasmWindowStack::end()
-{
- return m_windowStack.rend();
-}
-
-QWasmWindowStack::const_iterator QWasmWindowStack::begin() const
-{
- return m_windowStack.rbegin();
-}
-
-QWasmWindowStack::const_iterator QWasmWindowStack::end() const
-{
- return m_windowStack.rend();
-}
-
-QWasmWindowStack::const_reverse_iterator QWasmWindowStack::rbegin() const
-{
- return m_windowStack.begin();
-}
-
-QWasmWindowStack::const_reverse_iterator QWasmWindowStack::rend() const
-{
- return m_windowStack.end();
-}
-
-bool QWasmWindowStack::empty() const
-{
- return m_windowStack.empty();
-}
-
-size_t QWasmWindowStack::size() const
-{
- return m_windowStack.size();
-}
-
-QWasmWindow *QWasmWindowStack::topWindow() const
-{
- return m_windowStack.empty() ? nullptr : m_windowStack.last();
-}
-
-QWasmWindowStack::PositionPreference
-QWasmWindowStack::getWindowPositionPreference(StorageType::iterator windowIt) const
-{
- if (windowIt >= m_alwaysOnTopWindowsBegin)
- return PositionPreference::StayOnTop;
- if (windowIt >= m_regularWindowsBegin)
- return PositionPreference::Regular;
- return PositionPreference::StayOnBottom;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindowstack.h b/src/plugins/platforms/wasm/qwasmwindowstack.h
index c75001157a0..90ad4c9dd15 100644
--- a/src/plugins/platforms/wasm/qwasmwindowstack.h
+++ b/src/plugins/platforms/wasm/qwasmwindowstack.h
@@ -6,6 +6,7 @@
#include <qglobal.h>
#include <QtCore/qlist.h>
+#include <QDebug>
#include <vector>
@@ -21,31 +22,49 @@ class QWasmWindow;
// Access to the top element is facilitated by |topWindow|.
// Changes to the top element are signaled via the |topWindowChangedCallback| supplied at
// construction.
+
+// Requirement Window
+//
+// type Window {
+// Window *transientParent() const;
+// Qt::WindowFlags windowFlags() const;
+// bool isModal() const;
+// };
+
+template <typename Window=QWasmWindow>
class Q_AUTOTEST_EXPORT QWasmWindowStack
{
-public:
- using WindowOrderChangedCallbackType = std::function<void()>;
-
- using StorageType = QList<QWasmWindow *>;
+private:
+ QWasmWindowStack(const QWasmWindowStack &) = delete;
+ QWasmWindowStack(QWasmWindowStack &&) = delete;
- using iterator = StorageType::reverse_iterator;
- using const_iterator = StorageType::const_reverse_iterator;
- using const_reverse_iterator = StorageType::const_iterator;
+ QWasmWindowStack &operator=(const QWasmWindowStack &) = delete;
+ QWasmWindowStack &&operator=(QWasmWindowStack &&) = delete;
+public:
enum class PositionPreference {
StayOnBottom,
Regular,
StayOnTop,
+ StayAboveTransientParent // Parent is transientParent()
};
+ using WindowOrderChangedCallbackType = std::function<void()>;
+ using StorageType = QList<Window *>;
+
+ using iterator = typename StorageType::reverse_iterator;
+ using const_iterator = typename StorageType::const_reverse_iterator;
+ using const_reverse_iterator = typename StorageType::const_iterator;
+
explicit QWasmWindowStack(WindowOrderChangedCallbackType topWindowChangedCallback);
~QWasmWindowStack();
- void pushWindow(QWasmWindow *window, PositionPreference position);
- void removeWindow(QWasmWindow *window);
- void raise(QWasmWindow *window);
- void lower(QWasmWindow *window);
- void windowPositionPreferenceChanged(QWasmWindow *window, PositionPreference position);
+ void pushWindow(Window *window, PositionPreference position, bool insertAtRegionBegin = false,
+ bool callCallbacks = true);
+ void removeWindow(Window *window, bool callCallbacks = true);
+ void raise(Window *window);
+ void lower(Window *window);
+ void windowPositionPreferenceChanged(Window *window, PositionPreference position);
// Iterates top-to-bottom
iterator begin();
@@ -59,17 +78,25 @@ public:
bool empty() const;
size_t size() const;
- QWasmWindow *topWindow() const;
+ Window *topWindow() const;
+ PositionPreference getWindowPositionPreference(typename StorageType::const_iterator windowIt,
+ bool testStayAbove = true) const;
private:
- PositionPreference getWindowPositionPreference(StorageType::iterator windowIt) const;
-
+ bool raiseImpl(Window *window);
+ bool lowerImpl(Window *window);
+ bool shouldBeAboveTransientParent(const Window *window) const;
+ bool shouldBeAboveTransientParentFlags(Qt::WindowFlags flags) const;
+ void invariant();
WindowOrderChangedCallbackType m_windowOrderChangedCallback;
- QList<QWasmWindow *> m_windowStack;
- StorageType::iterator m_regularWindowsBegin;
- StorageType::iterator m_alwaysOnTopWindowsBegin;
+
+ StorageType m_windowStack;
+ typename StorageType::iterator m_regularWindowsBegin;
+ typename StorageType::iterator m_alwaysOnTopWindowsBegin;
};
+#include "qwasmwindowstack.inc"
+
QT_END_NAMESPACE
#endif // QWASMWINDOWSTACK_H
diff --git a/src/plugins/platforms/wasm/qwasmwindowstack.inc b/src/plugins/platforms/wasm/qwasmwindowstack.inc
new file mode 100644
index 00000000000..9f0b5b2d491
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindowstack.inc
@@ -0,0 +1,384 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QWASMWINDOWSTACK_INC
+#define QWASMWINDOWSTACK_INC
+
+template <typename Window>
+QWasmWindowStack<Window>::QWasmWindowStack(
+ WindowOrderChangedCallbackType windowOrderChangedCallback)
+ : m_windowOrderChangedCallback(std::move(windowOrderChangedCallback)),
+ m_regularWindowsBegin(m_windowStack.begin()),
+ m_alwaysOnTopWindowsBegin(m_windowStack.begin())
+{
+ invariant();
+}
+
+template <typename Window>
+QWasmWindowStack<Window>::~QWasmWindowStack()
+{
+ invariant();
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::invariant()
+{
+ Q_ASSERT(m_regularWindowsBegin >= m_windowStack.begin());
+ Q_ASSERT(m_regularWindowsBegin <= m_alwaysOnTopWindowsBegin);
+ Q_ASSERT(m_alwaysOnTopWindowsBegin <= m_windowStack.end());
+}
+
+/* insert a window at the correct location.
+ *
+ * There are three groups
+ * StayOnBottom
+ * Regular
+ * StayOnTop
+ *
+ * In addition there is StayAboveParent which
+ * can place the window in either of the
+ * three above groups, depending on which
+ * group the transient parent resides in.
+ *
+ * insertAtRegionBegin controls the placement
+ * within each of the groups. Either at the end
+ * or at the beginning.
+ *
+*/
+template <typename Window>
+void QWasmWindowStack<Window>::pushWindow(Window *window, PositionPreference position,
+ bool insertAtRegionBegin, bool callCallbacks)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 0);
+
+ auto regularDistance = std::distance(m_windowStack.begin(), m_regularWindowsBegin);
+ auto stayOnTopDistance = std::distance(m_windowStack.begin(), m_alwaysOnTopWindowsBegin);
+
+ if (position == PositionPreference::StayAboveTransientParent) {
+ Q_ASSERT(window->transientParent());
+ const auto it =
+ std::find(m_windowStack.begin(), m_windowStack.end(), window->transientParent());
+ if (it == m_windowStack.end()) {
+ qWarning() << "QWasmWindowStack<Window>::pushWindow - missing parent"
+ << window->transientParent();
+ pushWindow(window, PositionPreference::Regular, insertAtRegionBegin, callCallbacks);
+ return;
+ } else {
+ if (it >= m_alwaysOnTopWindowsBegin)
+ ;
+ else if (it >= m_regularWindowsBegin)
+ ++stayOnTopDistance;
+ else {
+ ++regularDistance;
+ ++stayOnTopDistance;
+ }
+ m_windowStack.insert(it + 1, window);
+ }
+ } else if (position == PositionPreference::StayOnTop) {
+ if (insertAtRegionBegin)
+ m_windowStack.insert(m_alwaysOnTopWindowsBegin, window);
+ else
+ m_windowStack.insert(m_windowStack.end(), window);
+
+ } else if (position == PositionPreference::Regular) {
+ ++stayOnTopDistance;
+ if (insertAtRegionBegin)
+ m_windowStack.insert(m_regularWindowsBegin, window);
+ else
+ m_windowStack.insert(m_alwaysOnTopWindowsBegin, window);
+
+ } else {
+ // StayOnBottom
+ ++regularDistance;
+ ++stayOnTopDistance;
+
+ if (insertAtRegionBegin)
+ m_windowStack.insert(m_windowStack.begin(), window);
+ else
+ m_windowStack.insert(m_regularWindowsBegin, window);
+ }
+
+ m_regularWindowsBegin = m_windowStack.begin() + regularDistance;
+ m_alwaysOnTopWindowsBegin = m_windowStack.begin() + stayOnTopDistance;
+
+ if (callCallbacks)
+ m_windowOrderChangedCallback();
+
+ Q_ASSERT(m_windowStack.count(window) == 1);
+ invariant();
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::removeWindow(Window *window, bool callCallbacks)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 1);
+
+ auto regularDistance = std::distance(m_windowStack.begin(), m_regularWindowsBegin);
+ auto stayOnTopDistance = std::distance(m_windowStack.begin(), m_alwaysOnTopWindowsBegin);
+
+ auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+
+ Q_ASSERT(it != m_windowStack.end());
+
+ if (it < m_regularWindowsBegin)
+ --regularDistance;
+ if (it < m_alwaysOnTopWindowsBegin)
+ --stayOnTopDistance;
+
+ m_windowStack.erase(it);
+
+ m_regularWindowsBegin = m_windowStack.begin() + regularDistance;
+ m_alwaysOnTopWindowsBegin = m_windowStack.begin() + stayOnTopDistance;
+
+ if (callCallbacks)
+ m_windowOrderChangedCallback();
+
+ Q_ASSERT(m_windowStack.count(window) == 0);
+ invariant();
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::raise(Window *window)
+{
+ if (raiseImpl(window))
+ m_windowOrderChangedCallback();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::raiseImpl(Window *window)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 1);
+
+ {
+ const auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+ const auto itEnd = ([this, it]() {
+ if (it < m_regularWindowsBegin)
+ return m_regularWindowsBegin;
+ if (it < m_alwaysOnTopWindowsBegin)
+ return m_alwaysOnTopWindowsBegin;
+ return m_windowStack.end();
+ })();
+
+ if (it + 1 == itEnd)
+ return false;
+
+ std::rotate(it, it + 1, itEnd);
+ }
+
+ std::vector<Window *> windowsToRaise;
+ {
+ for (auto trit = m_windowStack.begin(); trit != m_windowStack.end(); ++trit) {
+ const auto w = *trit;
+ if ((w != window) &&
+ (getWindowPositionPreference(trit) == PositionPreference::StayAboveTransientParent) &&
+ (w->transientParent() == window)) {
+ windowsToRaise.push_back(w);
+ }
+ }
+ }
+
+ for (const auto w : windowsToRaise)
+ {
+ raiseImpl(w);
+ }
+ invariant();
+ return true;
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::lower(Window *window)
+{
+ if (lowerImpl(window))
+ m_windowOrderChangedCallback();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::lowerImpl(Window *window)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 1);
+
+ {
+ const auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+ const auto itBegin = ([this, it]() {
+ if (it >= m_alwaysOnTopWindowsBegin)
+ return m_alwaysOnTopWindowsBegin;
+ if (it >= m_regularWindowsBegin)
+ return m_regularWindowsBegin;
+ return m_windowStack.begin();
+ })();
+
+ if (itBegin == it)
+ return false;
+
+ std::rotate(itBegin, it, it + 1);
+ }
+
+ std::vector<Window *> windowsToLower;
+ {
+ for (auto trit = m_windowStack.begin(); trit != m_windowStack.end(); ++trit) {
+ const auto w = *trit;
+ if ((w != window) &&
+ (getWindowPositionPreference(trit) == PositionPreference::StayAboveTransientParent) &&
+ (w->transientParent() == window)) {
+ windowsToLower.push_back(w);
+ }
+ }
+ }
+
+ for (const auto w : windowsToLower)
+ {
+ lowerImpl(w);
+ }
+ invariant();
+ return true;
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::windowPositionPreferenceChanged(Window *window,
+ PositionPreference position)
+{
+ invariant();
+
+ auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+ const auto currentPosition = getWindowPositionPreference(it);
+
+ if (position == currentPosition) {
+ ;
+ } else if (currentPosition == PositionPreference::StayAboveTransientParent) {
+ // Keep position if possible
+ const bool isStayOnBottom ( it < m_regularWindowsBegin);
+ const bool isRegular( !isStayOnBottom && (it < m_alwaysOnTopWindowsBegin));
+ const bool isStayOnTop(!isStayOnBottom && !isRegular);
+
+ if (isStayOnBottom && (position == PositionPreference::StayOnBottom))
+ ;
+ else if (isRegular && (position == PositionPreference::Regular))
+ ;
+ else if (isStayOnTop && (position == PositionPreference::StayOnTop))
+ ;
+ else {
+ auto current = *it;
+ removeWindow(current, false);
+ pushWindow(current, position, false, false);
+ m_windowOrderChangedCallback();
+ }
+ } else {
+ const bool insertAtRegionBegin = (
+ (currentPosition != PositionPreference::StayAboveTransientParent) &&
+ (position != PositionPreference::StayAboveTransientParent) &&
+ ((currentPosition == PositionPreference::StayOnBottom) ||
+ (position == PositionPreference::StayOnTop)));
+
+ auto current = *it;
+ removeWindow(current, false);
+ pushWindow(current, position, insertAtRegionBegin, false);
+ m_windowOrderChangedCallback();
+ }
+ invariant();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::iterator QWasmWindowStack<Window>::begin()
+{
+ return m_windowStack.rbegin();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::iterator QWasmWindowStack<Window>::end()
+{
+ return m_windowStack.rend();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_iterator QWasmWindowStack<Window>::begin() const
+{
+ return m_windowStack.rbegin();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_iterator QWasmWindowStack<Window>::end() const
+{
+ return m_windowStack.rend();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_reverse_iterator
+QWasmWindowStack<Window>::rbegin() const
+{
+ return m_windowStack.begin();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_reverse_iterator
+QWasmWindowStack<Window>::rend() const
+{
+ return m_windowStack.end();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::empty() const
+{
+ return m_windowStack.empty();
+}
+
+template <typename Window>
+size_t QWasmWindowStack<Window>::size() const
+{
+ return m_windowStack.size();
+}
+
+template <typename Window>
+Window *QWasmWindowStack<Window>::topWindow() const
+{
+ return m_windowStack.empty() ? nullptr : m_windowStack.last();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::shouldBeAboveTransientParentFlags(Qt::WindowFlags flags) const
+{
+ if (flags.testFlag(Qt::Tool) ||
+ flags.testFlag(Qt::SplashScreen) ||
+ flags.testFlag(Qt::ToolTip) ||
+ flags.testFlag(Qt::Popup))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::shouldBeAboveTransientParent(const Window *window) const
+{
+ if (!window->transientParent())
+ return false;
+
+ if (window->isModal())
+ return true;
+
+ if (shouldBeAboveTransientParentFlags(window->windowFlags()))
+ return true;
+
+ return false;
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::PositionPreference
+QWasmWindowStack<Window>::getWindowPositionPreference(
+ typename StorageType::const_iterator windowIt, bool testStayAbove) const
+{
+ Window *window = *windowIt;
+ if (testStayAbove && shouldBeAboveTransientParent(window))
+ return PositionPreference::StayAboveTransientParent;
+ if (windowIt >= m_alwaysOnTopWindowsBegin)
+ return PositionPreference::StayOnTop;
+ if (windowIt >= m_regularWindowsBegin)
+ return PositionPreference::Regular;
+ return PositionPreference::StayOnBottom;
+}
+
+#endif /* QWASMWINDOWSTACK_INC */
diff --git a/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp b/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp
index 08eb7e327b1..6f068b82a39 100644
--- a/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp
@@ -3,149 +3,4 @@
#include "qwasmwindowtreenode.h"
-#include "qwasmwindow.h"
-#include "qwasmscreen.h"
-
-uint64_t QWasmWindowTreeNode::s_nextActiveIndex = 0;
-
-QWasmWindowTreeNode::QWasmWindowTreeNode()
- : m_childStack(std::bind(&QWasmWindowTreeNode::onTopWindowChanged, this))
-{
-}
-
-QWasmWindowTreeNode::~QWasmWindowTreeNode() = default;
-
-void QWasmWindowTreeNode::shutdown()
-{
- QWasmWindow *window = asWasmWindow();
- if (!window ||
- !window->window() ||
- (QGuiApplication::focusWindow() && // Don't act if we have a focus window different from this
- QGuiApplication::focusWindow() != window->window()))
- return;
-
- // Make a list of all windows sorted on active index.
- // Skip windows with active index 0 as they have
- // never been active.
- std::map<uint64_t, QWasmWindow *> allWindows;
- for (const auto &w : window->platformScreen()->allWindows()) {
- if (w->getActiveIndex() > 0)
- allWindows.insert({w->getActiveIndex(), w});
- }
-
- // window is not in all windows
- if (window->getActiveIndex() > 0)
- allWindows.insert({window->getActiveIndex(), window});
-
- if (allWindows.size() >= 2) {
- const auto lastIt = std::prev(allWindows.end());
- const auto prevIt = std::prev(lastIt);
- const auto lastW = lastIt->second;
- const auto prevW = prevIt->second;
-
- if (lastW == window) // Only act if window is last to be active
- prevW->requestActivateWindow();
- }
-}
-
-void QWasmWindowTreeNode::onParentChanged(QWasmWindowTreeNode *previousParent,
- QWasmWindowTreeNode *currentParent,
- QWasmWindowStack::PositionPreference positionPreference)
-{
- auto *window = asWasmWindow();
- if (previousParent) {
- previousParent->m_childStack.removeWindow(window);
- previousParent->onSubtreeChanged(QWasmWindowTreeNodeChangeType::NodeRemoval, previousParent,
- window);
- }
-
- if (currentParent) {
- currentParent->m_childStack.pushWindow(window, positionPreference);
- currentParent->onSubtreeChanged(QWasmWindowTreeNodeChangeType::NodeInsertion, currentParent,
- window);
- }
-}
-
-QWasmWindow *QWasmWindowTreeNode::asWasmWindow()
-{
- return nullptr;
-}
-
-void QWasmWindowTreeNode::onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType,
- QWasmWindowTreeNode *parent, QWasmWindow *child)
-{
- if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this
- && m_childStack.topWindow()
- && m_childStack.topWindow()->window()) {
-
- const auto flags = m_childStack.topWindow()->window()->flags();
- const bool notToolOrPopup = ((flags & Qt::ToolTip) != Qt::ToolTip) && ((flags & Qt::Popup) != Qt::Popup);
- const QVariant showWithoutActivating = m_childStack.topWindow()->window()->property("_q_showWithoutActivating");
- if (!showWithoutActivating.isValid() || !showWithoutActivating.toBool()) {
- if (notToolOrPopup)
- m_childStack.topWindow()->requestActivateWindow();
- }
- }
-
- if (parentNode())
- parentNode()->onSubtreeChanged(changeType, parent, child);
-}
-
-void QWasmWindowTreeNode::setWindowZOrder(QWasmWindow *window, int z)
-{
- window->setZOrder(z);
-}
-
-void QWasmWindowTreeNode::onPositionPreferenceChanged(
- QWasmWindowStack::PositionPreference positionPreference)
-{
- if (parentNode()) {
- parentNode()->m_childStack.windowPositionPreferenceChanged(asWasmWindow(),
- positionPreference);
- }
-}
-
-void QWasmWindowTreeNode::setAsActiveNode()
-{
- if (parentNode())
- parentNode()->setActiveChildNode(asWasmWindow());
-
- // At the end, this is a recursive function
- m_activeIndex = ++s_nextActiveIndex;
-}
-
-void QWasmWindowTreeNode::bringToTop()
-{
- if (!parentNode())
- return;
- parentNode()->m_childStack.raise(asWasmWindow());
- parentNode()->bringToTop();
-}
-
-void QWasmWindowTreeNode::sendToBottom()
-{
- if (!parentNode())
- return;
- m_childStack.lower(asWasmWindow());
-}
-
-void QWasmWindowTreeNode::onTopWindowChanged()
-{
- constexpr int zOrderForElementInFrontOfScreen = 3;
- int z = zOrderForElementInFrontOfScreen;
- std::for_each(m_childStack.rbegin(), m_childStack.rend(),
- [this, &z](QWasmWindow *window) { setWindowZOrder(window, z++); });
-}
-
-void QWasmWindowTreeNode::setActiveChildNode(QWasmWindow *activeChild)
-{
- m_activeChild = activeChild;
-
- auto it = m_childStack.begin();
- if (it == m_childStack.end())
- return;
- for (; it != m_childStack.end(); ++it)
- (*it)->onActivationChanged(*it == m_activeChild);
-
- setAsActiveNode();
-}
+uint64_t QWasmWindowTreeNodeBase::s_nextActiveIndex = 0;
diff --git a/src/plugins/platforms/wasm/qwasmwindowtreenode.h b/src/plugins/platforms/wasm/qwasmwindowtreenode.h
index 170d777f02a..c955cd21740 100644
--- a/src/plugins/platforms/wasm/qwasmwindowtreenode.h
+++ b/src/plugins/platforms/wasm/qwasmwindowtreenode.h
@@ -6,6 +6,8 @@
#include "qwasmwindowstack.h"
+#include <QVariant>
+
namespace emscripten {
class val;
}
@@ -17,7 +19,14 @@ enum class QWasmWindowTreeNodeChangeType {
NodeRemoval,
};
-class QWasmWindowTreeNode
+class QWasmWindowTreeNodeBase
+{
+protected:
+ static uint64_t s_nextActiveIndex;
+};
+
+template<class Window = QWasmWindow>
+class QWasmWindowTreeNode : public QWasmWindowTreeNodeBase
{
public:
QWasmWindowTreeNode();
@@ -28,20 +37,20 @@ public:
protected:
virtual void onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current,
- QWasmWindowStack::PositionPreference positionPreference);
- virtual QWasmWindow *asWasmWindow();
+ typename QWasmWindowStack<Window>::PositionPreference positionPreference);
+ virtual Window *asWasmWindow();
virtual void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType,
- QWasmWindowTreeNode *parent, QWasmWindow *child);
- virtual void setWindowZOrder(QWasmWindow *window, int z);
+ QWasmWindowTreeNode *parent, Window *child);
+ virtual void setWindowZOrder(Window *window, int z);
- void onPositionPreferenceChanged(QWasmWindowStack::PositionPreference positionPreference);
+ void onPositionPreferenceChanged(typename QWasmWindowStack<Window>::PositionPreference positionPreference);
void setAsActiveNode();
void bringToTop();
void sendToBottom();
- void shutdown();
- const QWasmWindowStack &childStack() const { return m_childStack; }
- QWasmWindow *activeChild() const { return m_activeChild; }
+ const QWasmWindowStack<Window> &childStack() const { return m_childStack; }
+ QWasmWindowStack<Window> &childStack() { return m_childStack; }
+ Window *activeChild() const { return m_activeChild; }
uint64_t getActiveIndex() const {
return m_activeIndex;
@@ -49,13 +58,13 @@ protected:
private:
void onTopWindowChanged();
- void setActiveChildNode(QWasmWindow *activeChild);
+ void setActiveChildNode(Window *activeChild);
uint64_t m_activeIndex = 0;
- static uint64_t s_nextActiveIndex;
- QWasmWindowStack m_childStack;
- QWasmWindow *m_activeChild = nullptr;
+ QWasmWindowStack<Window> m_childStack;
+ Window *m_activeChild = nullptr;
};
+#endif
-#endif // QWASMWINDOWTREENODE_H
+#include "qwasmwindowtreenode.inc"
diff --git a/src/plugins/platforms/wasm/qwasmwindowtreenode.inc b/src/plugins/platforms/wasm/qwasmwindowtreenode.inc
new file mode 100644
index 00000000000..c5a228d7fab
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindowtreenode.inc
@@ -0,0 +1,128 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QWASMWINDOWTREENODE_INC
+#define QWASMWINDOWTREENODE_INC
+
+template<class Window>
+QWasmWindowTreeNode<Window>::QWasmWindowTreeNode()
+ : m_childStack(std::bind(&QWasmWindowTreeNode<Window>::onTopWindowChanged, this))
+{
+}
+
+template<class Window>
+QWasmWindowTreeNode<Window>::~QWasmWindowTreeNode() = default;
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::onParentChanged(QWasmWindowTreeNode *previousParent,
+ QWasmWindowTreeNode *currentParent,
+ typename QWasmWindowStack<Window>::PositionPreference positionPreference)
+{
+ auto *window = asWasmWindow();
+ if (previousParent) {
+ previousParent->m_childStack.removeWindow(window);
+ previousParent->onSubtreeChanged(QWasmWindowTreeNodeChangeType::NodeRemoval, previousParent,
+ window);
+ }
+
+ if (currentParent) {
+ currentParent->m_childStack.pushWindow(window, positionPreference);
+ currentParent->onSubtreeChanged(QWasmWindowTreeNodeChangeType::NodeInsertion, currentParent,
+ window);
+ }
+}
+
+template<class Window>
+Window *QWasmWindowTreeNode<Window>::asWasmWindow()
+{
+ return nullptr;
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType,
+ QWasmWindowTreeNode *parent, Window *child)
+{
+ if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this
+ && m_childStack.topWindow()
+ && m_childStack.topWindow()->window()) {
+
+ const auto flags = m_childStack.topWindow()->window()->flags();
+ const bool notToolOrPopup = ((flags & Qt::ToolTip) != Qt::ToolTip) && ((flags & Qt::Popup) != Qt::Popup);
+ const QVariant showWithoutActivating = m_childStack.topWindow()->window()->property("_q_showWithoutActivating");
+ if (!showWithoutActivating.isValid() || !showWithoutActivating.toBool()) {
+ if (notToolOrPopup)
+ m_childStack.topWindow()->requestActivateWindow();
+ }
+ }
+
+ if (parentNode())
+ parentNode()->onSubtreeChanged(changeType, parent, child);
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::setWindowZOrder(Window *window, int z)
+{
+ window->setZOrder(z);
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::onPositionPreferenceChanged(
+ typename QWasmWindowStack<Window>::PositionPreference positionPreference)
+{
+ if (parentNode()) {
+ parentNode()->m_childStack.windowPositionPreferenceChanged(asWasmWindow(),
+ positionPreference);
+ }
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::setAsActiveNode()
+{
+ if (parentNode())
+ parentNode()->setActiveChildNode(asWasmWindow());
+
+ // At the end, this is a recursive function
+ m_activeIndex = ++s_nextActiveIndex;
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::bringToTop()
+{
+ if (!parentNode())
+ return;
+ parentNode()->m_childStack.raise(asWasmWindow());
+ parentNode()->bringToTop();
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::sendToBottom()
+{
+ if (!parentNode())
+ return;
+ m_childStack.lower(asWasmWindow());
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::onTopWindowChanged()
+{
+ constexpr int zOrderForElementInFrontOfScreen = 3;
+ int z = zOrderForElementInFrontOfScreen;
+ std::for_each(m_childStack.rbegin(), m_childStack.rend(),
+ [this, &z](Window *window) { setWindowZOrder(window, z++); });
+}
+
+template<class Window>
+void QWasmWindowTreeNode<Window>::setActiveChildNode(Window *activeChild)
+{
+ m_activeChild = activeChild;
+
+ auto it = m_childStack.begin();
+ if (it == m_childStack.end())
+ return;
+ for (; it != m_childStack.end(); ++it)
+ (*it)->onActivationChanged(*it == m_activeChild);
+
+ setAsActiveNode();
+}
+
+#endif /* QWASMWINDOWTREENODE_INC */
diff --git a/tests/auto/wasm/qwasmwindowstack/tst_qwasmwindowstack.cpp b/tests/auto/wasm/qwasmwindowstack/tst_qwasmwindowstack.cpp
index fe169b52dca..3df72ba6379 100644
--- a/tests/auto/wasm/qwasmwindowstack/tst_qwasmwindowstack.cpp
+++ b/tests/auto/wasm/qwasmwindowstack/tst_qwasmwindowstack.cpp
@@ -2,15 +2,75 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../../../../src/plugins/platforms/wasm/qwasmwindowstack.h"
+
#include <QtGui/QWindow>
#include <QTest>
#include <emscripten/val.h>
-class QWasmWindow
+class TestWindow
{
+public:
+ TestWindow *transientParent() const { return m_transientParent; }
+ Qt::WindowFlags windowFlags() const { return m_windowFlags; }
+ bool isModal() const { return m_isModal; }
+
+public:
+ TestWindow *m_transientParent = nullptr;
+ Qt::WindowFlags m_windowFlags = {};
+ bool m_isModal = false;
};
+#define QWasmWindowStack QWasmWindowStack<TestWindow>
+#define QWasmWindow TestWindow
+
namespace {
+
+QDebug operator<<(QDebug d, const QWasmWindowStack::PositionPreference &pref)
+{
+ switch (pref) {
+ case QWasmWindowStack::PositionPreference::StayOnBottom:
+ d << "StayOnBottom";
+ break;
+ case QWasmWindowStack::PositionPreference::Regular:
+ d << "Regular";
+ break;
+ case QWasmWindowStack::PositionPreference::StayOnTop:
+ d << "StayOnTop";
+ break;
+ case QWasmWindowStack::PositionPreference::StayAboveTransientParent:
+ d << "StayAboveParent";
+ break;
+ } /* end-switch */
+ return d;
+}
+
+class LogWindows
+{
+public:
+ LogWindows(QWasmWindow *window, const QWasmWindowStack &stack)
+ : m_window(window), m_stack(&stack)
+ {
+ }
+
+public:
+ friend QDebug operator<<(QDebug d, const LogWindows &cl)
+ {
+ LogWindows &l = const_cast<LogWindows &>(cl);
+ d << "\n";
+ for (auto it = l.m_stack->rend(); it != l.m_stack->rbegin();) {
+ --it;
+ d << " Window " << (*it) - l.m_window
+ << l.m_stack->getWindowPositionPreference(it, false)
+ << l.m_stack->getWindowPositionPreference(it, true) << "\n";
+ }
+ return d;
+ }
+
+private:
+ QWasmWindow *m_window;
+ const QWasmWindowStack *m_stack;
+};
+
std::vector<QWasmWindow *> getWindowsFrontToBack(const QWasmWindowStack *stack)
{
return std::vector<QWasmWindow *>(stack->begin(), stack->end());
@@ -42,6 +102,11 @@ private slots:
void removingWithAlwaysOnTop();
void positionPreferenceChanges();
void clearing();
+ void stayAboveParentOnBottom1();
+ void stayAboveParentOnBottom2();
+ void stayAboveParentOnBottom3();
+ void stayAboveParentRegular();
+ void stayAboveParentOnTop();
private:
void onTopWindowChanged()
@@ -97,7 +162,6 @@ void tst_QWasmWindowStack::insertion()
void tst_QWasmWindowStack::raising()
{
QWasmWindowStack stack(m_mockCallback);
-
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
@@ -106,7 +170,6 @@ void tst_QWasmWindowStack::raising()
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
clearCallbackCounter();
-
QCOMPARE(&m_window5, stack.topWindow());
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window1); };
@@ -710,5 +773,254 @@ void tst_QWasmWindowStack::clearing()
QCOMPARE(0u, stack.size());
}
+void tst_QWasmWindowStack::stayAboveParentOnBottom1()
+{
+ QWasmWindow windows[5];
+ windows[4].m_transientParent = &windows[0];
+ windows[4].m_windowFlags = Qt::Tool;
+
+ QWasmWindowStack stack(m_mockCallback);
+
+ stack.pushWindow(windows + 0, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 1, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 2, QWasmWindowStack::PositionPreference::Regular);
+ stack.pushWindow(windows + 3, QWasmWindowStack::PositionPreference::StayOnTop);
+ stack.pushWindow(windows + 4, QWasmWindowStack::PositionPreference::StayAboveTransientParent);
+
+ {
+ const std::vector expectedWindowOrder = {
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 4,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+ {
+ // Check that window is moved to correct group:
+ // it was: StayAboveParent, in group StayOnBottom
+ // it is: StayOnTop
+ stack.windowPositionPreferenceChanged(
+ windows + 4,
+ QWasmWindowStack::PositionPreference::StayOnTop);
+
+ const std::vector expectedWindowOrder = {
+ windows + 4,
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+}
+
+void tst_QWasmWindowStack::stayAboveParentOnBottom2()
+{
+ QWasmWindow windows[5];
+ windows[4].m_transientParent = &windows[0];
+ windows[4].m_windowFlags = Qt::Tool;
+
+ QWasmWindowStack stack(m_mockCallback);
+
+ stack.pushWindow(windows + 0, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 1, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 2, QWasmWindowStack::PositionPreference::Regular);
+ stack.pushWindow(windows + 3, QWasmWindowStack::PositionPreference::StayOnTop);
+ stack.pushWindow(windows + 4, QWasmWindowStack::PositionPreference::StayAboveTransientParent);
+
+ {
+ const std::vector expectedWindowOrder = {
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 4,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+ {
+ // Check that order does not change:
+ // it was: StayAboveParent, in group StayOnBottom
+ // it is: StayOnBottom
+ stack.windowPositionPreferenceChanged(
+ windows + 4,
+ QWasmWindowStack::PositionPreference::StayOnBottom);
+
+ const std::vector expectedWindowOrder = {
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 4,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+}
+
+void tst_QWasmWindowStack::stayAboveParentOnBottom3()
+{
+ QWasmWindow windows[5];
+ windows[4].m_transientParent = &windows[0];
+ windows[4].m_windowFlags = Qt::Tool;
+
+ QWasmWindowStack stack(m_mockCallback);
+
+ stack.pushWindow(windows + 0, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 1, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 2, QWasmWindowStack::PositionPreference::Regular);
+ stack.pushWindow(windows + 3, QWasmWindowStack::PositionPreference::StayOnTop);
+ stack.pushWindow(windows + 4, QWasmWindowStack::PositionPreference::StayAboveTransientParent);
+
+ {
+ const std::vector expectedWindowOrder = {
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 4,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+ {
+ // Check that windows is moved to correct group
+ // it was: StayAboveParent, in group StayOnBottom
+ // it is: Regular
+ stack.windowPositionPreferenceChanged(
+ windows + 4,
+ QWasmWindowStack::PositionPreference::Regular);
+
+ const std::vector expectedWindowOrder = {
+ windows + 3,
+ windows + 4,
+ windows + 2,
+ windows + 1,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+}
+
+void tst_QWasmWindowStack::stayAboveParentRegular()
+{
+ QWasmWindow windows[5];
+ windows[4].m_transientParent = &windows[1];
+ windows[4].m_windowFlags = Qt::Tool;
+
+ QWasmWindowStack stack(m_mockCallback);
+
+ stack.pushWindow(windows + 0, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 1, QWasmWindowStack::PositionPreference::Regular);
+ stack.pushWindow(windows + 2, QWasmWindowStack::PositionPreference::Regular);
+ stack.pushWindow(windows + 3, QWasmWindowStack::PositionPreference::StayOnTop);
+ stack.pushWindow(windows + 4, QWasmWindowStack::PositionPreference::StayAboveTransientParent);
+
+ {
+ const std::vector expectedWindowOrder = {
+ windows + 3,
+ windows + 2,
+ windows + 4,
+ windows + 1,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+ {
+ stack.windowPositionPreferenceChanged(
+ windows + 4,
+ QWasmWindowStack::PositionPreference::StayOnTop);
+
+ const std::vector expectedWindowOrder = {
+ windows + 4,
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+}
+
+void tst_QWasmWindowStack::stayAboveParentOnTop()
+{
+ QWasmWindow windows[5];
+ windows[3].m_transientParent = &windows[2];
+ windows[3].m_windowFlags = Qt::Tool;
+
+ QWasmWindowStack stack(m_mockCallback);
+
+ stack.pushWindow(windows + 0, QWasmWindowStack::PositionPreference::StayOnBottom);
+ stack.pushWindow(windows + 1, QWasmWindowStack::PositionPreference::Regular);
+ stack.pushWindow(windows + 2, QWasmWindowStack::PositionPreference::StayOnTop);
+ stack.pushWindow(windows + 3, QWasmWindowStack::PositionPreference::StayAboveTransientParent);
+ stack.pushWindow(windows + 4, QWasmWindowStack::PositionPreference::StayOnTop);
+
+ {
+ const std::vector expectedWindowOrder = {
+ windows + 4,
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+ {
+ stack.windowPositionPreferenceChanged(
+ windows + 3,
+ QWasmWindowStack::PositionPreference::StayOnTop);
+
+ const std::vector expectedWindowOrder = {
+ windows + 4,
+ windows + 3,
+ windows + 2,
+ windows + 1,
+ windows + 0
+ };
+
+ qDebug() << LogWindows(windows + 0, stack);
+
+ QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
+ getWindowsFrontToBack(&stack).begin()));
+ }
+}
+
+
QTEST_MAIN(tst_QWasmWindowStack)
#include "tst_qwasmwindowstack.moc"
diff --git a/tests/auto/wasm/qwasmwindowtreenode/tst_qwasmwindowtreenode.cpp b/tests/auto/wasm/qwasmwindowtreenode/tst_qwasmwindowtreenode.cpp
index 763dbf9a073..62d8114befb 100644
--- a/tests/auto/wasm/qwasmwindowtreenode/tst_qwasmwindowtreenode.cpp
+++ b/tests/auto/wasm/qwasmwindowtreenode/tst_qwasmwindowtreenode.cpp
@@ -6,28 +6,33 @@
#include <QTest>
#include <emscripten/val.h>
-class QWasmWindow
+class TestQWindow
{
+public:
+ int flags() { return 0; }
+ QVariant property(const char *) { return QVariant(); }
};
+class TestWindowTreeNode;
+
using OnSubtreeChangedCallback = std::function<void(
- QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent, QWasmWindow *child)>;
-using SetWindowZOrderCallback = std::function<void(QWasmWindow *window, int z)>;
+ QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode<TestWindowTreeNode> *parent, TestWindowTreeNode *child)>;
+using SetWindowZOrderCallback = std::function<void(TestWindowTreeNode *window, int z)>;
struct OnSubtreeChangedCallData
{
QWasmWindowTreeNodeChangeType changeType;
- QWasmWindowTreeNode *parent;
- QWasmWindow *child;
+ QWasmWindowTreeNode<TestWindowTreeNode> *parent;
+ TestWindowTreeNode *child;
};
struct SetWindowZOrderCallData
{
- QWasmWindow *window;
+ TestWindowTreeNode *window;
int z;
};
-class TestWindowTreeNode final : public QWasmWindowTreeNode, public QWasmWindow
+class TestWindowTreeNode final : public QWasmWindowTreeNode<TestWindowTreeNode>
{
public:
TestWindowTreeNode(OnSubtreeChangedCallback onSubtreeChangedCallback,
@@ -42,7 +47,7 @@ public:
{
auto *previous = m_parent;
m_parent = parent;
- onParentChanged(previous, parent, QWasmWindowStack::PositionPreference::Regular);
+ onParentChanged(previous, parent, QWasmWindowStack<TestWindowTreeNode>::PositionPreference::Regular);
}
void setContainerElement(emscripten::val container) { m_containerElement = container; }
@@ -51,30 +56,42 @@ public:
void sendToBottom() { QWasmWindowTreeNode::sendToBottom(); }
- const QWasmWindowStack &childStack() { return QWasmWindowTreeNode::childStack(); }
+ const QWasmWindowStack<TestWindowTreeNode> &childStack() { return QWasmWindowTreeNode::childStack(); }
emscripten::val containerElement() final { return m_containerElement; }
QWasmWindowTreeNode *parentNode() final { return m_parent; }
- QWasmWindow *asWasmWindow() final { return this; }
+ TestWindowTreeNode *asWasmWindow() final { return this; }
+ TestWindowTreeNode *transientParent() const {
+ return nullptr;
+ }
+ TestQWindow *window() { return &m_qWindow; }
+ void requestActivateWindow() { ; }
+ void setZOrder(int) { ; }
+ bool isModal() const { return false; }
+ Qt::WindowFlags windowFlags() const { return Qt::WindowFlags(); }
protected:
- void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent,
- QWasmWindow *child) final
+ void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode<TestWindowTreeNode> *parent,
+ TestWindowTreeNode *child) final
{
m_onSubtreeChangedCallback(changeType, parent, child);
}
- void setWindowZOrder(QWasmWindow *window, int z) final { m_setWindowZOrderCallback(window, z); }
+ void setWindowZOrder(TestWindowTreeNode *window, int z) final { m_setWindowZOrderCallback(window, z); }
TestWindowTreeNode *m_parent = nullptr;
emscripten::val m_containerElement = emscripten::val::undefined();
OnSubtreeChangedCallback m_onSubtreeChangedCallback;
SetWindowZOrderCallback m_setWindowZOrderCallback;
+ TestQWindow m_qWindow;
};
+#define QWasmWindowTreeNode QWasmWindowTreeNode<TestWindowTreeNode>
+#define QWasmWindow TestWindowTreeNode
+
class tst_QWasmWindowTreeNode : public QObject
{
Q_OBJECT