From 5a8807bf89f18f8dd1a1f5c4499aca050dc043cb Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 1 Nov 2026 22:23:09 +0100 Subject: [PATCH] Keep multiview captions and controls below video and use logarithmic volume --- tools/chatgui/src/streamgrid_window.cpp | 21 +++++++++++++++------ tools/chatgui/src/streamgrid_window.h | 1 + tools/chatgui/src/streamplayer_window.cpp | 11 ++++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/tools/chatgui/src/streamgrid_window.cpp b/tools/chatgui/src/streamgrid_window.cpp index f6cc8ce1e..eadcbbb59 100644 --- a/tools/chatgui/src/streamgrid_window.cpp +++ b/tools/chatgui/src/streamgrid_window.cpp @@ -29,10 +29,12 @@ QString text(const char* value) { return QCoreApplication::translate("ChatGui", StreamGridWindow::StreamGridWindow(uint64_t group, QWidget* parent) : QWidget(parent), m_group(group), m_owner(++nextOwner) { setWindowTitle(text("All active streams")); resize(1100, 750); m_clock.start(); - auto* layout = new QVBoxLayout(this); auto* controls = new QHBoxLayout; - m_status = new QLabel(text("Connecting…"), this); controls->addWidget(m_status, 1); - auto* fullscreen = new QPushButton(text("Fullscreen"), this); controls->addWidget(fullscreen); - m_close = new QPushButton(text("Close player"), this); controls->addWidget(m_close); layout->addLayout(controls); + auto* layout = new QVBoxLayout(this); + m_controls = new QWidget(this); m_controls->setObjectName(QStringLiteral("streamGridControls")); + auto* controls = new QHBoxLayout(m_controls); controls->setContentsMargins(0, 0, 0, 0); + m_status = new QLabel(text("Connecting…"), m_controls); controls->addWidget(m_status, 1); + auto* fullscreen = new QPushButton(text("Fullscreen"), m_controls); controls->addWidget(fullscreen); + m_close = new QPushButton(text("Close player"), m_controls); controls->addWidget(m_close); connect(fullscreen, &QPushButton::clicked, this, [this] { if (isFullScreen()) showNormal(); else showFullScreen(); }); connect(m_close, &QPushButton::clicked, this, [this] { if (m_focused) restoreGrid(); else close(); }); auto* escape = new QShortcut(QKeySequence(Qt::Key_Escape), this); escape->setContext(Qt::WidgetWithChildrenShortcut); @@ -44,6 +46,7 @@ StreamGridWindow::StreamGridWindow(uint64_t group, QWidget* parent) m_empty = new QLabel(text("Waiting for active streams…"), this); m_empty->setAlignment(Qt::AlignCenter); layout->addWidget(m_empty); m_grid = new QGridLayout; m_grid->setSpacing(4); layout->addLayout(m_grid, 1); + layout->addWidget(m_controls); m_watching = gui_stream_watch(m_group, m_owner); connect(&m_timer, &QTimer::timeout, this, &StreamGridWindow::tick); m_timer.start(250); DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop multiview opened group=%llx owner=%llu", @@ -59,8 +62,8 @@ void StreamGridWindow::createTile(Tile& tile) { tile.widget->setObjectName(QStringLiteral("streamPreview_%1").arg(tile.source)); auto* layout = new QVBoxLayout(tile.widget); layout->setContentsMargins(3, 3, 3, 3); layout->setSpacing(2); tile.title = new QLabel(tile.name, tile.widget); tile.title->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - tile.title->setToolTip(tile.name); layout->addWidget(tile.title); - tile.status = new QLabel(text("Connecting…"), tile.widget); layout->addWidget(tile.status); + tile.title->setToolTip(tile.name); + tile.status = new QLabel(text("Connecting…"), tile.widget); #ifdef HAVE_STREAM_CAPTURE auto* preview = new VideoRenderWidget(tile.widget); tile.video = preview; @@ -68,12 +71,16 @@ void StreamGridWindow::createTile(Tile& tile) { tile.video = new QLabel(text("Video capture requires Qt Multimedia and FFmpeg"), tile.widget); #endif tile.video->setToolTip(text("Manage stream")); layout->addWidget(tile.video, 1); + layout->addWidget(tile.title); layout->addWidget(tile.status); updatePreview(tile); DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop multiview local preview group=%llx source=%llx epoch=%llx decoder=0 audio=0", (unsigned long long)m_group, (unsigned long long)tile.source, (unsigned long long)tile.epoch); } else { tile.player = new StreamPlayerWindow(m_group, tile.source, tile.epoch, tile.name, this, true); tile.widget = tile.player; tile.video = tile.player->findChild(); + connect(tile.player, &StreamPlayerWindow::fullscreenRequested, this, + [this] { if (isFullScreen()) showNormal(); else showFullScreen(); }); + connect(tile.player, &StreamPlayerWindow::closeRequested, this, &StreamGridWindow::restoreGrid); } if (!tile.video) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop multiview tile has no video source=%llx", (unsigned long long)tile.source); return; @@ -215,12 +222,14 @@ void StreamGridWindow::arrange() { for (int i = 0; i < m_side; ++i) { m_grid->setRowStretch(i, 1); m_grid->setColumnStretch(i, 1); } for (size_t i = 0; i < m_tiles.size(); ++i) { auto& tile = m_tiles[i]; + if (tile.player) tile.player->setWindowControlsVisible(m_focused == tile.source); if (m_focused && m_focused != tile.source) { tile.widget->hide(); continue; } if (m_focused) m_grid->addWidget(tile.widget, 0, 0, m_side, m_side); else m_grid->addWidget(tile.widget, static_cast(i) / m_side, static_cast(i) % m_side); tile.widget->show(); } m_empty->setVisible(m_tiles.empty()); + m_controls->setVisible(!m_focused); m_close->setText(text(m_focused ? "All active streams" : "Close player")); DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop multiview layout group=%llx tiles=%zu grid=%dx%d focused=%llx", (unsigned long long)m_group, m_tiles.size(), m_side, m_side, (unsigned long long)m_focused); diff --git a/tools/chatgui/src/streamgrid_window.h b/tools/chatgui/src/streamgrid_window.h index c82aa45dc..30410f4e2 100644 --- a/tools/chatgui/src/streamgrid_window.h +++ b/tools/chatgui/src/streamgrid_window.h @@ -64,6 +64,7 @@ private: std::vector m_tiles; // Каждый widget принадлежит окну; удаляется целиком при смене идентичности/stop. // Элементы интерфейса принадлежат QObject-дереву этого окна. QGridLayout* m_grid = nullptr; + QWidget* m_controls = nullptr; // Общая нижняя панель сетки; при focus её заменяет нижняя панель выбранного player. QLabel* m_status = nullptr; QLabel* m_empty = nullptr; QPushButton* m_close = nullptr; diff --git a/tools/chatgui/src/streamplayer_window.cpp b/tools/chatgui/src/streamplayer_window.cpp index 24ca2a157..edeac275a 100644 --- a/tools/chatgui/src/streamplayer_window.cpp +++ b/tools/chatgui/src/streamplayer_window.cpp @@ -25,6 +25,7 @@ #include #include #include +#include extern "C" { #include "../../../lib/debug_config.h" @@ -199,7 +200,7 @@ StreamPlayerWindow::StreamPlayerWindow(uint64_t group, uint64_t source, uint64_t auto* volumeBox = new QWidget(bar); auto* volumeLayout = new QVBoxLayout(volumeBox); volumeLayout->setContentsMargins(0, 0, 0, 0); volumeLayout->setSpacing(0); auto* volume = new StreamVolumeSlider(volumeBox); volume->setObjectName(QStringLiteral("streamVolume")); - volume->setAccessibleName(text("Stream volume")); volume->setToolTip(QStringLiteral("100%")); volumeLayout->addWidget(volume); + volume->setAccessibleName(text("Stream volume")); volume->setToolTip(QStringLiteral("0 dB")); volumeLayout->addWidget(volume); auto* volumeLabel = new QLabel(text("Volume"), volumeBox); volumeLabel->setAlignment(Qt::AlignCenter); volumeLayout->addWidget(volumeLabel); controls->addWidget(volumeBox); m_fullscreen = new QPushButton(text("Fullscreen"), bar); m_fullscreen->setObjectName(QStringLiteral("streamFullscreen")); @@ -210,8 +211,12 @@ StreamPlayerWindow::StreamPlayerWindow(uint64_t group, uint64_t source, uint64_t setWindowControlsVisible(false); layout->setContentsMargins(3, 3, 3, 3); layout->setSpacing(2); setMinimumSize(0, 0); } connect(volume, &QSlider::valueChanged, this, [this, volume](int value) { - m_playback->volume = value / 100.0f; volume->setToolTip(QStringLiteral("%1%").arg(value)); - DEBUG_DEBUG(DEBUG_CATEGORY_STREAM, "desktop viewer volume id=%llu percent=%d", (unsigned long long)m_playback->id, value); + const float decibels = (value - 100) * 0.6f; + const float gain = value ? std::pow(10.0f, decibels / 20.0f) : 0.0f; + m_playback->volume = gain; + volume->setToolTip(value ? QStringLiteral("%1 dB").arg(decibels, 0, 'f', 1) : QStringLiteral("−∞ dB")); + DEBUG_DEBUG(DEBUG_CATEGORY_STREAM, "desktop viewer volume id=%llu position=%d gain=%.6f", + (unsigned long long)m_playback->id, value, gain); }); connect(m_fullscreen, &QPushButton::clicked, this, [this, embedded] { if (embedded) emit fullscreenRequested(); else if (isFullScreen()) showNormal(); else showFullScreen();