void tst_QQuickAccessible::ignoredTest() { QScopedPointer<QQuickView> window(new QQuickView()); window->setSource(testFileUrl("ignored.qml")); window->show(); QQuickItem *contentItem = window->contentItem(); QVERIFY(contentItem); QQuickItem *rootItem = contentItem->childItems().first(); QVERIFY(rootItem); // the window becomes active QAccessible::State activatedChange; activatedChange.active = true; QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data()); QVERIFY(iface); QAccessibleInterface *rectangleA = iface->child(0); QCOMPARE(rectangleA->role(), QAccessible::StaticText); QCOMPARE(rectangleA->text(QAccessible::Name), QLatin1String("A")); static const char *expected = "BEFIHD"; // check if node "C" and "G" is skipped and that the order is as expected. for (int i = 0; i < rectangleA->childCount(); ++i) { QAccessibleInterface *child = rectangleA->child(i); QCOMPARE(child->text(QAccessible::Name), QString(QLatin1Char(expected[i]))); } QTestAccessibility::clearEvents(); }
void tst_QQuickAccessible::hitTest() { QQuickView *window = new QQuickView; window->setSource(testFileUrl("hittest.qml")); window->show(); QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window); QVERIFY(windowIface); QAccessibleInterface *rootItem = windowIface->child(0); QRect rootRect = rootItem->rect(); // check the root item from app QAccessibleInterface *appIface = QAccessible::queryAccessibleInterface(qApp); QVERIFY(appIface); QAccessibleInterface *itemHit = appIface->childAt(rootRect.x() + 200, rootRect.y() + 50); QVERIFY(itemHit); QCOMPARE(itemHit->rect(), rootRect); QAccessibleInterface *rootItemIface; for (int c = 0; c < rootItem->childCount(); ++c) { QAccessibleInterface *iface = rootItem->child(c); QString name = iface->text(QAccessible::Name); if (name == QLatin1String("rect1")) { // hit rect1 QAccessibleInterface *rect1 = iface; QRect rect1Rect = rect1->rect(); QAccessibleInterface *rootItemIface = rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10); QVERIFY(rootItemIface); QCOMPARE(rect1Rect, rootItemIface->rect()); QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect1")); // should also work from top level (app) QAccessibleInterface *app(QAccessible::queryAccessibleInterface(qApp)); QAccessibleInterface *itemHit2(topLevelChildAt(app, rect1Rect.x() + 10, rect1Rect.y() + 10)); QVERIFY(itemHit2); QCOMPARE(itemHit2->rect(), rect1Rect); QCOMPARE(itemHit2->text(QAccessible::Name), QLatin1String("rect1")); } else if (name == QLatin1String("rect2")) { QAccessibleInterface *rect2 = iface; // FIXME: This is seems broken on OS X // QCOMPARE(rect2->rect().translated(rootItem->rect().x(), rootItem->rect().y()), QRect(0, 50, 100, 100)); QAccessibleInterface *rect20 = rect2->child(0); QVERIFY(rect20); QCOMPARE(rect20->text(QAccessible::Name), QLatin1String("rect20")); QPoint p = rect20->rect().bottomRight() + QPoint(20, 20); QAccessibleInterface *rect201 = rect20->childAt(p.x(), p.y()); QVERIFY(rect201); QCOMPARE(rect201->text(QAccessible::Name), QLatin1String("rect201")); rootItemIface = topLevelChildAt(windowIface, p.x(), p.y()); QVERIFY(rootItemIface); QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect201")); } } delete window; QTestAccessibility::clearEvents(); }
void tst_QQuickAccessible::hitTest() { QQuickView *window = new QQuickView; window->setSource(testFileUrl("hittest.qml")); window->show(); QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window); QVERIFY(windowIface); QAccessibleInterface *rootItem = windowIface->child(0); QRect rootRect = rootItem->rect(); // check the root item from app QAccessibleInterface *appIface = QAccessible::queryAccessibleInterface(qApp); QVERIFY(appIface); QAccessibleInterface *itemHit(appIface->childAt(rootRect.x() + 200, rootRect.y() + 50)); QVERIFY(itemHit); QCOMPARE(rootRect, itemHit->rect()); // hit rect1 QAccessibleInterface *rect1(rootItem->child(0)); QRect rect1Rect = rect1->rect(); QAccessibleInterface *rootItemIface = rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10); QVERIFY(rootItemIface); QCOMPARE(rect1Rect, rootItemIface->rect()); QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect1")); // should also work from top level (app) QAccessibleInterface *app(QAccessible::queryAccessibleInterface(qApp)); QAccessibleInterface *itemHit2(topLevelChildAt(app, rect1Rect.x() + 10, rect1Rect.y() + 10)); QVERIFY(itemHit2); QCOMPARE(itemHit2->rect(), rect1Rect); QCOMPARE(itemHit2->text(QAccessible::Name), QLatin1String("rect1")); // hit rect201 QAccessibleInterface *rect2(rootItem->child(1)); QVERIFY(rect2); // FIXME: This is seems broken on mac // QCOMPARE(rect2->rect().translated(rootItem->rect().x(), rootItem->rect().y()), QRect(0, 50, 100, 100)); QAccessibleInterface *rect20(rect2->child(0)); QVERIFY(rect20); QAccessibleInterface *rect201(rect20->child(1)); QVERIFY(rect201); QRect rect201Rect = rect201->rect(); rootItemIface = windowIface->childAt(rect201Rect.x() + 20, rect201Rect.y() + 20); QVERIFY(rootItemIface); QCOMPARE(rootItemIface->rect(), rect201Rect); QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect201")); delete window; QTestAccessibility::clearEvents(); }
void tst_QQuickAccessible::basicPropertiesTest() { QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp); QCOMPARE(app->childCount(), 0); QQuickView *window = new QQuickView(); window->setSource(testFileUrl("statictext.qml")); window->show(); QCOMPARE(app->childCount(), 1); QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); QVERIFY(iface); QCOMPARE(iface->childCount(), 1); QAccessibleInterface *item = iface->child(0); QVERIFY(item); QCOMPARE(item->childCount(), 2); QCOMPARE(item->rect().size(), QSize(400, 400)); QCOMPARE(item->role(), QAccessible::Client); QCOMPARE(iface->indexOfChild(item), 0); QAccessibleInterface *text = item->child(0); QVERIFY(text); QCOMPARE(text->childCount(), 0); QCOMPARE(text->text(QAccessible::Name), QLatin1String("Hello Accessibility")); QCOMPARE(text->rect().size(), QSize(200, 50)); QCOMPARE(text->rect().x(), item->rect().x() + 100); QCOMPARE(text->rect().y(), item->rect().y() + 20); QCOMPARE(text->role(), QAccessible::StaticText); QCOMPARE(item->indexOfChild(text), 0); QAccessibleInterface *text2 = item->child(1); QVERIFY(text2); QCOMPARE(text2->childCount(), 0); QCOMPARE(text2->text(QAccessible::Name), QLatin1String("The Hello 2 accessible text")); QCOMPARE(text2->rect().size(), QSize(100, 40)); QCOMPARE(text2->rect().x(), item->rect().x() + 100); QCOMPARE(text2->rect().y(), item->rect().y() + 40); QCOMPARE(text2->role(), QAccessible::StaticText); QCOMPARE(item->indexOfChild(text2), 1); QCOMPARE(iface->indexOfChild(text2), -1); QCOMPARE(text2->indexOfChild(item), -1); delete window; QTestAccessibility::clearEvents(); }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; if (varChildID.vt == VT_EMPTY) return E_INVALIDARG; int childIndex = varChildID.lVal; QAccessibleInterface *acc = 0; if (childIndex < 0) { const int entry = childIndex; QPair<QObject*, int> ref = qAccessibleRecentSentEvents()->value(entry); if (ref.first) { acc = QAccessible::queryAccessibleInterface(ref.first); if (acc && ref.second) { if (ref.second) { QAccessibleInterface *res = acc->child(ref.second - 1); delete acc; if (!res) return E_INVALIDARG; acc = res; } } } } else { if (childIndex) { acc = accessible->child(childIndex - 1); } else { // FIXME Q_ASSERT(0); } } if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); wacc->QueryInterface(IID_IDispatch, (void**)ppdispChild); return S_OK; } *ppdispChild = 0; return S_FALSE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) { accessibleDebugClientCalls(accessible); if (!accessible->isValid()) return E_FAIL; if (varChildID.vt != VT_I4) return E_INVALIDARG; int childIndex = varChildID.lVal; QAccessibleInterface *acc = 0; if (childIndex < 0) { const int entry = childIndex; QPair<QObject*, int> ref = QWindowsAccessibility::getCachedObject(entry); if (ref.first) { acc = QAccessible::queryAccessibleInterface(ref.first); if (acc && ref.second >= 0) { QAccessibleInterface *res = acc->child(ref.second); delete acc; if (!res) return E_INVALIDARG; acc = res; } } else { qWarning("get_accChild got a negative varChildID, but did not find it in cache"); } } else { if (childIndex) { acc = accessible->child(childIndex - 1); } else { // Yes, some AT clients (Active Accessibility Object Inspector) // actually ask for the same object. As a consequence, we need to clone ourselves: if (QAccessibleInterface *par = accessible->parent()) { const int indexOf = par->indexOfChild(accessible); QAccessibleInterface *clone = par->child(indexOf); delete par; acc = clone; } } } if (acc) { *ppdispChild = QWindowsAccessibility::wrap(acc); return S_OK; } return E_FAIL; }
static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId) { QAccessibleInterface *iface = interfaceFromId(objectId); if (iface) { jintArray jArray = env->NewIntArray(jsize(iface->childCount())); for (int i = 0; i < iface->childCount(); ++i) { QAccessibleInterface *child = iface->child(i); if (child) { QAccessible::Id ifaceId = QAccessible::uniqueId(child); jint jid = ifaceId; env->SetIntArrayRegion(jArray, i, 1, &jid); } } return jArray; } return env->NewIntArray(jsize(0)); }
/* Recursiveley navigates the accessible hiearchy looking for an interface that passsed the Test (meaning it returns true). */ QAccessibleInterface *WidgetNavigator::recursiveSearch(TestBase *test, QAccessibleInterface *iface) { QStack<QAccessibleInterface *> todoInterfaces; todoInterfaces.push(iface); while (todoInterfaces.isEmpty() == false) { QAccessibleInterface *testInterface = todoInterfaces.pop(); if ((*test)(testInterface)) return testInterface; const int numChildren = testInterface->childCount(); for (int i = 0; i < numChildren; ++i) { QAccessibleInterface *childInterface = testInterface->child(i); if (childInterface) { todoInterfaces.push(childInterface); } } } return 0; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; QRect rect; if (varID.lVal) { QAIPointer child = QAIPointer(accessible->child(varID.lVal - 1)); if (child->isValid()) rect = child->rect(); } else { rect = accessible->rect(); } *pxLeft = rect.x(); *pyTop = rect.y(); *pcxWidth = rect.width(); *pcyHeight = rect.height(); return S_OK; }
HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accSelection(VARIANT *pvarChildren) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; int cc = accessible->childCount(); QVector<int> sel(cc); int selIndex = 0; for (int i = 0; i < cc; ++i) { bool isSelected = false; QAccessibleInterface *child = accessible->child(i); if (child) { isSelected = child->state().selected; delete child; } if (isSelected) sel[selIndex++] = i+1; } sel.resize(selIndex); if (sel.isEmpty()) { (*pvarChildren).vt = VT_EMPTY; return S_FALSE; } if (sel.size() == 1) { (*pvarChildren).vt = VT_I4; (*pvarChildren).lVal = sel[0]; return S_OK; } IEnumVARIANT *iface = new QWindowsEnumerate(sel); IUnknown *uiface; iface->QueryInterface(IID_IUnknown, (void**)&uiface); (*pvarChildren).vt = VT_UNKNOWN; (*pvarChildren).punkVal = uiface; return S_OK; }
void tst_QQuickAccessible::checkableTest() { QScopedPointer<QQuickView> window(new QQuickView()); window->setSource(testFileUrl("checkbuttons.qml")); window->show(); QQuickItem *contentItem = window->contentItem(); QVERIFY(contentItem); QQuickItem *rootItem = contentItem->childItems().first(); QVERIFY(rootItem); // the window becomes active QAccessible::State activatedChange; activatedChange.active = true; QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window.data()); QVERIFY(iface); QAccessibleInterface *root = iface->child(0); QAccessibleInterface *button1 = root->child(0); QCOMPARE(button1->role(), QAccessible::Button); QVERIFY(!(button1->state().checked)); QVERIFY(!(button1->state().checkable)); QVERIFY(button1->state().focusable); QVERIFY(!button1->state().focused); QTestAccessibility::clearEvents(); // set properties QQuickItem *button1item = qobject_cast<QQuickItem*>(rootItem->childItems().at(0)); QVERIFY(button1item); QCOMPARE(button1item->objectName(), QLatin1String("button1")); button1item->forceActiveFocus(); QVERIFY(button1->state().focusable); QVERIFY(button1->state().focused); QAccessibleEvent focusEvent(button1item, QAccessible::Focus); QVERIFY_EVENT(&focusEvent); QAccessibleInterface *button2 = root->child(1); QVERIFY(!(button2->state().checked)); QVERIFY(button2->state().checkable); QQuickItem *button2item = qobject_cast<QQuickItem*>(rootItem->childItems().at(1)); QVERIFY(button2item); QCOMPARE(button2item->objectName(), QLatin1String("button2")); QAccessibleInterface *button3 = root->child(2); QVERIFY(button3->state().checked); QVERIFY(button3->state().checkable); QAccessibleInterface *checkBox1 = root->child(3); QCOMPARE(checkBox1->role(), QAccessible::CheckBox); QVERIFY(checkBox1->state().checked); QVERIFY(checkBox1->state().checkable); QQuickItem *checkbox1item = qobject_cast<QQuickItem*>(rootItem->childItems().at(3)); QVERIFY(checkbox1item); QCOMPARE(checkbox1item->objectName(), QLatin1String("checkbox1")); checkbox1item->setProperty("checked", false); QVERIFY(!checkBox1->state().checked); QAccessible::State checkState; checkState.checked = true; QAccessibleStateChangeEvent checkChanged(checkbox1item, checkState); QVERIFY_EVENT(&checkChanged); checkbox1item->setProperty("checked", true); QVERIFY(checkBox1->state().checked); QVERIFY_EVENT(&checkChanged); QAccessibleInterface *checkBox2 = root->child(4); QVERIFY(!(checkBox2->state().checked)); QVERIFY(checkBox2->state().checkable); QTestAccessibility::clearEvents(); }
// moz: [important, but no need to implement up/down/left/right] HRESULT STDMETHODCALLTYPE QWindowsAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; QAccessibleInterface *acc = 0; switch (navDir) { case NAVDIR_FIRSTCHILD: acc = accessible->child(0); break; case NAVDIR_LASTCHILD: acc = accessible->child(accessible->childCount() - 1); break; case NAVDIR_NEXT: case NAVDIR_PREVIOUS: if (!varStart.lVal){ QAccessibleInterface *parent = accessible->parent(); if (parent) { int index = parent->indexOfChild(accessible); index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index >= 0 && index < parent->childCount()) acc = parent->child(index); delete parent; } } else { int index = varStart.lVal; index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index > 0 && index <= accessible->childCount()) acc = accessible->child(index - 1); } break; // Geometrical case NAVDIR_UP: case NAVDIR_DOWN: case NAVDIR_LEFT: case NAVDIR_RIGHT: if (QAccessibleInterface *pIface = accessible->parent()) { QRect startg = accessible->rect(); QPoint startc = startg.center(); QAccessibleInterface *candidate = 0; unsigned mindist = UINT_MAX; // will work on screen sizes at least up to 46340x46340 const int sibCount = pIface->childCount(); for (int i = 0; i < sibCount; ++i) { QAccessibleInterface *sibling = 0; sibling = pIface->child(i); Q_ASSERT(sibling); if ((accessible->relationTo(sibling) & QAccessible::Self) || sibling->state().invisible) { //ignore ourself and invisible siblings delete sibling; continue; } QRect sibg = sibling->rect(); QPoint sibc = sibg.center(); QPoint sibp; QPoint startp; QPoint distp; switch (navDir) { case NAVDIR_LEFT: startp = QPoint(startg.left(), startg.top() + startg.height() / 2); sibp = QPoint(sibg.right(), sibg.top() + sibg.height() / 2); if (QPoint(sibc - startc).x() >= 0) { delete sibling; continue; } distp = sibp - startp; break; case NAVDIR_RIGHT: startp = QPoint(startg.right(), startg.top() + startg.height() / 2); sibp = QPoint(sibg.left(), sibg.top() + sibg.height() / 2); if (QPoint(sibc - startc).x() <= 0) { delete sibling; continue; } distp = sibp - startp; break; case NAVDIR_UP: startp = QPoint(startg.left() + startg.width() / 2, startg.top()); sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.bottom()); if (QPoint(sibc - startc).y() >= 0) { delete sibling; continue; } distp = sibp - startp; break; case NAVDIR_DOWN: startp = QPoint(startg.left() + startg.width() / 2, startg.bottom()); sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.top()); if (QPoint(sibc - startc).y() <= 0) { delete sibling; continue; } distp = sibp - startp; break; default: break; } // Since we're *comparing* (and not measuring) distances, we can compare the // squared distance, (thus, no need to take the sqrt()). unsigned dist = distp.x() * distp.x() + distp.y() * distp.y(); if (dist < mindist) { delete candidate; candidate = sibling; mindist = dist; } else { delete sibling; } } delete pIface; acc = candidate; } break; default: break; } if (!acc) { (*pvarEnd).vt = VT_EMPTY; return S_FALSE; } QWindowsAccessible* wacc = new QWindowsAccessible(acc); IDispatch *iface = 0; wacc->QueryInterface(IID_IDispatch, (void**)&iface); if (iface) { (*pvarEnd).vt = VT_DISPATCH; (*pvarEnd).pdispVal = iface; return S_OK; } else { delete wacc; } (*pvarEnd).vt = VT_EMPTY; return S_FALSE; }
QAIPointer childPointer(VARIANT varID) { return QAIPointer(accessible->child(varID.lVal - 1)); }