wasm: a11y - Implement ParentChanged callback

Implement the QAccessible::ParentChanged callback.

Probably not possible with a perfect implementation.

What  can reasonably be done is to issue ParentChanged on;
QWidget::setParent, QWindow::setParent, QQuickItem::setParentItem.

However calling these functions do not necessarily map 1:1 to QAccessibleInterface::parent().

Fixes: QTBUG-138802
Fixes: QTBUG-134923
Change-Id: Iac4ac7843ba904bf0b7aa9964ee68a94294ad979
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Even Oscar Andersen 2025-08-28 12:04:33 +02:00
parent 26600c5c6d
commit 835c0d9117
5 changed files with 75 additions and 0 deletions

View File

@ -856,6 +856,12 @@ void QWindow::setParent(QWindow *parent)
QEvent parentChangedEvent(QEvent::ParentWindowChange);
QCoreApplication::sendEvent(this, &parentChangedEvent);
#if QT_CONFIG(accessibility)
if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing) {
QAccessibleEvent qaEvent(this, QAccessible::ParentChanged);
QAccessible::updateAccessibility(&qaEvent);
}
#endif
}
/*!

View File

@ -955,6 +955,35 @@ void QWasmAccessibility::removeObject(QAccessibleInterface *iface)
}
}
void QWasmAccessibility::unlinkParentForChildren(QAccessibleInterface *iface)
{
auto element = getHtmlElement(iface);
if (!element.isUndefined()) {
auto oldContainer = element["parentElement"];
auto newContainer = getElementContainer(iface);
if (!oldContainer.isUndefined() &&
!oldContainer.isNull() &&
oldContainer != newContainer) {
oldContainer.call<void>("removeChild", element);
}
}
for (int i = 0; i < iface->childCount(); ++i)
unlinkParentForChildren(iface->child(i));
}
void QWasmAccessibility::relinkParentForChildren(QAccessibleInterface *iface)
{
auto element = getHtmlElement(iface);
if (!element.isUndefined()) {
if (element["parentElement"].isUndefined() ||
element["parentElement"].isNull()) {
linkToParent(iface);
}
}
for (int i = 0; i < iface->childCount(); ++i)
relinkParentForChildren(iface->child(i));
}
void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
{
if (!m_accessibilityEnabled)
@ -983,6 +1012,11 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
createObject(iface);
break;
case QAccessible::ParentChanged:
unlinkParentForChildren(iface);
relinkParentForChildren(iface);
break;
default:
break;
};

View File

@ -94,6 +94,9 @@ private:
void populateAccessibilityTree(QAccessibleInterface *iface);
void createObject(QAccessibleInterface *iface);
void removeObject(QAccessibleInterface *iface);
void unlinkParentForChildren(QAccessibleInterface *iface);
void relinkParentForChildren(QAccessibleInterface *iface);
void notifyAccessibilityUpdate(QAccessibleEvent *event) override;
void setRootObject(QObject *o) override;
void initialize() override;

View File

@ -10960,6 +10960,14 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
}
}
}
#if QT_CONFIG(accessibility)
if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing) {
QAccessibleEvent qaEvent(this, QAccessible::ParentChanged);
QAccessible::updateAccessibility(&qaEvent);
}
#endif
}
void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f)

View File

@ -237,6 +237,8 @@ private slots:
void widgetLocaleTest();
void noInterfacesBeforeSetActive();
void parentChangedEvent();
protected slots:
void onClicked();
private:
@ -4859,5 +4861,27 @@ void tst_QAccessibility::noInterfacesBeforeSetActive()
}
}
void tst_QAccessibility::parentChangedEvent()
{
{
QMainWindow mainWindow;
QWidget w;
QTestAccessibility::clearEvents();
w.setParent(&mainWindow);
QAccessibleEvent parentChangedEvent(&w, QAccessible::ParentChanged);
QVERIFY(QTestAccessibility::containsEvent(&parentChangedEvent));
}
{
QWindow mainWindow;
QWindow w;
QTestAccessibility::clearEvents();
w.setParent(&mainWindow);
QAccessibleEvent parentChangedEvent(&w, QAccessible::ParentChanged);
QVERIFY(QTestAccessibility::containsEvent(&parentChangedEvent));
}
}
QTEST_MAIN(tst_QAccessibility)
#include "tst_qaccessibility.moc"