diff options
Diffstat (limited to 'src/plugins/platforms/wasm')
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmaccessibility.cpp | 218 | ||||
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmaccessibility.h | 10 |
2 files changed, 160 insertions, 68 deletions
diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp index a87c33c8346..56a7c466dcd 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp +++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp @@ -322,6 +322,16 @@ void QWasmAccessibility::setProperty(emscripten::val element, const std::string element.set(property, val); } +void QWasmAccessibility::setNamedAttribute(QAccessibleInterface *iface, const std::string &attribute, QAccessible::Text text) +{ + const emscripten::val element = getHtmlElement(iface); + setAttribute(element, attribute, iface->text(text).toStdString()); +} +void QWasmAccessibility::setNamedProperty(QAccessibleInterface *iface, const std::string &property, QAccessible::Text text) +{ + const emscripten::val element = getHtmlElement(iface); + setProperty(element, property, iface->text(text).toStdString()); +} void QWasmAccessibility::addEventListener(QAccessibleInterface *iface, emscripten::val element, const char *eventType) { @@ -331,6 +341,17 @@ void QWasmAccessibility::addEventListener(QAccessibleInterface *iface, emscripte true); } +void QWasmAccessibility::sendEvent(QAccessibleInterface *iface, QAccessible::Event eventType) +{ + if (iface->object()) { + QAccessibleEvent event(iface->object(), eventType); + handleUpdateByInterfaceRole(&event); + } else { + QAccessibleEvent event(iface, eventType); + handleUpdateByInterfaceRole(&event); + } +} + emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *iface) { // Get the html container element for the interface; this depends on which @@ -356,12 +377,41 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac addEventListener(iface, element, "click"); } break; + case QAccessible::Grouping: case QAccessible::CheckBox: { - element = document.call<emscripten::val>("createElement", std::string("input")); - setAttribute(element, "type", "checkbox"); - setAttribute(element, "checked", iface->state().checked); - setProperty(element, "indeterminate", iface->state().checkStateMixed); - addEventListener(iface, element, "change"); + // Three cases: + // 1) role=CheckBox, childCount() == 0 -> Checkbox + // 2) role=CheckBox, childCount() > 0 -> GroupBox w/checkbox + // 3) role=Grouping -> GroupBox w/label + + emscripten::val checkbox = emscripten::val::undefined(); + if (iface->role() == QAccessible::CheckBox) { + checkbox = document.call<emscripten::val>("createElement", std::string("input")); + setAttribute(checkbox, "type", "checkbox"); + setAttribute(checkbox, "checked", iface->state().checked); + setProperty(checkbox, "indeterminate", iface->state().checkStateMixed); + addEventListener(iface, checkbox, "change"); + } + + if (iface->childCount() > 0 || iface->role() == QAccessible::Grouping) { + auto label = document.call<emscripten::val>("createElement", std::string("span")); + + const std::string id = QString::asprintf("lbid%p", iface).toStdString(); + setAttribute(label, "id", id); + + element = document.call<emscripten::val>("createElement", std::string("div")); + element.call<void>("appendChild", label); + + setAttribute(element, "role", "group"); + setAttribute(element, "aria-labelledby", id); + + if (!checkbox.isUndefined()) { + element.call<void>("appendChild", checkbox); + addEventListener(iface, checkbox, "focus"); + } + } else { + element = checkbox; + } } break; case QAccessible::Switch: { @@ -484,11 +534,11 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac m_elements[iface] = element; setHtmlElementGeometry(iface); - setHtmlElementTextName(iface); setHtmlElementDisabled(iface); setHtmlElementVisibility(iface, !iface->state().invisible); handleIdentifierUpdate(iface); handleDescriptionChanged(iface); + sendEvent(iface, QAccessible::NameChanged); linkToParent(iface); // Link in child elements @@ -586,14 +636,7 @@ void QWasmAccessibility::linkToParent(QAccessibleInterface *iface) void QWasmAccessibility::setHtmlElementVisibility(QAccessibleInterface *iface, bool visible) { emscripten::val element = getHtmlElement(iface); - - if (visible) { - setAttribute(element, "aria-hidden", false); - setAttribute(element, "tabindex", ""); - } else { - setAttribute(element, "aria-hidden", true); // aria-hidden mean completely hidden; maybe some sort of soft-hidden should be used. - setAttribute(element, "tabindex", "-1"); - } + setAttribute(element, "aria-hidden", !visible); } void QWasmAccessibility::setHtmlElementGeometry(QAccessibleInterface *iface) @@ -631,28 +674,6 @@ void QWasmAccessibility::setHtmlElementGeometry(emscripten::val element, QRect g style.set("height", std::to_string(geometry.height()) + "px"); } -void QWasmAccessibility::setHtmlElementTextName(QAccessibleInterface *iface) -{ - const emscripten::val element = getHtmlElement(iface); - const QString name = iface->text(QAccessible::Name); - const QString value = iface->text(QAccessible::Value); - - // A <div> cannot contain aria-label - if (iface->role() == QAccessible::StaticText) - setProperty(element, "innerText", name.toStdString()); - else if (iface->role() == QAccessible::EditableText) - setProperty(element, "value", value.toStdString()); - else - setAttribute(element, "aria-label", name.toStdString()); -} - -void QWasmAccessibility::setHtmlElementTextNameLE(QAccessibleInterface *iface) -{ - const emscripten::val element = getHtmlElement(iface); - QString value = iface->text(QAccessible::Value); - setProperty(element, "value", value.toStdString()); -} - void QWasmAccessibility::setHtmlElementFocus(QAccessibleInterface *iface) { const auto element = getHtmlElement(iface); @@ -684,7 +705,8 @@ void QWasmAccessibility::handleStaticTextUpdate(QAccessibleEvent *event) { switch (event->type()) { case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + // StaticText is a div + setNamedProperty(event->accessibleInterface(), "innerText", QAccessible::Name); } break; default: qCDebug(lcQpaAccessibility) << "TODO: implement handleStaticTextUpdate for event" << event->type(); @@ -705,7 +727,7 @@ void QWasmAccessibility::handleLineEditUpdate(QAccessibleEvent *event) setProperty(element, "type", "text"); } break; case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedProperty(event->accessibleInterface(), "value", QAccessible::Value); } break; case QAccessible::ObjectShow: case QAccessible::Focus: { @@ -718,12 +740,12 @@ void QWasmAccessibility::handleLineEditUpdate(QAccessibleEvent *event) else setProperty(element, "type", "text"); } - setHtmlElementTextNameLE(iface); + setNamedProperty(event->accessibleInterface(), "value", QAccessible::Value); } break; case QAccessible::TextRemoved: case QAccessible::TextInserted: case QAccessible::TextCaretMoved: { - setHtmlElementTextNameLE(event->accessibleInterface()); + setNamedProperty(event->accessibleInterface(), "value", QAccessible::Value); } break; default: qCDebug(lcQpaAccessibility) << "TODO: implement handleLineEditUpdate for event" << event->type(); @@ -758,7 +780,15 @@ void QWasmAccessibility::handleEventFromHtmlElement(const emscripten::val event) void QWasmAccessibility::handleButtonUpdate(QAccessibleEvent *event) { - qCDebug(lcQpaAccessibility) << "TODO: implement handleButtonUpdate for event" << event->type(); + switch (event->type()) { + case QAccessible::Focus: + case QAccessible::NameChanged: { + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); + } break; + default: + qCDebug(lcQpaAccessibility) << "TODO: implement handleCheckBoxUpdate for event" << event->type(); + break; + } } void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event) @@ -766,7 +796,7 @@ void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event) switch (event->type()) { case QAccessible::Focus: case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::StateChanged: { QAccessibleInterface *accessible = event->accessibleInterface(); @@ -780,12 +810,42 @@ void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event) } } +void QWasmAccessibility::handleGroupBoxUpdate(QAccessibleEvent *event) +{ + QAccessibleInterface *iface = event->accessibleInterface(); + + emscripten::val parent = getHtmlElement(iface); + emscripten::val label = parent["children"][0]; + emscripten::val checkbox = emscripten::val::undefined(); + if (iface->role() == QAccessible::CheckBox) + checkbox = parent["children"][1]; + + switch (event->type()) { + case QAccessible::Focus: + case QAccessible::NameChanged: { + setProperty(label, "innerText", iface->text(QAccessible::Name).toStdString()); + if (!checkbox.isUndefined()) + setAttribute(checkbox, "aria-label", iface->text(QAccessible::Name).toStdString()); + } break; + case QAccessible::StateChanged: { + if (!checkbox.isUndefined()) { + setAttribute(checkbox, "checked", iface->state().checked); + setProperty(checkbox, "indeterminate", iface->state().checkStateMixed); + } + } break; + default: + qCDebug(lcQpaAccessibility) << "TODO: implement handleCheckBoxUpdate for event" << event->type(); + break; + } +} + void QWasmAccessibility::handleSwitchUpdate(QAccessibleEvent *event) { switch (event->type()) { case QAccessible::Focus: case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + /* A switch is like a button in this regard */ + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::StateChanged: { QAccessibleInterface *accessible = event->accessibleInterface(); @@ -848,7 +908,7 @@ void QWasmAccessibility::handleDialogUpdate(QAccessibleEvent *event) { case QAccessible::Focus: case QAccessible::DialogStart: case QAccessible::StateChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; default: qCDebug(lcQpaAccessibility) << "TODO: implement handleLineEditUpdate for event" << event->type(); @@ -876,10 +936,10 @@ void QWasmAccessibility::populateAccessibilityTree(QAccessibleInterface *iface) linkToParent(iface); setHtmlElementVisibility(iface, !iface->state().invisible); setHtmlElementGeometry(iface); - setHtmlElementTextName(iface); setHtmlElementDisabled(iface); handleIdentifierUpdate(iface); handleDescriptionChanged(iface); + sendEvent(iface, QAccessible::NameChanged); } } for (int i = 0; i < iface->childCount(); ++i) @@ -891,7 +951,7 @@ void QWasmAccessibility::handleRadioButtonUpdate(QAccessibleEvent *event) switch (event->type()) { case QAccessible::Focus: case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::StateChanged: { QAccessibleInterface *accessible = event->accessibleInterface(); @@ -912,7 +972,7 @@ void QWasmAccessibility::handleSpinBoxUpdate(QAccessibleEvent *event) } break; case QAccessible::Focus: case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::ValueChanged: { QAccessibleInterface *accessible = event->accessibleInterface(); @@ -934,7 +994,7 @@ void QWasmAccessibility::handleSliderUpdate(QAccessibleEvent *event) } break; case QAccessible::Focus: case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::ValueChanged: { QAccessibleInterface *accessible = event->accessibleInterface(); @@ -953,7 +1013,7 @@ void QWasmAccessibility::handleScrollBarUpdate(QAccessibleEvent *event) switch (event->type()) { case QAccessible::Focus: case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::ValueChanged: { QAccessibleInterface *accessible = event->accessibleInterface(); @@ -972,10 +1032,10 @@ void QWasmAccessibility::handlePageTabUpdate(QAccessibleEvent *event) { switch (event->type()) { case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::Focus: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; default: qDebug() << "TODO: implement handlePageTabUpdate for event" << event->type(); @@ -987,10 +1047,10 @@ void QWasmAccessibility::handlePageTabListUpdate(QAccessibleEvent *event) { switch (event->type()) { case QAccessible::NameChanged: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; case QAccessible::Focus: { - setHtmlElementTextName(event->accessibleInterface()); + setNamedAttribute(event->accessibleInterface(), "aria-label", QAccessible::Name); } break; default: qDebug() << "TODO: implement handlePageTabUpdate for event" << event->type(); @@ -1113,13 +1173,19 @@ void QWasmAccessibility::relinkParentForChildren(QAccessibleInterface *iface) void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) { + if (handleUpdateByEventType(event)) + handleUpdateByInterfaceRole(event); +} + +bool QWasmAccessibility::handleUpdateByEventType(QAccessibleEvent *event) +{ if (!m_accessibilityEnabled) - return; + return false; QAccessibleInterface *iface = event->accessibleInterface(); if (!iface) { - qWarning() << "notifyAccessibilityUpdate with null a11y interface" << event->type() << event->object(); - return; + qWarning() << "handleUpdateByEventType with null a11y interface" << event->type() << event->object(); + return false; } // Handle event types that creates/removes objects. @@ -1127,13 +1193,13 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::ObjectCreated: // Do nothing, there are too many changes to the interface // before ObjectShow is called - return; + return false; case QAccessible::ObjectDestroyed: // The object might be under destruction, and the interface is not valid // but we can look at the pointer, removeObject(iface); - return; + return false; case QAccessible::ObjectShow: // We do not get ObjectCreated from widgets, we get ObjectShow createObject(iface); @@ -1149,7 +1215,7 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) }; if (getHtmlElement(iface).isUndefined()) - return; + return false; // Handle some common event types. See // https://doc.qt.io/qt-5/qaccessible.html#Event-enum @@ -1162,7 +1228,7 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::DescriptionChanged: handleDescriptionChanged(iface); - return; + return false; case QAccessible::Focus: // We do not get all callbacks for the geometry @@ -1173,7 +1239,7 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::IdentifierChanged: handleIdentifierUpdate(iface); - return; + return false; case QAccessible::ObjectShow: linkToParent(iface); @@ -1181,23 +1247,37 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) // Sync up properties on show; setHtmlElementGeometry(iface); - setHtmlElementTextName(iface); + sendEvent(iface, QAccessible::NameChanged); break; case QAccessible::ObjectHide: linkToParent(iface); setHtmlElementVisibility(iface, false); - return; + return false; case QAccessible::LocationChanged: setHtmlElementGeometry(iface); - return; + return false; // TODO: maybe handle more types here default: break; }; + return true; +} + +void QWasmAccessibility::handleUpdateByInterfaceRole(QAccessibleEvent *event) +{ + if (!m_accessibilityEnabled) + return; + + QAccessibleInterface *iface = event->accessibleInterface(); + if (!iface) { + qWarning() << "handleUpdateByInterfaceRole with null a11y interface" << event->type() << event->object(); + return; + } + // Switch on interface role, see // https://doc.qt.io/qt-5/qaccessibleinterface.html#role switch (iface->role()) { @@ -1205,10 +1285,13 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) handleStaticTextUpdate(event); break; case QAccessible::Button: - handleStaticTextUpdate(event); + handleButtonUpdate(event); break; case QAccessible::CheckBox: - handleCheckBoxUpdate(event); + if (iface->childCount() > 0) + handleGroupBoxUpdate(event); + else + handleCheckBoxUpdate(event); break; case QAccessible::Switch: handleSwitchUpdate(event); @@ -1245,6 +1328,9 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::ScrollBar: handleScrollBarUpdate(event); break; + case QAccessible::Grouping: + handleGroupBoxUpdate(event); + break; default: qCDebug(lcQpaAccessibility) << "TODO: implement notifyAccessibilityUpdate for role" << iface->role(); }; diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.h b/src/plugins/platforms/wasm/qwasmaccessibility.h index ddbfec918d6..2fff281b0d6 100644 --- a/src/plugins/platforms/wasm/qwasmaccessibility.h +++ b/src/plugins/platforms/wasm/qwasmaccessibility.h @@ -73,8 +73,6 @@ private: void setHtmlElementVisibility(QAccessibleInterface *iface, bool visible); void setHtmlElementGeometry(QAccessibleInterface *iface); void setHtmlElementGeometry(emscripten::val element, QRect geometry); - void setHtmlElementTextName(QAccessibleInterface *iface); - void setHtmlElementTextNameLE(QAccessibleInterface *iface); void setHtmlElementFocus(QAccessibleInterface *iface); void setHtmlElementDisabled(QAccessibleInterface *iface); void setHtmlElementOrientation(emscripten::val element, QAccessibleInterface *iface); @@ -82,6 +80,7 @@ private: void handleStaticTextUpdate(QAccessibleEvent *event); void handleButtonUpdate(QAccessibleEvent *event); void handleCheckBoxUpdate(QAccessibleEvent *event); + void handleGroupBoxUpdate(QAccessibleEvent *event); void handleSwitchUpdate(QAccessibleEvent *event); void handleDialogUpdate(QAccessibleEvent *event); void handleMenuUpdate(QAccessibleEvent *event); @@ -105,6 +104,9 @@ private: void relinkParentForChildren(QAccessibleInterface *iface); void notifyAccessibilityUpdate(QAccessibleEvent *event) override; + bool handleUpdateByEventType(QAccessibleEvent *event); + void handleUpdateByInterfaceRole(QAccessibleEvent *event); + void setRootObject(QObject *o) override; void initialize() override; void cleanup() override; @@ -117,7 +119,11 @@ private: void setProperty(emscripten::val element, const std::string &attr, const char *val); void setProperty(emscripten::val element, const std::string &attr, bool val); + void setNamedAttribute(QAccessibleInterface *iface, const std::string &attribute, QAccessible::Text text); + void setNamedProperty(QAccessibleInterface *iface, const std::string &property, QAccessible::Text text); + void addEventListener(QAccessibleInterface *, emscripten::val element, const char *eventType); + void sendEvent(QAccessibleInterface *iface, QAccessible::Event eventType); private: static QWasmAccessibility *s_instance; |
