Support always on top/bottom window flags on WASM

The window stack will now upkeep three groups of windows, always
on bottom (1), regular (2), always on top (3). Windows belonging to
(3) will always appear on top of (2) and (1), and windows from (2) will
always appear on top of (1).

The first window created in the application gets the (1) status, which
is in line with the root window mechanism used before.

Activation has now been decoupled from the top position on the window
stack as a window in (1) or (2) may be active, in spite of the top
window belonging to a higher group.

Fixes: QTBUG-110098
Change-Id: I51f4d2d47163fab26ce5ef28f7a4f23a522c7f91
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
Mikolaj Boc 2023-01-26 18:38:21 +01:00
parent 96e031edd7
commit a596ea0fe2
6 changed files with 702 additions and 144 deletions

View File

@ -22,6 +22,16 @@ QWasmWindow *asWasmWindow(QWindow *window)
{
return static_cast<QWasmWindow*>(window->handle());
}
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
using namespace emscripten;
@ -67,8 +77,11 @@ void QWasmCompositor::destroy()
void QWasmCompositor::addWindow(QWasmWindow *window)
{
m_windowStack.pushWindow(window);
m_windowStack.topWindow()->requestActivateWindow();
if (m_windowStack.empty())
window->window()->setFlag(Qt::WindowStaysOnBottomHint);
m_windowStack.pushWindow(window, positionPreferenceFromWindowFlags(window->window()->flags()));
window->requestActivateWindow();
setActive(window);
updateEnabledState();
}
@ -77,12 +90,27 @@ void QWasmCompositor::removeWindow(QWasmWindow *window)
{
m_requestUpdateWindows.remove(window);
m_windowStack.removeWindow(window);
if (m_windowStack.topWindow())
if (m_windowStack.topWindow()) {
m_windowStack.topWindow()->requestActivateWindow();
setActive(m_windowStack.topWindow());
}
updateEnabledState();
}
void QWasmCompositor::setActive(QWasmWindow *window)
{
m_activeWindow = window;
auto it = m_windowStack.begin();
if (it == m_windowStack.end()) {
return;
}
for (; it != m_windowStack.end(); ++it) {
(*it)->onActivationChanged(*it == m_activeWindow);
}
}
void QWasmCompositor::updateEnabledState()
{
m_isEnabled = std::any_of(m_windowStack.begin(), m_windowStack.end(), [](QWasmWindow *window) {
@ -100,6 +128,11 @@ void QWasmCompositor::lower(QWasmWindow *window)
m_windowStack.lower(window);
}
void QWasmCompositor::windowPositionPreferenceChanged(QWasmWindow *window, Qt::WindowFlags flags)
{
m_windowStack.windowPositionPreferenceChanged(window, positionPreferenceFromWindowFlags(flags));
}
QWindow *QWasmCompositor::windowAt(QPoint targetPointInScreenCoords, int padding) const
{
const auto found = std::find_if(
@ -115,7 +148,7 @@ QWindow *QWasmCompositor::windowAt(QPoint targetPointInScreenCoords, int padding
QWindow *QWasmCompositor::keyWindow() const
{
return m_windowStack.topWindow() ? m_windowStack.topWindow()->window() : nullptr;
return m_activeWindow ? m_activeWindow->window() : nullptr;
}
void QWasmCompositor::requestUpdateAllWindows()
@ -234,16 +267,6 @@ void QWasmCompositor::onTopWindowChanged()
int z = zOrderForElementInFrontOfScreen;
std::for_each(m_windowStack.rbegin(), m_windowStack.rend(),
[&z](QWasmWindow *window) { window->setZOrder(z++); });
auto it = m_windowStack.begin();
if (it == m_windowStack.end()) {
return;
}
(*it)->onActivationChanged(true);
++it;
for (; it != m_windowStack.end(); ++it) {
(*it)->onActivationChanged(false);
}
}
QWasmScreen *QWasmCompositor::screen()

View File

@ -34,8 +34,10 @@ public:
void removeWindow(QWasmWindow *window);
void setVisible(QWasmWindow *window, bool visible);
void setActive(QWasmWindow *window);
void raise(QWasmWindow *window);
void lower(QWasmWindow *window);
void windowPositionPreferenceChanged(QWasmWindow *window, Qt::WindowFlags flags);
void onScreenDeleting();
@ -69,6 +71,7 @@ private:
void updateEnabledState();
QWasmWindowStack m_windowStack;
QWasmWindow *m_activeWindow = nullptr;
bool m_isEnabled = true;
QMap<QWasmWindow *, UpdateRequestDeliveryType> m_requestUpdateWindows;

View File

@ -93,6 +93,7 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
emscripten::val::module_property("specialHTMLTargets").set(canvasSelector(), m_canvas);
m_compositor->addWindow(this);
m_flags = window()->flags();
const auto pointerCallback = std::function([this](emscripten::val event) {
if (processPointer(*PointerEvent::fromWeb(event)))
@ -360,6 +361,8 @@ void QWasmWindow::onActivationChanged(bool active)
void QWasmWindow::setWindowFlags(Qt::WindowFlags flags)
{
if (flags.testFlag(Qt::WindowStaysOnTopHint) != m_flags.testFlag(Qt::WindowStaysOnTopHint))
m_compositor->windowPositionPreferenceChanged(this, flags);
m_flags = flags;
dom::syncCSSClassWith(m_qtWindow, "has-frame", hasFrame());
dom::syncCSSClassWith(m_qtWindow, "has-shadow", !flags.testFlag(Qt::NoDropShadowWindowHint));
@ -573,8 +576,10 @@ void QWasmWindow::requestActivateWindow()
return;
}
if (window()->isTopLevel())
if (window()->isTopLevel()) {
raise();
m_compositor->setActive(this);
}
if (!QWasmIntegration::get()->inputContext())
m_canvas.call<void>("focus");

View File

@ -5,20 +5,38 @@
QT_BEGIN_NAMESPACE
QWasmWindowStack::QWasmWindowStack(TopWindowChangedCallbackType topWindowChangedCallback)
: m_topWindowChangedCallback(std::move(topWindowChangedCallback))
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)
void QWasmWindowStack::pushWindow(QWasmWindow *window, PositionPreference position)
{
Q_ASSERT(m_windowStack.count(window) == 0);
m_windowStack.push_back(window);
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_topWindowChangedCallback();
m_windowOrderChangedCallback();
}
void QWasmWindowStack::removeWindow(QWasmWindow *window)
@ -26,41 +44,105 @@ void QWasmWindowStack::removeWindow(QWasmWindow *window)
Q_ASSERT(m_windowStack.count(window) == 1);
auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
const bool removingBottom = m_windowStack.begin() == it;
const bool removingTop = m_windowStack.end() - 1 == it;
if (removingBottom)
m_firstWindowTreatment = FirstWindowTreatment::Regular;
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);
if (removingTop)
m_topWindowChangedCallback();
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 == rootWindow() || window == topWindow())
if (window == topWindow())
return;
auto it = std::find(regularWindowsBegin(), m_windowStack.end(), window);
std::rotate(it, it + 1, m_windowStack.end());
m_topWindowChangedCallback();
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 == rootWindow())
if (window == *m_windowStack.begin())
return;
const bool loweringTopWindow = topWindow() == window;
auto it = std::find(regularWindowsBegin(), m_windowStack.end(), window);
std::rotate(regularWindowsBegin(), it, it + 1);
if (loweringTopWindow && topWindow() != window)
m_topWindowChangedCallback();
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()
@ -103,21 +185,19 @@ size_t QWasmWindowStack::size() const
return m_windowStack.size();
}
QWasmWindow *QWasmWindowStack::rootWindow() const
{
return m_firstWindowTreatment == FirstWindowTreatment::AlwaysAtBottom ? m_windowStack.first()
: nullptr;
}
QWasmWindow *QWasmWindowStack::topWindow() const
{
return m_windowStack.empty() ? nullptr : m_windowStack.last();
}
QWasmWindowStack::StorageType::iterator QWasmWindowStack::regularWindowsBegin()
QWasmWindowStack::PositionPreference
QWasmWindowStack::getWindowPositionPreference(StorageType::iterator windowIt) const
{
return m_windowStack.begin()
+ (m_firstWindowTreatment == FirstWindowTreatment::AlwaysAtBottom ? 1 : 0);
if (windowIt >= m_alwaysOnTopWindowsBegin)
return PositionPreference::StayOnTop;
if (windowIt >= m_regularWindowsBegin)
return PositionPreference::Regular;
return PositionPreference::StayOnBottom;
}
QT_END_NAMESPACE

View File

@ -24,7 +24,7 @@ class QWasmWindow;
class Q_AUTOTEST_EXPORT QWasmWindowStack
{
public:
using TopWindowChangedCallbackType = std::function<void()>;
using WindowOrderChangedCallbackType = std::function<void()>;
using StorageType = QList<QWasmWindow *>;
@ -32,13 +32,20 @@ public:
using const_iterator = StorageType::const_reverse_iterator;
using const_reverse_iterator = StorageType::const_iterator;
explicit QWasmWindowStack(TopWindowChangedCallbackType topWindowChangedCallback);
enum class PositionPreference {
StayOnBottom,
Regular,
StayOnTop,
};
explicit QWasmWindowStack(WindowOrderChangedCallbackType topWindowChangedCallback);
~QWasmWindowStack();
void pushWindow(QWasmWindow *window);
void pushWindow(QWasmWindow *window, PositionPreference position);
void removeWindow(QWasmWindow *window);
void raise(QWasmWindow *window);
void lower(QWasmWindow *window);
void windowPositionPreferenceChanged(QWasmWindow *window, PositionPreference position);
// Iterates top-to-bottom
iterator begin();
@ -55,14 +62,12 @@ public:
QWasmWindow *topWindow() const;
private:
enum class FirstWindowTreatment { AlwaysAtBottom, Regular };
PositionPreference getWindowPositionPreference(StorageType::iterator windowIt) const;
QWasmWindow *rootWindow() const;
StorageType::iterator regularWindowsBegin();
TopWindowChangedCallbackType m_topWindowChangedCallback;
WindowOrderChangedCallbackType m_windowOrderChangedCallback;
QList<QWasmWindow *> m_windowStack;
FirstWindowTreatment m_firstWindowTreatment = FirstWindowTreatment::AlwaysAtBottom;
StorageType::iterator m_regularWindowsBegin;
StorageType::iterator m_alwaysOnTopWindowsBegin;
};
QT_END_NAMESPACE

View File

@ -31,11 +31,16 @@ private slots:
void init();
void insertion();
void raisingTheRootIsImpossible();
void raising();
void raisingWithAlwaysOnBottom();
void raisingWithAlwaysOnTop();
void lowering();
void loweringWithAlwaysOnBottom();
void loweringWithAlwaysOnTop();
void removing();
void removingTheRoot();
void removingWithAlwaysOnBottom();
void removingWithAlwaysOnTop();
void positionPreferenceChanges();
void clearing();
private:
@ -46,7 +51,7 @@ private:
m_onTopLevelChangedAction();
}
void verifyTopWindowChangedCalled(int expected = 1)
void verifyWindowOrderChanged(int expected = 1)
{
QCOMPARE(expected, m_topLevelChangedCallCount);
clearCallbackCounter();
@ -54,8 +59,8 @@ private:
void clearCallbackCounter() { m_topLevelChangedCallCount = 0; }
QWasmWindowStack::TopWindowChangedCallbackType m_mockCallback;
QWasmWindowStack::TopWindowChangedCallbackType m_onTopLevelChangedAction;
QWasmWindowStack::WindowOrderChangedCallbackType m_mockCallback;
QWasmWindowStack::WindowOrderChangedCallbackType m_onTopLevelChangedAction;
int m_topLevelChangedCallCount = 0;
QWasmWindow m_root;
@ -68,7 +73,7 @@ private:
void tst_QWasmWindowStack::init()
{
m_onTopLevelChangedAction = QWasmWindowStack::TopWindowChangedCallbackType();
m_onTopLevelChangedAction = QWasmWindowStack::WindowOrderChangedCallbackType();
clearCallbackCounter();
}
@ -77,51 +82,28 @@ void tst_QWasmWindowStack::insertion()
QWasmWindowStack stack(m_mockCallback);
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_root); };
stack.pushWindow(&m_root);
verifyTopWindowChangedCalled();
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::Regular);
verifyWindowOrderChanged();
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window1); };
stack.pushWindow(&m_window1);
verifyTopWindowChangedCalled();
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
verifyWindowOrderChanged();
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window2); };
stack.pushWindow(&m_window2);
verifyTopWindowChangedCalled();
}
void tst_QWasmWindowStack::raisingTheRootIsImpossible()
{
QWasmWindowStack stack(m_mockCallback);
stack.pushWindow(&m_root);
stack.pushWindow(&m_window1);
stack.pushWindow(&m_window2);
stack.pushWindow(&m_window3);
stack.pushWindow(&m_window4);
stack.pushWindow(&m_window5);
clearCallbackCounter();
stack.raise(&m_root);
verifyTopWindowChangedCalled(0);
QCOMPARE(&m_window5, stack.topWindow());
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window2); };
stack.raise(&m_window2);
verifyTopWindowChangedCalled();
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
verifyWindowOrderChanged();
}
void tst_QWasmWindowStack::raising()
{
QWasmWindowStack stack(m_mockCallback);
stack.pushWindow(&m_root);
stack.pushWindow(&m_window1);
stack.pushWindow(&m_window2);
stack.pushWindow(&m_window3);
stack.pushWindow(&m_window4);
stack.pushWindow(&m_window5);
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window4, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
clearCallbackCounter();
@ -129,30 +111,176 @@ void tst_QWasmWindowStack::raising()
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window1); };
stack.raise(&m_window1);
verifyTopWindowChangedCalled();
verifyWindowOrderChanged();
QCOMPARE(&m_window1, stack.topWindow());
stack.raise(&m_window1);
verifyTopWindowChangedCalled(0);
verifyWindowOrderChanged(0);
QCOMPARE(&m_window1, stack.topWindow());
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window3); };
stack.raise(&m_window3);
verifyTopWindowChangedCalled();
verifyWindowOrderChanged();
QCOMPARE(&m_window3, stack.topWindow());
}
void tst_QWasmWindowStack::raisingWithAlwaysOnBottom()
{
QWasmWindowStack stack(m_mockCallback);
QWasmWindow alwaysOnBottomWindow1;
QWasmWindow alwaysOnBottomWindow2;
QWasmWindow alwaysOnBottomWindow3;
stack.pushWindow(&alwaysOnBottomWindow1, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&alwaysOnBottomWindow2, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&alwaysOnBottomWindow3, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
// Window order: 3 2 1 | B3 B2 B1
clearCallbackCounter();
std::vector<QWasmWindow *> expectedWindowOrder = { &m_window3,
&m_window2,
&m_window1,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow2,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
QCOMPARE(&m_window3, stack.topWindow());
// Window order: 1 3 2 | B3 B2 B1
stack.raise(&m_window1);
expectedWindowOrder = { &m_window1,
&m_window3,
&m_window2,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow2,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window1, stack.topWindow());
// Window order: 1 3 2 | B1 B3 B2
stack.raise(&alwaysOnBottomWindow1);
expectedWindowOrder = { &m_window1,
&m_window3,
&m_window2,
&alwaysOnBottomWindow1,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow2 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window1, stack.topWindow());
// Window order: 1 3 2 | B3 B1 B2
stack.raise(&alwaysOnBottomWindow3);
expectedWindowOrder = { &m_window1,
&m_window3,
&m_window2,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow1,
&alwaysOnBottomWindow2 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window1, stack.topWindow());
}
void tst_QWasmWindowStack::raisingWithAlwaysOnTop()
{
QWasmWindowStack stack(m_mockCallback);
QWasmWindow alwaysOnTopWindow1;
QWasmWindow alwaysOnTopWindow2;
QWasmWindow alwaysOnTopWindow3;
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&alwaysOnTopWindow1, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&alwaysOnTopWindow2, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&alwaysOnTopWindow3, QWasmWindowStack::PositionPreference::StayOnTop);
// Window order: T3 T2 T1 | 5 3 1 | R
clearCallbackCounter();
std::vector<QWasmWindow *> expectedWindowOrder = { &alwaysOnTopWindow3,
&alwaysOnTopWindow2,
&alwaysOnTopWindow1,
&m_window5,
&m_window3,
&m_window1,
&m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: T3 T2 T1 | 1 5 3 | R
stack.raise(&m_window1);
expectedWindowOrder = { &alwaysOnTopWindow3,
&alwaysOnTopWindow2,
&alwaysOnTopWindow1,
&m_window1,
&m_window5,
&m_window3,
&m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&alwaysOnTopWindow3, stack.topWindow());
// Window order: T3 T2 T1 3 1 5 R
stack.raise(&m_window3);
expectedWindowOrder = { &alwaysOnTopWindow3,
&alwaysOnTopWindow2,
&alwaysOnTopWindow1,
&m_window3,
&m_window1,
&m_window5,
&m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&alwaysOnTopWindow3, stack.topWindow());
// Window order: T1 T3 T2 3 1 5 R
stack.raise(&alwaysOnTopWindow1);
expectedWindowOrder = { &alwaysOnTopWindow1,
&alwaysOnTopWindow3,
&alwaysOnTopWindow2,
&m_window3,
&m_window1,
&m_window5,
&m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&alwaysOnTopWindow1, stack.topWindow());
}
void tst_QWasmWindowStack::lowering()
{
QWasmWindowStack stack(m_mockCallback);
stack.pushWindow(&m_root);
stack.pushWindow(&m_window1);
stack.pushWindow(&m_window2);
stack.pushWindow(&m_window3);
stack.pushWindow(&m_window4);
stack.pushWindow(&m_window5);
// Window order: 5 4 3 2 1 R
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window4, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
// Window order: 5 4 3 2 1 | R
clearCallbackCounter();
@ -160,29 +288,166 @@ void tst_QWasmWindowStack::lowering()
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window4); };
stack.lower(&m_window5);
// Window order: 4 3 2 1 5 R
verifyTopWindowChangedCalled();
verifyWindowOrderChanged();
QCOMPARE(&m_window4, stack.topWindow());
stack.lower(&m_window3);
// Window order: 4 2 1 5 3 R
verifyTopWindowChangedCalled(0);
verifyWindowOrderChanged();
std::vector<QWasmWindow *> expectedWindowOrder = { &m_window4, &m_window2, &m_window1,
&m_window5, &m_window3, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
}
void tst_QWasmWindowStack::loweringWithAlwaysOnBottom()
{
QWasmWindowStack stack(m_mockCallback);
QWasmWindow alwaysOnBottomWindow1;
QWasmWindow alwaysOnBottomWindow2;
QWasmWindow alwaysOnBottomWindow3;
stack.pushWindow(&alwaysOnBottomWindow1, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&alwaysOnBottomWindow2, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&alwaysOnBottomWindow3, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
// Window order: 3 2 1 | B3 B2 B1
clearCallbackCounter();
std::vector<QWasmWindow *> expectedWindowOrder = { &m_window3,
&m_window2,
&m_window1,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow2,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
QCOMPARE(&m_window3, stack.topWindow());
// Window order: 2 1 3 | B3 B2 B1
stack.lower(&m_window3);
expectedWindowOrder = { &m_window2,
&m_window1,
&m_window3,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow2,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window2, stack.topWindow());
// Window order: 2 1 3 | B2 B1 B3
stack.lower(&alwaysOnBottomWindow3);
expectedWindowOrder = { &m_window2,
&m_window1,
&m_window3,
&alwaysOnBottomWindow2,
&alwaysOnBottomWindow1,
&alwaysOnBottomWindow3 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window2, stack.topWindow());
// Window order: 2 1 3 | B2 B3 B1
stack.lower(&alwaysOnBottomWindow1);
expectedWindowOrder = { &m_window2,
&m_window1,
&m_window3,
&alwaysOnBottomWindow2,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window2, stack.topWindow());
}
void tst_QWasmWindowStack::loweringWithAlwaysOnTop()
{
QWasmWindowStack stack(m_mockCallback);
QWasmWindow alwaysOnTopWindow1;
QWasmWindow alwaysOnTopWindow2;
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&alwaysOnTopWindow1, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&alwaysOnTopWindow2, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
// Window order: T2 T1 5 3 1 R
clearCallbackCounter();
std::vector<QWasmWindow *> expectedWindowOrder = { &alwaysOnTopWindow2, &alwaysOnTopWindow1,
&m_window5, &m_window3,
&m_window1, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: T1 T2 5 3 1 R
stack.lower(&alwaysOnTopWindow2);
expectedWindowOrder = { &alwaysOnTopWindow1, &alwaysOnTopWindow2, &m_window5,
&m_window3, &m_window1, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&alwaysOnTopWindow1, stack.topWindow());
// Window order: T2 T1 5 3 1 R
stack.lower(&alwaysOnTopWindow1);
expectedWindowOrder = { &alwaysOnTopWindow2, &alwaysOnTopWindow1, &m_window5,
&m_window3, &m_window1, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&alwaysOnTopWindow2, stack.topWindow());
// Window order: T2 T1 3 1 5 R
stack.lower(&m_window5);
expectedWindowOrder = { &alwaysOnTopWindow2, &alwaysOnTopWindow1, &m_window3,
&m_window1, &m_window5, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&alwaysOnTopWindow2, stack.topWindow());
// Window order: T2 T1 3 5 1 R
stack.lower(&m_window1);
expectedWindowOrder = { &alwaysOnTopWindow2, &alwaysOnTopWindow1, &m_window3,
&m_window5, &m_window1, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&alwaysOnTopWindow2, stack.topWindow());
}
void tst_QWasmWindowStack::removing()
{
QWasmWindowStack stack(m_mockCallback);
stack.pushWindow(&m_root);
stack.pushWindow(&m_window1);
stack.pushWindow(&m_window2);
stack.pushWindow(&m_window3);
stack.pushWindow(&m_window4);
stack.pushWindow(&m_window5);
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window4, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
// Window order: 5 4 3 2 1 R
clearCallbackCounter();
@ -192,62 +457,239 @@ void tst_QWasmWindowStack::removing()
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window4); };
stack.removeWindow(&m_window5);
// Window order: 4 3 2 1 R
verifyTopWindowChangedCalled();
verifyWindowOrderChanged();
QCOMPARE(&m_window4, stack.topWindow());
stack.removeWindow(&m_window2);
// Window order: 4 3 1 R
verifyTopWindowChangedCalled(0);
verifyWindowOrderChanged();
std::vector<QWasmWindow *> expectedWindowOrder = { &m_window4, &m_window3, &m_window1,
&m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
}
void tst_QWasmWindowStack::removingTheRoot()
void tst_QWasmWindowStack::positionPreferenceChanges()
{
QWasmWindowStack stack(m_mockCallback);
stack.pushWindow(&m_root);
stack.pushWindow(&m_window1);
stack.pushWindow(&m_window2);
stack.pushWindow(&m_window3);
// Window order: 3 2 1 R
QWasmWindow window6;
QWasmWindow window7;
QWasmWindow window8;
QWasmWindow window9;
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window4, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&window6, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&window7, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&window8, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&window9, QWasmWindowStack::PositionPreference::StayOnTop);
// Window order: 9 8 7 | 6 5 4 | 3 2 1
clearCallbackCounter();
QCOMPARE(&m_window3, stack.topWindow());
stack.removeWindow(&m_root);
// Window order: 3 2 1
verifyTopWindowChangedCalled(0);
QCOMPARE(&m_window3, stack.topWindow());
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window1); };
// Check that the new bottom window is not treated specially as a root
stack.raise(&m_window1);
// Window order: 1 3 2
verifyTopWindowChangedCalled();
std::vector<QWasmWindow *> expectedWindowOrder = { &m_window1, &m_window3, &m_window2 };
std::vector<QWasmWindow *> expectedWindowOrder = { &window9, &window8, &window7,
&window6, &m_window5, &m_window4,
&m_window3, &m_window2, &m_window1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_window3); };
// Check that the new bottom window is not treated specially as a root
stack.lower(&m_window1);
// Window order: 3 2 1
verifyTopWindowChangedCalled();
expectedWindowOrder = { &m_window3, &m_window2, &m_window1 };
// Window order: 9 8 7 1 | 6 5 4 | 3 2
stack.windowPositionPreferenceChanged(&m_window1,
QWasmWindowStack::PositionPreference::StayOnTop);
expectedWindowOrder = {
&window9, &window8, &window7, &m_window1, &window6,
&m_window5, &m_window4, &m_window3, &m_window2,
};
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: 9 8 7 1 5 | 6 4 | 3 2
stack.windowPositionPreferenceChanged(&m_window5,
QWasmWindowStack::PositionPreference::StayOnTop);
expectedWindowOrder = {
&window9, &window8, &window7, &m_window1, &m_window5,
&window6, &m_window4, &m_window3, &m_window2,
};
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: 9 7 1 5 | 8 6 4 | 3 2
stack.windowPositionPreferenceChanged(&window8, QWasmWindowStack::PositionPreference::Regular);
expectedWindowOrder = {
&window9, &window7, &m_window1, &m_window5, &window8,
&window6, &m_window4, &m_window3, &m_window2,
};
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: 9 7 1 5 | 8 6 4 2 | 3
stack.windowPositionPreferenceChanged(&m_window2,
QWasmWindowStack::PositionPreference::Regular);
expectedWindowOrder = {
&window9, &window7, &m_window1, &m_window5, &window8,
&window6, &m_window4, &m_window2, &m_window3,
};
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: 7 1 5 | 8 6 4 2 | 9 3
stack.windowPositionPreferenceChanged(&window9,
QWasmWindowStack::PositionPreference::StayOnBottom);
expectedWindowOrder = {
&window7, &m_window1, &m_window5, &window8, &window6,
&m_window4, &m_window2, &window9, &m_window3,
};
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: 7 1 5 | 6 4 2 | 8 9 3
stack.windowPositionPreferenceChanged(&window8,
QWasmWindowStack::PositionPreference::StayOnBottom);
expectedWindowOrder = {
&window7, &m_window1, &m_window5, &window6, &m_window4,
&m_window2, &window8, &window9, &m_window3,
};
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
}
void tst_QWasmWindowStack::removingWithAlwaysOnBottom()
{
QWasmWindowStack stack(m_mockCallback);
QWasmWindow alwaysOnBottomWindow1;
QWasmWindow alwaysOnBottomWindow2;
QWasmWindow alwaysOnBottomWindow3;
stack.pushWindow(&alwaysOnBottomWindow1, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&alwaysOnBottomWindow2, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&alwaysOnBottomWindow3, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window2, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
// Window order: 3 2 1 | B3 B2 B1
clearCallbackCounter();
std::vector<QWasmWindow *> expectedWindowOrder = { &m_window3,
&m_window2,
&m_window1,
&alwaysOnBottomWindow3,
&alwaysOnBottomWindow2,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
QCOMPARE(&m_window3, stack.topWindow());
// Window order: 3 1 | B3 B2 B1
stack.removeWindow(&m_window2);
expectedWindowOrder = { &m_window3, &m_window1, &alwaysOnBottomWindow3, &alwaysOnBottomWindow2,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window3, stack.topWindow());
// Window order: 1 3 2 | B1 B3 B2
stack.removeWindow(&alwaysOnBottomWindow2);
expectedWindowOrder = { &m_window3, &m_window1, &alwaysOnBottomWindow3,
&alwaysOnBottomWindow1 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window3, stack.topWindow());
// Window order: 1 3 2 | B3 B1 B2
stack.removeWindow(&alwaysOnBottomWindow1);
expectedWindowOrder = { &m_window3, &m_window1, &alwaysOnBottomWindow3 };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
verifyWindowOrderChanged();
QCOMPARE(&m_window3, stack.topWindow());
}
void tst_QWasmWindowStack::removingWithAlwaysOnTop()
{
QWasmWindowStack stack(m_mockCallback);
QWasmWindow alwaysOnTopWindow1;
QWasmWindow alwaysOnTopWindow2;
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&alwaysOnTopWindow1, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&m_window3, QWasmWindowStack::PositionPreference::Regular);
stack.pushWindow(&alwaysOnTopWindow2, QWasmWindowStack::PositionPreference::StayOnTop);
stack.pushWindow(&m_window5, QWasmWindowStack::PositionPreference::Regular);
// Window order: T2 T1 5 3 1 R
clearCallbackCounter();
std::vector<QWasmWindow *> expectedWindowOrder = { &alwaysOnTopWindow2, &alwaysOnTopWindow1,
&m_window5, &m_window3,
&m_window1, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: T2 T1 5 1 R
stack.removeWindow(&m_window3);
verifyWindowOrderChanged();
expectedWindowOrder = { &alwaysOnTopWindow2, &alwaysOnTopWindow1, &m_window5, &m_window1,
&m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: T2 5 1 R
stack.removeWindow(&alwaysOnTopWindow1);
verifyWindowOrderChanged();
expectedWindowOrder = { &alwaysOnTopWindow2, &m_window5, &m_window1, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: T2 1 R
stack.removeWindow(&m_window5);
verifyWindowOrderChanged();
expectedWindowOrder = { &alwaysOnTopWindow2, &m_window1, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: T2 R
stack.removeWindow(&m_window1);
verifyWindowOrderChanged();
expectedWindowOrder = { &alwaysOnTopWindow2, &m_root };
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
getWindowsFrontToBack(&stack).begin()));
// Window order: R
stack.removeWindow(&alwaysOnTopWindow2);
verifyWindowOrderChanged();
QCOMPARE(&m_root, stack.topWindow());
}
void tst_QWasmWindowStack::clearing()
{
QWasmWindowStack stack(m_mockCallback);
stack.pushWindow(&m_root);
stack.pushWindow(&m_window1);
stack.pushWindow(&m_root, QWasmWindowStack::PositionPreference::StayOnBottom);
stack.pushWindow(&m_window1, QWasmWindowStack::PositionPreference::Regular);
// Window order: 1 R
clearCallbackCounter();
@ -257,13 +699,13 @@ void tst_QWasmWindowStack::clearing()
m_onTopLevelChangedAction = [this, &stack]() { QVERIFY(stack.topWindow() == &m_root); };
stack.removeWindow(&m_window1);
// Window order: R
verifyTopWindowChangedCalled();
verifyWindowOrderChanged();
QCOMPARE(&m_root, stack.topWindow());
m_onTopLevelChangedAction = [&stack]() { QVERIFY(stack.topWindow() == nullptr); };
stack.removeWindow(&m_root);
// Window order: <empty>
verifyTopWindowChangedCalled();
verifyWindowOrderChanged();
QCOMPARE(nullptr, stack.topWindow());
QCOMPARE(0u, stack.size());
}