diff options
author | Morten Sørvig <morten.sorvig@qt.io> | 2022-12-07 12:57:47 +0100 |
---|---|---|
committer | Morten Johan Sørvig <morten.sorvig@qt.io> | 2022-12-27 14:30:25 +0000 |
commit | 42d4619967688e4f313f5508de7594f0c0c976ff (patch) | |
tree | 8e61a4e0538530af6edb43b755302824a489b6a5 /src/plugins/platforms/wasm | |
parent | f4dd67461d9873cdbfe7bef970477366047924d7 (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.cpp | 123 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmaccessibility.h | 21 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.cpp | 7 |
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 |