Show call video in a separate resizable desktop window
This commit is contained in:
1 parent
401ca96d7d
commit
9f8fb64e6e
2 files changed
+44
-54
No files matched your search
@@ -35,12 +35,29 @@ enum class Glyph { Phone, PhoneDown, Mic, MicOff };
|
||||
|
||||
static QString callText(const char* text) { return QCoreApplication::translate("ChatGui", text); }
|
||||
|
||||
// Только изображение и миниатюра; подписи/настройки находятся в отдельной панели окна.
|
||||
class CallVideoCanvas : public QWidget {
|
||||
// Отдельное окно показа. Закрытие скрывает только изображение, не завершает звонок.
|
||||
class CallVideoWindow : public QWidget {
|
||||
public:
|
||||
explicit CallVideoCanvas(QWidget* parent) : QWidget(parent) { setMinimumSize(160, 120); }
|
||||
explicit CallVideoWindow(QWidget* parent) : QWidget(parent, Qt::Window) {
|
||||
setObjectName("callVideoWindow"); setMinimumSize(320, 240); setAttribute(Qt::WA_QuitOnClose, false);
|
||||
QSize available = parent->screen() ? parent->screen()->availableGeometry().size() : QSize(1280, 900);
|
||||
resize(qMin(1100, available.width() * 9 / 10), qMin(800, available.height() * 9 / 10));
|
||||
}
|
||||
QImage remote, local;
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event) override {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call video window closed; call continues"); event->accept();
|
||||
}
|
||||
void keyPressEvent(QKeyEvent* event) override {
|
||||
if (event->key() == Qt::Key_F11 || (event->key() == Qt::Key_Escape && isFullScreen())) {
|
||||
toggleFullScreen(); event->accept(); return;
|
||||
}
|
||||
QWidget::keyPressEvent(event);
|
||||
}
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override {
|
||||
if (event->button() == Qt::LeftButton) { toggleFullScreen(); event->accept(); return; }
|
||||
QWidget::mouseDoubleClickEvent(event);
|
||||
}
|
||||
void paintEvent(QPaintEvent*) override {
|
||||
QPainter painter(this); painter.fillRect(rect(), Qt::black); painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
if (remote.isNull()) return;
|
||||
@@ -53,6 +70,12 @@ protected:
|
||||
painter.setPen(QPen(Qt::white, 2)); painter.drawRect(target.adjusted(-1, -1, 1, 1)); painter.drawImage(target, local);
|
||||
}
|
||||
}
|
||||
private:
|
||||
void toggleFullScreen() {
|
||||
if (isFullScreen()) { showNormal(); if (!m_geometry.isEmpty()) restoreGeometry(m_geometry); }
|
||||
else { m_geometry = saveGeometry(); showFullScreen(); }
|
||||
}
|
||||
QByteArray m_geometry;
|
||||
};
|
||||
|
||||
static QColor colorForName(const QString& name) {
|
||||
@@ -215,10 +238,10 @@ CallWindow::CallWindow(QWidget* parent)
|
||||
|
||||
auto* root = new QVBoxLayout(this);
|
||||
root->setContentsMargins(12, 12, 12, 12);
|
||||
m_videoArea = new CallVideoCanvas(this); m_videoArea->hide(); root->addWidget(m_videoArea, 1);
|
||||
m_videoWindow = new CallVideoWindow(this);
|
||||
m_panel = new QWidget(this); m_panel->setObjectName("callPanel"); m_panel->setMaximumWidth(640);
|
||||
m_panel->setStyleSheet("QWidget#callPanel { background: palette(window); border-radius: 12px; }");
|
||||
root->addWidget(m_panel, 0, Qt::AlignHCenter | Qt::AlignBottom);
|
||||
root->addWidget(m_panel, 0, Qt::AlignCenter);
|
||||
QVBoxLayout* lay = new QVBoxLayout(m_panel);
|
||||
lay->setContentsMargins(16, 20, 16, 16);
|
||||
lay->setSpacing(8);
|
||||
@@ -239,10 +262,14 @@ CallWindow::CallWindow(QWidget* parent)
|
||||
m_videoEnabled = new QCheckBox(callText("Send video"), this); actions->addWidget(m_videoEnabled);
|
||||
actions->addStretch(1);
|
||||
m_settingsBtn = new QPushButton(callText("Call settings"), this); m_settingsBtn->setCheckable(true); actions->addWidget(m_settingsBtn);
|
||||
m_fullScreenBtn = new QPushButton(callText("Full screen"), this); actions->addWidget(m_fullScreenBtn);
|
||||
m_showVideoBtn = new QPushButton(callText("Show video"), this); actions->addWidget(m_showVideoBtn);
|
||||
lay->addLayout(actions);
|
||||
connect(m_settingsBtn, &QPushButton::toggled, this, [this] { updateVideoPresentation(); });
|
||||
connect(m_fullScreenBtn, &QPushButton::clicked, this, &CallWindow::toggleFullScreen);
|
||||
connect(m_showVideoBtn, &QPushButton::clicked, this, [this] {
|
||||
if (m_videoWindow->isMinimized()) m_videoWindow->showNormal(); else m_videoWindow->show();
|
||||
m_videoWindow->raise(); m_videoWindow->activateWindow();
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call video window reopened");
|
||||
});
|
||||
auto* modes = new QHBoxLayout();
|
||||
m_camera = new QComboBox(this);
|
||||
for (const auto& camera : CallVideoEngine::cameras()) m_camera->addItem(camera.first, camera.second);
|
||||
@@ -267,13 +294,13 @@ CallWindow::CallWindow(QWidget* parent)
|
||||
});
|
||||
connect(m_video, &CallVideoEngine::imageChanged, this, [this](const QImage& image) {
|
||||
bool changed = m_remoteImage.isNull() != image.isNull(); m_remoteImage = image;
|
||||
static_cast<CallVideoCanvas*>(m_videoArea)->remote = image; m_videoArea->update();
|
||||
m_videoWindow->remote = image; m_videoWindow->update();
|
||||
if (changed) updateVideoPresentation();
|
||||
update();
|
||||
});
|
||||
connect(m_video, &CallVideoEngine::localImageChanged, this, [this](const QImage& image) {
|
||||
bool changed = m_localImage.isNull() != image.isNull(); m_localImage = image;
|
||||
static_cast<CallVideoCanvas*>(m_videoArea)->local = image; m_videoArea->update();
|
||||
m_videoWindow->local = image; m_videoWindow->update();
|
||||
if (!image.isNull()) {
|
||||
QSize size = image.size().scaled(200, 150, Qt::KeepAspectRatio); m_localPreview->setFixedSize(size);
|
||||
m_localPreview->setPixmap(QPixmap::fromImage(image).scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
@@ -328,6 +355,7 @@ CallWindow::~CallWindow() {
|
||||
void CallWindow::setPeerName(const QString& name) {
|
||||
m_peerLabel->setText(name);
|
||||
m_avatarLabel->setPixmap(makeAvatar(name, 80));
|
||||
m_videoWindow->setWindowTitle(callText("Video — %1").arg(name));
|
||||
}
|
||||
|
||||
void CallWindow::setBgpPath(const QString& path) {
|
||||
@@ -401,58 +429,23 @@ void CallWindow::updateButtons() {
|
||||
updateVideoPresentation();
|
||||
}
|
||||
|
||||
void CallWindow::leaveFullScreen() {
|
||||
if (!isFullScreen()) return;
|
||||
showNormal();
|
||||
if (!m_windowGeometry.isEmpty()) restoreGeometry(m_windowGeometry);
|
||||
}
|
||||
|
||||
void CallWindow::toggleFullScreen() {
|
||||
if (m_remoteImage.isNull()) return;
|
||||
if (isFullScreen()) leaveFullScreen();
|
||||
else { m_windowGeometry = saveGeometry(); showFullScreen(); }
|
||||
m_fullScreenBtn->setText(isFullScreen() ? callText("Exit full screen") : callText("Full screen"));
|
||||
}
|
||||
|
||||
void CallWindow::updateVideoPresentation() {
|
||||
bool receiving = m_state == Active && !m_remoteImage.isNull();
|
||||
if (receiving != m_receivingVideo) {
|
||||
m_receivingVideo = receiving;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call video presentation remote=%d local=%d", receiving, !m_localImage.isNull());
|
||||
if (receiving) {
|
||||
m_settingsBtn->setChecked(false); m_windowGeometry = saveGeometry();
|
||||
QSize available = screen() ? screen()->availableGeometry().size() : QSize(1280, 900);
|
||||
resize(qMin(1100, available.width() * 9 / 10), qMin(800, available.height() * 9 / 10));
|
||||
} else {
|
||||
leaveFullScreen();
|
||||
if (!m_windowGeometry.isEmpty()) restoreGeometry(m_windowGeometry);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call video separate window remote=%d local=%d", receiving, !m_localImage.isNull());
|
||||
if (receiving) m_videoWindow->show(); else m_videoWindow->hide();
|
||||
}
|
||||
bool active = m_state == Active, settings = active && m_settingsBtn->isChecked();
|
||||
m_videoArea->setVisible(receiving);
|
||||
layout()->setAlignment(m_panel, receiving ? Qt::AlignHCenter | Qt::AlignBottom : Qt::AlignCenter);
|
||||
m_localPreview->setVisible(active && !receiving && !m_localImage.isNull());
|
||||
m_avatarLabel->setVisible(!receiving); m_statusLabel->setVisible(!receiving);
|
||||
m_connectionLabel->setVisible(settings); m_bgpPathLabel->setVisible(settings && !m_bgpPathLabel->text().isEmpty());
|
||||
m_statsLabel->setVisible(settings && !m_statsLabel->text().isEmpty());
|
||||
m_videoEnabled->setVisible(active); m_settingsBtn->setVisible(active);
|
||||
m_videoControls->setVisible(settings); m_fullScreenBtn->setVisible(receiving);
|
||||
m_fullScreenBtn->setText(isFullScreen() ? callText("Exit full screen") : callText("Full screen"));
|
||||
m_videoControls->setVisible(settings); m_showVideoBtn->setVisible(receiving);
|
||||
m_videoStatus->setVisible(active && !m_videoStatus->text().isEmpty());
|
||||
update();
|
||||
}
|
||||
|
||||
void CallWindow::keyPressEvent(QKeyEvent* event) {
|
||||
if (event->key() == Qt::Key_Escape && isFullScreen()) { toggleFullScreen(); event->accept(); return; }
|
||||
if (event->key() == Qt::Key_F11 && m_receivingVideo) { toggleFullScreen(); event->accept(); return; }
|
||||
QWidget::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void CallWindow::mouseDoubleClickEvent(QMouseEvent* event) {
|
||||
if (event->button() == Qt::LeftButton && m_receivingVideo) { toggleFullScreen(); event->accept(); return; }
|
||||
QWidget::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
void CallWindow::applyVideo() {
|
||||
if (!m_videoCall) return;
|
||||
QSize size = m_resolution->currentData().toSize();
|
||||
|
||||
Reference in new issue
Block a user