frontend: Implement sizeHint for VolumeMeter

This commit is contained in:
Warchamp7
2026-01-22 12:32:12 -05:00
committed by Ryan Foster
parent d3dfadfbf5
commit e73662f6c1
3 changed files with 95 additions and 63 deletions
+79 -57
View File
@@ -9,11 +9,15 @@
#include "moc_VolumeMeter.cpp"
// Size of the audio indicator in pixels
#define INDICATOR_THICKNESS 3
QPointer<QTimer> VolumeMeter::updateTimer = nullptr;
namespace {
constexpr int INDICATOR_THICKNESS = 3;
constexpr int CLIP_FLASH_DURATION_MS = 1000;
constexpr int TICK_SIZE = 2;
constexpr int TICK_DB_INTERVAL = 6;
} // namespace
static inline QColor color_from_int(long long val)
{
QColor color(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
@@ -274,17 +278,6 @@ void VolumeMeter::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
updateBackgroundCache();
}
void VolumeMeter::mousePressEvent(QMouseEvent *event)
{
setFocus(Qt::MouseFocusReason);
event->accept();
}
void VolumeMeter::wheelEvent(QWheelEvent *event)
{
QApplication::sendEvent(focusProxy(), event);
}
VolumeMeter::VolumeMeter(QWidget *parent, obs_source_t *source)
: QWidget(parent),
weakSource(OBSGetWeakRef(source)),
@@ -348,7 +341,7 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_source_t *source)
repaint();
});
connect(App(), &OBSApp::StyleChanged, this, &VolumeMeter::updateBackgroundCache);
connect(App(), &OBSApp::StyleChanged, this, &VolumeMeter::doLayout);
}
VolumeMeter::~VolumeMeter()
@@ -419,10 +412,10 @@ bool VolumeMeter::needLayoutChange()
if (displayNrAudioChannels != currentNrAudioChannels) {
displayNrAudioChannels = currentNrAudioChannels;
recalculateLayout = true;
return true;
}
return recalculateLayout;
return false;
}
void VolumeMeter::setVertical(bool vertical_)
@@ -465,6 +458,18 @@ void VolumeMeter::refreshColors()
updateBackgroundCache();
}
QRect VolumeMeter::getBarRect() const
{
QRect barRect = rect();
if (vertical) {
barRect.setWidth(displayNrAudioChannels * (meterThickness + 1) - 1);
} else {
barRect.setHeight(displayNrAudioChannels * (meterThickness + 1) - 1);
}
return barRect;
}
// When this is called from the constructor, obs_volmeter_get_nr_channels has not
// yet been called and Q_PROPERTY settings have not yet been read from the
// stylesheet.
@@ -476,28 +481,19 @@ inline void VolumeMeter::doLayout()
int meterSize = std::floor(22 / displayNrAudioChannels);
meterThickness = std::clamp(meterSize, 3, 6);
}
recalculateLayout = false;
tickFont = font();
QFontInfo info(tickFont);
tickFont.setPointSizeF(info.pointSizeF() * meterFontScaling);
QFontMetrics metrics(tickFont);
if (vertical) {
// Each meter channel is meterThickness pixels wide, plus one pixel
// between channels, but not after the last.
// Add 4 pixels for ticks, space to hold our longest label in this font,
// and a few pixels before the fader.
QRect scaleBounds = metrics.boundingRect("-88");
setMinimumSize(displayNrAudioChannels * (meterThickness + 1) - 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(100, displayNrAudioChannels * (meterThickness + 1) - 1 + 4 + metrics.capHeight());
}
QFontMetrics metrics(tickFont);
// This is a quick and naive assumption for widest potential tick label.
tickTextTokenRect = metrics.boundingRect(" -88 ");
updateBackgroundCache();
resetLevels();
updateGeometry();
}
inline bool VolumeMeter::detectIdle(uint64_t ts)
@@ -607,7 +603,7 @@ void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width)
painter.setPen(majorTickColor);
// Draw major tick lines and numeric indicators.
for (int i = 0; i >= minimumLevel; i -= 6) {
for (int i = 0; i >= minimumLevel; i -= TICK_DB_INTERVAL) {
int position = int(x + width - (i * scale) - 1);
QString str = QString::number(i);
@@ -624,7 +620,7 @@ void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width)
}
painter.drawText(pos, y + 4 + metrics.capHeight(), str);
painter.drawLine(position, y, position, y + 2);
painter.drawLine(position, y, position, y + TICK_SIZE);
}
}
@@ -637,7 +633,7 @@ void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height)
painter.setPen(majorTickColor);
// Draw major tick lines and numeric indicators.
for (int i = 0; i >= minimumLevel; i -= 6) {
for (int i = 0; i >= minimumLevel; i -= TICK_DB_INTERVAL) {
int position = y + int(i * scale);
QString str = QString::number(i);
@@ -648,12 +644,24 @@ void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height)
painter.drawText(x + 8, position + (metrics.capHeight() / 2), str);
}
painter.drawLine(x, position, x + 2, position);
painter.drawLine(x, position, x + TICK_SIZE, position);
}
}
void VolumeMeter::updateBackgroundCache()
{
if (!size().isValid()) {
return;
}
if (backgroundCache.size() == size() && !backgroundCache.isNull()) {
return;
}
if (displayNrAudioChannels <= 0) {
return;
}
QColor backgroundColor = palette().color(QPalette::Window);
backgroundCache = QPixmap(size() * devicePixelRatioF());
@@ -705,8 +713,6 @@ void VolumeMeter::updateBackgroundCache()
}
}
#define CLIP_FLASH_DURATION_MS 1000
inline int VolumeMeter::convertToInt(float number)
{
constexpr int min = std::numeric_limits<int>::min();
@@ -743,9 +749,6 @@ void VolumeMeter::paintEvent(QPaintEvent *)
const qreal scale = meterLength / minimumLevel;
// Paint cached background pixmap
if (backgroundCache.isNull() || backgroundCache.size() != size()) {
updateBackgroundCache();
}
painter.drawPixmap(0, 0, backgroundCache);
// Draw dynamic audio meter bars
@@ -849,23 +852,42 @@ void VolumeMeter::paintEvent(QPaintEvent *)
lastRedrawTime = ts;
}
QRect VolumeMeter::getBarRect() const
void VolumeMeter::resizeEvent(QResizeEvent *event)
{
QRect rec = rect();
updateBackgroundCache();
return QWidget::resizeEvent(event);
}
void VolumeMeter::mousePressEvent(QMouseEvent *event)
{
setFocus(Qt::MouseFocusReason);
event->accept();
}
void VolumeMeter::wheelEvent(QWheelEvent *event)
{
QApplication::sendEvent(focusProxy(), event);
}
QSize VolumeMeter::minimumSizeHint() const
{
return sizeHint();
}
QSize VolumeMeter::sizeHint() const
{
QRect meterRect = getBarRect();
int labelTotal = std::abs(minimumLevel / TICK_DB_INTERVAL) + 1;
if (vertical) {
rec.setWidth(displayNrAudioChannels * (meterThickness + 1) - 1);
int width = meterRect.width() + tickTextTokenRect.width() + TICK_SIZE + 10;
int height = (labelTotal * tickTextTokenRect.height()) + INDICATOR_THICKNESS;
return QSize(width, height * 1.1);
} else {
rec.setHeight(displayNrAudioChannels * (meterThickness + 1) - 1);
int width = (labelTotal * tickTextTokenRect.width()) + INDICATOR_THICKNESS;
int height = meterRect.height() + tickTextTokenRect.height();
return QSize(width * 1.1, height);
}
return rec;
}
void VolumeMeter::changeEvent(QEvent *e)
{
if (e->type() == QEvent::StyleChange) {
recalculateLayout = true;
}
QWidget::changeEvent(e);
}
+7 -3
View File
@@ -69,7 +69,6 @@ private:
QMutex dataMutex;
bool recalculateLayout{true};
uint64_t currentLastUpdateTime{0};
float currentMagnitude[MAX_AUDIO_CHANNELS];
float currentPeak[MAX_AUDIO_CHANNELS];
@@ -87,6 +86,8 @@ private:
void updateBackgroundCache();
QFont tickFont;
QRect tickTextTokenRect;
QColor backgroundNominalColor;
QColor backgroundWarningColor;
QColor backgroundErrorColor;
@@ -140,7 +141,6 @@ public:
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
QRect getBarRect() const;
bool needLayoutChange();
void setVertical(bool vertical = true);
@@ -148,6 +148,7 @@ public:
void setMuted(bool mute);
void refreshColors();
QRect getBarRect() const;
QColor getBackgroundNominalColor() const;
void setBackgroundNominalColor(QColor c);
@@ -190,12 +191,15 @@ public:
void setPeakDecayRate(qreal v);
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
virtual void resizeEvent(QResizeEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void wheelEvent(QWheelEvent *event) override;
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
protected:
void paintEvent(QPaintEvent *event) override;
void changeEvent(QEvent *e) override;
private slots:
void handleSourceDestroyed() { deleteLater(); }
+9 -3
View File
@@ -110,6 +110,7 @@ AudioMixer::AudioMixer(QWidget *parent) : QFrame(parent)
hVolumeWidgets->setLayout(hVolumeControlLayout);
hVolumeControlLayout->setContentsMargins(0, 0, 0, 0);
hVolumeControlLayout->setSpacing(0);
hVolumeControlLayout->setAlignment(Qt::AlignTop);
hMixerScrollArea->setWidget(hVolumeWidgets);
@@ -683,8 +684,8 @@ void AudioMixer::updateVolumeLayouts()
vMixerScrollArea->setWidgetResizable(false);
hMixerScrollArea->setWidgetResizable(false);
QSize minimumSize{};
for (const auto &entry : rankedVolumes) {
VolumeControl *volControl = entry.control;
if (!volControl) {
continue;
@@ -712,6 +713,10 @@ void AudioMixer::updateVolumeLayouts()
prevControl = volControl;
if (!minimumSize.isValid()) {
minimumSize = volControl->minimumSizeHint();
}
++index;
}
@@ -727,6 +732,9 @@ void AudioMixer::updateVolumeLayouts()
vMixerScrollArea->setWidgetResizable(true);
hMixerScrollArea->setWidgetResizable(true);
int scrollBarSize = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
stackedMixerArea->setMinimumSize(minimumSize.width() + scrollBarSize, minimumSize.height() + scrollBarSize);
setUpdatesEnabled(true);
}
@@ -740,7 +748,6 @@ void AudioMixer::setMixerLayoutVertical(bool vertical)
mixerVertical = vertical;
if (vertical) {
stackedMixerArea->setMinimumSize(180, 220);
stackedMixerArea->setCurrentIndex(1);
QIcon layoutIcon;
@@ -749,7 +756,6 @@ void AudioMixer::setMixerLayoutVertical(bool vertical)
layoutButton->setIcon(layoutIcon);
layoutButton->setToolTip(QTStr("Basic.AudioMixer.Layout.Horizontal"));
} else {
stackedMixerArea->setMinimumSize(220, 0);
stackedMixerArea->setCurrentIndex(0);
QIcon layoutIcon;