From 52ae5fc4bd518c67edd8ab94121ace05d9076892 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sat, 20 Apr 2024 19:58:20 -0400 Subject: [PATCH] UI: Update volume meter appearance --- UI/data/themes/Yami.obt | 94 ++++++++++++++++++-- UI/volume-control.cpp | 167 +++++++++++++++++++++++++++--------- UI/volume-control.hpp | 13 ++- libobs/obs-audio-controls.c | 7 +- libobs/obs-audio-controls.h | 4 + 5 files changed, 237 insertions(+), 48 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index e6056dcd6..cf9230b77 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -129,6 +129,10 @@ --spinbox_button_height: calc(var(--input_height) - 2px); + --volume_slider: calc(calc(6px + var(--font_base_value)) / 2); + --volume_slider_box: calc(var(--volume_slider) * 4); + --volume_slider_label: calc(var(--volume_slider) * 6); + --scrollbar_size: 12px; /* Inputs / Controls */ @@ -295,15 +299,10 @@ SourceTree QWidget { /* Misc */ -QAbstractItemView, -QStackedWidget#stackedMixerArea QWidget { +QAbstractItemView { background-color: var(--bg_base); } -QStackedWidget#stackedMixerArea QScrollBar { - background-color: var(--grey6); -} - QToolTip { background-color: var(--bg_base); color: var(--text); @@ -1210,6 +1209,89 @@ QSlider::handle:disabled { background-color: var(--button_bg_down); } +#stackedMixerArea { + border: none; + padding: 0px; + border-bottom: 1px solid #3c404b; +} + +VolControl #volLabel { + padding: var(--padding_base) 0px var(--padding_base); + text-align: center; + font-size: var(--font_base); + color: var(--text_muted); +} + +/* Horizontal Mixer */ +#hMixerScrollArea VolControl { + padding: 0px var(--padding_large); + border-bottom: 1px solid var(--border_color); +} + +#hMixerScrollArea VolControl QSlider { + margin: 0px 0px; +} + +#hMixerScrollArea VolControl QSlider::groove:horizontal { + background: var(--bg_window); + height: var(--volume_slider); +} + +#hMixerScrollArea VolControl QPushButton { + margin-right: var(--padding_xlarge); +} + +/* Vertical Mixer */ +#stackedMixerArea QScrollBar:vertical { + border-left: 1px solid var(--border_color); +} + +#vMixerScrollArea VolControl { + padding: var(--padding_large) 0px var(--padding_base); + border-right: 1px solid var(--border_color); +} + +#vMixerScrollArea VolControl QSlider { + width: var(--volume_slider_box); +} + +#vMixerScrollArea VolControl #volLabel { + padding: var(--padding_base) 0px var(--padding_base); + min-width: var(--volume_slider_label); + max-width: var(--volume_slider_label); + margin-right: 0; + text-align: center; +} + +#vMixerScrollArea VolControl QSlider::groove:vertical { + background: var(--bg_window); + width: var(--volume_slider); +} + +#vMixerScrollArea VolControl #volMeterFrame { + padding: var(--padding_large) var(--padding_xlarge); +} + +#vMixerScrollArea VolControl QLabel { + padding: 0px var(--padding_large); +} + +#vMixerScrollArea VolControl QPushButton { + margin-right: var(--padding_xlarge); +} + +#vMixerScrollArea VolControl MuteCheckBox { + margin-left: var(--padding_xlarge); +} + +VolControl { + background: var(--bg_base); +} + +VolumeMeter { + background: transparent; +} + VolumeMeter { qproperty-backgroundNominalColor: var(--green5); qproperty-backgroundWarningColor: var(--yellow5); diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index a8d29f3ca..06cb587ed 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -246,6 +246,9 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) volLabel = new QLabel(); mute = new MuteCheckBox(); + volLabel->setObjectName("volLabel"); + volLabel->setAlignment(Qt::AlignCenter); + QString sourceName = obs_source_get_name(source); setObjectName(sourceName); @@ -262,24 +265,28 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) } QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->setContentsMargins(4, 4, 4, 4); - mainLayout->setSpacing(2); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); if (vertical) { QHBoxLayout *nameLayout = new QHBoxLayout; QHBoxLayout *controlLayout = new QHBoxLayout; QHBoxLayout *volLayout = new QHBoxLayout; + QFrame *meterFrame = new QFrame; QHBoxLayout *meterLayout = new QHBoxLayout; volMeter = new VolumeMeter(nullptr, obs_volmeter, true); slider = new VolumeSlider(obs_fader, Qt::Vertical); slider->setLayoutDirection(Qt::LeftToRight); + slider->setDisplayTicks(true); nameLayout->setAlignment(Qt::AlignCenter); meterLayout->setAlignment(Qt::AlignCenter); controlLayout->setAlignment(Qt::AlignCenter); volLayout->setAlignment(Qt::AlignCenter); + meterFrame->setObjectName("volMeterFrame"); + nameLayout->setContentsMargins(0, 0, 0, 0); nameLayout->setSpacing(0); nameLayout->addWidget(nameLabel); @@ -287,28 +294,38 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) controlLayout->setContentsMargins(0, 0, 0, 0); controlLayout->setSpacing(0); + controlLayout->setAlignment(mute, Qt::AlignVCenter); + // Add Headphone (audio monitoring) widget here + controlLayout->addWidget(mute); + controlLayout->addItem( + new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum)); + if (showConfig) { controlLayout->addWidget(config); controlLayout->setAlignment(config, Qt::AlignVCenter); } - controlLayout->addItem(new QSpacerItem(3, 0)); - // Add Headphone (audio monitoring) widget here - controlLayout->addWidget(mute); - controlLayout->setAlignment(mute, Qt::AlignVCenter); - meterLayout->setContentsMargins(0, 0, 0, 0); meterLayout->setSpacing(0); - meterLayout->addWidget(volMeter); meterLayout->addWidget(slider); + meterLayout->addItem( + new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum)); + meterLayout->addWidget(volMeter); + + meterFrame->setLayout(meterLayout); volLayout->setContentsMargins(0, 0, 0, 0); volLayout->setSpacing(0); volLayout->addWidget(volLabel); + volLayout->addItem( + new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum)); mainLayout->addItem(nameLayout); mainLayout->addItem(volLayout); - mainLayout->addItem(meterLayout); + mainLayout->addWidget(meterFrame); mainLayout->addItem(controlLayout); volMeter->setFocusProxy(slider); @@ -316,17 +333,22 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) // Default size can cause clipping of long names in vertical layout. QFont font = nameLabel->font(); QFontInfo info(font); - font.setPointSizeF(0.8 * info.pointSizeF()); nameLabel->setFont(font); setMaximumWidth(110); } else { QHBoxLayout *textLayout = new QHBoxLayout; + QFrame *meterFrame = new QFrame; + QHBoxLayout *meterLayout = new QHBoxLayout; QHBoxLayout *botLayout = new QHBoxLayout; volMeter = new VolumeMeter(nullptr, obs_volmeter, false); + volMeter->setSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred); + slider = new VolumeSlider(obs_fader, Qt::Horizontal); slider->setLayoutDirection(Qt::LeftToRight); + slider->setDisplayTicks(true); textLayout->setContentsMargins(0, 0, 0, 0); textLayout->addWidget(nameLabel); @@ -334,20 +356,28 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) textLayout->setAlignment(nameLabel, Qt::AlignLeft); textLayout->setAlignment(volLabel, Qt::AlignRight); + meterFrame->setObjectName("volMeterFrame"); + meterFrame->setLayout(meterLayout); + + meterLayout->setContentsMargins(0, 0, 0, 0); + meterLayout->setSpacing(0); + + if (showConfig) { + meterLayout->addWidget(config); + meterLayout->setAlignment(config, Qt::AlignVCenter); + } + meterLayout->addWidget(volMeter); + botLayout->setContentsMargins(0, 0, 0, 0); - botLayout->setSpacing(5); - botLayout->addWidget(slider); + botLayout->setSpacing(0); botLayout->addWidget(mute); + botLayout->addWidget(slider); + botLayout->setAlignment(slider, Qt::AlignVCenter); botLayout->setAlignment(mute, Qt::AlignVCenter); - if (showConfig) { - botLayout->addWidget(config); - botLayout->setAlignment(config, Qt::AlignVCenter); - } - mainLayout->addItem(textLayout); - mainLayout->addWidget(volMeter); + mainLayout->addWidget(meterFrame); mainLayout->addItem(botLayout); volMeter->setFocusProxy(slider); @@ -915,6 +945,10 @@ inline void VolumeMeter::doLayout() { QMutexLocker locker(&dataMutex); + if (displayNrAudioChannels) { + int meterSize = std::floor(22 / displayNrAudioChannels); + setMeterThickness(std::clamp(meterSize, 3, 7)); + } recalculateLayout = false; tickFont = font(); @@ -928,14 +962,14 @@ inline void VolumeMeter::doLayout() // and a few pixels before the fader. QRect scaleBounds = metrics.boundingRect("-88"); setMinimumSize(displayNrAudioChannels * (meterThickness + 1) - - 1 + 4 + scaleBounds.width() + 2, - 130); + 1 + 10 + scaleBounds.width() + 2, + 100); } else { // Each meter channel is meterThickness pixels high, plus one pixel // between channels, but not after the last. // Add 4 pixels for ticks, and space high enough to hold our label in // this font, presuming that digits don't have descenders. - setMinimumSize(130, + setMinimumSize(100, displayNrAudioChannels * (meterThickness + 1) - 1 + 4 + metrics.capHeight()); } @@ -1085,14 +1119,6 @@ void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width) painter.drawLine(position, y, position, y + 2); } - - // Draw minor tick lines. - painter.setPen(minorTickColor); - for (int i = 0; i >= minimumLevel; i--) { - int position = int(x + width - (i * scale) - 1); - if (i % 5 != 0) - painter.drawLine(position, y, position, y + 1); - } } void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height) @@ -1110,24 +1136,16 @@ void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height) // Center the number on the tick, but don't overflow if (i == 0) { - painter.drawText(x + 6, position + metrics.capHeight(), + painter.drawText(x + 10, position + metrics.capHeight(), str); } else { - painter.drawText(x + 4, + painter.drawText(x + 8, position + (metrics.capHeight() / 2), str); } painter.drawLine(x, position, x + 2, position); } - - // Draw minor tick lines. - painter.setPen(minorTickColor); - for (int i = 0; i >= minimumLevel; i--) { - int position = y + int(i * scale) + METER_PADDING; - if (i % 5 != 0) - painter.drawLine(x, position, x + 1, position); - } } #define CLIP_FLASH_DURATION_MS 1000 @@ -1518,6 +1536,77 @@ VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, fad = fader; } +bool VolumeSlider::getDisplayTicks() const +{ + return displayTicks; +} + +void VolumeSlider::setDisplayTicks(bool display) +{ + displayTicks = display; +} + +void VolumeSlider::paintEvent(QPaintEvent *event) +{ + if (!getDisplayTicks()) { + QSlider::paintEvent(event); + return; + } + + QPainter painter(this); + QColor *tickColor = new QColor; + tickColor->setRgb(91, 98, 115, 255); + + obs_fader_conversion_t fader_db_to_def = obs_fader_db_to_def(fad); + + QStyleOptionSlider opt; + initStyleOption(&opt); + + QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, + QStyle::SC_SliderGroove, this); + QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, + QStyle::SC_SliderHandle, this); + + if (orientation() == Qt::Horizontal) { + const int sliderWidth = groove.width() - handle.width(); + + float tickLength = groove.height() * 1.5; + tickLength = std::max((int)tickLength + groove.height(), + 8 + groove.height()); + + float yPos = groove.center().y() - (tickLength / 2) + 1; + + for (int db = -10; db >= -90; db -= 10) { + float tickValue = fader_db_to_def(db); + + float xPos = groove.left() + (tickValue * sliderWidth) + + (handle.width() / 2); + painter.fillRect(xPos, yPos, 1, tickLength, *tickColor); + } + } + + if (orientation() == Qt::Vertical) { + const int sliderHeight = groove.height() - handle.height(); + + float tickLength = groove.width() * 1.5; + tickLength = std::max((int)tickLength + groove.width(), + 8 + groove.width()); + + float xPos = groove.center().x() - (tickLength / 2) + 1; + + for (int db = -10; db >= -96; db -= 10) { + float tickValue = fader_db_to_def(db); + + float yPos = groove.height() + groove.top() - + (tickValue * sliderHeight) - + (handle.height() / 2); + painter.fillRect(xPos, yPos, tickLength, 1, *tickColor); + } + } + + QSlider::paintEvent(event); +} + VolumeAccessibleInterface::VolumeAccessibleInterface(QWidget *w) : QAccessibleWidget(w) { diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 5a3efd5de..7b520e79a 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -12,6 +12,7 @@ class QPushButton; class VolumeMeterTimer; +class VolumeSlider; class VolumeMeter : public QWidget { Q_OBJECT @@ -276,7 +277,7 @@ class VolumeSlider; class MuteCheckBox; class OBSSourceLabel; -class VolControl : public QWidget { +class VolControl : public QFrame { Q_OBJECT private: @@ -342,6 +343,16 @@ public: VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr); VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr); + + bool getDisplayTicks() const; + void setDisplayTicks(bool display); + +private: + bool displayTicks = false; + QColor tickColor; + +protected: + virtual void paintEvent(QPaintEvent *event) override; }; class VolumeAccessibleInterface : public QAccessibleWidget { diff --git a/libobs/obs-audio-controls.c b/libobs/obs-audio-controls.c index ed85b6760..e47322b31 100644 --- a/libobs/obs-audio-controls.c +++ b/libobs/obs-audio-controls.c @@ -36,8 +36,6 @@ along with this program. If not, see . #define CLAMP(x, min, max) ((x) < min ? min : ((x) > max ? max : (x))) -typedef float (*obs_fader_conversion_t)(const float val); - struct fader_cb { obs_fader_changed_t callback; void *param; @@ -943,3 +941,8 @@ float obs_db_to_mul(float db) { return db_to_mul(db); } + +obs_fader_conversion_t obs_fader_db_to_def(obs_fader_t *fader) +{ + return fader->db_to_def; +} diff --git a/libobs/obs-audio-controls.h b/libobs/obs-audio-controls.h index 9346061c2..325471219 100644 --- a/libobs/obs-audio-controls.h +++ b/libobs/obs-audio-controls.h @@ -282,6 +282,10 @@ EXPORT void obs_volmeter_remove_callback(obs_volmeter_t *volmeter, EXPORT float obs_mul_to_db(float mul); EXPORT float obs_db_to_mul(float db); +typedef float (*obs_fader_conversion_t)(const float val); + +EXPORT obs_fader_conversion_t obs_fader_db_to_def(obs_fader_t *fader); + #ifdef __cplusplus } #endif