diff options
Diffstat (limited to 'src/quick/items/qquickaccessibleattached.cpp')
-rw-r--r-- | src/quick/items/qquickaccessibleattached.cpp | 233 |
1 files changed, 155 insertions, 78 deletions
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index c150e4efa2..4a5dfa4111 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -1,46 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qquickaccessibleattached_p.h" #if QT_CONFIG(accessibility) +#include <QtQml/qqmlinfo.h> + #include "private/qquickitem_p.h" QT_BEGIN_NAMESPACE @@ -107,7 +73,7 @@ QT_BEGIN_NAMESPACE This property sets an accessible description. Similar to the name it describes the item. The description can be a little more verbose and tell what the item does, - for example the functionallity of the button it describes. + for example the functionality of the button it describes. */ /*! @@ -154,6 +120,15 @@ QT_BEGIN_NAMESPACE \endtable */ +/*! + \qmlproperty string QtQuick::Accessible::id + + This property sets an identifier for the object. + It can be used to provide stable identifiers to UI tests. + By default, the identifier is set to the ID of the QML object. + If the ID is not set the default of \l QAccessible::Identifier is used. +*/ + /*! \qmlproperty bool QtQuick::Accessible::focusable \brief This property holds whether this item is focusable. @@ -273,71 +248,51 @@ QT_BEGIN_NAMESPACE \qmlsignal QtQuick::Accessible::pressAction() This signal is emitted when a press action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onPressAction. */ /*! \qmlsignal QtQuick::Accessible::toggleAction() This signal is emitted when a toggle action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onToggleAction. */ /*! \qmlsignal QtQuick::Accessible::increaseAction() This signal is emitted when a increase action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onIncreaseAction. */ /*! \qmlsignal QtQuick::Accessible::decreaseAction() This signal is emitted when a decrease action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onDecreaseAction. */ /*! \qmlsignal QtQuick::Accessible::scrollUpAction() This signal is emitted when a scroll up action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onScrollUpAction. */ /*! \qmlsignal QtQuick::Accessible::scrollDownAction() This signal is emitted when a scroll down action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onScrollDownAction. */ /*! \qmlsignal QtQuick::Accessible::scrollLeftAction() This signal is emitted when a scroll left action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onScrollLeftAction. */ /*! \qmlsignal QtQuick::Accessible::scrollRightAction() This signal is emitted when a scroll right action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onScrollRightAction. */ /*! \qmlsignal QtQuick::Accessible::previousPageAction() This signal is emitted when a previous page action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onPreviousPageAction. */ /*! \qmlsignal QtQuick::Accessible::nextPageAction() This signal is emitted when a next page action is received from an assistive tool such as a screen-reader. - - The corresponding handler is \c onNextPageAction. */ QMetaMethod QQuickAccessibleAttached::sigPress; @@ -355,21 +310,48 @@ QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent) : QObject(parent), m_role(QAccessible::NoRole) { Q_ASSERT(parent); - QQuickItem *item = qobject_cast<QQuickItem*>(parent); - if (!item) - return; - // Enable accessibility for items with accessible content. This also - // enables accessibility for the ancestors of souch items. - item->d_func()->setAccessible(); - QAccessibleEvent ev(item, QAccessible::ObjectCreated); + // enables accessibility for the ancestors of such items. + auto item = qobject_cast<QQuickItem *>(parent); + if (item) { + item->d_func()->setAccessible(); + } else { + const QLatin1StringView className(QQmlData::ensurePropertyCache(parent)->firstCppMetaObject()->className()); + if (className != QLatin1StringView("QQuickAction")) { + qmlWarning(parent) << "Accessible must be attached to an Item or an Action"; + return; + } + } + QAccessibleEvent ev(parent, QAccessible::ObjectCreated); QAccessible::updateAccessibility(&ev); - if (!parent->property("value").isNull()) { - connect(parent, SIGNAL(valueChanged()), this, SLOT(valueChanged())); - } - if (!parent->property("cursorPosition").isNull()) { - connect(parent, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged())); + if (const QMetaObject *pmo = parent->metaObject()) { + auto connectPropertyChangeSignal = [parent, pmo, this]( + const char *propertyName, const char *signalName, int slotIndex) + { + // basically does this: + // if the parent has the property \a propertyName with the associated \a signalName: + // connect(parent, signalName, this, slotIndex) + + // Note that we explicitly want to only connect to standard property/signal naming + // convention: "value" & "valueChanged" + // (e.g. avoid a compound property with e.g. a signal notifier named "updated()") + int idxProperty = pmo->indexOfProperty(propertyName); + if (idxProperty != -1) { + const QMetaProperty property = pmo->property(idxProperty); + const QMetaMethod signal = property.notifySignal(); + if (signal.name() == signalName) + QMetaObject::connect(parent, signal.methodIndex(), this, slotIndex); + } + return; + }; + const QMetaObject &smo = staticMetaObject; + static const int valueChangedIndex = smo.indexOfSlot("valueChanged()"); + connectPropertyChangeSignal("value", "valueChanged", valueChangedIndex); + + static const int cursorPositionChangedIndex = smo.indexOfSlot("cursorPositionChanged()"); + connectPropertyChangeSignal("cursorPosition", "cursorPositionChanged", + cursorPositionChangedIndex); } if (!sigPress.isValid()) { @@ -423,9 +405,10 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role) m_state.focusable = true; break; case QAccessible::StaticText: - if (!m_stateExplicitlySet.readOnly) { + if (!m_stateExplicitlySet.readOnly) m_state.readOnly = true; - } + if (!m_stateExplicitlySet.focusable) + m_state.focusable = true; break; default: break; @@ -433,6 +416,19 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role) } } +bool QQuickAccessibleAttached::wasNameExplicitlySet() const +{ + return m_nameExplicitlySet; +} + +// Allows types to attach an accessible name to an item as a convenience, +// so long as the user hasn't done so themselves. +void QQuickAccessibleAttached::setNameImplicitly(const QString &name) +{ + setName(name); + m_nameExplicitlySet = false; +} + QQuickAccessibleAttached *QQuickAccessibleAttached::qmlAttachedProperties(QObject *obj) { return new QQuickAccessibleAttached(obj); @@ -440,13 +436,15 @@ QQuickAccessibleAttached *QQuickAccessibleAttached::qmlAttachedProperties(QObjec bool QQuickAccessibleAttached::ignored() const { - return !item()->d_func()->isAccessible; + auto item = qobject_cast<QQuickItem *>(parent()); + return item ? !item->d_func()->isAccessible : false; } void QQuickAccessibleAttached::setIgnored(bool ignored) { - if (this->ignored() != ignored) { - item()->d_func()->isAccessible = !ignored; + auto item = qobject_cast<QQuickItem *>(parent()); + if (item && this->ignored() != ignored) { + item->d_func()->isAccessible = !ignored; emit ignoredChanged(); } } @@ -474,8 +472,13 @@ bool QQuickAccessibleAttached::doAction(const QString &actionName) sig = &sigPreviousPage; else if (actionName == QAccessibleActionInterface::nextPageAction()) sig = &sigNextPage; - if (sig && isSignalConnected(*sig)) - return sig->invoke(this); + if (sig && isSignalConnected(*sig)) { + bool ret = false; + if (m_proxying) + ret = sig->invoke(m_proxying); + ret |= sig->invoke(this); + return ret; + } return false; } @@ -503,6 +506,80 @@ void QQuickAccessibleAttached::availableActions(QStringList *actions) const actions->append(QAccessibleActionInterface::nextPageAction()); } +QString QQuickAccessibleAttached::stripHtml(const QString &html) +{ +#ifndef QT_NO_TEXTHTMLPARSER + QTextDocument doc; + doc.setHtml(html); + return doc.toPlainText(); +#else + return html; +#endif +} + +void QQuickAccessibleAttached::setProxying(QQuickAccessibleAttached *proxying) +{ + if (proxying == m_proxying) + return; + + const QMetaObject &mo = staticMetaObject; + if (m_proxying) { + // We disconnect all signals from the proxy into this object + auto mo = m_proxying->metaObject(); + auto propertyCache = QQmlData::ensurePropertyCache(m_proxying); + for (int signalIndex = propertyCache->signalOffset(); + signalIndex < propertyCache->signalCount(); ++signalIndex) { + const QMetaMethod m = mo->method(propertyCache->signal(signalIndex)->coreIndex()); + Q_ASSERT(m.methodType() == QMetaMethod::Signal); + if (m.methodType() != QMetaMethod::Signal) + continue; + + disconnect(m_proxying, m, this, m); + } + } + + m_proxying = proxying; + + if (m_proxying) { + // We connect all signals from the proxy into this object + auto propertyCache = QQmlData::ensurePropertyCache(m_proxying); + auto mo = m_proxying->metaObject(); + for (int signalIndex = propertyCache->signalOffset(); + signalIndex < propertyCache->signalCount(); ++signalIndex) { + const QMetaMethod m = mo->method(propertyCache->signal(signalIndex)->coreIndex()); + Q_ASSERT(m.methodType() == QMetaMethod::Signal); + connect(proxying, m, this, m); + } + } + + // We check all properties + for (int prop = mo.propertyOffset(); prop < mo.propertyCount(); ++prop) { + const QMetaProperty p = mo.property(prop); + if (!p.hasNotifySignal()) { + continue; + } + + const QMetaMethod signal = p.notifySignal(); + if (signal.parameterCount() == 0) + signal.invoke(this); + else + signal.invoke(this, Q_ARG(bool, p.read(this).toBool())); + } +} + +/*! + * \since 6.8 + * Issues an announcement event with a \a message with politeness \a politeness. + * + * \sa QAccessibleAnnouncementEvent + */ +void QQuickAccessibleAttached::announce(const QString &message, QAccessible::AnnouncementPoliteness politeness) +{ + QAccessibleAnnouncementEvent event(parent(), message); + event.setPoliteness(politeness); + QAccessible::updateAccessibility(&event); +} + QT_END_NAMESPACE #include "moc_qquickaccessibleattached_p.cpp" |