From 3b6acda9df2d23692290184ba9bda4de68b9adcb Mon Sep 17 00:00:00 2001 From: Hartmnt Date: Fri, 6 Jan 2023 15:56:23 +0000 Subject: [PATCH] FIX(a11y): Make the info in server and user information dialogs accessible Using the new AccessibleGroupBox make the information inside the server and user information dialogs accessible for screen readers. Fixes #1337 --- src/mumble/Accessibility.cpp | 4 +- src/mumble/Accessibility.h | 2 + src/mumble/ServerInformation.cpp | 14 ++ src/mumble/ServerInformation.ui | 206 +++++++++++---------- src/mumble/UserInformation.cpp | 14 ++ src/mumble/UserInformation.ui | 16 +- src/mumble/widgets/AccessibleQGroupBox.cpp | 97 ++++++---- src/mumble/widgets/AccessibleQGroupBox.h | 3 + 8 files changed, 225 insertions(+), 131 deletions(-) diff --git a/src/mumble/Accessibility.cpp b/src/mumble/Accessibility.cpp index bd430a836..452deb628 100644 --- a/src/mumble/Accessibility.cpp +++ b/src/mumble/Accessibility.cpp @@ -15,8 +15,10 @@ namespace Mumble { namespace Accessibility { + QString removeHTMLTags(QString value) { return value.remove(QRegExp("<[^>]*>")); } + void setDescriptionFromLabel(QWidget *widget, const QLabel *label) { - widget->setAccessibleDescription(label->text().remove(QRegExp("<[^>]*>"))); + widget->setAccessibleDescription(removeHTMLTags(label->text())); } void fixWizardButtonLabels(QWizard *wizard) { diff --git a/src/mumble/Accessibility.h b/src/mumble/Accessibility.h index ca08acdac..86e0efa00 100644 --- a/src/mumble/Accessibility.h +++ b/src/mumble/Accessibility.h @@ -17,6 +17,8 @@ namespace Mumble { namespace Accessibility { + QString removeHTMLTags(QString value); + void setDescriptionFromLabel(QWidget *widget, const QLabel *label); void fixWizardButtonLabels(QWizard *wizard); diff --git a/src/mumble/ServerInformation.cpp b/src/mumble/ServerInformation.cpp index 822130c9d..d9ceb89b5 100644 --- a/src/mumble/ServerInformation.cpp +++ b/src/mumble/ServerInformation.cpp @@ -26,6 +26,14 @@ ServerInformation::ServerInformation(QWidget *parent) : QDialog(parent) { setupUi(this); updateFields(); + + // Labels are not initialized properly here. We need to wait a tiny bit + // to make sure its contents are actually read. + QTimer::singleShot(0, [this]() { + qgbServerInformation->updateAccessibleText(); + qgbAudioBandwidth->updateAccessibleText(); + qgbTCPParameters->updateAccessibleText(); + }); } void ServerInformation::updateFields() { @@ -73,6 +81,8 @@ void ServerInformation::updateServerInformation() { serverInfo_protocol->setText(Version::toString(Global::get().sh->m_version)); serverInfo_release->setText(release); serverInfo_os->setText(os); + + qgbServerInformation->updateAccessibleText(); } static const QString currentCodec() { @@ -88,6 +98,8 @@ void ServerInformation::updateAudioBandwidth() { audio_current->setText(QString::fromLatin1("%1 kBit/s").arg(currentBandwidth, 0, 'f', 1)); audio_allowed->setText(QString::fromLatin1("%1 kBit/s").arg(maxBandwidthAllowed, 0, 'f', 1)); audio_codec->setText(currentCodec()); + + qgbAudioBandwidth->updateAccessibleText(); } void ServerInformation::updateConnectionDetails() { @@ -145,6 +157,8 @@ void ServerInformation::updateConnectionDetails() { // unavailable, the respective boolean flag is never touched and is therefore meaningless. connection_tcp_forwardSecrecy->setText(tr("Unknown")); #endif + + qgbTCPParameters->updateAccessibleText(); } void ServerInformation::populateUDPStatistics(const Connection &connection) { diff --git a/src/mumble/ServerInformation.ui b/src/mumble/ServerInformation.ui index 61949ab56..205ac38ee 100644 --- a/src/mumble/ServerInformation.ui +++ b/src/mumble/ServerInformation.ui @@ -23,14 +23,14 @@ 0 - -201 + 0 541 726 - + 0 @@ -90,7 +90,7 @@ - <b>Users</b>: + <b>Users:</b> @@ -189,7 +189,7 @@ - + 0 @@ -378,6 +378,9 @@ 0 + + Qt::ClickFocus + Qt::ScrollBarAlwaysOff @@ -482,77 +485,109 @@ TCP (Control) - + - - - <b>TLS version:</b> - - - - - - - - 0 - 0 - - - - <TLS> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + TCP Parameters + + + + + <b>TLS version:</b> + + + + + + + + 0 + 0 + + + + <TLS> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Cipher suite:</b> + + + + + + + + 0 + 0 + + + + <Cipher> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + <b>Avg. latency:</b> + + + + + + + + 0 + 0 + + + + <latency> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Whether the connection supports perfect forward secrecy (PFS). + + + <b>PFS:</b> + + + + + + + <forward secrecy> + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + - - - <b>Cipher suite:</b> - - - - - - - - 0 - 0 - - - - <Cipher> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - <b>Avg. latency:</b> - - - - - - - - 0 - 0 - - - - <latency> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - Qt::Vertical @@ -568,29 +603,6 @@ - - - - Whether the connection supports perfect forward secrecy (PFS). - - - <b>PFS:</b> - - - - - - - <forward secrecy> - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - @@ -648,6 +660,14 @@ + + + AccessibleQGroupBox + QGroupBox +
AccessibleQGroupBox.h
+ 1 +
+
diff --git a/src/mumble/UserInformation.cpp b/src/mumble/UserInformation.cpp index 03f72b179..0da63d2c4 100644 --- a/src/mumble/UserInformation.cpp +++ b/src/mumble/UserInformation.cpp @@ -31,6 +31,15 @@ UserInformation::UserInformation(const MumbleProto::UserStats &msg, QWidget *p) resize(sizeHint()); qfCertificateFont = qlCertificate->font(); + + // Labels are not initialized properly here. We need to wait a tiny bit + // to make sure its contents are actually read. + QTimer::singleShot(0, [this]() { + qgbConnection->updateAccessibleText(); + qgbPing->updateAccessibleText(); + qgbUDP->updateAccessibleText(); + qgbBandwidth->updateAccessibleText(); + }); } unsigned int UserInformation::session() const { @@ -197,4 +206,9 @@ void UserInformation::update(const MumbleProto::UserStats &msg) { qliBandwidth->setVisible(false); qlBandwidth->setText(QString()); } + + qgbConnection->updateAccessibleText(); + qgbPing->updateAccessibleText(); + qgbUDP->updateAccessibleText(); + qgbBandwidth->updateAccessibleText(); } diff --git a/src/mumble/UserInformation.ui b/src/mumble/UserInformation.ui index e5b9a3c95..de6e229d2 100644 --- a/src/mumble/UserInformation.ui +++ b/src/mumble/UserInformation.ui @@ -15,7 +15,7 @@ - + Connection Information @@ -183,7 +183,7 @@ - + Ping Statistics @@ -314,7 +314,7 @@ - + UDP Network statistics @@ -541,7 +541,7 @@ - + Bandwidth @@ -597,6 +597,14 @@ + + + AccessibleQGroupBox + QGroupBox +
AccessibleQGroupBox.h
+ 1 +
+
diff --git a/src/mumble/widgets/AccessibleQGroupBox.cpp b/src/mumble/widgets/AccessibleQGroupBox.cpp index 68de27189..f7bccf5c7 100644 --- a/src/mumble/widgets/AccessibleQGroupBox.cpp +++ b/src/mumble/widgets/AccessibleQGroupBox.cpp @@ -5,15 +5,36 @@ #include "AccessibleQGroupBox.h" +#include "Accessibility.h" + #include -#include #include +#include AccessibleQGroupBox::AccessibleQGroupBox(QWidget *parent) : QGroupBox(parent) { setFocusPolicy(Qt::TabFocus); updateAccessibleText(); } +QString AccessibleQGroupBox::textAtPosition(QGridLayout *gridLayout, int y, int x) { + QLayoutItem *item = gridLayout->itemAtPosition(y, x); + if (!item) { + return ""; + } + + QLabel *label = qobject_cast< QLabel * >(item->widget()); + if (!label || !label->isVisible()) { + return ""; + } + + QString content = Mumble::Accessibility::removeHTMLTags(label->text()); + if (content.trimmed().isEmpty()) { + content = tr("empty"); + } + + return content; +} + void AccessibleQGroupBox::updateAccessibleText() { QString text; @@ -21,22 +42,22 @@ void AccessibleQGroupBox::updateAccessibleText() { // 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; + bool tableMode = gridLayout->itemAtPosition(0, 0) == nullptr; + + for (int y = tableMode ? 1 : 0; y < gridLayout->rowCount(); y++) { + for (int x = tableMode ? 1 : 0; x < gridLayout->columnCount(); x++) { + if (tableMode) { + text += textAtPosition(gridLayout, y, 0); + text += " "; + text += textAtPosition(gridLayout, 0, x); + text += ", "; + text += textAtPosition(gridLayout, y, x); + } else { + text += textAtPosition(gridLayout, y, x); } - text += " "; - - QString content = label->text(); - if (content.trimmed().isEmpty()) { - content = tr("empty"); - } - text += content; + text += ", "; } - text += ","; } setAccessibleDescription(text); return; @@ -48,25 +69,35 @@ void AccessibleQGroupBox::updateAccessibleText() { if (formLayout) { for (int y = 0; y < formLayout->rowCount(); y++) { QLabel *label; + QLayoutItem *item; - 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"); + item = formLayout->itemAt(y, QFormLayout::SpanningRole); + if (item) { + label = qobject_cast< QLabel * >(item->widget()); + if (label && label->isVisible()) { + text += Mumble::Accessibility::removeHTMLTags(label->text()) + ", "; + continue; + } + } + + item = formLayout->itemAt(y, QFormLayout::LabelRole); + if (item) { + label = qobject_cast< QLabel * >(item->widget()); + if (label && label->isVisible()) { + text += Mumble::Accessibility::removeHTMLTags(label->text()); + } + } + + item = formLayout->itemAt(y, QFormLayout::FieldRole); + if (item) { + label = qobject_cast< QLabel * >(item->widget()); + if (label && label->isVisible()) { + QString content = Mumble::Accessibility::removeHTMLTags(label->text()); + if (content.trimmed().isEmpty()) { + content = "empty"; + } + text += " " + content; } - text += " " + content; } text += ", "; @@ -80,11 +111,11 @@ void AccessibleQGroupBox::updateAccessibleText() { QObjectList childObjects = children(); for (int i = 0; i < childObjects.length(); i++) { QLabel *label = qobject_cast< QLabel * >(childObjects[i]); - if (!label) { + if (!label || !label->isVisible()) { continue; } - text += label->text() + ","; + text += label->text() + ", "; } setAccessibleDescription(text); } diff --git a/src/mumble/widgets/AccessibleQGroupBox.h b/src/mumble/widgets/AccessibleQGroupBox.h index 9e9a5de75..e0b16e55c 100644 --- a/src/mumble/widgets/AccessibleQGroupBox.h +++ b/src/mumble/widgets/AccessibleQGroupBox.h @@ -6,6 +6,7 @@ #ifndef MUMBLE_MUMBLE_ACCESSIBLEQGROUPBOX_H_ #define MUMBLE_MUMBLE_ACCESSIBLEQGROUPBOX_H_ +#include #include class AccessibleQGroupBox : public QGroupBox { @@ -14,6 +15,8 @@ class AccessibleQGroupBox : public QGroupBox { public: AccessibleQGroupBox(QWidget *parent); + QString textAtPosition(QGridLayout *gridLayout, int y, int x); + void updateAccessibleText(); };