Skip to content

Commit

Permalink
Improve accessibility action support for Qt Quick
Browse files Browse the repository at this point in the history
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 <frederik.gladhorn@nokia.com>
  • Loading branch information
Morten Johan Sorvig authored and Qt by Nokia committed Mar 7, 2012
1 parent 3f9b58c commit 70966df
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 22 deletions.
62 changes: 62 additions & 0 deletions 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
}
}
67 changes: 67 additions & 0 deletions 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
}
}
16 changes: 14 additions & 2 deletions examples/quick/accessibility/accessibility.qml
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 }
}
}
}
5 changes: 4 additions & 1 deletion examples/quick/accessibility/content/Button.qml
Expand Up @@ -49,6 +49,9 @@ Rectangle {
Accessible.name: text
Accessible.description: "This button does " + text
Accessible.role: Accessible.Button
function accessiblePressAction() {
button.clicked()
}

signal clicked

Expand All @@ -74,7 +77,7 @@ Rectangle {
id: mouseArea
anchors.fill: parent
onClicked: {
checked = !checked;
parent.clicked()
}
}
}
1 change: 1 addition & 0 deletions src/plugins/accessible/quick/main.cpp
Expand Up @@ -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);
Expand Down
87 changes: 81 additions & 6 deletions src/plugins/accessible/shared/qqmlaccessible.cpp
Expand Up @@ -59,6 +59,13 @@ QQmlAccessible::QQmlAccessible(QObject *object)
{
}

void *QQmlAccessible::interface_cast(QAccessible::InterfaceType t)
{
if (t == QAccessible::ActionInterface)
return static_cast<QAccessibleActionInterface*>(this);
return QAccessibleObject::interface_cast(t);
}

QQmlAccessible::~QQmlAccessible()
{
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/plugins/accessible/shared/qqmlaccessible.h
Expand Up @@ -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;
Expand Down
22 changes: 9 additions & 13 deletions src/quick/items/qquickaccessibleattached.cpp
Expand Up @@ -104,6 +104,7 @@ QT_BEGIN_NAMESPACE
}
Accessible.name: label.text
Accessible.role: Accessible.Button
funtion accessiblePressAction { ... }
}
\endqml
Expand All @@ -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)
Expand Down

0 comments on commit 70966df

Please sign in to comment.