Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwasmaccessibility.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5#include "qwasmscreen.h"
6#include "qwasmwindow.h"
7#include "qwasmintegration.h"
8#include <QtGui/qwindow.h>
9
10#if QT_CONFIG(accessibility)
11
12#include <QtGui/private/qaccessiblebridgeutils_p.h>
13
14Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
15
16// Qt WebAssembly a11y backend
17//
18// This backend implements accessibility support by creating "shadowing" html
19// elements for each Qt UI element. We access the DOM by using Emscripten's
20// val.h API.
21//
22// Currently, html elements are created in response to notifyAccessibilityUpdate
23// events. In addition or alternatively, we could also walk the accessibility tree
24// from setRootObject().
25
26QWasmAccessibility::QWasmAccessibility()
27{
28
29 s_instance = this;
30}
31
32QWasmAccessibility::~QWasmAccessibility()
33{
34 s_instance = nullptr;
35}
36
37QWasmAccessibility *QWasmAccessibility::s_instance = nullptr;
38
39QWasmAccessibility* QWasmAccessibility::get()
40{
41 return s_instance;
42}
43
44void QWasmAccessibility::addAccessibilityEnableButton(QWindow *window)
45{
46 get()->addAccessibilityEnableButtonImpl(window);
47}
48
49void QWasmAccessibility::removeAccessibilityEnableButton(QWindow *window)
50{
51 get()->removeAccessibilityEnableButtonImpl(window);
52}
53
54void QWasmAccessibility::addAccessibilityEnableButtonImpl(QWindow *window)
55{
56 if (m_accessibilityEnabled)
57 return;
58
59 emscripten::val container = getContainer(window);
60 emscripten::val document = getDocument(container);
61 emscripten::val button = document.call<emscripten::val>("createElement", std::string("button"));
62 button.set("innerText", std::string("Enable Screen Reader"));
63 button["classList"].call<void>("add", emscripten::val("hidden-visually-read-by-screen-reader"));
64 container.call<void>("appendChild", button);
65
66 auto enableContext = std::make_tuple(button, std::make_unique<qstdweb::EventCallback>
67 (button, std::string("click"), [this](emscripten::val) { enableAccessibility(); }));
68 m_enableButtons.insert(std::make_pair(window, std::move(enableContext)));
69}
70
71void QWasmAccessibility::removeAccessibilityEnableButtonImpl(QWindow *window)
72{
73 auto it = m_enableButtons.find(window);
74 if (it == m_enableButtons.end())
75 return;
76
77 // Remove button
78 auto [element, callback] = it->second;
79 Q_UNUSED(callback);
80 element["parentElement"].call<void>("removeChild", element);
81 m_enableButtons.erase(it);
82}
83
84void QWasmAccessibility::enableAccessibility()
85{
86 // Enable accessibility globally for the applicaton. Remove all "enable"
87 // buttons and populate the accessibility tree, starting from the root object.
88
89 Q_ASSERT(!m_accessibilityEnabled);
90 m_accessibilityEnabled = true;
91 for (const auto& [key, value] : m_enableButtons) {
92 const auto &[element, callback] = value;
94 Q_UNUSED(callback);
95 element["parentElement"].call<void>("removeChild", element);
96 }
97 m_enableButtons.clear();
98 populateAccessibilityTree(QAccessible::queryAccessibleInterface(m_rootObject));
99}
100
101emscripten::val QWasmAccessibility::getContainer(QWindow *window)
102{
103 return window ? static_cast<QWasmWindow *>(window->handle())->a11yContainer()
104 : emscripten::val::undefined();
105}
106
107emscripten::val QWasmAccessibility::getContainer(QAccessibleInterface *iface)
108{
109 if (!iface)
110 return emscripten::val::undefined();
111 return getContainer(getWindow(iface));
112}
113
114QWindow *QWasmAccessibility::getWindow(QAccessibleInterface *iface)
115{
116 QWindow *window = iface->window();
117 // this is needed to add tabs as the window is not available
118 if (!window && iface->parent())
119 window = iface->parent()->window();
120 return window;
121}
122
123emscripten::val QWasmAccessibility::getDocument(const emscripten::val &container)
124{
125 if (container.isUndefined())
126 return emscripten::val::global("document");
127 return container["ownerDocument"];
128}
129
130emscripten::val QWasmAccessibility::getDocument(QAccessibleInterface *iface)
131{
132 return getDocument(getContainer(iface));
133}
134
135emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *iface)
136{
137 // Get the html container element for the interface; this depends on which
138 // QScreen it is on. If the interface is not on a screen yet we get an undefined
139 // container, and the code below handles that case as well.
140 emscripten::val container = getContainer(iface);
141
142 // Get the correct html document for the container, or fall back
143 // to the global document. TODO: Does using the correct document actually matter?
144 emscripten::val document = getDocument(container);
145
146 // Translate the Qt a11y elemen role into html element type + ARIA role.
147 // Here we can either create <div> elements with a spesific ARIA role,
148 // or create e.g. <button> elements which should have built-in accessibility.
149 emscripten::val element = [this, iface, document] {
150
151 emscripten::val element = emscripten::val::undefined();
152
153 switch (iface->role()) {
154
155 case QAccessible::Button: {
156 element = document.call<emscripten::val>("createElement", std::string("button"));
157 element.call<void>("addEventListener", emscripten::val("click"),
158 emscripten::val::module_property("qtEventReceived"), true);
159 } break;
160 case QAccessible::CheckBox: {
161 element = document.call<emscripten::val>("createElement", std::string("input"));
162 element.call<void>("setAttribute", std::string("type"), std::string("checkbox"));
163 if (iface->state().checked) {
164 element.call<void>("setAttribute", std::string("checked"), std::string("true"));
165 }
166 element.call<void>("addEventListener", emscripten::val("change"),
167 emscripten::val::module_property("qtEventReceived"), true);
168
169 } break;
170
171 case QAccessible::RadioButton: {
172 element = document.call<emscripten::val>("createElement", std::string("input"));
173 element.call<void>("setAttribute", std::string("type"), std::string("radio"));
174 if (iface->state().checked) {
175 element.call<void>("setAttribute", std::string("checked"), std::string("true"));
176 }
177 element.set(std::string("name"), std::string("buttonGroup"));
178 element.call<void>("addEventListener", emscripten::val("change"),
179 emscripten::val::module_property("qtEventReceived"), true);
180 } break;
181
182 case QAccessible::SpinBox: {
183 element = document.call<emscripten::val>("createElement", std::string("input"));
184 element.call<void>("setAttribute", std::string("type"), std::string("number"));
185 std::string valueString = iface->valueInterface()->currentValue().toString().toStdString();
186 element.call<void>("setAttribute", std::string("value"), valueString);
187 element.call<void>("addEventListener", emscripten::val("change"),
188 emscripten::val::module_property("qtEventReceived"), true);
189 } break;
190
191 case QAccessible::Slider: {
192 element = document.call<emscripten::val>("createElement", std::string("input"));
193 element.call<void>("setAttribute", std::string("type"), std::string("range"));
194 std::string valueString = iface->valueInterface()->currentValue().toString().toStdString();
195 element.call<void>("setAttribute", std::string("value"), valueString);
196 element.call<void>("addEventListener", emscripten::val("change"),
197 emscripten::val::module_property("qtEventReceived"), true);
198 } break;
199
200 case QAccessible::PageTabList:{
201 element = document.call<emscripten::val>("createElement", std::string("div"));
202 element.call<void>("setAttribute", std::string("role"), std::string("tablist"));
203 QString idName = iface->text(QAccessible::Name).replace(" ", "_");
204 idName += "_tabList";
205 element.call<void>("setAttribute", std::string("id"), idName.toStdString());
206
207 for (int i = 0; i < iface->childCount(); ++i) {
208 if (iface->child(i)->role() == QAccessible::PageTab){
209 emscripten::val elementTab = emscripten::val::undefined();
210 elementTab = ensureHtmlElement(iface->child(i));
211 elementTab.call<void>("setAttribute", std::string("aria-owns"), idName.toStdString());
212 setHtmlElementGeometry(iface->child(i));
213 }
214 }
215 } break;
216
217 case QAccessible::PageTab:{
218 element = document.call<emscripten::val>("createElement", std::string("button"));
219 element.call<void>("setAttribute", std::string("role"), std::string("tab"));
220 QString text = iface->text(QAccessible::Name);
221 element.call<void>("setAttribute", std::string("title"), text.toStdString());
222 element.call<void>("addEventListener", emscripten::val("click"),
223 emscripten::val::module_property("qtEventReceived"), true);
224 } break;
225
226 case QAccessible::ScrollBar: {
227 element = document.call<emscripten::val>("createElement", std::string("div"));
228 element.call<void>("setAttribute", std::string("role"), std::string("scrollbar"));
229 std::string valueString = iface->valueInterface()->currentValue().toString().toStdString();
230 element.call<void>("setAttribute", std::string("aria-valuenow"), valueString);
231 element.call<void>("addEventListener", emscripten::val("change"),
232 emscripten::val::module_property("qtEventReceived"), true);
233 } break;
234
235 case QAccessible::StaticText: {
236 element = document.call<emscripten::val>("createElement", std::string("textarea"));
237 element.call<void>("setAttribute", std::string("readonly"), std::string("true"));
238
239 } break;
240 case QAccessible::Dialog: {
241 element = document.call<emscripten::val>("createElement", std::string("dialog"));
242 }break;
243 case QAccessible::ToolBar:{
244 element = document.call<emscripten::val>("createElement", std::string("div"));
245 QString text = iface->text(QAccessible::Name);
246
247 element.call<void>("setAttribute", std::string("role"), std::string("toolbar"));
248 element.call<void>("setAttribute", std::string("title"), text.toStdString());
249 element.call<void>("addEventListener", emscripten::val("click"),
250 emscripten::val::module_property("qtEventReceived"), true);
251 }break;
252 case QAccessible::MenuItem:
253 case QAccessible::ButtonMenu: {
254 element = document.call<emscripten::val>("createElement", std::string("button"));
255 QString text = iface->text(QAccessible::Name);
256
257 element.call<void>("setAttribute", std::string("role"), std::string("menuitem"));
258 element.call<void>("setAttribute", std::string("title"), text.toStdString());
259 element.call<void>("addEventListener", emscripten::val("click"),
260 emscripten::val::module_property("qtEventReceived"), true);
261 }break;
262 case QAccessible::MenuBar:
263 case QAccessible::PopupMenu: {
264 element = document.call<emscripten::val>("createElement",std::string("div"));
265 QString text = iface->text(QAccessible::Name);
266 element.call<void>("setAttribute", std::string("role"), std::string("menubar"));
267 element.call<void>("setAttribute", std::string("title"), text.toStdString());
268 for (int i = 0; i < iface->childCount(); ++i) {
269 emscripten::val childElement = emscripten::val::undefined();
270 childElement= ensureHtmlElement(iface->child(i));
271 childElement.call<void>("setAttribute", std::string("aria-owns"), text.toStdString());
272 setHtmlElementTextName(iface->child(i));
273 setHtmlElementGeometry(iface->child(i));
274 }
275 }break;
276 case QAccessible::EditableText: {
277 element = document.call<emscripten::val>("createElement", std::string("input"));
278 element.call<void>("setAttribute", std::string("type"),std::string("text"));
279 element.call<void>("addEventListener", emscripten::val("input"),
280 emscripten::val::module_property("qtEventReceived"), true);
281 } break;
282 default:
283 qCDebug(lcQpaAccessibility) << "TODO: createHtmlElement() handle" << iface->role();
284 element = document.call<emscripten::val>("createElement", std::string("div"));
285 }
286
287 return element;
288
289 }();
290
291 // Add the html element to the container if we have one. If not there
292 // is a second chance when handling the ObjectShow event.
293 if (!container.isUndefined())
294 container.call<void>("appendChild", element);
295
296 return element;
297}
298
299void QWasmAccessibility::destroyHtmlElement(QAccessibleInterface *iface)
300{
301 Q_UNUSED(iface);
302 qCDebug(lcQpaAccessibility) << "TODO destroyHtmlElement";
303}
304
305emscripten::val QWasmAccessibility::ensureHtmlElement(QAccessibleInterface *iface)
306{
307 auto it = m_elements.find(iface);
308 if (it != m_elements.end())
309 return it.value();
310
311 emscripten::val element = createHtmlElement(iface);
312 m_elements.insert(iface, element);
313
314 return element;
315}
316
317void QWasmAccessibility::setHtmlElementVisibility(QAccessibleInterface *iface, bool visible)
318{
319 emscripten::val element = ensureHtmlElement(iface);
320 emscripten::val container = getContainer(iface);
321
322 if (container.isUndefined()) {
323 qCDebug(lcQpaAccessibility) << "TODO: setHtmlElementVisibility: unable to find html container for element" << iface;
324 return;
325 }
326
327 container.call<void>("appendChild", element);
328
329 element.set("ariaHidden", !visible); // ariaHidden mean completely hidden; maybe some sort of soft-hidden should be used.
330}
331
332void QWasmAccessibility::setHtmlElementGeometry(QAccessibleInterface *iface)
333{
334 emscripten::val element = ensureHtmlElement(iface);
335
336 // QAccessibleInterface gives us the geometry in global (screen) coordinates. Translate that
337 // to window geometry in order to position elements relative to window origin.
338 QWindow *window = getWindow(iface);
339 if (!window)
340 qCWarning(lcQpaAccessibility) << "Unable to find window for" << iface << "setting null geometry";
341 QRect screenGeometry = iface->rect();
342 QPoint windowPos = window ? window->mapFromGlobal(screenGeometry.topLeft()) : QPoint();
343 QRect windowGeometry(windowPos, screenGeometry.size());
344
345 setHtmlElementGeometry(element, windowGeometry);
346}
347
348void QWasmAccessibility::setHtmlElementGeometry(emscripten::val element, QRect geometry)
349{
350 // Position the element using "position: absolute" in order to place
351 // it under the corresponding Qt element in the screen.
352 emscripten::val style = element["style"];
353 style.set("position", std::string("absolute"));
354 style.set("z-index", std::string("-1")); // FIXME: "0" should be sufficient to order beheind the
355 // screen element, but isn't
356 style.set("left", std::to_string(geometry.x()) + "px");
357 style.set("top", std::to_string(geometry.y()) + "px");
358 style.set("width", std::to_string(geometry.width()) + "px");
359 style.set("height", std::to_string(geometry.height()) + "px");
360}
361
362void QWasmAccessibility::setHtmlElementTextName(QAccessibleInterface *iface)
363{
364 emscripten::val element = ensureHtmlElement(iface);
365 QString text = iface->text(QAccessible::Name);
366 element.set("innerHTML", text.toStdString()); // FIXME: use something else than innerHTML
367}
368
369void QWasmAccessibility::setHtmlElementTextNameLE(QAccessibleInterface *iface) {
370 emscripten::val element = ensureHtmlElement(iface);
371 QString text = iface->text(QAccessible::Name);
372 element.call<void>("setAttribute", std::string("name"), text.toStdString());
373 QString value = iface->text(QAccessible::Value);
374 element.set("innerHTML", value.toStdString());
375}
376
377void QWasmAccessibility::setHtmlElementDescription(QAccessibleInterface *iface) {
378 emscripten::val element = ensureHtmlElement(iface);
379 QString desc = iface->text(QAccessible::Description);
380 element.call<void>("setAttribute", std::string("aria-description"), desc.toStdString());
381}
382
383void QWasmAccessibility::handleStaticTextUpdate(QAccessibleEvent *event)
384{
385 switch (event->type()) {
386 case QAccessible::NameChanged: {
387 setHtmlElementTextName(event->accessibleInterface());
388 } break;
389 case QAccessible::DescriptionChanged: {
390 setHtmlElementDescription(event->accessibleInterface());
391 } break;
392 default:
393 qCDebug(lcQpaAccessibility) << "TODO: implement handleStaticTextUpdate for event" << event->type();
394 break;
395 }
396}
397
398void QWasmAccessibility::handleLineEditUpdate(QAccessibleEvent *event) {
399
400 switch (event->type()) {
401 case QAccessible::NameChanged: {
402 setHtmlElementTextName(event->accessibleInterface());
403 } break;
404 case QAccessible::Focus:
405 case QAccessible::TextRemoved:
406 case QAccessible::TextInserted:
407 case QAccessible::TextCaretMoved: {
408 setHtmlElementTextNameLE(event->accessibleInterface());
409 } break;
410 case QAccessible::DescriptionChanged: {
411 setHtmlElementDescription(event->accessibleInterface());
412 } break;
413 default:
414 qCDebug(lcQpaAccessibility) << "TODO: implement handleLineEditUpdate for event" << event->type();
415 break;
416 }
417}
418
419void QWasmAccessibility::handleEventFromHtmlElement(const emscripten::val event)
420{
421
422 QAccessibleInterface *iface = m_elements.key(event["target"]);
423 if (iface == nullptr) {
424 return;
425 } else {
426 QString eventType = QString::fromStdString(event["type"].as<std::string>());
427 const auto& actionNames = QAccessibleBridgeUtils::effectiveActionNames(iface);
428 if (actionNames.contains(QAccessibleActionInterface::pressAction())) {
429
430 iface->actionInterface()->doAction(QAccessibleActionInterface::pressAction());
431
432 } else if (actionNames.contains(QAccessibleActionInterface::toggleAction())) {
433
434 iface->actionInterface()->doAction(QAccessibleActionInterface::toggleAction());
435
436 } else if (actionNames.contains(QAccessibleActionInterface::increaseAction()) ||
437 actionNames.contains(QAccessibleActionInterface::decreaseAction())) {
438
439 QString val = QString::fromStdString(event["target"]["value"].as<std::string>());
440
441 iface->valueInterface()->setCurrentValue(val.toInt());
442
443 } else if (eventType == "input") {
444
445 // as EditableTextInterface is not implemented in qml accessibility
446 // so we need to check the role for text to update in the textbox during accessibility
447
448 if (iface->editableTextInterface() || iface->role() == QAccessible::EditableText) {
449 std::string insertText = event["target"]["value"].as<std::string>();
450 iface->setText(QAccessible::Value, QString::fromStdString(insertText));
451 }
452 }
453 }
454}
455
456void QWasmAccessibility::handleButtonUpdate(QAccessibleEvent *event)
457{
458 qCDebug(lcQpaAccessibility) << "TODO: implement handleButtonUpdate for event" << event->type();
459}
460
461void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event)
462{
463 switch (event->type()) {
464 case QAccessible::Focus:
465 case QAccessible::NameChanged: {
466 setHtmlElementTextName(event->accessibleInterface());
467 } break;
468 case QAccessible::StateChanged: {
469 QAccessibleInterface *accessible = event->accessibleInterface();
470 emscripten::val element = ensureHtmlElement(accessible);
471 bool checkedString = accessible->state().checked ? true : false;
472 element.call<void>("setAttribute", std::string("checked"), checkedString);
473 } break;
474 case QAccessible::DescriptionChanged: {
475 setHtmlElementDescription(event->accessibleInterface());
476 } break;
477 default:
478 qCDebug(lcQpaAccessibility) << "TODO: implement handleCheckBoxUpdate for event" << event->type();
479 break;
480 }
481}
482void QWasmAccessibility::handleToolUpdate(QAccessibleEvent *event)
483{
484 QAccessibleInterface *iface = event->accessibleInterface();
485 QString text = iface->text(QAccessible::Name);
486 QString desc = iface->text(QAccessible::Description);
487 switch (event->type()) {
488 case QAccessible::NameChanged:
489 case QAccessible::StateChanged:{
490 emscripten::val element = ensureHtmlElement(iface);
491 element.call<void>("setAttribute", std::string("title"), text.toStdString());
492 } break;
493 case QAccessible::DescriptionChanged: {
494 setHtmlElementDescription(event->accessibleInterface());
495 } break;
496 default:
497 qCDebug(lcQpaAccessibility) << "TODO: implement handleToolUpdate for event" << event->type();
498 break;
499 }
500}
501void QWasmAccessibility::handleMenuUpdate(QAccessibleEvent *event)
502{
503 QAccessibleInterface *iface = event->accessibleInterface();
504 QString text = iface->text(QAccessible::Name);
505 QString desc = iface->text(QAccessible::Description);
506 switch (event->type()) {
507 case QAccessible::Focus:
508 case QAccessible::NameChanged:
509 case QAccessible::MenuStart ://"TODO: To implement later
510 case QAccessible::PopupMenuStart://"TODO: To implement later
511 case QAccessible::StateChanged:{
512 emscripten::val element = ensureHtmlElement(iface);
513 element.call<void>("setAttribute", std::string("title"), text.toStdString());
514 } break;
515 case QAccessible::DescriptionChanged: {
516 setHtmlElementDescription(event->accessibleInterface());
517 } break;
518 default:
519 qCDebug(lcQpaAccessibility) << "TODO: implement handleMenuUpdate for event" << event->type();
520 break;
521 }
522}
523void QWasmAccessibility::handleDialogUpdate(QAccessibleEvent *event) {
524
525 switch (event->type()) {
526 case QAccessible::NameChanged:
527 case QAccessible::Focus:
528 case QAccessible::DialogStart:
529 case QAccessible::StateChanged: {
530 setHtmlElementTextName(event->accessibleInterface());
531 } break;
532 case QAccessible::DescriptionChanged: {
533 setHtmlElementDescription(event->accessibleInterface());
534 } break;
535 default:
536 qCDebug(lcQpaAccessibility) << "TODO: implement handleLineEditUpdate for event" << event->type();
537 break;
538 }
539}
540
541void QWasmAccessibility::populateAccessibilityTree(QAccessibleInterface *iface)
542{
543 if (!iface)
544 return;
545
546 // Create html element for the interface, sync up properties.
547 ensureHtmlElement(iface);
548 const bool visible = !iface->state().invisible;
549 setHtmlElementVisibility(iface, visible);
550 setHtmlElementGeometry(iface);
551 setHtmlElementTextName(iface);
552 setHtmlElementDescription(iface);
553
554 for (int i = 0; i < iface->childCount(); ++i)
555 populateAccessibilityTree(iface->child(i));
556}
557
558void QWasmAccessibility::handleRadioButtonUpdate(QAccessibleEvent *event)
559{
560 switch (event->type()) {
561 case QAccessible::Focus:
562 case QAccessible::NameChanged: {
563 setHtmlElementTextName(event->accessibleInterface());
564 } break;
565 case QAccessible::StateChanged: {
566 QAccessibleInterface *accessible = event->accessibleInterface();
567 emscripten::val element = ensureHtmlElement(accessible);
568 std::string checkedString = accessible->state().checked ? "true" : "false";
569 element.call<void>("setAttribute", std::string("checked"), checkedString);
570 } break;
571 case QAccessible::DescriptionChanged: {
572 setHtmlElementDescription(event->accessibleInterface());
573 } break;
574 default:
575 qDebug() << "TODO: implement handleRadioButtonUpdate for event" << event->type();
576 break;
577 }
578}
579
580void QWasmAccessibility::handleSpinBoxUpdate(QAccessibleEvent *event)
581{
582 switch (event->type()) {
583 case QAccessible::Focus:
584 case QAccessible::NameChanged: {
585 setHtmlElementTextName(event->accessibleInterface());
586 } break;
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("value"), valueString);
592 } break;
593 case QAccessible::DescriptionChanged: {
594 setHtmlElementDescription(event->accessibleInterface());
595 } break;
596 default:
597 qDebug() << "TODO: implement handleSpinBoxUpdate for event" << event->type();
598 break;
599 }
600}
601
602void QWasmAccessibility::handleSliderUpdate(QAccessibleEvent *event)
603{
604 switch (event->type()) {
605 case QAccessible::Focus:
606 case QAccessible::NameChanged: {
607 setHtmlElementTextName(event->accessibleInterface());
608 } break;
609 case QAccessible::ValueChanged: {
610 QAccessibleInterface *accessible = event->accessibleInterface();
611 emscripten::val element = ensureHtmlElement(accessible);
612 std::string valueString = accessible->valueInterface()->currentValue().toString().toStdString();
613 element.call<void>("setAttribute", std::string("value"), valueString);
614 } break;
615 case QAccessible::DescriptionChanged: {
616 setHtmlElementDescription(event->accessibleInterface());
617 } break;
618 default:
619 qDebug() << "TODO: implement handleSliderUpdate for event" << event->type();
620 break;
621 }
622}
623
624void QWasmAccessibility::handleScrollBarUpdate(QAccessibleEvent *event)
625{
626 switch (event->type()) {
627 case QAccessible::Focus:
628 case QAccessible::NameChanged: {
629 setHtmlElementTextName(event->accessibleInterface());
630 } break;
631 case QAccessible::ValueChanged: {
632 QAccessibleInterface *accessible = event->accessibleInterface();
633 emscripten::val element = ensureHtmlElement(accessible);
634 std::string valueString = accessible->valueInterface()->currentValue().toString().toStdString();
635 element.call<void>("setAttribute", std::string("aria-valuenow"), valueString);
636 } break;
637 case QAccessible::DescriptionChanged: {
638 setHtmlElementDescription(event->accessibleInterface());
639 } break;
640 default:
641 qDebug() << "TODO: implement handleSliderUpdate for event" << event->type();
642 break;
643 }
644
645}
646
647void QWasmAccessibility::handlePageTabUpdate(QAccessibleEvent *event)
648{
649 switch (event->type()) {
650 case QAccessible::NameChanged: {
651 setHtmlElementTextName(event->accessibleInterface());
652 } break;
653 case QAccessible::Focus: {
654 setHtmlElementTextName(event->accessibleInterface());
655 } break;
656 case QAccessible::DescriptionChanged: {
657 setHtmlElementDescription(event->accessibleInterface());
658 } break;
659 default:
660 qDebug() << "TODO: implement handlePageTabUpdate for event" << event->type();
661 break;
662 }
663}
664
665void QWasmAccessibility::handlePageTabListUpdate(QAccessibleEvent *event)
666{
667 switch (event->type()) {
668 case QAccessible::NameChanged: {
669 setHtmlElementTextName(event->accessibleInterface());
670 } break;
671 case QAccessible::Focus: {
672 setHtmlElementTextName(event->accessibleInterface());
673 } break;
674 case QAccessible::DescriptionChanged: {
675 setHtmlElementDescription(event->accessibleInterface());
676 } break;
677 default:
678 qDebug() << "TODO: implement handlePageTabUpdate for event" << event->type();
679 break;
680 }
681}
682
683void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
684{
685 if (!m_accessibilityEnabled)
686 return;
687
688 QAccessibleInterface *iface = event->accessibleInterface();
689 if (!iface) {
690 qWarning() << "notifyAccessibilityUpdate with null a11y interface" ;
691 return;
692 }
693
694 // Handle some common event types. See
695 // https://doc.qt.io/qt-5/qaccessible.html#Event-enum
696 switch (event->type()) {
697 case QAccessible::ObjectShow:
698 setHtmlElementVisibility(iface, true);
699
700 // Sync up properties on show;
701 setHtmlElementGeometry(iface);
702 setHtmlElementTextName(iface);
703 setHtmlElementDescription(iface);
704
705 return;
706 break;
707 case QAccessible::ObjectHide:
708 setHtmlElementVisibility(iface, false);
709 return;
710 break;
711 // TODO: maybe handle more types here
712 default:
713 break;
714 };
715
716 // Switch on interface role, see
717 // https://doc.qt.io/qt-5/qaccessibleinterface.html#role
718 switch (iface->role()) {
719 case QAccessible::StaticText:
720 handleStaticTextUpdate(event);
721 break;
722 case QAccessible::Button:
723 handleStaticTextUpdate(event);
724 break;
725 case QAccessible::CheckBox:
726 handleCheckBoxUpdate(event);
727 break;
728 case QAccessible::EditableText:
729 handleLineEditUpdate(event);
730 break;
731 case QAccessible::Dialog:
732 handleDialogUpdate(event);
733 break;
734 case QAccessible::MenuItem:
735 case QAccessible::MenuBar:
736 case QAccessible::PopupMenu:
737 handleMenuUpdate(event);
738 break;
739 case QAccessible::ToolBar:
740 case QAccessible::ButtonMenu:
741 handleToolUpdate(event);
742 case QAccessible::RadioButton:
743 handleRadioButtonUpdate(event);
744 break;
745 case QAccessible::SpinBox:
746 handleSpinBoxUpdate(event);
747 break;
748 case QAccessible::Slider:
749 handleSliderUpdate(event);
750 break;
751 case QAccessible::PageTab:
752 handlePageTabUpdate(event);
753 break;
754 case QAccessible::PageTabList:
755 handlePageTabListUpdate(event);
756 break;
757 case QAccessible::ScrollBar:
758 handleScrollBarUpdate(event);
759 break;
760 default:
761 qCDebug(lcQpaAccessibility) << "TODO: implement notifyAccessibilityUpdate for role" << iface->role();
762 };
763}
764
765void QWasmAccessibility::setRootObject(QObject *root)
766{
767 m_rootObject = root;
768}
769
770void QWasmAccessibility::initialize()
771{
772
773}
774
775void QWasmAccessibility::cleanup()
776{
777
778}
779
780void QWasmAccessibility::onHtmlEventReceived(emscripten::val event)
781{
782 static_cast<QWasmAccessibility *>(QWasmIntegration::get()->accessibility())->handleEventFromHtmlElement(event);
783}
784
785EMSCRIPTEN_BINDINGS(qtButtonEvent) {
786 function("qtEventReceived", &QWasmAccessibility::onHtmlEventReceived);
787}
788
789#endif // QT_CONFIG(accessibility)
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:221
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:185
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:188
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromStdString(const std::string &s)
Definition qstring.h:1447
std::string toStdString() const
Returns a std::string object with the data contained in this QString.
Definition qstring.h:1444
static QWasmIntegration * get()
\inmodule QtGui
Definition qwindow.h:63
QString text
QPushButton * button
[2]
QSet< QString >::iterator it
QStringList effectiveActionNames(QAccessibleInterface *iface)
constexpr QBindableInterface iface
Definition qproperty.h:666
emscripten::val document()
Definition qwasmdom.h:49
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
static QDBusError::ErrorType get(const char *name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLuint64 key
struct _cl_event * event
GLuint GLfloat * val
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
EMSCRIPTEN_BINDINGS(qtClipboardModule)
aWidget window() -> setWindowTitle("New Window Title")
[2]