From 4ff5a571b3e7fc3c2c83b7f96a6899197be72b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Wed, 24 Nov 2021 15:42:19 +0100 Subject: [PATCH] wasm: Support non-canvas container elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support specifying that Qt should use a div element as the root container, in which case canvas management is moved to Qt C++ code. This enables us to take ownership of the canvas and its configuration. In addition, it allows creating child elements for the Qt container (canvas element children have a special role are as fallback elements displayed in case the browser can’t show the canvas) Remove support for reading Module.canvas, which was deprecated in Qt 5. Add support for reading Module.qtContainerElements, which can be either a canvas element (legacy) or a div element (preferred). Deprecate qtCanvasElements and print warning on usage. Change QWasmScreen to accept either a canvas or any html element. In the latter case, create the canvas and configure it as required by Qt. Change-Id: I57df8fb5772b2bfbba081af3f572b8b0e7d51897 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/wasm/qtloader.js | 6 +-- .../platforms/wasm/qwasmcompositor.cpp | 2 - src/plugins/platforms/wasm/qwasmcompositor.h | 2 +- .../platforms/wasm/qwasmintegration.cpp | 39 +++++++++++++------ src/plugins/platforms/wasm/qwasmscreen.cpp | 32 ++++++++++++++- src/plugins/platforms/wasm/qwasmscreen.h | 4 +- 6 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js index 08bf0ed421c..4f5bfcc2d87 100644 --- a/src/plugins/platforms/wasm/qtloader.js +++ b/src/plugins/platforms/wasm/qtloader.js @@ -142,7 +142,7 @@ function QtLoader(config) // Qt properties. These are propagated to the Emscripten module after // it has been created. - self.qtCanvasElements = undefined; + self.qtContainerElements = undefined; self.qtFontDpi = 96; function webAssemblySupported() { @@ -431,13 +431,13 @@ function QtLoader(config) module.ENV[key.toUpperCase()] = value; } // Propagate Qt module properties - module.qtCanvasElements = self.qtCanvasElements; + module.qtContainerElements = self.qtContainerElements; module.qtFontDpi = self.qtFontDpi; }); self.moduleConfig.mainScriptUrlOrBlob = new Blob([emscriptenModuleSource], {type: 'text/javascript'}); - self.qtCanvasElements = config.canvasElements; + self.qtContainerElements = config.canvasElements; config.restart = function() { diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index fe0ea81692a..5c5fc80adba 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -102,8 +102,6 @@ QWasmCompositor::QWasmCompositor(QWasmScreen *screen) QPointingDevice::Capability::Position | QPointingDevice::Capability::Area | QPointingDevice::Capability::NormalizedPosition, 10, 0); QWindowSystemInterface::registerInputDevice(touchDevice); - - initEventHandlers(); } QWasmCompositor::~QWasmCompositor() diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index d405f8035b8..27f518635d6 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -72,6 +72,7 @@ class QWasmCompositor : public QObject public: QWasmCompositor(QWasmScreen *screen); ~QWasmCompositor(); + void initEventHandlers(); void deregisterEventHandlers(); void destroy(); @@ -160,7 +161,6 @@ private slots: void frame(); private: - void initEventHandlers(); void notifyTopWindowChanged(QWasmWindow *window); void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 5a09cc710a6..cf3a506ef8c 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -123,19 +123,34 @@ QWasmIntegration::QWasmIntegration() platform = AndroidPlatform; } - // We expect that qtloader.js has populated Module.qtCanvasElements with one or more canvases. - emscripten::val qtCanvaseElements = val::module_property("qtCanvasElements"); - emscripten::val canvas = val::module_property("canvas"); // TODO: remove for Qt 6.0 - - if (!qtCanvaseElements.isUndefined()) { - int screenCount = qtCanvaseElements["length"].as(); - for (int i = 0; i < screenCount; ++i) { - addScreen(qtCanvaseElements[i].as()); + // Create screens for container elements. Each container element can be a div element (preferred), + // or a canvas element (legacy). Qt versions prior to 6.x read the "qtCanvasElements" module property, + // which we continue to do to preserve compatibility. The preferred property is now "qtContainerElements". + emscripten::val qtContainerElements = val::module_property("qtContainerElements"); + emscripten::val qtCanvasElements = val::module_property("qtCanvasElements"); + if (!qtContainerElements.isUndefined()) { + emscripten::val length = qtContainerElements["length"]; + int count = length.as(); + if (length.isUndefined()) + qWarning("qtContainerElements does not have the length property set. Qt expects an array of html elements (possibly containing one element only)"); + for (int i = 0; i < count; ++i) { + emscripten::val element = qtContainerElements[i].as(); + if (element.isNull() ||element.isUndefined()) { + qWarning() << "Skipping null or undefined element in qtContainerElements"; + } else { + addScreen(element); + } } - } else if (!canvas.isUndefined()) { - qWarning() << "Module.canvas is deprecated. A future version of Qt will stop reading this property. " - << "Instead, set Module.qtCanvasElements to be an array of canvas elements, or use qtloader.js."; - addScreen(canvas); + } else if (!qtCanvasElements.isUndefined()) { + qWarning() << "The qtCanvaseElements property is deprecated. Qt will stop reading" + << "it in some future veresion, please use qtContainerElements instead"; + emscripten::val length = qtCanvasElements["length"]; + int count = length.as(); + for (int i = 0; i < count; ++i) + addScreen(qtCanvasElements[i].as()); + } else { + // No screens, which may or may not be intended + qWarning() << "Note: The qtContainerElements module property was not set. Proceeding with no screens."; } // install browser window resize handler diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index 2ee56bc5aec..52da924f8ff 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -52,11 +52,30 @@ QT_BEGIN_NAMESPACE const char * QWasmScreen::m_canvasResizeObserverCallbackContextPropertyName = "data-qtCanvasResizeObserverCallbackContext"; -QWasmScreen::QWasmScreen(const emscripten::val &canvas) - : m_canvas(canvas) +QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas) + : m_container(containerOrCanvas) + , m_canvas(emscripten::val::undefined()) , m_compositor(new QWasmCompositor(this)) , m_eventTranslator(new QWasmEventTranslator()) { + // Each screen is backed by a html canvas element. Use either + // a user-supplied canvas or create one as a child of the user- + // supplied root element. + std::string tagName = containerOrCanvas["tagName"].as(); + if (tagName == "CANVAS" || tagName == "canvas") { + m_canvas = containerOrCanvas; + } else { + // Create the canvas (for the correct document) as a child of the container + m_canvas = containerOrCanvas["ownerDocument"].call("createElement", std::string("canvas")); + containerOrCanvas.call("appendChild", m_canvas); + m_canvas.set("id", std::string("qtcanvas_") + std::to_string(uint32_t(this))); + + // Make the canvas occupy 100% of parent + emscripten::val style = m_canvas["style"]; + style.set("width", std::string("100%")); + style.set("height", std::string("100%")); + } + // Configure canvas emscripten::val style = m_canvas["style"]; style.set("border", std::string("0px none")); @@ -78,6 +97,10 @@ QWasmScreen::QWasmScreen(const emscripten::val &canvas) event.call("preventDefault"); }); + // Install event handlers on the container/canvas. This must be + // done after the canvas has been created above. + m_compositor->initEventHandlers(); + updateQScreenAndCanvasRenderSize(); m_canvas.call("focus"); } @@ -113,6 +136,11 @@ QWasmEventTranslator *QWasmScreen::eventTranslator() return m_eventTranslator.get(); } +emscripten::val QWasmScreen::container() const +{ + return m_container; +} + emscripten::val QWasmScreen::canvas() const { return m_canvas; diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index 41b5aeeb2f4..4fb387f10a2 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -53,12 +53,13 @@ class QWasmScreen : public QObject, public QPlatformScreen { Q_OBJECT public: - QWasmScreen(const emscripten::val &canvas); + QWasmScreen(const emscripten::val &containerOrCanvas); ~QWasmScreen(); void destroy(); static QWasmScreen *get(QPlatformScreen *screen); static QWasmScreen *get(QScreen *screen); + emscripten::val container() const; emscripten::val canvas() const; QString canvasId() const; @@ -86,6 +87,7 @@ public slots: void setGeometry(const QRect &rect); private: + emscripten::val m_container; emscripten::val m_canvas; std::unique_ptr m_compositor; std::unique_ptr m_eventTranslator;