qt5base-lts/tests/auto/wasm/tst_qwasmwindowtreenode.cpp
Mikolaj Boc fc4fca6d9d 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>
2023-06-15 09:41:06 +02:00

258 lines
8.4 KiB
C++

// 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"