FIX(a11y): Improve Accessibility for CertWizard

The certificate wizard shows boxes with certificate info.
These boxes are created by using labels in a QGroupBox.
However, this information is not easily readable using a
screen reader (especially Orca).

This commit introduces the AccessibleGroupBox class
which aims to solve that by dynamically setting the
accessible description using layout information of
the labels inside the box.

Fixes #1337
This commit is contained in:
Hartmnt 2024-01-17 18:29:03 +00:00
parent 3474d63b86
commit 6685fbf807
5 changed files with 122 additions and 2 deletions

View File

@ -289,6 +289,8 @@ set(MUMBLE_SOURCES
"XMLTools.cpp"
"XMLTools.h"
"widgets/AccessibleQGroupBox.cpp"
"widgets/AccessibleQGroupBox.h"
"widgets/CompletablePage.cpp"
"widgets/CompletablePage.h"
"widgets/EventFilters.cpp"

View File

@ -18,6 +18,7 @@
#include "Utils.h"
#include "Global.h"
#include <QTimer>
#include <QtCore/QUrl>
#include <QtGui/QDesktopServices>
#include <QtWidgets/QFileDialog>
@ -29,7 +30,7 @@
#define SSL_STRING(x) QString::fromLatin1(x).toUtf8().data()
CertView::CertView(QWidget *p) : QGroupBox(p) {
CertView::CertView(QWidget *p) : AccessibleQGroupBox(p) {
QGridLayout *grid = new QGridLayout(this);
QLabel *l;
@ -65,6 +66,8 @@ CertView::CertView(QWidget *p) : QGroupBox(p) {
grid->addWidget(qlExpiry, 3, 1, 1, 1);
grid->setColumnStretch(1, 1);
updateAccessibleText();
}
void CertView::setCert(const QList< QSslCertificate > &cert) {
@ -116,6 +119,8 @@ void CertView::setCert(const QList< QSslCertificate > &cert) {
qlIssuerName->setText((issuerName == name) ? tr("Self-signed") : issuerName);
}
updateAccessibleText();
}
CertWizard::CertWizard(QWidget *p) : QWizard(p) {
@ -144,6 +149,8 @@ CertWizard::CertWizard(QWidget *p) : QWizard(p) {
installEventFilter(m_overrideFilter);
connect(this, &CertWizard::currentIdChanged, this, &CertWizard::showPage);
QTimer::singleShot(0, [this] { this->showPage(0); });
}
int CertWizard::nextId() const {

View File

@ -14,12 +14,13 @@
#include <QtNetwork/QSslCertificate>
#include "Settings.h"
#include "widgets/AccessibleQGroupBox.h"
#include "widgets/EventFilters.h"
class QLabel;
class QWidget;
class CertView : public QGroupBox {
class CertView : public AccessibleQGroupBox {
private:
Q_OBJECT
Q_DISABLE_COPY(CertView)

View File

@ -0,0 +1,90 @@
// Copyright 2024 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "AccessibleQGroupBox.h"
#include <QFormLayout>
#include <QGridLayout>
#include <QLabel>
AccessibleQGroupBox::AccessibleQGroupBox(QWidget *parent) : QGroupBox(parent) {
setFocusPolicy(Qt::TabFocus);
updateAccessibleText();
}
void AccessibleQGroupBox::updateAccessibleText() {
QString text;
// If we have a grid layout, we iterate over it top-to-bottom left-to-right
// and concat all the label contents.
QGridLayout *gridLayout = qobject_cast< QGridLayout * >(layout());
if (gridLayout) {
for (int y = 0; y < gridLayout->rowCount(); y++) {
for (int x = 0; x < gridLayout->columnCount(); x++) {
QLabel *label = qobject_cast< QLabel * >(gridLayout->itemAtPosition(y, x)->widget());
if (!label) {
continue;
}
text += " ";
QString content = label->text();
if (content.trimmed().isEmpty()) {
content = tr("empty");
}
text += content;
}
text += ",";
}
setAccessibleDescription(text);
return;
}
// Form layouts contain rows with 1 or 2 items. It can either contain a
// "spanning role", or a label with a field.
QFormLayout *formLayout = qobject_cast< QFormLayout * >(layout());
if (formLayout) {
for (int y = 0; y < formLayout->rowCount(); y++) {
QLabel *label;
label = qobject_cast< QLabel * >(formLayout->itemAt(y, QFormLayout::SpanningRole)->widget());
if (label) {
text += label->text() + ", ";
continue;
}
label = qobject_cast< QLabel * >(formLayout->itemAt(y, QFormLayout::LabelRole)->widget());
if (label) {
text += label->text();
}
label = qobject_cast< QLabel * >(formLayout->itemAt(y, QFormLayout::FieldRole)->widget());
if (label) {
QString content = label->text();
if (content.trimmed().isEmpty()) {
content = tr("empty");
}
text += " " + content;
}
text += ", ";
}
setAccessibleDescription(text);
return;
}
// There is no layout we can easily parse, so just concat all
// child label texts.
QObjectList childObjects = children();
for (int i = 0; i < childObjects.length(); i++) {
QLabel *label = qobject_cast< QLabel * >(childObjects[i]);
if (!label) {
continue;
}
text += label->text() + ",";
}
setAccessibleDescription(text);
}

View File

@ -0,0 +1,20 @@
// Copyright 2024 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#ifndef MUMBLE_MUMBLE_ACCESSIBLEQGROUPBOX_H_
#define MUMBLE_MUMBLE_ACCESSIBLEQGROUPBOX_H_
#include <QGroupBox>
class AccessibleQGroupBox : public QGroupBox {
Q_OBJECT
public:
AccessibleQGroupBox(QWidget *parent);
void updateAccessibleText();
};
#endif