diff options
author | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-06-14 14:54:34 +0200 |
---|---|---|
committer | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-06-15 09:41:06 +0200 |
commit | fc4fca6d9dc22839ca73898c362faff96c81214c (patch) | |
tree | a30fcc5804cf28f81631f970d54e2c6207549f9a /tests/auto | |
parent | eb92d52dc7190efefae0fae89c7c6eb9e16cdd9d (diff) |
Support child windows on WASM
Setting parents for WASM platform windows is now supported. This means
that windows now reside in a hierarchical window tree, with the screen
and individual windows being nodes (QWasmWindowTreeNode), each
maintaining their own child window stack.
The divs backing windows are properly reparented in response to Qt
window parent changes, so that the html structure reflects what is
happening in Qt.
Change-Id: I55c91d90caf58714342dcd747043967ebfdf96bb
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Diffstat (limited to 'tests/auto')
-rw-r--r-- | tests/auto/wasm/CMakeLists.txt | 13 | ||||
-rw-r--r-- | tests/auto/wasm/tst_qwasmwindowtreenode.cpp | 257 |
2 files changed, 270 insertions, 0 deletions
diff --git a/tests/auto/wasm/CMakeLists.txt b/tests/auto/wasm/CMakeLists.txt index 47031037e0..0d67ae8e79 100644 --- a/tests/auto/wasm/CMakeLists.txt +++ b/tests/auto/wasm/CMakeLists.txt @@ -30,6 +30,19 @@ qt_internal_add_test(tst_qwasmwindowstack Qt::Widgets ) +qt_internal_add_test(tst_qwasmwindowtreenode + SOURCES + tst_qwasmwindowtreenode.cpp + DEFINES + QT_NO_FOREACH + LIBRARIES + Qt::GuiPrivate + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + Qt::Widgets +) + qt_internal_add_test(tst_qwasmkeytranslator SOURCES tst_qwasmkeytranslator.cpp diff --git a/tests/auto/wasm/tst_qwasmwindowtreenode.cpp b/tests/auto/wasm/tst_qwasmwindowtreenode.cpp new file mode 100644 index 0000000000..2fd2ab2e66 --- /dev/null +++ b/tests/auto/wasm/tst_qwasmwindowtreenode.cpp @@ -0,0 +1,257 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "../../../src/plugins/platforms/wasm/qwasmwindowtreenode.h" +#include <QtGui/QWindow> +#include <QTest> +#include <emscripten/val.h> + +class QWasmWindow +{ +}; + +using OnSubtreeChangedCallback = std::function<void( + QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent, QWasmWindow *child)>; +using SetWindowZOrderCallback = std::function<void(QWasmWindow *window, int z)>; + +struct OnSubtreeChangedCallData +{ + QWasmWindowTreeNodeChangeType changeType; + QWasmWindowTreeNode *parent; + QWasmWindow *child; +}; + +struct SetWindowZOrderCallData +{ + QWasmWindow *window; + int z; +}; + +class TestWindowTreeNode final : public QWasmWindowTreeNode, public QWasmWindow +{ +public: + TestWindowTreeNode(OnSubtreeChangedCallback onSubtreeChangedCallback, + SetWindowZOrderCallback setWindowZOrderCallback) + : m_onSubtreeChangedCallback(std::move(onSubtreeChangedCallback)), + m_setWindowZOrderCallback(std::move(setWindowZOrderCallback)) + { + } + ~TestWindowTreeNode() final { } + + void setParent(TestWindowTreeNode *parent) + { + auto *previous = m_parent; + m_parent = parent; + onParentChanged(previous, parent, QWasmWindowStack::PositionPreference::Regular); + } + + void setContainerElement(emscripten::val container) { m_containerElement = container; } + + void bringToTop() { QWasmWindowTreeNode::bringToTop(); } + + void sendToBottom() { QWasmWindowTreeNode::sendToBottom(); } + + const QWasmWindowStack &childStack() { return QWasmWindowTreeNode::childStack(); } + + emscripten::val containerElement() final { return m_containerElement; } + + QWasmWindowTreeNode *parentNode() final { return m_parent; } + + QWasmWindow *asWasmWindow() final { return this; } + +protected: + void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent, + QWasmWindow *child) final + { + m_onSubtreeChangedCallback(changeType, parent, child); + } + + void setWindowZOrder(QWasmWindow *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; +}; + +class tst_QWasmWindowTreeNode : public QObject +{ + Q_OBJECT + +public: + tst_QWasmWindowTreeNode() { } + +private slots: + void init(); + + void nestedWindowStacks(); + void settingChildWindowZOrder(); +}; + +void tst_QWasmWindowTreeNode::init() { } + +bool operator==(const OnSubtreeChangedCallData &lhs, const OnSubtreeChangedCallData &rhs) +{ + return lhs.changeType == rhs.changeType && lhs.parent == rhs.parent && lhs.child == rhs.child; +} + +bool operator==(const SetWindowZOrderCallData &lhs, const SetWindowZOrderCallData &rhs) +{ + return lhs.window == rhs.window && lhs.z == rhs.z; +} + +void tst_QWasmWindowTreeNode::nestedWindowStacks() +{ + QList<OnSubtreeChangedCallData> calls; + OnSubtreeChangedCallback mockOnSubtreeChanged = + [&calls](QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent, + QWasmWindow *child) { + calls.push_back(OnSubtreeChangedCallData{ changeType, parent, child }); + }; + SetWindowZOrderCallback ignoreSetWindowZOrder = [](QWasmWindow *, int) {}; + TestWindowTreeNode node(mockOnSubtreeChanged, ignoreSetWindowZOrder); + node.bringToTop(); + + OnSubtreeChangedCallback ignoreSubtreeChanged = [](QWasmWindowTreeNodeChangeType, + QWasmWindowTreeNode *, QWasmWindow *) {}; + TestWindowTreeNode node2(ignoreSubtreeChanged, ignoreSetWindowZOrder); + node2.setParent(&node); + + QCOMPARE(node.childStack().size(), 1u); + QCOMPARE(node2.childStack().size(), 0u); + QCOMPARE(node.childStack().topWindow(), &node2); + QCOMPARE(calls.size(), 1u); + { + OnSubtreeChangedCallData expected{ QWasmWindowTreeNodeChangeType::NodeInsertion, &node, + &node2 }; + QCOMPARE(calls[0], expected); + calls.clear(); + } + + TestWindowTreeNode node3(ignoreSubtreeChanged, ignoreSetWindowZOrder); + node3.setParent(&node); + + QCOMPARE(node.childStack().size(), 2u); + QCOMPARE(node2.childStack().size(), 0u); + QCOMPARE(node3.childStack().size(), 0u); + QCOMPARE(node.childStack().topWindow(), &node3); + { + OnSubtreeChangedCallData expected{ QWasmWindowTreeNodeChangeType::NodeInsertion, &node, + &node3 }; + QCOMPARE(calls[0], expected); + calls.clear(); + } + + TestWindowTreeNode node4(ignoreSubtreeChanged, ignoreSetWindowZOrder); + node4.setParent(&node); + + QCOMPARE(node.childStack().size(), 3u); + QCOMPARE(node2.childStack().size(), 0u); + QCOMPARE(node3.childStack().size(), 0u); + QCOMPARE(node4.childStack().size(), 0u); + QCOMPARE(node.childStack().topWindow(), &node4); + { + OnSubtreeChangedCallData expected{ QWasmWindowTreeNodeChangeType::NodeInsertion, &node, + &node4 }; + QCOMPARE(calls[0], expected); + calls.clear(); + } + + node3.bringToTop(); + QCOMPARE(node.childStack().topWindow(), &node3); + + node4.setParent(nullptr); + QCOMPARE(node.childStack().size(), 2u); + QCOMPARE(node.childStack().topWindow(), &node3); + { + OnSubtreeChangedCallData expected{ QWasmWindowTreeNodeChangeType::NodeRemoval, &node, + &node4 }; + QCOMPARE(calls[0], expected); + calls.clear(); + } + + node2.setParent(nullptr); + QCOMPARE(node.childStack().size(), 1u); + QCOMPARE(node.childStack().topWindow(), &node3); + { + OnSubtreeChangedCallData expected{ QWasmWindowTreeNodeChangeType::NodeRemoval, &node, + &node2 }; + QCOMPARE(calls[0], expected); + calls.clear(); + } + + node3.setParent(nullptr); + QVERIFY(node.childStack().empty()); + QCOMPARE(node.childStack().topWindow(), nullptr); + { + OnSubtreeChangedCallData expected{ QWasmWindowTreeNodeChangeType::NodeRemoval, &node, + &node3 }; + QCOMPARE(calls[0], expected); + calls.clear(); + } +} + +void tst_QWasmWindowTreeNode::settingChildWindowZOrder() +{ + QList<SetWindowZOrderCallData> calls; + OnSubtreeChangedCallback ignoreSubtreeChanged = [](QWasmWindowTreeNodeChangeType, + QWasmWindowTreeNode *, QWasmWindow *) {}; + SetWindowZOrderCallback onSetWindowZOrder = [&calls](QWasmWindow *window, int z) { + calls.push_back(SetWindowZOrderCallData{ window, z }); + }; + SetWindowZOrderCallback ignoreSetWindowZOrder = [](QWasmWindow *, int) {}; + TestWindowTreeNode node(ignoreSubtreeChanged, onSetWindowZOrder); + + TestWindowTreeNode node2(ignoreSubtreeChanged, ignoreSetWindowZOrder); + node2.setParent(&node); + + { + QCOMPARE(calls.size(), 1u); + SetWindowZOrderCallData expected{ &node2, 3 }; + QCOMPARE(calls[0], expected); + calls.clear(); + } + + TestWindowTreeNode node3(ignoreSubtreeChanged, ignoreSetWindowZOrder); + node3.setParent(&node); + + { + QCOMPARE(calls.size(), 2u); + SetWindowZOrderCallData expected{ &node2, 3 }; + QCOMPARE(calls[0], expected); + expected = SetWindowZOrderCallData{ &node3, 4 }; + QCOMPARE(calls[1], expected); + calls.clear(); + } + + TestWindowTreeNode node4(ignoreSubtreeChanged, ignoreSetWindowZOrder); + node4.setParent(&node); + + { + QCOMPARE(calls.size(), 3u); + SetWindowZOrderCallData expected{ &node2, 3 }; + QCOMPARE(calls[0], expected); + expected = SetWindowZOrderCallData{ &node3, 4 }; + QCOMPARE(calls[1], expected); + expected = SetWindowZOrderCallData{ &node4, 5 }; + QCOMPARE(calls[2], expected); + calls.clear(); + } + + node2.bringToTop(); + + { + QCOMPARE(calls.size(), 3u); + SetWindowZOrderCallData expected{ &node3, 3 }; + QCOMPARE(calls[0], expected); + expected = SetWindowZOrderCallData{ &node4, 4 }; + QCOMPARE(calls[1], expected); + expected = SetWindowZOrderCallData{ &node2, 5 }; + QCOMPARE(calls[2], expected); + calls.clear(); + } +} + +QTEST_MAIN(tst_QWasmWindowTreeNode) +#include "tst_qwasmwindowtreenode.moc" |