Windows with a transient parent does not reflect the relationship in the stacking order. Essentially AboveTransientParent is missing as a configuration choice. What makes this slightly convoluted is that the window stack does not depend on the window (for testability). We solve this problem by making the stack and treenode templates, and provide test class as arguments when testing. QWasmWindow and QWasmScreen are not templated as before. There is also a new order type StayAboveTransientParent. Which means that we can no longer use order type to get to the group location (Since StayAboveTransientParent can map to either of the three types). The window stack tests have been updated to handle the StayAboveTransientParent type. Finally, we do not do anything with a normal parent child relationship as this should already work correctly. Fixes: QTBUG-131699 Change-Id: Ie08e18f9e0a2339175c4a09da0a831f031df71e1 Reviewed-by: Lorn Potter <lorn.potter@qt.io>
275 lines
9.1 KiB
C++
275 lines
9.1 KiB
C++
// Copyright (C) 2022 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
|
|
|
#include "../../../../src/plugins/platforms/wasm/qwasmwindowtreenode.h"
|
|
#include <QtGui/QWindow>
|
|
#include <QTest>
|
|
#include <emscripten/val.h>
|
|
|
|
class TestQWindow
|
|
{
|
|
public:
|
|
int flags() { return 0; }
|
|
QVariant property(const char *) { return QVariant(); }
|
|
};
|
|
|
|
class TestWindowTreeNode;
|
|
|
|
using OnSubtreeChangedCallback = std::function<void(
|
|
QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode<TestWindowTreeNode> *parent, TestWindowTreeNode *child)>;
|
|
using SetWindowZOrderCallback = std::function<void(TestWindowTreeNode *window, int z)>;
|
|
|
|
struct OnSubtreeChangedCallData
|
|
{
|
|
QWasmWindowTreeNodeChangeType changeType;
|
|
QWasmWindowTreeNode<TestWindowTreeNode> *parent;
|
|
TestWindowTreeNode *child;
|
|
};
|
|
|
|
struct SetWindowZOrderCallData
|
|
{
|
|
TestWindowTreeNode *window;
|
|
int z;
|
|
};
|
|
|
|
class TestWindowTreeNode final : public QWasmWindowTreeNode<TestWindowTreeNode>
|
|
{
|
|
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<TestWindowTreeNode>::PositionPreference::Regular);
|
|
}
|
|
|
|
void setContainerElement(emscripten::val container) { m_containerElement = container; }
|
|
|
|
void bringToTop() { QWasmWindowTreeNode::bringToTop(); }
|
|
|
|
void sendToBottom() { QWasmWindowTreeNode::sendToBottom(); }
|
|
|
|
const QWasmWindowStack<TestWindowTreeNode> &childStack() { return QWasmWindowTreeNode::childStack(); }
|
|
|
|
emscripten::val containerElement() final { return m_containerElement; }
|
|
|
|
QWasmWindowTreeNode *parentNode() final { return m_parent; }
|
|
|
|
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<TestWindowTreeNode> *parent,
|
|
TestWindowTreeNode *child) final
|
|
{
|
|
m_onSubtreeChangedCallback(changeType, parent, child);
|
|
}
|
|
|
|
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
|
|
|
|
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"
|