|
|
|
|
@ -16,6 +16,8 @@
|
|
|
|
|
#include <QTimer> |
|
|
|
|
#include <QDateTime> |
|
|
|
|
#include <QCloseEvent> |
|
|
|
|
#include <QKeyEvent> |
|
|
|
|
#include <QMouseEvent> |
|
|
|
|
#include <QPainter> |
|
|
|
|
#include <QPainterPath> |
|
|
|
|
#include <cstring> |
|
|
|
|
@ -142,8 +144,8 @@ static QWidget* wrapWithCaption(QWidget* btn, const QString& caption, QLabel** c
|
|
|
|
|
CallWindow::CallWindow(QWidget* parent) |
|
|
|
|
: QWidget(parent, Qt::Window) { |
|
|
|
|
setWindowTitle(QCoreApplication::translate("ChatGui", "Call")); |
|
|
|
|
resize(640, 800); |
|
|
|
|
setMinimumSize(400, 600); |
|
|
|
|
resize(520, 520); |
|
|
|
|
setMinimumSize(420, 360); |
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose, false); |
|
|
|
|
|
|
|
|
|
m_avatarLabel = new QLabel(this); |
|
|
|
|
@ -188,7 +190,12 @@ CallWindow::CallWindow(QWidget* parent)
|
|
|
|
|
"QComboBox { border: 1px solid palette(mid); border-radius: 4px;" |
|
|
|
|
" padding: 6px 10px; font-size: 13px; background: palette(base); }"); |
|
|
|
|
|
|
|
|
|
QVBoxLayout* lay = new QVBoxLayout(this); |
|
|
|
|
auto* root = new QVBoxLayout(this); |
|
|
|
|
root->setContentsMargins(12, 12, 12, 12); root->addStretch(1); |
|
|
|
|
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); |
|
|
|
|
QVBoxLayout* lay = new QVBoxLayout(m_panel); |
|
|
|
|
lay->setContentsMargins(16, 20, 16, 16); |
|
|
|
|
lay->setSpacing(8); |
|
|
|
|
lay->addWidget(m_avatarLabel, 0, Qt::AlignHCenter); |
|
|
|
|
@ -199,12 +206,19 @@ CallWindow::CallWindow(QWidget* parent)
|
|
|
|
|
lay->addWidget(m_bgpPathLabel); |
|
|
|
|
lay->addWidget(m_statsLabel); |
|
|
|
|
m_video = new CallVideoEngine(this); |
|
|
|
|
m_videoImage = new QLabel(this); m_videoImage->setMinimumHeight(180); m_videoImage->setAlignment(Qt::AlignCenter); |
|
|
|
|
lay->addWidget(m_videoImage, 1); |
|
|
|
|
m_localPreview = new QLabel(this); m_localPreview->setAlignment(Qt::AlignCenter); m_localPreview->hide(); |
|
|
|
|
lay->addWidget(m_localPreview, 0, Qt::AlignHCenter); |
|
|
|
|
m_videoStatus = new QLabel(this); m_videoStatus->setWordWrap(true); lay->addWidget(m_videoStatus); |
|
|
|
|
m_videoControls = new QWidget(this); auto* videoLayout = new QVBoxLayout(m_videoControls); |
|
|
|
|
videoLayout->setContentsMargins(0, 0, 0, 0); |
|
|
|
|
m_videoEnabled = new QCheckBox(tr("Send video"), this); videoLayout->addWidget(m_videoEnabled); |
|
|
|
|
auto* actions = new QHBoxLayout(); |
|
|
|
|
m_videoEnabled = new QCheckBox(tr("Send video"), this); actions->addWidget(m_videoEnabled); |
|
|
|
|
actions->addStretch(1); |
|
|
|
|
m_settingsBtn = new QPushButton(tr("Call settings"), this); m_settingsBtn->setCheckable(true); actions->addWidget(m_settingsBtn); |
|
|
|
|
m_fullScreenBtn = new QPushButton(tr("Full screen"), this); actions->addWidget(m_fullScreenBtn); |
|
|
|
|
lay->addLayout(actions); |
|
|
|
|
connect(m_settingsBtn, &QPushButton::toggled, this, [this] { updateVideoPresentation(); }); |
|
|
|
|
connect(m_fullScreenBtn, &QPushButton::clicked, this, &CallWindow::toggleFullScreen); |
|
|
|
|
auto* modes = new QHBoxLayout(); |
|
|
|
|
m_camera = new QComboBox(this); |
|
|
|
|
for (const auto& camera : CallVideoEngine::cameras()) m_camera->addItem(camera.first, camera.second); |
|
|
|
|
@ -224,9 +238,22 @@ CallWindow::CallWindow(QWidget* parent)
|
|
|
|
|
}; |
|
|
|
|
connect(m_videoMin, &QSpinBox::editingFinished, this, updateRange); |
|
|
|
|
connect(m_videoMax, &QSpinBox::editingFinished, this, updateRange); |
|
|
|
|
connect(m_video, &CallVideoEngine::statusChanged, m_videoStatus, &QLabel::setText); |
|
|
|
|
connect(m_video, &CallVideoEngine::statusChanged, this, [this](const QString& text) { |
|
|
|
|
m_videoStatus->setText(text); m_videoStatus->setVisible(m_state == Active && !text.isEmpty()); |
|
|
|
|
}); |
|
|
|
|
connect(m_video, &CallVideoEngine::imageChanged, this, [this](const QImage& image) { |
|
|
|
|
m_videoImage->setPixmap(image.isNull() ? QPixmap() : QPixmap::fromImage(image).scaled(m_videoImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); |
|
|
|
|
bool changed = m_remoteImage.isNull() != image.isNull(); m_remoteImage = image; |
|
|
|
|
if (changed) updateVideoPresentation(); |
|
|
|
|
update(); |
|
|
|
|
}); |
|
|
|
|
connect(m_video, &CallVideoEngine::localImageChanged, this, [this](const QImage& image) { |
|
|
|
|
bool changed = m_localImage.isNull() != image.isNull(); m_localImage = image; |
|
|
|
|
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)); |
|
|
|
|
} else m_localPreview->clear(); |
|
|
|
|
if (changed) updateVideoPresentation(); |
|
|
|
|
update(); |
|
|
|
|
}); |
|
|
|
|
auto* videoTimer = new QTimer(this); videoTimer->setInterval(100); |
|
|
|
|
connect(videoTimer, &QTimer::timeout, this, [this] { |
|
|
|
|
@ -237,7 +264,6 @@ CallWindow::CallWindow(QWidget* parent)
|
|
|
|
|
m_videoMin->setValue(status.minimum_bps / 1000); m_videoMax->setValue(status.maximum_bps / 1000); m_videoRangeReady = true; |
|
|
|
|
} |
|
|
|
|
}); videoTimer->start(); |
|
|
|
|
lay->addStretch(1); |
|
|
|
|
|
|
|
|
|
m_acceptCell = wrapWithCaption(m_acceptBtn, QCoreApplication::translate("ChatGui", "Accept")); |
|
|
|
|
m_declineCell = wrapWithCaption(m_declineBtn, QCoreApplication::translate("ChatGui", "Decline")); |
|
|
|
|
@ -253,8 +279,7 @@ CallWindow::CallWindow(QWidget* parent)
|
|
|
|
|
btn->addWidget(m_muteCell); |
|
|
|
|
btn->addStretch(1); |
|
|
|
|
lay->addLayout(btn); |
|
|
|
|
lay->addStretch(1); |
|
|
|
|
lay->addWidget(m_deviceCombo); |
|
|
|
|
videoLayout->addWidget(m_deviceCombo); |
|
|
|
|
|
|
|
|
|
connect(m_acceptBtn, &QPushButton::clicked, this, &CallWindow::onAccept); |
|
|
|
|
connect(m_declineBtn, &QPushButton::clicked, this, &CallWindow::onDecline); |
|
|
|
|
@ -278,7 +303,7 @@ void CallWindow::setPeerName(const QString& name) {
|
|
|
|
|
|
|
|
|
|
void CallWindow::setBgpPath(const QString& path) { |
|
|
|
|
m_bgpPathLabel->setText(path); |
|
|
|
|
m_bgpPathLabel->setVisible(!path.isEmpty()); |
|
|
|
|
m_bgpPathLabel->setVisible(!path.isEmpty() && m_settingsBtn->isChecked()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CallWindow::setConnectionType(uint8_t type, quint64 nextHop) { |
|
|
|
|
@ -344,10 +369,66 @@ void CallWindow::updateButtons() {
|
|
|
|
|
m_declineCell->setVisible(m_state == Incoming); |
|
|
|
|
m_hangupCell->setVisible(m_state == Outgoing || m_state == Ringing || m_state == Active); |
|
|
|
|
m_muteCell->setVisible(m_state == Active); |
|
|
|
|
m_deviceCombo->setVisible(m_state == Active); |
|
|
|
|
m_videoControls->setVisible(m_state == Active); |
|
|
|
|
m_videoImage->setVisible(m_state == Active); |
|
|
|
|
m_videoStatus->setVisible(m_state == Active); |
|
|
|
|
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() ? tr("Exit full screen") : tr("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(); showFullScreen(); |
|
|
|
|
} else leaveFullScreen(); |
|
|
|
|
} |
|
|
|
|
bool active = m_state == Active, settings = active && m_settingsBtn->isChecked(); |
|
|
|
|
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() ? tr("Exit full screen") : tr("Full screen")); |
|
|
|
|
m_videoStatus->setVisible(active && !m_videoStatus->text().isEmpty()); |
|
|
|
|
update(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CallWindow::paintEvent(QPaintEvent* event) { |
|
|
|
|
QWidget::paintEvent(event); |
|
|
|
|
if (!m_receivingVideo) return; |
|
|
|
|
QPainter painter(this); painter.fillRect(rect(), Qt::black); painter.setRenderHint(QPainter::SmoothPixmapTransform); |
|
|
|
|
QSize remote = m_remoteImage.size().scaled(size(), Qt::KeepAspectRatio); |
|
|
|
|
painter.drawImage(QRect(QPoint((width() - remote.width()) / 2, (height() - remote.height()) / 2), remote), m_remoteImage); |
|
|
|
|
if (!m_localImage.isNull()) { |
|
|
|
|
QSize preview = m_localImage.size().scaled(qMin(240, width() / 4), qMin(180, height() / 4), Qt::KeepAspectRatio); |
|
|
|
|
QRect target(QPoint(width() - preview.width() - 20, 20), preview); |
|
|
|
|
painter.setPen(QPen(Qt::white, 2)); painter.drawRect(target.adjusted(-1, -1, 1, 1)); |
|
|
|
|
painter.drawImage(target, m_localImage); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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() { |
|
|
|
|
|