From 04fa4c49d92a56fb039ca04971b49bb9e97fcbdf Mon Sep 17 00:00:00 2001 From: Shaolin Date: Tue, 3 Apr 2018 05:21:57 -0300 Subject: [PATCH 1/4] UI: Clean up includes and code-style --- UI/volume-control.cpp | 293 ++++++++++++++++-------------------------- UI/volume-control.hpp | 13 +- 2 files changed, 119 insertions(+), 187 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 88b812a80..db4943d3a 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -3,24 +3,16 @@ #include "obs-app.hpp" #include "mute-checkbox.hpp" #include "slider-absoluteset-style.hpp" -#include -#include -#include #include #include -#include #include -#include #include #include #include -#include -#include -#include using namespace std; -#define CLAMP(x, min, max) ((x) < min ? min : ((x) > max ? max : (x))) +#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) QWeakPointer VolumeMeter::updateTimer; @@ -33,9 +25,9 @@ void VolControl::OBSVolumeChanged(void *data, float db) } void VolControl::OBSVolumeLevel(void *data, - const float magnitude[MAX_AUDIO_CHANNELS], - const float peak[MAX_AUDIO_CHANNELS], - const float inputPeak[MAX_AUDIO_CHANNELS]) + const float magnitude[MAX_AUDIO_CHANNELS], + const float peak[MAX_AUDIO_CHANNELS], + const float inputPeak[MAX_AUDIO_CHANNELS]) { VolControl *volControl = static_cast(data); @@ -120,11 +112,11 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType) } VolControl::VolControl(OBSSource source_, bool showConfig) - : source (source_), - levelTotal (0.0f), - levelCount (0.0f), - obs_fader (obs_fader_create(OBS_FADER_CUBIC)), - obs_volmeter (obs_volmeter_create(OBS_FADER_LOG)) + : source (source_), + levelTotal (0.0f), + levelCount (0.0f), + obs_fader (obs_fader_create(OBS_FADER_CUBIC)), + obs_volmeter (obs_volmeter_create(OBS_FADER_LOG)) { QHBoxLayout *volLayout = new QHBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout(); @@ -133,7 +125,7 @@ VolControl::VolControl(OBSSource source_, bool showConfig) nameLabel = new QLabel(); volLabel = new QLabel(); - volMeter = new VolumeMeter(0, obs_volmeter); + volMeter = new VolumeMeter(nullptr, obs_volmeter); mute = new MuteCheckBox(); slider = new QSlider(Qt::Horizontal); @@ -452,7 +444,7 @@ void VolumeMeter::setPeakMeterType(enum obs_peak_meter_type peakMeterType) } VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter) - : QWidget(parent), obs_volmeter(obs_volmeter) + : QWidget(parent), obs_volmeter(obs_volmeter) { // Use a font that can be rendered small. tickFont = QFont("Arial"); @@ -495,10 +487,9 @@ VolumeMeter::~VolumeMeter() updateTimerRef->RemoveVolControl(this); } -void VolumeMeter::setLevels( - const float magnitude[MAX_AUDIO_CHANNELS], - const float peak[MAX_AUDIO_CHANNELS], - const float inputPeak[MAX_AUDIO_CHANNELS]) +void VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS], + const float peak[MAX_AUDIO_CHANNELS], + const float inputPeak[MAX_AUDIO_CHANNELS]) { uint64_t ts = os_gettime_ns(); QMutexLocker locker(&dataMutex); @@ -551,7 +542,7 @@ inline void VolumeMeter::handleChannelCofigurationChange() inline bool VolumeMeter::detectIdle(uint64_t ts) { - float timeSinceLastUpdate = (ts - currentLastUpdateTime) * 0.000000001; + double timeSinceLastUpdate = (ts - currentLastUpdateTime) * 0.000000001; if (timeSinceLastUpdate > 0.5) { resetLevels(); return true; @@ -561,7 +552,7 @@ inline bool VolumeMeter::detectIdle(uint64_t ts) } inline void VolumeMeter::calculateBallisticsForChannel(int channelNr, - uint64_t ts, qreal timeSinceLastRedraw) + uint64_t ts, qreal timeSinceLastRedraw) { if (currentPeak[channelNr] >= displayPeak[channelNr] || isnan(displayPeak[channelNr])) { @@ -571,7 +562,7 @@ inline void VolumeMeter::calculateBallisticsForChannel(int channelNr, // Decay of peak is 40 dB / 1.7 seconds for Fast Profile // 20 dB / 1.7 seconds for Medium Profile (Type I PPM) // 24 dB / 2.8 seconds for Slow Profile (Type II PPM) - qreal decay = peakDecayRate * timeSinceLastRedraw; + float decay = float(peakDecayRate * timeSinceLastRedraw); displayPeak[channelNr] = CLAMP(displayPeak[channelNr] - decay, currentPeak[channelNr], 0); } @@ -615,53 +606,49 @@ inline void VolumeMeter::calculateBallisticsForChannel(int channelNr, if (!isfinite(displayMagnitude[channelNr])) { // The statements in the else-leg do not work with // NaN and infinite displayMagnitude. - displayMagnitude[channelNr] = - currentMagnitude[channelNr]; + displayMagnitude[channelNr] = currentMagnitude[channelNr]; } else { // A VU meter will integrate to the new value to 99% in 300 ms. // The calculation here is very simplified and is more accurate // with higher frame-rate. - qreal attack = (currentMagnitude[channelNr] - + float attack = float((currentMagnitude[channelNr] - displayMagnitude[channelNr]) * (timeSinceLastRedraw / - magnitudeIntegrationTime) * 0.99; - displayMagnitude[channelNr] = CLAMP( - displayMagnitude[channelNr] + attack, - minimumLevel, 0); + magnitudeIntegrationTime) * 0.99); + displayMagnitude[channelNr] = CLAMP(displayMagnitude[channelNr] + + attack, (float)minimumLevel, 0); } } inline void VolumeMeter::calculateBallistics(uint64_t ts, - qreal timeSinceLastRedraw) + qreal timeSinceLastRedraw) { QMutexLocker locker(&dataMutex); - for (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++) { + for (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++) calculateBallisticsForChannel(channelNr, ts, - timeSinceLastRedraw); - } + timeSinceLastRedraw); } -void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, - int width, int height, float peakHold) +void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, int width, + int height, float peakHold) { QMutexLocker locker(&dataMutex); - if (peakHold < minimumInputLevel) { + if (peakHold < minimumInputLevel) painter.fillRect(x, y, width, height, backgroundNominalColor); - } else if (peakHold < warningLevel) { + else if (peakHold < warningLevel) painter.fillRect(x, y, width, height, foregroundNominalColor); - } else if (peakHold < errorLevel) { + else if (peakHold < errorLevel) painter.fillRect(x, y, width, height, foregroundWarningColor); - } else if (peakHold <= clipLevel) { + else if (peakHold <= clipLevel) painter.fillRect(x, y, width, height, foregroundErrorColor); - } else { + else painter.fillRect(x, y, width, height, clipColor); - } } -void VolumeMeter::paintTicks(QPainter &painter, int x, int y, - int width, int height) +void VolumeMeter::paintTicks(QPainter &painter, int x, int y, int width, + int height) { qreal scale = width / minimumLevel; @@ -670,25 +657,22 @@ void VolumeMeter::paintTicks(QPainter &painter, int x, int y, // Draw major tick lines and numeric indicators. for (int i = 0; i >= minimumLevel; i-= 5) { - int position = x + width - (i * scale) - 1; + int position = int(x + width - (i * scale) - 1); QString str = QString::number(i); - if (i == 0 || i == -5) { + if (i == 0 || i == -5) painter.drawText(position - 3, height, str); - } else { + else painter.drawText(position - 5, height, str); - } painter.drawLine(position, y, position, y + 2); } // Draw minor tick lines. painter.setPen(minorTickColor); for (int i = 0; i >= minimumLevel; i--) { - int position = x + width - (i * scale) - 1; - - if (i % 5 != 0) { + int position = int(x + width - (i * scale) - 1); + if (i % 5 != 0) painter.drawLine(position, y, position, y + 1); - } } } @@ -699,19 +683,19 @@ void VolumeMeter::ClipEnding() clipping = false; } -void VolumeMeter::paintMeter(QPainter &painter, int x, int y, - int width, int height, float magnitude, float peak, float peakHold) +void VolumeMeter::paintMeter(QPainter &painter, int x, int y, int width, + int height, float magnitude, float peak, float peakHold) { qreal scale = width / minimumLevel; QMutexLocker locker(&dataMutex); int minimumPosition = x + 0; int maximumPosition = x + width; - int magnitudePosition = x + width - (magnitude * scale); - int peakPosition = x + width - (peak * scale); - int peakHoldPosition = x + width - (peakHold * scale); - int warningPosition = x + width - (warningLevel * scale); - int errorPosition = x + width - (errorLevel * scale); + int magnitudePosition = int(x + width - (magnitude * scale)); + int peakPosition = int(x + width - (peak * scale)); + int peakHoldPosition = int(x + width - (peakHold * scale)); + int warningPosition = int(x + width - (warningLevel * scale)); + int errorPosition = int(x + width - (errorLevel * scale)); int nominalLength = warningPosition - minimumPosition; int warningLength = errorPosition - warningPosition; @@ -723,72 +707,42 @@ void VolumeMeter::paintMeter(QPainter &painter, int x, int y, } if (peakPosition < minimumPosition) { - painter.fillRect( - minimumPosition, y, - nominalLength, height, - backgroundNominalColor); - painter.fillRect( - warningPosition, y, - warningLength, height, - backgroundWarningColor); - painter.fillRect( - errorPosition, y, - errorLength, height, - backgroundErrorColor); - + painter.fillRect(minimumPosition, y, nominalLength, height, + backgroundNominalColor); + painter.fillRect(warningPosition, y, warningLength, height, + backgroundWarningColor); + painter.fillRect(errorPosition, y, errorLength, height, + backgroundErrorColor); } else if (peakPosition < warningPosition) { - painter.fillRect( - minimumPosition, y, - peakPosition - minimumPosition, height, - foregroundNominalColor); - painter.fillRect( - peakPosition, y, - warningPosition - peakPosition, height, - backgroundNominalColor); - painter.fillRect( - warningPosition, y, - warningLength, height, - backgroundWarningColor); - painter.fillRect(errorPosition, y, - errorLength, height, - backgroundErrorColor); - + painter.fillRect(minimumPosition, y, peakPosition - + minimumPosition, height, + foregroundNominalColor); + painter.fillRect(peakPosition, y, warningPosition - + peakPosition, height, backgroundNominalColor); + painter.fillRect(warningPosition, y, warningLength, height, + backgroundWarningColor); + painter.fillRect(errorPosition, y, errorLength, height, + backgroundErrorColor); } else if (peakPosition < errorPosition) { - painter.fillRect( - minimumPosition, y, - nominalLength, height, - foregroundNominalColor); - painter.fillRect( - warningPosition, y, - peakPosition - warningPosition, height, - foregroundWarningColor); - painter.fillRect( - peakPosition, y, - errorPosition - peakPosition, height, - backgroundWarningColor); - painter.fillRect( - errorPosition, y, - errorLength, height, - backgroundErrorColor); - + painter.fillRect(minimumPosition, y, nominalLength, height, + foregroundNominalColor); + painter.fillRect(warningPosition, y, + peakPosition - warningPosition, height, + foregroundWarningColor); + painter.fillRect(peakPosition, y, errorPosition - + peakPosition, height, backgroundWarningColor); + painter.fillRect(errorPosition, y, errorLength, height, + backgroundErrorColor); } else if (peakPosition < maximumPosition) { - painter.fillRect( - minimumPosition, y, - nominalLength, height, - foregroundNominalColor); - painter.fillRect( - warningPosition, y, - warningLength, height, - foregroundWarningColor); - painter.fillRect( - errorPosition, y, - peakPosition - errorPosition, height, - foregroundErrorColor); - painter.fillRect( - peakPosition, y, - maximumPosition - peakPosition, height, - backgroundErrorColor); - + painter.fillRect(minimumPosition, y, nominalLength, height, + foregroundNominalColor); + painter.fillRect(warningPosition, y, warningLength, height, + foregroundWarningColor); + painter.fillRect(errorPosition, y, peakPosition - errorPosition, + height, foregroundErrorColor); + painter.fillRect(peakPosition, y, + maximumPosition - peakPosition, height, + backgroundErrorColor); } else { if (!clipping) { QTimer::singleShot(CLIP_FLASH_DURATION_MS, this, @@ -796,56 +750,34 @@ void VolumeMeter::paintMeter(QPainter &painter, int x, int y, clipping = true; } - qreal end = errorLength + warningLength + nominalLength; - painter.fillRect( - minimumPosition, y, - end, height, - QBrush(foregroundErrorColor)); + int end = errorLength + warningLength + nominalLength; + painter.fillRect(minimumPosition, y, end, height, + QBrush(foregroundErrorColor)); } - if (peakHoldPosition - 3 < minimumPosition) { - // Peak-hold below minimum, no drawing. + if (peakHoldPosition - 3 < minimumPosition) + ;// Peak-hold below minimum, no drawing. + else if (peakHoldPosition < warningPosition) + painter.fillRect(peakHoldPosition - 3, y, 3, height, + foregroundNominalColor); + else if (peakHoldPosition < errorPosition) + painter.fillRect(peakHoldPosition - 3, y, 3, height, + foregroundWarningColor); + else + painter.fillRect(peakHoldPosition - 3, y, 3, height, + foregroundErrorColor); - } else if (peakHoldPosition < warningPosition) { - painter.fillRect( - peakHoldPosition - 3, y, - 3, height, - foregroundNominalColor); - - } else if (peakHoldPosition < errorPosition) { - painter.fillRect( - peakHoldPosition - 3, y, - 3, height, - foregroundWarningColor); - - } else { - painter.fillRect( - peakHoldPosition - 3, y, - 3, height, - foregroundErrorColor); - } - - if (magnitudePosition - 3 < minimumPosition) { - // Magnitude below minimum, no drawing. - - } else if (magnitudePosition < warningPosition) { - painter.fillRect( - magnitudePosition - 3, y, - 3, height, - magnitudeColor); - - } else if (magnitudePosition < errorPosition) { - painter.fillRect( - magnitudePosition - 3, y, - 3, height, - magnitudeColor); - - } else { - painter.fillRect( - magnitudePosition - 3, y, - 3, height, - magnitudeColor); - } + if (magnitudePosition - 3 < minimumPosition) + ;// Magnitude below minimum, no drawing. + else if (magnitudePosition < warningPosition) + painter.fillRect(magnitudePosition - 3, y, 3, height, + magnitudeColor); + else if (magnitudePosition < errorPosition) + painter.fillRect(magnitudePosition - 3, y, 3, height, + magnitudeColor); + else + painter.fillRect(magnitudePosition - 3, y, 3, height, + magnitudeColor); } void VolumeMeter::paintEvent(QPaintEvent *event) @@ -864,7 +796,7 @@ void VolumeMeter::paintEvent(QPaintEvent *event) // Draw the ticks in a off-screen buffer when the widget changes size. QSize tickPaintCacheSize = QSize(width, 9); - if (tickPaintCache == NULL || + if (tickPaintCache == nullptr || tickPaintCache->size() != tickPaintCacheSize) { delete tickPaintCache; tickPaintCache = new QPixmap(tickPaintCacheSize); @@ -874,7 +806,7 @@ void VolumeMeter::paintEvent(QPaintEvent *event) QPainter tickPainter(tickPaintCache); paintTicks(tickPainter, 6, 0, tickPaintCacheSize.width() - 6, - tickPaintCacheSize.height()); + tickPaintCacheSize.height()); tickPainter.end(); } @@ -884,18 +816,17 @@ void VolumeMeter::paintEvent(QPaintEvent *event) for (int channelNr = 0; channelNr < displayNrAudioChannels; channelNr++) { - paintMeter(painter, - 5, channelNr * 4, width - 5, 3, - displayMagnitude[channelNr], displayPeak[channelNr], - displayPeakHold[channelNr]); + paintMeter(painter, 5, channelNr * 4, width - 5, 3, + displayMagnitude[channelNr], + displayPeak[channelNr], + displayPeakHold[channelNr]); if (!idle) { // By not drawing the input meter boxes the user can // see that the audio stream has been stopped, without // having too much visual impact. - paintInputMeter(painter, - 0, channelNr * 4, 3, 3, - displayInputPeakHold[channelNr]); + paintInputMeter(painter, 0, channelNr * 4, 3, 3, + displayInputPeakHold[channelNr]); } } diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 256199d8c..60e88ffd9 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -109,7 +110,7 @@ private: float currentPeak[MAX_AUDIO_CHANNELS]; float currentInputPeak[MAX_AUDIO_CHANNELS]; - QPixmap *tickPaintCache = NULL; + QPixmap *tickPaintCache = nullptr; int displayNrAudioChannels = 0; float displayMagnitude[MAX_AUDIO_CHANNELS]; float displayPeak[MAX_AUDIO_CHANNELS]; @@ -143,8 +144,8 @@ private: bool clipping = false; public: - explicit VolumeMeter(QWidget *parent = 0, - obs_volmeter_t *obs_volmeter = 0); + explicit VolumeMeter(QWidget *parent = nullptr, + obs_volmeter_t *obs_volmeter = nullptr); ~VolumeMeter(); void setLevels( @@ -193,7 +194,7 @@ public: void setPeakMeterType(enum obs_peak_meter_type peakMeterType); protected: - void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event) override; }; class VolumeMeterTimer : public QTimer { @@ -206,7 +207,7 @@ public: void RemoveVolControl(VolumeMeter *meter); protected: - virtual void timerEvent(QTimerEvent *event) override; + void timerEvent(QTimerEvent *event) override; QList volumeMeters; }; @@ -251,7 +252,7 @@ signals: void ConfigClicked(); public: - VolControl(OBSSource source, bool showConfig = false); + explicit VolControl(OBSSource source, bool showConfig = false); ~VolControl(); inline obs_source_t *GetSource() const {return source;} From 5aab9bfb3ef425ddd5ab724120404d501ba5cee1 Mon Sep 17 00:00:00 2001 From: Shaolin Date: Tue, 3 Apr 2018 05:25:40 -0300 Subject: [PATCH 2/4] UI: Avoid copies in Volume Control --- UI/volume-control.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index db4943d3a..320f79dde 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -112,7 +112,7 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType) } VolControl::VolControl(OBSSource source_, bool showConfig) - : source (source_), + : source (std::move(source_)), levelTotal (0.0f), levelCount (0.0f), obs_fader (obs_fader_create(OBS_FADER_CUBIC)), @@ -227,7 +227,7 @@ QColor VolumeMeter::getBackgroundNominalColor() const void VolumeMeter::setBackgroundNominalColor(QColor c) { - backgroundNominalColor = c; + backgroundNominalColor = std::move(c); } QColor VolumeMeter::getBackgroundWarningColor() const @@ -237,7 +237,7 @@ QColor VolumeMeter::getBackgroundWarningColor() const void VolumeMeter::setBackgroundWarningColor(QColor c) { - backgroundWarningColor = c; + backgroundWarningColor = std::move(c); } QColor VolumeMeter::getBackgroundErrorColor() const @@ -247,7 +247,7 @@ QColor VolumeMeter::getBackgroundErrorColor() const void VolumeMeter::setBackgroundErrorColor(QColor c) { - backgroundErrorColor = c; + backgroundErrorColor = std::move(c); } QColor VolumeMeter::getForegroundNominalColor() const @@ -257,7 +257,7 @@ QColor VolumeMeter::getForegroundNominalColor() const void VolumeMeter::setForegroundNominalColor(QColor c) { - foregroundNominalColor = c; + foregroundNominalColor = std::move(c); } QColor VolumeMeter::getForegroundWarningColor() const @@ -267,7 +267,7 @@ QColor VolumeMeter::getForegroundWarningColor() const void VolumeMeter::setForegroundWarningColor(QColor c) { - foregroundWarningColor = c; + foregroundWarningColor = std::move(c); } QColor VolumeMeter::getForegroundErrorColor() const @@ -277,7 +277,7 @@ QColor VolumeMeter::getForegroundErrorColor() const void VolumeMeter::setForegroundErrorColor(QColor c) { - foregroundErrorColor = c; + foregroundErrorColor = std::move(c); } QColor VolumeMeter::getClipColor() const @@ -287,7 +287,7 @@ QColor VolumeMeter::getClipColor() const void VolumeMeter::setClipColor(QColor c) { - clipColor = c; + clipColor = std::move(c); } QColor VolumeMeter::getMagnitudeColor() const @@ -297,7 +297,7 @@ QColor VolumeMeter::getMagnitudeColor() const void VolumeMeter::setMagnitudeColor(QColor c) { - magnitudeColor = c; + magnitudeColor = std::move(c); } QColor VolumeMeter::getMajorTickColor() const @@ -307,7 +307,7 @@ QColor VolumeMeter::getMajorTickColor() const void VolumeMeter::setMajorTickColor(QColor c) { - majorTickColor = c; + majorTickColor = std::move(c); } QColor VolumeMeter::getMinorTickColor() const @@ -317,7 +317,7 @@ QColor VolumeMeter::getMinorTickColor() const void VolumeMeter::setMinorTickColor(QColor c) { - minorTickColor = c; + minorTickColor = std::move(c); } qreal VolumeMeter::getMinimumLevel() const From 288339ceda906feea9aef2382fc3579b4c7215c5 Mon Sep 17 00:00:00 2001 From: Shaolin Date: Tue, 17 Apr 2018 12:54:40 -0300 Subject: [PATCH 3/4] UI: Simplify Volume Control draw logic --- UI/volume-control.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 320f79dde..62b4a7900 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -634,17 +634,20 @@ void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold) { QMutexLocker locker(&dataMutex); + QColor color; if (peakHold < minimumInputLevel) - painter.fillRect(x, y, width, height, backgroundNominalColor); + color = backgroundNominalColor; else if (peakHold < warningLevel) - painter.fillRect(x, y, width, height, foregroundNominalColor); + color = foregroundNominalColor; else if (peakHold < errorLevel) - painter.fillRect(x, y, width, height, foregroundWarningColor); + color = foregroundWarningColor; else if (peakHold <= clipLevel) - painter.fillRect(x, y, width, height, foregroundErrorColor); + color = foregroundErrorColor; else - painter.fillRect(x, y, width, height, clipColor); + color = clipColor; + + painter.fillRect(x, y, width, height, color); } void VolumeMeter::paintTicks(QPainter &painter, int x, int y, int width, @@ -767,15 +770,7 @@ void VolumeMeter::paintMeter(QPainter &painter, int x, int y, int width, painter.fillRect(peakHoldPosition - 3, y, 3, height, foregroundErrorColor); - if (magnitudePosition - 3 < minimumPosition) - ;// Magnitude below minimum, no drawing. - else if (magnitudePosition < warningPosition) - painter.fillRect(magnitudePosition - 3, y, 3, height, - magnitudeColor); - else if (magnitudePosition < errorPosition) - painter.fillRect(magnitudePosition - 3, y, 3, height, - magnitudeColor); - else + if (magnitudePosition - 3 >= minimumPosition) painter.fillRect(magnitudePosition - 3, y, 3, height, magnitudeColor); } From c7c328dc83c054cc8b3a64fba6b72f6ba3c63ee9 Mon Sep 17 00:00:00 2001 From: Shaolin Date: Fri, 27 Apr 2018 23:49:48 -0300 Subject: [PATCH 4/4] UI: Add Vertical Mixer option --- UI/CMakeLists.txt | 2 + UI/data/locale/en-US.ini | 1 + UI/data/themes/Acri.qss | 33 +- UI/data/themes/Dark.qss | 44 +-- UI/data/themes/Rachni.qss | 50 ++- UI/forms/OBSBasic.ui | 163 ++++++--- .../frontend-tools/CMakeLists.txt | 2 + UI/horizontal-scroll-area.cpp | 10 + UI/horizontal-scroll-area.hpp | 19 + UI/obs-app.cpp | 3 + UI/volume-control.cpp | 333 ++++++++++++++---- UI/volume-control.hpp | 23 +- UI/window-basic-main.cpp | 71 +++- UI/window-basic-main.hpp | 6 +- 14 files changed, 588 insertions(+), 172 deletions(-) create mode 100644 UI/horizontal-scroll-area.cpp create mode 100644 UI/horizontal-scroll-area.hpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 880869c2c..d33e931b3 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -161,6 +161,7 @@ set(obs_SOURCES item-widget-helpers.cpp visibility-checkbox.cpp locked-checkbox.cpp + horizontal-scroll-area.cpp vertical-scroll-area.cpp visibility-item-widget.cpp slider-absoluteset-style.cpp @@ -209,6 +210,7 @@ set(obs_HEADERS item-widget-helpers.hpp visibility-checkbox.hpp locked-checkbox.hpp + horizontal-scroll-area.hpp vertical-scroll-area.hpp visibility-item-widget.hpp slider-absoluteset-style.hpp diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 48b540fbc..5c0941479 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -83,6 +83,7 @@ None="None" StudioMode.Preview="Preview" StudioMode.Program="Program" ShowInMultiview="Show in Multiview" +VerticalLayout="Vertical Layout" # warning if program already open AlreadyRunning.Title="OBS is already running" diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index af3eb9070..f4b767e48 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -505,16 +505,20 @@ QSlider::handle:horizontal { } QSlider::handle:horizontal:pressed { - background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0, + background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(240,239,240), stop: 0.25 rgb(200,199,200), stop: 1 rgb(162,161,162)); } +QSlider::sub-page:horizontal { + background-color: #2a3a75; +} + QSlider::sub-page:horizontal:disabled { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(31,30,31), - stop: 0.75 rgb(50, 49, 50)); + background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0, + stop: 0 rgb(26,25,26), + stop: 0.75 rgb(10, 10, 10)); border-radius: 2px; } @@ -533,23 +537,27 @@ QSlider::handle:vertical { stop: 0.25 rgb(200,199,200), stop: 1 rgb(162,161,162)); border: 1px solid rgb(24,24,25); - border-radius: 4px; + border-radius: 3px; width: 10px; height: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ + margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ } QSlider::handle:vertical:pressed { - background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, + background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgb(240,239,240), stop: 0.25 rgb(200,199,200), stop: 1 rgb(162,161,162)); } -QSlider::sub-page:vertical:disabled { +QSlider::add-page:vertical { + background-color: #2a3a75; +} + +QSlider::add-page:vertical:disabled { background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0 rgb(31,30,31), - stop: 0.75 rgb(50, 49, 50)); + stop: 0 rgb(26,25,26), + stop: 0.75 rgb(10, 10, 10)); border-radius: 2px; } @@ -557,15 +565,10 @@ QSlider::handle:hover { background-color: rgb(200,199,200); } -QSlider::sub-page { - background-color: #2a3a75; -} - QSlider::handle:disabled { background-color: rgb(15,15,16); } - /* Volume Control */ /* Old Meters */ diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index 4a9326c6a..78c343dbf 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -423,9 +423,9 @@ QPushButton::menu-indicator { /* Sliders */ QSlider::groove:horizontal { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(50, 49, 50)); + background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0, + stop: 0 rgb(50, 49, 50), /* dark */ + stop: 0.75 rgb(88,87,88)); /* kindaDark */ height: 4px; border: none; border-radius: 2px; @@ -450,32 +450,37 @@ QSlider::handle:horizontal:pressed { stop: 1 rgb(162,161,162)); /* light */ } +QSlider::sub-page:horizontal { + background-color: rgb(42,130,218); /* blue */ + border-radius: 2px; +} + QSlider::sub-page:horizontal:disabled { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, + background-color: QLinearGradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(50, 49, 50)); + stop: 0.75 rgb(50, 49, 50)); /* dark */ border-radius: 2px; } QSlider::groove:vertical { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(50, 49, 50)); + background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, + stop: 0 rgb(50, 49, 50), /* dark */ + stop: 0.75 rgb(88,87,88)); /* kindaDark */ width: 4px; border: none; border-radius: 2px; } QSlider::handle:vertical { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, + background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, stop: 0 rgb(240,239,240), /* lighter */ stop: 0.25 rgb(200,199,200), stop: 1 rgb(162,161,162)); /* light */ border: 1px solid rgb(58,57,58); /* dark */ - border-radius: 4px; + border-radius: 3px; width: 10px; height: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ + margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ } QSlider::handle:vertical:pressed { @@ -485,10 +490,15 @@ QSlider::handle:vertical:pressed { stop: 1 rgb(162,161,162)); /* light */ } -QSlider::sub-page:vertical:disabled { - background-color: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, +QSlider::add-page:vertical { + background-color: rgb(42,130,218); /* blue */ + border-radius: 2px; +} + +QSlider::add-page:vertical:disabled { + background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, stop: 0 rgb(31,30,31), /* veryDark */ - stop: 0.75 rgb(50, 49, 50)); + stop: 0.75 rgb(50, 49, 50)); /* dark */ border-radius: 2px; } @@ -496,16 +506,10 @@ QSlider::handle:hover { background-color: rgb(200,199,200); /* veryLight */ } -QSlider::sub-page { - background-color: rgb(42,130,218); /* blue */ - border-radius: 2px; -} - QSlider::handle:disabled { background-color: rgb(122,121,122); /* light */ } - /* Volume Control */ VolumeMeter { diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index 5c7887cdb..5249acc91 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -1022,10 +1022,55 @@ QSlider::handle:horizontal:pressed { stop: 1 rgb(162, 161, 162)); } +QSlider::sub-page:horizontal { + background-color: rgb(0, 188, 212); /* Cyan (Primary) */ + border-radius: 2px; +} + QSlider::sub-page:horizontal:disabled { background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(35, 38, 41), /* Dark Gray */ + stop: 0.75 rgb(35, 38, 41)); /* Dark Gray */ + border-radius: 2px; +} + +QSlider::groove:vertical { + background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, + stop: 0 rgb(35, 38, 41), /* Dark Gray */ stop: 0.75 rgb(50, 49, 50)); + width: 4px; + border: none; + border-radius: 2px; +} + +QSlider::handle:vertical { + background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, + stop: 0 rgb(240, 239, 240), + stop: 0.25 rgb(200, 199, 200), + stop: 1 rgb(162, 161, 162)); + border: 1px solid rgb(58, 57, 58); + border-radius: 3px; + width: 10px; + height: 18px; + margin: 0 -3px; +} + +QSlider::handle:vertical:pressed { + background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, + stop: 0 rgb(240, 239, 240), + stop: 0.25 rgb(200, 199, 200), + stop: 1 rgb(162, 161, 162)); +} + +QSlider::add-page:vertical { + background-color: rgb(0, 188, 212); /* Cyan (Primary) */ + border-radius: 2px; +} + +QSlider::add-page:vertical:disabled { + background-color: QLinearGradient(x1: 1, y1: 0, x2: 0, y2: 0, + stop: 0 rgb(35, 38, 41), /* Dark Gray */ + stop: 0.75 rgb(35, 38, 41)); /* Dark Gray */ border-radius: 2px; } @@ -1033,11 +1078,6 @@ QSlider::handle:hover { background-color: rgb(200, 199, 200); } -QSlider::sub-page { - background-color: rgb(0, 188, 212); /* Cyan (Primary) */ - border-radius: 2px; -} - QSlider::handle:disabled { background-color: rgb(122, 121, 122); } diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 55e66c4da..5f06b0ce3 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -615,63 +615,118 @@ 4 - - - - 220 - 0 - - - - Qt::CustomContextMenu - - - QFrame::StyledPanel - - - QFrame::Sunken - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 230 - 16 - + + + + + 175 + 220 + - - - 0 - 0 - + + Qt::CustomContextMenu - - - 0 + + QFrame::StyledPanel + + + QFrame::Sunken + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 230 + 16 + - - 0 + + + 0 + 0 + - - 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::CustomContextMenu + + + QFrame::StyledPanel + + + QFrame::Sunken + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOn + + + true + + + + + 0 + 0 + 16 + 230 + - - 0 + + + 0 + 0 + - - 0 - - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + @@ -1614,6 +1669,12 @@ QStatusBar
window-basic-status-bar.hpp
+ + HScrollArea + QScrollArea +
horizontal-scroll-area.hpp
+ 1 +
VScrollArea QScrollArea diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 37a022a88..ca29cad87 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -25,6 +25,7 @@ set(frontend-tools_HEADERS tool-helpers.hpp ../../properties-view.hpp ../../properties-view.moc.hpp + ../../horizontal-scroll-area.hpp ../../vertical-scroll-area.hpp ../../double-slider.hpp ) @@ -34,6 +35,7 @@ set(frontend-tools_SOURCES frontend-tools.c output-timer.cpp ../../properties-view.cpp + ../../horizontal-scroll-area.cpp ../../vertical-scroll-area.cpp ../../double-slider.cpp ) diff --git a/UI/horizontal-scroll-area.cpp b/UI/horizontal-scroll-area.cpp new file mode 100644 index 000000000..8f927fc59 --- /dev/null +++ b/UI/horizontal-scroll-area.cpp @@ -0,0 +1,10 @@ +#include +#include "horizontal-scroll-area.hpp" + +void HScrollArea::resizeEvent(QResizeEvent *event) +{ + if (!!widget()) + widget()->setMaximumHeight(event->size().height()); + + QScrollArea::resizeEvent(event); +} diff --git a/UI/horizontal-scroll-area.hpp b/UI/horizontal-scroll-area.hpp new file mode 100644 index 000000000..8a64c3ea8 --- /dev/null +++ b/UI/horizontal-scroll-area.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +class QResizeEvent; + +class HScrollArea : public QScrollArea { + Q_OBJECT + +public: + inline HScrollArea(QWidget *parent = nullptr) + : QScrollArea(parent) + { + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + } + +protected: + virtual void resizeEvent(QResizeEvent *event) override; +}; diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 752337fe5..f8f76755c 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -418,6 +418,9 @@ bool OBSApp::InitGlobalConfigDefaults() "CurrentTheme", "Dark"); } + config_set_default_bool(globalConfig, "BasicWindow", + "VerticalVolControl", false); + #ifdef _WIN32 config_set_default_bool(globalConfig, "Audio", "DisableAudioDucking", true); diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 62b4a7900..532fa8728 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -111,57 +111,20 @@ void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType) volMeter->setPeakMeterType(peakMeterType); } -VolControl::VolControl(OBSSource source_, bool showConfig) +VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) : source (std::move(source_)), levelTotal (0.0f), levelCount (0.0f), obs_fader (obs_fader_create(OBS_FADER_CUBIC)), - obs_volmeter (obs_volmeter_create(OBS_FADER_LOG)) + obs_volmeter (obs_volmeter_create(OBS_FADER_LOG)), + vertical (vertical) { - QHBoxLayout *volLayout = new QHBoxLayout(); - QVBoxLayout *mainLayout = new QVBoxLayout(); - QHBoxLayout *textLayout = new QHBoxLayout(); - QHBoxLayout *botLayout = new QHBoxLayout(); - nameLabel = new QLabel(); volLabel = new QLabel(); - volMeter = new VolumeMeter(nullptr, obs_volmeter); mute = new MuteCheckBox(); - slider = new QSlider(Qt::Horizontal); - - QFont font = nameLabel->font(); - font.setPointSize(font.pointSize()-1); - QString sourceName = obs_source_get_name(source); setObjectName(sourceName); - nameLabel->setText(sourceName); - nameLabel->setFont(font); - volLabel->setFont(font); - slider->setMinimum(0); - slider->setMaximum(100); - -// slider->setMaximumHeight(13); - - textLayout->setContentsMargins(0, 0, 0, 0); - textLayout->addWidget(nameLabel); - textLayout->addWidget(volLabel); - textLayout->setAlignment(nameLabel, Qt::AlignLeft); - textLayout->setAlignment(volLabel, Qt::AlignRight); - - bool muted = obs_source_muted(source); - mute->setChecked(muted); - mute->setAccessibleName( - QTStr("VolControl.Mute").arg(sourceName)); - - volLayout->addWidget(slider); - volLayout->addWidget(mute); - volLayout->setSpacing(5); - - botLayout->setContentsMargins(0, 0, 0, 0); - botLayout->setSpacing(0); - botLayout->addLayout(volLayout); - if (showConfig) { config = new QPushButton(this); config->setProperty("themeID", "configIconSmall"); @@ -176,18 +139,99 @@ VolControl::VolControl(OBSSource source_, bool showConfig) connect(config, &QAbstractButton::clicked, this, &VolControl::EmitConfigClicked); - - botLayout->addWidget(config); } + QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->setContentsMargins(4, 4, 4, 4); mainLayout->setSpacing(2); - mainLayout->addItem(textLayout); - mainLayout->addWidget(volMeter); - mainLayout->addItem(botLayout); + + if (vertical) { + QHBoxLayout *nameLayout = new QHBoxLayout; + QHBoxLayout *controlLayout = new QHBoxLayout; + QHBoxLayout *volLayout = new QHBoxLayout; + QHBoxLayout *meterLayout = new QHBoxLayout; + + volMeter = new VolumeMeter(nullptr, obs_volmeter, true); + slider = new QSlider(Qt::Vertical); + + nameLayout->setAlignment(Qt::AlignCenter); + meterLayout->setAlignment(Qt::AlignCenter); + controlLayout->setAlignment(Qt::AlignCenter); + volLayout->setAlignment(Qt::AlignCenter); + + nameLayout->setContentsMargins(0, 0, 0, 0); + nameLayout->setSpacing(0); + nameLayout->addWidget(nameLabel); + + controlLayout->setContentsMargins(0, 0, 0, 0); + controlLayout->setSpacing(0); + + if (showConfig) + controlLayout->addWidget(config); + + controlLayout->addItem(new QSpacerItem(3, 0)); + // Add Headphone (audio monitoring) widget here + controlLayout->addWidget(mute); + + meterLayout->setContentsMargins(0, 0, 0, 0); + meterLayout->setSpacing(0); + meterLayout->addWidget(volMeter); + meterLayout->addWidget(slider); + + volLayout->setContentsMargins(0, 0, 0, 0); + volLayout->setSpacing(0); + volLayout->addWidget(volLabel); + + mainLayout->addItem(nameLayout); + mainLayout->addItem(volLayout); + mainLayout->addItem(meterLayout); + mainLayout->addItem(controlLayout); + + setMaximumWidth(110); + } else { + QHBoxLayout *volLayout = new QHBoxLayout; + QHBoxLayout *textLayout = new QHBoxLayout; + QHBoxLayout *botLayout = new QHBoxLayout; + + volMeter = new VolumeMeter(nullptr, obs_volmeter, false); + slider = new QSlider(Qt::Horizontal); + + textLayout->setContentsMargins(0, 0, 0, 0); + textLayout->addWidget(nameLabel); + textLayout->addWidget(volLabel); + textLayout->setAlignment(nameLabel, Qt::AlignLeft); + textLayout->setAlignment(volLabel, Qt::AlignRight); + + volLayout->addWidget(slider); + volLayout->addWidget(mute); + volLayout->setSpacing(5); + + botLayout->setContentsMargins(0, 0, 0, 0); + botLayout->setSpacing(0); + botLayout->addLayout(volLayout); + + if (showConfig) + botLayout->addWidget(config); + + mainLayout->addItem(textLayout); + mainLayout->addWidget(volMeter); + mainLayout->addItem(botLayout); + } setLayout(mainLayout); + QFont font = nameLabel->font(); + font.setPointSize(font.pointSize()-1); + + nameLabel->setText(sourceName); + nameLabel->setFont(font); + volLabel->setFont(font); + slider->setMinimum(0); + slider->setMaximum(100); + + bool muted = obs_source_muted(source); + mute->setChecked(muted); + mute->setAccessibleName(QTStr("VolControl.Mute").arg(sourceName)); obs_fader_add_callback(obs_fader, OBSVolumeChanged, this); obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this); @@ -443,8 +487,10 @@ void VolumeMeter::setPeakMeterType(enum obs_peak_meter_type peakMeterType) } } -VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter) - : QWidget(parent), obs_volmeter(obs_volmeter) +VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter, + bool vertical) + : QWidget(parent), obs_volmeter(obs_volmeter), + vertical(vertical) { // Use a font that can be rendered small. tickFont = QFont("Arial"); @@ -532,9 +578,12 @@ inline void VolumeMeter::handleChannelCofigurationChange() if (displayNrAudioChannels != currentNrAudioChannels) { displayNrAudioChannels = currentNrAudioChannels; - // Make room for 3 pixels high meter, with one pixel between - // each. Then 9 pixels below it for ticks and numbers. - setMinimumSize(130, displayNrAudioChannels * 4 + 8); + // Make room for 3 pixels meter, with one pixel between each. + // Then 9/13 pixels for ticks and numbers. + if (vertical) + setMinimumSize(displayNrAudioChannels * 4 + 14, 130); + else + setMinimumSize(130, displayNrAudioChannels * 4 + 8); resetLevels(); } @@ -650,7 +699,7 @@ void VolumeMeter::paintInputMeter(QPainter &painter, int x, int y, int width, painter.fillRect(x, y, width, height, color); } -void VolumeMeter::paintTicks(QPainter &painter, int x, int y, int width, +void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width, int height) { qreal scale = width / minimumLevel; @@ -679,6 +728,36 @@ void VolumeMeter::paintTicks(QPainter &painter, int x, int y, int width, } } +void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height) +{ + qreal scale = height / minimumLevel; + + painter.setFont(tickFont); + painter.setPen(majorTickColor); + + // Draw major tick lines and numeric indicators. + for (int i = 0; i >= minimumLevel; i-= 5) { + int position = y + int((i * scale) - 1); + QString str = QString::number(i); + + if (i == 0) + painter.drawText(x + 5, position + 4, str); + else if (i == -60) + painter.drawText(x + 4, position, str); + else + painter.drawText(x + 4, position + 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) - 1); + if (i % 5 != 0) + painter.drawLine(x, position, x + 1, position); + } +} + #define CLIP_FLASH_DURATION_MS 1000 void VolumeMeter::ClipEnding() @@ -686,7 +765,7 @@ void VolumeMeter::ClipEnding() clipping = false; } -void VolumeMeter::paintMeter(QPainter &painter, int x, int y, int width, +void VolumeMeter::paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak, float peakHold) { qreal scale = width / minimumLevel; @@ -775,22 +854,111 @@ void VolumeMeter::paintMeter(QPainter &painter, int x, int y, int width, magnitudeColor); } +void VolumeMeter::paintVMeter(QPainter &painter, int x, int y, int width, + int height, float magnitude, float peak, float peakHold) +{ + qreal scale = height / minimumLevel; + + QMutexLocker locker(&dataMutex); + int minimumPosition = y + 0; + int maximumPosition = y + height; + int magnitudePosition = int(y + height - (magnitude * scale)); + int peakPosition = int(y + height - (peak * scale)); + int peakHoldPosition = int(y + height - (peakHold * scale)); + int warningPosition = int(y + height - (warningLevel * scale)); + int errorPosition = int(y + height - (errorLevel * scale)); + + int nominalLength = warningPosition - minimumPosition; + int warningLength = errorPosition - warningPosition; + int errorLength = maximumPosition - errorPosition; + locker.unlock(); + + if (clipping) { + peakPosition = maximumPosition; + } + + if (peakPosition < minimumPosition) { + painter.fillRect(x, minimumPosition, width, nominalLength, + backgroundNominalColor); + painter.fillRect(x, warningPosition, width, warningLength, + backgroundWarningColor); + painter.fillRect(x, errorPosition, width, errorLength, + backgroundErrorColor); + } else if (peakPosition < warningPosition) { + painter.fillRect(x, minimumPosition, width, peakPosition - + minimumPosition, foregroundNominalColor); + painter.fillRect(x, peakPosition, width, warningPosition - + peakPosition, backgroundNominalColor); + painter.fillRect(x, warningPosition, width, warningLength, + backgroundWarningColor); + painter.fillRect(x, errorPosition, width, errorLength, + backgroundErrorColor); + } else if (peakPosition < errorPosition) { + painter.fillRect(x,minimumPosition, width, nominalLength, + foregroundNominalColor); + painter.fillRect(x, warningPosition, width, peakPosition - + warningPosition, foregroundWarningColor); + painter.fillRect(x, peakPosition, width, errorPosition - + peakPosition, backgroundWarningColor); + painter.fillRect(x, errorPosition, width, errorLength, + backgroundErrorColor); + } else if (peakPosition < maximumPosition) { + painter.fillRect(x, minimumPosition, width, nominalLength, + foregroundNominalColor); + painter.fillRect(x, warningPosition, width, warningLength, + foregroundWarningColor); + painter.fillRect(x, errorPosition, width, peakPosition - + errorPosition, foregroundErrorColor); + painter.fillRect(x, peakPosition, width, maximumPosition - + peakPosition, backgroundErrorColor); + } else { + if (!clipping) { + QTimer::singleShot(CLIP_FLASH_DURATION_MS, this, + SLOT(ClipEnding())); + clipping = true; + } + + int end = errorLength + warningLength + nominalLength; + painter.fillRect(x, minimumPosition, width, end, + QBrush(foregroundErrorColor)); + } + + if (peakHoldPosition - 3 < minimumPosition) + ;// Peak-hold below minimum, no drawing. + else if (peakHoldPosition < warningPosition) + painter.fillRect(x, peakHoldPosition - 3, width, 3, + foregroundNominalColor); + else if (peakHoldPosition < errorPosition) + painter.fillRect(x, peakHoldPosition - 3, width, 3, + foregroundWarningColor); + else + painter.fillRect(x, peakHoldPosition - 3, width, 3, + foregroundErrorColor); + + if (magnitudePosition - 3 >= minimumPosition) + painter.fillRect(x, magnitudePosition - 3, width, 3, + magnitudeColor); +} + void VolumeMeter::paintEvent(QPaintEvent *event) { - UNUSED_PARAMETER(event); - uint64_t ts = os_gettime_ns(); qreal timeSinceLastRedraw = (ts - lastRedrawTime) * 0.000000001; - int width = size().width(); - int height = size().height(); + const QRect rect = event->region().boundingRect(); + int width = rect.width(); + int height = rect.height(); handleChannelCofigurationChange(); calculateBallistics(ts, timeSinceLastRedraw); bool idle = detectIdle(ts); // Draw the ticks in a off-screen buffer when the widget changes size. - QSize tickPaintCacheSize = QSize(width, 9); + QSize tickPaintCacheSize; + if (vertical) + tickPaintCacheSize = QSize(14, height); + else + tickPaintCacheSize = QSize(width, 9); if (tickPaintCache == nullptr || tickPaintCache->size() != tickPaintCacheSize) { delete tickPaintCache; @@ -800,29 +968,56 @@ void VolumeMeter::paintEvent(QPaintEvent *event) tickPaintCache->fill(clearColor); QPainter tickPainter(tickPaintCache); - paintTicks(tickPainter, 6, 0, tickPaintCacheSize.width() - 6, - tickPaintCacheSize.height()); + if (vertical) { + tickPainter.translate(0, height); + tickPainter.scale(1, -1); + paintVTicks(tickPainter, 0, 11, + tickPaintCacheSize.height() - 11); + } else { + paintHTicks(tickPainter, 6, 0, + tickPaintCacheSize.width() - 6, + tickPaintCacheSize.height()); + } tickPainter.end(); } // Actual painting of the widget starts here. QPainter painter(this); - painter.drawPixmap(0, height - 9, *tickPaintCache); + if (vertical) { + // Invert the Y axis to ease the math + painter.translate(0, height); + painter.scale(1, -1); + painter.drawPixmap(displayNrAudioChannels * 4 - 1, 7, + *tickPaintCache); + } else { + painter.drawPixmap(0, height - 9, *tickPaintCache); + } for (int channelNr = 0; channelNr < displayNrAudioChannels; channelNr++) { - paintMeter(painter, 5, channelNr * 4, width - 5, 3, - displayMagnitude[channelNr], - displayPeak[channelNr], - displayPeakHold[channelNr]); + if (vertical) + paintVMeter(painter, channelNr * 4, 8, 3, height - 10, + displayMagnitude[channelNr], + displayPeak[channelNr], + displayPeakHold[channelNr]); + else + paintHMeter(painter, 5, channelNr * 4, width - 5, 3, + displayMagnitude[channelNr], + displayPeak[channelNr], + displayPeakHold[channelNr]); - if (!idle) { - // By not drawing the input meter boxes the user can - // see that the audio stream has been stopped, without - // having too much visual impact. + if (idle) + continue; + + // By not drawing the input meter boxes the user can + // see that the audio stream has been stopped, without + // having too much visual impact. + if (vertical) + paintInputMeter(painter, channelNr * 4, 3, 3, 3, + displayInputPeakHold[channelNr]); + else paintInputMeter(painter, 0, channelNr * 4, 3, 3, displayInputPeakHold[channelNr]); - } } lastRedrawTime = ts; diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 60e88ffd9..fde0d12f4 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -96,12 +96,15 @@ private: inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw); - void paintInputMeter(QPainter &painter, int x, int y, - int width, int height, float peakHold); - void paintMeter(QPainter &painter, int x, int y, - int width, int height, - float magnitude, float peak, float peakHold); - void paintTicks(QPainter &painter, int x, int y, int width, int height); + void paintInputMeter(QPainter &painter, int x, int y, int width, + int height, float peakHold); + void paintHMeter(QPainter &painter, int x, int y, int width, int height, + float magnitude, float peak, float peakHold); + void paintHTicks(QPainter &painter, int x, int y, int width, + int height); + void paintVMeter(QPainter &painter, int x, int y, int width, int height, + float magnitude, float peak, float peakHold); + void paintVTicks(QPainter &painter, int x, int y, int height); QMutex dataMutex; @@ -142,10 +145,12 @@ private: uint64_t lastRedrawTime = 0; bool clipping = false; + bool vertical; public: explicit VolumeMeter(QWidget *parent = nullptr, - obs_volmeter_t *obs_volmeter = nullptr); + obs_volmeter_t *obs_volmeter = nullptr, + bool vertical = false); ~VolumeMeter(); void setLevels( @@ -230,6 +235,7 @@ private: float levelCount; obs_fader_t *obs_fader; obs_volmeter_t *obs_volmeter; + bool vertical; static void OBSVolumeChanged(void *param, float db); static void OBSVolumeLevel(void *data, @@ -252,7 +258,8 @@ signals: void ConfigClicked(); public: - explicit VolControl(OBSSource source, bool showConfig = false); + explicit VolControl(OBSSource source, bool showConfig = false, + bool vertical = false); ~VolControl(); inline obs_source_t *GetSource() const {return source;} diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 18eac1bac..50c1ecb9b 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1572,6 +1572,10 @@ void OBSBasic::OBSInit() } } + bool vertical = config_get_bool(App()->GlobalConfig(), "BasicWindow", + "VerticalVolControl"); + ui->stackedMixerArea->setCurrentIndex(vertical); + if (config_get_bool(basicConfig, "General", "OpenStatsOnStartup")) on_stats_triggered(); @@ -2471,6 +2475,11 @@ void OBSBasic::VolControlContextMenu() QAction propertiesAction(QTStr("Properties"), this); QAction advPropAction(QTStr("Basic.MainMenu.Edit.AdvAudio"), this); + QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); + toggleControlLayoutAction.setCheckable(true); + toggleControlLayoutAction.setChecked(config_get_bool(GetGlobalConfig(), + "BasicWindow", "VerticalVolControl")); + /* ------------------- */ connect(&hideAction, &QAction::triggered, @@ -2495,6 +2504,12 @@ void OBSBasic::VolControlContextMenu() /* ------------------- */ + connect(&toggleControlLayoutAction, &QAction::changed, this, + &OBSBasic::ToggleVolControlLayout, + Qt::DirectConnection); + + /* ------------------- */ + hideAction.setProperty("volControl", QVariant::fromValue(vol)); mixerRenameAction.setProperty("volControl", @@ -2512,18 +2527,35 @@ void OBSBasic::VolControlContextMenu() popup.addAction(&hideAction); popup.addAction(&mixerRenameAction); popup.addSeparator(); + popup.addAction(&toggleControlLayoutAction); + popup.addSeparator(); popup.addAction(&filtersAction); popup.addAction(&propertiesAction); popup.addAction(&advPropAction); popup.exec(QCursor::pos()); } -void OBSBasic::on_mixerScrollArea_customContextMenuRequested() +void OBSBasic::on_hMixerScrollArea_customContextMenuRequested() +{ + StackedMixerAreaContextMenuRequested(); +} + +void OBSBasic::on_vMixerScrollArea_customContextMenuRequested() +{ + StackedMixerAreaContextMenuRequested(); +} + +void OBSBasic::StackedMixerAreaContextMenuRequested() { QAction unhideAllAction(QTStr("UnhideAll"), this); QAction advPropAction(QTStr("Basic.MainMenu.Edit.AdvAudio"), this); + QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); + toggleControlLayoutAction.setCheckable(true); + toggleControlLayoutAction.setChecked(config_get_bool(GetGlobalConfig(), + "BasicWindow", "VerticalVolControl")); + /* ------------------- */ connect(&unhideAllAction, &QAction::triggered, @@ -2536,19 +2568,49 @@ void OBSBasic::on_mixerScrollArea_customContextMenuRequested() /* ------------------- */ + connect(&toggleControlLayoutAction, &QAction::changed, this, + &OBSBasic::ToggleVolControlLayout, + Qt::DirectConnection); + + /* ------------------- */ + QMenu popup(this); popup.addAction(&unhideAllAction); popup.addSeparator(); + popup.addAction(&toggleControlLayoutAction); + popup.addSeparator(); popup.addAction(&advPropAction); popup.exec(QCursor::pos()); } +void OBSBasic::ToggleVolControlLayout() +{ + bool vertical = !config_get_bool(GetGlobalConfig(), "BasicWindow", + "VerticalVolControl"); + config_set_bool(GetGlobalConfig(), "BasicWindow", "VerticalVolControl", + vertical); + ui->stackedMixerArea->setCurrentIndex(vertical); + + // We need to store it so we can delete current and then add + // at the right order + vector sources; + for (size_t i = 0; i != volumes.size(); i++) + sources.emplace_back(volumes[i]->GetSource()); + + ClearVolumeControls(); + + for (const auto &source : sources) + ActivateAudioSource(source); +} + void OBSBasic::ActivateAudioSource(OBSSource source) { if (SourceMixerHidden(source)) return; - VolControl *vol = new VolControl(source, true); + bool vertical = config_get_bool(GetGlobalConfig(), "BasicWindow", + "VerticalVolControl"); + VolControl *vol = new VolControl(source, true, vertical); double meterDecayRate = config_get_double(basicConfig, "Audio", "MeterDecayRate"); @@ -2582,7 +2644,10 @@ void OBSBasic::ActivateAudioSource(OBSSource source) InsertQObjectByName(volumes, vol); for (auto volume : volumes) { - ui->volumeWidgets->layout()->addWidget(volume); + if (vertical) + ui->vVolControlLayout->addWidget(volume); + else + ui->hVolControlLayout->addWidget(volume); } } diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 2951438fa..277d6f6e2 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -258,6 +258,7 @@ private: void GetAudioSourceFilters(); void GetAudioSourceProperties(); void VolControlContextMenu(); + void ToggleVolControlLayout(); void RefreshSceneCollections(); void ChangeSceneCollection(); @@ -455,7 +456,8 @@ private slots: void MixerRenameSource(); - void on_mixerScrollArea_customContextMenuRequested(); + void on_vMixerScrollArea_customContextMenuRequested(); + void on_hMixerScrollArea_customContextMenuRequested(); void on_actionCopySource_triggered(); void on_actionPasteRef_triggered(); @@ -715,6 +717,8 @@ private slots: void DeferredLoad(const QString &file, int requeueCount); + void StackedMixerAreaContextMenuRequested(); + public slots: void on_actionResetTransform_triggered();