From 70966df1be02dd94ecf9a122ff9e4976245aeb92 Mon Sep 17 00:00:00 2001 From: Morten Johan Sorvig Date: Tue, 10 Jan 2012 12:12:51 +0100 Subject: Improve accessibility action support for Qt Quick Add interface_cast for the action interface. Implement actions for the following roles: Button : Press CheckBox, RadioButton : Press, Check, Uncheck Slider, Spinbox, Dial, ScrollBar : Increment, Decrement Change-Id: Ic8e0d17c709ba51655f3f4b699092baf603b6f18 Reviewed-by: Frederik Gladhorn --- .../declarative/accessibility/widgets/Checkbox.qml | 62 +++++++++++++++ .../declarative/accessibility/widgets/Slider.qml | 67 +++++++++++++++++ examples/quick/accessibility/accessibility.qml | 16 +++- examples/quick/accessibility/content/Button.qml | 5 +- src/plugins/accessible/quick/main.cpp | 1 + src/plugins/accessible/shared/qqmlaccessible.cpp | 87 ++++++++++++++++++++-- src/plugins/accessible/shared/qqmlaccessible.h | 1 + src/quick/items/qquickaccessibleattached.cpp | 22 +++--- 8 files changed, 239 insertions(+), 22 deletions(-) create mode 100644 examples/declarative/accessibility/widgets/Checkbox.qml create mode 100644 examples/declarative/accessibility/widgets/Slider.qml diff --git a/examples/declarative/accessibility/widgets/Checkbox.qml b/examples/declarative/accessibility/widgets/Checkbox.qml new file mode 100644 index 0000000000..17eb733197 --- /dev/null +++ b/examples/declarative/accessibility/widgets/Checkbox.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.0 + + +Rectangle { + id: checkbox + + Accessible.role: Accessible.CheckBox + + property bool checked // required variable + + width: 30 + height: 30 + + + Text { + id: checkboxText + text: parent.checked ? "on" : "off" + anchors.centerIn: parent + } +} diff --git a/examples/declarative/accessibility/widgets/Slider.qml b/examples/declarative/accessibility/widgets/Slider.qml new file mode 100644 index 0000000000..32aa6a3a41 --- /dev/null +++ b/examples/declarative/accessibility/widgets/Slider.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.0 + +// Minimal slider implementation +Rectangle { + id: slider + + property alias text : buttonText.text + Accessible.role: Accessible.Slider + + property int value // required + property int minimumValue : 0 // optional (default INT_MIN) + property int maximumValue : 20 // optional (default INT_MAX) + property int stepSize : 1 // optional (default 1) + + width: 30 + height: 30 + + + Text { + id: buttonText + text: parent.value + anchors.centerIn: parent + font.pixelSize: parent.height * .5 + } +} diff --git a/examples/quick/accessibility/accessibility.qml b/examples/quick/accessibility/accessibility.qml index 2804d2abc8..e987561bb1 100644 --- a/examples/quick/accessibility/accessibility.qml +++ b/examples/quick/accessibility/accessibility.qml @@ -59,12 +59,13 @@ Rectangle { id: column spacing: 6 anchors.fill: parent + anchors.margins: 10 width: parent.width Row { spacing: 6 width: column.width - Button { width: 100; height: column.h + 20; text: "Send" } - Button { width: 100; height: column.h + 20; text: "Discard" } + Button { width: 100; height: column.h + 20; text: "Send"; onClicked : { status.text = "Send" } } + Button { width: 100; height: column.h + 20; text: "Discard"; onClicked : { status.text = "Discard" } } } Row { @@ -106,5 +107,16 @@ Rectangle { wrapMode: TextEdit.WordWrap } } + Text { + id : status + width: column.width + } + + Row { + spacing: 6 + width: column.width + Checkbox { checked: false } + Slider { value: 10 } + } } } diff --git a/examples/quick/accessibility/content/Button.qml b/examples/quick/accessibility/content/Button.qml index 2c203abe11..3d5086f741 100644 --- a/examples/quick/accessibility/content/Button.qml +++ b/examples/quick/accessibility/content/Button.qml @@ -49,6 +49,9 @@ Rectangle { Accessible.name: text Accessible.description: "This button does " + text Accessible.role: Accessible.Button + function accessiblePressAction() { + button.clicked() + } signal clicked @@ -74,7 +77,7 @@ Rectangle { id: mouseArea anchors.fill: parent onClicked: { - checked = !checked; + parent.clicked() } } } diff --git a/src/plugins/accessible/quick/main.cpp b/src/plugins/accessible/quick/main.cpp index 6ff3d5db9e..2c75e594c2 100644 --- a/src/plugins/accessible/quick/main.cpp +++ b/src/plugins/accessible/quick/main.cpp @@ -99,6 +99,7 @@ QAccessibleInterface *AccessibleQuickFactory::create(const QString &classname, Q case QAccessible::Slider: case QAccessible::SpinBox: case QAccessible::Dial: + case QAccessible::ScrollBar: return new QAccessibleQuickItemValueInterface(item); default: return new QAccessibleQuickItem(item); diff --git a/src/plugins/accessible/shared/qqmlaccessible.cpp b/src/plugins/accessible/shared/qqmlaccessible.cpp index 1818ebebfe..70c6b90efe 100644 --- a/src/plugins/accessible/shared/qqmlaccessible.cpp +++ b/src/plugins/accessible/shared/qqmlaccessible.cpp @@ -59,6 +59,13 @@ QQmlAccessible::QQmlAccessible(QObject *object) { } +void *QQmlAccessible::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::ActionInterface) + return static_cast(this); + return QAccessibleObject::interface_cast(t); +} + QQmlAccessible::~QQmlAccessible() { } @@ -131,7 +138,15 @@ QStringList QQmlAccessible::actionNames() const break; case QAccessible::RadioButton: case QAccessible::CheckBox: - actions << QAccessibleActionInterface::checkAction(); + actions << QAccessibleActionInterface::checkAction() + << QAccessibleActionInterface::uncheckAction() + << QAccessibleActionInterface::pressAction(); + break; + case QAccessible::Slider: + case QAccessible::SpinBox: + case QAccessible::ScrollBar: + actions << QAccessibleActionInterface::increaseAction() + << QAccessibleActionInterface::decreaseAction(); break; default: break; @@ -141,12 +156,72 @@ QStringList QQmlAccessible::actionNames() const void QQmlAccessible::doAction(const QString &actionName) { - if (role() == QAccessible::PushButton && actionName == QAccessibleActionInterface::pressAction()) { - QMetaObject::invokeMethod(object(), "accessibleAction"); + // Look for and call the accessible[actionName]Action() function on the item. + // This allows for overriding the default action handling. + const QByteArray functionName = "accessible" + actionName.toLatin1() + "Action()"; + if (object()->metaObject()->indexOfMethod(functionName) != -1) { + QMetaObject::invokeMethod(object(), functionName, Q_ARG(QString, actionName)); + return; } - if ((role() == QAccessible::CheckBox || role() == QAccessible::RadioButton) && actionName == QAccessibleActionInterface::checkAction()) { - bool checked = object()->property("checked").toBool(); - object()->setProperty("checked", QVariant(!checked)); + + // Role-specific default action handling follows. Items are excepted to provide + // properties according to role conventions. These will then be read and/or updated + // by the accessibility system. + // Checkable roles : checked + // Value-based roles : (via the value interface: value, minimumValue, maximumValue), stepSize + switch (role()) { + case QAccessible::RadioButton: + case QAccessible::CheckBox: { + QVariant checked = object()->property("checked"); + if (checked.isValid()) { + if (actionName == QAccessibleActionInterface::pressAction()) { + object()->setProperty("checked", QVariant(!checked.toBool())); + } else if (actionName == QAccessibleActionInterface::checkAction()) { + object()->setProperty("checked", QVariant(true)); + } else if (actionName == QAccessibleActionInterface::uncheckAction()) { + object()->setProperty("checked", QVariant(false)); + } + } + break; + } + case QAccessible::Slider: + case QAccessible::SpinBox: + case QAccessible::Dial: + case QAccessible::ScrollBar: { + if (actionName != QAccessibleActionInterface::increaseAction() && + actionName != QAccessibleActionInterface::decreaseAction()) + break; + + // Update the value using QAccessibleValueInterface, respecting + // the minimum and maximum value (if set). Also check for and + // use the "stepSize" property on the item + if (QAccessibleValueInterface *valueIface = valueInterface()) { + QVariant valueV = valueIface->currentValue(); + qreal newValue = valueV.toInt(); + + QVariant stepSizeV = object()->property("stepSize"); + qreal stepSize = stepSizeV.isValid() ? stepSizeV.toReal() : qreal(1.0); + if (actionName == QAccessibleActionInterface::increaseAction()) { + newValue += stepSize; + } else { + newValue -= stepSize; + } + + QVariant minimumValueV = valueIface->minimumValue(); + if (minimumValueV.isValid()) { + newValue = qMax(newValue, minimumValueV.toReal()); + } + QVariant maximumValueV = valueIface->maximumValue(); + if (maximumValueV.isValid()) { + newValue = qMin(newValue, maximumValueV.toReal()); + } + + valueIface->setCurrentValue(QVariant(newValue)); + } + break; + } + default: + break; } } diff --git a/src/plugins/accessible/shared/qqmlaccessible.h b/src/plugins/accessible/shared/qqmlaccessible.h index 570a3c8c40..ec26aa2306 100644 --- a/src/plugins/accessible/shared/qqmlaccessible.h +++ b/src/plugins/accessible/shared/qqmlaccessible.h @@ -74,6 +74,7 @@ class QQmlAccessible: public QAccessibleObject, public QAccessibleActionInterfac { public: ~QQmlAccessible(); + void *interface_cast(QAccessible::InterfaceType t); virtual QRect viewRect() const = 0; QAccessibleInterface *childAt(int, int) const; diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 177454eedc..1e07d96690 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -104,6 +104,7 @@ QT_BEGIN_NAMESPACE } Accessible.name: label.text Accessible.role: Accessible.Button + funtion accessiblePressAction { ... } } \endqml @@ -117,24 +118,19 @@ QT_BEGIN_NAMESPACE \o \row - \o CheckBox - \o checked - \o The check state of the check box. + \o Button + \o function accessiblePressAction + \o Called when the button receives a press action. The implementation should visually simulate a button click and perform the button action. \row - \o RadioButton + \o CheckBox, Radiobutton \o checked - \o The selected state of the radio button. - \row - \o Button - \o checkable - \o Whether the button is checkable. + \o The check state of the check box. Updated on Press, Check and Uncheck actions. \row - \o Button - \o checked - \o Whether the button is checked (only if checkable is true). + \o Slider, SpinBox, Dial, ScrollBar + \o value, minimumValue, maximumValue, stepSize + \o value will be updated on increase and decrase actions, in accordance with the other properties \endtable - */ QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent) -- cgit v1.2.3