diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp
index f26a558c6..862d746f7 100644
--- a/UI/adv-audio-control.cpp
+++ b/UI/adv-audio-control.cpp
@@ -22,7 +22,7 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
{
QHBoxLayout *hlayout;
signal_handler_t *handler = obs_source_get_signal_handler(source);
- const char *sourceName = obs_source_get_name(source);
+ QString sourceName = QT_UTF8(obs_source_get_name(source));
float vol = obs_source_get_volume(source);
uint32_t flags = obs_source_get_flags(source);
uint32_t mixers = obs_source_get_audio_mixers(source);
@@ -90,7 +90,7 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
iconLabel->setFixedSize(16, 16);
iconLabel->setStyleSheet("background: none");
- nameLabel->setText(QT_UTF8(sourceName));
+ nameLabel->setText(sourceName);
nameLabel->setAlignment(Qt::AlignVCenter);
bool isActive = obs_source_active(source);
@@ -109,15 +109,21 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
volume->setSuffix(" dB");
volume->setValue(obs_mul_to_db(vol));
volume->setFixedWidth(100);
+ volume->setAccessibleName(
+ QTStr("Basic.AdvAudio.VolumeSource").arg(sourceName));
- if (volume->value() < MIN_DB)
+ if (volume->value() < MIN_DB) {
volume->setSpecialValueText("-inf dB");
+ volume->setAccessibleDescription("-inf dB");
+ }
percent->setMinimum(0);
percent->setMaximum(2000);
percent->setSuffix("%");
percent->setValue((int)(obs_source_get_volume(source) * 100.0f));
percent->setFixedWidth(100);
+ percent->setAccessibleName(
+ QTStr("Basic.AdvAudio.VolumeSource").arg(sourceName));
stackedWidget->addWidget(volume);
stackedWidget->addWidget(percent);
@@ -128,6 +134,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
SetVolumeWidget(volType);
forceMono->setChecked((flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0);
+ forceMono->setAccessibleName(
+ QTStr("Basic.AdvAudio.MonoSource").arg(sourceName));
forceMonoContainer->layout()->addWidget(forceMono);
forceMonoContainer->layout()->setAlignment(forceMono, Qt::AlignVCenter);
@@ -138,6 +146,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
balance->setMaximum(100);
balance->setTickPosition(QSlider::TicksAbove);
balance->setTickInterval(50);
+ balance->setAccessibleName(
+ QTStr("Basic.AdvAudio.BalanceSource").arg(sourceName));
const char *speakers =
config_get_string(main->Config(), "Audio", "ChannelSetup");
@@ -156,6 +166,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
syncOffset->setSuffix(" ms");
syncOffset->setValue(int(cur_sync / NSEC_PER_MSEC));
syncOffset->setFixedWidth(100);
+ syncOffset->setAccessibleName(
+ QTStr("Basic.AdvAudio.SyncOffsetSource").arg(sourceName));
int idx;
#if defined(_WIN32) || defined(__APPLE__) || HAVE_PULSEAUDIO
@@ -168,20 +180,34 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_)
int mt = (int)obs_source_get_monitoring_type(source);
idx = monitoringType->findData(mt);
monitoringType->setCurrentIndex(idx);
+ monitoringType->setAccessibleName(
+ QTStr("Basic.AdvAudio.MonitoringSource").arg(sourceName));
#endif
mixer1->setText("1");
mixer1->setChecked(mixers & (1 << 0));
+ mixer1->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track1"));
mixer2->setText("2");
mixer2->setChecked(mixers & (1 << 1));
+ mixer2->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track2"));
mixer3->setText("3");
mixer3->setChecked(mixers & (1 << 2));
+ mixer3->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track3"));
mixer4->setText("4");
mixer4->setChecked(mixers & (1 << 3));
+ mixer4->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track4"));
mixer5->setText("5");
mixer5->setChecked(mixers & (1 << 4));
+ mixer5->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track5"));
mixer6->setText("6");
mixer6->setChecked(mixers & (1 << 5));
+ mixer6->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track6"));
speaker_layout sl = obs_source_get_speaker_layout(source);
diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini
index 4c445023e..826b19f1b 100644
--- a/UI/data/locale/en-US.ini
+++ b/UI/data/locale/en-US.ini
@@ -408,8 +408,8 @@ Deinterlacing.TopFieldFirst="Top Field First"
Deinterlacing.BottomFieldFirst="Bottom Field First"
# volume control accessibility text
-VolControl.SliderUnmuted="Volume slider for '%1': %2"
-VolControl.SliderMuted="Volume slider for '%1': %2 (currently muted)"
+VolControl.SliderUnmuted="Volume slider for '%1':"
+VolControl.SliderMuted="Volume slider for '%1': (currently muted)"
VolControl.Mute="Mute '%1'"
VolControl.Properties="Properties for '%1'"
@@ -449,6 +449,12 @@ Basic.SourceSelect.CreateNew="Create new"
Basic.SourceSelect.AddExisting="Add Existing"
Basic.SourceSelect.AddVisible="Make source visible"
+# source box
+Basic.Main.Sources.Visibility="Visibility"
+Basic.Main.Sources.VisibilityDescription="Controls the visibility of '%1' in the canvas"
+Basic.Main.Sources.Lock="Lock"
+Basic.Main.Sources.LockDescription="Locks the position and scale of '%1' in the canvas"
+
# properties window
Basic.PropertiesWindow="Properties for '%1'"
Basic.PropertiesWindow.AutoSelectFormat="%1 (autoselect: %2)"
@@ -784,6 +790,7 @@ Basic.Settings.Output.Adv.TwitchVodTrack="Twitch VOD Track"
# basic mode 'output' settings - advanced section - recording subsection
Basic.Settings.Output.Adv.Recording="Recording"
+Basic.Settings.Output.Adv.Recording.RecType="Recording Type"
Basic.Settings.Output.Adv.Recording.Type="Type"
Basic.Settings.Output.Adv.Recording.Type.Standard="Standard"
Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Custom Output (FFmpeg)"
@@ -919,13 +926,18 @@ Basic.AdvAudio="Advanced Audio Properties"
Basic.AdvAudio.ActiveOnly="Active Sources Only"
Basic.AdvAudio.Name="Name"
Basic.AdvAudio.Volume="Volume"
+Basic.AdvAudio.VolumeSource="Volume for '%1'"
Basic.AdvAudio.Mono="Mono"
+Basic.AdvAudio.MonoSource="Mono Downmix for '%1'"
Basic.AdvAudio.Balance="Balance"
+Basic.AdvAudio.BalanceSource="Balance for '%1'"
Basic.AdvAudio.SyncOffset="Sync Offset"
+Basic.AdvAudio.SyncOffsetSource="Sync Offset for '%1'"
Basic.AdvAudio.Monitoring="Audio Monitoring"
Basic.AdvAudio.Monitoring.None="Monitor Off"
Basic.AdvAudio.Monitoring.MonitorOnly="Monitor Only (mute output)"
Basic.AdvAudio.Monitoring.Both="Monitor and Output"
+Basic.AdvAudio.MonitoringSource="Audio Monitoring for '%1'"
Basic.AdvAudio.AudioTracks="Tracks"
# basic mode 'hotkeys' settings
diff --git a/UI/forms/OBSBasicFilters.ui b/UI/forms/OBSBasicFilters.ui
index 572a31f9d..272dfc21e 100644
--- a/UI/forms/OBSBasicFilters.ui
+++ b/UI/forms/OBSBasicFilters.ui
@@ -104,6 +104,9 @@
addIconSmall
+
+ Add
+
-
@@ -130,6 +133,9 @@
removeIconSmall
+
+ Remove
+
-
@@ -156,6 +162,9 @@
upArrowIconSmall
+
+ MoveUp
+
-
@@ -182,6 +191,9 @@
downArrowIconSmall
+
+ MoveDown
+
@@ -280,6 +292,9 @@
addIconSmall
+
+ Add
+
-
@@ -306,6 +321,9 @@
removeIconSmall
+
+ Remove
+
-
diff --git a/UI/hotkey-edit.cpp b/UI/hotkey-edit.cpp
index 37e5a7d4d..2ee0cc39b 100644
--- a/UI/hotkey-edit.cpp
+++ b/UI/hotkey-edit.cpp
@@ -293,11 +293,13 @@ void OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)
auto add = new QPushButton;
add->setProperty("themeID", "addIconSmall");
+ add->setToolTip(QTStr("Add"));
add->setFixedSize(24, 24);
add->setFlat(true);
auto remove = new QPushButton;
remove->setProperty("themeID", "removeIconSmall");
+ remove->setToolTip(QTStr("Remove"));
remove->setEnabled(removeButtons.size() > 0);
remove->setFixedSize(24, 24);
remove->setFlat(true);
diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp
index 58dcef2f7..ab7ee45ec 100644
--- a/UI/obs-app.cpp
+++ b/UI/obs-app.cpp
@@ -35,10 +35,12 @@
#include
#include
#include
+#include
#include "qt-wrappers.hpp"
#include "obs-app.hpp"
#include "log-viewer.hpp"
+#include "slider-ignorewheel.hpp"
#include "window-basic-main.hpp"
#include "window-basic-settings.hpp"
#include "crash-report.hpp"
@@ -1901,6 +1903,17 @@ static auto ProfilerFree = [](void *) {
profiler_free();
};
+QAccessibleInterface *accessibleFactory(const QString &classname,
+ QObject *object)
+{
+ if (classname == QLatin1String("VolumeSlider") && object &&
+ object->isWidgetType())
+ return new VolumeAccessibleInterface(
+ static_cast(object));
+
+ return nullptr;
+}
+
static const char *run_program_init = "run_program_init";
static int run_program(fstream &logFile, int argc, char *argv[])
{
@@ -1932,6 +1945,8 @@ static int run_program(fstream &logFile, int argc, char *argv[])
OBSApp program(argc, argv, profilerNameStore.get());
try {
+ QAccessible::installFactory(accessibleFactory);
+
bool created_log = false;
program.AppInit();
diff --git a/UI/slider-ignorewheel.cpp b/UI/slider-ignorewheel.cpp
index 8203c81f9..11023d638 100644
--- a/UI/slider-ignorewheel.cpp
+++ b/UI/slider-ignorewheel.cpp
@@ -1,4 +1,5 @@
#include "slider-ignorewheel.hpp"
+#include "volume-control.hpp"
SliderIgnoreScroll::SliderIgnoreScroll(QWidget *parent) : QSlider(parent)
{
@@ -20,3 +21,77 @@ void SliderIgnoreScroll::wheelEvent(QWheelEvent *event)
else
QSlider::wheelEvent(event);
}
+
+VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent)
+ : SliderIgnoreScroll(parent)
+{
+ fad = fader;
+}
+
+VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
+ QWidget *parent)
+ : SliderIgnoreScroll(orientation, parent)
+{
+ fad = fader;
+}
+
+VolumeAccessibleInterface::VolumeAccessibleInterface(QWidget *w)
+ : QAccessibleWidget(w)
+{
+}
+
+VolumeSlider *VolumeAccessibleInterface::slider() const
+{
+ return qobject_cast(object());
+}
+
+QString VolumeAccessibleInterface::text(QAccessible::Text t) const
+{
+ if (slider()->isVisible()) {
+ switch (t) {
+ case QAccessible::Text::Value:
+ return currentValue().toString();
+ default:
+ break;
+ }
+ }
+ return QAccessibleWidget::text(t);
+}
+
+QVariant VolumeAccessibleInterface::currentValue() const
+{
+ QString text;
+ float db = obs_fader_get_db(slider()->fad);
+
+ if (db < -96.0f)
+ text = "-inf dB";
+ else
+ text = QString::number(db, 'f', 1).append(" dB");
+
+ return text;
+}
+
+void VolumeAccessibleInterface::setCurrentValue(const QVariant &value)
+{
+ slider()->setValue(value.toInt());
+}
+
+QVariant VolumeAccessibleInterface::maximumValue() const
+{
+ return slider()->maximum();
+}
+
+QVariant VolumeAccessibleInterface::minimumValue() const
+{
+ return slider()->minimum();
+}
+
+QVariant VolumeAccessibleInterface::minimumStepSize() const
+{
+ return slider()->singleStep();
+}
+
+QAccessible::Role VolumeAccessibleInterface::role() const
+{
+ return QAccessible::Role::Slider;
+}
diff --git a/UI/slider-ignorewheel.hpp b/UI/slider-ignorewheel.hpp
index f5c7e5d72..40d04487c 100644
--- a/UI/slider-ignorewheel.hpp
+++ b/UI/slider-ignorewheel.hpp
@@ -1,8 +1,10 @@
#pragma once
+#include "obs.hpp"
#include
#include
#include
+#include
class SliderIgnoreScroll : public QSlider {
Q_OBJECT
@@ -15,3 +17,35 @@ public:
protected:
virtual void wheelEvent(QWheelEvent *event) override;
};
+
+class VolumeSlider : public SliderIgnoreScroll {
+ Q_OBJECT
+
+public:
+ obs_fader_t *fad;
+
+ VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
+ VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation,
+ QWidget *parent = nullptr);
+};
+
+class VolumeAccessibleInterface : public QAccessibleWidget {
+
+public:
+ VolumeAccessibleInterface(QWidget *w);
+
+ QVariant currentValue() const;
+ void setCurrentValue(const QVariant &value);
+
+ QVariant maximumValue() const;
+ QVariant minimumValue() const;
+
+ QVariant minimumStepSize() const;
+
+private:
+ VolumeSlider *slider() const;
+
+protected:
+ virtual QAccessible::Role role() const override;
+ virtual QString text(QAccessible::Text t) const override;
+};
diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp
index d6ee35b64..34994f9f4 100644
--- a/UI/source-tree.cpp
+++ b/UI/source-tree.cpp
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include
#include
@@ -84,12 +85,18 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_)
vis->setFixedSize(16, 16);
vis->setChecked(obs_sceneitem_visible(sceneitem));
vis->setStyleSheet("background: none");
+ vis->setAccessibleName(QTStr("Basic.Main.Sources.Visibility"));
+ vis->setAccessibleDescription(
+ QTStr("Basic.Main.Sources.VisibilityDescription").arg(name));
lock = new LockedCheckBox();
lock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
lock->setFixedSize(16, 16);
lock->setChecked(obs_sceneitem_locked(sceneitem));
lock->setStyleSheet("background: none");
+ lock->setAccessibleName(QTStr("Basic.Main.Sources.Lock"));
+ lock->setAccessibleDescription(
+ QTStr("Basic.Main.Sources.LockDescription").arg(name));
label = new QLabel(QT_UTF8(name));
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp
index 7543d153e..cfc64b6c8 100644
--- a/UI/volume-control.cpp
+++ b/UI/volume-control.cpp
@@ -89,7 +89,7 @@ void VolControl::updateText()
: "VolControl.SliderUnmuted";
QString sourceName = obs_source_get_name(source);
- QString accText = QTStr(accTextLookup).arg(sourceName, db);
+ QString accText = QTStr(accTextLookup).arg(sourceName);
slider->setAccessibleName(accText);
}
@@ -161,7 +161,7 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
QHBoxLayout *meterLayout = new QHBoxLayout;
volMeter = new VolumeMeter(nullptr, obs_volmeter, true);
- slider = new SliderIgnoreScroll(Qt::Vertical);
+ slider = new VolumeSlider(obs_fader, Qt::Vertical);
nameLayout->setAlignment(Qt::AlignCenter);
meterLayout->setAlignment(Qt::AlignCenter);
@@ -205,7 +205,7 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
QHBoxLayout *botLayout = new QHBoxLayout;
volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
- slider = new SliderIgnoreScroll(Qt::Horizontal);
+ slider = new VolumeSlider(obs_fader, Qt::Horizontal);
textLayout->setContentsMargins(0, 0, 0, 0);
textLayout->addWidget(nameLabel);
diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp
index fd081ce10..70237ed8c 100644
--- a/UI/window-basic-settings.cpp
+++ b/UI/window-basic-settings.cpp
@@ -817,6 +817,56 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
InitStreamPage();
LoadSettings(false);
+ ui->advOutTrack1->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track1"));
+ ui->advOutTrack2->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track2"));
+ ui->advOutTrack3->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track3"));
+ ui->advOutTrack4->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track4"));
+ ui->advOutTrack5->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track5"));
+ ui->advOutTrack6->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track6"));
+
+ ui->advOutRecTrack1->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track1"));
+ ui->advOutRecTrack2->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track2"));
+ ui->advOutRecTrack3->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track3"));
+ ui->advOutRecTrack4->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track4"));
+ ui->advOutRecTrack5->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track5"));
+ ui->advOutRecTrack6->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track6"));
+
+ ui->advOutFFTrack1->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track1"));
+ ui->advOutFFTrack2->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track2"));
+ ui->advOutFFTrack3->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track3"));
+ ui->advOutFFTrack4->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track4"));
+ ui->advOutFFTrack5->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track5"));
+ ui->advOutFFTrack6->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Audio.Track6"));
+
+ ui->snappingEnabled->setAccessibleName(
+ QTStr("Basic.Settings.General.Snapping"));
+ ui->systemTrayEnabled->setAccessibleName(
+ QTStr("Basic.Settings.General.SysTray"));
+ ui->label_31->setAccessibleName(
+ QTStr("Basic.Settings.Output.Adv.Recording.RecType"));
+ ui->streamDelayEnable->setAccessibleName(
+ QTStr("Basic.Settings.Advanced.StreamDelay"));
+ ui->reconnectEnable->setAccessibleName(
+ QTStr("Basic.Settings.Output.Reconnect"));
+
// Add warning checks to advanced output recording section controls
connect(ui->advOutRecTrack1, SIGNAL(clicked()), this,
SLOT(AdvOutRecCheckWarnings()));