summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/wasm
diff options
context:
space:
mode:
authorMorten Sørvig <morten.sorvig@qt.io>2022-12-07 12:57:47 +0100
committerMorten Johan Sørvig <morten.sorvig@qt.io>2022-12-27 14:30:25 +0000
commit42d4619967688e4f313f5508de7594f0c0c976ff (patch)
tree8e61a4e0538530af6edb43b755302824a489b6a5 /src/plugins/platforms/wasm
parentf4dd67461d9873cdbfe7bef970477366047924d7 (diff)
wasm: add end-user accessibillty opt-in
We want to make support for screen readers available incrementally to avoid destabilizing existing functionality. Accomplish this by initially adding a single "enable screen reader" button only. When pressed, this button will remove itself and populate the page with the actual accessibility elements. Pick-to: 6.5 Change-Id: I65036d249c408d4dc1fa75e8c807d9b7300e4722 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Diffstat (limited to 'src/plugins/platforms/wasm')
-rw-r--r--src/plugins/platforms/wasm/qwasmaccessibility.cpp123
-rw-r--r--src/plugins/platforms/wasm/qwasmaccessibility.h21
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp7
3 files changed, 133 insertions, 18 deletions
diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
index eb3e30b140..cb1e3f135c 100644
--- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp
+++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
@@ -3,6 +3,7 @@
#include "qwasmaccessibility.h"
#include "qwasmscreen.h"
+#include "qwasmwindow.h"
#include <QtGui/qwindow.h>
@@ -18,33 +19,105 @@ Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
// events. In addition or alternatively, we could also walk the accessibility tree
// from setRootObject().
+namespace {
+QWasmWindow *asWasmWindow(QWindow *window)
+{
+ return static_cast<QWasmWindow*>(window->handle());
+}
+} // namespace
QWasmAccessibility::QWasmAccessibility()
{
+ s_instance = this;
}
QWasmAccessibility::~QWasmAccessibility()
{
+ s_instance = nullptr;
+}
+
+QWasmAccessibility *QWasmAccessibility::s_instance = nullptr;
+
+QWasmAccessibility* QWasmAccessibility::get()
+{
+ return s_instance;
+}
+
+void QWasmAccessibility::addAccessibilityEnableButton(QWindow *window)
+{
+ get()->addAccessibilityEnableButtonImpl(window);
+}
+
+void QWasmAccessibility::removeAccessibilityEnableButton(QWindow *window)
+{
+ get()->removeAccessibilityEnableButtonImpl(window);
+}
+
+void QWasmAccessibility::addAccessibilityEnableButtonImpl(QWindow *window)
+{
+ if (m_accessibilityEnabled)
+ return;
+
+ emscripten::val container = getContainer(window);
+ emscripten::val document = getDocument(container);
+ emscripten::val button = document.call<emscripten::val>("createElement", std::string("button"));
+ button.set("innerText", std::string("Enable Screen Reader"));
+ container.call<void>("appendChild", button);
+
+ emscripten::val style = button["style"];
+ style.set("width", "100%");
+ style.set("height", "100%");
+
+ auto enableContext = std::make_tuple(button, std::make_unique<qstdweb::EventCallback>
+ (button, std::string("click"), [this](emscripten::val) { enableAccessibility(); }));
+ m_enableButtons.insert(std::make_pair(window, std::move(enableContext)));
+}
+
+void QWasmAccessibility::removeAccessibilityEnableButtonImpl(QWindow *window)
+{
+ auto it = m_enableButtons.find(window);
+ if (it == m_enableButtons.end())
+ return;
+
+ // Remove button
+ auto [element, callback] = it->second;
+ Q_UNUSED(callback);
+ element["parentElement"].call<void>("removeChild", element);
+ m_enableButtons.erase(it);
+}
+void QWasmAccessibility::enableAccessibility()
+{
+ // Enable accessibility globally for the applicaton. Remove all "enable"
+ // buttons and populate the accessibility tree, starting from the root object.
+
+ Q_ASSERT(!m_accessibilityEnabled);
+ m_accessibilityEnabled = true;
+ for (const auto& [key, value] : m_enableButtons) {
+ const auto &[element, callback] = value;
+ Q_UNUSED(key);
+ Q_UNUSED(callback);
+ element["parentElement"].call<void>("removeChild", element);
+ }
+ m_enableButtons.clear();
+ populateAccessibilityTree(QAccessible::queryAccessibleInterface(m_rootObject));
+}
+
+emscripten::val QWasmAccessibility::getContainer(QWindow *window)
+{
+ return window ? asWasmWindow(window)->a11yContainer() : emscripten::val::undefined();
}
emscripten::val QWasmAccessibility::getContainer(QAccessibleInterface *iface)
{
- // Get to QWasmScreen::container(), return undefined element if unable to
- QWindow *window = iface->window();
- if (!window)
- return emscripten::val::undefined();
- QWasmScreen *screen = QWasmScreen::get(window->screen());
- if (!screen)
- return emscripten::val::undefined();
- return screen->element();
+ return getContainer(iface->window());
}
emscripten::val QWasmAccessibility::getDocument(const emscripten::val &container)
{
if (container.isUndefined())
- return emscripten::val::undefined();
+ return emscripten::val::global("document");
return container["ownerDocument"];
}
@@ -62,7 +135,7 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac
// Get the correct html document for the container, or fall back
// to the global document. TODO: Does using the correct document actually matter?
- emscripten::val document = container.isUndefined() ? emscripten::val::global("document") : getDocument(container);
+ emscripten::val document = getDocument(container);
// Translate the Qt a11y elemen role into html element type + ARIA role.
// Here we can either create <div> elements with a spesific ARIA role,
@@ -136,14 +209,13 @@ void QWasmAccessibility::setHtmlElementVisibility(QAccessibleInterface *iface, b
void QWasmAccessibility::setHtmlElementGeometry(QAccessibleInterface *iface)
{
emscripten::val element = ensureHtmlElement(iface);
- setHtmlElementGeometry(iface, element);
+ setHtmlElementGeometry(element, iface->rect());
}
-void QWasmAccessibility::setHtmlElementGeometry(QAccessibleInterface *iface, emscripten::val element)
+void QWasmAccessibility::setHtmlElementGeometry(emscripten::val element, QRect geometry)
{
// Position the element using "position: absolute" in order to place
// it under the corresponding Qt element in the screen.
- QRect geometry = iface->rect();
emscripten::val style = element["style"];
style.set("position", std::string("absolute"));
style.set("z-index", std::string("-1")); // FIXME: "0" should be sufficient to order beheind the
@@ -190,8 +262,27 @@ void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event)
}
}
+void QWasmAccessibility::populateAccessibilityTree(QAccessibleInterface *iface)
+{
+ if (!iface)
+ return;
+
+ // Create html element for the interface, sync up properties.
+ ensureHtmlElement(iface);
+ const bool visible = !iface->state().invisible;
+ setHtmlElementVisibility(iface, visible);
+ setHtmlElementGeometry(iface);
+ setHtmlElementTextName(iface);
+
+ for (int i = 0; i < iface->childCount(); ++i)
+ populateAccessibilityTree(iface->child(i));
+}
+
void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
{
+ if (!m_accessibilityEnabled)
+ return;
+
QAccessibleInterface *iface = event->accessibleInterface();
if (!iface) {
qWarning() << "notifyAccessibilityUpdate with null a11y interface" ;
@@ -237,11 +328,9 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
};
}
-void QWasmAccessibility::setRootObject(QObject *o)
+void QWasmAccessibility::setRootObject(QObject *root)
{
- qCDebug(lcQpaAccessibility) << "setRootObject" << o;
- QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o);
- Q_UNUSED(iface)
+ m_rootObject = root;
}
void QWasmAccessibility::initialize()
diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.h b/src/plugins/platforms/wasm/qwasmaccessibility.h
index 6d5330c560..2c76f41c00 100644
--- a/src/plugins/platforms/wasm/qwasmaccessibility.h
+++ b/src/plugins/platforms/wasm/qwasmaccessibility.h
@@ -5,11 +5,14 @@
#define QWASMACCESIBILITY_H
#include <QtCore/qhash.h>
+#include <private/qstdweb_p.h>
#include <qpa/qplatformaccessibility.h>
#include <emscripten/val.h>
#include <QLoggingCategory>
+#include <map>
+
Q_DECLARE_LOGGING_CATEGORY(lcQpaAccessibility)
class QWasmAccessibility : public QPlatformAccessibility
@@ -18,6 +21,17 @@ public:
QWasmAccessibility();
~QWasmAccessibility();
+ static QWasmAccessibility* get();
+
+ static void addAccessibilityEnableButton(QWindow *window);
+ static void removeAccessibilityEnableButton(QWindow *window);
+
+private:
+ void addAccessibilityEnableButtonImpl(QWindow *window);
+ void removeAccessibilityEnableButtonImpl(QWindow *window);
+ void enableAccessibility();
+
+ static emscripten::val getContainer(QWindow *window);
static emscripten::val getContainer(QAccessibleInterface *iface);
static emscripten::val getDocument(const emscripten::val &container);
static emscripten::val getDocument(QAccessibleInterface *iface);
@@ -27,19 +41,24 @@ public:
emscripten::val ensureHtmlElement(QAccessibleInterface *iface);
void setHtmlElementVisibility(QAccessibleInterface *iface, bool visible);
void setHtmlElementGeometry(QAccessibleInterface *iface);
- void setHtmlElementGeometry(QAccessibleInterface *iface, emscripten::val element);
+ void setHtmlElementGeometry(emscripten::val element, QRect geometry);
void setHtmlElementTextName(QAccessibleInterface *iface);
void handleStaticTextUpdate(QAccessibleEvent *event);
void handleButtonUpdate(QAccessibleEvent *event);
void handleCheckBoxUpdate(QAccessibleEvent *event);
+ void populateAccessibilityTree(QAccessibleInterface *iface);
void notifyAccessibilityUpdate(QAccessibleEvent *event) override;
void setRootObject(QObject *o) override;
void initialize() override;
void cleanup() override;
private:
+ static QWasmAccessibility *s_instance;
+ QObject *m_rootObject = nullptr;
+ bool m_accessibilityEnabled = false;
+ std::map<QWindow *, std::tuple<emscripten::val, std::shared_ptr<qstdweb::EventCallback>>> m_enableButtons;
QHash<QAccessibleInterface *, emscripten::val> m_elements;
};
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 85920d618a..aa229b5776 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -19,6 +19,7 @@
#include "qwasmevent.h"
#include "qwasmeventdispatcher.h"
#include "qwasmstring.h"
+#include "qwasmaccessibility.h"
#include <iostream>
#include <emscripten/val.h>
@@ -93,6 +94,7 @@ QWasmWindow::~QWasmWindow()
m_compositor->removeWindow(this);
if (m_requestAnimationFrameId > -1)
emscripten_cancel_animation_frame(m_requestAnimationFrameId);
+ QWasmAccessibility::removeAccessibilityEnableButton(window());
}
void QWasmWindow::onRestoreClicked()
@@ -177,6 +179,11 @@ void QWasmWindow::initialize()
setWindowIcon(window()->icon());
m_normalGeometry = rect;
QPlatformWindow::setGeometry(m_normalGeometry);
+
+ // Add accessibility-enable button. The user can activate this
+ // button to opt-in to accessibility.
+ if (window()->isTopLevel())
+ QWasmAccessibility::addAccessibilityEnableButton(window());
}
QWasmScreen *QWasmWindow::platformScreen() const