Maintain the window z-order properly in wasm compositor
The old stack structure used to keep track of windows has been improved to conform to the actual windowing assumptions: there shall be one root window, which is always at the bottom. The first created window immediately becomes the root window. Should the root window be removed, all windows are non-root, i.e. any of them can become the top-level window Fixes: QTBUG-105094 Pick-to: 6.4 Change-Id: Ic553244fa9f5bc3ee590b702935e66cfc62d5f8f Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>bb10
parent
878328c6ab
commit
1007964f2d
|
|
@ -34,6 +34,7 @@ qt_internal_add_plugin(QWasmIntegrationPlugin
|
|||
qwasmwindow.cpp qwasmwindow.h
|
||||
qwasminputcontext.cpp qwasminputcontext.h
|
||||
qwasmdrag.cpp qwasmdrag.h
|
||||
qwasmwindowstack.cpp qwasmwindowstack.h
|
||||
DEFINES
|
||||
QT_EGL_NO_X11
|
||||
QT_NO_FOREACH
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@
|
|||
#include <emscripten/bind.h>
|
||||
|
||||
namespace {
|
||||
QWasmWindow *AsWasmWindow(QWindow *window) {
|
||||
QWasmWindow *asWasmWindow(QWindow *window)
|
||||
{
|
||||
return static_cast<QWasmWindow*>(window->handle());
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -50,10 +51,11 @@ EMSCRIPTEN_BINDINGS(qtMouseModule) {
|
|||
}
|
||||
|
||||
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
|
||||
: QObject(screen)
|
||||
, m_windowManipulation(screen)
|
||||
, m_blitter(new QOpenGLTextureBlitter)
|
||||
, m_eventTranslator(std::make_unique<QWasmEventTranslator>())
|
||||
: QObject(screen),
|
||||
m_windowManipulation(screen),
|
||||
m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this, std::placeholders::_1)),
|
||||
m_blitter(new QOpenGLTextureBlitter),
|
||||
m_eventTranslator(std::make_unique<QWasmEventTranslator>())
|
||||
{
|
||||
m_touchDevice = std::make_unique<QPointingDevice>(
|
||||
"touchscreen", 1, QInputDevice::DeviceType::TouchScreen,
|
||||
|
|
@ -171,23 +173,14 @@ void QWasmCompositor::setEnabled(bool enabled)
|
|||
void QWasmCompositor::addWindow(QWasmWindow *window)
|
||||
{
|
||||
m_windowVisibility.insert(window, false);
|
||||
|
||||
m_windowStack.append(window);
|
||||
|
||||
notifyTopWindowChanged(window);
|
||||
m_windowStack.pushWindow(window);
|
||||
}
|
||||
|
||||
void QWasmCompositor::removeWindow(QWasmWindow *window)
|
||||
{
|
||||
m_windowStack.removeAll(window);
|
||||
m_windowVisibility.remove(window);
|
||||
m_requestUpdateWindows.remove(window);
|
||||
|
||||
if (!m_windowStack.isEmpty() && !QGuiApplication::focusWindow()) {
|
||||
auto m_lastMouseTargetWindow = m_windowStack.last();
|
||||
m_lastMouseTargetWindow->requestActivateWindow();
|
||||
notifyTopWindowChanged(m_lastMouseTargetWindow);
|
||||
}
|
||||
m_windowStack.removeWindow(window);
|
||||
}
|
||||
|
||||
void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
|
||||
|
|
@ -205,47 +198,36 @@ void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
|
|||
|
||||
void QWasmCompositor::raise(QWasmWindow *window)
|
||||
{
|
||||
if (m_windowStack.size() <= 1)
|
||||
return;
|
||||
|
||||
m_windowStack.removeAll(window);
|
||||
m_windowStack.append(window);
|
||||
|
||||
notifyTopWindowChanged(window);
|
||||
m_windowStack.raise(window);
|
||||
}
|
||||
|
||||
void QWasmCompositor::lower(QWasmWindow *window)
|
||||
{
|
||||
if (m_windowStack.size() <= 1)
|
||||
return;
|
||||
|
||||
m_windowStack.removeAll(window);
|
||||
m_windowStack.prepend(window);
|
||||
m_globalDamage = window->window()->geometry(); // repaint previously covered area.
|
||||
|
||||
notifyTopWindowChanged(window);
|
||||
m_windowStack.lower(window);
|
||||
}
|
||||
|
||||
int QWasmCompositor::windowCount() const
|
||||
{
|
||||
return m_windowStack.count();
|
||||
return m_windowStack.size();
|
||||
}
|
||||
|
||||
QWindow *QWasmCompositor::windowAt(QPoint targetPointInScreenCoords, int padding) const
|
||||
{
|
||||
const auto found = std::find_if(m_windowStack.rbegin(), m_windowStack.rend(),
|
||||
[this, padding, &targetPointInScreenCoords](const QWasmWindow* window) {
|
||||
const QRect geometry = window->windowFrameGeometry()
|
||||
.adjusted(-padding, -padding, padding, padding);
|
||||
const auto found = std::find_if(
|
||||
m_windowStack.begin(), m_windowStack.end(),
|
||||
[this, padding, &targetPointInScreenCoords](const QWasmWindow *window) {
|
||||
const QRect geometry = window->windowFrameGeometry().adjusted(-padding, -padding,
|
||||
padding, padding);
|
||||
|
||||
return m_windowVisibility[window] && geometry.contains(targetPointInScreenCoords);
|
||||
});
|
||||
return found != m_windowStack.rend() ? (*found)->window() : nullptr;
|
||||
return m_windowVisibility[window] && geometry.contains(targetPointInScreenCoords);
|
||||
});
|
||||
return found != m_windowStack.end() ? (*found)->window() : nullptr;
|
||||
}
|
||||
|
||||
QWindow *QWasmCompositor::keyWindow() const
|
||||
{
|
||||
return m_windowStack.at(m_windowStack.count() - 1)->window();
|
||||
return m_windowStack.topWindow() ? m_windowStack.topWindow()->window() : nullptr;
|
||||
}
|
||||
|
||||
void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry)
|
||||
|
|
@ -267,7 +249,8 @@ void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
|
|||
blitter->blit(texture->textureId(), m, QOpenGLTextureBlitter::OriginTopLeft);
|
||||
}
|
||||
|
||||
void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
|
||||
void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
|
||||
const QWasmWindow *window)
|
||||
{
|
||||
QWasmBackingStore *backingStore = window->backingStore();
|
||||
if (!backingStore)
|
||||
|
|
@ -498,7 +481,8 @@ QWasmCompositor::QWasmTitleBarOptions QWasmCompositor::makeTitleBarOptions(const
|
|||
return titleBarOptions;
|
||||
}
|
||||
|
||||
void QWasmCompositor::drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
|
||||
void QWasmCompositor::drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
|
||||
const QWasmWindow *window)
|
||||
{
|
||||
int width = window->windowFrameGeometry().width();
|
||||
int height = window->windowFrameGeometry().height();
|
||||
|
|
@ -753,7 +737,8 @@ void QWasmCompositor::drawShadePanel(QWasmTitleBarOptions options, QPainter *pai
|
|||
|
||||
}
|
||||
|
||||
void QWasmCompositor::drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
|
||||
void QWasmCompositor::drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
|
||||
const QWasmWindow *window)
|
||||
{
|
||||
if (window->window()->type() != Qt::Popup && !(window->m_windowState & Qt::WindowFullScreen))
|
||||
drawWindowDecorations(blitter, screen, window);
|
||||
|
|
@ -767,9 +752,9 @@ void QWasmCompositor::frame()
|
|||
|
||||
QWasmWindow *someWindow = nullptr;
|
||||
|
||||
for (QWasmWindow *window : qAsConst(m_windowStack)) {
|
||||
for (QWasmWindow *window : m_windowStack) {
|
||||
if (window->window()->surfaceClass() == QSurface::Window
|
||||
&& qt_window_private(static_cast<QWindow *>(window->window()))->receivedExpose) {
|
||||
&& qt_window_private(window->window())->receivedExpose) {
|
||||
someWindow = window;
|
||||
break;
|
||||
}
|
||||
|
|
@ -801,10 +786,10 @@ void QWasmCompositor::frame()
|
|||
m_blitter->bind();
|
||||
m_blitter->setRedBlueSwizzle(true);
|
||||
|
||||
for (QWasmWindow *window : qAsConst(m_windowStack)) {
|
||||
std::for_each(m_windowStack.rbegin(), m_windowStack.rend(), [this](const QWasmWindow *window) {
|
||||
if (m_windowVisibility[window])
|
||||
drawWindow(m_blitter.data(), screen(), window);
|
||||
}
|
||||
});
|
||||
|
||||
m_blitter->release();
|
||||
|
||||
|
|
@ -835,14 +820,18 @@ void QWasmCompositor::WindowManipulation::resizeWindow(const QPoint& amount)
|
|||
));
|
||||
}
|
||||
|
||||
void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
|
||||
void QWasmCompositor::onTopWindowChanged(QWasmWindow *window)
|
||||
{
|
||||
if (!QGuiApplication::focusWindow())
|
||||
window->requestActivateWindow();
|
||||
|
||||
QWindow *modalWindow;
|
||||
bool isTargetWindowBlocked = QGuiApplicationPrivate::instance()->isWindowBlocked(window->window(), &modalWindow);
|
||||
const bool isTargetWindowBlocked =
|
||||
QGuiApplicationPrivate::instance()->isWindowBlocked(window->window(), &modalWindow);
|
||||
|
||||
if (isTargetWindowBlocked) {
|
||||
modalWindow->requestActivate();
|
||||
raise(AsWasmWindow(modalWindow));
|
||||
raise(asWasmWindow(modalWindow));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -913,7 +902,7 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
|
|||
m_windowUnderMouse = targetWindow;
|
||||
}
|
||||
|
||||
QWasmWindow *wasmTargetWindow = AsWasmWindow(targetWindow);
|
||||
QWasmWindow *wasmTargetWindow = asWasmWindow(targetWindow);
|
||||
Qt::WindowStates windowState = targetWindow->windowState();
|
||||
const bool isTargetWindowResizable = !windowState.testFlag(Qt::WindowMaximized) && !windowState.testFlag(Qt::WindowFullScreen);
|
||||
|
||||
|
|
@ -936,7 +925,9 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
|
|||
|
||||
if (m_pressedWindow) {
|
||||
// Always deliver the released event to the same window that was pressed
|
||||
AsWasmWindow(m_pressedWindow)->injectMouseReleased(pointInTargetWindowCoords, targetPointInScreenCoords, event.mouseButton, event.modifiers);
|
||||
asWasmWindow(m_pressedWindow)
|
||||
->injectMouseReleased(pointInTargetWindowCoords, targetPointInScreenCoords,
|
||||
event.mouseButton, event.modifiers);
|
||||
if (event.mouseButton == Qt::MouseButton::LeftButton)
|
||||
m_pressedWindow = nullptr;
|
||||
} else {
|
||||
|
|
@ -1064,20 +1055,21 @@ void QWasmCompositor::WindowManipulation::onPointerDown(
|
|||
const auto pointInScreenCoords = m_screen->geometry().topLeft() + event.point;
|
||||
|
||||
std::unique_ptr<std::variant<ResizeState, MoveState>> operationSpecific;
|
||||
if (AsWasmWindow(windowAtPoint)->isPointOnTitle(pointInScreenCoords)) {
|
||||
if (asWasmWindow(windowAtPoint)->isPointOnTitle(pointInScreenCoords)) {
|
||||
operationSpecific = std::make_unique<std::variant<ResizeState, MoveState>>(MoveState {
|
||||
.m_lastPointInScreenCoords = pointInScreenCoords
|
||||
});
|
||||
} else if (AsWasmWindow(windowAtPoint)->isPointOnResizeRegion(pointInScreenCoords)) {
|
||||
operationSpecific = std::make_unique<std::variant<ResizeState, MoveState>>(ResizeState {
|
||||
.m_resizeMode = AsWasmWindow(windowAtPoint)->resizeModeAtPoint(pointInScreenCoords),
|
||||
.m_originInScreenCoords = pointInScreenCoords,
|
||||
.m_initialWindowBounds = windowAtPoint->geometry(),
|
||||
.m_minShrink = QPoint(windowAtPoint->minimumWidth() - windowAtPoint->geometry().width(),
|
||||
windowAtPoint->minimumHeight() - windowAtPoint->geometry().height()),
|
||||
.m_maxGrow = QPoint(
|
||||
windowAtPoint->maximumWidth() - windowAtPoint->geometry().width(),
|
||||
windowAtPoint->maximumHeight() - windowAtPoint->geometry().height()),
|
||||
} else if (asWasmWindow(windowAtPoint)->isPointOnResizeRegion(pointInScreenCoords)) {
|
||||
operationSpecific = std::make_unique<std::variant<ResizeState, MoveState>>(ResizeState{
|
||||
.m_resizeMode = asWasmWindow(windowAtPoint)->resizeModeAtPoint(pointInScreenCoords),
|
||||
.m_originInScreenCoords = pointInScreenCoords,
|
||||
.m_initialWindowBounds = windowAtPoint->geometry(),
|
||||
.m_minShrink =
|
||||
QPoint(windowAtPoint->minimumWidth() - windowAtPoint->geometry().width(),
|
||||
windowAtPoint->minimumHeight() - windowAtPoint->geometry().height()),
|
||||
.m_maxGrow =
|
||||
QPoint(windowAtPoint->maximumWidth() - windowAtPoint->geometry().width(),
|
||||
windowAtPoint->maximumHeight() - windowAtPoint->geometry().height()),
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
|
|
@ -1204,7 +1196,7 @@ int QWasmCompositor::handleTouch(int eventType, const EmscriptenTouchEvent *touc
|
|||
{
|
||||
QList<QWindowSystemInterface::TouchPoint> touchPointList;
|
||||
touchPointList.reserve(touchEvent->numTouches);
|
||||
QWindow *targetWindow;
|
||||
QWindow *targetWindow = nullptr;
|
||||
|
||||
for (int i = 0; i < touchEvent->numTouches; i++) {
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#ifndef QWASMCOMPOSITOR_H
|
||||
#define QWASMCOMPOSITOR_H
|
||||
|
||||
#include "qwasmwindowstack.h"
|
||||
|
||||
#include <QtGui/qregion.h>
|
||||
#include <qpa/qplatformwindow.h>
|
||||
|
||||
|
|
@ -174,12 +176,14 @@ private:
|
|||
std::unique_ptr<OperationState> m_state;
|
||||
};
|
||||
|
||||
void notifyTopWindowChanged(QWasmWindow *window);
|
||||
void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
|
||||
void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
|
||||
void onTopWindowChanged(QWasmWindow *window);
|
||||
void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QWasmWindow *window);
|
||||
void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
|
||||
const QWasmWindow *window);
|
||||
void blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry);
|
||||
|
||||
void drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
|
||||
void drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
|
||||
const QWasmWindow *window);
|
||||
|
||||
static QPalette makeWindowPalette();
|
||||
|
||||
|
|
@ -199,12 +203,12 @@ private:
|
|||
static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
|
||||
|
||||
WindowManipulation m_windowManipulation;
|
||||
QWasmWasmWindowStack m_windowStack;
|
||||
|
||||
QScopedPointer<QOpenGLContext> m_context;
|
||||
QScopedPointer<QOpenGLTextureBlitter> m_blitter;
|
||||
|
||||
QHash<const QWasmWindow *, bool> m_windowVisibility;
|
||||
QList<QWasmWindow *> m_windowStack;
|
||||
QRegion m_globalDamage; // damage caused by expose, window close, etc.
|
||||
bool m_needComposit = false;
|
||||
bool m_inFlush = false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "qwasmwindowstack.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QWasmWasmWindowStack::QWasmWasmWindowStack(TopWindowChangedCallbackType topWindowChangedCallback)
|
||||
: m_topWindowChangedCallback(std::move(topWindowChangedCallback))
|
||||
{
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::~QWasmWasmWindowStack() = default;
|
||||
|
||||
void QWasmWasmWindowStack::pushWindow(QWasmWindow *window)
|
||||
{
|
||||
Q_ASSERT(m_windowStack.count(window) == 0);
|
||||
|
||||
m_windowStack.push_back(window);
|
||||
|
||||
m_topWindowChangedCallback(window);
|
||||
}
|
||||
|
||||
void QWasmWasmWindowStack::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;
|
||||
|
||||
m_windowStack.erase(it);
|
||||
|
||||
if (removingTop)
|
||||
m_topWindowChangedCallback(topWindow());
|
||||
}
|
||||
|
||||
void QWasmWasmWindowStack::raise(QWasmWindow *window)
|
||||
{
|
||||
Q_ASSERT(m_windowStack.count(window) == 1);
|
||||
|
||||
if (window == rootWindow() || window == topWindow())
|
||||
return;
|
||||
|
||||
auto it = std::find(regularWindowsBegin(), m_windowStack.end(), window);
|
||||
std::rotate(it, it + 1, m_windowStack.end());
|
||||
m_topWindowChangedCallback(topWindow());
|
||||
}
|
||||
|
||||
void QWasmWasmWindowStack::lower(QWasmWindow *window)
|
||||
{
|
||||
Q_ASSERT(m_windowStack.count(window) == 1);
|
||||
|
||||
if (window == rootWindow())
|
||||
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(topWindow());
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::iterator QWasmWasmWindowStack::begin()
|
||||
{
|
||||
return m_windowStack.rbegin();
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::iterator QWasmWasmWindowStack::end()
|
||||
{
|
||||
return m_windowStack.rend();
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::const_iterator QWasmWasmWindowStack::begin() const
|
||||
{
|
||||
return m_windowStack.rbegin();
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::const_iterator QWasmWasmWindowStack::end() const
|
||||
{
|
||||
return m_windowStack.rend();
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::const_reverse_iterator QWasmWasmWindowStack::rbegin() const
|
||||
{
|
||||
return m_windowStack.begin();
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::const_reverse_iterator QWasmWasmWindowStack::rend() const
|
||||
{
|
||||
return m_windowStack.end();
|
||||
}
|
||||
|
||||
bool QWasmWasmWindowStack::empty() const
|
||||
{
|
||||
return m_windowStack.empty();
|
||||
}
|
||||
|
||||
size_t QWasmWasmWindowStack::size() const
|
||||
{
|
||||
return m_windowStack.size();
|
||||
}
|
||||
|
||||
QWasmWindow *QWasmWasmWindowStack::rootWindow() const
|
||||
{
|
||||
return m_firstWindowTreatment == FirstWindowTreatment::AlwaysAtBottom ? m_windowStack.first()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
QWasmWindow *QWasmWasmWindowStack::topWindow() const
|
||||
{
|
||||
return m_windowStack.last();
|
||||
}
|
||||
|
||||
QWasmWasmWindowStack::StorageType::iterator QWasmWasmWindowStack::regularWindowsBegin()
|
||||
{
|
||||
return m_windowStack.begin()
|
||||
+ (m_firstWindowTreatment == FirstWindowTreatment::AlwaysAtBottom ? 1 : 0);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#ifndef QWASMWINDOWSTACK_H
|
||||
#define QWASMWINDOWSTACK_H
|
||||
|
||||
#include <qglobal.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWasmWindow;
|
||||
|
||||
// Maintains a z-order hierarchy for a set of windows. The first added window is always treated as
|
||||
// the 'root', which always stays at the bottom. Other windows are 'regular', which means they are
|
||||
// subject to z-order changes via |raise| and |lower|/
|
||||
// If the root is ever removed, all of the current and future windows in the stack are treated as
|
||||
// regular.
|
||||
// Access to the top element is facilitated by |topWindow|.
|
||||
// Changes to the top element are signaled via the |topWindowChangedCallback| supplied at
|
||||
// construction.
|
||||
Q_AUTOTEST_EXPORT class QWasmWasmWindowStack
|
||||
{
|
||||
public:
|
||||
using TopWindowChangedCallbackType = std::function<void(QWasmWindow *window)>;
|
||||
|
||||
using StorageType = QList<QWasmWindow *>;
|
||||
|
||||
using iterator = StorageType::reverse_iterator;
|
||||
using const_iterator = StorageType::const_reverse_iterator;
|
||||
using const_reverse_iterator = StorageType::const_iterator;
|
||||
|
||||
explicit QWasmWasmWindowStack(TopWindowChangedCallbackType topWindowChangedCallback);
|
||||
~QWasmWasmWindowStack();
|
||||
|
||||
void pushWindow(QWasmWindow *window);
|
||||
void removeWindow(QWasmWindow *window);
|
||||
void raise(QWasmWindow *window);
|
||||
void lower(QWasmWindow *window);
|
||||
|
||||
// Iterates top-to-bottom
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
// Iterates bottom-to-top
|
||||
const_reverse_iterator rbegin() const;
|
||||
const_reverse_iterator rend() const;
|
||||
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
QWasmWindow *topWindow() const;
|
||||
|
||||
private:
|
||||
enum class FirstWindowTreatment { AlwaysAtBottom, Regular };
|
||||
|
||||
QWasmWindow *rootWindow() const;
|
||||
StorageType::iterator regularWindowsBegin();
|
||||
|
||||
TopWindowChangedCallbackType m_topWindowChangedCallback;
|
||||
QList<QWasmWindow *> m_windowStack;
|
||||
FirstWindowTreatment m_firstWindowTreatment = FirstWindowTreatment::AlwaysAtBottom;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWASMWINDOWSTACK_H
|
||||
|
|
@ -17,3 +17,16 @@ qt_internal_add_test(tst_localfileapi
|
|||
Qt::Gui
|
||||
Qt::Widgets
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qwasmwindowstack
|
||||
SOURCES
|
||||
tst_qwasmwindowstack.cpp
|
||||
DEFINES
|
||||
QT_NO_FOREACH
|
||||
LIBRARIES
|
||||
Qt::GuiPrivate
|
||||
PUBLIC_LIBRARIES
|
||||
Qt::Core
|
||||
Qt::Gui
|
||||
Qt::Widgets
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,264 @@
|
|||
// 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/qwasmwindowstack.h"
|
||||
#include <QtGui/QWindow>
|
||||
#include <QTest>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
class QWasmWindow
|
||||
{
|
||||
};
|
||||
|
||||
namespace {
|
||||
std::vector<QWasmWindow *> getWindowsFrontToBack(const QWasmWasmWindowStack *stack)
|
||||
{
|
||||
return std::vector<QWasmWindow *>(stack->begin(), stack->end());
|
||||
}
|
||||
}
|
||||
|
||||
class tst_QWasmWindowStack : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QWasmWindowStack()
|
||||
: m_mockCallback(
|
||||
std::bind(&tst_QWasmWindowStack::onTopWindowChanged, this, std::placeholders::_1))
|
||||
{
|
||||
}
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
|
||||
void insertion();
|
||||
void raisingTheRootIsImpossible();
|
||||
void raising();
|
||||
void lowering();
|
||||
void removing();
|
||||
void removingTheRoot();
|
||||
|
||||
private:
|
||||
void onTopWindowChanged(QWasmWindow *topWindow)
|
||||
{
|
||||
++m_topLevelChangedCallCount;
|
||||
if (m_onTopLevelChangedAction)
|
||||
m_onTopLevelChangedAction(topWindow);
|
||||
}
|
||||
|
||||
void verifyTopWindowChangedCalled(int expected = 1)
|
||||
{
|
||||
QCOMPARE(expected, m_topLevelChangedCallCount);
|
||||
clearCallbackCounter();
|
||||
}
|
||||
|
||||
void clearCallbackCounter() { m_topLevelChangedCallCount = 0; }
|
||||
|
||||
QWasmWasmWindowStack::TopWindowChangedCallbackType m_mockCallback;
|
||||
QWasmWasmWindowStack::TopWindowChangedCallbackType m_onTopLevelChangedAction;
|
||||
int m_topLevelChangedCallCount = 0;
|
||||
|
||||
QWasmWindow m_root;
|
||||
QWasmWindow m_window1;
|
||||
QWasmWindow m_window2;
|
||||
QWasmWindow m_window3;
|
||||
QWasmWindow m_window4;
|
||||
QWasmWindow m_window5;
|
||||
};
|
||||
|
||||
void tst_QWasmWindowStack::init()
|
||||
{
|
||||
m_onTopLevelChangedAction = QWasmWasmWindowStack::TopWindowChangedCallbackType();
|
||||
clearCallbackCounter();
|
||||
}
|
||||
|
||||
void tst_QWasmWindowStack::insertion()
|
||||
{
|
||||
QWasmWasmWindowStack stack(m_mockCallback);
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) { QVERIFY(topWindow == &m_root); };
|
||||
stack.pushWindow(&m_root);
|
||||
verifyTopWindowChangedCalled();
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) {
|
||||
QVERIFY(topWindow == &m_window1);
|
||||
};
|
||||
stack.pushWindow(&m_window1);
|
||||
verifyTopWindowChangedCalled();
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) {
|
||||
QVERIFY(topWindow == &m_window2);
|
||||
};
|
||||
stack.pushWindow(&m_window2);
|
||||
verifyTopWindowChangedCalled();
|
||||
}
|
||||
|
||||
void tst_QWasmWindowStack::raisingTheRootIsImpossible()
|
||||
{
|
||||
QWasmWasmWindowStack 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](QWasmWindow *topWindow) {
|
||||
QVERIFY(topWindow == &m_window2);
|
||||
};
|
||||
stack.raise(&m_window2);
|
||||
verifyTopWindowChangedCalled();
|
||||
}
|
||||
|
||||
void tst_QWasmWindowStack::raising()
|
||||
{
|
||||
QWasmWasmWindowStack 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();
|
||||
|
||||
QCOMPARE(&m_window5, stack.topWindow());
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) {
|
||||
QVERIFY(topWindow == &m_window1);
|
||||
};
|
||||
stack.raise(&m_window1);
|
||||
verifyTopWindowChangedCalled();
|
||||
QCOMPARE(&m_window1, stack.topWindow());
|
||||
|
||||
stack.raise(&m_window1);
|
||||
verifyTopWindowChangedCalled(0);
|
||||
QCOMPARE(&m_window1, stack.topWindow());
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) {
|
||||
QVERIFY(topWindow == &m_window3);
|
||||
};
|
||||
stack.raise(&m_window3);
|
||||
verifyTopWindowChangedCalled();
|
||||
QCOMPARE(&m_window3, stack.topWindow());
|
||||
}
|
||||
|
||||
void tst_QWasmWindowStack::lowering()
|
||||
{
|
||||
QWasmWasmWindowStack 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
|
||||
|
||||
clearCallbackCounter();
|
||||
|
||||
QCOMPARE(&m_window5, stack.topWindow());
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) {
|
||||
QVERIFY(topWindow == &m_window4);
|
||||
};
|
||||
stack.lower(&m_window5);
|
||||
// Window order: 4 3 2 1 5 R
|
||||
verifyTopWindowChangedCalled();
|
||||
QCOMPARE(&m_window4, stack.topWindow());
|
||||
|
||||
stack.lower(&m_window3);
|
||||
// Window order: 4 2 1 5 3 R
|
||||
verifyTopWindowChangedCalled(0);
|
||||
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::removing()
|
||||
{
|
||||
QWasmWasmWindowStack 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
|
||||
|
||||
clearCallbackCounter();
|
||||
|
||||
QCOMPARE(&m_window5, stack.topWindow());
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) {
|
||||
QVERIFY(topWindow == &m_window4);
|
||||
};
|
||||
stack.removeWindow(&m_window5);
|
||||
// Window order: 4 3 2 1 R
|
||||
verifyTopWindowChangedCalled();
|
||||
QCOMPARE(&m_window4, stack.topWindow());
|
||||
|
||||
stack.removeWindow(&m_window2);
|
||||
// Window order: 4 3 1 R
|
||||
verifyTopWindowChangedCalled(0);
|
||||
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()
|
||||
{
|
||||
QWasmWasmWindowStack 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
|
||||
|
||||
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](QWasmWindow *topWindow) {
|
||||
QVERIFY(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 };
|
||||
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
|
||||
getWindowsFrontToBack(&stack).begin()));
|
||||
|
||||
m_onTopLevelChangedAction = [this](QWasmWindow *topWindow) {
|
||||
QVERIFY(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 };
|
||||
QVERIFY(std::equal(expectedWindowOrder.begin(), expectedWindowOrder.end(),
|
||||
getWindowsFrontToBack(&stack).begin()));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QWasmWindowStack)
|
||||
#include "tst_qwasmwindowstack.moc"
|
||||
Loading…
Reference in New Issue