Просмотр исходного кода

frontend: Implement sizeHint for VolumeMeter

Warchamp7 3 недель назад
Родитель
Сommit
e73662f6c1

+ 76 - 54
frontend/components/VolumeMeter.cpp

@@ -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());
-	}
+	// 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();
-	if (vertical) {
-		rec.setWidth(displayNrAudioChannels * (meterThickness + 1) - 1);
-	} else {
-		rec.setHeight(displayNrAudioChannels * (meterThickness + 1) - 1);
-	}
+	updateBackgroundCache();
+	return QWidget::resizeEvent(event);
+}
 
-	return rec;
+void VolumeMeter::mousePressEvent(QMouseEvent *event)
+{
+	setFocus(Qt::MouseFocusReason);
+	event->accept();
 }
 
-void VolumeMeter::changeEvent(QEvent *e)
+void VolumeMeter::wheelEvent(QWheelEvent *event)
 {
-	if (e->type() == QEvent::StyleChange) {
-		recalculateLayout = true;
-	}
+	QApplication::sendEvent(focusProxy(), event);
+}
+
+QSize VolumeMeter::minimumSizeHint() const
+{
+	return sizeHint();
+}
 
-	QWidget::changeEvent(e);
+QSize VolumeMeter::sizeHint() const
+{
+	QRect meterRect = getBarRect();
+	int labelTotal = std::abs(minimumLevel / TICK_DB_INTERVAL) + 1;
+
+	if (vertical) {
+		int width = meterRect.width() + tickTextTokenRect.width() + TICK_SIZE + 10;
+		int height = (labelTotal * tickTextTokenRect.height()) + INDICATOR_THICKNESS;
+
+		return QSize(width, height * 1.1);
+	} else {
+		int width = (labelTotal * tickTextTokenRect.width()) + INDICATOR_THICKNESS;
+		int height = meterRect.height() + tickTextTokenRect.height();
+
+		return QSize(width * 1.1, height);
+	}
 }

+ 7 - 3
frontend/components/VolumeMeter.hpp

@@ -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
frontend/widgets/AudioMixer.cpp

@@ -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;