aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@digia.com>2014-01-10 16:36:05 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-27 16:00:13 +0100
commitf0595284cbe85b5716daaf307154c1ed7e660a26 (patch)
treea3a0c1347130b1b6c8418780d16d2d7f308ebf69
parenta1ee538395198f998a73f3ef7545392aeb680900 (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>
-rw-r--r--src/plugins/accessible/quick/qaccessiblequickitem.cpp54
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp51
-rw-r--r--src/quick/items/qquickaccessibleattached_p.h74
-rw-r--r--tests/auto/quick/qquickaccessible/data/checkbuttons.qml11
-rw-r--r--tests/auto/quick/qquickaccessible/qquickaccessible.pro2
-rw-r--r--tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp93
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)