wasm: Support non-canvas container elements

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ø <tor.arne.vestbo@qt.io>
This commit is contained in:
Morten Johan Sørvig 2021-11-24 15:42:19 +01:00
parent aa4a2bb1e1
commit 4ff5a571b3
6 changed files with 64 additions and 21 deletions

View File

@ -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() {

View File

@ -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()

View File

@ -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);

View File

@ -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<int>();
for (int i = 0; i < screenCount; ++i) {
addScreen(qtCanvaseElements[i].as<emscripten::val>());
// 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<int>();
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<emscripten::val>();
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<int>();
for (int i = 0; i < count; ++i)
addScreen(qtCanvasElements[i].as<emscripten::val>());
} 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

View File

@ -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<std::string>();
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<emscripten::val>("createElement", std::string("canvas"));
containerOrCanvas.call<void>("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<void>("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<void>("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;

View File

@ -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<QWasmCompositor> m_compositor;
std::unique_ptr<QWasmEventTranslator> m_eventTranslator;