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 QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickaccessibleattached_p.h"
41
42#if QT_CONFIG(accessibility)
43
44#include <QtQml/qqmlinfo.h>
45
46#include "private/qquickitem_p.h"
47
48QT_BEGIN_NAMESPACE
49
50/*!
51 \qmltype Accessible
52 \instantiates QQuickAccessibleAttached
53 \brief Enables accessibility of QML items.
54
55 \inqmlmodule QtQuick
56 \ingroup qtquick-visual-utility
57 \ingroup accessibility
58
59 This class is part of the \l {Accessibility for Qt Quick Applications}.
60
61 Items the user interacts with or that give information to the user
62 need to expose their information to the accessibility framework.
63 Then assistive tools can make use of that information to enable
64 users to interact with the application in various ways.
65 This enables Qt Quick applications to be used with screen-readers for example.
66
67 The most important properties are \l name, \l description and \l role.
68
69 Example implementation of a simple button:
70 \qml
71 Rectangle {
72 id: myButton
73 Text {
74 id: label
75 text: "next"
76 }
77 Accessible.role: Accessible.Button
78 Accessible.name: label.text
79 Accessible.description: "shows the next page"
80 Accessible.onPressAction: {
81 // do a button click
82 }
83 }
84 \endqml
85 The \l role is set to \c Button to indicate the type of control.
86 \l {Accessible::}{name} is the most important information and bound to the text on the button.
87 The name is a short and consise description of the control and should reflect the visual label.
88 In this case it is not clear what the button does with the name only, so \l description contains
89 an explanation.
90 There is also a signal handler \l {Accessible::pressAction}{Accessible.pressAction} which can be invoked by assistive tools to trigger
91 the button. This signal handler needs to have the same effect as tapping or clicking the button would have.
92
93 \sa Accessibility
94*/
95
96/*!
97 \qmlproperty string QtQuick::Accessible::name
98
99 This property sets an accessible name.
100 For a button for example, this should have a binding to its text.
101 In general this property should be set to a simple and concise
102 but human readable name. Do not include the type of control
103 you want to represent but just the name.
104*/
105
106/*!
107 \qmlproperty string QtQuick::Accessible::description
108
109 This property sets an accessible description.
110 Similar to the name it describes the item. The description
111 can be a little more verbose and tell what the item does,
112 for example the functionallity of the button it describes.
113*/
114
115/*!
116 \qmlproperty enumeration QtQuick::Accessible::role
117
118 This flags sets the semantic type of the widget.
119 A button for example would have "Button" as type.
120 The value must be one of \l QAccessible::Role.
121
122 Some roles have special semantics.
123 In order to implement check boxes for example a "checked" property is expected.
124
125 \table
126 \header
127 \li \b {Role}
128 \li \b {Properties and signals}
129 \li \b {Explanation}
130 \row
131 \li All interactive elements
132 \li \l focusable and \l focused
133 \li All elements that the user can interact with should have focusable set to \c true and
134 set \c focus to \c true when they have the focus. This is important even for applications
135 that run on touch-only devices since screen readers often implement a virtual focus that
136 can be moved from item to item.
137 \row
138 \li Button, CheckBox, RadioButton
139 \li \l {Accessible::pressAction}{Accessible.pressAction}
140 \li A button should have a signal handler with the name \c onPressAction.
141 This signal may be emitted by an assistive tool such as a screen-reader.
142 The implementation needs to behave the same as a mouse click or tap on the button.
143 \row
144 \li CheckBox, RadioButton
145 \li \l checkable, \l checked, \l {Accessible::toggleAction}{Accessible.toggleAction}
146
147 \li The check state of the check box. Updated on Press, Check and Uncheck actions.
148 \row
149 \li Slider, SpinBox, Dial, ScrollBar
150 \li \c value, \c minimumValue, \c maximumValue, \c stepSize
151 \li These properties reflect the state and possible values for the elements.
152 \row
153 \li Slider, SpinBox, Dial, ScrollBar
154 \li \l {Accessible::increaseAction}{Accessible.increaseAction}, \l {Accessible::decreaseAction}{Accessible.decreaseAction}
155 \li Actions to increase and decrease the value of the element.
156 \endtable
157*/
158
159/*! \qmlproperty bool QtQuick::Accessible::focusable
160 \brief This property holds whether this item is focusable.
161
162 By default, this property is \c false except for items where the role is one of
163 \c CheckBox, \c RadioButton, \c Button, \c MenuItem, \c PageTab, \c EditableText, \c SpinBox, \c ComboBox,
164 \c Terminal or \c ScrollBar.
165 \sa focused
166*/
167/*! \qmlproperty bool QtQuick::Accessible::focused
168 \brief This property holds whether this item currently has the active focus.
169
170 By default, this property is \c false, but it will return \c true for items that
171 have \l QQuickItem::hasActiveFocus() returning \c true.
172 \sa focusable
173*/
174/*! \qmlproperty bool QtQuick::Accessible::checkable
175 \brief This property holds whether this item is checkable (like a check box or some buttons).
176
177 By default this property is \c false.
178 \sa checked
179*/
180/*! \qmlproperty bool QtQuick::Accessible::checked
181 \brief This property holds whether this item is currently checked.
182
183 By default this property is \c false.
184 \sa checkable
185*/
186/*! \qmlproperty bool QtQuick::Accessible::editable
187 \brief This property holds whether this item has editable text.
188
189 By default this property is \c false.
190*/
191/*! \qmlproperty bool QtQuick::Accessible::searchEdit
192 \brief This property holds whether this item is input for a search query.
193 This property will only affect editable text.
194
195 By default this property is \c false.
196*/
197/*! \qmlproperty bool QtQuick::Accessible::ignored
198 \brief This property holds whether this item should be ignored by the accessibility framework.
199
200 Sometimes an item is part of a group of items that should be treated as one. For example two labels might be
201 visually placed next to each other, but separate items. For accessibility purposes they should be treated as one
202 and thus they are represented by a third invisible item with the right geometry.
203
204 For example a speed display adds "m/s" as a smaller label:
205 \qml
206 Row {
207 Label {
208 id: speedLabel
209 text: "Speed: 5"
210 Accessible.ignored: true
211 }
212 Label {
213 text: qsTr("m/s")
214 Accessible.ignored: true
215 }
216 Accessible.role: Accessible.StaticText
217 Accessible.name: speedLabel.text + " meters per second"
218 }
219 \endqml
220
221 \since 5.4
222 By default this property is \c false.
223*/
224/*! \qmlproperty bool QtQuick::Accessible::multiLine
225 \brief This property holds whether this item has multiple text lines.
226
227 By default this property is \c false.
228*/
229/*! \qmlproperty bool QtQuick::Accessible::readOnly
230 \brief This property indicates that a text field is read only.
231
232 It is relevant when the role is \l QAccessible::EditableText and set to be read-only.
233 By default this property is \c false.
234*/
235/*! \qmlproperty bool QtQuick::Accessible::selected
236 \brief This property holds whether this item is selected.
237
238 By default this property is \c false.
239 \sa selectable
240*/
241/*! \qmlproperty bool QtQuick::Accessible::selectable
242 \brief This property holds whether this item can be selected.
243
244 By default this property is \c false.
245 \sa selected
246*/
247/*! \qmlproperty bool QtQuick::Accessible::pressed
248 \brief This property holds whether this item is pressed (for example a button during a mouse click).
249
250 By default this property is \c false.
251*/
252/*! \qmlproperty bool QtQuick::Accessible::checkStateMixed
253 \brief This property holds whether this item is in the partially checked state.
254
255 By default this property is \c false.
256 \sa checked, checkable
257*/
258/*! \qmlproperty bool QtQuick::Accessible::defaultButton
259 \brief This property holds whether this item is the default button of a dialog.
260
261 By default this property is \c false.
262*/
263/*! \qmlproperty bool QtQuick::Accessible::passwordEdit
264 \brief This property holds whether this item is a password text edit.
265
266 By default this property is \c false.
267*/
268/*! \qmlproperty bool QtQuick::Accessible::selectableText
269 \brief This property holds whether this item contains selectable text.
270
271 By default this property is \c false.
272*/
273
274/*!
275 \qmlsignal QtQuick::Accessible::pressAction()
276
277 This signal is emitted when a press action is received from an assistive tool such as a screen-reader.
278*/
279/*!
280 \qmlsignal QtQuick::Accessible::toggleAction()
281
282 This signal is emitted when a toggle action is received from an assistive tool such as a screen-reader.
283*/
284/*!
285 \qmlsignal QtQuick::Accessible::increaseAction()
286
287 This signal is emitted when a increase action is received from an assistive tool such as a screen-reader.
288*/
289/*!
290 \qmlsignal QtQuick::Accessible::decreaseAction()
291
292 This signal is emitted when a decrease action is received from an assistive tool such as a screen-reader.
293*/
294/*!
295 \qmlsignal QtQuick::Accessible::scrollUpAction()
296
297 This signal is emitted when a scroll up action is received from an assistive tool such as a screen-reader.
298*/
299/*!
300 \qmlsignal QtQuick::Accessible::scrollDownAction()
301
302 This signal is emitted when a scroll down action is received from an assistive tool such as a screen-reader.
303*/
304/*!
305 \qmlsignal QtQuick::Accessible::scrollLeftAction()
306
307 This signal is emitted when a scroll left action is received from an assistive tool such as a screen-reader.
308*/
309/*!
310 \qmlsignal QtQuick::Accessible::scrollRightAction()
311
312 This signal is emitted when a scroll right action is received from an assistive tool such as a screen-reader.
313*/
314/*!
315 \qmlsignal QtQuick::Accessible::previousPageAction()
316
317 This signal is emitted when a previous page action is received from an assistive tool such as a screen-reader.
318*/
319/*!
320 \qmlsignal QtQuick::Accessible::nextPageAction()
321
322 This signal is emitted when a next page action is received from an assistive tool such as a screen-reader.
323*/
324
325QMetaMethod QQuickAccessibleAttached::sigPress;
326QMetaMethod QQuickAccessibleAttached::sigToggle;
327QMetaMethod QQuickAccessibleAttached::sigIncrease;
328QMetaMethod QQuickAccessibleAttached::sigDecrease;
329QMetaMethod QQuickAccessibleAttached::sigScrollUp;
330QMetaMethod QQuickAccessibleAttached::sigScrollDown;
331QMetaMethod QQuickAccessibleAttached::sigScrollLeft;
332QMetaMethod QQuickAccessibleAttached::sigScrollRight;
333QMetaMethod QQuickAccessibleAttached::sigPreviousPage;
334QMetaMethod QQuickAccessibleAttached::sigNextPage;
335
336QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent)
337 : QObject(parent), m_role(QAccessible::NoRole)
338{
339 Q_ASSERT(parent);
340 if (!item()) {
341 qmlWarning(me: parent) << "Accessible must be attached to an Item";
342 return;
343 }
344
345 // Enable accessibility for items with accessible content. This also
346 // enables accessibility for the ancestors of souch items.
347 item()->d_func()->setAccessible();
348 QAccessibleEvent ev(item(), QAccessible::ObjectCreated);
349 QAccessible::updateAccessibility(event: &ev);
350
351 if (!parent->property(name: "value").isNull()) {
352 connect(sender: parent, SIGNAL(valueChanged()), receiver: this, SLOT(valueChanged()));
353 }
354 if (!parent->property(name: "cursorPosition").isNull()) {
355 connect(sender: parent, SIGNAL(cursorPositionChanged()), receiver: this, SLOT(cursorPositionChanged()));
356 }
357
358 if (!sigPress.isValid()) {
359 sigPress = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::pressAction);
360 sigToggle = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::toggleAction);
361 sigIncrease = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::increaseAction);
362 sigDecrease = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::decreaseAction);
363 sigScrollUp = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::scrollUpAction);
364 sigScrollDown = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::scrollDownAction);
365 sigScrollLeft = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::scrollLeftAction);
366 sigScrollRight = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::scrollRightAction);
367 sigPreviousPage = QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::previousPageAction);
368 sigNextPage= QMetaMethod::fromSignal(signal: &QQuickAccessibleAttached::nextPageAction);
369 }
370}
371
372QQuickAccessibleAttached::~QQuickAccessibleAttached()
373{
374}
375
376void QQuickAccessibleAttached::setRole(QAccessible::Role role)
377{
378 if (role != m_role) {
379 m_role = role;
380 Q_EMIT roleChanged();
381 // There is no way to signify role changes at the moment.
382 // QAccessible::updateAccessibility(parent(), 0, QAccessible::);
383
384 switch (role) {
385 case QAccessible::CheckBox:
386 case QAccessible::RadioButton:
387 if (!m_stateExplicitlySet.focusable)
388 m_state.focusable = true;
389 if (!m_stateExplicitlySet.checkable)
390 m_state.checkable = true;
391 break;
392 case QAccessible::Button:
393 case QAccessible::MenuItem:
394 case QAccessible::PageTab:
395 case QAccessible::SpinBox:
396 case QAccessible::ComboBox:
397 case QAccessible::Terminal:
398 case QAccessible::ScrollBar:
399 if (!m_stateExplicitlySet.focusable)
400 m_state.focusable = true;
401 break;
402 case QAccessible::EditableText:
403 if (!m_stateExplicitlySet.editable)
404 m_state.editable = true;
405 if (!m_stateExplicitlySet.focusable)
406 m_state.focusable = true;
407 break;
408 case QAccessible::StaticText:
409 if (!m_stateExplicitlySet.readOnly)
410 m_state.readOnly = true;
411 if (!m_stateExplicitlySet.focusable)
412 m_state.focusable = true;
413 break;
414 default:
415 break;
416 }
417 }
418}
419
420bool QQuickAccessibleAttached::wasNameExplicitlySet() const
421{
422 return m_nameExplicitlySet;
423}
424
425// Allows types to attach an accessible name to an item as a convenience,
426// so long as the user hasn't done so themselves.
427void QQuickAccessibleAttached::setNameImplicitly(const QString &name)
428{
429 setName(name);
430 m_nameExplicitlySet = false;
431}
432
433QQuickAccessibleAttached *QQuickAccessibleAttached::qmlAttachedProperties(QObject *obj)
434{
435 return new QQuickAccessibleAttached(obj);
436}
437
438bool QQuickAccessibleAttached::ignored() const
439{
440 return item() ? !item()->d_func()->isAccessible : false;
441}
442
443void QQuickAccessibleAttached::setIgnored(bool ignored)
444{
445 if (this->ignored() != ignored && item()) {
446 item()->d_func()->isAccessible = !ignored;
447 emit ignoredChanged();
448 }
449}
450
451bool QQuickAccessibleAttached::doAction(const QString &actionName)
452{
453 QMetaMethod *sig = nullptr;
454 if (actionName == QAccessibleActionInterface::pressAction())
455 sig = &sigPress;
456 else if (actionName == QAccessibleActionInterface::toggleAction())
457 sig = &sigToggle;
458 else if (actionName == QAccessibleActionInterface::increaseAction())
459 sig = &sigIncrease;
460 else if (actionName == QAccessibleActionInterface::decreaseAction())
461 sig = &sigDecrease;
462 else if (actionName == QAccessibleActionInterface::scrollUpAction())
463 sig = &sigScrollUp;
464 else if (actionName == QAccessibleActionInterface::scrollDownAction())
465 sig = &sigScrollDown;
466 else if (actionName == QAccessibleActionInterface::scrollLeftAction())
467 sig = &sigScrollLeft;
468 else if (actionName == QAccessibleActionInterface::scrollRightAction())
469 sig = &sigScrollRight;
470 else if (actionName == QAccessibleActionInterface::previousPageAction())
471 sig = &sigPreviousPage;
472 else if (actionName == QAccessibleActionInterface::nextPageAction())
473 sig = &sigNextPage;
474 if (sig && isSignalConnected(signal: *sig))
475 return sig->invoke(object: this);
476 return false;
477}
478
479void QQuickAccessibleAttached::availableActions(QStringList *actions) const
480{
481 if (isSignalConnected(signal: sigPress))
482 actions->append(t: QAccessibleActionInterface::pressAction());
483 if (isSignalConnected(signal: sigToggle))
484 actions->append(t: QAccessibleActionInterface::toggleAction());
485 if (isSignalConnected(signal: sigIncrease))
486 actions->append(t: QAccessibleActionInterface::increaseAction());
487 if (isSignalConnected(signal: sigDecrease))
488 actions->append(t: QAccessibleActionInterface::decreaseAction());
489 if (isSignalConnected(signal: sigScrollUp))
490 actions->append(t: QAccessibleActionInterface::scrollUpAction());
491 if (isSignalConnected(signal: sigScrollDown))
492 actions->append(t: QAccessibleActionInterface::scrollDownAction());
493 if (isSignalConnected(signal: sigScrollLeft))
494 actions->append(t: QAccessibleActionInterface::scrollLeftAction());
495 if (isSignalConnected(signal: sigScrollRight))
496 actions->append(t: QAccessibleActionInterface::scrollRightAction());
497 if (isSignalConnected(signal: sigPreviousPage))
498 actions->append(t: QAccessibleActionInterface::previousPageAction());
499 if (isSignalConnected(signal: sigNextPage))
500 actions->append(t: QAccessibleActionInterface::nextPageAction());
501}
502
503QT_END_NAMESPACE
504
505#include "moc_qquickaccessibleattached_p.cpp"
506
507#endif
508

source code of qtdeclarative/src/quick/items/qquickaccessibleattached.cpp