Accessibility: QSpinBox should not have any children
On both iOS and Android it is very confusing to be able to move the focus to both, the line edit and the outer frame that is the spin box. For Linux this fixes an issue that orca would not read the value correctly after pressing the up/down buttons. Task-number: QTBUG-39861 Task-number: QTBUG-39442 Change-Id: I73c50c91e9021324c52d168d537afd0ea719a48f Reviewed-by: Jan Arve Sæther <jan-arve.saether@digia.com>bb10
parent
cf621f1b9e
commit
41f496cb7f
|
|
@ -70,7 +70,8 @@ static QList<QWidget*> childWidgets(const QWidget *widget)
|
|||
#if !defined(QT_NO_MENU)
|
||||
&& !qobject_cast<QMenu*>(w)
|
||||
#endif
|
||||
&& w->objectName() != QLatin1String("qt_rubberband"))
|
||||
&& w->objectName() != QLatin1String("qt_rubberband")
|
||||
&& w->objectName() != QLatin1String("qt_spinbox_lineedit"))
|
||||
widgets.append(w);
|
||||
}
|
||||
return widgets;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,10 @@ QAccessibleInterface *qAccessibleFactory(const QString &classname, QObject *obje
|
|||
if (false) {
|
||||
#ifndef QT_NO_LINEEDIT
|
||||
} else if (classname == QLatin1String("QLineEdit")) {
|
||||
iface = new QAccessibleLineEdit(widget);
|
||||
if (widget->objectName() == QLatin1String("qt_spinbox_lineedit"))
|
||||
iface = 0;
|
||||
else
|
||||
iface = new QAccessibleLineEdit(widget);
|
||||
#endif
|
||||
#ifndef QT_NO_COMBOBOX
|
||||
} else if (classname == QLatin1String("QComboBox")) {
|
||||
|
|
|
|||
|
|
@ -51,20 +51,28 @@
|
|||
#include <qglobal.h>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QDial>
|
||||
#include <QtWidgets/qlineedit.h>
|
||||
#include <qmath.h>
|
||||
#include <private/qmath_p.h>
|
||||
|
||||
#include "simplewidgets.h" // let spinbox use line edit's interface
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
|
||||
#ifndef QT_NO_SPINBOX
|
||||
QAccessibleAbstractSpinBox::QAccessibleAbstractSpinBox(QWidget *w)
|
||||
: QAccessibleWidget(w, QAccessible::SpinBox)
|
||||
: QAccessibleWidget(w, QAccessible::SpinBox), lineEdit(Q_NULLPTR)
|
||||
{
|
||||
Q_ASSERT(abstractSpinBox());
|
||||
}
|
||||
|
||||
QAccessibleAbstractSpinBox::~QAccessibleAbstractSpinBox()
|
||||
{
|
||||
delete lineEdit;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the underlying QAbstractSpinBox.
|
||||
*/
|
||||
|
|
@ -73,6 +81,14 @@ QAbstractSpinBox *QAccessibleAbstractSpinBox::abstractSpinBox() const
|
|||
return qobject_cast<QAbstractSpinBox*>(object());
|
||||
}
|
||||
|
||||
QAccessibleInterface *QAccessibleAbstractSpinBox::lineEditIface() const
|
||||
{
|
||||
// QAccessibleLineEdit is only used to forward the text functions
|
||||
if (!lineEdit)
|
||||
lineEdit = new QAccessibleLineEdit(abstractSpinBox()->lineEdit());
|
||||
return lineEdit;
|
||||
}
|
||||
|
||||
QString QAccessibleAbstractSpinBox::text(QAccessible::Text t) const
|
||||
{
|
||||
if (t == QAccessible::Value)
|
||||
|
|
@ -84,6 +100,10 @@ void *QAccessibleAbstractSpinBox::interface_cast(QAccessible::InterfaceType t)
|
|||
{
|
||||
if (t == QAccessible::ValueInterface)
|
||||
return static_cast<QAccessibleValueInterface*>(this);
|
||||
if (t == QAccessible::TextInterface)
|
||||
return static_cast<QAccessibleTextInterface*>(this);
|
||||
if (t == QAccessible::EditableTextInterface)
|
||||
return static_cast<QAccessibleEditableTextInterface*>(this);
|
||||
return QAccessibleWidget::interface_cast(t);
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +132,102 @@ QVariant QAccessibleAbstractSpinBox::minimumStepSize() const
|
|||
return abstractSpinBox()->property("stepSize");
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::addSelection(int startOffset, int endOffset)
|
||||
{
|
||||
lineEditIface()->textInterface()->addSelection(startOffset, endOffset);
|
||||
}
|
||||
|
||||
QString QAccessibleAbstractSpinBox::attributes(int offset, int *startOffset, int *endOffset) const
|
||||
{
|
||||
return lineEditIface()->textInterface()->attributes(offset, startOffset, endOffset);
|
||||
}
|
||||
|
||||
int QAccessibleAbstractSpinBox::cursorPosition() const
|
||||
{
|
||||
return lineEditIface()->textInterface()->cursorPosition();
|
||||
}
|
||||
|
||||
QRect QAccessibleAbstractSpinBox::characterRect(int offset) const
|
||||
{
|
||||
return lineEditIface()->textInterface()->characterRect(offset);
|
||||
}
|
||||
|
||||
int QAccessibleAbstractSpinBox::selectionCount() const
|
||||
{
|
||||
return lineEditIface()->textInterface()->selectionCount();
|
||||
}
|
||||
|
||||
int QAccessibleAbstractSpinBox::offsetAtPoint(const QPoint &point) const
|
||||
{
|
||||
return lineEditIface()->textInterface()->offsetAtPoint(point);
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::selection(int selectionIndex, int *startOffset, int *endOffset) const
|
||||
{
|
||||
lineEditIface()->textInterface()->selection(selectionIndex, startOffset, endOffset);
|
||||
}
|
||||
|
||||
QString QAccessibleAbstractSpinBox::text(int startOffset, int endOffset) const
|
||||
{
|
||||
return lineEditIface()->textInterface()->text(startOffset, endOffset);
|
||||
}
|
||||
|
||||
QString QAccessibleAbstractSpinBox::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const
|
||||
{
|
||||
return lineEditIface()->textInterface()->textBeforeOffset(offset, boundaryType, startOffset, endOffset);
|
||||
}
|
||||
|
||||
QString QAccessibleAbstractSpinBox::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const
|
||||
{
|
||||
return lineEditIface()->textInterface()->textAfterOffset(offset, boundaryType, startOffset, endOffset);
|
||||
}
|
||||
|
||||
QString QAccessibleAbstractSpinBox::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const
|
||||
{
|
||||
return lineEditIface()->textInterface()->textAtOffset(offset, boundaryType, startOffset, endOffset);
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::removeSelection(int selectionIndex)
|
||||
{
|
||||
lineEditIface()->textInterface()->removeSelection(selectionIndex);
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::setCursorPosition(int position)
|
||||
{
|
||||
lineEditIface()->textInterface()->setCursorPosition(position);
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::setSelection(int selectionIndex, int startOffset, int endOffset)
|
||||
{
|
||||
lineEditIface()->textInterface()->setSelection(selectionIndex, startOffset, endOffset);
|
||||
}
|
||||
|
||||
int QAccessibleAbstractSpinBox::characterCount() const
|
||||
{
|
||||
return lineEditIface()->textInterface()->characterCount();
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::scrollToSubstring(int startIndex, int endIndex)
|
||||
{
|
||||
lineEditIface()->textInterface()->scrollToSubstring(startIndex, endIndex);
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::deleteText(int startOffset, int endOffset)
|
||||
{
|
||||
lineEditIface()->editableTextInterface()->deleteText(startOffset, endOffset);
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::insertText(int offset, const QString &text)
|
||||
{
|
||||
lineEditIface()->editableTextInterface()->insertText(offset, text);
|
||||
}
|
||||
|
||||
void QAccessibleAbstractSpinBox::replaceText(int startOffset, int endOffset, const QString &text)
|
||||
{
|
||||
lineEditIface()->editableTextInterface()->replaceText(startOffset, endOffset, text);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\class QAccessibleSpinBox
|
||||
\brief The QAccessibleSpinBox class implements the QAccessibleInterface for spinbox widgets.
|
||||
|
|
|
|||
|
|
@ -55,12 +55,18 @@ class QSlider;
|
|||
class QSpinBox;
|
||||
class QDoubleSpinBox;
|
||||
class QDial;
|
||||
class QAccessibleLineEdit;
|
||||
|
||||
#ifndef QT_NO_SPINBOX
|
||||
class QAccessibleAbstractSpinBox: public QAccessibleWidget, public QAccessibleValueInterface // TODO, public QAccessibleActionInterface
|
||||
class QAccessibleAbstractSpinBox:
|
||||
public QAccessibleWidget,
|
||||
public QAccessibleValueInterface,
|
||||
public QAccessibleTextInterface,
|
||||
public QAccessibleEditableTextInterface
|
||||
{
|
||||
public:
|
||||
explicit QAccessibleAbstractSpinBox(QWidget *w);
|
||||
virtual ~QAccessibleAbstractSpinBox();
|
||||
|
||||
QString text(QAccessible::Text t) const Q_DECL_OVERRIDE;
|
||||
void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE;
|
||||
|
|
@ -72,10 +78,37 @@ public:
|
|||
QVariant minimumValue() const Q_DECL_OVERRIDE;
|
||||
QVariant minimumStepSize() const Q_DECL_OVERRIDE;
|
||||
|
||||
// FIXME Action interface
|
||||
// QAccessibleTextInterface
|
||||
void addSelection(int startOffset, int endOffset) Q_DECL_OVERRIDE;
|
||||
QString attributes(int offset, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE;
|
||||
int cursorPosition() const Q_DECL_OVERRIDE;
|
||||
QRect characterRect(int offset) const Q_DECL_OVERRIDE;
|
||||
int selectionCount() const Q_DECL_OVERRIDE;
|
||||
int offsetAtPoint(const QPoint &point) const Q_DECL_OVERRIDE;
|
||||
void selection(int selectionIndex, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE;
|
||||
QString text(int startOffset, int endOffset) const Q_DECL_OVERRIDE;
|
||||
QString textBeforeOffset (int offset, QAccessible::TextBoundaryType boundaryType,
|
||||
int *endOffset, int *startOffset) const Q_DECL_OVERRIDE;
|
||||
QString textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType,
|
||||
int *startOffset, int *endOffset) const Q_DECL_OVERRIDE;
|
||||
QString textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType,
|
||||
int *startOffset, int *endOffset) const Q_DECL_OVERRIDE;
|
||||
void removeSelection(int selectionIndex) Q_DECL_OVERRIDE;
|
||||
void setCursorPosition(int position) Q_DECL_OVERRIDE;
|
||||
void setSelection(int selectionIndex, int startOffset, int endOffset) Q_DECL_OVERRIDE;
|
||||
int characterCount() const Q_DECL_OVERRIDE;
|
||||
void scrollToSubstring(int startIndex, int endIndex) Q_DECL_OVERRIDE;
|
||||
|
||||
// QAccessibleEditableTextInterface
|
||||
void deleteText(int startOffset, int endOffset) Q_DECL_OVERRIDE;
|
||||
void insertText(int offset, const QString &text) Q_DECL_OVERRIDE;
|
||||
void replaceText(int startOffset, int endOffset, const QString &text) Q_DECL_OVERRIDE;
|
||||
|
||||
protected:
|
||||
QAbstractSpinBox *abstractSpinBox() const;
|
||||
QAccessibleInterface *lineEditIface() const;
|
||||
private:
|
||||
mutable QAccessibleLineEdit *lineEdit;
|
||||
};
|
||||
|
||||
class QAccessibleSpinBox : public QAccessibleAbstractSpinBox
|
||||
|
|
@ -94,6 +127,7 @@ public:
|
|||
|
||||
QString text(QAccessible::Text t) const Q_DECL_OVERRIDE;
|
||||
|
||||
using QAccessibleAbstractSpinBox::text;
|
||||
protected:
|
||||
QDoubleSpinBox *doubleSpinBox() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ public:
|
|||
void replaceText(int startOffset, int endOffset, const QString &text) Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
QLineEdit *lineEdit() const;
|
||||
friend class QAccessibleAbstractSpinBox;
|
||||
};
|
||||
#endif // QT_NO_LINEEDIT
|
||||
|
||||
|
|
|
|||
|
|
@ -699,6 +699,7 @@ void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
|
|||
}
|
||||
d->updateEditFieldGeometry();
|
||||
d->edit->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
d->edit->d_func()->control->setAccessibleObject(this);
|
||||
|
||||
if (isVisible())
|
||||
d->edit->show();
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ private:
|
|||
|
||||
Q_DECLARE_PRIVATE(QAbstractSpinBox)
|
||||
Q_DISABLE_COPY(QAbstractSpinBox)
|
||||
friend class QAccessibleAbstractSpinBox;
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractSpinBox::StepEnabled)
|
||||
|
||||
|
|
|
|||
|
|
@ -737,15 +737,15 @@ void QWidgetLineControl::internalSetText(const QString &txt, int pos, bool edite
|
|||
#ifndef QT_NO_ACCESSIBILITY
|
||||
if (changed) {
|
||||
if (oldText.isEmpty()) {
|
||||
QAccessibleTextInsertEvent event(parent(), 0, txt);
|
||||
QAccessibleTextInsertEvent event(accessibleObject(), 0, txt);
|
||||
event.setCursorPosition(m_cursor);
|
||||
QAccessible::updateAccessibility(&event);
|
||||
} else if (txt.isEmpty()) {
|
||||
QAccessibleTextRemoveEvent event(parent(), 0, oldText);
|
||||
QAccessibleTextRemoveEvent event(accessibleObject(), 0, oldText);
|
||||
event.setCursorPosition(m_cursor);
|
||||
QAccessible::updateAccessibility(&event);
|
||||
} else {
|
||||
QAccessibleTextUpdateEvent event(parent(), 0, oldText, txt);
|
||||
QAccessibleTextUpdateEvent event(accessibleObject(), 0, oldText, txt);
|
||||
event.setCursorPosition(m_cursor);
|
||||
QAccessible::updateAccessibility(&event);
|
||||
}
|
||||
|
|
@ -803,7 +803,7 @@ void QWidgetLineControl::internalInsert(const QString &s)
|
|||
if (m_maskData) {
|
||||
QString ms = maskString(m_cursor, s);
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
QAccessibleTextInsertEvent insertEvent(parent(), m_cursor, ms);
|
||||
QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, ms);
|
||||
QAccessible::updateAccessibility(&insertEvent);
|
||||
#endif
|
||||
for (int i = 0; i < (int) ms.length(); ++i) {
|
||||
|
|
@ -815,14 +815,14 @@ void QWidgetLineControl::internalInsert(const QString &s)
|
|||
m_cursor = nextMaskBlank(m_cursor);
|
||||
m_textDirty = true;
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
QAccessibleTextCursorEvent event(parent(), m_cursor);
|
||||
QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
|
||||
QAccessible::updateAccessibility(&event);
|
||||
#endif
|
||||
} else {
|
||||
int remaining = m_maxLength - m_text.length();
|
||||
if (remaining != 0) {
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
QAccessibleTextInsertEvent insertEvent(parent(), m_cursor, s);
|
||||
QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, s);
|
||||
QAccessible::updateAccessibility(&insertEvent);
|
||||
#endif
|
||||
m_text.insert(m_cursor, s.left(remaining));
|
||||
|
|
@ -853,7 +853,7 @@ void QWidgetLineControl::internalDelete(bool wasBackspace)
|
|||
addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
|
||||
m_cursor, m_text.at(m_cursor), -1, -1));
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
QAccessibleTextRemoveEvent event(parent(), m_cursor, m_text.at(m_cursor));
|
||||
QAccessibleTextRemoveEvent event(accessibleObject(), m_cursor, m_text.at(m_cursor));
|
||||
QAccessible::updateAccessibility(&event);
|
||||
#endif
|
||||
if (m_maskData) {
|
||||
|
|
@ -894,7 +894,7 @@ void QWidgetLineControl::removeSelectedText()
|
|||
addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
|
||||
}
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
QAccessibleTextRemoveEvent event(parent(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart));
|
||||
QAccessibleTextRemoveEvent event(accessibleObject(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart));
|
||||
QAccessible::updateAccessibility(&event);
|
||||
#endif
|
||||
if (m_maskData) {
|
||||
|
|
@ -1384,7 +1384,7 @@ void QWidgetLineControl::emitCursorPositionChanged()
|
|||
#ifndef QT_NO_ACCESSIBILITY
|
||||
// otherwise we send a selection update which includes the cursor
|
||||
if (!hasSelectedText()) {
|
||||
QAccessibleTextCursorEvent event(parent(), m_cursor);
|
||||
QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
|
||||
QAccessible::updateAccessibility(&event);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ public:
|
|||
, m_passwordMaskDelayOverride(-1)
|
||||
#endif
|
||||
, m_keyboardScheme(0)
|
||||
, m_accessibleObject(0)
|
||||
{
|
||||
init(txt);
|
||||
}
|
||||
|
|
@ -108,6 +109,19 @@ public:
|
|||
delete [] m_maskData;
|
||||
}
|
||||
|
||||
void setAccessibleObject(QObject *object)
|
||||
{
|
||||
Q_ASSERT(object);
|
||||
m_accessibleObject = object;
|
||||
}
|
||||
|
||||
QObject *accessibleObject()
|
||||
{
|
||||
if (m_accessibleObject)
|
||||
return m_accessibleObject;
|
||||
return parent();
|
||||
}
|
||||
|
||||
int nextMaskBlank(int pos)
|
||||
{
|
||||
int c = findInMask(pos, true, false);
|
||||
|
|
@ -532,6 +546,9 @@ private Q_SLOTS:
|
|||
|
||||
private:
|
||||
int m_keyboardScheme;
|
||||
|
||||
// accessibility events are sent for this object
|
||||
QObject *m_accessibleObject;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -1676,13 +1676,10 @@ void tst_QAccessibility::spinBoxTest()
|
|||
QCOMPARE(accessibleRect, widgetRect);
|
||||
QCOMPARE(interface->text(QAccessible::Value), QLatin1String("3"));
|
||||
|
||||
// one child, the line edit
|
||||
// make sure that the line edit is not there
|
||||
const int numChildren = interface->childCount();
|
||||
QCOMPARE(numChildren, 1);
|
||||
QAccessibleInterface *lineEdit = interface->child(0);
|
||||
|
||||
QCOMPARE(lineEdit->role(), QAccessible::EditableText);
|
||||
QCOMPARE(lineEdit->text(QAccessible::Value), QLatin1String("3"));
|
||||
QCOMPARE(numChildren, 0);
|
||||
QVERIFY(interface->child(0) == Q_NULLPTR);
|
||||
|
||||
QVERIFY(interface->valueInterface());
|
||||
QCOMPARE(interface->valueInterface()->currentValue().toInt(), 3);
|
||||
|
|
@ -1696,6 +1693,10 @@ void tst_QAccessibility::spinBoxTest()
|
|||
QTest::qWait(200);
|
||||
QAccessibleValueChangeEvent expectedEvent(spinBox, spinBox->value());
|
||||
QVERIFY(QTestAccessibility::containsEvent(&expectedEvent));
|
||||
|
||||
QAccessibleTextInterface *textIface = interface->textInterface();
|
||||
QVERIFY(textIface);
|
||||
|
||||
delete spinBox;
|
||||
QTestAccessibility::clearEvents();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue