9#include <QtGui/qwindow.h>
11#if QT_CONFIG(accessibility)
13#include <QtGui/private/qaccessiblebridgeutils_p.h>
27QWasmAccessibility::QWasmAccessibility()
33QWasmAccessibility::~QWasmAccessibility()
38QWasmAccessibility *QWasmAccessibility::s_instance =
nullptr;
40QWasmAccessibility* QWasmAccessibility::get()
45void QWasmAccessibility::addAccessibilityEnableButton(
QWindow *
window)
47 get()->addAccessibilityEnableButtonImpl(
window);
50void QWasmAccessibility::removeAccessibilityEnableButton(
QWindow *
window)
52 get()->removeAccessibilityEnableButtonImpl(
window);
55void QWasmAccessibility::addAccessibilityEnableButtonImpl(
QWindow *
window)
57 if (m_accessibilityEnabled)
60 emscripten::val container = getContainer(
window);
61 emscripten::val
document = getDocument(container);
62 emscripten::val
button =
document.call<emscripten::val>(
"createElement", std::string(
"button"));
63 button.set(
"innerText", std::string(
"Enable Screen Reader"));
64 button[
"classList"].call<
void>(
"add", emscripten::val(
"hidden-visually-read-by-screen-reader"));
65 container.call<
void>(
"appendChild",
button);
67 auto enableContext = std::make_tuple(
button, std::make_unique<qstdweb::EventCallback>
68 (
button, std::string(
"click"), [
this](emscripten::val) { enableAccessibility(); }));
69 m_enableButtons.insert(std::make_pair(
window, std::move(enableContext)));
72void QWasmAccessibility::removeAccessibilityEnableButtonImpl(
QWindow *
window)
75 if (
it == m_enableButtons.
end())
79 auto [element, callback] =
it->second;
81 element[
"parentElement"].call<
void>(
"removeChild", element);
82 m_enableButtons.erase(
it);
85void QWasmAccessibility::enableAccessibility()
91 m_accessibilityEnabled =
true;
92 for (
const auto& [
key,
value] : m_enableButtons) {
93 const auto &[element, callback] =
value;
96 element[
"parentElement"].call<
void>(
"removeChild", element);
98 m_enableButtons.clear();
99 populateAccessibilityTree(QAccessible::queryAccessibleInterface(m_rootObject));
102emscripten::val QWasmAccessibility::getContainer(
QWindow *
window)
108emscripten::val QWasmAccessibility::getContainer(QAccessibleInterface *iface)
111 return emscripten::val::undefined();
112 return getContainer(getWindow(iface));
115QWindow *QWasmAccessibility::getWindow(QAccessibleInterface *iface)
124emscripten::val QWasmAccessibility::getDocument(
const emscripten::val &container)
126 if (container.isUndefined())
127 return emscripten::val::global(
"document");
128 return container[
"ownerDocument"];
131emscripten::val QWasmAccessibility::getDocument(QAccessibleInterface *iface)
133 return getDocument(getContainer(iface));
136emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *iface)
141 emscripten::val container = getContainer(iface);
145 emscripten::val
document = getDocument(container);
152 emscripten::val element = emscripten::val::undefined();
154 switch (
iface->role()) {
156 case QAccessible::Button: {
157 element =
document.call<emscripten::val>(
"createElement", std::string(
"button"));
158 element.call<
void>(
"addEventListener", emscripten::val(
"click"),
159 emscripten::val::module_property(
"qtEventReceived"),
true);
161 case QAccessible::CheckBox: {
162 element =
document.call<emscripten::val>(
"createElement", std::string(
"input"));
163 element.call<
void>(
"setAttribute", std::string(
"type"), std::string(
"checkbox"));
164 if (
iface->state().checked) {
165 element.call<
void>(
"setAttribute", std::string(
"checked"), std::string(
"true"));
167 element.call<
void>(
"addEventListener", emscripten::val(
"change"),
168 emscripten::val::module_property(
"qtEventReceived"),
true);
172 case QAccessible::RadioButton: {
173 element =
document.call<emscripten::val>(
"createElement", std::string(
"input"));
174 element.call<
void>(
"setAttribute", std::string(
"type"), std::string(
"radio"));
175 if (
iface->state().checked) {
176 element.call<
void>(
"setAttribute", std::string(
"checked"), std::string(
"true"));
178 element.set(std::string(
"name"), std::string(
"buttonGroup"));
179 element.call<
void>(
"addEventListener", emscripten::val(
"change"),
180 emscripten::val::module_property(
"qtEventReceived"),
true);
183 case QAccessible::SpinBox: {
184 element =
document.call<emscripten::val>(
"createElement", std::string(
"input"));
185 element.call<
void>(
"setAttribute", std::string(
"type"), std::string(
"number"));
186 std::string valueString =
iface->valueInterface()->currentValue().toString().toStdString();
187 element.call<
void>(
"setAttribute", std::string(
"value"), valueString);
188 element.call<
void>(
"addEventListener", emscripten::val(
"change"),
189 emscripten::val::module_property(
"qtEventReceived"),
true);
192 case QAccessible::Slider: {
193 element =
document.call<emscripten::val>(
"createElement", std::string(
"input"));
194 element.call<
void>(
"setAttribute", std::string(
"type"), std::string(
"range"));
195 std::string valueString =
iface->valueInterface()->currentValue().toString().toStdString();
196 element.call<
void>(
"setAttribute", std::string(
"value"), valueString);
197 element.call<
void>(
"addEventListener", emscripten::val(
"change"),
198 emscripten::val::module_property(
"qtEventReceived"),
true);
201 case QAccessible::PageTabList:{
202 element =
document.call<emscripten::val>(
"createElement", std::string(
"div"));
203 element.call<
void>(
"setAttribute", std::string(
"role"), std::string(
"tablist"));
204 QString idName =
iface->text(QAccessible::Name).replace(
" ",
"_");
205 idName +=
"_tabList";
206 element.call<
void>(
"setAttribute", std::string(
"id"), idName.toStdString());
208 for (
int i = 0;
i <
iface->childCount(); ++
i) {
209 if (
iface->child(
i)->role() == QAccessible::PageTab){
210 emscripten::val elementTab = emscripten::val::undefined();
211 elementTab = ensureHtmlElement(
iface->child(
i));
212 elementTab.call<
void>(
"setAttribute", std::string(
"aria-owns"), idName.toStdString());
213 setHtmlElementGeometry(
iface->child(
i));
218 case QAccessible::PageTab:{
219 element =
document.call<emscripten::val>(
"createElement", std::string(
"button"));
220 element.call<
void>(
"setAttribute", std::string(
"role"), std::string(
"tab"));
222 element.call<
void>(
"setAttribute", std::string(
"title"),
text.
toStdString());
223 element.call<
void>(
"addEventListener", emscripten::val(
"click"),
224 emscripten::val::module_property(
"qtEventReceived"),
true);
227 case QAccessible::ScrollBar: {
228 element =
document.call<emscripten::val>(
"createElement", std::string(
"div"));
229 element.call<
void>(
"setAttribute", std::string(
"role"), std::string(
"scrollbar"));
230 std::string valueString =
iface->valueInterface()->currentValue().toString().toStdString();
231 element.call<
void>(
"setAttribute", std::string(
"aria-valuenow"), valueString);
232 element.call<
void>(
"addEventListener", emscripten::val(
"change"),
233 emscripten::val::module_property(
"qtEventReceived"),
true);
236 case QAccessible::StaticText: {
237 element =
document.call<emscripten::val>(
"createElement", std::string(
"textarea"));
238 element.call<
void>(
"setAttribute", std::string(
"readonly"), std::string(
"true"));
241 case QAccessible::Dialog: {
242 element =
document.call<emscripten::val>(
"createElement", std::string(
"dialog"));
244 case QAccessible::ToolBar:
245 case QAccessible::ButtonMenu: {
246 element =
document.call<emscripten::val>(
"createElement", std::string(
"div"));
249 element.call<
void>(
"setAttribute", std::string(
"role"), std::string(
"widget"));
250 element.call<
void>(
"setAttribute", std::string(
"title"),
text.
toStdString());
251 element.call<
void>(
"addEventListener", emscripten::val(
"click"),
252 emscripten::val::module_property(
"qtEventReceived"),
true);
254 case QAccessible::MenuBar:
255 case QAccessible::PopupMenu: {
256 element =
document.call<emscripten::val>(
"createElement",std::string(
"div"));
258 element.call<
void>(
"setAttribute", std::string(
"role"), std::string(
"widget"));
259 element.call<
void>(
"setAttribute", std::string(
"title"),
text.
toStdString());
260 for (
int i = 0;
i <
iface->childCount(); ++
i) {
261 ensureHtmlElement(
iface->child(
i));
262 setHtmlElementTextName(
iface->child(
i));
263 setHtmlElementGeometry(
iface->child(
i));
266 case QAccessible::EditableText: {
267 element =
document.call<emscripten::val>(
"createElement", std::string(
"input"));
268 element.call<
void>(
"setAttribute", std::string(
"type"),std::string(
"text"));
269 element.call<
void>(
"addEventListener", emscripten::val(
"input"),
270 emscripten::val::module_property(
"qtEventReceived"),
true);
273 qCDebug(lcQpaAccessibility) <<
"TODO: createHtmlElement() handle" <<
iface->role();
274 element =
document.call<emscripten::val>(
"createElement", std::string(
"div"));
283 if (!container.isUndefined())
284 container.call<
void>(
"appendChild", element);
289void QWasmAccessibility::destroyHtmlElement(QAccessibleInterface *iface)
292 qCDebug(lcQpaAccessibility) <<
"TODO destroyHtmlElement";
295emscripten::val QWasmAccessibility::ensureHtmlElement(QAccessibleInterface *iface)
297 auto it = m_elements.
find(iface);
298 if (
it != m_elements.
end())
301 emscripten::val element = createHtmlElement(iface);
302 m_elements.insert(iface, element);
307void QWasmAccessibility::setHtmlElementVisibility(QAccessibleInterface *iface,
bool visible)
309 emscripten::val element = ensureHtmlElement(iface);
310 emscripten::val container = getContainer(iface);
312 if (container.isUndefined()) {
313 qCDebug(lcQpaAccessibility) <<
"TODO: setHtmlElementVisibility: unable to find html container for element" <<
iface;
317 container.call<
void>(
"appendChild", element);
319 element.set(
"ariaHidden", !visible);
322void QWasmAccessibility::setHtmlElementGeometry(QAccessibleInterface *iface)
324 emscripten::val element = ensureHtmlElement(iface);
330 qCWarning(lcQpaAccessibility) <<
"Unable to find window for" <<
iface <<
"setting null geometry";
333 QRect windowGeometry(windowPos, screenGeometry.
size());
335 setHtmlElementGeometry(element, windowGeometry);
338void QWasmAccessibility::setHtmlElementGeometry(emscripten::val element,
QRect geometry)
342 emscripten::val style = element[
"style"];
343 style.set(
"position", std::string(
"absolute"));
344 style.set(
"z-index", std::string(
"-1"));
346 style.set(
"left", std::to_string(geometry.
x()) +
"px");
347 style.set(
"top", std::to_string(geometry.
y()) +
"px");
348 style.set(
"width", std::to_string(geometry.
width()) +
"px");
349 style.set(
"height", std::to_string(geometry.
height()) +
"px");
352void QWasmAccessibility::setHtmlElementTextName(QAccessibleInterface *iface)
354 emscripten::val element = ensureHtmlElement(iface);
359void QWasmAccessibility::setHtmlElementTextNameLE(QAccessibleInterface *iface) {
360 emscripten::val element = ensureHtmlElement(iface);
362 element.call<
void>(
"setAttribute", std::string(
"name"),
text.
toStdString());
364 element.set(
"innerHTML",
value.toStdString());
367void QWasmAccessibility::handleStaticTextUpdate(QAccessibleEvent *
event)
369 switch (
event->type()) {
370 case QAccessible::NameChanged: {
371 setHtmlElementTextName(
event->accessibleInterface());
374 qCDebug(lcQpaAccessibility) <<
"TODO: implement handleStaticTextUpdate for event" <<
event->type();
379void QWasmAccessibility::handleLineEditUpdate(QAccessibleEvent *
event) {
381 switch (
event->type()) {
382 case QAccessible::NameChanged: {
383 setHtmlElementTextName(
event->accessibleInterface());
385 case QAccessible::Focus:
386 case QAccessible::TextRemoved:
387 case QAccessible::TextInserted:
388 case QAccessible::TextCaretMoved: {
389 setHtmlElementTextNameLE(
event->accessibleInterface());
393 qCDebug(lcQpaAccessibility) <<
"TODO: implement handleLineEditUpdate for event" <<
event->type();
398void QWasmAccessibility::handleEventFromHtmlElement(
const emscripten::val
event)
401 QAccessibleInterface *
iface = m_elements.key(
event[
"target"]);
403 if (iface ==
nullptr) {
409 if (actionNames.contains(QAccessibleActionInterface::pressAction())) {
411 iface->actionInterface()->doAction(QAccessibleActionInterface::pressAction());
413 }
else if (actionNames.contains(QAccessibleActionInterface::toggleAction())) {
415 iface->actionInterface()->doAction(QAccessibleActionInterface::toggleAction());
417 }
else if (actionNames.contains(QAccessibleActionInterface::increaseAction()) ||
418 actionNames.contains(QAccessibleActionInterface::decreaseAction())) {
422 iface->valueInterface()->setCurrentValue(
val.toInt());
424 }
else if (eventType ==
"input") {
426 if (
iface->editableTextInterface()) {
427 std::string insertText =
event[
"target"][
"value"].as<std::string>();
434void QWasmAccessibility::handleButtonUpdate(QAccessibleEvent *
event)
436 qCDebug(lcQpaAccessibility) <<
"TODO: implement handleButtonUpdate for event" <<
event->type();
439void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *
event)
441 switch (
event->type()) {
442 case QAccessible::Focus:
443 case QAccessible::NameChanged: {
444 setHtmlElementTextName(
event->accessibleInterface());
446 case QAccessible::StateChanged: {
447 QAccessibleInterface *accessible =
event->accessibleInterface();
448 emscripten::val element = ensureHtmlElement(accessible);
449 bool checkedString = accessible->state().checked ? true :
false;
450 element.call<
void>(
"setAttribute", std::string(
"checked"), checkedString);
453 qCDebug(lcQpaAccessibility) <<
"TODO: implement handleCheckBoxUpdate for event" <<
event->type();
457void QWasmAccessibility::handleToolUpdate(QAccessibleEvent *
event)
459 QAccessibleInterface *
iface =
event->accessibleInterface();
462 switch (
event->type()) {
463 case QAccessible::NameChanged:
464 case QAccessible::StateChanged:{
465 emscripten::val element = ensureHtmlElement(iface);
466 element.call<
void>(
"setAttribute", std::string(
"title"),
text.
toStdString());
469 qCDebug(lcQpaAccessibility) <<
"TODO: implement handleToolUpdate for event" <<
event->type();
473void QWasmAccessibility::handleMenuUpdate(QAccessibleEvent *
event)
475 QAccessibleInterface *
iface =
event->accessibleInterface();
478 switch (
event->type()) {
479 case QAccessible::NameChanged:
480 case QAccessible::MenuStart :
481 case QAccessible::PopupMenuStart:
482 case QAccessible::StateChanged:{
483 emscripten::val element = ensureHtmlElement(iface);
484 element.call<
void>(
"setAttribute", std::string(
"title"),
text.
toStdString());
487 qCDebug(lcQpaAccessibility) <<
"TODO: implement handleMenuUpdate for event" <<
event->type();
491void QWasmAccessibility::handleDialogUpdate(QAccessibleEvent *
event) {
493 switch (
event->type()) {
494 case QAccessible::NameChanged:
495 case QAccessible::Focus:
496 case QAccessible::DialogStart:
497 case QAccessible::StateChanged: {
498 setHtmlElementTextName(
event->accessibleInterface());
502 qCDebug(lcQpaAccessibility) <<
"TODO: implement handleLineEditUpdate for event" <<
event->type();
507void QWasmAccessibility::populateAccessibilityTree(QAccessibleInterface *iface)
513 ensureHtmlElement(iface);
514 const bool visible = !
iface->state().invisible;
515 setHtmlElementVisibility(iface, visible);
516 setHtmlElementGeometry(iface);
517 setHtmlElementTextName(iface);
519 for (
int i = 0;
i <
iface->childCount(); ++
i)
520 populateAccessibilityTree(
iface->child(
i));
523void QWasmAccessibility::handleRadioButtonUpdate(QAccessibleEvent *
event)
525 switch (
event->type()) {
526 case QAccessible::Focus:
527 case QAccessible::NameChanged: {
528 setHtmlElementTextName(
event->accessibleInterface());
530 case QAccessible::StateChanged: {
531 QAccessibleInterface *accessible =
event->accessibleInterface();
532 emscripten::val element = ensureHtmlElement(accessible);
533 std::string checkedString = accessible->state().checked ?
"true" :
"false";
534 element.call<
void>(
"setAttribute", std::string(
"checked"), checkedString);
537 qDebug() <<
"TODO: implement handleRadioButtonUpdate for event" <<
event->type();
542void QWasmAccessibility::handleSpinBoxUpdate(QAccessibleEvent *
event)
544 switch (
event->type()) {
545 case QAccessible::Focus:
546 case QAccessible::NameChanged: {
547 setHtmlElementTextName(
event->accessibleInterface());
549 case QAccessible::ValueChanged: {
550 QAccessibleInterface *accessible =
event->accessibleInterface();
551 emscripten::val element = ensureHtmlElement(accessible);
552 std::string valueString = accessible->valueInterface()->currentValue().toString().toStdString();
553 element.call<
void>(
"setAttribute", std::string(
"value"), valueString);
556 qDebug() <<
"TODO: implement handleSpinBoxUpdate for event" <<
event->type();
561void QWasmAccessibility::handleSliderUpdate(QAccessibleEvent *
event)
563 switch (
event->type()) {
564 case QAccessible::Focus:
565 case QAccessible::NameChanged: {
566 setHtmlElementTextName(
event->accessibleInterface());
568 case QAccessible::ValueChanged: {
569 QAccessibleInterface *accessible =
event->accessibleInterface();
570 emscripten::val element = ensureHtmlElement(accessible);
571 std::string valueString = accessible->valueInterface()->currentValue().toString().toStdString();
572 element.call<
void>(
"setAttribute", std::string(
"value"), valueString);
575 qDebug() <<
"TODO: implement handleSliderUpdate for event" <<
event->type();
580void QWasmAccessibility::handleScrollBarUpdate(QAccessibleEvent *
event)
582 switch (
event->type()) {
583 case QAccessible::Focus:
584 case QAccessible::NameChanged: {
585 setHtmlElementTextName(
event->accessibleInterface());
587 case QAccessible::ValueChanged: {
588 QAccessibleInterface *accessible =
event->accessibleInterface();
589 emscripten::val element = ensureHtmlElement(accessible);
590 std::string valueString = accessible->valueInterface()->currentValue().toString().toStdString();
591 element.call<
void>(
"setAttribute", std::string(
"aria-valuenow"), valueString);
594 qDebug() <<
"TODO: implement handleSliderUpdate for event" <<
event->type();
600void QWasmAccessibility::handlePageTabUpdate(QAccessibleEvent *
event)
602 switch (
event->type()) {
603 case QAccessible::NameChanged: {
604 setHtmlElementTextName(
event->accessibleInterface());
606 case QAccessible::Focus: {
607 setHtmlElementTextName(
event->accessibleInterface());
610 qDebug() <<
"TODO: implement handlePageTabUpdate for event" <<
event->type();
615void QWasmAccessibility::handlePageTabListUpdate(QAccessibleEvent *
event)
617 switch (
event->type()) {
618 case QAccessible::NameChanged: {
619 setHtmlElementTextName(
event->accessibleInterface());
621 case QAccessible::Focus: {
622 setHtmlElementTextName(
event->accessibleInterface());
625 qDebug() <<
"TODO: implement handlePageTabUpdate for event" <<
event->type();
630void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *
event)
632 if (!m_accessibilityEnabled)
635 QAccessibleInterface *
iface =
event->accessibleInterface();
637 qWarning() <<
"notifyAccessibilityUpdate with null a11y interface" ;
643 switch (
event->type()) {
644 case QAccessible::ObjectShow:
646 setHtmlElementVisibility(iface,
true);
649 setHtmlElementGeometry(iface);
650 setHtmlElementTextName(iface);
654 case QAccessible::ObjectHide:
655 setHtmlElementVisibility(iface,
false);
665 switch (
iface->role()) {
666 case QAccessible::StaticText:
667 handleStaticTextUpdate(
event);
669 case QAccessible::Button:
670 handleStaticTextUpdate(
event);
672 case QAccessible::CheckBox:
673 handleCheckBoxUpdate(
event);
675 case QAccessible::EditableText:
676 handleLineEditUpdate(
event);
678 case QAccessible::Dialog:
679 handleDialogUpdate(
event);
681 case QAccessible::MenuItem:
682 case QAccessible::MenuBar:
683 case QAccessible::PopupMenu:
684 handleMenuUpdate(
event);
686 case QAccessible::ToolBar:
687 case QAccessible::ButtonMenu:
688 handleToolUpdate(
event);
689 case QAccessible::RadioButton:
690 handleRadioButtonUpdate(
event);
692 case QAccessible::SpinBox:
693 handleSpinBoxUpdate(
event);
695 case QAccessible::Slider:
696 handleSliderUpdate(
event);
698 case QAccessible::PageTab:
699 handlePageTabUpdate(
event);
701 case QAccessible::PageTabList:
702 handlePageTabListUpdate(
event);
704 case QAccessible::ScrollBar:
705 handleScrollBarUpdate(
event);
708 qCDebug(lcQpaAccessibility) <<
"TODO: implement notifyAccessibilityUpdate for role" <<
iface->role();
712void QWasmAccessibility::setRootObject(
QObject *root)
717void QWasmAccessibility::initialize()
722void QWasmAccessibility::cleanup()
727void QWasmAccessibility::onHtmlEventReceived(emscripten::val
event)
733 function(
"qtEventReceived", &QWasmAccessibility::onHtmlEventReceived);
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
constexpr int height() const noexcept
Returns the height of the rectangle.
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
constexpr QSize size() const noexcept
Returns the size of the rectangle.
constexpr int width() const noexcept
Returns the width of the rectangle.
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
iterator find(const T &value)
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromStdString(const std::string &s)
std::string toStdString() const
Returns a std::string object with the data contained in this QString.
static QWasmIntegration * get()
QPlatformAccessibility * accessibility() const override
QSet< QString >::iterator it
QStringList effectiveActionNames(QAccessibleInterface *iface)
constexpr QBindableInterface iface
emscripten::val document()
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
static QDBusError::ErrorType get(const char *name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
EMSCRIPTEN_BINDINGS(qtClipboardModule)