Skip to content

Commit

Permalink
FIX(a11y): Add semantic description to sliders in Settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Hartmnt committed Jan 2, 2024
1 parent 2d69a14 commit 7280bf1
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/mumble/Accessibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include <queue>

#include <QDebug>

namespace Mumble {
namespace Accessibility {

Expand Down Expand Up @@ -150,6 +152,9 @@ namespace Accessibility {
// We set the value for the slider, any potential focus proxy,
// and the QAccessibleInterface. Orca will still read the
// raw slider value.

qDebug() << value;

QVariant newValue = QVariant(value);
slider->setFocus();

Expand Down
37 changes: 34 additions & 3 deletions src/mumble/AudioConfigDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "AudioConfigDialog.h"

#include "Accessibility.h"
#include "AudioInput.h"
#include "AudioOutput.h"
#include "AudioOutputSample.h"
Expand Down Expand Up @@ -287,8 +288,11 @@ void AudioInputDialog::save() const {
}

void AudioInputDialog::on_qsFrames_valueChanged(int v) {
qlFrames->setText(tr("%1 ms").arg((v == 1) ? 10 : (v - 1) * 20));
int val = (v == 1) ? 10 : (v - 1) * 20;
qlFrames->setText(tr("%1 ms").arg(val));
updateBitrate();

Mumble::Accessibility::setSliderSemanticValue(qsFrames, QString("%1 %2").arg(val).arg(tr("milliseconds")));
}

void AudioInputDialog::on_qsDoublePush_valueChanged(int v) {
Expand All @@ -299,21 +303,29 @@ void AudioInputDialog::on_qsDoublePush_valueChanged(int v) {
}

void AudioInputDialog::on_qsPTTHold_valueChanged(int v) {
if (v == 0)
if (v == 0) {
qlPTTHold->setText(tr("Off"));
else
Mumble::Accessibility::setSliderSemanticValue(qsPTTHold, tr("Off"));
} else {
qlPTTHold->setText(tr("%1 ms").arg(v));
Mumble::Accessibility::setSliderSemanticValue(qsPTTHold, QString("%1 %2").arg(v).arg(tr("milliseconds")));
}
}

void AudioInputDialog::on_qsTransmitHold_valueChanged(int v) {
float val = static_cast< float >(v * 10);
val = val / 1000.0f;
qlTransmitHold->setText(tr("%1 s").arg(val, 0, 'f', 2));
Mumble::Accessibility::setSliderSemanticValue(qsTransmitHold,
QString("%1 %2").arg(val, 0, 'f', 2).arg(tr("seconds")));
}

void AudioInputDialog::on_qsQuality_valueChanged(int v) {
qlQuality->setText(tr("%1 kb/s").arg(static_cast< float >(v) / 1000.0f, 0, 'f', 1));
updateBitrate();

Mumble::Accessibility::setSliderSemanticValue(
qsQuality, QString("%1 %2").arg(static_cast< float >(v) / 1000.0f, 0, 'f', 1).arg(tr("kilobit per second")));
}

void AudioInputDialog::on_qsSpeexNoiseSupStrength_valueChanged(int v) {
Expand All @@ -322,8 +334,11 @@ void AudioInputDialog::on_qsSpeexNoiseSupStrength_valueChanged(int v) {
if (v < 15) {
qlSpeexNoiseSupStrength->setText(tr("Off"));
pal.setColor(qlSpeexNoiseSupStrength->foregroundRole(), Qt::red);
Mumble::Accessibility::setSliderSemanticValue(qsSpeexNoiseSupStrength, tr("Off"));
} else {
qlSpeexNoiseSupStrength->setText(tr("-%1 dB").arg(v));
Mumble::Accessibility::setSliderSemanticValue(qsSpeexNoiseSupStrength,
QString("-%1 %2").arg(v).arg(tr("decibel")));
}
qlSpeexNoiseSupStrength->setPalette(pal);
}
Expand All @@ -332,6 +347,8 @@ void AudioInputDialog::on_qsAmp_valueChanged(int v) {
v = 18000 - v + 2000;
float d = 20000.0f / static_cast< float >(v);
qlAmp->setText(QString::fromLatin1("%1").arg(d, 0, 'f', 2));

Mumble::Accessibility::setSliderSemanticValue(qsAmp, QString("%1").arg(d, 0, 'f', 2));
}

void AudioInputDialog::updateBitrate() {
Expand Down Expand Up @@ -813,6 +830,7 @@ void AudioOutputDialog::on_qcbSystem_currentIndexChanged(int) {

void AudioOutputDialog::on_qsJitter_valueChanged(int v) {
qlJitter->setText(tr("%1 ms").arg(v * 10));
Mumble::Accessibility::setSliderSemanticValue(qsJitter, QString("%1 %2").arg(v * 10).arg(tr("milliseconds")));
}

void AudioOutputDialog::on_qsVolume_valueChanged(int v) {
Expand All @@ -824,22 +842,30 @@ void AudioOutputDialog::on_qsVolume_valueChanged(int v) {
qlVolume->setPalette(pal);

qlVolume->setText(tr("%1 %").arg(v));
Mumble::Accessibility::setSliderSemanticValue(qsVolume, Mumble::Accessibility::SliderAccesibilityMode::PERCENT,
"%");
}

void AudioOutputDialog::on_qsOtherVolume_valueChanged(int v) {
qlOtherVolume->setText(tr("%1 %").arg(v));
Mumble::Accessibility::setSliderSemanticValue(qsOtherVolume, Mumble::Accessibility::SliderAccesibilityMode::PERCENT,
"%");
}

void AudioOutputDialog::on_qsPacketDelay_valueChanged(int v) {
qlPacketDelay->setText(tr("%1 ms").arg(v));
Mumble::Accessibility::setSliderSemanticValue(qsPacketDelay, QString("%1 %2").arg(v).arg(tr("milliseconds")));
}

void AudioOutputDialog::on_qsPacketLoss_valueChanged(int v) {
qlPacketLoss->setText(tr("%1 %").arg(v));
Mumble::Accessibility::setSliderSemanticValue(qsPacketLoss, Mumble::Accessibility::SliderAccesibilityMode::PERCENT,
"%");
}

void AudioOutputDialog::on_qsDelay_valueChanged(int v) {
qlDelay->setText(tr("%1 ms").arg(v * 10));
Mumble::Accessibility::setSliderSemanticValue(qsDelay, QString("%1 %2").arg(v * 10).arg(tr("milliseconds")));
}

void AudioOutputDialog::on_qcbLoopback_currentIndexChanged(int v) {
Expand All @@ -864,6 +890,7 @@ void AudioOutputDialog::on_qsMinDistance_valueChanged(int value) {
void AudioOutputDialog::on_qsbMinimumDistance_valueChanged(double value) {
QSignalBlocker blocker(qsMinDistance);
qsMinDistance->setValue(value * 10);
Mumble::Accessibility::setSliderSemanticValue(qsMinDistance, QString("%1 %2").arg(value * 10).arg(tr("meters")));

// Ensure that max distance is always a least 1m larger than min distance
qsMaxDistance->setValue(std::max(qsMaxDistance->value(), static_cast< int >(value * 10) + 10));
Expand All @@ -879,6 +906,7 @@ void AudioOutputDialog::on_qsMaxDistance_valueChanged(int value) {
void AudioOutputDialog::on_qsbMaximumDistance_valueChanged(double value) {
QSignalBlocker blocker(qsMaxDistance);
qsMaxDistance->setValue(value * 10);
Mumble::Accessibility::setSliderSemanticValue(qsMaxDistance, QString("%1 %2").arg(value * 10).arg(tr("meters")));

// Ensure that min distance is always a least 1m less than max distance
qsMinDistance->setValue(std::min(qsMinDistance->value(), static_cast< int >(value * 10) - 10));
Expand All @@ -887,6 +915,8 @@ void AudioOutputDialog::on_qsbMaximumDistance_valueChanged(double value) {
void AudioOutputDialog::on_qsMinimumVolume_valueChanged(int value) {
QSignalBlocker blocker(qsbMinimumVolume);
qsbMinimumVolume->setValue(value);
Mumble::Accessibility::setSliderSemanticValue(qsMinimumVolume,
Mumble::Accessibility::SliderAccesibilityMode::PERCENT, "%");
}

void AudioOutputDialog::on_qsbMinimumVolume_valueChanged(int value) {
Expand All @@ -897,6 +927,7 @@ void AudioOutputDialog::on_qsbMinimumVolume_valueChanged(int value) {
void AudioOutputDialog::on_qsBloom_valueChanged(int value) {
QSignalBlocker blocker(qsbBloom);
qsbBloom->setValue(value);
Mumble::Accessibility::setSliderSemanticValue(qsBloom, Mumble::Accessibility::SliderAccesibilityMode::PERCENT, "%");
}

void AudioOutputDialog::on_qsbBloom_valueChanged(int value) {
Expand Down
2 changes: 2 additions & 0 deletions src/mumble/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ set(MUMBLE_SOURCES
"widgets/SearchDialogItemDelegate.h"
"widgets/SearchDialogTree.cpp"
"widgets/SearchDialogTree.h"
"widgets/SemanticSlider.cpp"
"widgets/SemanticSlider.h"


"${SHARED_SOURCE_DIR}/ACL.cpp"
Expand Down
5 changes: 5 additions & 0 deletions src/mumble/Log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "Log.h"

#include "Accessibility.h"
#include "AudioOutput.h"
#include "AudioOutputSample.h"
#include "AudioOutputToken.h"
Expand Down Expand Up @@ -370,14 +371,18 @@ void LogConfig::browseForAudioFile() {

void LogConfig::on_qsNotificationVolume_valueChanged(int value) {
qsbNotificationVolume->setValue(value);
Mumble::Accessibility::setSliderSemanticValue(qsNotificationVolume, QString("%1 %2").arg(value).arg(tr("decibel")));
}

void LogConfig::on_qsCueVolume_valueChanged(int value) {
qsbCueVolume->setValue(value);
Mumble::Accessibility::setSliderSemanticValue(qsCueVolume, QString("%1 %2").arg(value).arg(tr("decibel")));
}

void LogConfig::on_qsTTSVolume_valueChanged(int value) {
qsbTTSVolume->setValue(value);
Mumble::Accessibility::setSliderSemanticValue(qsTTSVolume, Mumble::Accessibility::SliderAccesibilityMode::PERCENT,
"%");
}

void LogConfig::on_qsbNotificationVolume_valueChanged(int value) {
Expand Down
5 changes: 5 additions & 0 deletions src/mumble/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
# include "AppNap.h"
#endif

#include <QAccessible>
#include <QtCore/QStandardPaths>
#include <QtCore/QUrlQuery>
#include <QtGui/QClipboard>
Expand All @@ -80,6 +81,8 @@
#include <QtWidgets/QToolTip>
#include <QtWidgets/QWhatsThis>

#include "widgets/SemanticSlider.h"

#ifdef Q_OS_WIN
# include <dbt.h>
#endif
Expand Down Expand Up @@ -197,6 +200,8 @@ MainWindow::MainWindow(QWidget *p)

QObject::connect(this, &MainWindow::serverSynchronized, Global::get().pluginManager,
&PluginManager::on_serverSynchronized);

QAccessible::installFactory(SemanticSlider::semanticSliderFactory);
}

void MainWindow::createActions() {
Expand Down
106 changes: 106 additions & 0 deletions src/mumble/widgets/SemanticSlider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2023-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 "SemanticSlider.h"

#include <QDebug>

SemanticAbstractSlider::SemanticAbstractSlider(QWidget *w, QAccessible::Role r)
: QAccessibleWidget(w, r)
{
Q_ASSERT(qobject_cast<QAbstractSlider *>(w));
}

void *SemanticAbstractSlider::interface_cast(QAccessible::InterfaceType t)
{
/**
if (t == QAccessible::ValueInterface) {
return static_cast<QAccessibleValueInterface*>(this);
}
**/
return QAccessibleWidget::interface_cast(t);
}

/**
QVariant SemanticAbstractSlider::currentValue() const
{
return abstractSlider()->value();
}
void SemanticAbstractSlider::setCurrentValue(const QVariant &value)
{
abstractSlider()->setValue(value.toInt());
}
QVariant SemanticAbstractSlider::maximumValue() const
{
return abstractSlider()->maximum();
}
QVariant SemanticAbstractSlider::minimumValue() const
{
return abstractSlider()->minimum();
}
QVariant SemanticAbstractSlider::minimumStepSize() const
{
return abstractSlider()->singleStep();
}
*/

QAbstractSlider *SemanticAbstractSlider::abstractSlider() const
{
return static_cast<QAbstractSlider *>(object());
}

SemanticSlider::SemanticSlider(QWidget *w)
: SemanticAbstractSlider(w), m_semanticValue("test 1.56 milliseconds")
{
Q_ASSERT(slider());
addControllingSignal(QLatin1String("valueChanged(int)"));
}

QSlider *SemanticSlider::slider() const
{
return qobject_cast<QSlider*>(object());
}

QString SemanticSlider::text(QAccessible::Text t) const
{
qDebug() << "text method" << t;
if (t == QAccessible::Name) {
qDebug() << "send text";
qDebug() << (SemanticAbstractSlider::text(t) + " " + m_semanticValue);
return SemanticAbstractSlider::text(t) + " " + m_semanticValue;
}

return SemanticAbstractSlider::text(t);
}

/**
QVariant SemanticSlider::currentValue() const
{
qDebug() << "current Value " << m_semanticValue;
return m_semanticValue;
}
**/

QAccessibleInterface *SemanticSlider::semanticSliderFactory(const QString &classname, QObject *object) {
QAccessibleInterface *interface = nullptr;

if(object && object->isWidgetType()) {
if (classname == QLatin1String("QSlider")) {
interface = new SemanticSlider(static_cast<QWidget *>(object));
qDebug() << "created interface";
} else if (classname == QLatin1String("QAbstractSlider")) {
qDebug() << "created abstract interface";
interface = new SemanticAbstractSlider(static_cast<QWidget *>(object));
} else {
qDebug() << "not interfaced";
}
}

return interface;
}
56 changes: 56 additions & 0 deletions src/mumble/widgets/SemanticSlider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2023-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_WIDGETS_SEMANTICSLIDER_H_
#define MUMBLE_MUMBLE_WIDGETS_SEMANTICSLIDER_H_

#include <QAccessible>
#include <QAccessibleValueInterface>
#include <QAccessibleWidget>

#include <QString>
#include <QWidget>
#include <QObject>
#include <QVariant>

#include <QtWidgets/QAbstractSlider>
#include <QtWidgets/QSlider>

class SemanticAbstractSlider: public QAccessibleWidget//, public QAccessibleValueInterface
{
public:
explicit SemanticAbstractSlider(QWidget *w, QAccessible::Role r = QAccessible::Slider);
void *interface_cast(QAccessible::InterfaceType t) override;

/**
QVariant currentValue() const override;
void setCurrentValue(const QVariant &value) override;
QVariant maximumValue() const override;
QVariant minimumValue() const override;
QVariant minimumStepSize() const override;
**/

protected:
QAbstractSlider *abstractSlider() const;
};

class SemanticSlider : public SemanticAbstractSlider
{
public:
explicit SemanticSlider(QWidget *w);
QString text(QAccessible::Text t) const override;
/**
QVariant currentValue() const override;
**/

static QAccessibleInterface *semanticSliderFactory(const QString &classname, QObject *object);

QString m_semanticValue;

protected:
QSlider *slider() const;
};

#endif

0 comments on commit 7280bf1

Please sign in to comment.