summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPiotr Wierciński <[email protected]>2023-10-24 11:00:10 +0200
committerPiotr Wierciński <[email protected]>2023-11-02 13:56:52 +0200
commitd90692ce96fec6c3e74a609d77691e2304fb0690 (patch)
treed71b066470718510fac50fec71d45f3ba40627ed
parent99c0ffb71a41fe5667921e24b019a414d4178d1e (diff)
wasm: Backport support for child windows
Backport support for child windows on WASM. The main source of changes is commit fc4fca6d9dc22839ca73898c362faff96c81214c. Fixes: QTBUG-117897 Change-Id: Iaf1016e32581c2c138d5a4dceb3c050aa88cc636 Reviewed-by: Morten Johan Sørvig <[email protected]> Reviewed-by: Qt CI Bot <[email protected]> Reviewed-by: Piotr Wierciński <[email protected]>
-rw-r--r--src/plugins/platforms/wasm/CMakeLists.txt1
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp123
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.h21
-rw-r--r--src/plugins/platforms/wasm/qwasmcssstyle.cpp7
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp45
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.h14
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp111
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h18
-rw-r--r--src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp12
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowstack.cpp142
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowstack.h23
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowtreenode.cpp104
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowtreenode.h53
13 files changed, 485 insertions, 189 deletions
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt
index deedf243751..b977322e5db 100644
--- a/src/plugins/platforms/wasm/CMakeLists.txt
+++ b/src/plugins/platforms/wasm/CMakeLists.txt
@@ -32,6 +32,7 @@ qt_internal_add_plugin(QWasmIntegrationPlugin
qwasmstring.cpp qwasmstring.h
qwasmtheme.cpp qwasmtheme.h
qwasmwindow.cpp qwasmwindow.h
+ qwasmwindowtreenode.cpp qwasmwindowtreenode.h
qwasmwindowclientarea.cpp qwasmwindowclientarea.h
qwasmwindownonclientarea.cpp qwasmwindownonclientarea.h
qwasminputcontext.cpp qwasminputcontext.h
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index 7fcdc196a71..1fc71fd65dd 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -21,8 +21,7 @@ using namespace emscripten;
Q_GUI_EXPORT int qt_defaultDpiX();
-QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
- : QObject(screen), m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this))
+QWasmCompositor::QWasmCompositor(QWasmScreen *screen) : QObject(screen)
{
QWindowSystemInterface::setSynchronousWindowSystemEvents(true);
}
@@ -35,6 +34,22 @@ QWasmCompositor::~QWasmCompositor()
destroy();
}
+void QWasmCompositor::onWindowTreeChanged(QWasmWindowTreeNodeChangeType changeType,
+ QWasmWindow *window)
+{
+ auto allWindows = screen()->allWindows();
+ setEnabled(std::any_of(allWindows.begin(), allWindows.end(), [](QWasmWindow *element) {
+ return !element->context2d().isUndefined();
+ }));
+ if (changeType == QWasmWindowTreeNodeChangeType::NodeRemoval)
+ m_requestUpdateWindows.remove(window);
+}
+
+void QWasmCompositor::setEnabled(bool enabled)
+{
+ m_isEnabled = enabled;
+}
+
void QWasmCompositor::onScreenDeleting()
{
deregisterEventHandlers();
@@ -58,59 +73,6 @@ void QWasmCompositor::destroy()
m_isEnabled = false; // prevent frame() from creating a new m_context
}
-void QWasmCompositor::addWindow(QWasmWindow *window)
-{
- m_windowStack.pushWindow(window);
- m_windowStack.topWindow()->requestActivateWindow();
-
- updateEnabledState();
-}
-
-void QWasmCompositor::removeWindow(QWasmWindow *window)
-{
- m_requestUpdateWindows.remove(window);
- m_windowStack.removeWindow(window);
- if (m_windowStack.topWindow())
- m_windowStack.topWindow()->requestActivateWindow();
-
- updateEnabledState();
-}
-
-void QWasmCompositor::updateEnabledState()
-{
- m_isEnabled = std::any_of(m_windowStack.begin(), m_windowStack.end(), [](QWasmWindow *window) {
- return !window->context2d().isUndefined();
- });
-}
-
-void QWasmCompositor::raise(QWasmWindow *window)
-{
- m_windowStack.raise(window);
-}
-
-void QWasmCompositor::lower(QWasmWindow *window)
-{
- m_windowStack.lower(window);
-}
-
-QWindow *QWasmCompositor::windowAt(QPoint targetPointInScreenCoords, int padding) const
-{
- const auto found = std::find_if(
- m_windowStack.begin(), m_windowStack.end(),
- [padding, &targetPointInScreenCoords](const QWasmWindow *window) {
- const QRect geometry = window->windowFrameGeometry().adjusted(-padding, -padding,
- padding, padding);
-
- return window->isVisible() && geometry.contains(targetPointInScreenCoords);
- });
- return found != m_windowStack.end() ? (*found)->window() : nullptr;
-}
-
-QWindow *QWasmCompositor::keyWindow() const
-{
- return m_windowStack.topWindow() ? m_windowStack.topWindow()->window() : nullptr;
-}
-
void QWasmCompositor::requestUpdateAllWindows()
{
m_requestUpdateAllWindows = true;
@@ -158,29 +120,19 @@ void QWasmCompositor::deliverUpdateRequests()
// update set.
auto requestUpdateWindows = m_requestUpdateWindows;
m_requestUpdateWindows.clear();
- bool requestUpdateAllWindows = m_requestUpdateAllWindows;
- m_requestUpdateAllWindows = false;
// Update window content, either all windows or a spesific set of windows. Use the correct
// update type: QWindow subclasses expect that requested and delivered updateRequests matches
// exactly.
m_inDeliverUpdateRequest = true;
- if (requestUpdateAllWindows) {
- for (QWasmWindow *window : m_windowStack) {
- auto it = requestUpdateWindows.find(window);
- UpdateRequestDeliveryType updateType =
- (it == m_requestUpdateWindows.end() ? ExposeEventDelivery : it.value());
- deliverUpdateRequest(window, updateType);
- }
- } else {
- for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) {
- auto *window = it.key();
- UpdateRequestDeliveryType updateType = it.value();
- deliverUpdateRequest(window, updateType);
- }
+ for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) {
+ auto *window = it.key();
+ UpdateRequestDeliveryType updateType = it.value();
+ deliverUpdateRequest(window, updateType);
}
+
m_inDeliverUpdateRequest = false;
- frame(requestUpdateAllWindows, requestUpdateWindows.keys());
+ frame(requestUpdateWindows.keys());
}
void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType)
@@ -213,36 +165,15 @@ int dpiScaled(qreal value)
return value * (qreal(qt_defaultDpiX()) / 96.0);
}
-void QWasmCompositor::frame(bool all, const QList<QWasmWindow *> &windows)
+void QWasmCompositor::frame(const QList<QWasmWindow *> &windows)
{
- if (!m_isEnabled || m_windowStack.empty() || !screen())
+ if (!m_isEnabled || !screen())
return;
- if (all) {
- std::for_each(m_windowStack.rbegin(), m_windowStack.rend(),
- [](QWasmWindow *window) { window->paint(); });
- } else {
- std::for_each(windows.begin(), windows.end(), [](QWasmWindow *window) { window->paint(); });
- }
+ for (QWasmWindow *window : windows)
+ window->paint();
}
-void QWasmCompositor::onTopWindowChanged()
-{
- constexpr int zOrderForElementInFrontOfScreen = 3;
- 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()
{
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h
index 5b8f22aaac3..055e83ab019 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.h
+++ b/src/plugins/platforms/wasm/qwasmcompositor.h
@@ -23,6 +23,8 @@ class QWasmScreen;
class QOpenGLContext;
class QOpenGLTexture;
+enum class QWasmWindowTreeNodeChangeType;
+
class QWasmCompositor final : public QObject
{
Q_OBJECT
@@ -30,30 +32,21 @@ public:
QWasmCompositor(QWasmScreen *screen);
~QWasmCompositor() final;
- void addWindow(QWasmWindow *window);
- void removeWindow(QWasmWindow *window);
-
void setVisible(QWasmWindow *window, bool visible);
- void raise(QWasmWindow *window);
- void lower(QWasmWindow *window);
-
void onScreenDeleting();
- QWindow *windowAt(QPoint globalPoint, int padding = 0) const;
- QWindow *keyWindow() const;
-
QWasmScreen *screen();
+ void setEnabled(bool enabled);
enum UpdateRequestDeliveryType { ExposeEventDelivery, UpdateRequestDelivery };
void requestUpdateAllWindows();
void requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType = ExposeEventDelivery);
void handleBackingStoreFlush(QWindow *window);
+ void onWindowTreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindow *window);
private:
- void frame(bool all, const QList<QWasmWindow *> &windows);
-
- void onTopWindowChanged();
+ void frame(const QList<QWasmWindow *> &windows);
void deregisterEventHandlers();
void destroy();
@@ -66,10 +59,6 @@ private:
bool processTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
- void updateEnabledState();
-
- QWasmWindowStack m_windowStack;
-
bool m_isEnabled = true;
QMap<QWasmWindow *, UpdateRequestDeliveryType> m_requestUpdateWindows;
bool m_requestUpdateAllWindows = false;
diff --git a/src/plugins/platforms/wasm/qwasmcssstyle.cpp b/src/plugins/platforms/wasm/qwasmcssstyle.cpp
index c0873880b6d..56c5fbc7922 100644
--- a/src/plugins/platforms/wasm/qwasmcssstyle.cpp
+++ b/src/plugins/platforms/wasm/qwasmcssstyle.cpp
@@ -35,6 +35,11 @@ const char *Style = R"css(
background-color: lightgray;
}
+.qt-window-contents {
+ overflow: hidden;
+ position: relative;
+}
+
.qt-window.transparent-for-input {
pointer-events: none;
}
@@ -133,7 +138,7 @@ const char *Style = R"css(
padding-bottom: 4px;
}
-.qt-window.has-border .title-bar {
+.qt-window.has-border > .title-bar {
display: flex;
}
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index 20dbbebc5d9..0a99b46af84 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -201,12 +201,18 @@ void QWasmScreen::resizeMaximizedWindows()
QWindow *QWasmScreen::topWindow() const
{
- return m_compositor->keyWindow();
+ return activeChild() ? activeChild()->window() : nullptr;
}
QWindow *QWasmScreen::topLevelAt(const QPoint &p) const
{
- return m_compositor->windowAt(p);
+ const auto found =
+ std::find_if(childStack().begin(), childStack().end(), [&p](const QWasmWindow *window) {
+ const QRect geometry = window->windowFrameGeometry();
+
+ return window->isVisible() && geometry.contains(p);
+ });
+ return found != childStack().end() ? (*found)->window() : nullptr;
}
QPointF QWasmScreen::mapFromLocal(const QPointF &p) const
@@ -234,6 +240,18 @@ void QWasmScreen::setGeometry(const QRect &rect)
resizeMaximizedWindows();
}
+void QWasmScreen::onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType,
+ QWasmWindowTreeNode *parent, QWasmWindow *child)
+{
+ Q_UNUSED(parent);
+ if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this
+ && childStack().size() == 1) {
+ child->window()->setFlag(Qt::WindowStaysOnBottomHint);
+ }
+ QWasmWindowTreeNode::onSubtreeChanged(changeType, parent, child);
+ m_compositor->onWindowTreeChanged(changeType, child);
+}
+
void QWasmScreen::updateQScreenAndCanvasRenderSize()
{
// The HTML canvas has two sizes: the CSS size and the canvas render size.
@@ -308,4 +326,27 @@ void QWasmScreen::installCanvasResizeObserver()
resizeObserver.call<void>("observe", m_shadowContainer);
}
+emscripten::val QWasmScreen::containerElement()
+{
+ return m_shadowContainer;
+}
+
+QWasmWindowTreeNode *QWasmScreen::parentNode()
+{
+ return nullptr;
+}
+
+QList<QWasmWindow *> QWasmScreen::allWindows()
+{
+ QList<QWasmWindow *> windows;
+ for (auto *child : childStack()) {
+ QWindowList list = child->window()->findChildren<QWindow *>(Qt::FindChildrenRecursively);
+ std::transform(
+ list.begin(), list.end(), std::back_inserter(windows),
+ [](const QWindow *window) { return static_cast<QWasmWindow *>(window->handle()); });
+ windows.push_back(child);
+ }
+ return windows;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h
index 633cf853f7e..47ce11495f2 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.h
+++ b/src/plugins/platforms/wasm/qwasmscreen.h
@@ -6,6 +6,8 @@
#include "qwasmcursor.h"
+#include "qwasmwindowtreenode.h"
+
#include <qpa/qplatformscreen.h>
#include <QtCore/qscopedpointer.h>
@@ -23,7 +25,7 @@ class QWasmCompositor;
class QWasmDeadKeySupport;
class QOpenGLContext;
-class QWasmScreen : public QObject, public QPlatformScreen
+class QWasmScreen : public QObject, public QPlatformScreen, public QWasmWindowTreeNode
{
Q_OBJECT
public:
@@ -41,6 +43,8 @@ public:
QWasmCompositor *compositor();
QWasmDeadKeySupport *deadKeySupport() { return m_deadKeySupport.get(); }
+ QList<QWasmWindow *> allWindows();
+
QRect geometry() const override;
int depth() const override;
QImage::Format format() const override;
@@ -53,6 +57,10 @@ public:
QWindow *topWindow() const;
QWindow *topLevelAt(const QPoint &p) const override;
+ // QWasmWindowTreeNode:
+ emscripten::val containerElement() final;
+ QWasmWindowTreeNode *parentNode() final;
+
QPointF mapFromLocal(const QPointF &p) const;
QPointF clipPoint(const QPointF &p) const;
@@ -65,6 +73,10 @@ public slots:
void setGeometry(const QRect &rect);
private:
+ // QWasmWindowTreeNode:
+ void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType, QWasmWindowTreeNode *parent,
+ QWasmWindow *child) final;
+
emscripten::val m_container;
emscripten::val m_shadowContainer;
std::unique_ptr<QWasmCompositor> m_compositor;
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 11513fdd8b2..d655f272205 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -33,6 +33,17 @@
QT_BEGIN_NAMESPACE
+namespace {
+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
+
Q_GUI_EXPORT int qt_defaultDpiX();
QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
@@ -57,6 +68,7 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
m_clientArea = std::make_unique<ClientArea>(this, compositor->screen(), m_windowContents);
+ m_windowContents.set("className", "qt-window-contents");
m_qtWindow.call<void>("appendChild", m_windowContents);
m_canvas["classList"].call<void>("add", emscripten::val("qt-window-content"));
@@ -83,8 +95,6 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
m_canvasContainer.call<void>("appendChild", m_a11yContainer);
m_a11yContainer["classList"].call<void>("add", emscripten::val("qt-window-a11y-container"));
- compositor->screen()->element().call<void>("appendChild", m_qtWindow);
-
const bool rendersTo2dContext = w->surfaceType() != QSurface::OpenGLSurface;
if (rendersTo2dContext)
m_context2d = m_canvas.call<emscripten::val>("getContext", emscripten::val("2d"));
@@ -93,8 +103,6 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
m_qtWindow.set("id", "qt-window-" + std::to_string(m_winId));
emscripten::val::module_property("specialHTMLTargets").set(canvasSelector(), m_canvas);
- m_compositor->addWindow(this);
-
const auto pointerCallback = std::function([this](emscripten::val event) {
if (processPointer(*PointerEvent::fromWeb(event)))
event.call<void>("preventDefault");
@@ -125,13 +133,16 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport,
m_keyDownCallback =
std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keydown", keyCallback);
m_keyUpCallback = std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keyup", keyCallback);
+
+ setParent(parent());
}
QWasmWindow::~QWasmWindow()
{
emscripten::val::module_property("specialHTMLTargets").delete_(canvasSelector());
- destroy();
- m_compositor->removeWindow(this);
+ m_canvasContainer.call<void>("removeChild", m_canvas);
+ m_context2d = emscripten::val::undefined();
+ commitParent(nullptr);
if (m_requestAnimationFrameId > -1)
emscripten_cancel_animation_frame(m_requestAnimationFrameId);
#if QT_CONFIG(accessibility)
@@ -167,8 +178,7 @@ void QWasmWindow::onCloseClicked()
void QWasmWindow::onNonClientAreaInteraction()
{
- if (!isActive())
- requestActivateWindow();
+ requestActivateWindow();
}
bool QWasmWindow::onNonClientEvent(const PointerEvent &event)
@@ -182,14 +192,6 @@ bool QWasmWindow::onNonClientEvent(const PointerEvent &event)
event.modifiers);
}
-void QWasmWindow::destroy()
-{
- m_qtWindow["parentElement"].call<emscripten::val>("removeChild", m_qtWindow);
-
- m_canvasContainer.call<void>("removeChild", m_canvas);
- m_context2d = emscripten::val::undefined();
-}
-
void QWasmWindow::initialize()
{
QRect rect = windowGeometry();
@@ -262,21 +264,31 @@ void QWasmWindow::setGeometry(const QRect &rect)
if (m_state.testFlag(Qt::WindowMaximized))
return platformScreen()->availableGeometry().marginsRemoved(frameMargins());
- const auto screenGeometry = screen()->geometry();
+ auto offset = rect.topLeft() - (!parent() ? screen()->geometry().topLeft() : QPoint());
+
+ // In viewport
+ auto containerGeometryInViewport =
+ QRectF::fromDOMRect(parentNode()->containerElement().call<emscripten::val>(
+ "getBoundingClientRect"))
+ .toRect();
- QRect result(rect);
- result.moveTop(std::max(std::min(rect.y(), screenGeometry.bottom()),
- screenGeometry.y() + margins.top()));
- result.setSize(
- result.size().expandedTo(windowMinimumSize()).boundedTo(windowMaximumSize()));
- return result;
+ auto rectInViewport = QRect(containerGeometryInViewport.topLeft() + offset, rect.size());
+
+ QRect cappedGeometry(rectInViewport);
+ cappedGeometry.moveTop(
+ std::max(std::min(rectInViewport.y(), containerGeometryInViewport.bottom()),
+ containerGeometryInViewport.y() + margins.top()));
+ cappedGeometry.setSize(
+ cappedGeometry.size().expandedTo(windowMinimumSize()).boundedTo(windowMaximumSize()));
+ return QRect(QPoint(rect.x(), rect.y() + cappedGeometry.y() - rectInViewport.y()),
+ rect.size());
})();
m_nonClientArea->onClientAreaWidthChange(clientAreaRect.width());
const auto frameRect =
clientAreaRect
.adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom())
- .translated(-screen()->geometry().topLeft());
+ .translated(!parent() ? -screen()->geometry().topLeft() : QPoint());
m_qtWindow["style"].set("left", std::to_string(frameRect.left()) + "px");
m_qtWindow["style"].set("top", std::to_string(frameRect.top()) + "px");
@@ -337,13 +349,13 @@ QMargins QWasmWindow::frameMargins() const
void QWasmWindow::raise()
{
- m_compositor->raise(this);
+ bringToTop();
invalidate();
}
void QWasmWindow::lower()
{
- m_compositor->lower(this);
+ sendToBottom();
invalidate();
}
@@ -374,6 +386,11 @@ void QWasmWindow::onActivationChanged(bool active)
void QWasmWindow::setWindowFlags(Qt::WindowFlags flags)
{
+ if (flags.testFlag(Qt::WindowStaysOnTopHint) != m_flags.testFlag(Qt::WindowStaysOnTopHint)
+ || flags.testFlag(Qt::WindowStaysOnBottomHint)
+ != m_flags.testFlag(Qt::WindowStaysOnBottomHint)) {
+ onPositionPreferenceChanged(positionPreferenceFromWindowFlags(flags));
+ }
m_flags = flags;
dom::syncCSSClassWith(m_qtWindow, "frameless", !hasFrame());
dom::syncCSSClassWith(m_qtWindow, "has-border", hasBorder());
@@ -455,6 +472,12 @@ void QWasmWindow::applyWindowState()
setGeometry(newGeom);
}
+void QWasmWindow::commitParent(QWasmWindowTreeNode *parent)
+{
+ onParentChanged(m_commitedParent, parent, positionPreferenceFromWindowFlags(window()->flags()));
+ m_commitedParent = parent;
+}
+
bool QWasmWindow::processKey(const KeyEvent &event)
{
constexpr bool ProceedToNativeEvent = false;
@@ -603,8 +626,8 @@ void QWasmWindow::requestActivateWindow()
return;
}
- if (window()->isTopLevel())
- raise();
+ raise();
+ setAsActiveNode();
if (!QWasmIntegration::get()->inputContext())
m_canvas.call<void>("focus");
@@ -652,9 +675,41 @@ void QWasmWindow::setMask(const QRegion &region)
m_qtWindow["style"].set("clipPath", emscripten::val(cssClipPath.str()));
}
+void QWasmWindow::setParent(const QPlatformWindow *)
+{
+ commitParent(parentNode());
+}
+
std::string QWasmWindow::canvasSelector() const
{
return "!qtwindow" + std::to_string(m_winId);
}
+emscripten::val QWasmWindow::containerElement()
+{
+ return m_windowContents;
+}
+
+QWasmWindowTreeNode *QWasmWindow::parentNode()
+{
+ if (parent())
+ return static_cast<QWasmWindow *>(parent());
+ return platformScreen();
+}
+
+QWasmWindow *QWasmWindow::asWasmWindow()
+{
+ return this;
+}
+
+void QWasmWindow::onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current,
+ QWasmWindowStack::PositionPreference positionPreference)
+{
+ if (previous)
+ previous->containerElement().call<void>("removeChild", m_qtWindow);
+ if (current)
+ current->containerElement().call<void>("appendChild", m_qtWindow);
+ QWasmWindowTreeNode::onParentChanged(previous, current, positionPreference);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index 7db125f48e8..bfeb46972ad 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -11,6 +11,8 @@
#include "qwasmscreen.h"
#include "qwasmcompositor.h"
#include "qwasmwindownonclientarea.h"
+#include "qwasmwindowstack.h"
+#include "qwasmwindowtreenode.h"
#include <QtCore/private/qstdweb_p.h>
#include "QtGui/qopenglcontext.h"
@@ -37,7 +39,7 @@ struct PointerEvent;
class QWasmDeadKeySupport;
struct WheelEvent;
-class QWasmWindow final : public QPlatformWindow
+class QWasmWindow final : public QPlatformWindow, public QWasmWindowTreeNode
{
public:
QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, QWasmCompositor *compositor,
@@ -46,7 +48,6 @@ public:
QSurfaceFormat format() const override;
- void destroy();
void paint();
void setZOrder(int order);
void setWindowCursor(QByteArray cssCursorName);
@@ -81,6 +82,7 @@ public:
bool setMouseGrabEnabled(bool grab) final;
bool windowEvent(QEvent *event) final;
void setMask(const QRegion &region) final;
+ void setParent(const QPlatformWindow *window) final;
QWasmScreen *platformScreen() const;
void setBackingStore(QWasmBackingStore *store) { m_backingStore = store; }
@@ -92,10 +94,19 @@ public:
emscripten::val a11yContainer() const { return m_a11yContainer; }
emscripten::val inputHandlerElement() const { return m_windowContents; }
+ // QWasmWindowTreeNode:
+ emscripten::val containerElement() final;
+ QWasmWindowTreeNode *parentNode() final;
+
private:
friend class QWasmScreen;
static constexpr auto minSizeForRegularWindows = 100;
+ // QWasmWindowTreeNode:
+ QWasmWindow *asWasmWindow() final;
+ void onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current,
+ QWasmWindowStack::PositionPreference positionPreference) final;
+
void invalidate();
bool hasFrame() const;
bool hasTitleBar() const;
@@ -103,6 +114,7 @@ private:
bool hasShadow() const;
bool hasMaximizeButton() const;
void applyWindowState();
+ void commitParent(QWasmWindowTreeNode *parent);
bool processKey(const KeyEvent &event);
bool processPointer(const PointerEvent &event);
@@ -126,6 +138,8 @@ private:
std::unique_ptr<NonClientArea> m_nonClientArea;
std::unique_ptr<ClientArea> m_clientArea;
+ QWasmWindowTreeNode *m_commitedParent = nullptr;
+
std::unique_ptr<qstdweb::EventCallback> m_keyDownCallback;
std::unique_ptr<qstdweb::EventCallback> m_keyUpCallback;
diff --git a/src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp b/src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp
index 68f14425109..07c90d5fe92 100644
--- a/src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindownonclientarea.cpp
@@ -408,9 +408,15 @@ bool TitleBar::onDoubleClick()
QPointF TitleBar::clipPointWithScreen(const QPointF &pointInTitleBarCoords) const
{
- auto *screen = m_window->platformScreen();
- return screen->clipPoint(screen->mapFromLocal(
- dom::mapPoint(m_element, screen->element(), pointInTitleBarCoords)));
+ auto containerRect =
+ QRectF::fromDOMRect(m_window->parentNode()->containerElement().call<emscripten::val>(
+ "getBoundingClientRect"));
+ const auto p = dom::mapPoint(m_element, m_window->parentNode()->containerElement(),
+ pointInTitleBarCoords);
+
+ auto result = QPointF(qBound(0., qreal(p.x()), containerRect.width()),
+ qBound(0., qreal(p.y()), containerRect.height()));
+ return m_window->parent() ? result : m_window->platformScreen()->mapFromLocal(result).toPoint();
}
NonClientArea::NonClientArea(QWasmWindow *window, emscripten::val qtWindowElement)
diff --git a/src/plugins/platforms/wasm/qwasmwindowstack.cpp b/src/plugins/platforms/wasm/qwasmwindowstack.cpp
index 098f1c1ff20..d3769c7a1bb 100644
--- a/src/plugins/platforms/wasm/qwasmwindowstack.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindowstack.cpp
@@ -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);
-
- m_topWindowChangedCallback();
+ 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_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
diff --git a/src/plugins/platforms/wasm/qwasmwindowstack.h b/src/plugins/platforms/wasm/qwasmwindowstack.h
index e98ebf904c2..32090b9039f 100644
--- a/src/plugins/platforms/wasm/qwasmwindowstack.h
+++ b/src/plugins/platforms/wasm/qwasmwindowstack.h
@@ -24,7 +24,7 @@ class QWasmWindow;
Q_AUTOTEST_EXPORT class 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 };
-
- QWasmWindow *rootWindow() const;
- StorageType::iterator regularWindowsBegin();
+ PositionPreference getWindowPositionPreference(StorageType::iterator windowIt) const;
- 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
diff --git a/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp b/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp
new file mode 100644
index 00000000000..e16410dcde2
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindowtreenode.cpp
@@ -0,0 +1,104 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "qwasmwindowtreenode.h"
+
+#include "qwasmwindow.h"
+
+QWasmWindowTreeNode::QWasmWindowTreeNode()
+ : m_childStack(std::bind(&QWasmWindowTreeNode::onTopWindowChanged, this))
+{
+}
+
+QWasmWindowTreeNode::~QWasmWindowTreeNode() = default;
+
+void QWasmWindowTreeNode::onParentChanged(QWasmWindowTreeNode *previousParent,
+ QWasmWindowTreeNode *currentParent,
+ QWasmWindowStack::PositionPreference positionPreference)
+{
+ auto *window = asWasmWindow();
+ if (previousParent) {
+ previousParent->m_childStack.removeWindow(window);
+ previousParent->onSubtreeChanged(QWasmWindowTreeNodeChangeType::NodeRemoval, previousParent,
+ window);
+ }
+
+ if (currentParent) {
+ currentParent->m_childStack.pushWindow(window, positionPreference);
+ currentParent->onSubtreeChanged(QWasmWindowTreeNodeChangeType::NodeInsertion, currentParent,
+ window);
+ }
+}
+
+QWasmWindow *QWasmWindowTreeNode::asWasmWindow()
+{
+ return nullptr;
+}
+
+void QWasmWindowTreeNode::onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType,
+ QWasmWindowTreeNode *parent, QWasmWindow *child)
+{
+ if (changeType == QWasmWindowTreeNodeChangeType::NodeInsertion && parent == this
+ && m_childStack.topWindow()) {
+ m_childStack.topWindow()->requestActivateWindow();
+ }
+
+ if (parentNode())
+ parentNode()->onSubtreeChanged(changeType, parent, child);
+}
+
+void QWasmWindowTreeNode::setWindowZOrder(QWasmWindow *window, int z)
+{
+ window->setZOrder(z);
+}
+
+void QWasmWindowTreeNode::onPositionPreferenceChanged(
+ QWasmWindowStack::PositionPreference positionPreference)
+{
+ if (parentNode()) {
+ parentNode()->m_childStack.windowPositionPreferenceChanged(asWasmWindow(),
+ positionPreference);
+ }
+}
+
+void QWasmWindowTreeNode::setAsActiveNode()
+{
+ if (parentNode())
+ parentNode()->setActiveChildNode(asWasmWindow());
+}
+
+void QWasmWindowTreeNode::bringToTop()
+{
+ if (!parentNode())
+ return;
+ parentNode()->m_childStack.raise(asWasmWindow());
+ parentNode()->bringToTop();
+}
+
+void QWasmWindowTreeNode::sendToBottom()
+{
+ if (!parentNode())
+ return;
+ m_childStack.lower(asWasmWindow());
+}
+
+void QWasmWindowTreeNode::onTopWindowChanged()
+{
+ constexpr int zOrderForElementInFrontOfScreen = 3;
+ int z = zOrderForElementInFrontOfScreen;
+ std::for_each(m_childStack.rbegin(), m_childStack.rend(),
+ [this, &z](QWasmWindow *window) { setWindowZOrder(window, z++); });
+}
+
+void QWasmWindowTreeNode::setActiveChildNode(QWasmWindow *activeChild)
+{
+ m_activeChild = activeChild;
+
+ auto it = m_childStack.begin();
+ if (it == m_childStack.end())
+ return;
+ for (; it != m_childStack.end(); ++it)
+ (*it)->onActivationChanged(*it == m_activeChild);
+
+ setAsActiveNode();
+}
diff --git a/src/plugins/platforms/wasm/qwasmwindowtreenode.h b/src/plugins/platforms/wasm/qwasmwindowtreenode.h
new file mode 100644
index 00000000000..344fdb43cbd
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindowtreenode.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QWASMWINDOWTREENODE_H
+#define QWASMWINDOWTREENODE_H
+
+#include "qwasmwindowstack.h"
+
+namespace emscripten {
+class val;
+}
+
+class QWasmWindow;
+
+enum class QWasmWindowTreeNodeChangeType {
+ NodeInsertion,
+ NodeRemoval,
+};
+
+class QWasmWindowTreeNode
+{
+public:
+ QWasmWindowTreeNode();
+ virtual ~QWasmWindowTreeNode();
+
+ virtual emscripten::val containerElement() = 0;
+ virtual QWasmWindowTreeNode *parentNode() = 0;
+
+protected:
+ virtual void onParentChanged(QWasmWindowTreeNode *previous, QWasmWindowTreeNode *current,
+ QWasmWindowStack::PositionPreference positionPreference);
+ virtual QWasmWindow *asWasmWindow();
+ virtual void onSubtreeChanged(QWasmWindowTreeNodeChangeType changeType,
+ QWasmWindowTreeNode *parent, QWasmWindow *child);
+ virtual void setWindowZOrder(QWasmWindow *window, int z);
+
+ void onPositionPreferenceChanged(QWasmWindowStack::PositionPreference positionPreference);
+ void setAsActiveNode();
+ void bringToTop();
+ void sendToBottom();
+
+ const QWasmWindowStack &childStack() const { return m_childStack; }
+ QWasmWindow *activeChild() const { return m_activeChild; }
+
+private:
+ void onTopWindowChanged();
+ void setActiveChildNode(QWasmWindow *activeChild);
+
+ QWasmWindowStack m_childStack;
+ QWasmWindow *m_activeChild = nullptr;
+};
+
+#endif // QWASMWINDOWTREENODE_H