1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtCore/qglobal.h>
31#ifdef Q_OS_WIN
32# include <QtCore/qt_windows.h>
33#ifndef Q_OS_WINRT
34# include <oleacc.h>
35# include <QtWindowsUIAutomationSupport/private/qwindowsuiawrapper_p.h>
36#endif
37# include <servprov.h>
38# include <winuser.h>
39#endif
40#include <QtTest/QtTest>
41#include <QtGui>
42#include <QtWidgets>
43#include <math.h>
44#include <qpa/qplatformnativeinterface.h>
45#include <qpa/qplatformintegration.h>
46#include <qpa/qplatformaccessibility.h>
47#include <QtGui/private/qguiapplication_p.h>
48#include <QtGui/private/qhighdpiscaling_p.h>
49
50#if defined(Q_OS_WIN) && defined(interface)
51# undef interface
52#endif
53
54#include "QtTest/qtestaccessible.h"
55
56#include <algorithm>
57
58#include "accessiblewidgets.h"
59
60#include <QtTest/private/qtesthelpers_p.h>
61
62using namespace QTestPrivate;
63
64static inline bool verifyChild(QWidget *child, QAccessibleInterface *interface,
65 int index, const QRect &domain)
66{
67 if (!child) {
68 qWarning(msg: "tst_QAccessibility::verifyChild: null pointer to child.");
69 return false;
70 }
71
72 if (!interface) {
73 qWarning(msg: "tst_QAccessibility::verifyChild: null pointer to interface.");
74 return false;
75 }
76
77 // Verify that we get a valid QAccessibleInterface for the child.
78 QAccessibleInterface *childInterface(QAccessible::queryAccessibleInterface(child));
79 if (!childInterface) {
80 qWarning(msg: "tst_QAccessibility::verifyChild: Failed to retrieve interface for child.");
81 return false;
82 }
83
84 // QAccessibleInterface::indexOfChild():
85 // Verify that indexOfChild() returns an index equal to the index passed in
86 int indexFromIndexOfChild = interface->indexOfChild(childInterface);
87 if (indexFromIndexOfChild != index) {
88 qWarning(msg: "tst_QAccessibility::verifyChild (indexOfChild()):");
89 qWarning() << "Expected:" << index;
90 qWarning() << "Actual: " << indexFromIndexOfChild;
91 return false;
92 }
93
94 // Navigate to child, compare its object and role with the interface from queryAccessibleInterface(child).
95 QAccessibleInterface *navigatedChildInterface(interface->child(index));
96 if (!navigatedChildInterface)
97 return false;
98
99 const QRect rectFromInterface = navigatedChildInterface->rect();
100
101 // QAccessibleInterface::childAt():
102 // Calculate global child position and check that the interface
103 // returns the correct index for that position.
104 QPoint globalChildPos = child->mapToGlobal(QPoint(0, 0));
105 QAccessibleInterface *childAtInterface(interface->childAt(x: globalChildPos.x(), y: globalChildPos.y()));
106 if (!childAtInterface) {
107 qWarning(msg: "tst_QAccessibility::verifyChild (childAt()):");
108 qWarning() << "Expected:" << childInterface;
109 qWarning() << "Actual: no child";
110 return false;
111 }
112 if (childAtInterface->object() != childInterface->object()) {
113 qWarning(msg: "tst_QAccessibility::verifyChild (childAt()):");
114 qWarning() << "Expected:" << childInterface;
115 qWarning() << "Actual: " << childAtInterface;
116 return false;
117 }
118
119 // QAccessibleInterface::rect():
120 // Calculate global child geometry and check that the interface
121 // returns a QRect which is equal to the calculated QRect.
122 const QRect expectedGlobalRect = QRect(globalChildPos, child->size());
123 if (expectedGlobalRect != rectFromInterface) {
124 qWarning(msg: "tst_QAccessibility::verifyChild (rect()):");
125 qWarning() << "Expected:" << expectedGlobalRect;
126 qWarning() << "Actual: " << rectFromInterface;
127 return false;
128 }
129
130 // Verify that the child is within its domain.
131 if (!domain.contains(r: rectFromInterface)) {
132 qWarning(msg: "tst_QAccessibility::verifyChild: Child is not within its domain.");
133 return false;
134 }
135
136 return true;
137}
138
139#define EXPECT(cond) \
140 do { \
141 if (!errorAt && !(cond)) { \
142 errorAt = __LINE__; \
143 qWarning("level: %d, role: %d (%s)", treelevel, iface->role(), #cond); \
144 break; \
145 } \
146 } while (0)
147
148static int verifyHierarchy(QAccessibleInterface *iface)
149{
150 int errorAt = 0;
151 static int treelevel = 0; // for error diagnostics
152 QAccessibleInterface *if2 = 0;
153 ++treelevel;
154 for (int i = 0; i < iface->childCount() && !errorAt; ++i) {
155 if2 = iface->child(index: i);
156 EXPECT(if2 != 0);
157 EXPECT(iface->indexOfChild(if2) == i);
158 // navigate Ancestor
159 QAccessibleInterface *parent = if2->parent();
160 EXPECT(iface->object() == parent->object());
161 EXPECT(iface == parent);
162
163 // verify children
164 if (!errorAt)
165 errorAt = verifyHierarchy(iface: if2);
166 }
167
168 --treelevel;
169 return errorAt;
170}
171
172QRect childRect(QAccessibleInterface *iface, int index = 0)
173{
174 return iface->child(index)->rect();
175}
176
177class tst_QAccessibility : public QObject
178{
179 Q_OBJECT
180public:
181 tst_QAccessibility();
182
183public slots:
184 void initTestCase();
185 void cleanupTestCase();
186 void init();
187 void cleanup();
188private slots:
189 void eventTest();
190 void customWidget();
191 void deletedWidget();
192 void subclassedWidget();
193
194 void statesStructTest();
195 void navigateHierarchy();
196 void sliderTest();
197 void textAttributes_data();
198 void textAttributes();
199 void hideShowTest();
200
201 void actionTest();
202
203 void applicationTest();
204 void mainWindowTest();
205 void subWindowTest();
206 void buttonTest();
207 void scrollBarTest();
208 void tabTest();
209 void tabWidgetTest();
210 void menuTest();
211 void spinBoxTest();
212 void doubleSpinBoxTest();
213 void textEditTest();
214 void textBrowserTest();
215 void mdiAreaTest();
216 void mdiSubWindowTest();
217 void lineEditTest();
218 void lineEditTextFunctions_data();
219 void lineEditTextFunctions();
220 void textInterfaceTest_data();
221 void textInterfaceTest();
222 void groupBoxTest();
223 void dialogButtonBoxTest();
224 void dialTest();
225 void rubberBandTest();
226 void abstractScrollAreaTest();
227 void scrollAreaTest();
228
229 void listTest();
230 void treeTest();
231 void tableTest();
232
233 void calendarWidgetTest();
234 void dockWidgetTest();
235 void comboBoxTest();
236 void accessibleName();
237 void labelTest();
238 void accelerators();
239 void bridgeTest();
240 void focusChild();
241
242protected slots:
243 void onClicked();
244private:
245 int click_count;
246};
247
248QAccessible::State state(QWidget * const widget)
249{
250 QAccessibleInterface *iface(QAccessible::queryAccessibleInterface(widget));
251 if (!iface) {
252 qWarning() << "Cannot get QAccessibleInterface for widget";
253 return QAccessible::State();
254 }
255 return iface->state();
256}
257
258tst_QAccessibility::tst_QAccessibility()
259{
260 click_count = 0;
261}
262
263void tst_QAccessibility::onClicked()
264{
265 click_count++;
266}
267
268void tst_QAccessibility::initTestCase()
269{
270 QTestAccessibility::initialize();
271 QPlatformIntegration *pfIntegration = QGuiApplicationPrivate::platformIntegration();
272 if (!pfIntegration->accessibility())
273 QSKIP("This platform does not support accessibility");
274 pfIntegration->accessibility()->setActive(true);
275}
276
277void tst_QAccessibility::cleanupTestCase()
278{
279 QTestAccessibility::cleanup();
280}
281
282void tst_QAccessibility::init()
283{
284 QTestAccessibility::clearEvents();
285}
286
287void tst_QAccessibility::cleanup()
288{
289 const EventList list = QTestAccessibility::events();
290 if (!list.isEmpty()) {
291 qWarning(msg: "%d accessibility event(s) were not handled in testfunction '%s':", list.count(),
292 QString(QTest::currentTestFunction()).toLatin1().constData());
293 for (int i = 0; i < list.count(); ++i)
294 qWarning(msg: " %d: Object: %p Event: '%s' Child: %d", i + 1, list.at(i)->object(),
295 qAccessibleEventString(event: list.at(i)->type()), list.at(i)->child());
296 }
297 QTestAccessibility::clearEvents();
298 QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty());
299}
300
301void tst_QAccessibility::eventTest()
302{
303 QPushButton* button = new QPushButton(0);
304 QAccessible::queryAccessibleInterface(button);
305 button->setObjectName(QString("Olaf"));
306 setFrameless(button);
307
308 button->show();
309 QAccessibleEvent showEvent(button, QAccessible::ObjectShow);
310 // some platforms might send other events first, (such as state change event active=1)
311 QVERIFY(QTestAccessibility::containsEvent(&showEvent));
312 button->setFocus(Qt::MouseFocusReason);
313 QTestAccessibility::clearEvents();
314 QTest::mouseClick(widget: button, button: Qt::LeftButton, stateKey: { });
315
316 button->setAccessibleName("Olaf the second");
317 QAccessibleEvent nameEvent(button, QAccessible::NameChanged);
318 QVERIFY_EVENT(&nameEvent);
319 button->setAccessibleDescription("This is a button labeled Olaf");
320 QAccessibleEvent descEvent(button, QAccessible::DescriptionChanged);
321 QVERIFY_EVENT(&descEvent);
322
323 button->hide();
324 QAccessibleEvent hideEvent(button, QAccessible::ObjectHide);
325 // some platforms might send other events first, (such as state change event active=1)
326 QVERIFY(QTestAccessibility::containsEvent(&hideEvent));
327
328 delete button;
329
330 // Make sure that invalid events don't bring down the system
331 // these events can be in user code.
332 QWidget *widget = new QWidget();
333 QAccessibleEvent ev1(widget, QAccessible::Focus);
334 QAccessible::updateAccessibility(event: &ev1);
335
336 QAccessibleEvent ev2(widget, QAccessible::Focus);
337 ev2.setChild(7);
338 QAccessible::updateAccessibility(event: &ev2);
339 delete widget;
340
341 QObject *object = new QObject();
342 QAccessibleEvent ev3(object, QAccessible::Focus);
343 QAccessible::updateAccessibility(event: &ev3);
344 delete object;
345
346 QTestAccessibility::clearEvents();
347}
348
349void tst_QAccessibility::customWidget()
350{
351 {
352 QtTestAccessibleWidget* widget = new QtTestAccessibleWidget(0, "Heinz");
353 widget->show();
354 QVERIFY(QTest::qWaitForWindowExposed(widget));
355 // By default we create QAccessibleWidget
356 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget);
357 QVERIFY(iface != 0);
358 QVERIFY(iface->isValid());
359 QCOMPARE(iface->object(), (QObject*)widget);
360 QCOMPARE(iface->object()->objectName(), QString("Heinz"));
361 QCOMPARE(iface->rect().height(), widget->height());
362 QCOMPARE(iface->text(QAccessible::Help), QString());
363 QCOMPARE(iface->rect().height(), widget->height());
364 delete widget;
365 }
366 {
367 QAccessible::installFactory(QtTestAccessibleWidgetIface::ifaceFactory);
368 QtTestAccessibleWidget* widget = new QtTestAccessibleWidget(0, "Heinz");
369 widget->show();
370 QVERIFY(QTest::qWaitForWindowExposed(widget));
371 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget);
372 QVERIFY(iface != 0);
373 QVERIFY(iface->isValid());
374 QCOMPARE(iface->object(), (QObject*)widget);
375 QCOMPARE(iface->object()->objectName(), QString("Heinz"));
376 QCOMPARE(iface->rect().height(), widget->height());
377 // The help text is only set if our factory works
378 QCOMPARE(iface->text(QAccessible::Help), QString("Help yourself"));
379 delete widget;
380 }
381 {
382 // A subclass of any class should still get the right QAccessibleInterface
383 QtTestAccessibleWidgetSubclass* subclassedWidget = new QtTestAccessibleWidgetSubclass(0, "Hans");
384 QAccessibleInterface *subIface = QAccessible::queryAccessibleInterface(subclassedWidget);
385 QVERIFY(subIface != 0);
386 QVERIFY(subIface->isValid());
387 QCOMPARE(subIface->object(), (QObject*)subclassedWidget);
388 QCOMPARE(subIface->text(QAccessible::Help), QString("Help yourself"));
389 delete subclassedWidget;
390 }
391 QTestAccessibility::clearEvents();
392}
393
394void tst_QAccessibility::deletedWidget()
395{
396 QtTestAccessibleWidget *widget = new QtTestAccessibleWidget(0, "Ralf");
397 QAccessible::installFactory(QtTestAccessibleWidgetIface::ifaceFactory);
398 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget);
399 QVERIFY(iface != 0);
400 QVERIFY(iface->isValid());
401 QCOMPARE(iface->object(), (QObject*)widget);
402
403 delete widget;
404 widget = 0;
405 // fixme: QVERIFY(!iface->isValid());
406}
407
408void tst_QAccessibility::subclassedWidget()
409{
410 KFooButton button("Ploink", 0);
411 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&button);
412 QVERIFY(iface);
413 QCOMPARE(iface->object(), (QObject*)&button);
414 QCOMPARE(iface->text(QAccessible::Name), button.text());
415 QTestAccessibility::clearEvents();
416}
417
418void tst_QAccessibility::statesStructTest()
419{
420 QAccessible::State s1;
421 QVERIFY(s1.disabled == 0);
422 QVERIFY(s1.focusable == 0);
423 QVERIFY(s1.modal == 0);
424
425 QAccessible::State s2;
426 QCOMPARE(s2, s1);
427 s2.busy = true;
428 QVERIFY(!(s2 == s1));
429 s1.busy = true;
430 QCOMPARE(s2, s1);
431 s1 = QAccessible::State();
432 QVERIFY(!(s2 == s1));
433 s1 = s2;
434 QCOMPARE(s2, s1);
435 QVERIFY(s1.busy == 1);
436}
437
438void tst_QAccessibility::sliderTest()
439{
440 {
441 QSlider *slider = new QSlider(0);
442 setFrameless(slider);
443 slider->setObjectName(QString("Slidy"));
444 slider->show();
445 QAccessibleInterface *iface(QAccessible::queryAccessibleInterface(slider));
446 QVERIFY(iface);
447 QVERIFY(iface->isValid());
448
449 QCOMPARE(iface->childCount(), 0);
450 QCOMPARE(iface->role(), QAccessible::Slider);
451
452 QAccessibleValueInterface *valueIface = iface->valueInterface();
453 QVERIFY(valueIface != 0);
454 QCOMPARE(valueIface->minimumValue().toInt(), slider->minimum());
455 QCOMPARE(valueIface->maximumValue().toInt(), slider->maximum());
456 QCOMPARE(valueIface->minimumStepSize().toInt(), slider->singleStep());
457 slider->setValue(50);
458 QCOMPARE(valueIface->currentValue().toInt(), slider->value());
459 slider->setValue(0);
460 QCOMPARE(valueIface->currentValue().toInt(), slider->value());
461 slider->setValue(100);
462 QCOMPARE(valueIface->currentValue().toInt(), slider->value());
463 valueIface->setCurrentValue(77);
464 QCOMPARE(77, slider->value());
465 slider->setSingleStep(2);
466 QCOMPARE(valueIface->minimumStepSize().toInt(), 2);
467
468 delete slider;
469 }
470 QTestAccessibility::clearEvents();
471}
472
473void tst_QAccessibility::navigateHierarchy()
474{
475 {
476 QWidget *w = new QWidget(0);
477 w->setObjectName(QString("Hans"));
478 w->show();
479 QWidget *w1 = new QWidget(w);
480 w1->setObjectName(QString("1"));
481 w1->show();
482 QWidget *w2 = new QWidget(w);
483 w2->setObjectName(QString("2"));
484 w2->show();
485 QWidget *w3 = new QWidget(w);
486 w3->setObjectName(QString("3"));
487 w3->show();
488 QWidget *w31 = new QWidget(w3);
489 w31->setObjectName(QString("31"));
490 w31->show();
491
492 QAccessibleInterface *ifaceW(QAccessible::queryAccessibleInterface(w));
493 QVERIFY(ifaceW != 0);
494 QVERIFY(ifaceW->isValid());
495
496 QAccessibleInterface *target = ifaceW->child(index: 14);
497 QVERIFY(!target);
498 target = ifaceW->child(index: -1);
499 QVERIFY(!target);
500 target = ifaceW->child(index: 0);
501 QAccessibleInterface *interfaceW1(ifaceW->child(index: 0));
502 QVERIFY(target);
503 QVERIFY(target->isValid());
504 QCOMPARE(target->object(), (QObject*)w1);
505 QVERIFY(interfaceW1 != 0);
506 QVERIFY(interfaceW1->isValid());
507 QCOMPARE(interfaceW1->object(), (QObject*)w1);
508
509 target = ifaceW->child(index: 2);
510 QVERIFY(target != 0);
511 QVERIFY(target->isValid());
512 QCOMPARE(target->object(), (QObject*)w3);
513
514 QAccessibleInterface *child = target->child(index: 1);
515 QVERIFY(!child);
516 child = target->child(index: 0);
517 QVERIFY(child != 0);
518 QVERIFY(child->isValid());
519 QCOMPARE(child->object(), (QObject*)w31);
520
521 ifaceW = QAccessible::queryAccessibleInterface(w);
522 QAccessibleInterface *acc3(ifaceW->child(index: 2));
523 target = acc3->child(index: 0);
524 QCOMPARE(target->object(), (QObject*)w31);
525
526 QAccessibleInterface *parent = target->parent();
527 QVERIFY(parent != 0);
528 QVERIFY(parent->isValid());
529 QCOMPARE(parent->object(), (QObject*)w3);
530
531 delete w;
532 }
533 QTestAccessibility::clearEvents();
534}
535
536#define QSETCOMPARE(thetypename, elements, otherelements) \
537 QCOMPARE((QSet<thetypename>() << elements), (QSet<thetypename>() << otherelements))
538
539static QWidget *createWidgets()
540{
541 QWidget *w = new QWidget();
542
543 QHBoxLayout *box = new QHBoxLayout(w);
544
545 int i = 0;
546 box->addWidget(new QComboBox(w));
547 box->addWidget(new QPushButton(QLatin1String("widget text ") + QString::number(i++), w));
548 box->addWidget(new QHeaderView(Qt::Vertical, w));
549 box->addWidget(new QTreeView(w));
550 box->addWidget(new QTreeWidget(w));
551 box->addWidget(new QListView(w));
552 box->addWidget(new QListWidget(w));
553 box->addWidget(new QTableView(w));
554 box->addWidget(new QTableWidget(w));
555 box->addWidget(new QCalendarWidget(w));
556 box->addWidget(new QDialogButtonBox(w));
557 box->addWidget(new QGroupBox(QLatin1String("widget text ") + QString::number(i++), w));
558 box->addWidget(new QFrame(w));
559 box->addWidget(new QLineEdit(QLatin1String("widget text ") + QString::number(i++), w));
560 box->addWidget(new QProgressBar(w));
561 box->addWidget(new QTabWidget(w));
562 box->addWidget(new QCheckBox(QLatin1String("widget text ") + QString::number(i++), w));
563 box->addWidget(new QRadioButton(QLatin1String("widget text ") + QString::number(i++), w));
564 box->addWidget(new QDial(w));
565 box->addWidget(new QScrollBar(w));
566 box->addWidget(new QSlider(w));
567 box->addWidget(new QDateTimeEdit(w));
568 box->addWidget(new QDoubleSpinBox(w));
569 box->addWidget(new QSpinBox(w));
570 box->addWidget(new QLabel(QLatin1String("widget text ") + QString::number(i++), w));
571 box->addWidget(new QLCDNumber(w));
572 box->addWidget(new QStackedWidget(w));
573 box->addWidget(new QToolBox(w));
574 box->addWidget(new QLabel(QLatin1String("widget text ") + QString::number(i++), w));
575 box->addWidget(new QTextEdit(QLatin1String("widget text ") + QString::number(i++), w));
576
577 /* Not in the list
578 * QAbstractItemView, QGraphicsView, QScrollArea,
579 * QToolButton, QDockWidget, QFocusFrame, QMainWindow, QMenu, QMenuBar, QSizeGrip, QSplashScreen, QSplitterHandle,
580 * QStatusBar, QSvgWidget, QTabBar, QToolBar, QSplitter
581 */
582 return w;
583}
584
585void tst_QAccessibility::accessibleName()
586{
587 QWidget *toplevel = createWidgets();
588 toplevel->show();
589 QVERIFY(QTest::qWaitForWindowExposed(toplevel));
590
591 QLayout *lout = toplevel->layout();
592 for (int i = 0; i < lout->count(); i++) {
593 QLayoutItem *item = lout->itemAt(index: i);
594 QWidget *child = item->widget();
595
596 QString name = tr(s: "Widget Name %1").arg(a: i);
597 child->setAccessibleName(name);
598 QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(child);
599 QVERIFY(acc);
600 QCOMPARE(acc->text(QAccessible::Name), name);
601
602 QString desc = tr(s: "Widget Description %1").arg(a: i);
603 child->setAccessibleDescription(desc);
604 QCOMPARE(acc->text(QAccessible::Description), desc);
605 }
606
607 delete toplevel;
608 QTestAccessibility::clearEvents();
609}
610
611// note: color should probably always be part of the attributes
612void tst_QAccessibility::textAttributes_data()
613{
614 QTest::addColumn<QFont>(name: "defaultFont");
615 QTest::addColumn<QString>(name: "text");
616 QTest::addColumn<int>(name: "offset");
617 QTest::addColumn<int>(name: "startOffsetResult");
618 QTest::addColumn<int>(name: "endOffsetResult");
619 QTest::addColumn<QStringList>(name: "attributeResult");
620
621 static QFont defaultFont;
622 defaultFont.setFamily("");
623 defaultFont.setPointSize(13);
624
625 static QFont defaultComplexFont = defaultFont;
626 defaultComplexFont.setFamily("Arial");
627 defaultComplexFont.setPointSize(20);
628 defaultComplexFont.setWeight(QFont::Bold);
629 defaultComplexFont.setStyle(QFont::StyleItalic);
630 defaultComplexFont.setUnderline(true);
631
632 static QStringList defaults = QString("font-style:normal;font-weight:normal;text-align:left;text-position:baseline;font-size:13pt").split(sep: ';');
633 static QStringList bold = defaults;
634 bold[1] = QString::fromLatin1(str: "font-weight:bold");
635
636 static QStringList italic = defaults;
637 italic[0] = QString::fromLatin1(str: "font-style:italic");
638
639 static QStringList boldItalic = defaults;
640 boldItalic[0] = QString::fromLatin1(str: "font-style:italic");
641 boldItalic[1] = QString::fromLatin1(str: "font-weight:bold");
642
643 static QStringList monospace = defaults;
644 monospace.append(t: QLatin1String("font-family:\"monospace\""));
645
646 static QStringList font8pt = defaults;
647 font8pt[4] = (QLatin1String("font-size:8pt"));
648
649 static QStringList color = defaults;
650 color << QLatin1String("color:rgb(240,241,242)") << QLatin1String("background-color:rgb(20,240,30)");
651
652 static QStringList rightAlign = defaults;
653 rightAlign[2] = QStringLiteral("text-align:right");
654
655 static QStringList defaultFontDifferent = defaults;
656 defaultFontDifferent[0] = QString::fromLatin1(str: "font-style:italic");
657 defaultFontDifferent[1] = QString::fromLatin1(str: "font-weight:bold");
658 defaultFontDifferent[4] = QString::fromLatin1(str: "font-size:20pt");
659 defaultFontDifferent.append(t: "text-underline-style:solid");
660 defaultFontDifferent.append(t: "text-underline-type:single");
661 defaultFontDifferent.append(t: "font-family:\"Arial\"");
662
663 static QStringList defaultFontDifferentBoldItalic = defaultFontDifferent;
664 defaultFontDifferentBoldItalic[0] = QString::fromLatin1(str: "font-style:italic");
665 defaultFontDifferentBoldItalic[1] = QString::fromLatin1(str: "font-weight:bold");
666
667 static QStringList defaultFontDifferentMonospace = defaultFontDifferent;
668 defaultFontDifferentMonospace[7] = (QLatin1String("font-family:\"monospace\""));
669
670 static QStringList defaultFontDifferentFont8pt = defaultFontDifferent;
671 defaultFontDifferentFont8pt[4] = (QLatin1String("font-size:8pt"));
672
673 static QStringList defaultFontDifferentColor = defaultFontDifferent;
674 defaultFontDifferentColor << QLatin1String("color:rgb(240,241,242)") << QLatin1String("background-color:rgb(20,240,30)");
675
676 QTest::newRow(dataTag: "defaults 1") << defaultFont << "hello" << 0 << 0 << 5 << defaults;
677 QTest::newRow(dataTag: "defaults 2") << defaultFont << "hello" << 1 << 0 << 5 << defaults;
678 QTest::newRow(dataTag: "defaults 3") << defaultFont << "hello" << 4 << 0 << 5 << defaults;
679 QTest::newRow(dataTag: "defaults 4") << defaultFont << "hello" << 5 << 0 << 5 << defaults;
680 QTest::newRow(dataTag: "offset -1 length") << defaultFont << "hello" << -1 << 0 << 5 << defaults;
681 QTest::newRow(dataTag: "offset -2 cursor pos") << defaultFont << "hello" << -2 << 0 << 5 << defaults;
682 QTest::newRow(dataTag: "offset -3") << defaultFont << "hello" << -3 << -1 << -1 << QStringList();
683 QTest::newRow(dataTag: "invalid offset 2") << defaultFont << "hello" << 6 << -1 << -1 << QStringList();
684 QTest::newRow(dataTag: "invalid offset 3") << defaultFont << "" << 1 << -1 << -1 << QStringList();
685
686 QString boldText = QLatin1String("<html><b>bold</b>text");
687 QTest::newRow(dataTag: "bold 0") << defaultFont << boldText << 0 << 0 << 4 << bold;
688 QTest::newRow(dataTag: "bold 2") << defaultFont << boldText << 2 << 0 << 4 << bold;
689 QTest::newRow(dataTag: "bold 3") << defaultFont << boldText << 3 << 0 << 4 << bold;
690 QTest::newRow(dataTag: "bold 4") << defaultFont << boldText << 4 << 4 << 8 << defaults;
691 QTest::newRow(dataTag: "bold 6") << defaultFont << boldText << 6 << 4 << 8 << defaults;
692
693 QString longText = QLatin1String("<html>"
694 "Hello, <b>this</b> is an <i><b>example</b> text</i>."
695 "<span style=\"font-family: monospace\">Multiple fonts are used.</span>"
696 "Multiple <span style=\"font-size: 8pt\">text sizes</span> are used."
697 "Let's give some color to <span style=\"color:#f0f1f2; background-color:#14f01e\">Qt</span>.");
698
699 QTest::newRow(dataTag: "default 5") << defaultFont << longText << 6 << 0 << 7 << defaults;
700 QTest::newRow(dataTag: "default 6") << defaultFont << longText << 7 << 7 << 11 << bold;
701 QTest::newRow(dataTag: "bold 7") << defaultFont << longText << 10 << 7 << 11 << bold;
702 QTest::newRow(dataTag: "bold 8") << defaultFont << longText << 10 << 7 << 11 << bold;
703 QTest::newRow(dataTag: "bold italic") << defaultFont << longText << 18 << 18 << 25 << boldItalic;
704 QTest::newRow(dataTag: "monospace") << defaultFont << longText << 34 << 31 << 55 << monospace;
705 QTest::newRow(dataTag: "8pt") << defaultFont << longText << 65 << 64 << 74 << font8pt;
706 QTest::newRow(dataTag: "color") << defaultFont << longText << 110 << 109 << 111 << color;
707
708 // make sure unset font properties default to those of document's default font
709 QTest::newRow(dataTag: "defaultFont default 5") << defaultComplexFont << longText << 6 << 0 << 7 << defaultFontDifferent;
710 QTest::newRow(dataTag: "defaultFont default 6") << defaultComplexFont << longText << 7 << 7 << 11 << defaultFontDifferent;
711 QTest::newRow(dataTag: "defaultFont bold 7") << defaultComplexFont << longText << 10 << 7 << 11 << defaultFontDifferent;
712 QTest::newRow(dataTag: "defaultFont bold 8") << defaultComplexFont << longText << 10 << 7 << 11 << defaultFontDifferent;
713 QTest::newRow(dataTag: "defaultFont bold italic") << defaultComplexFont << longText << 18 << 18 << 25 << defaultFontDifferentBoldItalic;
714 QTest::newRow(dataTag: "defaultFont monospace") << defaultComplexFont << longText << 34 << 31 << 55 << defaultFontDifferentMonospace;
715 QTest::newRow(dataTag: "defaultFont 8pt") << defaultComplexFont << longText << 65 << 64 << 74 << defaultFontDifferentFont8pt;
716 QTest::newRow(dataTag: "defaultFont color") << defaultComplexFont << longText << 110 << 109 << 111 << defaultFontDifferentColor;
717
718 QString rightAligned = QLatin1String("<html><p align=\"right\">right</p>");
719 QTest::newRow(dataTag: "right aligned 1") << defaultFont << rightAligned << 0 << 0 << 5 << rightAlign;
720 QTest::newRow(dataTag: "right aligned 2") << defaultFont << rightAligned << 1 << 0 << 5 << rightAlign;
721 QTest::newRow(dataTag: "right aligned 3") << defaultFont << rightAligned << 5 << 0 << 5 << rightAlign;
722
723 // left \n right \n left, make sure bold and alignment borders coincide
724 QString leftRightLeftAligned = QLatin1String("<html><p><b>left</b></p><p align=\"right\">right</p><p><b>left</b></p>");
725 QTest::newRow(dataTag: "left right left aligned 1") << defaultFont << leftRightLeftAligned << 1 << 0 << 4 << bold;
726 QTest::newRow(dataTag: "left right left aligned 3") << defaultFont << leftRightLeftAligned << 3 << 0 << 4 << bold;
727 QTest::newRow(dataTag: "left right left aligned 4") << defaultFont << leftRightLeftAligned << 4 << 4 << 5 << defaults;
728 QTest::newRow(dataTag: "left right left aligned 5") << defaultFont << leftRightLeftAligned << 5 << 5 << 10 << rightAlign;
729 QTest::newRow(dataTag: "left right left aligned 8") << defaultFont << leftRightLeftAligned << 8 << 5 << 10 << rightAlign;
730 QTest::newRow(dataTag: "left right left aligned 9") << defaultFont << leftRightLeftAligned << 9 << 5 << 10 << rightAlign;
731 QTest::newRow(dataTag: "left right left aligned 10") << defaultFont << leftRightLeftAligned << 10 << 10 << 11 << rightAlign;
732 QTest::newRow(dataTag: "left right left aligned 11") << defaultFont << leftRightLeftAligned << 11 << 11 << 15 << bold;
733 QTest::newRow(dataTag: "left right left aligned 15") << defaultFont << leftRightLeftAligned << 15 << 11 << 15 << bold;
734 QTest::newRow(dataTag: "empty with no fragments") << defaultFont << QString::fromLatin1(str: "\n\n\n\n") << 0 << 0 << 1 << defaults;
735}
736
737void tst_QAccessibility::textAttributes()
738{
739 {
740 QFETCH(QFont, defaultFont);
741 QFETCH(QString, text);
742 QFETCH(int, offset);
743 QFETCH(int, startOffsetResult);
744 QFETCH(int, endOffsetResult);
745 QFETCH(QStringList, attributeResult);
746
747 QTextEdit textEdit;
748 textEdit.document()->setDefaultFont(defaultFont);
749 textEdit.setText(text);
750 if (textEdit.document()->characterCount() > 1)
751 textEdit.textCursor().setPosition(pos: 1);
752 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&textEdit);
753 QAccessibleTextInterface *textInterface=interface->textInterface();
754 QVERIFY(textInterface);
755 QCOMPARE(textInterface->characterCount(), textEdit.toPlainText().length());
756
757 int startOffset = -1;
758 int endOffset = -1;
759 QString attributes = textInterface->attributes(offset, startOffset: &startOffset, endOffset: &endOffset);
760
761 QCOMPARE(startOffset, startOffsetResult);
762 QCOMPARE(endOffset, endOffsetResult);
763 QStringList attrList = attributes.split(sep: QChar(';'), behavior: Qt::SkipEmptyParts);
764 attributeResult.sort();
765 attrList.sort();
766 QCOMPARE(attrList, attributeResult);
767 }
768 QTestAccessibility::clearEvents();
769}
770
771void tst_QAccessibility::hideShowTest()
772{
773 QWidget * const window = new QWidget();
774 window->resize(w: 200, h: 200);
775 QWidget * const child = new QWidget(window);
776
777 QVERIFY(state(window).invisible);
778 QVERIFY(state(child).invisible);
779
780 QTestAccessibility::clearEvents();
781
782 // show() and veryfy that both window and child are not invisible and get ObjectShow events.
783 window->show();
784 QVERIFY(!state(window).invisible);
785 QVERIFY(!state(child).invisible);
786
787 QAccessibleEvent show(window, QAccessible::ObjectShow);
788 QVERIFY(QTestAccessibility::containsEvent(&show));
789 QAccessibleEvent showChild(child, QAccessible::ObjectShow);
790 QVERIFY(QTestAccessibility::containsEvent(&showChild));
791 QTestAccessibility::clearEvents();
792
793 // hide() and veryfy that both window and child are invisible and get ObjectHide events.
794 window->hide();
795 QVERIFY(state(window).invisible);
796 QVERIFY(state(child).invisible);
797 QAccessibleEvent hide(window, QAccessible::ObjectHide);
798 QVERIFY(QTestAccessibility::containsEvent(&hide));
799 QAccessibleEvent hideChild(child, QAccessible::ObjectHide);
800 QVERIFY(QTestAccessibility::containsEvent(&hideChild));
801 QTestAccessibility::clearEvents();
802
803 delete window;
804 QTestAccessibility::clearEvents();
805}
806
807
808void tst_QAccessibility::actionTest()
809{
810 {
811 QCOMPARE(QAccessibleActionInterface::pressAction(), QString(QStringLiteral("Press")));
812
813 QWidget *widget = new QWidget;
814 widget->setFocusPolicy(Qt::NoFocus);
815 widget->show();
816
817 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(widget);
818 QVERIFY(interface);
819 QVERIFY(interface->isValid());
820 QAccessibleActionInterface *actions = interface->actionInterface();
821 QVERIFY(actions);
822
823 // no actions by default, except when focusable
824 QCOMPARE(actions->actionNames(), QStringList());
825 widget->setFocusPolicy(Qt::StrongFocus);
826 QCOMPARE(actions->actionNames(), QStringList(QAccessibleActionInterface::setFocusAction()));
827
828 delete widget;
829 }
830 QTestAccessibility::clearEvents();
831
832 {
833 QPushButton *button = new QPushButton;
834 setFrameless(button);
835 button->show();
836 QVERIFY(QTest::qWaitForWindowExposed(button));
837 button->clearFocus();
838 QCOMPARE(button->hasFocus(), false);
839 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(button);
840 QAccessibleActionInterface *actions = interface->actionInterface();
841 QVERIFY(actions);
842
843 // Make sure the "primary action" press comes first!
844 QCOMPARE(actions->actionNames(), QStringList() << QAccessibleActionInterface::pressAction() << QAccessibleActionInterface::setFocusAction());
845
846 actions->doAction(actionName: QAccessibleActionInterface::setFocusAction());
847 QTest::qWait(ms: 500);
848 QCOMPARE(button->hasFocus(), true);
849
850 connect(sender: button, SIGNAL(clicked()), receiver: this, SLOT(onClicked()));
851 QCOMPARE(click_count, 0);
852 actions->doAction(actionName: QAccessibleActionInterface::pressAction());
853 QTest::qWait(ms: 500);
854 QCOMPARE(click_count, 1);
855
856 delete button;
857 }
858 QTestAccessibility::clearEvents();
859}
860
861void tst_QAccessibility::applicationTest()
862{
863 {
864 QLatin1String name = QLatin1String("My Name");
865 qApp->setApplicationName(name);
866 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(qApp);
867 QCOMPARE(interface->text(QAccessible::Name), name);
868 QCOMPARE(interface->text(QAccessible::Description), qApp->applicationFilePath());
869 QCOMPARE(interface->text(QAccessible::Value), QString());
870 QCOMPARE(interface->role(), QAccessible::Application);
871 QCOMPARE(interface->window(), static_cast<QWindow*>(0));
872 QCOMPARE(interface->parent(), static_cast<QAccessibleInterface*>(0));
873 QCOMPARE(interface->focusChild(), static_cast<QAccessibleInterface*>(0));
874 QCOMPARE(interface->indexOfChild(0), -1);
875 QCOMPARE(interface->child(0), static_cast<QAccessibleInterface*>(0));
876 QCOMPARE(interface->child(-1), static_cast<QAccessibleInterface*>(0));
877 QCOMPARE(interface->child(1), static_cast<QAccessibleInterface*>(0));
878 QCOMPARE(interface->childCount(), 0);
879
880 // Check that asking for the application interface twice returns the same object
881 QAccessibleInterface *app2 = QAccessible::queryAccessibleInterface(qApp);
882 QCOMPARE(interface, app2);
883
884 QWidget widget;
885 widget.show();
886 qApp->setActiveWindow(&widget);
887 QVERIFY(QTest::qWaitForWindowActive(&widget));
888
889 QAccessibleInterface *widgetIface = QAccessible::queryAccessibleInterface(&widget);
890 QCOMPARE(interface->childCount(), 1);
891 QAccessibleInterface *focus = interface->focusChild();
892 QCOMPARE(focus->object(), &widget);
893 QCOMPARE(interface->indexOfChild(0), -1);
894 QCOMPARE(interface->indexOfChild(widgetIface), 0);
895 QAccessibleInterface *child = interface->child(index: 0);
896 QCOMPARE(child->object(), &widget);
897 QCOMPARE(interface->child(-1), static_cast<QAccessibleInterface*>(0));
898 QCOMPARE(interface->child(1), static_cast<QAccessibleInterface*>(0));
899 }
900 QTestAccessibility::clearEvents();
901}
902
903void tst_QAccessibility::mainWindowTest()
904{
905 {
906 QMainWindow *mw = new QMainWindow;
907 mw->resize(w: 300, h: 200);
908 mw->show(); // triggers layout
909 qApp->setActiveWindow(mw);
910
911 QLatin1String name = QLatin1String("I am the main window");
912 mw->setWindowTitle(name);
913 QVERIFY(QTest::qWaitForWindowActive(mw));
914
915 // The order of events is not really that important.
916 QAccessibleEvent show(mw, QAccessible::ObjectShow);
917 QVERIFY(QTestAccessibility::containsEvent(&show));
918 QAccessible::State activeState;
919 activeState.active = true;
920 QAccessibleStateChangeEvent active(mw, activeState);
921 QVERIFY(QTestAccessibility::containsEvent(&active));
922
923 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(mw);
924 QCOMPARE(iface->text(QAccessible::Name), name);
925 QCOMPARE(iface->role(), QAccessible::Window);
926 QVERIFY(iface->state().active);
927
928
929 delete mw;
930 }
931 QTestAccessibility::clearEvents();
932
933 {
934 QWindow window;
935 window.setGeometry(posx: 80, posy: 80, w: 40, h: 40);
936 window.show();
937 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
938
939 // We currently don't have an accessible interface for QWindow
940 // the active state is either in the QMainWindow or QQuickView
941 QAccessibleInterface *windowIface(QAccessible::queryAccessibleInterface(&window));
942 QVERIFY(!windowIface);
943
944 QAccessible::State activeState;
945 activeState.active = true;
946
947 // We should still not crash if we somehow end up sending state change events
948 // Note that we do not QVERIFY_EVENT, as that relies on the updateHandler being
949 // called, which does not happen/make sense when there's no interface for the event.
950 QAccessibleStateChangeEvent active(&window, activeState);
951 QAccessibleStateChangeEvent deactivate(&window, activeState);
952 }
953}
954
955// Dialogs and other sub-windows must appear in the
956// accessibility hierarchy exactly once as top level objects
957void tst_QAccessibility::subWindowTest()
958{
959 {
960 QWidget mainWidget;
961 mainWidget.setGeometry(ax: 100, ay: 100, aw: 100, ah: 100);
962 mainWidget.show();
963 QLabel label(QStringLiteral("Window Contents"), &mainWidget);
964 mainWidget.setLayout(new QHBoxLayout());
965 mainWidget.layout()->addWidget(w: &label);
966
967 QDialog d(&mainWidget);
968 d.show();
969
970 QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp);
971 QVERIFY(app);
972 QCOMPARE(app->childCount(), 2);
973
974 QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(&mainWidget);
975 QVERIFY(windowIface);
976 QCOMPARE(windowIface->childCount(), 1);
977 QCOMPARE(app->child(0), windowIface);
978 QCOMPARE(windowIface->parent(), app);
979
980 QAccessibleInterface *dialogIface = QAccessible::queryAccessibleInterface(&d);
981 QVERIFY(dialogIface);
982 QCOMPARE(app->child(1), dialogIface);
983 QCOMPARE(dialogIface->parent(), app);
984 QCOMPARE(dialogIface->parent(), app);
985 }
986
987 {
988 QMainWindow mainWindow;
989 mainWindow.setGeometry(ax: 100, ay: 100, aw: 100, ah: 100);
990 mainWindow.show();
991 QLabel label(QStringLiteral("Window Contents"), &mainWindow);
992 mainWindow.setCentralWidget(&label);
993
994 QDialog d(&mainWindow);
995 d.show();
996
997 QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp);
998 QVERIFY(app);
999 QCOMPARE(app->childCount(), 2);
1000
1001 QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(&mainWindow);
1002 QVERIFY(windowIface);
1003 QCOMPARE(windowIface->childCount(), 1);
1004 QCOMPARE(app->child(0), windowIface);
1005
1006 QAccessibleInterface *dialogIface = QAccessible::queryAccessibleInterface(&d);
1007 QVERIFY(dialogIface);
1008 QCOMPARE(app->child(1), dialogIface);
1009 QCOMPARE(dialogIface->parent(), app);
1010 QCOMPARE(windowIface->parent(), app);
1011 }
1012 QTestAccessibility::clearEvents();
1013}
1014
1015class CounterButton : public QPushButton {
1016 Q_OBJECT
1017public:
1018 CounterButton(const QString& name, QWidget* parent)
1019 : QPushButton(name, parent), clickCount(0)
1020 {
1021 connect(asender: this, SIGNAL(clicked(bool)), SLOT(incClickCount()));
1022 }
1023 int clickCount;
1024public Q_SLOTS:
1025 void incClickCount() {
1026 ++clickCount;
1027 }
1028};
1029
1030void tst_QAccessibility::buttonTest()
1031{
1032 QWidget window;
1033 window.setLayout(new QVBoxLayout);
1034
1035 // Standard push button
1036 CounterButton pushButton("Ok", &window);
1037
1038 // toggle button
1039 QPushButton toggleButton("Toggle", &window);
1040 toggleButton.setCheckable(true);
1041
1042 // standard checkbox
1043 QCheckBox checkBox("Check me!", &window);
1044
1045 // tristate checkbox
1046 QCheckBox tristate("Tristate!", &window);
1047 tristate.setTristate(true);
1048
1049 // radiobutton
1050 QRadioButton radio("Radio me!", &window);
1051
1052 // standard toolbutton
1053 QToolButton toolbutton(&window);
1054 toolbutton.setText("Tool");
1055 toolbutton.setMinimumSize(minw: 20,minh: 20);
1056
1057 // standard toolbutton
1058 QToolButton toggletool(&window);
1059 toggletool.setCheckable(true);
1060 toggletool.setText("Toggle");
1061 toggletool.setMinimumSize(minw: 20,minh: 20);
1062
1063 // test push button
1064 QAccessibleInterface* interface = QAccessible::queryAccessibleInterface(&pushButton);
1065 QAccessibleActionInterface* actionInterface = interface->actionInterface();
1066 QVERIFY(actionInterface != 0);
1067 QCOMPARE(interface->role(), QAccessible::PushButton);
1068
1069 // buttons only have a click action
1070 QCOMPARE(actionInterface->actionNames().size(), 2);
1071 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::pressAction() << QAccessibleActionInterface::setFocusAction());
1072 QCOMPARE(pushButton.clickCount, 0);
1073 actionInterface->doAction(actionName: QAccessibleActionInterface::pressAction());
1074 QTest::qWait(ms: 500);
1075 QCOMPARE(pushButton.clickCount, 1);
1076
1077 // test toggle button
1078 interface = QAccessible::queryAccessibleInterface(&toggleButton);
1079 actionInterface = interface->actionInterface();
1080 QCOMPARE(interface->role(), QAccessible::CheckBox);
1081 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1082 QCOMPARE(actionInterface->localizedActionDescription(QAccessibleActionInterface::toggleAction()), QString("Toggles the state"));
1083 QVERIFY(!toggleButton.isChecked());
1084 QVERIFY(!interface->state().checked);
1085 actionInterface->doAction(actionName: QAccessibleActionInterface::toggleAction());
1086 QTest::qWait(ms: 500);
1087 QVERIFY(toggleButton.isChecked());
1088 QCOMPARE(actionInterface->actionNames().at(0), QAccessibleActionInterface::toggleAction());
1089 QVERIFY(interface->state().checked);
1090
1091 {
1092 // test menu push button
1093 QAction *foo = new QAction("Foo", 0);
1094 foo->setShortcut(QKeySequence("Ctrl+F"));
1095 QMenu *menu = new QMenu();
1096 menu->addAction(action: foo);
1097 QPushButton menuButton;
1098 setFrameless(&menuButton);
1099 menuButton.setMenu(menu);
1100 menuButton.show();
1101 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&menuButton);
1102 QCOMPARE(interface->role(), QAccessible::ButtonMenu);
1103 QVERIFY(interface->state().hasPopup);
1104 QCOMPARE(interface->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction() << QAccessibleActionInterface::setFocusAction());
1105 // showing the menu enters a new event loop...
1106// interface->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1107// QTest::qWait(500);
1108 delete menu;
1109 }
1110
1111
1112 QTestAccessibility::clearEvents();
1113 {
1114 // test check box
1115 interface = QAccessible::queryAccessibleInterface(&checkBox);
1116 actionInterface = interface->actionInterface();
1117 QCOMPARE(interface->role(), QAccessible::CheckBox);
1118 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1119 QVERIFY(!interface->state().checked);
1120 actionInterface->doAction(actionName: QAccessibleActionInterface::toggleAction());
1121
1122 QTest::qWait(ms: 500);
1123 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1124 QVERIFY(interface->state().checked);
1125 QVERIFY(checkBox.isChecked());
1126 QAccessible::State st;
1127 st.checked = true;
1128 QAccessibleStateChangeEvent ev(&checkBox, st);
1129 QVERIFY_EVENT(&ev);
1130 checkBox.setChecked(false);
1131 QVERIFY_EVENT(&ev);
1132 }
1133
1134 {
1135 // test radiobutton
1136 interface = QAccessible::queryAccessibleInterface(&radio);
1137 actionInterface = interface->actionInterface();
1138 QCOMPARE(interface->role(), QAccessible::RadioButton);
1139 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1140 QVERIFY(!interface->state().checked);
1141 actionInterface->doAction(actionName: QAccessibleActionInterface::toggleAction());
1142 QTest::qWait(ms: 500);
1143 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1144 QVERIFY(interface->state().checked);
1145 QVERIFY(radio.isChecked());
1146 QAccessible::State st;
1147 st.checked = true;
1148 QAccessibleStateChangeEvent ev(&radio, st);
1149 QVERIFY_EVENT(&ev);
1150 }
1151
1152// // test standard toolbutton
1153// QVERIFY(QAccessible::queryAccessibleInterface(&toolbutton, &test));
1154// QCOMPARE(test->role(), QAccessible::PushButton);
1155// QCOMPARE(test->defaultAction(0), QAccessible::Press);
1156// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press"));
1157// QCOMPARE(test->state(), (int)QAccessible::Normal);
1158// test->release();
1159
1160// // toggle tool button
1161// QVERIFY(QAccessible::queryAccessibleInterface(&toggletool, &test));
1162// QCOMPARE(test->role(), QAccessible::CheckBox);
1163// QCOMPARE(test->defaultAction(0), QAccessible::Press);
1164// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Check"));
1165// QCOMPARE(test->state(), (int)QAccessible::Normal);
1166// QVERIFY(test->doAction(QAccessible::Press, 0));
1167// QTest::qWait(500);
1168// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Uncheck"));
1169// QCOMPARE(test->state(), (int)QAccessible::Checked);
1170// test->release();
1171
1172// // test menu toolbutton
1173// QVERIFY(QAccessible::queryAccessibleInterface(&menuToolButton, &test));
1174// QCOMPARE(test->role(), QAccessible::ButtonMenu);
1175// QCOMPARE(test->defaultAction(0), 1);
1176// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Open"));
1177// QCOMPARE(test->state(), (int)QAccessible::HasPopup);
1178// QCOMPARE(test->actionCount(0), 1);
1179// QCOMPARE(test->actionText(QAccessible::Press, QAccessible::Name, 0), QString("Press"));
1180// test->release();
1181
1182// // test split menu toolbutton
1183// QVERIFY(QAccessible::queryAccessibleInterface(&splitToolButton, &test));
1184// QCOMPARE(test->childCount(), 2);
1185// QCOMPARE(test->role(), QAccessible::ButtonDropDown);
1186// QCOMPARE(test->role(1), QAccessible::PushButton);
1187// QCOMPARE(test->role(2), QAccessible::ButtonMenu);
1188// QCOMPARE(test->defaultAction(0), QAccessible::Press);
1189// QCOMPARE(test->defaultAction(1), QAccessible::Press);
1190// QCOMPARE(test->defaultAction(2), QAccessible::Press);
1191// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press"));
1192// QCOMPARE(test->state(), (int)QAccessible::HasPopup);
1193// QCOMPARE(test->actionCount(0), 1);
1194// QCOMPARE(test->actionText(1, QAccessible::Name, 0), QString("Open"));
1195// QCOMPARE(test->actionText(test->defaultAction(1), QAccessible::Name, 1), QString("Press"));
1196// QCOMPARE(test->state(1), (int)QAccessible::Normal);
1197// QCOMPARE(test->actionText(test->defaultAction(2), QAccessible::Name, 2), QString("Open"));
1198// QCOMPARE(test->state(2), (int)QAccessible::HasPopup);
1199// test->release();
1200}
1201
1202void tst_QAccessibility::scrollBarTest()
1203{
1204 QScrollBar *scrollBar = new QScrollBar(Qt::Horizontal);
1205 QAccessibleInterface * const scrollBarInterface = QAccessible::queryAccessibleInterface(scrollBar);
1206 QVERIFY(scrollBarInterface);
1207 QVERIFY(scrollBarInterface->state().invisible);
1208 scrollBar->resize(w: 200, h: 50);
1209 scrollBar->show();
1210 QVERIFY(!scrollBarInterface->state().invisible);
1211 QAccessibleEvent show(scrollBar, QAccessible::ObjectShow);
1212 QVERIFY(QTestAccessibility::containsEvent(&show));
1213 QTestAccessibility::clearEvents();
1214
1215 scrollBar->hide();
1216 QVERIFY(scrollBarInterface->state().invisible);
1217 QAccessibleEvent hide(scrollBar, QAccessible::ObjectHide);
1218 QVERIFY(QTestAccessibility::containsEvent(&hide));
1219 QTestAccessibility::clearEvents();
1220
1221 // Test that the left/right subcontrols are set to unavailable when the scrollBar is at the minimum/maximum.
1222 scrollBar->show();
1223 scrollBar->setMinimum(11);
1224 scrollBar->setMaximum(111);
1225
1226 QAccessibleValueInterface *valueIface = scrollBarInterface->valueInterface();
1227 QVERIFY(valueIface != 0);
1228 QCOMPARE(valueIface->minimumValue().toInt(), scrollBar->minimum());
1229 QCOMPARE(valueIface->maximumValue().toInt(), scrollBar->maximum());
1230 scrollBar->setValue(50);
1231 QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value());
1232 scrollBar->setValue(0);
1233 QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value());
1234 scrollBar->setValue(100);
1235 QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value());
1236 valueIface->setCurrentValue(77);
1237 QCOMPARE(77, scrollBar->value());
1238
1239 const QRect scrollBarRect = scrollBarInterface->rect();
1240 QVERIFY(scrollBarRect.isValid());
1241
1242 delete scrollBar;
1243
1244 QTestAccessibility::clearEvents();
1245}
1246
1247void tst_QAccessibility::tabTest()
1248{
1249 QTabBar *tabBar = new QTabBar();
1250 setFrameless(tabBar);
1251 tabBar->show();
1252
1253 QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabBar);
1254 QVERIFY(interface);
1255 QCOMPARE(interface->childCount(), 2);
1256
1257 // Test that the Invisible bit for the navigation buttons gets set
1258 // and cleared correctly.
1259 QAccessibleInterface *leftButton = interface->child(index: 0);
1260 QCOMPARE(leftButton->role(), QAccessible::PushButton);
1261 QVERIFY(leftButton->state().invisible);
1262
1263 const int lots = 5;
1264 for (int i = 0; i < lots; ++i) {
1265 tabBar->addTab(text: "Foo");
1266 tabBar->setTabToolTip(index: i, tip: QLatin1String("Cool tool tip"));
1267 tabBar->setTabWhatsThis(index: i, text: QLatin1String("I don't know"));
1268 }
1269
1270 QAccessibleInterface *child1 = interface->child(index: 0);
1271 QAccessibleInterface *child2 = interface->child(index: 1);
1272 QVERIFY(child1);
1273 QCOMPARE(child1->role(), QAccessible::PageTab);
1274 QVERIFY(child2);
1275 QCOMPARE(child2->role(), QAccessible::PageTab);
1276
1277 QCOMPARE(child1->text(QAccessible::Name), QLatin1String("Foo"));
1278 QCOMPARE(child1->text(QAccessible::Description), QLatin1String("Cool tool tip"));
1279 QCOMPARE(child1->text(QAccessible::Help), QLatin1String("I don't know"));
1280
1281 QVERIFY(!(child1->state().invisible));
1282 tabBar->hide();
1283
1284 QCoreApplication::processEvents();
1285 QTest::qWait(ms: 100);
1286
1287 QVERIFY(child1->state().invisible);
1288
1289 tabBar->show();
1290 tabBar->setCurrentIndex(0);
1291
1292 // Test that sending a focus action to a tab does not select it.
1293// child2->doAction(QAccessible::Focus, 2, QVariantList());
1294 QCOMPARE(tabBar->currentIndex(), 0);
1295
1296 // Test that sending a press action to a tab selects it.
1297 QVERIFY(child2->actionInterface());
1298 QCOMPARE(child2->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1299 QCOMPARE(tabBar->currentIndex(), 0);
1300 child2->actionInterface()->doAction(actionName: QAccessibleActionInterface::pressAction());
1301 QCOMPARE(tabBar->currentIndex(), 1);
1302
1303 // Test that setAccessibleTabName changes a tab's accessible name
1304 tabBar->setAccessibleTabName(index: 0, name: "AccFoo");
1305 tabBar->setAccessibleTabName(index: 1, name: "AccBar");
1306 QCOMPARE(child1->text(QAccessible::Name), QLatin1String("AccFoo"));
1307 QCOMPARE(child2->text(QAccessible::Name), QLatin1String("AccBar"));
1308 tabBar->setCurrentIndex(0);
1309 QCOMPARE(interface->text(QAccessible::Name), QLatin1String("AccFoo"));
1310 tabBar->setCurrentIndex(1);
1311 QCOMPARE(interface->text(QAccessible::Name), QLatin1String("AccBar"));
1312
1313 delete tabBar;
1314 QTestAccessibility::clearEvents();
1315}
1316
1317void tst_QAccessibility::tabWidgetTest()
1318{
1319 QTabWidget *tabWidget = new QTabWidget();
1320 tabWidget->show();
1321
1322 // the interface for the tab is just a container for tabbar and stacked widget
1323 QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabWidget);
1324 QVERIFY(interface);
1325 QCOMPARE(interface->childCount(), 2);
1326 QCOMPARE(interface->role(), QAccessible::Client);
1327
1328 // Create pages, check navigation
1329 QLabel *label1 = new QLabel("Page 1", tabWidget);
1330 tabWidget->addTab(widget: label1, "Tab 1");
1331 QLabel *label2 = new QLabel("Page 2", tabWidget);
1332 tabWidget->addTab(widget: label2, "Tab 2");
1333
1334 QCOMPARE(interface->childCount(), 2);
1335
1336 QAccessibleInterface* tabBarInterface = 0;
1337 // there is no special logic to sort the children, so the contents will be 1, the tab bar 2
1338 tabBarInterface = interface->child(index: 1);
1339 QCOMPARE(verifyHierarchy(tabBarInterface), 0);
1340 QVERIFY(tabBarInterface);
1341 QCOMPARE(tabBarInterface->childCount(), 4);
1342 QCOMPARE(tabBarInterface->role(), QAccessible::PageTabList);
1343
1344 QAccessibleInterface* tabButton1Interface = tabBarInterface->child(index: 0);
1345 QVERIFY(tabButton1Interface);
1346 QCOMPARE(tabButton1Interface->role(), QAccessible::PageTab);
1347 QCOMPARE(tabButton1Interface->text(QAccessible::Name), QLatin1String("Tab 1"));
1348
1349 QAccessibleInterface* tabButton2Interface = tabBarInterface->child(index: 1);
1350 QVERIFY(tabButton2Interface);
1351 QCOMPARE(tabButton2Interface->role(), QAccessible::PageTab);
1352 QCOMPARE(tabButton2Interface->text(QAccessible::Name), QLatin1String("Tab 2"));
1353
1354 // Test that setAccessibleTabName changes a tab's accessible name
1355 tabWidget->setCurrentIndex(0);
1356 tabWidget->tabBar()->setAccessibleTabName(index: 0, name: "Acc Tab");
1357 QCOMPARE(tabButton1Interface->role(), QAccessible::PageTab);
1358 QCOMPARE(tabButton1Interface->text(QAccessible::Name), QLatin1String("Acc Tab"));
1359 QCOMPARE(tabBarInterface->text(QAccessible::Name), QLatin1String("Acc Tab"));
1360
1361 QAccessibleInterface* tabButtonLeft = tabBarInterface->child(index: 2);
1362 QVERIFY(tabButtonLeft);
1363 QCOMPARE(tabButtonLeft->role(), QAccessible::PushButton);
1364 QCOMPARE(tabButtonLeft->text(QAccessible::Name), QLatin1String("Scroll Left"));
1365
1366 QAccessibleInterface* tabButtonRight = tabBarInterface->child(index: 3);
1367 QVERIFY(tabButtonRight);
1368 QCOMPARE(tabButtonRight->role(), QAccessible::PushButton);
1369 QCOMPARE(tabButtonRight->text(QAccessible::Name), QLatin1String("Scroll Right"));
1370
1371 QAccessibleInterface* stackWidgetInterface = interface->child(index: 0);
1372 QVERIFY(stackWidgetInterface);
1373 QCOMPARE(stackWidgetInterface->childCount(), 2);
1374 QCOMPARE(stackWidgetInterface->role(), QAccessible::LayeredPane);
1375
1376 QAccessibleInterface* stackChild1Interface = stackWidgetInterface->child(index: 0);
1377 QVERIFY(stackChild1Interface);
1378#ifndef Q_CC_INTEL
1379 QCOMPARE(stackChild1Interface->childCount(), 0);
1380#endif
1381 QCOMPARE(stackChild1Interface->role(), QAccessible::StaticText);
1382 QCOMPARE(stackChild1Interface->text(QAccessible::Name), QLatin1String("Page 1"));
1383 QCOMPARE(label1, stackChild1Interface->object());
1384
1385 // Navigation in stack widgets should be consistent
1386 QAccessibleInterface* parent = stackChild1Interface->parent();
1387 QVERIFY(parent);
1388#ifndef Q_CC_INTEL
1389 QCOMPARE(parent->childCount(), 2);
1390#endif
1391 QCOMPARE(parent->role(), QAccessible::LayeredPane);
1392
1393 QAccessibleInterface* stackChild2Interface = stackWidgetInterface->child(index: 1);
1394 QVERIFY(stackChild2Interface);
1395 QCOMPARE(stackChild2Interface->childCount(), 0);
1396 QCOMPARE(stackChild2Interface->role(), QAccessible::StaticText);
1397 QCOMPARE(label2, stackChild2Interface->object());
1398 QCOMPARE(label2->text(), stackChild2Interface->text(QAccessible::Name));
1399
1400 parent = stackChild2Interface->parent();
1401 QVERIFY(parent);
1402#ifndef Q_CC_INTEL
1403 QCOMPARE(parent->childCount(), 2);
1404#endif
1405 QCOMPARE(parent->role(), QAccessible::LayeredPane);
1406
1407 delete tabWidget;
1408 QTestAccessibility::clearEvents();
1409}
1410
1411void tst_QAccessibility::menuTest()
1412{
1413 {
1414 QMainWindow mw;
1415 mw.resize(w: 300, h: 200);
1416 mw.menuBar()->setNativeMenuBar(false);
1417 QMenu *file = mw.menuBar()->addMenu(title: "&File");
1418 QMenu *fileNew = file->addMenu(title: "&New...");
1419 fileNew->menuAction()->setShortcut(tr(s: "Ctrl+N"));
1420 fileNew->addAction(text: "Text file");
1421 fileNew->addAction(text: "Image file");
1422 file->addAction(text: "&Open")->setShortcut(tr(s: "Ctrl+O"));
1423 file->addAction(text: "&Save")->setShortcut(tr(s: "Ctrl+S"));
1424 file->addSeparator();
1425 file->addAction(text: "E&xit")->setShortcut(tr(s: "Alt+F4"));
1426
1427 QMenu *edit = mw.menuBar()->addMenu(title: "&Edit");
1428 edit->addAction(text: "&Undo")->setShortcut(tr(s: "Ctrl+Z"));
1429 edit->addAction(text: "&Redo")->setShortcut(tr(s: "Ctrl+Y"));
1430 edit->addSeparator();
1431 edit->addAction(text: "Cu&t")->setShortcut(tr(s: "Ctrl+X"));
1432 edit->addAction(text: "&Copy")->setShortcut(tr(s: "Ctrl+C"));
1433 edit->addAction(text: "&Paste")->setShortcut(tr(s: "Ctrl+V"));
1434 edit->addAction(text: "&Delete")->setShortcut(tr(s: "Del"));
1435 edit->addSeparator();
1436 edit->addAction(text: "Pr&operties");
1437
1438 mw.menuBar()->addSeparator();
1439
1440 QMenu *help = mw.menuBar()->addMenu(title: "&Help");
1441 help->addAction(text: "&Contents");
1442 help->addAction(text: "&About");
1443
1444 mw.menuBar()->addAction(text: "Action!");
1445
1446 QMenu *childOfMainWindow = new QMenu(QStringLiteral("&Tools"), &mw);
1447 childOfMainWindow->addAction(text: "&Options");
1448 mw.menuBar()->addMenu(menu: childOfMainWindow);
1449
1450 mw.show(); // triggers layout
1451 QTest::qWait(ms: 100);
1452
1453 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&mw);
1454 QCOMPARE(verifyHierarchy(interface), 0);
1455
1456 interface = QAccessible::queryAccessibleInterface(mw.menuBar());
1457
1458 QVERIFY(interface);
1459 QCOMPARE(interface->childCount(), 6);
1460 QCOMPARE(interface->role(), QAccessible::MenuBar);
1461
1462 QAccessibleInterface *iFile = interface->child(index: 0);
1463 QAccessibleInterface *iEdit = interface->child(index: 1);
1464 QAccessibleInterface *iSeparator = interface->child(index: 2);
1465 QAccessibleInterface *iHelp = interface->child(index: 3);
1466 QAccessibleInterface *iAction = interface->child(index: 4);
1467
1468 QCOMPARE(iFile->role(), QAccessible::MenuItem);
1469 QCOMPARE(iEdit->role(), QAccessible::MenuItem);
1470 QCOMPARE(iSeparator->role(), QAccessible::Separator);
1471 QCOMPARE(iHelp->role(), QAccessible::MenuItem);
1472 QCOMPARE(iAction->role(), QAccessible::MenuItem);
1473#ifndef Q_OS_MAC
1474 QCOMPARE(mw.mapFromGlobal(interface->rect().topLeft()), mw.menuBar()->geometry().topLeft());
1475 QCOMPARE(interface->rect().size(), mw.menuBar()->size());
1476
1477 QVERIFY(interface->rect().contains(iFile->rect()));
1478 QVERIFY(interface->rect().contains(iEdit->rect()));
1479 // QVERIFY(interface->rect().contains(childSeparator->rect())); //separator might be invisible
1480 QVERIFY(interface->rect().contains(iHelp->rect()));
1481 QVERIFY(interface->rect().contains(iAction->rect()));
1482#endif
1483
1484 QCOMPARE(iFile->text(QAccessible::Name), QString("File"));
1485 QCOMPARE(iEdit->text(QAccessible::Name), QString("Edit"));
1486 QCOMPARE(iSeparator->text(QAccessible::Name), QString());
1487 QCOMPARE(iHelp->text(QAccessible::Name), QString("Help"));
1488 QCOMPARE(iAction->text(QAccessible::Name), QString("Action!"));
1489
1490// TODO: Currently not working, task to fix is #100019.
1491#ifndef Q_OS_MAC
1492 QCOMPARE(iFile->text(QAccessible::Accelerator), tr("Alt+F"));
1493 QCOMPARE(iEdit->text(QAccessible::Accelerator), tr("Alt+E"));
1494 QCOMPARE(iSeparator->text(QAccessible::Accelerator), QString());
1495 QCOMPARE(iHelp->text(QAccessible::Accelerator), tr("Alt+H"));
1496 QCOMPARE(iAction->text(QAccessible::Accelerator), QString());
1497#endif
1498
1499 QVERIFY(iFile->actionInterface());
1500
1501 QCOMPARE(iFile->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction());
1502 QCOMPARE(iSeparator->actionInterface()->actionNames(), QStringList());
1503 QCOMPARE(iHelp->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction());
1504 QCOMPARE(iAction->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1505
1506 bool menuFade = qApp->isEffectEnabled(Qt::UI_FadeMenu);
1507 int menuFadeDelay = 300;
1508 iFile->actionInterface()->doAction(actionName: QAccessibleActionInterface::showMenuAction());
1509 if(menuFade)
1510 QTest::qWait(ms: menuFadeDelay);
1511 QTRY_VERIFY(file->isVisible() && !edit->isVisible() && !help->isVisible());
1512 iEdit->actionInterface()->doAction(actionName: QAccessibleActionInterface::showMenuAction());
1513 if(menuFade)
1514 QTest::qWait(ms: menuFadeDelay);
1515 QTRY_VERIFY(!file->isVisible() && edit->isVisible() && !help->isVisible());
1516 iHelp->actionInterface()->doAction(actionName: QAccessibleActionInterface::showMenuAction());
1517 if(menuFade)
1518 QTest::qWait(ms: menuFadeDelay);
1519 QTRY_VERIFY(!file->isVisible() && !edit->isVisible() && help->isVisible());
1520 iAction->actionInterface()->doAction(actionName: QAccessibleActionInterface::showMenuAction());
1521 if(menuFade)
1522 QTest::qWait(ms: menuFadeDelay);
1523 QTRY_VERIFY(!file->isVisible() && !edit->isVisible() && !help->isVisible());
1524
1525 QVERIFY(interface->actionInterface());
1526 QCOMPARE(interface->actionInterface()->actionNames(), QStringList());
1527 interface = QAccessible::queryAccessibleInterface(file);
1528 QCOMPARE(interface->childCount(), 5);
1529 QCOMPARE(interface->role(), QAccessible::PopupMenu);
1530
1531 QAccessibleInterface *iFileNew = interface->child(index: 0);
1532 QAccessibleInterface *iFileOpen = interface->child(index: 1);
1533 QAccessibleInterface *iFileSave = interface->child(index: 2);
1534 QAccessibleInterface *iFileSeparator = interface->child(index: 3);
1535 QAccessibleInterface *iFileExit = interface->child(index: 4);
1536
1537 QCOMPARE(iFileNew->role(), QAccessible::MenuItem);
1538 QCOMPARE(iFileOpen->role(), QAccessible::MenuItem);
1539 QCOMPARE(iFileSave->role(), QAccessible::MenuItem);
1540 QCOMPARE(iFileSeparator->role(), QAccessible::Separator);
1541 QCOMPARE(iFileExit->role(), QAccessible::MenuItem);
1542 QCOMPARE(iFileNew->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction());
1543 QCOMPARE(iFileOpen->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1544 QCOMPARE(iFileSave->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1545 QCOMPARE(iFileSeparator->actionInterface()->actionNames(), QStringList());
1546 QCOMPARE(iFileExit->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1547
1548 QAccessibleInterface *iface = 0;
1549 QAccessibleInterface *iface2 = 0;
1550
1551 // traverse siblings with navigate(Sibling, ...)
1552 iface = interface->child(index: 0);
1553 QVERIFY(iface);
1554 QCOMPARE(iface->role(), QAccessible::MenuItem);
1555
1556 QAccessible::Role fileRoles[5] = {
1557 QAccessible::MenuItem,
1558 QAccessible::MenuItem,
1559 QAccessible::MenuItem,
1560 QAccessible::Separator,
1561 QAccessible::MenuItem
1562 };
1563 for (int child = 0; child < 5; ++child) {
1564 iface2 = interface->child(index: child);
1565 QVERIFY(iface2);
1566 QCOMPARE(iface2->role(), fileRoles[child]);
1567 }
1568
1569 // "New" item
1570 iface = interface->child(index: 0);
1571 QVERIFY(iface);
1572 QCOMPARE(iface->role(), QAccessible::MenuItem);
1573
1574 // "New" menu
1575 iface2 = iface->child(index: 0);
1576 iface = iface2;
1577 QVERIFY(iface);
1578 QCOMPARE(iface->role(), QAccessible::PopupMenu);
1579
1580 // "Text file" menu item
1581 iface2 = iface->child(index: 0);
1582 iface = iface2;
1583 QVERIFY(iface);
1584 QCOMPARE(iface->role(), QAccessible::MenuItem);
1585
1586 // move mouse pointer away, since that might influence the
1587 // subsequent tests
1588 QTest::mouseMove(widget: &mw, pos: QPoint(-1, -1));
1589 QTest::qWait(ms: 100);
1590 if (menuFade)
1591 QTest::qWait(ms: menuFadeDelay);
1592
1593 iFile->actionInterface()->doAction(actionName: QAccessibleActionInterface::showMenuAction());
1594 iFileNew->actionInterface()->doAction(actionName: QAccessibleActionInterface::showMenuAction());
1595
1596 QTRY_VERIFY(file->isVisible());
1597 QTRY_VERIFY(fileNew->isVisible());
1598 QVERIFY(!edit->isVisible());
1599 QVERIFY(!help->isVisible());
1600
1601 QTestAccessibility::clearEvents();
1602 mw.hide();
1603
1604 // Do not crash if the menu don't have a parent
1605 QMenu *menu = new QMenu;
1606 menu->addAction(text: QLatin1String("one"));
1607 menu->addAction(text: QLatin1String("two"));
1608 menu->addAction(text: QLatin1String("three"));
1609 iface = QAccessible::queryAccessibleInterface(menu);
1610 iface2 = iface->parent();
1611 QVERIFY(iface2);
1612 QCOMPARE(iface2->role(), QAccessible::Application);
1613 // caused a *crash*
1614 iface2->state();
1615 delete menu;
1616
1617 }
1618 QTestAccessibility::clearEvents();
1619}
1620
1621void tst_QAccessibility::spinBoxTest()
1622{
1623 QSpinBox * const spinBox = new QSpinBox();
1624 setFrameless(spinBox);
1625 spinBox->setValue(3);
1626 spinBox->show();
1627
1628 QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(spinBox);
1629 QVERIFY(interface);
1630 QCOMPARE(interface->role(), QAccessible::SpinBox);
1631
1632 QVERIFY(QTest::qWaitForWindowExposed(spinBox));
1633
1634 const QRect widgetRect = spinBox->geometry();
1635 const QRect accessibleRect = interface->rect();
1636 QCOMPARE(accessibleRect, widgetRect);
1637 QCOMPARE(interface->text(QAccessible::Value), QLatin1String("3"));
1638
1639 // make sure that the line edit is not there
1640 const int numChildren = interface->childCount();
1641 QCOMPARE(numChildren, 0);
1642 QVERIFY(!interface->child(0));
1643
1644 QVERIFY(interface->valueInterface());
1645 QCOMPARE(interface->valueInterface()->currentValue().toInt(), 3);
1646 interface->valueInterface()->setCurrentValue(23);
1647 QCOMPARE(interface->valueInterface()->currentValue().toInt(), 23);
1648 QCOMPARE(spinBox->value(), 23);
1649
1650 spinBox->setFocus();
1651 QTestAccessibility::clearEvents();
1652 QTest::keyPress(widget: spinBox, key: Qt::Key_Up);
1653 QTest::qWait(ms: 200);
1654 QAccessibleValueChangeEvent expectedEvent(spinBox, spinBox->value());
1655 QVERIFY(QTestAccessibility::containsEvent(&expectedEvent));
1656
1657 QAccessibleTextInterface *textIface = interface->textInterface();
1658 QVERIFY(textIface);
1659
1660 delete spinBox;
1661 QTestAccessibility::clearEvents();
1662}
1663
1664void tst_QAccessibility::doubleSpinBoxTest()
1665{
1666 QDoubleSpinBox *doubleSpinBox = new QDoubleSpinBox;
1667 setFrameless(doubleSpinBox);
1668 doubleSpinBox->show();
1669
1670 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(doubleSpinBox);
1671 QVERIFY(interface);
1672
1673 QVERIFY(QTest::qWaitForWindowExposed(doubleSpinBox));
1674
1675 const QRect widgetRect = doubleSpinBox->geometry();
1676 const QRect accessibleRect = interface->rect();
1677 QCOMPARE(accessibleRect, widgetRect);
1678
1679 // Test that we get valid rects for all the spinbox child interfaces.
1680 const int numChildren = interface->childCount();
1681 for (int i = 0; i < numChildren; ++i) {
1682 QAccessibleInterface *childIface = interface->child(index: i);
1683 const QRect childRect = childIface->rect();
1684 QVERIFY(childRect.isValid());
1685 }
1686
1687 delete doubleSpinBox;
1688 QTestAccessibility::clearEvents();
1689}
1690
1691static QRect characterRect(const QTextEdit &edit, int offset)
1692{
1693 QTextBlock block = edit.document()->findBlock(pos: offset);
1694 QTextLayout *layout = block.layout();
1695 QPointF layoutPosition = layout->position();
1696 int relativeOffset = offset - block.position();
1697 QTextLine line = layout->lineForTextPosition(pos: relativeOffset);
1698 QTextBlock::iterator it = block.begin();
1699 while (!it.fragment().contains(position: offset))
1700 ++it;
1701 QFontMetrics fm(it.fragment().charFormat().font());
1702 QChar ch = edit.document()->characterAt(pos: offset);
1703 int w = fm.horizontalAdvance(ch);
1704 int h = fm.height();
1705
1706 qreal x = line.cursorToX(cursorPos: relativeOffset);
1707 QRect r(layoutPosition.x() + x, layoutPosition.y() + line.y() + line.ascent() + fm.descent() - h, w, h);
1708 r.moveTo(p: edit.viewport()->mapToGlobal(r.topLeft()));
1709
1710 return r;
1711}
1712
1713/* The rects does not have to be exactly the same. They may be slightly different due to
1714 different ways of calculating them. By having an acceptable delta, this should also
1715 make the test a bit more resilient against any future changes in the behavior of
1716 characterRect().
1717*/
1718static bool fuzzyRectCompare(const QRect &a, const QRect &b)
1719{
1720 static const int MAX_ACCEPTABLE_DELTA = 1;
1721 const QMargins delta(a.left() - b.left(), a.top() - b.top(),
1722 a.right() - b.right(), a.bottom() - b.bottom());
1723
1724 return qAbs(t: delta.left()) <= MAX_ACCEPTABLE_DELTA && qAbs(t: delta.top()) <= MAX_ACCEPTABLE_DELTA
1725 && qAbs(t: delta.right()) <= MAX_ACCEPTABLE_DELTA && qAbs(t: delta.bottom()) <= MAX_ACCEPTABLE_DELTA;
1726}
1727
1728static QByteArray msgRectMismatch(const QRect &a, const QRect &b)
1729{
1730 QString result;
1731 QDebug(&result) << a << "!=" << b;
1732 return result.toLocal8Bit();
1733}
1734
1735void tst_QAccessibility::textEditTest()
1736{
1737 for (int pass = 0; pass < 2; ++pass) {
1738 {
1739 QTextEdit edit;
1740 edit.setMinimumSize(minw: 600, minh: 400);
1741 setFrameless(&edit);
1742 int startOffset;
1743 int endOffset;
1744 // create two blocks of text. The first block has two lines.
1745 QString text = "<p>hello world.<br/>How are you today?</p><p>I'm fine, thanks</p>";
1746 edit.setHtml(text);
1747 if (pass == 1) {
1748 QFont font("Helvetica");
1749 font.setPointSizeF(12.5);
1750 font.setWordSpacing(1.1);
1751 edit.document()->setDefaultFont(font);
1752 }
1753
1754 edit.show();
1755 QVERIFY(QTest::qWaitForWindowExposed(&edit));
1756 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&edit);
1757 QCOMPARE(iface->text(QAccessible::Value), edit.toPlainText());
1758 QVERIFY(iface->state().focusable);
1759 QVERIFY(!iface->state().selectable);
1760 QVERIFY(!iface->state().selected);
1761 QVERIFY(iface->state().selectableText);
1762
1763 QAccessibleTextInterface *textIface = iface->textInterface();
1764 QVERIFY(textIface);
1765
1766 QCOMPARE(textIface->textAtOffset(8, QAccessible::WordBoundary, &startOffset, &endOffset), QString("world"));
1767 QCOMPARE(startOffset, 6);
1768 QCOMPARE(endOffset, 11);
1769 QCOMPARE(textIface->textAtOffset(15, QAccessible::LineBoundary, &startOffset, &endOffset), QString("How are you today?"));
1770 QCOMPARE(startOffset, 13);
1771 QCOMPARE(endOffset, 31);
1772 QCOMPARE(textIface->characterCount(), 48);
1773 QFontMetrics fm(edit.document()->defaultFont());
1774 QCOMPARE(textIface->characterRect(0).size(), QSize(fm.horizontalAdvance("h"), fm.height()));
1775 QCOMPARE(textIface->characterRect(5).size(), QSize(fm.horizontalAdvance(" "), fm.height()));
1776 QCOMPARE(textIface->characterRect(6).size(), QSize(fm.horizontalAdvance("w"), fm.height()));
1777
1778 int offset = 10;
1779 QCOMPARE(textIface->text(offset, offset + 1), QStringLiteral("d"));
1780 const QRect actual10 = textIface->characterRect(offset);
1781 const QRect expected10 = characterRect(edit, offset);
1782 QVERIFY2(fuzzyRectCompare(actual10, expected10), msgRectMismatch(actual10, expected10).constData());
1783 offset = 13;
1784 QCOMPARE(textIface->text(offset, offset + 1), QStringLiteral("H"));
1785 const QRect actual13 = textIface->characterRect(offset);
1786 const QRect expected13 = characterRect(edit, offset);
1787 QVERIFY2(fuzzyRectCompare(actual13, expected13), msgRectMismatch(actual13, expected13).constData());
1788 offset = 21;
1789 QCOMPARE(textIface->text(offset, offset + 1), QStringLiteral("y"));
1790 const QRect actual21 = textIface->characterRect(offset);
1791 const QRect expected21 = characterRect(edit, offset);
1792 QVERIFY2(fuzzyRectCompare(actual21, expected21), msgRectMismatch(actual21, expected21).constData());
1793 offset = 32;
1794 QCOMPARE(textIface->text(offset, offset + 1), QStringLiteral("I"));
1795 const QRect actual32 = textIface->characterRect(offset);
1796 const QRect expected32 = characterRect(edit, offset);
1797 QVERIFY2(fuzzyRectCompare(actual32, expected32), msgRectMismatch(actual32, expected32).constData());
1798
1799 QTestAccessibility::clearEvents();
1800
1801 // select text
1802 QTextCursor c = edit.textCursor();
1803 c.setPosition(pos: 2);
1804 c.setPosition(pos: 4, mode: QTextCursor::KeepAnchor);
1805 edit.setTextCursor(c);
1806 QAccessibleTextSelectionEvent sel(&edit, 2, 4);
1807 QVERIFY_EVENT(&sel);
1808 QAccessibleTextCursorEvent cursor(&edit, 4);
1809 QVERIFY_EVENT(&cursor);
1810
1811 edit.selectAll();
1812 int end = edit.textCursor().position();
1813 sel.setCursorPosition(end);
1814 sel.setSelection(start: 0, end);
1815 QVERIFY_EVENT(&sel);
1816
1817 // check that we have newlines handled
1818 QString poem = QStringLiteral("Once upon a midnight dreary,\nwhile I pondered, weak and weary,\nOver many a quaint and curious volume of forgotten lore\n");
1819 QAccessibleEditableTextInterface *editableTextIface = iface->editableTextInterface();
1820 QVERIFY(editableTextIface);
1821 editableTextIface->replaceText(startOffset: 0, endOffset: end, text: poem);
1822 QCOMPARE(iface->text(QAccessible::Value), poem);
1823 QCOMPARE(textIface->text(0, poem.size()), poem);
1824 QCOMPARE(textIface->text(28, 29), QLatin1String("\n"));
1825 int start;
1826 QCOMPARE(textIface->textAtOffset(42, QAccessible::LineBoundary, &start, &end), QStringLiteral("while I pondered, weak and weary,"));
1827 QCOMPARE(start, 29);
1828 QCOMPARE(end, 62);
1829 QCOMPARE(textIface->textAtOffset(28, QAccessible::CharBoundary, &start, &end), QLatin1String("\n"));
1830 QCOMPARE(start, 28);
1831 QCOMPARE(end, 29);
1832
1833 edit.clear();
1834 QTestAccessibility::clearEvents();
1835
1836 // make sure we get notifications when typing text
1837 QTestEventList keys;
1838 keys.addKeyClick(ascii: 'A');
1839 keys.simulate(w: &edit);
1840 keys.clear();
1841 QAccessibleTextInsertEvent insertA(&edit, 0, "A");
1842 QVERIFY_EVENT(&insertA);
1843 QAccessibleTextCursorEvent move1(&edit, 1);
1844 QVERIFY_EVENT(&move1);
1845
1846
1847 keys.addKeyClick(ascii: 'c');
1848 keys.simulate(w: &edit);
1849 keys.clear();
1850 QAccessibleTextInsertEvent insertC(&edit, 1, "c");
1851 QVERIFY_EVENT(&insertC);
1852 QAccessibleTextCursorEvent move2(&edit, 2);
1853 QVERIFY_EVENT(&move2);
1854
1855 keys.addKeyClick(qtKey: Qt::Key_Backspace);
1856 keys.simulate(w: &edit);
1857 keys.clear();
1858
1859 // FIXME this should get a proper string instead of space
1860 QAccessibleTextRemoveEvent del(&edit, 1, " ");
1861 QVERIFY_EVENT(&del);
1862 QVERIFY_EVENT(&move1);
1863
1864 // it would be nicer to get a text update event, but the current implementation
1865 // instead does remove and insert which is also fine
1866 edit.setText(QStringLiteral("Accessibility rocks"));
1867 QAccessibleTextRemoveEvent remove(&edit, 0, " ");
1868 QVERIFY_EVENT(&remove);
1869
1870 QAccessibleTextInsertEvent insert(&edit, 0, "Accessibility rocks");
1871 QVERIFY_EVENT(&insert);
1872 }
1873 QTestAccessibility::clearEvents();
1874 }
1875}
1876
1877void tst_QAccessibility::textBrowserTest()
1878{
1879 {
1880 QTextBrowser textBrowser;
1881 QString text = QLatin1String("Hello world\nhow are you today?\n");
1882 textBrowser.setText(text);
1883 textBrowser.show();
1884
1885 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&textBrowser);
1886 QVERIFY(iface);
1887 QCOMPARE(iface->role(), QAccessible::StaticText);
1888 QCOMPARE(iface->text(QAccessible::Value), text);
1889 int startOffset;
1890 int endOffset;
1891 QCOMPARE(iface->textInterface()->textAtOffset(8, QAccessible::WordBoundary, &startOffset, &endOffset), QString("world"));
1892 QCOMPARE(startOffset, 6);
1893 QCOMPARE(endOffset, 11);
1894 QCOMPARE(iface->textInterface()->textAtOffset(14, QAccessible::LineBoundary, &startOffset, &endOffset), QString("how are you today?"));
1895 QCOMPARE(startOffset, 12);
1896 QCOMPARE(endOffset, 30);
1897 QCOMPARE(iface->textInterface()->characterCount(), 31);
1898 }
1899 QTestAccessibility::clearEvents();
1900}
1901
1902void tst_QAccessibility::mdiAreaTest()
1903{
1904 {
1905 QMdiArea mdiArea;
1906 mdiArea.resize(w: 400,h: 300);
1907 mdiArea.show();
1908 const int subWindowCount = 3;
1909 for (int i = 0; i < subWindowCount; ++i)
1910 mdiArea.addSubWindow(widget: new QWidget, flags: Qt::Dialog)->show();
1911
1912 QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList();
1913 QCOMPARE(subWindows.count(), subWindowCount);
1914
1915 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&mdiArea);
1916 QVERIFY(interface);
1917 QCOMPARE(interface->childCount(), subWindowCount);
1918
1919 }
1920 QTestAccessibility::clearEvents();
1921}
1922
1923void tst_QAccessibility::mdiSubWindowTest()
1924{
1925 {
1926 QMdiArea mdiArea;
1927 mdiArea.show();
1928 qApp->setActiveWindow(&mdiArea);
1929 QVERIFY(QTest::qWaitForWindowActive(&mdiArea));
1930
1931
1932 const int subWindowCount = 5;
1933 for (int i = 0; i < subWindowCount; ++i) {
1934 QMdiSubWindow *window = mdiArea.addSubWindow(widget: new QPushButton("QAccessibilityTest"));
1935 window->setAttribute(Qt::WA_LayoutUsesWidgetRect);
1936 window->show();
1937 // Parts of this test requires that the sub windows are placed next
1938 // to each other. In order to achieve that QMdiArea must have
1939 // a width which is larger than subWindow->width() * subWindowCount.
1940 if (i == 0) {
1941 int minimumWidth = window->width() * subWindowCount + 20;
1942 mdiArea.resize(mdiArea.size().expandedTo(otherSize: QSize(minimumWidth, 0)));
1943#if defined(Q_OS_UNIX)
1944 QCoreApplication::processEvents();
1945 QTest::qWait(ms: 100);
1946#endif
1947 }
1948 }
1949
1950 QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList();
1951 QCOMPARE(subWindows.count(), subWindowCount);
1952
1953 QMdiSubWindow *testWindow = subWindows.at(i: 3);
1954 QVERIFY(testWindow);
1955 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(testWindow);
1956
1957 // childCount
1958 QVERIFY(interface);
1959 QCOMPARE(interface->childCount(), 1);
1960
1961 // setText / text
1962 QCOMPARE(interface->text(QAccessible::Name), QString());
1963 testWindow->setWindowTitle(QLatin1String("ReplaceMe"));
1964 QCOMPARE(interface->text(QAccessible::Name), QLatin1String("ReplaceMe"));
1965 interface->setText(t: QAccessible::Name, text: QLatin1String("TitleSetOnWindow"));
1966 QCOMPARE(interface->text(QAccessible::Name), QLatin1String("TitleSetOnWindow"));
1967
1968 mdiArea.setActiveSubWindow(testWindow);
1969
1970 // state
1971 QAccessible::State state;
1972 state.focusable = true;
1973 state.focused = true;
1974 state.movable = true;
1975 state.sizeable = true;
1976
1977 QCOMPARE(interface->state(), state);
1978 const QRect originalGeometry = testWindow->geometry();
1979 testWindow->showMaximized();
1980 state.sizeable = false;
1981 state.movable = false;
1982 QCOMPARE(interface->state(), state);
1983 testWindow->showNormal();
1984 testWindow->move(ax: -10, ay: 0);
1985 QVERIFY(interface->state().offscreen);
1986 testWindow->setVisible(false);
1987 QVERIFY(interface->state().invisible);
1988 testWindow->setVisible(true);
1989 testWindow->setEnabled(false);
1990 QVERIFY(interface->state().disabled);
1991 testWindow->setEnabled(true);
1992 qApp->setActiveWindow(&mdiArea);
1993 mdiArea.setActiveSubWindow(testWindow);
1994 testWindow->setFocus();
1995 QVERIFY(testWindow->isAncestorOf(qApp->focusWidget()));
1996 QVERIFY(interface->state().focused);
1997 testWindow->setGeometry(originalGeometry);
1998
1999 // rect
2000 const QPoint globalPos = testWindow->mapToGlobal(QPoint(0, 0));
2001 QCOMPARE(interface->rect(), QRect(globalPos, testWindow->size()));
2002 testWindow->hide();
2003 QCOMPARE(interface->rect(), QRect());
2004 QCOMPARE(childRect(interface), QRect());
2005 testWindow->showMinimized();
2006 QCOMPARE(childRect(interface), QRect());
2007 testWindow->showNormal();
2008 testWindow->widget()->hide();
2009 QCOMPARE(childRect(interface), QRect());
2010 testWindow->widget()->show();
2011 const QRect widgetGeometry = testWindow->contentsRect();
2012 const QPoint globalWidgetPos = QPoint(globalPos.x() + widgetGeometry.x(),
2013 globalPos.y() + widgetGeometry.y());
2014#ifdef Q_OS_MAC
2015 QSKIP("QTBUG-22812");
2016#endif
2017 QCOMPARE(childRect(interface), QRect(globalWidgetPos, widgetGeometry.size()));
2018
2019 // childAt
2020 QCOMPARE(interface->childAt(-10, 0), static_cast<QAccessibleInterface*>(0));
2021 QCOMPARE(interface->childAt(globalPos.x(), globalPos.y()), static_cast<QAccessibleInterface*>(0));
2022 QAccessibleInterface *child = interface->childAt(x: globalWidgetPos.x(), y: globalWidgetPos.y());
2023 QCOMPARE(child->role(), QAccessible::PushButton);
2024 QCOMPARE(child->text(QAccessible::Name), QString("QAccessibilityTest"));
2025 testWindow->widget()->hide();
2026 QCOMPARE(interface->childAt(globalWidgetPos.x(), globalWidgetPos.y()), static_cast<QAccessibleInterface*>(0));
2027
2028 }
2029 QTestAccessibility::clearEvents();
2030}
2031
2032void tst_QAccessibility::lineEditTest()
2033{
2034 QWidget *toplevel = new QWidget;
2035 {
2036 QLineEdit *le = new QLineEdit;
2037 QAccessibleInterface *iface(QAccessible::queryAccessibleInterface(le));
2038 QVERIFY(iface);
2039 le->show();
2040
2041 QApplication::processEvents();
2042 QCOMPARE(iface->childCount(), 0);
2043 QVERIFY(iface->state().sizeable);
2044 QVERIFY(iface->state().movable);
2045 QVERIFY(iface->state().focusable);
2046 QVERIFY(!iface->state().selectable);
2047 QVERIFY(iface->state().selectableText);
2048 QVERIFY(!iface->state().hasPopup);
2049 QVERIFY(!iface->state().readOnly);
2050 QVERIFY(iface->state().editable);
2051
2052 le->setReadOnly(true);
2053 QVERIFY(iface->state().editable);
2054 QVERIFY(iface->state().readOnly);
2055 le->setReadOnly(false);
2056 QVERIFY(!iface->state().readOnly);
2057
2058 QCOMPARE(bool(iface->state().focused), le->hasFocus());
2059
2060 QString secret(QLatin1String("secret"));
2061 le->setText(secret);
2062 le->setEchoMode(QLineEdit::Normal);
2063 QVERIFY(!(iface->state().passwordEdit));
2064 QCOMPARE(iface->text(QAccessible::Value), secret);
2065 le->setEchoMode(QLineEdit::NoEcho);
2066 QVERIFY(iface->state().passwordEdit);
2067 QCOMPARE(iface->text(QAccessible::Value), QString());
2068 le->setEchoMode(QLineEdit::Password);
2069 QVERIFY(iface->state().passwordEdit);
2070 QCOMPARE(iface->text(QAccessible::Value), QString(secret.length(), QLatin1Char('*')));
2071 le->setEchoMode(QLineEdit::PasswordEchoOnEdit);
2072 QVERIFY(iface->state().passwordEdit);
2073 QCOMPARE(iface->text(QAccessible::Value), QString(secret.length(), QLatin1Char('*')));
2074 le->setEchoMode(QLineEdit::Normal);
2075 QVERIFY(!(iface->state().passwordEdit));
2076 QCOMPARE(iface->text(QAccessible::Value), secret);
2077
2078 le->setParent(toplevel);
2079 toplevel->show();
2080 QApplication::processEvents();
2081 QVERIFY(!(iface->state().sizeable));
2082 QVERIFY(!(iface->state().movable));
2083 QVERIFY(iface->state().focusable);
2084 QVERIFY(!iface->state().selectable);
2085 QVERIFY(!iface->state().selected);
2086 QVERIFY(iface->state().selectableText);
2087 QVERIFY(!iface->state().hasPopup);
2088 QCOMPARE(bool(iface->state().focused), le->hasFocus());
2089
2090 QLineEdit *le2 = new QLineEdit(toplevel);
2091 le2->show();
2092 QTest::qWait(ms: 100);
2093 le2->activateWindow();
2094 QTest::qWait(ms: 100);
2095 le->setFocus(Qt::TabFocusReason);
2096 QTestAccessibility::clearEvents();
2097 le2->setFocus(Qt::TabFocusReason);
2098 QAccessibleEvent ev(le2, QAccessible::Focus);
2099 QTRY_VERIFY(QTestAccessibility::containsEvent(&ev));
2100
2101 le->setText(QLatin1String("500"));
2102 le->setValidator(new QIntValidator());
2103 iface->setText(t: QAccessible::Value, text: QLatin1String("This text is not a number"));
2104 QCOMPARE(le->text(), QLatin1String("500"));
2105
2106 delete le;
2107 delete le2;
2108 }
2109
2110 {
2111 // Text interface to get the current text
2112 QString cite = "I always pass on good advice. It is the only thing to do with it. It is never of any use to oneself. --Oscar Wilde";
2113 QLineEdit *le3 = new QLineEdit(cite, toplevel);
2114 le3->show();
2115 QAccessibleInterface *iface(QAccessible::queryAccessibleInterface(le3));
2116 QAccessibleTextInterface* textIface = iface->textInterface();
2117 le3->deselect();
2118 QTestAccessibility::clearEvents();
2119 le3->setCursorPosition(3);
2120 QCOMPARE(textIface->cursorPosition(), 3);
2121
2122 QAccessibleTextCursorEvent caretEvent(le3, 3);
2123 QTRY_VERIFY(QTestAccessibility::containsEvent(&caretEvent));
2124 QCOMPARE(textIface->selectionCount(), 0);
2125 QTestAccessibility::clearEvents();
2126
2127 int start, end;
2128 QCOMPARE(textIface->text(0, 8), QString::fromLatin1("I always"));
2129 QCOMPARE(textIface->textAtOffset(0, QAccessible::CharBoundary,&start,&end), QString::fromLatin1("I"));
2130 QCOMPARE(start, 0);
2131 QCOMPARE(end, 1);
2132 QCOMPARE(textIface->textBeforeOffset(0, QAccessible::CharBoundary,&start,&end), QString());
2133 QCOMPARE(textIface->textAfterOffset(0, QAccessible::CharBoundary,&start,&end), QString::fromLatin1(" "));
2134 QCOMPARE(start, 1);
2135 QCOMPARE(end, 2);
2136
2137 QCOMPARE(textIface->textAtOffset(5, QAccessible::CharBoundary,&start,&end), QString::fromLatin1("a"));
2138 QCOMPARE(start, 5);
2139 QCOMPARE(end, 6);
2140 QCOMPARE(textIface->textBeforeOffset(5, QAccessible::CharBoundary,&start,&end), QString::fromLatin1("w"));
2141 QCOMPARE(textIface->textAfterOffset(5, QAccessible::CharBoundary,&start,&end), QString::fromLatin1("y"));
2142
2143 QCOMPARE(textIface->textAtOffset(5, QAccessible::WordBoundary,&start,&end), QString::fromLatin1("always"));
2144 QCOMPARE(start, 2);
2145 QCOMPARE(end, 8);
2146
2147 QCOMPARE(textIface->textAtOffset(2, QAccessible::WordBoundary,&start,&end), QString::fromLatin1("always"));
2148 QCOMPARE(textIface->textAtOffset(7, QAccessible::WordBoundary,&start,&end), QString::fromLatin1("always"));
2149 QCOMPARE(textIface->textAtOffset(8, QAccessible::WordBoundary,&start,&end), QString::fromLatin1(" "));
2150 QCOMPARE(textIface->textAtOffset(25, QAccessible::WordBoundary,&start,&end), QString::fromLatin1("advice"));
2151 QCOMPARE(textIface->textAtOffset(92, QAccessible::WordBoundary,&start,&end), QString::fromLatin1("oneself"));
2152 QCOMPARE(textIface->textAtOffset(101, QAccessible::WordBoundary,&start,&end), QString::fromLatin1(". --"));
2153
2154 QCOMPARE(textIface->textBeforeOffset(5, QAccessible::WordBoundary,&start,&end), QString::fromLatin1(" "));
2155 QCOMPARE(textIface->textAfterOffset(5, QAccessible::WordBoundary,&start,&end), QString::fromLatin1(" "));
2156 QCOMPARE(textIface->textAtOffset(5, QAccessible::SentenceBoundary,&start,&end), QString::fromLatin1("I always pass on good advice. "));
2157 QCOMPARE(start, 0);
2158 QCOMPARE(end, 30);
2159
2160 QCOMPARE(textIface->textBeforeOffset(40, QAccessible::SentenceBoundary,&start,&end), QString::fromLatin1("I always pass on good advice. "));
2161 QCOMPARE(textIface->textAfterOffset(5, QAccessible::SentenceBoundary,&start,&end), QString::fromLatin1("It is the only thing to do with it. "));
2162
2163 QCOMPARE(textIface->textAtOffset(5, QAccessible::ParagraphBoundary,&start,&end), cite);
2164 QCOMPARE(start, 0);
2165 QCOMPARE(end, cite.length());
2166 QCOMPARE(textIface->textAtOffset(5, QAccessible::LineBoundary,&start,&end), cite);
2167 QCOMPARE(textIface->textAtOffset(5, QAccessible::NoBoundary,&start,&end), cite);
2168
2169 QTestAccessibility::clearEvents();
2170 }
2171
2172 {
2173 QLineEdit le(QStringLiteral("My characters have geometries."), toplevel);
2174 // characterRect()
2175 le.show();
2176 QVERIFY(QTest::qWaitForWindowExposed(&le));
2177 QAccessibleInterface *iface(QAccessible::queryAccessibleInterface(&le));
2178 QAccessibleTextInterface* textIface = iface->textInterface();
2179 QVERIFY(textIface);
2180 const QRect lineEditRect = iface->rect();
2181 // Only first 10 characters, check if they are within the bounds of line edit
2182 for (int i = 0; i < 10; ++i) {
2183 QVERIFY(lineEditRect.contains(textIface->characterRect(i)));
2184 }
2185 QTestAccessibility::clearEvents();
2186 }
2187
2188 {
2189 // Test events: cursor movement, selection, text changes
2190 QString text = "Hello, world";
2191 QLineEdit *lineEdit = new QLineEdit(text, toplevel);
2192 lineEdit->show();
2193 QTestAccessibility::clearEvents();
2194 // cursor
2195 lineEdit->setCursorPosition(5);
2196 QAccessibleTextCursorEvent cursorEvent(lineEdit, 5);
2197 QVERIFY_EVENT(&cursorEvent);
2198 lineEdit->setCursorPosition(0);
2199 cursorEvent.setCursorPosition(0);
2200 QVERIFY_EVENT(&cursorEvent);
2201
2202 // selection
2203 lineEdit->setSelection(2, 4);
2204
2205 QAccessibleTextSelectionEvent sel(lineEdit, 2, 2+4);
2206 QVERIFY_EVENT(&sel);
2207
2208 lineEdit->selectAll();
2209 sel.setSelection(start: 0, end: lineEdit->text().length());
2210 sel.setCursorPosition(lineEdit->text().length());
2211 QVERIFY_EVENT(&sel);
2212
2213 lineEdit->setSelection(10, -4);
2214 QCOMPARE(lineEdit->cursorPosition(), 6);
2215 QAccessibleTextSelectionEvent sel2(lineEdit, 6, 10);
2216 sel2.setCursorPosition(6);
2217 QVERIFY_EVENT(&sel2);
2218
2219 lineEdit->deselect();
2220 QAccessibleTextSelectionEvent sel3(lineEdit, -1, -1);
2221 sel3.setCursorPosition(6);
2222 QVERIFY_EVENT(&sel3);
2223
2224 // editing
2225 lineEdit->clear();
2226 // FIXME: improve redundant updates
2227 QAccessibleTextRemoveEvent remove(lineEdit, 0, text);
2228 QVERIFY_EVENT(&remove);
2229
2230 QAccessibleTextSelectionEvent noSel(lineEdit, -1, -1);
2231 QVERIFY_EVENT(&noSel);
2232 QAccessibleTextCursorEvent cursor(lineEdit, 0);
2233 QVERIFY_EVENT(&cursor);
2234
2235 lineEdit->setText("foo");
2236 cursorEvent.setCursorPosition(3);
2237 QVERIFY_EVENT(&cursorEvent);
2238
2239 QAccessibleTextInsertEvent e(lineEdit, 0, "foo");
2240 QVERIFY(QTestAccessibility::containsEvent(&e));
2241
2242 lineEdit->setText("bar");
2243 QAccessibleTextUpdateEvent update(lineEdit, 0, "foo", "bar");
2244 QVERIFY(QTestAccessibility::containsEvent(&update));
2245
2246 // FIXME check what extra events are around and get rid of them
2247 QTestAccessibility::clearEvents();
2248
2249 QTestEventList keys;
2250 keys.addKeyClick(ascii: 'D');
2251 keys.simulate(w: lineEdit);
2252
2253 QAccessibleTextInsertEvent insertD(lineEdit, 3, "D");
2254 QVERIFY_EVENT(&insertD);
2255 keys.clear();
2256 keys.addKeyClick(ascii: 'E');
2257 keys.simulate(w: lineEdit);
2258
2259 QAccessibleTextInsertEvent insertE(lineEdit, 4, "E");
2260 QVERIFY(QTestAccessibility::containsEvent(&insertE));
2261 keys.clear();
2262 keys.addKeyClick(qtKey: Qt::Key_Left);
2263 keys.addKeyClick(qtKey: Qt::Key_Left);
2264 keys.simulate(w: lineEdit);
2265 cursorEvent.setCursorPosition(4);
2266 QVERIFY(QTestAccessibility::containsEvent(&cursorEvent));
2267 cursorEvent.setCursorPosition(3);
2268 QVERIFY(QTestAccessibility::containsEvent(&cursorEvent));
2269
2270 keys.clear();
2271 keys.addKeyClick(ascii: 'C');
2272 keys.simulate(w: lineEdit);
2273
2274 QAccessibleTextInsertEvent insertC(lineEdit, 3, "C");
2275 QVERIFY(QTestAccessibility::containsEvent(&insertC));
2276
2277 keys.clear();
2278 keys.addKeyClick(ascii: 'O');
2279 keys.simulate(w: lineEdit);
2280 QAccessibleTextInsertEvent insertO(lineEdit, 4, "O");
2281 QVERIFY(QTestAccessibility::containsEvent(&insertO));
2282 }
2283 delete toplevel;
2284 QTestAccessibility::clearEvents();
2285}
2286
2287void tst_QAccessibility::lineEditTextFunctions_data()
2288{
2289 QTest::addColumn<QString>(name: "text");
2290 QTest::addColumn<int>(name: "textFunction"); // before = 0, at = 1, after = 2
2291 QTest::addColumn<int>(name: "boundaryType");
2292 QTest::addColumn<int>(name: "cursorPosition");
2293 QTest::addColumn<int>(name: "offset");
2294 QTest::addColumn<int>(name: "expectedStart");
2295 QTest::addColumn<int>(name: "expectedEnd");
2296 QTest::addColumn<QString>(name: "expectedText");
2297
2298 // -2 gives cursor position, -1 is length
2299 // invalid positions will return empty strings and either -1 and -1 or both the cursor position, both is fine
2300 QTest::newRow(dataTag: "char before -2") << "hello" << 0 << (int) QAccessible::CharBoundary << 3 << -2 << 2 << 3 << "l";
2301 QTest::newRow(dataTag: "char at -2") << "hello" << 1 << (int) QAccessible::CharBoundary << 3 << -2 << 3 << 4 << "l";
2302 QTest::newRow(dataTag: "char after -2") << "hello" << 2 << (int) QAccessible::CharBoundary << 3 << -2 << 4 << 5 << "o";
2303 QTest::newRow(dataTag: "char before -1") << "hello" << 0 << (int) QAccessible::CharBoundary << 3 << -1 << 4 << 5 << "o";
2304 QTest::newRow(dataTag: "char at -1") << "hello" << 1 << (int) QAccessible::CharBoundary << 3 << -1 << -1 << -1 << "";
2305 QTest::newRow(dataTag: "char after -1") << "hello" << 2 << (int) QAccessible::CharBoundary << 3 << -1 << -1 << -1 << "";
2306 QTest::newRow(dataTag: "char before 0") << "hello" << 0 << (int) QAccessible::CharBoundary << 0 << 0 << -1 << -1 << "";
2307 QTest::newRow(dataTag: "char at 0") << "hello" << 1 << (int) QAccessible::CharBoundary << 0 << 0 << 0 << 1 << "h";
2308 QTest::newRow(dataTag: "char after 0") << "hello" << 2 << (int) QAccessible::CharBoundary << 0 << 0 << 1 << 2 << "e";
2309 QTest::newRow(dataTag: "char before 1") << "hello" << 0 << (int) QAccessible::CharBoundary << 3 << 1 << 0 << 1 << "h";
2310 QTest::newRow(dataTag: "char at 1") << "hello" << 1 << (int) QAccessible::CharBoundary << 3 << 1 << 1 << 2 << "e";
2311 QTest::newRow(dataTag: "char after 1") << "hello" << 2 << (int) QAccessible::CharBoundary << 3 << 1 << 2 << 3 << "l";
2312 QTest::newRow(dataTag: "char before 4") << "hello" << 0 << (int) QAccessible::CharBoundary << 3 << 4 << 3 << 4 << "l";
2313 QTest::newRow(dataTag: "char at 4") << "hello" << 1 << (int) QAccessible::CharBoundary << 3 << 4 << 4 << 5 << "o";
2314 QTest::newRow(dataTag: "char after 4") << "hello" << 2 << (int) QAccessible::CharBoundary << 3 << 4 << -1 << -1 << "";
2315 QTest::newRow(dataTag: "char before 5") << "hello" << 0 << (int) QAccessible::CharBoundary << 3 << 5 << 4 << 5 << "o";
2316 QTest::newRow(dataTag: "char at 5") << "hello" << 1 << (int) QAccessible::CharBoundary << 3 << 5 << -1 << -1 << "";
2317 QTest::newRow(dataTag: "char after 5") << "hello" << 2 << (int) QAccessible::CharBoundary << 3 << 5 << -1 << -1 << "";
2318 QTest::newRow(dataTag: "char before 6") << "hello" << 0 << (int) QAccessible::CharBoundary << 3 << 6 << -1 << -1 << "";
2319 QTest::newRow(dataTag: "char at 6") << "hello" << 1 << (int) QAccessible::CharBoundary << 3 << 6 << -1 << -1 << "";
2320 QTest::newRow(dataTag: "char after 6") << "hello" << 2 << (int) QAccessible::CharBoundary << 3 << 6 << -1 << -1 << "";
2321
2322 for (int i = -2; i < 6; ++i) {
2323 const QByteArray iB = QByteArray::number(i);
2324 QTest::newRow(dataTag: ("line before " + iB).constData())
2325 << "hello" << 0 << (int) QAccessible::LineBoundary << 3 << i << -1 << -1 << "";
2326 QTest::newRow(dataTag: ("line at " + iB).constData())
2327 << "hello" << 1 << (int) QAccessible::LineBoundary << 3 << i << 0 << 5 << "hello";
2328 QTest::newRow(dataTag: ("line after " + iB).constData())
2329 << "hello" << 2 << (int) QAccessible::LineBoundary << 3 << i << -1 << -1 << "";
2330 }
2331}
2332
2333void tst_QAccessibility::lineEditTextFunctions()
2334{
2335 {
2336 QFETCH(QString, text);
2337 QFETCH(int, textFunction);
2338 QFETCH(int, boundaryType);
2339 QFETCH(int, cursorPosition);
2340 QFETCH(int, offset);
2341 QFETCH(int, expectedStart);
2342 QFETCH(int, expectedEnd);
2343 QFETCH(QString, expectedText);
2344
2345 QLineEdit le;
2346 le.show();
2347 le.setText(text);
2348 le.setCursorPosition(cursorPosition);
2349
2350 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&le);
2351 QVERIFY(iface);
2352 QAccessibleTextInterface *textIface = iface->textInterface();
2353 QVERIFY(textIface);
2354
2355 int start = -33;
2356 int end = -33;
2357 QString result;
2358 switch (textFunction) {
2359 case 0:
2360 result = textIface->textBeforeOffset(offset, boundaryType: (QAccessible::TextBoundaryType) boundaryType, startOffset: &start, endOffset: &end);
2361 break;
2362 case 1:
2363 result = textIface->textAtOffset(offset, boundaryType: (QAccessible::TextBoundaryType) boundaryType, startOffset: &start, endOffset: &end);
2364 break;
2365 case 2:
2366 result = textIface->textAfterOffset(offset, boundaryType: (QAccessible::TextBoundaryType) boundaryType, startOffset: &start, endOffset: &end);
2367 break;
2368 }
2369 QCOMPARE(result, expectedText);
2370 QCOMPARE(start, expectedStart);
2371 QCOMPARE(end, expectedEnd);
2372 }
2373 QTestAccessibility::clearEvents();
2374}
2375
2376void tst_QAccessibility::textInterfaceTest_data()
2377{
2378 lineEditTextFunctions_data();
2379 QString hello = QStringLiteral("hello\nworld\nend");
2380 QTest::newRow(dataTag: "multi line at 0") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 0 << 0 << 6 << "hello\n";
2381 QTest::newRow(dataTag: "multi line at 1") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 1 << 0 << 6 << "hello\n";
2382 QTest::newRow(dataTag: "multi line at 2") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 2 << 0 << 6 << "hello\n";
2383 QTest::newRow(dataTag: "multi line at 5") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 5 << 0 << 6 << "hello\n";
2384 QTest::newRow(dataTag: "multi line at 6") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 6 << 6 << 12 << "world\n";
2385 QTest::newRow(dataTag: "multi line at 7") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 7 << 6 << 12 << "world\n";
2386 QTest::newRow(dataTag: "multi line at 8") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 8 << 6 << 12 << "world\n";
2387 QTest::newRow(dataTag: "multi line at 10") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 10 << 6 << 12 << "world\n";
2388 QTest::newRow(dataTag: "multi line at 11") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 11 << 6 << 12 << "world\n";
2389 QTest::newRow(dataTag: "multi line at 12") << hello << 1 << (int) QAccessible::LineBoundary << 0 << 12 << 12 << 15 << "end";
2390
2391 QTest::newRow(dataTag: "multi line before 0") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 0 << -1 << -1 << "";
2392 QTest::newRow(dataTag: "multi line before 1") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 1 << -1 << -1 << "";
2393 QTest::newRow(dataTag: "multi line before 2") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 2 << -1 << -1 << "";
2394 QTest::newRow(dataTag: "multi line before 5") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 5 << -1 << -1 << "";
2395 QTest::newRow(dataTag: "multi line before 6") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 6 << 0 << 6 << "hello\n";
2396 QTest::newRow(dataTag: "multi line before 7") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 7 << 0 << 6 << "hello\n";
2397 QTest::newRow(dataTag: "multi line before 8") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 8 << 0 << 6 << "hello\n";
2398 QTest::newRow(dataTag: "multi line before 10") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 10 << 0 << 6 << "hello\n";
2399 QTest::newRow(dataTag: "multi line before 11") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 11 << 0 << 6 << "hello\n";
2400 QTest::newRow(dataTag: "multi line before 12") << hello << 0 << (int) QAccessible::LineBoundary << 0 << 12 << 6 << 12 << "world\n";
2401
2402 QTest::newRow(dataTag: "multi line after 0") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 0 << 6 << 12 << "world\n";
2403 QTest::newRow(dataTag: "multi line after 1") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 1 << 6 << 12 << "world\n";
2404 QTest::newRow(dataTag: "multi line after 2") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 2 << 6 << 12 << "world\n";
2405 QTest::newRow(dataTag: "multi line after 5") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 5 << 6 << 12 << "world\n";
2406 QTest::newRow(dataTag: "multi line after 6") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 6 << 12 << 15 << "end";
2407 QTest::newRow(dataTag: "multi line after 7") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 7 << 12 << 15 << "end";
2408 QTest::newRow(dataTag: "multi line after 8") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 8 << 12 << 15 << "end";
2409 QTest::newRow(dataTag: "multi line after 10") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 10 << 12 << 15 << "end";
2410 QTest::newRow(dataTag: "multi line after 11") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 11 << 12 << 15 << "end";
2411 QTest::newRow(dataTag: "multi line after 12") << hello << 2 << (int) QAccessible::LineBoundary << 0 << 12 << -1 << -1 << "";
2412
2413 QTest::newRow(dataTag: "before 4 \\nFoo\\n") << QStringLiteral("\nFoo\n") << 0 << (int) QAccessible::LineBoundary << 0 << 4 << 0 << 1 << "\n";
2414 QTest::newRow(dataTag: "at 4 \\nFoo\\n") << QStringLiteral("\nFoo\n") << 1 << (int) QAccessible::LineBoundary << 0 << 4 << 1 << 5 << "Foo\n";
2415 QTest::newRow(dataTag: "after 4 \\nFoo\\n") << QStringLiteral("\nFoo\n") << 2 << (int) QAccessible::LineBoundary << 0 << 4 << 5 << 5 << "";
2416 QTest::newRow(dataTag: "before 4 Foo\\nBar\\n") << QStringLiteral("Foo\nBar\n") << 0 << (int) QAccessible::LineBoundary << 0 << 7 << 0 << 4 << "Foo\n";
2417 QTest::newRow(dataTag: "at 4 Foo\\nBar\\n") << QStringLiteral("Foo\nBar\n") << 1 << (int) QAccessible::LineBoundary << 0 << 7 << 4 << 8 << "Bar\n";
2418 QTest::newRow(dataTag: "after 4 Foo\\nBar\\n") << QStringLiteral("Foo\nBar\n") << 2 << (int) QAccessible::LineBoundary << 0 << 7 << 8 << 8 << "";
2419 QTest::newRow(dataTag: "at 0 Foo\\n") << QStringLiteral("Foo\n") << 1 << (int) QAccessible::LineBoundary << 0 << 0 << 0 << 4 << "Foo\n";
2420}
2421
2422void tst_QAccessibility::textInterfaceTest()
2423{
2424 QFETCH(QString, text);
2425 QFETCH(int, textFunction);
2426 QFETCH(int, boundaryType);
2427 QFETCH(int, cursorPosition);
2428 QFETCH(int, offset);
2429 QFETCH(int, expectedStart);
2430 QFETCH(int, expectedEnd);
2431 QFETCH(QString, expectedText);
2432
2433 QAccessible::installFactory(CustomTextWidgetIface::ifaceFactory);
2434 CustomTextWidget *w = new CustomTextWidget();
2435 w->text = text;
2436 w->cursorPosition = cursorPosition;
2437
2438 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(w);
2439 QVERIFY(iface);
2440 QCOMPARE(iface->text(QAccessible::Value), text);
2441 QAccessibleTextInterface *textIface = iface->textInterface();
2442 QVERIFY(textIface);
2443
2444 int start = -33;
2445 int end = -33;
2446 QString result;
2447 switch (textFunction) {
2448 case 0:
2449 result = textIface->textBeforeOffset(offset, boundaryType: (QAccessible::TextBoundaryType) boundaryType, startOffset: &start, endOffset: &end);
2450 break;
2451 case 1:
2452 result = textIface->textAtOffset(offset, boundaryType: (QAccessible::TextBoundaryType) boundaryType, startOffset: &start, endOffset: &end);
2453 break;
2454 case 2:
2455 result = textIface->textAfterOffset(offset, boundaryType: (QAccessible::TextBoundaryType) boundaryType, startOffset: &start, endOffset: &end);
2456 break;
2457 }
2458
2459 QCOMPARE(result, expectedText);
2460 QCOMPARE(start, expectedStart);
2461 QCOMPARE(end, expectedEnd);
2462
2463 delete w;
2464 QAccessible::removeFactory(CustomTextWidgetIface::ifaceFactory);
2465 QTestAccessibility::clearEvents();
2466}
2467
2468void tst_QAccessibility::groupBoxTest()
2469{
2470 {
2471 QGroupBox *groupBox = new QGroupBox();
2472 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(groupBox);
2473
2474 groupBox->setTitle(QLatin1String("Test QGroupBox"));
2475
2476 QAccessibleEvent ev(groupBox, QAccessible::NameChanged);
2477 QVERIFY_EVENT(&ev);
2478
2479 groupBox->setToolTip(QLatin1String("This group box will be used to test accessibility"));
2480 QVBoxLayout *layout = new QVBoxLayout();
2481 QRadioButton *rbutton = new QRadioButton();
2482 layout->addWidget(rbutton);
2483 groupBox->setLayout(layout);
2484 QAccessibleInterface *rButtonIface = QAccessible::queryAccessibleInterface(rbutton);
2485
2486 QCOMPARE(iface->childCount(), 1);
2487 QCOMPARE(iface->role(), QAccessible::Grouping);
2488 QCOMPARE(iface->text(QAccessible::Name), QLatin1String("Test QGroupBox"));
2489 QCOMPARE(iface->text(QAccessible::Description), QLatin1String("This group box will be used to test accessibility"));
2490 QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > relations = rButtonIface->relations();
2491 QCOMPARE(relations.size(), 1);
2492 QPair<QAccessibleInterface*, QAccessible::Relation> relation = relations.first();
2493 QCOMPARE(relation.first->object(), groupBox);
2494 QCOMPARE(relation.second, QAccessible::Label);
2495
2496 delete groupBox;
2497 }
2498
2499 {
2500 QGroupBox *groupBox = new QGroupBox();
2501 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(groupBox);
2502 QVERIFY(!iface->state().checkable);
2503 groupBox->setCheckable(true);
2504
2505 groupBox->setChecked(false);
2506 QAccessible::State st;
2507 st.checked = true;
2508 QAccessibleStateChangeEvent ev(groupBox, st);
2509 QVERIFY_EVENT(&ev);
2510
2511 QCOMPARE(iface->role(), QAccessible::CheckBox);
2512 QAccessibleActionInterface *actionIface = iface->actionInterface();
2513 QVERIFY(actionIface);
2514 QAccessible::State state = iface->state();
2515 QVERIFY(state.checkable);
2516 QVERIFY(!state.checked);
2517 QVERIFY(actionIface->actionNames().contains(QAccessibleActionInterface::toggleAction()));
2518 actionIface->doAction(actionName: QAccessibleActionInterface::toggleAction());
2519 QVERIFY(groupBox->isChecked());
2520 state = iface->state();
2521 QVERIFY(state.checked);
2522 QAccessibleStateChangeEvent ev2(groupBox, st);
2523 QVERIFY_EVENT(&ev2);
2524
2525 delete groupBox;
2526 }
2527}
2528
2529bool accessibleInterfaceLeftOf(const QAccessibleInterface *a1, const QAccessibleInterface *a2)
2530{
2531 return a1->rect().x() < a2->rect().x();
2532}
2533
2534bool accessibleInterfaceAbove(const QAccessibleInterface *a1, const QAccessibleInterface *a2)
2535{
2536 return a1->rect().y() < a2->rect().y();
2537}
2538
2539void tst_QAccessibility::dialogButtonBoxTest()
2540{
2541 {
2542 QDialogButtonBox box(QDialogButtonBox::Reset |
2543 QDialogButtonBox::Help |
2544 QDialogButtonBox::Ok, Qt::Horizontal);
2545
2546
2547 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&box);
2548 QVERIFY(iface);
2549 box.show();
2550#if defined(Q_OS_UNIX)
2551 QCoreApplication::processEvents();
2552 QTest::qWait(ms: 100);
2553#endif
2554
2555 QApplication::processEvents();
2556 QCOMPARE(iface->childCount(), 3);
2557 QCOMPARE(iface->role(), QAccessible::Grouping);
2558 QStringList actualOrder;
2559 QAccessibleInterface *child;
2560 child = iface->child(index: 0);
2561 QCOMPARE(child->role(), QAccessible::PushButton);
2562
2563 QVector<QAccessibleInterface *> buttons;
2564 for (int i = 0; i < iface->childCount(); ++i)
2565 buttons << iface->child(index: i);
2566
2567 std::sort(first: buttons.begin(), last: buttons.end(), comp: accessibleInterfaceLeftOf);
2568
2569 for (int i = 0; i < buttons.count(); ++i)
2570 actualOrder << buttons.at(i)->text(t: QAccessible::Name);
2571
2572 QStringList expectedOrder;
2573 QDialogButtonBox::ButtonLayout btnlout =
2574 QDialogButtonBox::ButtonLayout(QApplication::style()->styleHint(stylehint: QStyle::SH_DialogButtonLayout));
2575 switch (btnlout) {
2576 case QDialogButtonBox::WinLayout:
2577 expectedOrder << QDialogButtonBox::tr(s: "Reset")
2578 << QDialogButtonBox::tr(s: "OK")
2579 << QDialogButtonBox::tr(s: "Help");
2580 break;
2581 case QDialogButtonBox::GnomeLayout:
2582 case QDialogButtonBox::KdeLayout:
2583 case QDialogButtonBox::MacLayout:
2584 case QDialogButtonBox::AndroidLayout:
2585 expectedOrder << QDialogButtonBox::tr(s: "Help")
2586 << QDialogButtonBox::tr(s: "Reset")
2587 << QDialogButtonBox::tr(s: "OK");
2588 break;
2589 }
2590 QCOMPARE(actualOrder, expectedOrder);
2591 QApplication::processEvents();
2592 QTestAccessibility::clearEvents();
2593 }
2594
2595 {
2596 QDialogButtonBox box(QDialogButtonBox::Reset |
2597 QDialogButtonBox::Help |
2598 QDialogButtonBox::Ok, Qt::Horizontal);
2599 setFrameless(&box);
2600
2601
2602 // Test up and down navigation
2603 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&box);
2604 QVERIFY(iface);
2605 box.setOrientation(Qt::Vertical);
2606 box.show();
2607#if defined(Q_OS_UNIX)
2608 QCoreApplication::processEvents();
2609 QTest::qWait(ms: 100);
2610#endif
2611
2612 QApplication::processEvents();
2613 QStringList actualOrder;
2614
2615 QVector<QAccessibleInterface *> buttons;
2616 for (int i = 0; i < iface->childCount(); ++i)
2617 buttons << iface->child(index: i);
2618
2619 std::sort(first: buttons.begin(), last: buttons.end(), comp: accessibleInterfaceAbove);
2620
2621 for (int i = 0; i < buttons.count(); ++i)
2622 actualOrder << buttons.at(i)->text(t: QAccessible::Name);
2623
2624 QStringList expectedOrder;
2625 expectedOrder << QDialogButtonBox::tr(s: "OK")
2626 << QDialogButtonBox::tr(s: "Reset")
2627 << QDialogButtonBox::tr(s: "Help");
2628
2629 QCOMPARE(actualOrder, expectedOrder);
2630 QApplication::processEvents();
2631
2632 }
2633 QTestAccessibility::clearEvents();
2634}
2635
2636void tst_QAccessibility::dialTest()
2637{
2638 {
2639 QDial dial;
2640 setFrameless(&dial);
2641 dial.setMinimum(23);
2642 dial.setMaximum(121);
2643 dial.setValue(42);
2644 QCOMPARE(dial.value(), 42);
2645 dial.show();
2646
2647 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&dial);
2648 QVERIFY(interface);
2649 QCOMPARE(interface->childCount(), 0);
2650
2651 QVERIFY(QTest::qWaitForWindowExposed(&dial));
2652
2653 QCOMPARE(interface->text(QAccessible::Value), QString::number(dial.value()));
2654 QCOMPARE(interface->rect(), dial.geometry());
2655
2656 QAccessibleValueInterface *valueIface = interface->valueInterface();
2657 QVERIFY(valueIface != 0);
2658 QCOMPARE(valueIface->minimumValue().toInt(), dial.minimum());
2659 QCOMPARE(valueIface->maximumValue().toInt(), dial.maximum());
2660 QCOMPARE(valueIface->currentValue().toInt(), 42);
2661 dial.setValue(50);
2662 QCOMPARE(valueIface->currentValue().toInt(), dial.value());
2663 dial.setValue(0);
2664 QCOMPARE(valueIface->currentValue().toInt(), dial.value());
2665 dial.setValue(100);
2666 QCOMPARE(valueIface->currentValue().toInt(), dial.value());
2667 valueIface->setCurrentValue(77);
2668 QCOMPARE(77, dial.value());
2669 }
2670 QTestAccessibility::clearEvents();
2671}
2672
2673void tst_QAccessibility::rubberBandTest()
2674{
2675 QRubberBand rubberBand(QRubberBand::Rectangle);
2676 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&rubberBand);
2677 QVERIFY(interface);
2678 QCOMPARE(interface->role(), QAccessible::Border);
2679 QTestAccessibility::clearEvents();
2680}
2681
2682void tst_QAccessibility::abstractScrollAreaTest()
2683{
2684 {
2685 QAbstractScrollArea abstractScrollArea;
2686
2687 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&abstractScrollArea);
2688 QVERIFY(interface);
2689 QVERIFY(!interface->rect().isValid());
2690 QCOMPARE(interface->childAt(200, 200), static_cast<QAccessibleInterface*>(0));
2691
2692 abstractScrollArea.resize(w: 400, h: 400);
2693 abstractScrollArea.show();
2694#if defined(Q_OS_UNIX)
2695 QCoreApplication::processEvents();
2696 QTest::qWait(ms: 100);
2697#endif
2698 const QRect globalGeometry = QRect(abstractScrollArea.mapToGlobal(QPoint(0, 0)),
2699 abstractScrollArea.size());
2700
2701 // Viewport.
2702 QCOMPARE(interface->childCount(), 1);
2703 QWidget *viewport = abstractScrollArea.viewport();
2704 QVERIFY(viewport);
2705 QVERIFY(verifyChild(viewport, interface, 0, globalGeometry));
2706
2707 // Horizontal scrollBar.
2708 abstractScrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
2709 QWidget *horizontalScrollBar = abstractScrollArea.horizontalScrollBar();
2710
2711 // On OS X >= 10.9 the scrollbar will be hidden unless explicitly enabled in the preferences
2712 bool scrollBarsVisible = !horizontalScrollBar->style()->styleHint(stylehint: QStyle::SH_ScrollBar_Transient, opt: 0, widget: horizontalScrollBar);
2713 int childCount = scrollBarsVisible ? 2 : 1;
2714 QCOMPARE(interface->childCount(), childCount);
2715 QWidget *horizontalScrollBarContainer = horizontalScrollBar->parentWidget();
2716 if (scrollBarsVisible)
2717 QVERIFY(verifyChild(horizontalScrollBarContainer, interface, 1, globalGeometry));
2718
2719 // Horizontal scrollBar widgets.
2720 QLabel *secondLeftLabel = new QLabel(QLatin1String("L2"));
2721 abstractScrollArea.addScrollBarWidget(widget: secondLeftLabel, alignment: Qt::AlignLeft);
2722 QCOMPARE(interface->childCount(), childCount);
2723
2724 QLabel *firstLeftLabel = new QLabel(QLatin1String("L1"));
2725 abstractScrollArea.addScrollBarWidget(widget: firstLeftLabel, alignment: Qt::AlignLeft);
2726 QCOMPARE(interface->childCount(), childCount);
2727
2728 QLabel *secondRightLabel = new QLabel(QLatin1String("R2"));
2729 abstractScrollArea.addScrollBarWidget(widget: secondRightLabel, alignment: Qt::AlignRight);
2730 QCOMPARE(interface->childCount(), childCount);
2731
2732 QLabel *firstRightLabel = new QLabel(QLatin1String("R1"));
2733 abstractScrollArea.addScrollBarWidget(widget: firstRightLabel, alignment: Qt::AlignRight);
2734 QCOMPARE(interface->childCount(), childCount);
2735
2736 // Vertical scrollBar.
2737 abstractScrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
2738 if (scrollBarsVisible)
2739 ++childCount;
2740 QCOMPARE(interface->childCount(), childCount);
2741 QWidget *verticalScrollBar = abstractScrollArea.verticalScrollBar();
2742 QWidget *verticalScrollBarContainer = verticalScrollBar->parentWidget();
2743 if (scrollBarsVisible)
2744 QVERIFY(verifyChild(verticalScrollBarContainer, interface, 2, globalGeometry));
2745
2746 // Vertical scrollBar widgets.
2747 QLabel *secondTopLabel = new QLabel(QLatin1String("T2"));
2748 abstractScrollArea.addScrollBarWidget(widget: secondTopLabel, alignment: Qt::AlignTop);
2749 QCOMPARE(interface->childCount(), childCount);
2750
2751 QLabel *firstTopLabel = new QLabel(QLatin1String("T1"));
2752 abstractScrollArea.addScrollBarWidget(widget: firstTopLabel, alignment: Qt::AlignTop);
2753 QCOMPARE(interface->childCount(), childCount);
2754
2755 QLabel *secondBottomLabel = new QLabel(QLatin1String("B2"));
2756 abstractScrollArea.addScrollBarWidget(widget: secondBottomLabel, alignment: Qt::AlignBottom);
2757 QCOMPARE(interface->childCount(), childCount);
2758
2759 QLabel *firstBottomLabel = new QLabel(QLatin1String("B1"));
2760 abstractScrollArea.addScrollBarWidget(widget: firstBottomLabel, alignment: Qt::AlignBottom);
2761 QCOMPARE(interface->childCount(), childCount);
2762
2763 // CornerWidget.
2764 ++childCount;
2765 abstractScrollArea.setCornerWidget(new QLabel(QLatin1String("C")));
2766 QCOMPARE(interface->childCount(), childCount);
2767 QWidget *cornerWidget = abstractScrollArea.cornerWidget();
2768 if (scrollBarsVisible)
2769 QVERIFY(verifyChild(cornerWidget, interface, 3, globalGeometry));
2770
2771 QCOMPARE(verifyHierarchy(interface), 0);
2772
2773 }
2774
2775 QTestAccessibility::clearEvents();
2776}
2777
2778void tst_QAccessibility::scrollAreaTest()
2779{
2780 {
2781 QScrollArea scrollArea;
2782 scrollArea.show();
2783#if defined(Q_OS_UNIX)
2784 QCoreApplication::processEvents();
2785 QTest::qWait(ms: 100);
2786#endif
2787 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&scrollArea);
2788 QVERIFY(interface);
2789 QCOMPARE(interface->childCount(), 1); // The viewport.
2790 }
2791 QTestAccessibility::clearEvents();
2792}
2793
2794void tst_QAccessibility::listTest()
2795{
2796 {
2797 QListWidget *listView = new QListWidget;
2798 listView->addItem(label: "Oslo");
2799 listView->addItem(label: "Berlin");
2800 listView->addItem(label: "Brisbane");
2801 listView->resize(w: 400,h: 400);
2802 listView->show();
2803 QTest::qWait(ms: 1); // Need this for indexOfchild to work.
2804 QCoreApplication::processEvents();
2805 QTest::qWait(ms: 100);
2806
2807 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(listView);
2808 QCOMPARE(verifyHierarchy(iface), 0);
2809
2810 QCOMPARE((int)iface->role(), (int)QAccessible::List);
2811 QCOMPARE(iface->childCount(), 3);
2812
2813 {
2814 QAccessibleInterface *child1 = iface->child(index: 0);
2815 QVERIFY(child1);
2816 QCOMPARE(iface->indexOfChild(child1), 0);
2817 QCOMPARE(child1->text(QAccessible::Name), QString("Oslo"));
2818 QCOMPARE(child1->role(), QAccessible::ListItem);
2819
2820 QAccessibleInterface *child2 = iface->child(index: 1);
2821 QVERIFY(child2);
2822 QCOMPARE(iface->indexOfChild(child2), 1);
2823 QCOMPARE(child2->text(QAccessible::Name), QString("Berlin"));
2824
2825 QAccessibleInterface *child3 = iface->child(index: 2);
2826 QVERIFY(child3);
2827 QCOMPARE(iface->indexOfChild(child3), 2);
2828 QCOMPARE(child3->text(QAccessible::Name), QString("Brisbane"));
2829 }
2830 QTestAccessibility::clearEvents();
2831
2832 // Check for events
2833 QTest::mouseClick(widget: listView->viewport(), button: Qt::LeftButton, stateKey: { }, pos: listView->visualItemRect(item: listView->item(row: 1)).center());
2834 QAccessibleEvent selectionEvent(listView, QAccessible::SelectionAdd);
2835 selectionEvent.setChild(1);
2836 QAccessibleEvent focusEvent(listView, QAccessible::Focus);
2837 focusEvent.setChild(1);
2838 QVERIFY(QTestAccessibility::containsEvent(&selectionEvent));
2839 QVERIFY(QTestAccessibility::containsEvent(&focusEvent));
2840 QTest::mouseClick(widget: listView->viewport(), button: Qt::LeftButton, stateKey: { }, pos: listView->visualItemRect(item: listView->item(row: 2)).center());
2841
2842 QAccessibleEvent selectionEvent2(listView, QAccessible::SelectionAdd);
2843 selectionEvent2.setChild(2);
2844 QAccessibleEvent focusEvent2(listView, QAccessible::Focus);
2845 focusEvent2.setChild(2);
2846 QVERIFY(QTestAccessibility::containsEvent(&selectionEvent2));
2847 QVERIFY(QTestAccessibility::containsEvent(&focusEvent2));
2848
2849 listView->addItem(label: "Munich");
2850 QCOMPARE(iface->childCount(), 4);
2851
2852 // table 2
2853 QAccessibleTableInterface *table2 = iface->tableInterface();
2854 QVERIFY(table2);
2855 QCOMPARE(table2->columnCount(), 1);
2856 QCOMPARE(table2->rowCount(), 4);
2857 QAccessibleInterface *cell1 = table2->cellAt(row: 0,column: 0);
2858 QVERIFY(cell1);
2859 QCOMPARE(cell1->text(QAccessible::Name), QString("Oslo"));
2860
2861 QAccessibleInterface *cell4 = table2->cellAt(row: 3,column: 0);
2862 QVERIFY(cell4);
2863 QCOMPARE(cell4->text(QAccessible::Name), QString("Munich"));
2864 QCOMPARE(cell4->role(), QAccessible::ListItem);
2865
2866 QAccessibleTableCellInterface *cellInterface = cell4->tableCellInterface();
2867 QVERIFY(cellInterface);
2868 QCOMPARE(cellInterface->rowIndex(), 3);
2869 QCOMPARE(cellInterface->columnIndex(), 0);
2870 QCOMPARE(cellInterface->rowExtent(), 1);
2871 QCOMPARE(cellInterface->columnExtent(), 1);
2872 QVERIFY(cellInterface->rowHeaderCells().isEmpty());
2873 QVERIFY(cellInterface->columnHeaderCells().isEmpty());
2874
2875 QCOMPARE(cellInterface->table()->object(), listView);
2876
2877 listView->clearSelection();
2878 QVERIFY(!(cell4->state().expandable));
2879 QVERIFY( (cell4->state().selectable));
2880 QVERIFY(!(cell4->state().selected));
2881 table2->selectRow(row: 3);
2882 QCOMPARE(listView->selectedItems().size(), 1);
2883 QCOMPARE(listView->selectedItems().at(0)->text(), QLatin1String("Munich"));
2884 QVERIFY(cell4->state().selected);
2885 QVERIFY(cellInterface->isSelected());
2886
2887 QVERIFY(table2->cellAt(-1, 0) == 0);
2888 QVERIFY(table2->cellAt(0, -1) == 0);
2889 QVERIFY(table2->cellAt(0, 1) == 0);
2890 QVERIFY(table2->cellAt(4, 0) == 0);
2891
2892 // verify that unique id stays the same
2893 QAccessible::Id axidMunich = QAccessible::uniqueId(iface: cell4);
2894 // insertion and deletion of items
2895 listView->insertItem(row: 1, label: "Helsinki");
2896 // list: Oslo, Helsinki, Berlin, Brisbane, Munich
2897
2898 QAccessibleInterface *cellMunich2 = table2->cellAt(row: 4,column: 0);
2899 QCOMPARE(cell4, cellMunich2);
2900 QCOMPARE(axidMunich, QAccessible::uniqueId(cellMunich2));
2901
2902 delete listView->takeItem(row: 2);
2903 delete listView->takeItem(row: 2);
2904 // list: Oslo, Helsinki, Munich
2905
2906 QAccessibleInterface *cellMunich3 = table2->cellAt(row: 2,column: 0);
2907 QCOMPARE(cell4, cellMunich3);
2908 QCOMPARE(axidMunich, QAccessible::uniqueId(cellMunich3));
2909 delete listView->takeItem(row: 2);
2910 // list: Oslo, Helsinki
2911 // verify that it doesn't return an invalid item from the cache
2912 QVERIFY(table2->cellAt(2,0) == 0);
2913
2914 delete listView;
2915 }
2916 QTestAccessibility::clearEvents();
2917}
2918
2919void tst_QAccessibility::treeTest()
2920{
2921 QTreeWidget *treeView = new QTreeWidget;
2922
2923 // Empty model (do not crash, etc)
2924 treeView->setColumnCount(0);
2925 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(treeView);
2926 QCOMPARE(iface->child(0), static_cast<QAccessibleInterface*>(0));
2927
2928 treeView->setColumnCount(2);
2929 QTreeWidgetItem *header = new QTreeWidgetItem;
2930 header->setText(column: 0, atext: "Artist");
2931 header->setText(column: 1, atext: "Work");
2932 treeView->setHeaderItem(header);
2933
2934 QTreeWidgetItem *root1 = new QTreeWidgetItem;
2935 root1->setText(column: 0, atext: "Spain");
2936 treeView->addTopLevelItem(item: root1);
2937
2938 QTreeWidgetItem *item1 = new QTreeWidgetItem;
2939 item1->setText(column: 0, atext: "Picasso");
2940 item1->setText(column: 1, atext: "Guernica");
2941 root1->addChild(child: item1);
2942
2943 QTreeWidgetItem *item2 = new QTreeWidgetItem;
2944 item2->setText(column: 0, atext: "Tapies");
2945 item2->setText(column: 1, atext: "Ambrosia");
2946 root1->addChild(child: item2);
2947
2948 QTreeWidgetItem *root2 = new QTreeWidgetItem;
2949 root2->setText(column: 0, atext: "Austria");
2950 treeView->addTopLevelItem(item: root2);
2951
2952 QTreeWidgetItem *item3 = new QTreeWidgetItem;
2953 item3->setText(column: 0, atext: "Klimt");
2954 item3->setText(column: 1, atext: "The Kiss");
2955 root2->addChild(child: item3);
2956
2957 treeView->resize(w: 400,h: 400);
2958 treeView->show();
2959
2960 QCoreApplication::processEvents();
2961 QTest::qWait(ms: 100);
2962
2963 QCOMPARE(verifyHierarchy(iface), 0);
2964
2965 QCOMPARE((int)iface->role(), (int)QAccessible::Tree);
2966 // header and 2 rows (the others are not expanded, thus not visible)
2967 QCOMPARE(iface->childCount(), 6);
2968
2969 QAccessibleInterface *header1 = iface->child(index: 0);
2970 QVERIFY(header1);
2971 QCOMPARE(iface->indexOfChild(header1), 0);
2972 QCOMPARE(header1->text(QAccessible::Name), QString("Artist"));
2973 QCOMPARE(header1->role(), QAccessible::ColumnHeader);
2974
2975 QAccessibleInterface *child1 = iface->child(index: 2);
2976 QVERIFY(child1);
2977 QCOMPARE(iface->indexOfChild(child1), 2);
2978 QCOMPARE(child1->text(QAccessible::Name), QString("Spain"));
2979 QCOMPARE(child1->role(), QAccessible::TreeItem);
2980 QVERIFY(!(child1->state().expanded));
2981
2982 QAccessibleInterface *child2 = 0;
2983 child2 = iface->child(index: 4);
2984 QVERIFY(child2);
2985 QCOMPARE(iface->indexOfChild(child2), 4);
2986 QCOMPARE(child2->text(QAccessible::Name), QString("Austria"));
2987
2988 bool headerHidden = true;
2989 do {
2990 treeView->setHeaderHidden(headerHidden);
2991 header1 = iface->child(index: 0);
2992 QCOMPARE(header1->role(), QAccessible::ColumnHeader);
2993 QCOMPARE(!!header1->state().invisible, headerHidden);
2994 QCOMPARE(header1->text(QAccessible::Name), QStringLiteral("Artist"));
2995 header1 = iface->child(index: 1);
2996 QCOMPARE(header1->role(), QAccessible::ColumnHeader);
2997 QCOMPARE(!!header1->state().invisible, headerHidden);
2998 QCOMPARE(header1->text(QAccessible::Name), QStringLiteral("Work"));
2999
3000 QAccessibleInterface *accSpain = iface->child(index: 2);
3001 QCOMPARE(accSpain->role(), QAccessible::TreeItem);
3002 QCOMPARE(iface->indexOfChild(accSpain), 2);
3003 headerHidden = !headerHidden;
3004 } while (!headerHidden);
3005
3006 QTestAccessibility::clearEvents();
3007
3008 // table 2
3009 QAccessibleTableInterface *table2 = iface->tableInterface();
3010 QVERIFY(table2);
3011 QCOMPARE(table2->columnCount(), 2);
3012 QCOMPARE(table2->rowCount(), 2);
3013 QAccessibleInterface *cell1 = table2->cellAt(row: 0,column: 0);
3014 QVERIFY(cell1);
3015 QCOMPARE(cell1->text(QAccessible::Name), QString("Spain"));
3016 QAccessibleInterface *cell2 = table2->cellAt(row: 1,column: 0);
3017 QVERIFY(cell2);
3018 QCOMPARE(cell2->text(QAccessible::Name), QString("Austria"));
3019 QCOMPARE(cell2->role(), QAccessible::TreeItem);
3020 QCOMPARE(cell2->tableCellInterface()->rowIndex(), 1);
3021 QCOMPARE(cell2->tableCellInterface()->columnIndex(), 0);
3022 QVERIFY(cell2->state().expandable);
3023 QCOMPARE(iface->indexOfChild(cell2), 4);
3024 QVERIFY(!(cell2->state().expanded));
3025 QCOMPARE(table2->columnDescription(1), QString("Work"));
3026
3027 treeView->expandAll();
3028
3029 // Need this for indexOfchild to work.
3030 QCoreApplication::processEvents();
3031 QTest::qWait(ms: 100);
3032
3033 QCOMPARE(table2->columnCount(), 2);
3034 QCOMPARE(table2->rowCount(), 5);
3035 cell1 = table2->cellAt(row: 1,column: 0);
3036 QCOMPARE(cell1->text(QAccessible::Name), QString("Picasso"));
3037 QCOMPARE(iface->indexOfChild(cell1), 4); // 2 header + 2 for root item
3038
3039 cell2 = table2->cellAt(row: 4,column: 0);
3040 QCOMPARE(cell2->text(QAccessible::Name), QString("Klimt"));
3041 QCOMPARE(cell2->role(), QAccessible::TreeItem);
3042 QCOMPARE(cell2->tableCellInterface()->rowIndex(), 4);
3043 QCOMPARE(cell2->tableCellInterface()->columnIndex(), 0);
3044 QVERIFY(!(cell2->state().expandable));
3045 QCOMPARE(iface->indexOfChild(cell2), 10);
3046
3047 QPoint pos = treeView->mapToGlobal(QPoint(0,0));
3048 QModelIndex index = treeView->model()->index(row: 0, column: 0, parent: treeView->model()->index(row: 1, column: 0));
3049 pos += treeView->visualRect(index).center();
3050 pos += QPoint(0, treeView->header()->height());
3051 QAccessibleInterface *childAt2(iface->childAt(x: pos.x(), y: pos.y()));
3052 QVERIFY(childAt2);
3053 QCOMPARE(childAt2->text(QAccessible::Name), QString("Klimt"));
3054
3055 QCOMPARE(table2->columnDescription(0), QString("Artist"));
3056 QCOMPARE(table2->columnDescription(1), QString("Work"));
3057
3058 delete treeView;
3059 QTestAccessibility::clearEvents();
3060}
3061
3062// The table used below is this:
3063// Button (0) | h1 (1) | h2 (2) | h3 (3)
3064// v1 (4) | 0.0 (5) | 1.0 (6) | 2.0 (7)
3065// v2 (8) | 0.1 (9) | 1.1 (10) | 2.1 (11)
3066// v3 (12) | 0.2 (13) | 1.2 (14) | 2.2 (15)
3067void tst_QAccessibility::tableTest()
3068{
3069 QTableWidget *tableView = new QTableWidget(3, 3);
3070 tableView->setColumnCount(3);
3071 QStringList hHeader;
3072 hHeader << "h1" << "h2" << "h3";
3073 tableView->setHorizontalHeaderLabels(hHeader);
3074
3075 QStringList vHeader;
3076 vHeader << "v1" << "v2" << "v3";
3077 tableView->setVerticalHeaderLabels(vHeader);
3078
3079 for (int i = 0; i<9; ++i) {
3080 QTableWidgetItem *item = new QTableWidgetItem;
3081 item->setText(QString::number(i/3) + QString(".") + QString::number(i%3));
3082 tableView->setItem(row: i/3, column: i%3, item);
3083 }
3084
3085 tableView->resize(w: 600,h: 600);
3086 tableView->show();
3087 QVERIFY(QTest::qWaitForWindowExposed(tableView));
3088
3089 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(tableView);
3090 QCOMPARE(verifyHierarchy(iface), 0);
3091
3092 QCOMPARE(iface->role(), QAccessible::Table);
3093 // header and 2 rows (the others are not expanded, thus not visible)
3094 QCOMPARE(iface->childCount(), 9+3+3+1); // cell+headers+topleft button
3095
3096 QAccessibleInterface *cornerButton(iface->child(index: 0));
3097 QVERIFY(cornerButton);
3098 QCOMPARE(iface->indexOfChild(cornerButton), 0);
3099 QCOMPARE(cornerButton->role(), QAccessible::Pane);
3100
3101 QAccessibleInterface *h2(iface->child(index: 2));
3102 QVERIFY(h2);
3103 QCOMPARE(iface->indexOfChild(h2), 2);
3104 QCOMPARE(h2->text(QAccessible::Name), QString("h2"));
3105 QCOMPARE(h2->role(), QAccessible::ColumnHeader);
3106 QVERIFY(!(h2->state().expanded));
3107
3108 QAccessibleInterface *v3(iface->child(index: 12));
3109 QVERIFY(v3);
3110 QCOMPARE(iface->indexOfChild(v3), 12);
3111 QCOMPARE(v3->text(QAccessible::Name), QString("v3"));
3112 QCOMPARE(v3->role(), QAccessible::RowHeader);
3113 QVERIFY(!(v3->state().expanded));
3114
3115
3116 QAccessibleInterface *child10(iface->child(index: 10));
3117 QVERIFY(child10);
3118 QCOMPARE(iface->indexOfChild(child10), 10);
3119 QCOMPARE(child10->text(QAccessible::Name), QString("1.1"));
3120 QAccessibleTableCellInterface *cell10Iface = child10->tableCellInterface();
3121 QCOMPARE(cell10Iface->rowIndex(), 1);
3122 QCOMPARE(cell10Iface->columnIndex(), 1);
3123 QPoint pos = tableView->mapToGlobal(QPoint(0,0));
3124 pos += tableView->visualRect(index: tableView->model()->index(row: 1, column: 1)).center();
3125 pos += QPoint(tableView->verticalHeader()->width(), tableView->horizontalHeader()->height());
3126 QAccessibleInterface *childAt10(iface->childAt(x: pos.x(), y: pos.y()));
3127 QCOMPARE(childAt10->text(QAccessible::Name), QString("1.1"));
3128
3129 QAccessibleInterface *child11(iface->child(index: 11));
3130 QCOMPARE(iface->indexOfChild(child11), 11);
3131 QCOMPARE(child11->text(QAccessible::Name), QString("1.2"));
3132
3133
3134 QTestAccessibility::clearEvents();
3135
3136 // table 2
3137 QAccessibleTableInterface *table2 = iface->tableInterface();
3138 QVERIFY(table2);
3139 QCOMPARE(table2->columnCount(), 3);
3140 QCOMPARE(table2->rowCount(), 3);
3141 QAccessibleInterface *cell1 = table2->cellAt(row: 0,column: 0);
3142 QVERIFY(cell1);
3143 QCOMPARE(cell1->text(QAccessible::Name), QString("0.0"));
3144 QCOMPARE(iface->indexOfChild(cell1), 5);
3145
3146 QAccessibleInterface *cell2(table2->cellAt(row: 0,column: 1));
3147 QVERIFY(cell2);
3148 QCOMPARE(cell2->text(QAccessible::Name), QString("0.1"));
3149 QCOMPARE(cell2->role(), QAccessible::Cell);
3150 QCOMPARE(cell2->tableCellInterface()->rowIndex(), 0);
3151 QCOMPARE(cell2->tableCellInterface()->columnIndex(), 1);
3152 QCOMPARE(iface->indexOfChild(cell2), 6);
3153
3154 QAccessibleInterface *cell3(table2->cellAt(row: 1,column: 2));
3155 QVERIFY(cell3);
3156 QCOMPARE(cell3->text(QAccessible::Name), QString("1.2"));
3157 QCOMPARE(cell3->role(), QAccessible::Cell);
3158 QCOMPARE(cell3->tableCellInterface()->rowIndex(), 1);
3159 QCOMPARE(cell3->tableCellInterface()->columnIndex(), 2);
3160 QCOMPARE(iface->indexOfChild(cell3), 11);
3161
3162 QCOMPARE(table2->columnDescription(0), QString("h1"));
3163 QCOMPARE(table2->columnDescription(1), QString("h2"));
3164 QCOMPARE(table2->columnDescription(2), QString("h3"));
3165 QCOMPARE(table2->rowDescription(0), QString("v1"));
3166 QCOMPARE(table2->rowDescription(1), QString("v2"));
3167 QCOMPARE(table2->rowDescription(2), QString("v3"));
3168
3169 tableView->clearSelection();
3170 tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
3171 tableView->setSelectionMode(QAbstractItemView::SingleSelection);
3172 QVERIFY(!table2->selectRow(0));
3173 QVERIFY(!table2->isRowSelected(0));
3174 tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
3175 QVERIFY(table2->selectRow(0));
3176 QVERIFY(table2->selectRow(1));
3177 QVERIFY(!table2->isRowSelected(0));
3178 tableView->setSelectionMode(QAbstractItemView::MultiSelection);
3179 QVERIFY(table2->selectRow(0));
3180 QVERIFY(table2->isRowSelected(1));
3181 QVERIFY(table2->unselectRow(0));
3182 QVERIFY(!table2->isRowSelected(0));
3183 tableView->setSelectionBehavior(QAbstractItemView::SelectColumns);
3184 QVERIFY(!table2->selectRow(0));
3185 QVERIFY(!table2->isRowSelected(0));
3186 tableView->clearSelection();
3187 QCOMPARE(table2->selectedColumnCount(), 0);
3188 QCOMPARE(table2->selectedRowCount(), 0);
3189 QVERIFY(table2->selectColumn(1));
3190 QVERIFY(table2->isColumnSelected(1));
3191 tableView->clearSelection();
3192 tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
3193 table2->selectColumn(column: 0);
3194 table2->selectColumn(column: 2);
3195 QVERIFY(!(table2->isColumnSelected(2) && table2->isColumnSelected(0)));
3196 tableView->clearSelection();
3197 tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
3198 tableView->setSelectionMode(QAbstractItemView::MultiSelection);
3199 table2->selectColumn(column: 1);
3200 table2->selectRow(row: 1);
3201 QVERIFY(table2->isColumnSelected(1));
3202 QVERIFY(table2->isRowSelected(1));
3203
3204 QAccessibleInterface *cell4 = table2->cellAt(row: 2,column: 2);
3205 QVERIFY(cell1->actionInterface());
3206 QVERIFY(cell1->tableCellInterface());
3207
3208 tableView->clearSelection();
3209 tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
3210 tableView->setSelectionMode(QAbstractItemView::SingleSelection);
3211 QVERIFY(!cell1->tableCellInterface()->isSelected());
3212 QVERIFY(cell1->actionInterface()->actionNames().contains(QAccessibleActionInterface::toggleAction()));
3213 cell1->actionInterface()->doAction(actionName: QAccessibleActionInterface::toggleAction());
3214 QVERIFY(cell2->tableCellInterface()->isSelected());
3215
3216 tableView->clearSelection();
3217 tableView->setSelectionBehavior(QAbstractItemView::SelectColumns);
3218 cell3->actionInterface()->doAction(actionName: QAccessibleActionInterface::toggleAction());
3219 QVERIFY(cell4->tableCellInterface()->isSelected());
3220
3221 tableView->clearSelection();
3222 tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
3223 tableView->setSelectionMode(QAbstractItemView::SingleSelection);
3224 cell1->actionInterface()->doAction(actionName: QAccessibleActionInterface::toggleAction());
3225 QVERIFY(cell1->tableCellInterface()->isSelected());
3226 cell2->actionInterface()->doAction(actionName: QAccessibleActionInterface::toggleAction());
3227 QVERIFY(!cell1->tableCellInterface()->isSelected());
3228
3229 tableView->clearSelection();
3230 tableView->setSelectionMode(QAbstractItemView::MultiSelection);
3231 cell1->actionInterface()->doAction(actionName: QAccessibleActionInterface::toggleAction());
3232 cell2->actionInterface()->doAction(actionName: QAccessibleActionInterface::toggleAction());
3233 QVERIFY(cell1->tableCellInterface()->isSelected());
3234 QVERIFY(cell2->tableCellInterface()->isSelected());
3235 cell2->actionInterface()->doAction(actionName: QAccessibleActionInterface::toggleAction());
3236 QVERIFY(cell1->tableCellInterface()->isSelected());
3237 QVERIFY(!cell2->tableCellInterface()->isSelected());
3238
3239 QAccessibleInterface *cell00 = table2->cellAt(row: 0, column: 0);
3240 QAccessible::Id id00 = QAccessible::uniqueId(iface: cell00);
3241 QVERIFY(id00);
3242 QCOMPARE(cell00->tableCellInterface()->rowIndex(), 0);
3243 QCOMPARE(cell00->tableCellInterface()->columnIndex(), 0);
3244
3245 QAccessibleInterface *cell01 = table2->cellAt(row: 0, column: 1);
3246
3247 QAccessibleInterface *cell02 = table2->cellAt(row: 0, column: 2);
3248 QAccessible::Id id02 = QAccessible::uniqueId(iface: cell02);
3249 QVERIFY(id02);
3250 QCOMPARE(cell02->tableCellInterface()->rowIndex(), 0);
3251 QCOMPARE(cell02->tableCellInterface()->columnIndex(), 2);
3252
3253 QAccessibleInterface *cell20 = table2->cellAt(row: 2, column: 0);
3254 QAccessible::Id id20 = QAccessible::uniqueId(iface: cell20);
3255 QVERIFY(id20);
3256 QCOMPARE(cell20->tableCellInterface()->rowIndex(), 2);
3257 QCOMPARE(cell20->tableCellInterface()->columnIndex(), 0);
3258
3259 QAccessibleInterface *cell22 = table2->cellAt(row: 2, column: 2);
3260 QAccessible::Id id22 = QAccessible::uniqueId(iface: cell22);
3261 QVERIFY(id22);
3262 QCOMPARE(cell22->tableCellInterface()->rowIndex(), 2);
3263 QCOMPARE(cell22->tableCellInterface()->columnIndex(), 2);
3264
3265 // modification: inserting and removing rows/columns
3266 tableView->insertRow(row: 2);
3267 // Button (0) | h1 (1) | h2 (2) | h3 (3)
3268 // v1 (4) | 0.0 (5) | 1.0 (6) | 2.0 (7)
3269 // v2 (8) | 0.1 (9) | 1.1 (10) | 2.1 (11)
3270 // new (12) | (13) | (14) | (15)
3271 // v3 (16) | 0.2 (17) | 1.2 (18) | 2.2 (19)
3272
3273 QAccessibleInterface *cell00_new = table2->cellAt(row: 0, column: 0);
3274 QCOMPARE(cell00, cell00_new);
3275 QCOMPARE(cell00->tableCellInterface()->rowIndex(), 0);
3276 QCOMPARE(cell00->tableCellInterface()->columnIndex(), 0);
3277
3278 QAccessibleInterface *cell02_new = table2->cellAt(row: 0, column: 2);
3279 QCOMPARE(cell02, cell02_new);
3280 QCOMPARE(cell02_new->tableCellInterface()->rowIndex(), 0);
3281 QCOMPARE(cell02_new->tableCellInterface()->columnIndex(), 2);
3282
3283 QAccessibleInterface *cell20_new = table2->cellAt(row: 2, column: 0);
3284 QAccessibleInterface *cell30_new = table2->cellAt(row: 3, column: 0);
3285 QAccessible::Id id20_new = QAccessible::uniqueId(iface: cell20_new);
3286 QVERIFY(id20_new != id20);
3287 QAccessible::Id id30_new = QAccessible::uniqueId(iface: cell30_new);
3288 QCOMPARE(id20, id30_new);
3289 QCOMPARE(cell20->tableCellInterface()->rowIndex(), 3);
3290 QCOMPARE(cell20->tableCellInterface()->columnIndex(), 0);
3291
3292 QAccessibleInterface *cell22_new = table2->cellAt(row: 2, column: 2);
3293 QAccessibleInterface *cell32_new = table2->cellAt(row: 3, column: 2);
3294 QAccessible::Id id22_new = QAccessible::uniqueId(iface: cell22_new);
3295 QVERIFY(id22_new != id22);
3296 QAccessible::Id id32_new = QAccessible::uniqueId(iface: cell32_new);
3297 QCOMPARE(id22, id32_new);
3298 QCOMPARE(cell32_new->tableCellInterface()->rowIndex(), 3);
3299 QCOMPARE(cell32_new->tableCellInterface()->columnIndex(), 2);
3300
3301
3302 QVERIFY(table2->cellAt(0, 0) == cell1);
3303
3304 tableView->insertColumn(column: 2);
3305 // Button (0) | h1 (1) | h2 (2) | (3) | h3 (4)
3306 // v1 (5) | 0.0 (6) | 1.0 (7) | (8) | 2.0 (9)
3307 // v2 (10) | 0.1 (11) | 1.1 (12) | (13) | 2.1 (14)
3308 // new (15) | (16) | (17) | (18) | (19)
3309 // v3 (20) | 0.2 (21) | 1.2 (22) | (23) | 2.2 (24)
3310
3311 cell00_new = table2->cellAt(row: 0, column: 0);
3312 QCOMPARE(cell00, cell00_new);
3313 QCOMPARE(cell00->tableCellInterface()->rowIndex(), 0);
3314 QCOMPARE(cell00->tableCellInterface()->columnIndex(), 0);
3315
3316 QAccessibleInterface *cell01_new = table2->cellAt(row: 0, column: 1);
3317 QCOMPARE(cell01, cell01_new);
3318 QCOMPARE(cell01_new->tableCellInterface()->rowIndex(), 0);
3319 QCOMPARE(cell01_new->tableCellInterface()->columnIndex(), 1);
3320
3321 QAccessibleInterface *cell03_new = table2->cellAt(row: 0, column: 3);
3322 QVERIFY(cell03_new);
3323 QCOMPARE(cell03_new->tableCellInterface()->rowIndex(), 0);
3324 QCOMPARE(cell03_new->tableCellInterface()->columnIndex(), 3);
3325 QCOMPARE(iface->indexOfChild(cell03_new), 9);
3326 QCOMPARE(cell03_new, cell02);
3327
3328 cell30_new = table2->cellAt(row: 3, column: 0);
3329 QCOMPARE(cell30_new, cell20);
3330 QCOMPARE(iface->indexOfChild(cell30_new), 21);
3331
3332
3333 {
3334 QTestAccessibility::clearEvents();
3335 QModelIndex index00 = tableView->model()->index(row: 1, column: 1, parent: tableView->rootIndex());
3336 tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
3337 tableView->setSelectionMode(QAbstractItemView::SingleSelection);
3338 tableView->selectionModel()->select(index: index00, command: QItemSelectionModel::ClearAndSelect);
3339 QAccessibleEvent event(tableView, QAccessible::SelectionAdd);
3340 event.setChild(12);
3341 QCOMPARE(QTestAccessibility::containsEvent(&event), true);
3342 QTestAccessibility::clearEvents();
3343 tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
3344 tableView->setSelectionMode(QAbstractItemView::SingleSelection);
3345 tableView->selectionModel()->select(index: index00, command: QItemSelectionModel::ClearAndSelect);
3346 tableView->horizontalHeader()->setVisible(false);
3347
3348 }
3349 delete tableView;
3350 QVERIFY(!QAccessible::accessibleInterface(id00));
3351 QTestAccessibility::clearEvents();
3352}
3353
3354void tst_QAccessibility::calendarWidgetTest()
3355{
3356#if QT_CONFIG(calendarwidget)
3357 {
3358 QCalendarWidget calendarWidget;
3359
3360 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&calendarWidget);
3361 QVERIFY(interface);
3362 QCOMPARE(interface->role(), QAccessible::Table);
3363 QVERIFY(!interface->rect().isValid());
3364 QCOMPARE(interface->childAt(200, 200), static_cast<QAccessibleInterface*>(0));
3365
3366 calendarWidget.resize(w: 400, h: 300);
3367 calendarWidget.show();
3368#if defined(Q_OS_UNIX)
3369 QCoreApplication::processEvents();
3370 QTest::qWait(ms: 100);
3371#endif
3372
3373 // 1 = navigationBar, 2 = view.
3374 QCOMPARE(interface->childCount(), 2);
3375
3376 const QRect globalGeometry = QRect(calendarWidget.mapToGlobal(QPoint(0, 0)),
3377 calendarWidget.size());
3378 QCOMPARE(interface->rect(), globalGeometry);
3379
3380 QWidget *navigationBar = 0;
3381 foreach (QObject *child, calendarWidget.children()) {
3382 if (child->objectName() == QLatin1String("qt_calendar_navigationbar")) {
3383 navigationBar = static_cast<QWidget *>(child);
3384 break;
3385 }
3386 }
3387 QVERIFY(navigationBar);
3388 QVERIFY(verifyChild(navigationBar, interface, 0, globalGeometry));
3389
3390 QAbstractItemView *calendarView = 0;
3391 foreach (QObject *child, calendarWidget.children()) {
3392 if (child->objectName() == QLatin1String("qt_calendar_calendarview")) {
3393 calendarView = static_cast<QAbstractItemView *>(child);
3394 break;
3395 }
3396 }
3397 QVERIFY(calendarView);
3398 QVERIFY(verifyChild(calendarView, interface, 1, globalGeometry));
3399
3400 // Hide navigation bar.
3401 calendarWidget.setNavigationBarVisible(false);
3402 QCOMPARE(interface->childCount(), 1);
3403 QVERIFY(!navigationBar->isVisible());
3404
3405 QVERIFY(verifyChild(calendarView, interface, 0, globalGeometry));
3406
3407 // Show navigation bar.
3408 calendarWidget.setNavigationBarVisible(true);
3409 QCOMPARE(interface->childCount(), 2);
3410 QVERIFY(navigationBar->isVisible());
3411
3412 // Navigate to the navigation bar via Child.
3413 QAccessibleInterface *navigationBarInterface = interface->child(index: 0);
3414 QVERIFY(navigationBarInterface);
3415 QCOMPARE(navigationBarInterface->object(), (QObject*)navigationBar);
3416
3417 // Navigate to the view via Child.
3418 QAccessibleInterface *calendarViewInterface = interface->child(index: 1);
3419 QVERIFY(calendarViewInterface);
3420 QCOMPARE(calendarViewInterface->object(), (QObject*)calendarView);
3421
3422 QVERIFY(!interface->child(-1));
3423
3424 // In order for geometric navigation to work they must share the same parent
3425 QCOMPARE(navigationBarInterface->parent()->object(), calendarViewInterface->parent()->object());
3426 QVERIFY(navigationBarInterface->rect().bottom() < calendarViewInterface->rect().top());
3427 calendarViewInterface = 0;
3428 navigationBarInterface = 0;
3429
3430 }
3431 QTestAccessibility::clearEvents();
3432#endif // QT_CONFIG(calendarwidget)
3433}
3434
3435void tst_QAccessibility::dockWidgetTest()
3436{
3437#if QT_CONFIG(dockwidget)
3438 // Set up a proper main window with two dock widgets
3439 QMainWindow *mw = new QMainWindow();
3440 QFrame *central = new QFrame(mw);
3441 mw->setCentralWidget(central);
3442 QMenuBar *mb = new QMenuBar(mw);
3443 mb->addAction(text: tr(s: "&File"));
3444 mw->setMenuBar(mb);
3445
3446 QDockWidget *dock1 = new QDockWidget(mw);
3447 dock1->setWindowTitle("Dock 1");
3448 mw->addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: dock1);
3449 QPushButton *pb1 = new QPushButton(tr(s: "Push me"), dock1);
3450 dock1->setWidget(pb1);
3451
3452 QDockWidget *dock2 = new QDockWidget(mw);
3453 dock2->setWindowTitle("Dock 2");
3454 mw->addDockWidget(area: Qt::BottomDockWidgetArea, dockwidget: dock2);
3455 QPushButton *pb2 = new QPushButton(tr(s: "Push me"), dock2);
3456 dock2->setWidget(pb2);
3457 dock2->setFeatures(QDockWidget::DockWidgetClosable);
3458
3459 mw->resize(w: 600,h: 400);
3460 mw->show();
3461 QVERIFY(QTest::qWaitForWindowExposed(mw));
3462
3463 QAccessibleInterface *accMainWindow = QAccessible::queryAccessibleInterface(mw);
3464 // 4 children: menu bar, dock1, dock2, and central widget
3465 QCOMPARE(accMainWindow->childCount(), 4);
3466 QAccessibleInterface *accDock1 = 0;
3467 QAccessibleInterface *accDock2 = 0;
3468 for (int i = 0; i < 4; ++i) {
3469 QAccessibleInterface *child = accMainWindow->child(index: i);
3470 if (child && child->object() == dock1)
3471 accDock1 = child;
3472 if (child && child->object() == dock2)
3473 accDock2 = child;
3474 }
3475
3476 // Dock widgets consist of
3477 // 0 contents
3478 // 1 close button
3479 // 2 float button
3480 QVERIFY(accDock1);
3481 QCOMPARE(accDock1->role(), QAccessible::Window);
3482 QCOMPARE(accDock1->text(QAccessible::Name), dock1->windowTitle());
3483 QCOMPARE(accDock1->childCount(), 3);
3484
3485 QAccessibleInterface *dock1Widget = accDock1->child(index: 0);
3486 QCOMPARE(dock1Widget->role(), QAccessible::Button);
3487 QCOMPARE(dock1Widget->text(QAccessible::Name), pb1->text());
3488
3489#ifdef Q_OS_MAC
3490 QEXPECT_FAIL("", "Dock Widget geometry on Mac seems broken.", Continue);
3491#endif
3492 QVERIFY(accDock1->rect().contains(dock1Widget->rect()));
3493 QCOMPARE(accDock1->indexOfChild(dock1Widget), 0);
3494 QCOMPARE(dock1Widget->parent()->object(), dock1);
3495
3496 QAccessibleInterface *dock1Close = accDock1->child(index: 1);
3497 QCOMPARE(dock1Close->role(), QAccessible::Button);
3498 QCOMPARE(dock1Close->text(QAccessible::Name), QDockWidget::tr("Close"));
3499 QVERIFY(accDock1->rect().contains(dock1Close->rect()));
3500 QCOMPARE(accDock1->indexOfChild(dock1Close), 1);
3501 QCOMPARE(dock1Close->parent()->object(), dock1);
3502
3503 QAccessibleInterface *dock1Float = accDock1->child(index: 2);
3504 QCOMPARE(dock1Float->role(), QAccessible::Button);
3505 QCOMPARE(dock1Float->text(QAccessible::Name), QDockWidget::tr("Float"));
3506 QVERIFY(accDock1->rect().contains(dock1Float->rect()));
3507 QCOMPARE(accDock1->indexOfChild(dock1Float), 2);
3508 QVERIFY(!dock1Float->state().invisible);
3509
3510 QVERIFY(accDock2);
3511 QCOMPARE(accDock2->role(), QAccessible::Window);
3512 QCOMPARE(accDock2->text(QAccessible::Name), dock2->windowTitle());
3513 QCOMPARE(accDock2->childCount(), 3);
3514
3515 QAccessibleInterface *dock2Widget = accDock2->child(index: 0);
3516 QCOMPARE(dock2Widget->role(), QAccessible::Button);
3517 QCOMPARE(dock2Widget->text(QAccessible::Name), pb1->text());
3518#ifdef Q_OS_MAC
3519 QEXPECT_FAIL("", "Dock Widget geometry on Mac seems broken.", Continue);
3520#endif
3521 QVERIFY(accDock2->rect().contains(dock2Widget->rect()));
3522 QCOMPARE(accDock2->indexOfChild(dock2Widget), 0);
3523
3524 QAccessibleInterface *dock2Close = accDock2->child(index: 1);
3525 QCOMPARE(dock2Close->role(), QAccessible::Button);
3526 QCOMPARE(dock2Close->text(QAccessible::Name), QDockWidget::tr("Close"));
3527 QVERIFY(accDock2->rect().contains(dock2Close->rect()));
3528 QCOMPARE(accDock2->indexOfChild(dock2Close), 1);
3529 QVERIFY(!dock2Close->state().invisible);
3530
3531 QAccessibleInterface *dock2Float = accDock2->child(index: 2);
3532 QCOMPARE(dock2Float->role(), QAccessible::Button);
3533 QCOMPARE(dock2Float->text(QAccessible::Name), QDockWidget::tr("Float"));
3534 QCOMPARE(accDock2->indexOfChild(dock2Float), 2);
3535 QVERIFY(dock2Float->state().invisible);
3536
3537 QPoint buttonPoint = pb2->mapToGlobal(QPoint(pb2->width()/2, pb2->height()/2));
3538 QAccessibleInterface *childAt = accDock2->childAt(x: buttonPoint.x(), y: buttonPoint.y());
3539 QVERIFY(childAt);
3540 QCOMPARE(childAt->object(), pb2);
3541
3542 QWidget *close1 = qobject_cast<QWidget*>(o: dock1Close->object());
3543 QPoint close1ButtonPoint = close1->mapToGlobal(QPoint(close1->width()/2, close1->height()/2));
3544 QAccessibleInterface *childAt2 = accDock1->childAt(x: close1ButtonPoint.x(), y: close1ButtonPoint.y());
3545 QVERIFY(childAt2);
3546 QCOMPARE(childAt2->object(), close1);
3547
3548 // custom title bar widget
3549 QDockWidget *dock3 = new QDockWidget(mw);
3550 dock3->setWindowTitle("Dock 3");
3551 mw->addDockWidget(area: Qt::LeftDockWidgetArea, dockwidget: dock3);
3552 QPushButton *pb3 = new QPushButton(tr(s: "Push me"), dock3);
3553 dock3->setWidget(pb3);
3554 QLabel *titleLabel = new QLabel("I am a title widget");
3555 dock3->setTitleBarWidget(titleLabel);
3556
3557 QAccessibleInterface *accDock3 = accMainWindow->child(index: 4);
3558 QVERIFY(accDock3);
3559 QCOMPARE(accDock3->role(), QAccessible::Window);
3560 QCOMPARE(accDock3->text(QAccessible::Name), dock3->windowTitle());
3561 QCOMPARE(accDock3->childCount(), 2);
3562 QAccessibleInterface *titleWidget = accDock3->child(index: 1);
3563 QVERIFY(titleWidget);
3564 QCOMPARE(titleWidget->text(QAccessible::Name), titleLabel->text());
3565 QAccessibleInterface *dock3Widget = accDock3->child(index: 0);
3566 QCOMPARE(dock3Widget->text(QAccessible::Name), pb3->text());
3567
3568 delete mw;
3569 QTestAccessibility::clearEvents();
3570#endif // QT_CONFIG(dockwidget)
3571}
3572
3573void tst_QAccessibility::comboBoxTest()
3574{
3575 { // not editable combobox
3576 QComboBox combo;
3577 combo.addItems(texts: QStringList() << "one" << "two" << "three");
3578 // Fully decorated windows have a minimum width of 160 on Windows.
3579 combo.setMinimumWidth(200);
3580 combo.show();
3581 QVERIFY(QTest::qWaitForWindowExposed(&combo));
3582
3583 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&combo);
3584 QCOMPARE(verifyHierarchy(iface), 0);
3585
3586 QCOMPARE(iface->role(), QAccessible::ComboBox);
3587 QCOMPARE(iface->childCount(), 1);
3588
3589#ifdef Q_OS_UNIX
3590 QCOMPARE(iface->text(QAccessible::Name), QLatin1String("one"));
3591#endif
3592 QCOMPARE(iface->text(QAccessible::Value), QLatin1String("one"));
3593 combo.setCurrentIndex(2);
3594#ifdef Q_OS_UNIX
3595 QCOMPARE(iface->text(QAccessible::Name), QLatin1String("three"));
3596#endif
3597 QCOMPARE(iface->text(QAccessible::Value), QLatin1String("three"));
3598
3599 QAccessibleInterface *listIface = iface->child(index: 0);
3600 QCOMPARE(listIface->role(), QAccessible::List);
3601 QCOMPARE(listIface->childCount(), 3);
3602
3603 QVERIFY(!combo.view()->isVisible());
3604 QVERIFY(iface->actionInterface());
3605 QCOMPARE(iface->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction() << QAccessibleActionInterface::pressAction());
3606 iface->actionInterface()->doAction(actionName: QAccessibleActionInterface::showMenuAction());
3607 QTRY_VERIFY(combo.view()->isVisible());
3608
3609 }
3610
3611 { // editable combobox
3612 QComboBox editableCombo;
3613 editableCombo.setMinimumWidth(200);
3614 editableCombo.show();
3615 editableCombo.setEditable(true);
3616 editableCombo.addItems(texts: QStringList() << "foo" << "bar" << "baz");
3617
3618 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&editableCombo);
3619 QCOMPARE(verifyHierarchy(iface), 0);
3620
3621 QCOMPARE(iface->role(), QAccessible::ComboBox);
3622 QCOMPARE(iface->childCount(), 2);
3623
3624 QAccessibleInterface *listIface = iface->child(index: 0);
3625 QCOMPARE(listIface->role(), QAccessible::List);
3626 QAccessibleInterface *editIface = iface->child(index: 1);
3627 QCOMPARE(editIface->role(), QAccessible::EditableText);
3628 }
3629
3630 QTestAccessibility::clearEvents();
3631}
3632
3633void tst_QAccessibility::labelTest()
3634{
3635 QWidget *window = new QWidget;
3636 QString text = "Hello World";
3637 QLabel *label = new QLabel(text, window);
3638 setFrameless(label);
3639 QLineEdit *buddy = new QLineEdit(window);
3640 label->setBuddy(buddy);
3641 window->resize(w: 320, h: 200);
3642 window->show();
3643
3644 QVERIFY(QTest::qWaitForWindowExposed(window));
3645#if defined(Q_OS_UNIX)
3646 QCoreApplication::processEvents();
3647#endif
3648 QTest::qWait(ms: 100);
3649
3650 QAccessibleInterface *acc_label = QAccessible::queryAccessibleInterface(label);
3651 QVERIFY(acc_label);
3652
3653 QCOMPARE(acc_label->text(QAccessible::Name), text);
3654 QCOMPARE(acc_label->state().editable, false);
3655 QCOMPARE(acc_label->state().passwordEdit, false);
3656 QCOMPARE(acc_label->state().disabled, false);
3657 QCOMPARE(acc_label->state().focused, false);
3658 QCOMPARE(acc_label->state().focusable, false);
3659 QCOMPARE(acc_label->state().readOnly, true);
3660
3661 QVector<QPair<QAccessibleInterface *, QAccessible::Relation> > rels = acc_label->relations();
3662 QCOMPARE(rels.count(), 1);
3663 QAccessibleInterface *iface = rels.first().first;
3664 QAccessible::Relation rel = rels.first().second;
3665
3666 QCOMPARE(rel, QAccessible::Labelled);
3667 QCOMPARE(iface->role(), QAccessible::EditableText);
3668
3669 delete window;
3670 QTestAccessibility::clearEvents();
3671
3672 QPixmap testPixmap(50, 50);
3673 testPixmap.fill();
3674
3675 QLabel imageLabel;
3676 imageLabel.setPixmap(testPixmap);
3677 imageLabel.setToolTip("Test Description");
3678
3679 acc_label = QAccessible::queryAccessibleInterface(&imageLabel);
3680 QVERIFY(acc_label);
3681
3682 QAccessibleImageInterface *imageInterface = acc_label->imageInterface();
3683 QVERIFY(imageInterface);
3684
3685 QCOMPARE(imageInterface->imageSize(), testPixmap.size());
3686 QCOMPARE(imageInterface->imageDescription(), QString::fromLatin1("Test Description"));
3687 const QPoint labelPos = imageLabel.mapToGlobal(QPoint(0,0));
3688 QCOMPARE(imageInterface->imagePosition(), labelPos);
3689
3690 QTestAccessibility::clearEvents();
3691}
3692
3693void tst_QAccessibility::accelerators()
3694{
3695 QWidget *window = new QWidget;
3696 QHBoxLayout *lay = new QHBoxLayout(window);
3697 QLabel *label = new QLabel(tr(s: "&Line edit"), window);
3698 QLineEdit *le = new QLineEdit(window);
3699 lay->addWidget(label);
3700 lay->addWidget(le);
3701 label->setBuddy(le);
3702
3703 window->show();
3704
3705 QAccessibleInterface *accLineEdit = QAccessible::queryAccessibleInterface(le);
3706 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("L"));
3707 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("L"));
3708 label->setText(tr(s: "Q &"));
3709 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QString());
3710 label->setText(tr(s: "Q &&"));
3711 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QString());
3712 label->setText(tr(s: "Q && A"));
3713 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QString());
3714 label->setText(tr(s: "Q &&&A"));
3715 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("A"));
3716 label->setText(tr(s: "Q &&A"));
3717 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QString());
3718
3719#if !defined(QT_NO_DEBUG) && !defined(Q_OS_MAC)
3720 QTest::ignoreMessage(type: QtWarningMsg, message: "QKeySequence::mnemonic: \"Q &A&B\" contains multiple occurrences of '&'");
3721#endif
3722 label->setText(tr(s: "Q &A&B"));
3723 QCOMPARE(accLineEdit->text(QAccessible::Accelerator), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("A"));
3724
3725#if defined(Q_OS_UNIX)
3726 QCoreApplication::processEvents();
3727#endif
3728 QTest::qWait(ms: 100);
3729 delete window;
3730 QTestAccessibility::clearEvents();
3731}
3732
3733#ifdef QT_SUPPORTS_IACCESSIBLE2
3734static IUnknown *queryIA2(IAccessible *acc, const IID &iid)
3735{
3736 IUnknown *resultInterface = 0;
3737 IServiceProvider *pService = 0;
3738 HRESULT hr = acc->QueryInterface(IID_IServiceProvider, (void **)&pService);
3739 if (SUCCEEDED(hr)) {
3740 IAccessible2 *pIA2 = 0;
3741 hr = pService->QueryService(IID_IAccessible, IID_IAccessible2, (void**)&pIA2);
3742 if (SUCCEEDED(hr) && pIA2) {
3743 // The control supports IAccessible2.
3744 // pIA2 is the reference to the accessible object's IAccessible2 interface.
3745 hr = pIA2->QueryInterface(iid, (void**)&resultInterface);
3746 pIA2->Release();
3747 }
3748 // The control supports IAccessible2.
3749 pService->Release();
3750 }
3751 return resultInterface;
3752}
3753#endif
3754
3755void tst_QAccessibility::bridgeTest()
3756{
3757 // For now this is a simple test to see if the bridge is working at all.
3758 // Ideally it should be extended to test all aspects of the bridge.
3759#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
3760
3761 QWidget window;
3762 QVBoxLayout *lay = new QVBoxLayout(&window);
3763 QPushButton *button = new QPushButton(tr("Push me"), &window);
3764 QTextEdit *te = new QTextEdit(&window);
3765 te->setText(QLatin1String("hello world\nhow are you today?\n"));
3766
3767 // Add QTableWidget
3768 QTableWidget *tableWidget = new QTableWidget(3, 3, &window);
3769 tableWidget->setColumnCount(3);
3770 QStringList hHeader;
3771 hHeader << "h1" << "h2" << "h3";
3772 tableWidget->setHorizontalHeaderLabels(hHeader);
3773
3774 QStringList vHeader;
3775 vHeader << "v1" << "v2" << "v3";
3776 tableWidget->setVerticalHeaderLabels(vHeader);
3777
3778 for (int i = 0; i<9; ++i) {
3779 QTableWidgetItem *item = new QTableWidgetItem;
3780 item->setText(QString::number(i/3) + QString(".") + QString::number(i%3));
3781 tableWidget->setItem(i/3, i%3, item);
3782 }
3783
3784 tableWidget->setFixedSize(600, 600);
3785
3786 QLabel *label = new QLabel(tr("Push my buddy"));
3787 label->setBuddy(button);
3788
3789 lay->addWidget(button);
3790 lay->addWidget(te);
3791 lay->addWidget(tableWidget);
3792 lay->addWidget(label);
3793
3794 window.show();
3795 QVERIFY(QTest::qWaitForWindowExposed(&window));
3796
3797 // Validate button position through the accessible interface.
3798 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(button);
3799 QPoint buttonPos = button->mapToGlobal(QPoint(0,0));
3800 QRect buttonRect = iface->rect();
3801 QCOMPARE(buttonRect.topLeft(), buttonPos);
3802
3803 // All set, now test the bridge.
3804 const QPoint nativePos = QHighDpi::toNativePixels(buttonRect.center(), window.windowHandle());
3805 POINT pt{nativePos.x(), nativePos.y()};
3806
3807 // Initialize COM stuff.
3808 HRESULT hr = CoInitialize(nullptr);
3809 QVERIFY(SUCCEEDED(hr));
3810
3811 // Get UI Automation interface.
3812 IUIAutomation *automation = nullptr;
3813 hr = CoCreateInstance(CLSID_CUIAutomation, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&automation));
3814 QVERIFY(SUCCEEDED(hr));
3815
3816 // Get button element from UI Automation using point.
3817 IUIAutomationElement *buttonElement = nullptr;
3818 hr = automation->ElementFromPoint(pt, &buttonElement);
3819 QVERIFY(SUCCEEDED(hr));
3820
3821 // Check that it has a button control type ID.
3822 CONTROLTYPEID controlTypeId;
3823 hr = buttonElement->get_CurrentControlType(&controlTypeId);
3824 QVERIFY(SUCCEEDED(hr));
3825 QCOMPARE(controlTypeId, UIA_ButtonControlTypeId);
3826
3827 // Test the bounding rectangle.
3828 RECT rect;
3829 hr = buttonElement->get_CurrentBoundingRectangle(&rect);
3830 QVERIFY(SUCCEEDED(hr));
3831 QCOMPARE(buttonRect, QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top));
3832
3833 buttonElement->Release();
3834
3835 // Get native window handle.
3836 QWindow *windowHandle = window.windowHandle();
3837 QVERIFY(windowHandle != 0);
3838 QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface();
3839 QVERIFY(platform != 0);
3840 HWND hWnd = (HWND)platform->nativeResourceForWindow("handle", windowHandle);
3841 QVERIFY(hWnd != 0);
3842
3843 // Get automation element for the window from handle.
3844 IUIAutomationElement *windowElement = nullptr;
3845 hr = automation->ElementFromHandle(hWnd, &windowElement);
3846 QVERIFY(SUCCEEDED(hr));
3847 QVERIFY(windowElement != 0);
3848
3849 // Validate that the top-level widget is reported as a window.
3850 hr = windowElement->get_CurrentControlType(&controlTypeId);
3851 QVERIFY(SUCCEEDED(hr));
3852 QCOMPARE(controlTypeId, UIA_WindowControlTypeId);
3853
3854 // Get a tree walker to walk over elements.
3855 IUIAutomationTreeWalker *controlWalker = nullptr;
3856 IUIAutomationElement *node = nullptr;
3857 QList<IUIAutomationElement *> nodeList;
3858
3859 hr = automation->get_ControlViewWalker(&controlWalker);
3860 QVERIFY(SUCCEEDED(hr));
3861 QVERIFY(controlWalker != 0);
3862
3863 hr = controlWalker->GetFirstChildElement(windowElement, &node);
3864 QVERIFY(SUCCEEDED(hr));
3865 QVERIFY(node != 0);
3866
3867 int numElements = 5; // Title bar + 4 widgets
3868
3869 while (node) {
3870 nodeList.append(node);
3871 QVERIFY(nodeList.size() <= numElements);
3872 IUIAutomationElement *next = nullptr;
3873 hr = controlWalker->GetNextSiblingElement(node, &next);
3874 QVERIFY(SUCCEEDED(hr));
3875 node = next;
3876 }
3877 QCOMPARE(nodeList.size(), numElements);
3878
3879 // Title bar
3880 hr = nodeList.at(0)->get_CurrentControlType(&controlTypeId);
3881 QVERIFY(SUCCEEDED(hr));
3882 QCOMPARE(controlTypeId, UIA_TitleBarControlTypeId);
3883
3884 // Button
3885 hr = nodeList.at(1)->get_CurrentControlType(&controlTypeId);
3886 QVERIFY(SUCCEEDED(hr));
3887 QCOMPARE(controlTypeId, UIA_ButtonControlTypeId);
3888
3889 // Edit
3890 hr = nodeList.at(2)->get_CurrentControlType(&controlTypeId);
3891 QVERIFY(SUCCEEDED(hr));
3892 QCOMPARE(controlTypeId, UIA_EditControlTypeId);
3893
3894 // Table
3895 hr = nodeList.at(3)->get_CurrentControlType(&controlTypeId);
3896 QVERIFY(SUCCEEDED(hr));
3897 QCOMPARE(controlTypeId, UIA_TableControlTypeId);
3898
3899 // Label
3900 hr = nodeList.at(4)->get_CurrentControlType(&controlTypeId);
3901 QVERIFY(SUCCEEDED(hr));
3902 QCOMPARE(controlTypeId, UIA_TextControlTypeId);
3903
3904 for (auto nd : nodeList) {
3905 nd->Release();
3906 }
3907
3908 controlWalker->Release();
3909 windowElement->Release();
3910 automation->Release();
3911 CoUninitialize();
3912
3913 QTestAccessibility::clearEvents();
3914#endif
3915}
3916
3917class FocusChildTestAccessibleInterface : public QAccessibleInterface
3918{
3919public:
3920 FocusChildTestAccessibleInterface(int index, bool focus, QAccessibleInterface *parent)
3921 : m_parent(parent)
3922 , m_index(index)
3923 , m_focus(focus)
3924 {
3925 QAccessible::registerAccessibleInterface(iface: this);
3926 }
3927
3928 bool isValid() const override { return true; }
3929 QObject *object() const override { return nullptr; }
3930 QAccessibleInterface *childAt(int, int) const override { return nullptr; }
3931 QAccessibleInterface *parent() const override { return m_parent; }
3932 QAccessibleInterface *child(int) const override { return nullptr; }
3933 int childCount() const override { return 0; }
3934 int indexOfChild(const QAccessibleInterface *) const override { return -1; }
3935 QString text(QAccessible::Text) const override { return QStringLiteral("FocusChildTestAccessibleInterface %1").arg(a: m_index); }
3936 void setText(QAccessible::Text, const QString &) override { }
3937 QRect rect() const override { return QRect(); }
3938 QAccessible::Role role() const override { return QAccessible::StaticText; }
3939
3940 QAccessible::State state() const override
3941 {
3942 QAccessible::State s;
3943 s.focused = m_focus;
3944 return s;
3945 }
3946
3947private:
3948 QAccessibleInterface *m_parent;
3949 int m_index;
3950 bool m_focus;
3951};
3952
3953class FocusChildTestAccessibleWidget : public QAccessibleWidget
3954{
3955public:
3956 static QAccessibleInterface *ifaceFactory(const QString &key, QObject *o)
3957 {
3958 if (key == "QtTestAccessibleWidget")
3959 return new FocusChildTestAccessibleWidget(static_cast<QtTestAccessibleWidget *>(o));
3960 return 0;
3961 }
3962
3963 FocusChildTestAccessibleWidget(QtTestAccessibleWidget *w)
3964 : QAccessibleWidget(w)
3965 {
3966 m_children.push_back(t: new FocusChildTestAccessibleInterface(0, false, this));
3967 m_children.push_back(t: new FocusChildTestAccessibleInterface(1, true, this));
3968 m_children.push_back(t: new FocusChildTestAccessibleInterface(2, false, this));
3969 }
3970
3971 QAccessible::State state() const override
3972 {
3973 QAccessible::State s = QAccessibleWidget::state();
3974 s.focused = false;
3975 return s;
3976 }
3977
3978 QAccessibleInterface *focusChild() const override
3979 {
3980 for (int i = 0; i < childCount(); ++i) {
3981 if (child(index: i)->state().focused)
3982 return child(index: i);
3983 }
3984
3985 return nullptr;
3986 }
3987
3988 QAccessibleInterface *child(int index) const override
3989 {
3990 return m_children[index];
3991 }
3992
3993 int childCount() const override
3994 {
3995 return m_children.size();
3996 }
3997
3998private:
3999 QVector<QAccessibleInterface *> m_children;
4000};
4001
4002void tst_QAccessibility::focusChild()
4003{
4004 {
4005 QMainWindow mainWindow;
4006 QtTestAccessibleWidget *widget1 = new QtTestAccessibleWidget(0, "Widget1");
4007 QAccessibleInterface *iface1 = QAccessible::queryAccessibleInterface(widget1);
4008 QtTestAccessibleWidget *widget2 = new QtTestAccessibleWidget(0, "Widget2");
4009 QAccessibleInterface *iface2 = QAccessible::queryAccessibleInterface(widget2);
4010
4011 QWidget *centralWidget = new QWidget;
4012 QHBoxLayout *centralLayout = new QHBoxLayout;
4013 centralWidget->setLayout(centralLayout);
4014 mainWindow.setCentralWidget(centralWidget);
4015 centralLayout->addWidget(widget1);
4016 centralLayout->addWidget(widget2);
4017
4018 mainWindow.show();
4019 QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
4020
4021 // widget1 has not been focused yet -> it has no active focus nor focus widget.
4022 QVERIFY(!iface1->focusChild());
4023
4024 // widget1 is focused -> it has active focus and focus widget.
4025 widget1->setFocus();
4026 QCOMPARE(iface1->focusChild(), iface1);
4027 QCOMPARE(QAccessible::queryAccessibleInterface(&mainWindow)->focusChild(), iface1);
4028
4029 // widget1 lose focus -> it has no active focus but has focus widget what is itself.
4030 // In this case, the focus child of widget1's interface is itself and the focusChild() call
4031 // should not run into an infinite recursion.
4032 widget2->setFocus();
4033 QCOMPARE(iface1->focusChild(), iface1);
4034 QCOMPARE(iface2->focusChild(), iface2);
4035 QCOMPARE(QAccessible::queryAccessibleInterface(&mainWindow)->focusChild(), iface2);
4036
4037 delete widget1;
4038 delete widget2;
4039 delete centralWidget;
4040 QTestAccessibility::clearEvents();
4041 }
4042
4043 {
4044 QMainWindow mainWindow;
4045 QAccessible::installFactory(&FocusChildTestAccessibleWidget::ifaceFactory);
4046 QtTestAccessibleWidget *widget = new QtTestAccessibleWidget(0, "FocusChildTestWidget");
4047 mainWindow.setCentralWidget(widget);
4048 mainWindow.show();
4049 QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
4050
4051 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&mainWindow);
4052 QVERIFY(!iface->focusChild());
4053 widget->setFocus();
4054 QCOMPARE(iface->focusChild(), QAccessible::queryAccessibleInterface(widget)->child(1));
4055
4056 delete widget;
4057 QAccessible::removeFactory(FocusChildTestAccessibleWidget::ifaceFactory);
4058 QTestAccessibility::clearEvents();
4059 }
4060
4061 {
4062 QMainWindow mainWindow;
4063 QTabBar *tabBar = new QTabBar();
4064 tabBar->insertTab(index: 0, text: "First tab");
4065 tabBar->insertTab(index: 1, text: "Second tab");
4066 tabBar->insertTab(index: 2, text: "Third tab");
4067 mainWindow.setCentralWidget(tabBar);
4068 mainWindow.show();
4069 QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
4070
4071 tabBar->setFocus();
4072 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&mainWindow);
4073 QCOMPARE(iface->focusChild()->text(QAccessible::Name), QStringLiteral("First tab"));
4074 QCOMPARE(iface->focusChild()->role(), QAccessible::PageTab);
4075 tabBar->setCurrentIndex(1);
4076 QCOMPARE(iface->focusChild()->text(QAccessible::Name), QStringLiteral("Second tab"));
4077 QCOMPARE(iface->focusChild()->role(), QAccessible::PageTab);
4078
4079 delete tabBar;
4080 QTestAccessibility::clearEvents();
4081 }
4082}
4083
4084
4085QTEST_MAIN(tst_QAccessibility)
4086#include "tst_qaccessibility.moc"
4087

source code of qtbase/tests/auto/other/qaccessibility/tst_qaccessibility.cpp