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
This commit is contained in:
Hartmnt 2023-01-06 15:56:23 +00:00
parent 6685fbf807
commit 3b6acda9df
8 changed files with 225 additions and 131 deletions

View File

@ -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) {

View File

@ -17,6 +17,8 @@
namespace Mumble {
namespace Accessibility {
QString removeHTMLTags(QString value);
void setDescriptionFromLabel(QWidget *widget, const QLabel *label);
void fixWizardButtonLabels(QWizard *wizard);

View File

@ -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) {

View File

@ -23,14 +23,14 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-201</y>
<y>0</y>
<width>541</width>
<height>726</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="qgbServerInformation">
<widget class="AccessibleQGroupBox" name="qgbServerInformation">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -90,7 +90,7 @@
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;b&gt;Users&lt;/b&gt;:</string>
<string>&lt;b&gt;Users:&lt;/b&gt;</string>
</property>
</widget>
</item>
@ -189,7 +189,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbAudioBandwidth">
<widget class="AccessibleQGroupBox" name="qgbAudioBandwidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -378,6 +378,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
@ -482,77 +485,109 @@
<attribute name="title">
<string>TCP (Control)</string>
</attribute>
<layout class="QFormLayout" name="formLayout_2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>&lt;b&gt;TLS version:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="connection_tcp_tls">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;TLS&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<widget class="AccessibleQGroupBox" name="qgbTCPParameters">
<property name="title">
<string>TCP Parameters</string>
</property>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>&lt;b&gt;TLS version:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="connection_tcp_tls">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;TLS&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>&lt;b&gt;Cipher suite:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="connection_tcp_cipher">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;Cipher&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>&lt;b&gt;Avg. latency:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="connection_tcp_latency">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;latency&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="toolTip">
<string>Whether the connection supports perfect forward secrecy (PFS).</string>
</property>
<property name="text">
<string>&lt;b&gt;PFS:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="connection_tcp_forwardSecrecy">
<property name="text">
<string>&lt;forward secrecy&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>&lt;b&gt;Cipher suite:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="connection_tcp_cipher">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;Cipher&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>&lt;b&gt;Avg. latency:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="connection_tcp_latency">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;latency&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -568,29 +603,6 @@
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="toolTip">
<string>Whether the connection supports perfect forward secrecy (PFS).</string>
</property>
<property name="text">
<string>&lt;b&gt;PFS:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="connection_tcp_forwardSecrecy">
<property name="text">
<string>&lt;forward secrecy&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
@ -648,6 +660,14 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AccessibleQGroupBox</class>
<extends>QGroupBox</extends>
<header>AccessibleQGroupBox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -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();
}

View File

@ -15,7 +15,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="qgbConnection">
<widget class="AccessibleQGroupBox" name="qgbConnection">
<property name="title">
<string>Connection Information</string>
</property>
@ -183,7 +183,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbPing">
<widget class="AccessibleQGroupBox" name="qgbPing">
<property name="title">
<string>Ping Statistics</string>
</property>
@ -314,7 +314,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbUDP">
<widget class="AccessibleQGroupBox" name="qgbUDP">
<property name="title">
<string>UDP Network statistics</string>
</property>
@ -541,7 +541,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="qgbBandwidth">
<widget class="AccessibleQGroupBox" name="qgbBandwidth">
<property name="title">
<string comment="GroupBox">Bandwidth</string>
</property>
@ -597,6 +597,14 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AccessibleQGroupBox</class>
<extends>QGroupBox</extends>
<header>AccessibleQGroupBox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -5,15 +5,36 @@
#include "AccessibleQGroupBox.h"
#include "Accessibility.h"
#include <QFormLayout>
#include <QGridLayout>
#include <QLabel>
#include <QLayoutItem>
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);
}

View File

@ -6,6 +6,7 @@
#ifndef MUMBLE_MUMBLE_ACCESSIBLEQGROUPBOX_H_
#define MUMBLE_MUMBLE_ACCESSIBLEQGROUPBOX_H_
#include <QGridLayout>
#include <QGroupBox>
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();
};