Accessibility: simplify dockwidget accessible

This patch merges the two classes that were used to make
dock widgets accessible into one.
The title bar does not need to be represented by its own
accessible object.

In addition the buttons on the toolbar are now labelled.

Task-number: QTBUG-33946
Change-Id: Id90d8c09f15ed683e64dbe3f6ac55bca7a0b300f
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
bb10
Frederik Gladhorn 2012-10-19 15:08:42 +02:00 committed by The Qt Project
parent 9ebd76b021
commit f86ffab402
4 changed files with 142 additions and 248 deletions

View File

@ -558,37 +558,56 @@ QWidget *QAccessibleCalendarWidget::navigationBar() const
#endif // QT_NO_CALENDARWIDGET
#ifndef QT_NO_DOCKWIDGET
// Dock Widget - order of children:
// - Content widget
// - Float button
// - Close button
// If there is a custom title bar widget, that one becomes child 1, after the content 0
// (in that case the buttons are ignored)
QAccessibleDockWidget::QAccessibleDockWidget(QWidget *widget)
: QAccessibleWidget(widget, QAccessible::Window)
{
}
QAccessibleInterface *QAccessibleDockWidget::child(int index) const
QDockWidgetLayout *QAccessibleDockWidget::dockWidgetLayout() const
{
if (index == 0) {
return new QAccessibleTitleBar(dockWidget());
} else if (index == 1 && dockWidget()->widget()) {
return QAccessible::queryAccessibleInterface(dockWidget()->widget());
}
return 0;
return qobject_cast<QDockWidgetLayout*>(dockWidget()->layout());
}
int QAccessibleDockWidget::childCount() const
{
return dockWidget()->widget() ? 2 : 1;
if (dockWidget()->titleBarWidget()) {
return dockWidget()->widget() ? 2 : 1;
}
return dockWidgetLayout()->count();
}
QAccessibleInterface *QAccessibleDockWidget::child(int index) const
{
if (dockWidget()->titleBarWidget()) {
if ((!dockWidget()->widget() && index == 0) || (index == 1))
return QAccessible::queryAccessibleInterface(dockWidget()->titleBarWidget());
if (index == 0)
return QAccessible::queryAccessibleInterface(dockWidget()->widget());
} else {
QLayoutItem *item = dockWidgetLayout()->itemAt(index);
if (item)
return QAccessible::queryAccessibleInterface(item->widget());
}
return 0;
}
int QAccessibleDockWidget::indexOfChild(const QAccessibleInterface *child) const
{
if (child) {
if (child->role() == QAccessible::TitleBar) {
return 0;
} else {
return 1; // FIXME
}
if (!child || !child->object() || child->object()->parent() != object())
return -1;
if (dockWidget()->titleBarWidget() == child->object()) {
return dockWidget()->widget() ? 1 : 0;
}
return -1;
return dockWidgetLayout()->indexOf(qobject_cast<QWidget*>(child->object()));
}
QRect QAccessibleDockWidget::rect() const
@ -610,190 +629,13 @@ QDockWidget *QAccessibleDockWidget::dockWidget() const
return static_cast<QDockWidget *>(object());
}
////
// QAccessibleTitleBar
////
QAccessibleTitleBar::QAccessibleTitleBar(QDockWidget *widget)
: m_dockWidget(widget)
{
}
QAccessibleInterface *QAccessibleTitleBar::parent() const
{
return new QAccessibleDockWidget(dockWidget());
}
QAccessibleInterface *QAccessibleTitleBar::child(int index) const
{
if (index >= 0) {
QDockWidgetLayout *layout = dockWidgetLayout();
int role;
int currentIndex = 0;
for (role = QDockWidgetLayout::CloseButton; role <= QDockWidgetLayout::FloatButton; ++role) {
QWidget *w = layout->widgetForRole((QDockWidgetLayout::Role)role);
if (!w || !w->isVisible())
continue;
if (currentIndex == index)
return QAccessible::queryAccessibleInterface(w);
++currentIndex;
}
}
return 0;
}
int QAccessibleTitleBar::indexOfChild(const QAccessibleInterface * /*child*/) const
{
return -1;
}
int QAccessibleTitleBar::childCount() const
{
QDockWidgetLayout *layout = dockWidgetLayout();
int count = 0;
for (int role = QDockWidgetLayout::CloseButton; role <= QDockWidgetLayout::FloatButton; ++role) {
QWidget *w = layout->widgetForRole((QDockWidgetLayout::Role)role);
if (w && w->isVisible())
++count;
}
return count;
}
QString QAccessibleTitleBar::text(QAccessible::Text t) const
QString QAccessibleDockWidget::text(QAccessible::Text t) const
{
if (t == QAccessible::Name || t == QAccessible::Value) {
return qt_accStripAmp(dockWidget()->windowTitle());
}
return QString();
}
QAccessible::State QAccessibleTitleBar::state() const
{
QAccessible::State state;
QDockWidget *w = dockWidget();
if (w->testAttribute(Qt::WA_WState_Visible) == false)
state.invisible = true;
if (w->focusPolicy() != Qt::NoFocus && w->isActiveWindow())
state.focusable = true;
if (w->hasFocus())
state.focused = true;
if (!w->isEnabled())
state.disabled = true;
return state;
}
QRect QAccessibleTitleBar::rect() const
{
bool mapToGlobal = true;
QRect rect;
if (dockWidget()->isFloating()) {
rect = dockWidget()->frameGeometry();
if (dockWidget()->widget()) {
QPoint globalPos = dockWidget()->mapToGlobal(dockWidget()->widget()->rect().topLeft());
globalPos.ry()--;
rect.setBottom(globalPos.y());
mapToGlobal = false;
}
} else {
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(dockWidget()->layout());
rect = layout->titleArea();
}
if (rect.isNull())
return rect;
if (mapToGlobal)
rect.moveTopLeft(dockWidget()->mapToGlobal(rect.topLeft()));
return rect;
}
QAccessibleInterface *QAccessibleTitleBar::childAt(int x, int y) const
{
for (int i = 0; i < childCount(); ++i) {
QAccessibleInterface *childIface = child(i);
if (childIface->rect().contains(x,y)) {
return childIface;
}
}
return 0;
}
QObject *QAccessibleTitleBar::object() const
{
return 0;
}
QDockWidgetLayout *QAccessibleTitleBar::dockWidgetLayout() const
{
return qobject_cast<QDockWidgetLayout*>(dockWidget()->layout());
}
QDockWidget *QAccessibleTitleBar::dockWidget() const
{
return m_dockWidget;
}
//QString QAccessibleTitleBar::actionText(int action, Text t, int child) const
//{
// QString str;
// if (child >= 1 && child <= childCount()) {
// if (t == Name) {
// switch (action) {
// case Press:
// case DefaultAction:
// if (child == QDockWidgetLayout::CloseButton) {
// str = QDockWidget::tr("Close");
// } else if (child == QDockWidgetLayout::FloatButton) {
// str = dockWidget()->isFloating() ? QDockWidget::tr("Dock")
// : QDockWidget::tr("Float");
// }
// break;
// default:
// break;
// }
// }
// }
// return str;
//}
//bool QAccessibleTitleBar::doAction(int action, int child, const QVariantList& /*params*/)
//{
// if (!child || !dockWidget()->isEnabled())
// return false;
// switch (action) {
// case DefaultAction:
// case Press: {
// QDockWidgetLayout *layout = dockWidgetLayout();
// QAbstractButton *btn = static_cast<QAbstractButton *>(layout->widgetForRole((QDockWidgetLayout::Role)child));
// if (btn)
// btn->animateClick();
// return true;
// break;}
// default:
// break;
// }
// return false;
//}
QAccessible::Role QAccessibleTitleBar::role() const
{
return QAccessible::TitleBar;
}
void QAccessibleTitleBar::setText(QAccessible::Text /*t*/, const QString &/*text*/)
{
}
bool QAccessibleTitleBar::isValid() const
{
return dockWidget();
}
#endif // QT_NO_DOCKWIDGET
#ifndef QT_NO_CURSOR

View File

@ -283,31 +283,10 @@ public:
int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE;
int childCount() const Q_DECL_OVERRIDE;
QRect rect () const Q_DECL_OVERRIDE;
QDockWidget *dockWidget() const;
};
class QAccessibleTitleBar : public QAccessibleInterface
{
public:
explicit QAccessibleTitleBar(QDockWidget *widget);
QAccessibleInterface *parent() const Q_DECL_OVERRIDE;
QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE;
int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE;
int childCount() const Q_DECL_OVERRIDE;
QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE;
void setText(QAccessible::Text t, const QString &text) Q_DECL_OVERRIDE;
QString text(QAccessible::Text t) const Q_DECL_OVERRIDE;
QAccessible::Role role() const Q_DECL_OVERRIDE;
QRect rect () const Q_DECL_OVERRIDE;
QAccessible::State state() const Q_DECL_OVERRIDE;
QObject *object() const Q_DECL_OVERRIDE;
bool isValid() const Q_DECL_OVERRIDE;
QPointer<QDockWidget> m_dockWidget;
QDockWidget *dockWidget() const;
protected:
QDockWidgetLayout *dockWidgetLayout() const;
};

View File

@ -674,12 +674,18 @@ void QDockWidgetPrivate::updateButtons()
= qobject_cast<QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::FloatButton));
button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q));
button->setVisible(canFloat && !hideButtons);
#ifndef QT_NO_ACCESSIBILITY
button->setAccessibleName(QDockWidget::tr("Float"));
button->setAccessibleDescription(QDockWidget::tr("Undocks and re-attaches the dock widget"));
#endif
button
= qobject_cast <QAbstractButton*>(dwLayout->widgetForRole(QDockWidgetLayout::CloseButton));
button->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q));
button->setVisible(canClose && !hideButtons);
#ifndef QT_NO_ACCESSIBILITY
button->setAccessibleName(QDockWidget::tr("Close"));
button->setAccessibleDescription(QDockWidget::tr("Closes the dock widget"));
#endif
q->setAttribute(Qt::WA_ContentsPropagated,
(canFloat || canClose) && !hideButtons);

View File

@ -3056,60 +3056,127 @@ void tst_QAccessibility::dockWidgetTest()
mw->setMenuBar(mb);
QDockWidget *dock1 = new QDockWidget(mw);
dock1->setWindowTitle("Dock 1");
mw->addDockWidget(Qt::LeftDockWidgetArea, dock1);
QPushButton *pb1 = new QPushButton(tr("Push me"), dock1);
dock1->setWidget(pb1);
QDockWidget *dock2 = new QDockWidget(mw);
dock2->setWindowTitle("Dock 2");
mw->addDockWidget(Qt::BottomDockWidgetArea, dock2);
QPushButton *pb2 = new QPushButton(tr("Push me"), dock2);
dock2->setWidget(pb2);
dock2->setFeatures(QDockWidget::DockWidgetClosable);
mw->resize(600,400);
mw->show();
#if defined(Q_OS_UNIX)
QCoreApplication::processEvents();
QTest::qWait(100);
#endif
QTest::qWaitForWindowExposed(mw);
QAccessibleInterface *accMainWindow = QAccessible::queryAccessibleInterface(mw);
// 4 children: menu bar, dock1, dock2, and central widget
QCOMPARE(accMainWindow->childCount(), 4);
QAccessibleInterface *accDock1 = 0;
QAccessibleInterface *accDock2 = 0;
for (int i = 0; i < 4; ++i) {
accDock1 = accMainWindow->child(i);
if (accMainWindow->role() == QAccessible::Window) {
if (accDock1 && qobject_cast<QDockWidget*>(accDock1->object()) == dock1) {
break;
}
}
QAccessibleInterface *child = accMainWindow->child(i);
if (child && child->object() == dock1)
accDock1 = child;
if (child && child->object() == dock2)
accDock2 = child;
}
// Dock widgets consist of
// 0 contents
// 1 close button
// 2 float button
QVERIFY(accDock1);
QCOMPARE(accDock1->role(), QAccessible::Window);
QCOMPARE(accDock1->text(QAccessible::Name), dock1->windowTitle());
QCOMPARE(accDock1->childCount(), 3);
QAccessibleInterface *dock1TitleBar = accDock1->child(0);
QCOMPARE(dock1TitleBar->role(), QAccessible::TitleBar);
QVERIFY(accDock1->rect().contains(dock1TitleBar->rect()));
QAccessibleInterface *dock1Widget = accDock1->child(0);
QCOMPARE(dock1Widget->role(), QAccessible::Button);
QCOMPARE(dock1Widget->text(QAccessible::Name), pb1->text());
QPoint globalPos = dock1->mapToGlobal(QPoint(0,0));
globalPos.rx()+=5; //### query style
globalPos.ry()+=5;
QAccessibleInterface *childAt = accDock1->childAt(globalPos.x(), globalPos.y()); //###
QCOMPARE(childAt->role(), QAccessible::TitleBar);
int index = accDock1->indexOfChild(childAt);
QAccessibleInterface *accTitleBar = accDock1->child(index);
#ifdef Q_OS_MAC
QEXPECT_FAIL("", "Dock Widget geometry on Mac seems broken.", Continue);
#endif
QVERIFY(accDock1->rect().contains(dock1Widget->rect()));
QCOMPARE(accDock1->indexOfChild(dock1Widget), 0);
QCOMPARE(dock1Widget->parent()->object(), dock1);
QCOMPARE(accTitleBar->role(), QAccessible::TitleBar);
QCOMPARE(accDock1->indexOfChild(accTitleBar), 0);
QAccessibleInterface *acc;
acc = accTitleBar->parent();
QVERIFY(acc);
QCOMPARE(acc->role(), QAccessible::Window);
QAccessibleInterface *dock1Close = accDock1->child(1);
QCOMPARE(dock1Close->role(), QAccessible::Button);
QCOMPARE(dock1Close->text(QAccessible::Name), QDockWidget::tr("Close"));
QVERIFY(accDock1->rect().contains(dock1Close->rect()));
QCOMPARE(accDock1->indexOfChild(dock1Close), 1);
QCOMPARE(dock1Close->parent()->object(), dock1);
QAccessibleInterface *dock1Float = accDock1->child(2);
QCOMPARE(dock1Float->role(), QAccessible::Button);
QCOMPARE(dock1Float->text(QAccessible::Name), QDockWidget::tr("Float"));
QVERIFY(accDock1->rect().contains(dock1Float->rect()));
QCOMPARE(accDock1->indexOfChild(dock1Float), 2);
QVERIFY(!dock1Float->state().invisible);
QVERIFY(accDock2);
QCOMPARE(accDock2->role(), QAccessible::Window);
QCOMPARE(accDock2->text(QAccessible::Name), dock2->windowTitle());
QCOMPARE(accDock2->childCount(), 3);
QAccessibleInterface *dock2Widget = accDock2->child(0);
QCOMPARE(dock2Widget->role(), QAccessible::Button);
QCOMPARE(dock2Widget->text(QAccessible::Name), pb1->text());
#ifdef Q_OS_MAC
QEXPECT_FAIL("", "Dock Widget geometry on Mac seems broken.", Continue);
#endif
QVERIFY(accDock2->rect().contains(dock2Widget->rect()));
QCOMPARE(accDock2->indexOfChild(dock2Widget), 0);
QAccessibleInterface *dock2Close = accDock2->child(1);
QCOMPARE(dock2Close->role(), QAccessible::Button);
QCOMPARE(dock2Close->text(QAccessible::Name), QDockWidget::tr("Close"));
QVERIFY(accDock2->rect().contains(dock2Close->rect()));
QCOMPARE(accDock2->indexOfChild(dock2Close), 1);
QVERIFY(!dock2Close->state().invisible);
QAccessibleInterface *dock2Float = accDock2->child(2);
QCOMPARE(dock2Float->role(), QAccessible::Button);
QCOMPARE(dock2Float->text(QAccessible::Name), QDockWidget::tr("Float"));
QCOMPARE(accDock2->indexOfChild(dock2Float), 2);
QVERIFY(dock2Float->state().invisible);
QPoint buttonPoint = pb2->mapToGlobal(QPoint(pb2->width()/2, pb2->height()/2));
QAccessibleInterface *childAt = accDock2->childAt(buttonPoint.x(), buttonPoint.y());
QVERIFY(childAt);
QVERIFY(childAt->object() == pb2);
QWidget *close1 = qobject_cast<QWidget*>(dock1Close->object());
QPoint close1ButtonPoint = close1->mapToGlobal(QPoint(close1->width()/2, close1->height()/2));
QAccessibleInterface *childAt2 = accDock1->childAt(close1ButtonPoint.x(), close1ButtonPoint.y());
QVERIFY(childAt2);
QVERIFY(childAt2->object() == close1);
// custom title bar widget
QDockWidget *dock3 = new QDockWidget(mw);
dock3->setWindowTitle("Dock 3");
mw->addDockWidget(Qt::LeftDockWidgetArea, dock3);
QPushButton *pb3 = new QPushButton(tr("Push me"), dock3);
dock3->setWidget(pb3);
QLabel *titleLabel = new QLabel("I am a title widget");
dock3->setTitleBarWidget(titleLabel);
QAccessibleInterface *accDock3 = accMainWindow->child(4);
QVERIFY(accDock3);
QCOMPARE(accDock3->role(), QAccessible::Window);
QCOMPARE(accDock3->text(QAccessible::Name), dock3->windowTitle());
QCOMPARE(accDock3->childCount(), 2);
QAccessibleInterface *titleWidget = accDock3->child(1);
QVERIFY(titleWidget);
QCOMPARE(titleWidget->text(QAccessible::Name), titleLabel->text());
QAccessibleInterface *dock3Widget = accDock3->child(0);
QCOMPARE(dock3Widget->text(QAccessible::Name), pb3->text());
delete pb1;
delete pb2;
delete dock1;
delete dock2;
delete mw;
QTestAccessibility::clearEvents();
#endif // QT_NO_DOCKWIDGET