diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-01-10 16:36:05 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-27 16:00:13 +0100 |
commit | f0595284cbe85b5716daaf307154c1ed7e660a26 (patch) | |
tree | a3a0c1347130b1b6c8418780d16d2d7f308ebf69 | |
parent | a1ee538395198f998a73f3ef7545392aeb680900 (diff) |
Accessibility: add states to QQuickAccessibleAttached
This makes it possible to set the state of accessible objects in qml.
Change-Id: Ide70b885dac8fed180d2b221540cf2b699ac78ff
Reviewed-by: Jan Arve Sæther <jan-arve.saether@digia.com>
6 files changed, 233 insertions, 52 deletions
diff --git a/src/plugins/accessible/quick/qaccessiblequickitem.cpp b/src/plugins/accessible/quick/qaccessiblequickitem.cpp index 3521d4f98e..4b68574149 100644 --- a/src/plugins/accessible/quick/qaccessiblequickitem.cpp +++ b/src/plugins/accessible/quick/qaccessiblequickitem.cpp @@ -152,52 +152,24 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const QAccessible::State QAccessibleQuickItem::state() const { - QAccessible::State state; + QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item()); + if (!attached) + return QAccessible::State(); - if (item()->hasActiveFocus()) { - state.focusable = true; - state.focused = true; - } - - if (item()->activeFocusOnTab()) - state.focusable = true; + QAccessible::State st = attached->state(); if (!item()->window() || !item()->window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) - state.invisible = true; + st.invisible = true; - QAccessible::Role r = role(); - switch (r) { - case QAccessible::Button: { - state.focusable = true; - QVariant checkable = item()->property("checkable"); - if (!checkable.toBool()) - break; - // fall through - } - case QAccessible::CheckBox: - case QAccessible::RadioButton: { - state.focusable = true; - state.checkable = true; - state.checked = item()->property("checked").toBool(); - break; - } - case QAccessible::MenuItem: - case QAccessible::PageTab: - case QAccessible::EditableText: - case QAccessible::SpinBox: - case QAccessible::Terminal: - case QAccessible::ScrollBar: - state.focusable = true; - break; - case QAccessible::ComboBox: - state.focusable = true; - state.editable = item()->property("editable").toBool(); - break; - default: - break; - } + if (item()->activeFocusOnTab()) + st.focusable = true; + if (item()->hasActiveFocus()) + st.focused = true; + + if (role() == QAccessible::ComboBox) + st.editable = item()->property("editable").toBool(); - return state; + return st; } QAccessible::Role QAccessibleQuickItem::role() const diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 3c00a7d62d..b5cc6ea3ef 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -139,6 +139,57 @@ QT_BEGIN_NAMESPACE \endtable */ +/*! \qmlproperty bool focusable + \brief This property holds whether this item is focusable. + + By default, this property is false except for items where the role is one of + CheckBox, RadioButton, Button, MenuItem, PageTab, EditableText, SpinBox, ComboBox, + Terminal or ScrollBar. +*/ +/*! \qmlproperty bool focused + \brief This property holds whether this item currently has the active focus. + + By default, this property is false, but it will return true for items that + have \l QQuickItem::hasActiveFocus() returning true. +*/ +/*! \qmlproperty bool checkable + \brief This property holds whether this item is checkable (like a check box or some buttons). +*/ +/*! \qmlproperty bool checked + \brief This property holds whether this item is currently checked. +*/ +/*! \qmlproperty bool editable + \brief This property holds whether this item has editable text. +*/ +/*! \qmlproperty bool multiLine + \brief This property holds whether this item has multiple text lines. +*/ +/*! \qmlproperty bool readOnly + \brief This property holds whether this item while being of type \l QAccessible::EditableText + is set to read-only. +*/ +/*! \qmlproperty bool selected + \brief This property holds whether this item is selected. +*/ +/*! \qmlproperty bool selectable + \brief This property holds whether this item can be selected. +*/ +/*! \qmlproperty bool pressed + \brief This property holds whether this item is pressed (for example a button during a mouse click). +*/ +/*! \qmlproperty bool checkStateMixed + \brief This property holds whether this item is in the partially checked state. +*/ +/*! \qmlproperty bool defaultButton + \brief This property holds whether this item is the default button of a dialog. +*/ +/*! \qmlproperty bool passwordEdit + \brief This property holds whether this item is a password text edit. +*/ +/*! \qmlproperty bool selectableText + \brief This property holds whether this item contains selectable text. +*/ + QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent) : QObject(parent), m_role(QAccessible::NoRole) { diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index 298c473cf8..521151f3dd 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -55,15 +55,46 @@ QT_BEGIN_NAMESPACE +#define STATE_PROPERTY(P) \ + Q_PROPERTY(bool P READ P WRITE set_ ## P NOTIFY P ## Changed FINAL) \ + bool P() const { return m_state.P ; } \ + void set_ ## P(bool arg) \ + { \ + if (m_state.P == arg) \ + return; \ + m_state.P = arg; \ + emit P ## Changed(arg); \ + QAccessible::State changedState; \ + changedState.P = true; \ + QAccessibleStateChangeEvent ev(parent(), changedState); \ + QAccessible::updateAccessibility(&ev); \ + } \ + Q_SIGNAL void P ## Changed(bool arg); + + class Q_QUICK_PRIVATE_EXPORT QQuickAccessibleAttached : public QObject { Q_OBJECT - Q_PROPERTY(QAccessible::Role role READ role WRITE setRole NOTIFY roleChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) + Q_PROPERTY(QAccessible::Role role READ role WRITE setRole NOTIFY roleChanged FINAL) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged FINAL) public: - Q_ENUMS(QAccessible::Role QAccessible::Event QAccessible::State) + Q_ENUMS(QAccessible::Role QAccessible::Event) + STATE_PROPERTY(checkable) + STATE_PROPERTY(checked) + STATE_PROPERTY(editable) + STATE_PROPERTY(focusable) + STATE_PROPERTY(focused) + STATE_PROPERTY(multiLine) + STATE_PROPERTY(readOnly) + STATE_PROPERTY(selected) + STATE_PROPERTY(selectable) + STATE_PROPERTY(pressed) + STATE_PROPERTY(checkStateMixed) + STATE_PROPERTY(defaultButton) + STATE_PROPERTY(passwordEdit) + STATE_PROPERTY(selectableText) QQuickAccessibleAttached(QObject *parent); ~QQuickAccessibleAttached(); @@ -76,6 +107,26 @@ public: Q_EMIT roleChanged(); // There is no way to signify role changes at the moment. // QAccessible::updateAccessibility(parent(), 0, QAccessible::); + + switch (role) { + case QAccessible::CheckBox: + case QAccessible::RadioButton: + m_state.focusable = true; + m_state.checkable = true; + break; + case QAccessible::Button: + case QAccessible::MenuItem: + case QAccessible::PageTab: + case QAccessible::EditableText: + case QAccessible::SpinBox: + case QAccessible::ComboBox: + case QAccessible::Terminal: + case QAccessible::ScrollBar: + m_state.focusable = true; + break; + default: + break; + } } } QString name() const { return m_name; } @@ -102,12 +153,12 @@ public: // Factory function static QQuickAccessibleAttached *qmlAttachedProperties(QObject *obj); - // Property getter - static QObject *attachedProperties(const QObject *obj) + static QQuickAccessibleAttached *attachedProperties(const QObject *obj) { - return qmlAttachedPropertiesObject<QQuickAccessibleAttached>(obj, false); + return qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(obj, false)); } + // Property getter static QVariant property(const QObject *object, const char *propertyName) { if (QObject *attachedObject = QQuickAccessibleAttached::attachedProperties(object)) @@ -128,8 +179,8 @@ public: static QObject *findAccessible(QObject *object, QAccessible::Role role = QAccessible::NoRole) { while (object) { - QObject *att = QQuickAccessibleAttached::attachedProperties(object); - if (att && (role == QAccessible::NoRole || att->property("role").toInt() == role)) { + QQuickAccessibleAttached *att = QQuickAccessibleAttached::attachedProperties(object); + if (att && (role == QAccessible::NoRole || att->role() == role)) { break; } object = object->parent(); @@ -137,6 +188,8 @@ public: return object; } + QAccessible::State state() { return m_state; } + public Q_SLOTS: void valueChanged() { QAccessibleValueChangeEvent ev(parent(), parent()->property("value")); @@ -153,7 +206,10 @@ Q_SIGNALS: void descriptionChanged(); private: + QQuickItem *item() const { return static_cast<QQuickItem*>(parent()); } + QAccessible::Role m_role; + QAccessible::State m_state; QString m_name; QString m_description; diff --git a/tests/auto/quick/qquickaccessible/data/checkbuttons.qml b/tests/auto/quick/qquickaccessible/data/checkbuttons.qml index 22cdad1377..8769c04095 100644 --- a/tests/auto/quick/qquickaccessible/data/checkbuttons.qml +++ b/tests/auto/quick/qquickaccessible/data/checkbuttons.qml @@ -6,6 +6,7 @@ Item { // button, not checkable Rectangle { + objectName: "button1" y: 20 width: 100; height: 20 Accessible.role : Accessible.Button @@ -13,34 +14,44 @@ Item { // button, checkable, not checked Rectangle { + objectName: "button2" y: 40 width: 100; height: 20 Accessible.role : Accessible.Button + Accessible.checkable: checkable + Accessible.checked: checked property bool checkable: true property bool checked: false } // button, checkable, checked Rectangle { + objectName: "button3" y: 60 width: 100; height: 20 Accessible.role : Accessible.Button + Accessible.checkable: checkable + Accessible.checked: checked property bool checkable: true property bool checked: true } // check box, checked Rectangle { + objectName: "checkbox1" y: 80 width: 100; height: 20 Accessible.role : Accessible.CheckBox + Accessible.checked: checked property bool checked: true } // check box, not checked Rectangle { + objectName: "checkbox2" y: 100 width: 100; height: 20 Accessible.role : Accessible.CheckBox + Accessible.checked: checked property bool checked: false } } diff --git a/tests/auto/quick/qquickaccessible/qquickaccessible.pro b/tests/auto/quick/qquickaccessible/qquickaccessible.pro index 6fc6011229..99c3834147 100644 --- a/tests/auto/quick/qquickaccessible/qquickaccessible.pro +++ b/tests/auto/quick/qquickaccessible/qquickaccessible.pro @@ -1,7 +1,7 @@ CONFIG += testcase TARGET = tst_qquickaccessible -QT += qml-private network quick-private testlib gui-private +QT += qml-private network quick-private testlib gui-private core-private macx:CONFIG -= app_bundle SOURCES += tst_qquickaccessible.cpp diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index ec1b1de7c0..06dc348f61 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -44,6 +44,10 @@ #include "QtTest/qtestaccessible.h" #include <QtGui/qaccessible.h> +#include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformnativeinterface.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformaccessibility.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> @@ -96,6 +100,12 @@ public: tst_QQuickAccessible(); virtual ~tst_QQuickAccessible(); +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + private slots: void commonTests_data(); void commonTests(); @@ -116,6 +126,37 @@ tst_QQuickAccessible::~tst_QQuickAccessible() } +void tst_QQuickAccessible::initTestCase() +{ + QQmlDataTest::initTestCase(); + QTestAccessibility::initialize(); + QPlatformIntegration *pfIntegration = QGuiApplicationPrivate::platformIntegration(); + pfIntegration->accessibility()->setActive(true); +} + +void tst_QQuickAccessible::cleanupTestCase() +{ + QTestAccessibility::cleanup(); +} + +void tst_QQuickAccessible::init() +{ + QTestAccessibility::clearEvents(); +} + +void tst_QQuickAccessible::cleanup() +{ + const EventList list = QTestAccessibility::events(); + if (!list.isEmpty()) { + qWarning("%d accessibility event(s) were not handled in testfunction '%s':", list.count(), + QString(QTest::currentTestFunction()).toLatin1().constData()); + for (int i = 0; i < list.count(); ++i) + qWarning(" %d: Object: %p Event: '%s' Child: %d", i + 1, list.at(i)->object(), + qAccessibleEventString(list.at(i)->type()), list.at(i)->child()); + } + QTestAccessibility::clearEvents(); +} + void tst_QQuickAccessible::commonTests_data() { QTest::addColumn<QString>("accessibleRoleFileName"); @@ -141,6 +182,7 @@ void tst_QQuickAccessible::commonTests() QVERIFY(iface); delete view; + QTestAccessibility::clearEvents(); } void tst_QQuickAccessible::quickAttachedProperties() @@ -215,6 +257,7 @@ void tst_QQuickAccessible::quickAttachedProperties() } delete object; } + QTestAccessibility::clearEvents(); } @@ -265,6 +308,7 @@ void tst_QQuickAccessible::basicPropertiesTest() QCOMPARE(text2->indexOfChild(item), -1); delete window; + QTestAccessibility::clearEvents(); } QAccessibleInterface *topLevelChildAt(QAccessibleInterface *iface, int x, int y) @@ -330,6 +374,7 @@ void tst_QQuickAccessible::hitTest() QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect201")); delete window; + QTestAccessibility::clearEvents(); } void tst_QQuickAccessible::checkableTest() @@ -337,6 +382,17 @@ void tst_QQuickAccessible::checkableTest() QQuickView *window = new QQuickView(); window->setSource(testFileUrl("checkbuttons.qml")); window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + + QQuickItem *contentItem = window->contentItem(); + QVERIFY(contentItem); + QQuickItem *rootItem = contentItem->childItems().first(); + QVERIFY(rootItem); + + // the window becomes active + QAccessible::State activatedChange; + activatedChange.active = true; QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); QVERIFY(iface); @@ -347,9 +403,28 @@ void tst_QQuickAccessible::checkableTest() QVERIFY(!(button1->state().checked)); QVERIFY(!(button1->state().checkable)); + QVERIFY(button1->state().focusable); + QVERIFY(!button1->state().focused); + + QTestAccessibility::clearEvents(); + + // set properties + QQuickItem *button1item = qobject_cast<QQuickItem*>(rootItem->childItems().at(0)); + QVERIFY(button1item); + QCOMPARE(button1item->objectName(), QLatin1String("button1")); + button1item->forceActiveFocus(); + QVERIFY(button1->state().focusable); + QVERIFY(button1->state().focused); + + QAccessibleEvent focusEvent(button1item, QAccessible::Focus); + QVERIFY_EVENT(&focusEvent); + QAccessibleInterface *button2 = root->child(1); QVERIFY(!(button2->state().checked)); QVERIFY(button2->state().checkable); + QQuickItem *button2item = qobject_cast<QQuickItem*>(rootItem->childItems().at(1)); + QVERIFY(button2item); + QCOMPARE(button2item->objectName(), QLatin1String("button2")); QAccessibleInterface *button3 = root->child(2); QVERIFY(button3->state().checked); @@ -357,12 +432,28 @@ void tst_QQuickAccessible::checkableTest() QAccessibleInterface *checkBox1 = root->child(3); QCOMPARE(checkBox1->role(), QAccessible::CheckBox); - QVERIFY((checkBox1->state().checked)); + QVERIFY(checkBox1->state().checked); QVERIFY(checkBox1->state().checkable); + QQuickItem *checkbox1item = qobject_cast<QQuickItem*>(rootItem->childItems().at(3)); + QVERIFY(checkbox1item); + QCOMPARE(checkbox1item->objectName(), QLatin1String("checkbox1")); + + checkbox1item->setProperty("checked", false); + QVERIFY(!checkBox1->state().checked); + QAccessible::State checkState; + checkState.checked = true; + QAccessibleStateChangeEvent checkChanged(checkbox1item, checkState); + QVERIFY_EVENT(&checkChanged); + + checkbox1item->setProperty("checked", true); + QVERIFY(checkBox1->state().checked); + QVERIFY_EVENT(&checkChanged); QAccessibleInterface *checkBox2 = root->child(4); QVERIFY(!(checkBox2->state().checked)); QVERIFY(checkBox2->state().checkable); + + QTestAccessibility::clearEvents(); } QTEST_MAIN(tst_QQuickAccessible) |