fc4fca6d9d
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>
258 lines
8.4 KiB
C++
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"
|