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, 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; }
/*! \reimp */ int QAccessibleWidget::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { if (!target) return -1; *target = 0; QObject *targetObject = 0; QWidgetList childList = childWidgets(widget()); bool complexWidget = childList.size() < childCount(); switch (relation) { // Hierarchical case Self: targetObject = object(); break; case Child: if (complexWidget) { if (entry > 0 && entry <= childCount()) return entry; return -1; }else { if (entry > 0 && childList.size() >= entry) targetObject = childList.at(entry - 1); } break; case Ancestor: { if (entry <= 0) return -1; targetObject = widget()->parentWidget(); int i; for (i = entry; i > 1 && targetObject; --i) targetObject = targetObject->parent(); if (!targetObject && i == 1) targetObject = qApp; } break; case Sibling: { QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(parentObject()); if (!iface) return -1; iface->navigate(Child, entry, target); delete iface; if (*target) return 0; } break; // Geometrical case QAccessible::Left: if (complexWidget && entry) { if (entry < 2 || widget()->height() > widget()->width() + 20) // looks vertical return -1; return entry - 1; } // fall through case QAccessible::Right: if (complexWidget && entry) { if (entry >= childCount() || widget()->height() > widget()->width() + 20) // looks vertical return -1; return entry + 1; } // fall through case QAccessible::Up: if (complexWidget && entry) { if (entry < 2 || widget()->width() > widget()->height() + 20) // looks horizontal return - 1; return entry - 1; } // fall through case QAccessible::Down: if (complexWidget && entry) { if (entry >= childCount() || widget()->width() > widget()->height() + 20) // looks horizontal return - 1; return entry + 1; } else { QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject()); if (!pIface) return -1; QRect startg = rect(0); QPoint startc = startg.center(); QAccessibleInterface *candidate = 0; int mindist = 100000; int sibCount = pIface->childCount(); for (int i = 0; i < sibCount; ++i) { QAccessibleInterface *sibling = 0; pIface->navigate(Child, i+1, &sibling); Q_ASSERT(sibling); if ((relationTo(0, sibling, 0) & Self) || (sibling->state(0) & QAccessible::Invisible)) { //ignore ourself and invisible siblings delete sibling; continue; } QRect sibg = sibling->rect(0); QPoint sibc = sibg.center(); QPoint sibp; QPoint startp; QPoint distp; switch (relation) { case QAccessible::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 QAccessible::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 QAccessible::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 QAccessible::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; } int dist = (int)qSqrt((qreal)distp.x() * distp.x() + distp.y() * distp.y()); if (dist < mindist) { delete candidate; candidate = sibling; mindist = dist; } else { delete sibling; } } delete pIface; *target = candidate; if (*target) return 0; } break; case Covers: if (entry > 0) { QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject()); if (!pIface) return -1; QRect r = rect(0); int sibCount = pIface->childCount(); QAccessibleInterface *sibling = 0; for (int i = pIface->indexOfChild(this) + 1; i <= sibCount && entry; ++i) { pIface->navigate(Child, i, &sibling); if (!sibling || (sibling->state(0) & Invisible)) { delete sibling; sibling = 0; continue; } if (sibling->rect(0).intersects(r)) --entry; if (!entry) break; delete sibling; sibling = 0; } delete pIface; *target = sibling; if (*target) return 0; } break; case Covered: if (entry > 0) { QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject()); if (!pIface) return -1; QRect r = rect(0); int index = pIface->indexOfChild(this); QAccessibleInterface *sibling = 0; for (int i = 1; i < index && entry; ++i) { pIface->navigate(Child, i, &sibling); Q_ASSERT(sibling); if (!sibling || (sibling->state(0) & Invisible)) { delete sibling; sibling = 0; continue; } if (sibling->rect(0).intersects(r)) --entry; if (!entry) break; delete sibling; sibling = 0; } delete pIface; *target = sibling; if (*target) return 0; } break; // Logical case FocusChild: { if (widget()->hasFocus()) { targetObject = object(); break; } QWidget *fw = widget()->focusWidget(); if (!fw) return -1; if (isAncestor(widget(), fw) || fw == widget()) targetObject = fw; /* ### QWidget *parent = fw; while (parent && !targetObject) { parent = parent->parentWidget(); if (parent == widget()) targetObject = fw; } */ } break; case Label: if (entry > 0) { QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject()); if (!pIface) return -1; // first check for all siblings that are labels to us // ideally we would go through all objects and check, but that // will be too expensive int sibCount = pIface->childCount(); QAccessibleInterface *candidate = 0; for (int i = 0; i < sibCount && entry; ++i) { const int childId = pIface->navigate(Child, i+1, &candidate); Q_ASSERT(childId >= 0); if (childId > 0) candidate = pIface; if (candidate->relationTo(childId, this, 0) & Label) --entry; if (!entry) break; if (candidate != pIface) delete candidate; candidate = 0; } if (!candidate) { if (pIface->relationTo(0, this, 0) & Label) --entry; if (!entry) candidate = pIface; } if (pIface != candidate) delete pIface; *target = candidate; if (*target) return 0; } break; case Labelled: // only implemented in subclasses break; case Controller: if (entry > 0) { // check all senders we are connected to, // and figure out which one are controllers to us QACConnectionObject *connectionObject = (QACConnectionObject*)object(); QObjectList allSenders = connectionObject->senderList(); QObjectList senders; for (int s = 0; s < allSenders.size(); ++s) { QObject *sender = allSenders.at(s); QAccessibleInterface *candidate = QAccessible::queryAccessibleInterface(sender); if (!candidate) continue; if (candidate->relationTo(0, this, 0)&Controller) senders << sender; delete candidate; } if (entry <= senders.size()) targetObject = senders.at(entry-1); } break; case Controlled: if (entry > 0) { QObjectList allReceivers; QACConnectionObject *connectionObject = (QACConnectionObject*)object(); for (int sig = 0; sig < d->primarySignals.count(); ++sig) { QObjectList receivers = connectionObject->receiverList(d->primarySignals.at(sig).toAscii()); allReceivers += receivers; } if (entry <= allReceivers.size()) targetObject = allReceivers.at(entry-1); } break; default: break; } *target = QAccessible::queryAccessibleInterface(targetObject); return *target ? 0 : -1; }
HRESULT STDMETHODCALLTYPE QWindowsAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd) { #ifdef DEBUG_SHOW_ATCLIENT_COMMANDS showDebug(__FUNCTION__, accessible); #endif //DEBUG_SHOW_ATCLIENT_COMMANDS if (!accessible->isValid()) return E_FAIL; QAccessibleInterface *acc = 0; int control = -1; switch(navDir) { case NAVDIR_FIRSTCHILD: control = accessible->navigate(Child, 1, &acc); break; case NAVDIR_LASTCHILD: control = accessible->navigate(Child, accessible->childCount(), &acc); break; case NAVDIR_NEXT: case NAVDIR_PREVIOUS: if (!varStart.lVal){ QAccessibleInterface *parent = 0; accessible->navigate(Ancestor, 1, &parent); if (parent) { int index = parent->indexOfChild(accessible); index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index > 0 && index <= parent->childCount()) control = parent->navigate(Child, index, &acc); delete parent; } } else { int index = varStart.lVal; index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index > 0 && index <= accessible->childCount()) control = accessible->navigate(Child, index, &acc); } break; case NAVDIR_UP: control = accessible->navigate(Up, varStart.lVal, &acc); break; case NAVDIR_DOWN: control = accessible->navigate(Down, varStart.lVal, &acc); break; case NAVDIR_LEFT: control = accessible->navigate(Left, varStart.lVal, &acc); break; case NAVDIR_RIGHT: control = accessible->navigate(Right, varStart.lVal, &acc); break; default: break; } if (control == -1) { (*pvarEnd).vt = VT_EMPTY; return S_FALSE; } if (!acc) { (*pvarEnd).vt = VT_I4; (*pvarEnd).lVal = control; return S_OK; } 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; }
/*! \reimp */ QAccessible::Relation QAccessibleWidget::relationTo(int child, const QAccessibleInterface *other, int otherChild) const { Relation relation = Unrelated; if (d->asking == this) // recursive call return relation; QObject *o = other ? other->object() : 0; if (!o) return relation; QWidget *focus = widget()->focusWidget(); if (object() == focus && isAncestor(o, focus)) relation |= FocusChild; QACConnectionObject *connectionObject = (QACConnectionObject*)object(); for (int sig = 0; sig < d->primarySignals.count(); ++sig) { if (connectionObject->isSender(o, d->primarySignals.at(sig).toAscii())) { relation |= Controller; break; } } // test for passive relationships. // d->asking protects from endless recursion. d->asking = this; int inverse = other->relationTo(otherChild, this, child); d->asking = 0; if (inverse & Controller) relation |= Controlled; if (inverse & Label) relation |= Labelled; if(o == object()) { if (child && !otherChild) return relation | Child; if (!child && otherChild) return relation | Ancestor; if (!child && !otherChild) return relation | Self; } QObject *parent = object()->parent(); if (o == parent) return relation | Child; if (o->parent() == parent) { relation |= Sibling; QAccessibleInterface *sibIface = QAccessible::queryAccessibleInterface(o); Q_ASSERT(sibIface); QRect wg = rect(0); QRect sg = sibIface->rect(0); if (wg.intersects(sg)) { QAccessibleInterface *pIface = 0; sibIface->navigate(Ancestor, 1, &pIface); if (pIface && !((sibIface->state(0) | state(0)) & Invisible)) { int wi = pIface->indexOfChild(this); int si = pIface->indexOfChild(sibIface); if (wi > si) relation |= QAccessible::Covers; else relation |= QAccessible::Covered; } delete pIface; } else { QPoint wc = wg.center(); QPoint sc = sg.center(); if (wc.x() < sc.x()) relation |= QAccessible::Left; else if(wc.x() > sc.x()) relation |= QAccessible::Right; if (wc.y() < sc.y()) relation |= QAccessible::Up; else if (wc.y() > sc.y()) relation |= QAccessible::Down; } delete sibIface; return relation; } if (isAncestor(o, object())) return relation | Descendent; if (isAncestor(object(), o)) return relation | Ancestor; return relation; }