From da27670cbe474cf7e4f487b11d58c63e7cefb976 Mon Sep 17 00:00:00 2001 From: trilene Date: Tue, 22 Sep 2020 12:07:36 -0400 Subject: [PATCH 1/5] Port ActiveCallBar to Qml --- CMakeLists.txt | 2 - resources/qml/ImageButton.qml | 5 +- resources/qml/TimelineView.qml | 140 +++++++++++++++++++++++ src/ActiveCallBar.cpp | 160 --------------------------- src/ActiveCallBar.h | 40 ------- src/CallManager.cpp | 22 +--- src/CallManager.h | 4 - src/ChatPage.cpp | 8 -- src/ChatPage.h | 2 - src/MainWindow.cpp | 4 +- src/TextInputWidget.cpp | 8 +- src/TextInputWidget.h | 2 +- src/WebRTCSession.cpp | 43 ++++--- src/WebRTCSession.h | 41 ++++--- src/timeline/TimelineViewManager.cpp | 2 + src/timeline/TimelineViewManager.h | 6 + 16 files changed, 212 insertions(+), 277 deletions(-) delete mode 100644 src/ActiveCallBar.cpp delete mode 100644 src/ActiveCallBar.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fe686ddf..f5628e2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -279,7 +279,6 @@ set(SRC_FILES src/ui/Theme.cpp src/ui/ThemeManager.cpp - src/ActiveCallBar.cpp src/AvatarProvider.cpp src/BlurhashProvider.cpp src/Cache.cpp @@ -491,7 +490,6 @@ qt5_wrap_cpp(MOC_HEADERS src/notifications/Manager.h - src/ActiveCallBar.h src/AvatarProvider.h src/BlurhashProvider.h src/Cache_p.h diff --git a/resources/qml/ImageButton.qml b/resources/qml/ImageButton.qml index dd67d597..0a33d376 100644 --- a/resources/qml/ImageButton.qml +++ b/resources/qml/ImageButton.qml @@ -2,7 +2,8 @@ import QtQuick 2.3 import QtQuick.Controls 2.3 AbstractButton { - property string image: undefined + property string image + property string src width: 16 height: 16 id: button @@ -11,7 +12,7 @@ AbstractButton { id: buttonImg // Workaround, can't get icon.source working for now... anchors.fill: parent - source: "image://colorimage/" + image + "?" + (button.hovered ? colors.highlight : colors.buttonText) + source: src ? src : ("image://colorimage/" + image + "?" + (button.hovered ? colors.highlight : colors.buttonText)) } MouseArea diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index de818cd9..4ea15f7b 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -497,6 +497,146 @@ Page { } } } + + Rectangle { + id: activeCallBar + visible: timelineManager.callState != WebRTCState.DISCONNECTED + + Layout.fillWidth: true + implicitHeight: topLayout.height + 16 + color: "#2ECC71" + z: 3 + + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 8 + anchors.verticalCenter: parent.verticalCenter + + Avatar { + Layout.column: 1 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + + width: avatarSize + height: avatarSize + + url: chat.model ? chat.model.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : "" + displayName: chat.model ? chat.model.roomName : qsTr("No room selected") + } + + Label { + Layout.column: 2 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + + font.pointSize: fontMetrics.font.pointSize * 1.1 + text: chat.model ? " " + chat.model.roomName + " " : "" + } + + Image { + Layout.column: 3 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: 23 + Layout.preferredHeight: 23 + source: "qrc:/icons/icons/ui/place-call.png" + } + + Connections { + target: timelineManager + function onCallStateChanged(state) { + switch (state) { + case WebRTCState.INITIATING: + callStateLabel.text = "Initiating call..." + break; + case WebRTCState.INITIATED: + callStateLabel.text = "Call initiated..." + break; + case WebRTCState.OFFERSENT: + callStateLabel.text = "Calling..." + break; + case WebRTCState.CONNECTING: + callStateLabel.text = "Connecting..." + break; + case WebRTCState.CONNECTED: + callStateLabel.text = "00:00" + var d = new Date() + callTimer.startTime = Math.floor(d.getTime() / 1000) + break; + } + } + } + + Label { + id: callStateLabel + Layout.column: 4 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + font.pointSize: fontMetrics.font.pointSize * 1.1 + } + + Timer { + id: callTimer + property int startTime + interval: 1000 + running: timelineManager.callState == WebRTCState.CONNECTED + repeat: true + onTriggered: { + var d = new Date() + let seconds = Math.floor(d.getTime() / 1000 - startTime) + let s = Math.floor(seconds % 60) + let m = Math.floor(seconds / 60) % 60 + let h = Math.floor(seconds / 3600) + callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s) + } + + function pad(n) { + return (n < 10) ? ("0" + n) : n + } + } + + Item { + Layout.column: 5 + Layout.fillWidth: true + } + + ImageButton { + Layout.column: 6 + Layout.row: 0 + Layout.rowSpan: 2 + Layout.alignment: Qt.AlignVCenter + + width: 22 + height: 22 + src: "qrc:/icons/icons/ui/microphone-mute.png" + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Mute Mic") + + onClicked: { + if (timelineManager.toggleMuteAudioSource()) { + src = "qrc:/icons/icons/ui/microphone-unmute.png" + ToolTip.text = qsTr("Unmute Mic") + } + else { + src = "qrc:/icons/icons/ui/microphone-mute.png" + ToolTip.text = qsTr("Mute Mic") + } + } + } + + Item { + Layout.column: 7 + implicitWidth: 16 + } + } + } } } } diff --git a/src/ActiveCallBar.cpp b/src/ActiveCallBar.cpp deleted file mode 100644 index c0d2c13a..00000000 --- a/src/ActiveCallBar.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include "ActiveCallBar.h" -#include "ChatPage.h" -#include "Utils.h" -#include "WebRTCSession.h" -#include "ui/Avatar.h" -#include "ui/FlatButton.h" - -ActiveCallBar::ActiveCallBar(QWidget *parent) - : QWidget(parent) -{ - setAutoFillBackground(true); - auto p = palette(); - p.setColor(backgroundRole(), QColor(46, 204, 113)); - setPalette(p); - - QFont f; - f.setPointSizeF(f.pointSizeF()); - - const int fontHeight = QFontMetrics(f).height(); - const int widgetMargin = fontHeight / 3; - const int contentHeight = fontHeight * 3; - - setFixedHeight(contentHeight + widgetMargin); - - layout_ = new QHBoxLayout(this); - layout_->setSpacing(widgetMargin); - layout_->setContentsMargins(2 * widgetMargin, widgetMargin, 2 * widgetMargin, widgetMargin); - - QFont labelFont; - labelFont.setPointSizeF(labelFont.pointSizeF() * 1.1); - labelFont.setWeight(QFont::Medium); - - avatar_ = new Avatar(this, QFontMetrics(f).height() * 2.5); - - callPartyLabel_ = new QLabel(this); - callPartyLabel_->setFont(labelFont); - - stateLabel_ = new QLabel(this); - stateLabel_->setFont(labelFont); - - durationLabel_ = new QLabel(this); - durationLabel_->setFont(labelFont); - durationLabel_->hide(); - - muteBtn_ = new FlatButton(this); - setMuteIcon(false); - muteBtn_->setFixedSize(buttonSize_, buttonSize_); - muteBtn_->setCornerRadius(buttonSize_ / 2); - connect(muteBtn_, &FlatButton::clicked, this, [this]() { - if (WebRTCSession::instance().toggleMuteAudioSrc(muted_)) - setMuteIcon(muted_); - }); - - layout_->addWidget(avatar_, 0, Qt::AlignLeft); - layout_->addWidget(callPartyLabel_, 0, Qt::AlignLeft); - layout_->addWidget(stateLabel_, 0, Qt::AlignLeft); - layout_->addWidget(durationLabel_, 0, Qt::AlignLeft); - layout_->addStretch(); - layout_->addWidget(muteBtn_, 0, Qt::AlignCenter); - layout_->addSpacing(18); - - timer_ = new QTimer(this); - connect(timer_, &QTimer::timeout, this, [this]() { - auto seconds = QDateTime::currentSecsSinceEpoch() - callStartTime_; - int s = seconds % 60; - int m = (seconds / 60) % 60; - int h = seconds / 3600; - char buf[12]; - if (h) - snprintf(buf, sizeof(buf), "%.2d:%.2d:%.2d", h, m, s); - else - snprintf(buf, sizeof(buf), "%.2d:%.2d", m, s); - durationLabel_->setText(buf); - }); - - connect( - &WebRTCSession::instance(), &WebRTCSession::stateChanged, this, &ActiveCallBar::update); -} - -void -ActiveCallBar::setMuteIcon(bool muted) -{ - QIcon icon; - if (muted) { - muteBtn_->setToolTip("Unmute Mic"); - icon.addFile(":/icons/icons/ui/microphone-unmute.png"); - } else { - muteBtn_->setToolTip("Mute Mic"); - icon.addFile(":/icons/icons/ui/microphone-mute.png"); - } - muteBtn_->setIcon(icon); - muteBtn_->setIconSize(QSize(buttonSize_, buttonSize_)); -} - -void -ActiveCallBar::setCallParty(const QString &userid, - const QString &displayName, - const QString &roomName, - const QString &avatarUrl) -{ - callPartyLabel_->setText(" " + (displayName.isEmpty() ? userid : displayName) + " "); - - if (!avatarUrl.isEmpty()) - avatar_->setImage(avatarUrl); - else - avatar_->setLetter(utils::firstChar(roomName)); -} - -void -ActiveCallBar::update(WebRTCSession::State state) -{ - switch (state) { - case WebRTCSession::State::INITIATING: - show(); - stateLabel_->setText("Initiating call..."); - break; - case WebRTCSession::State::INITIATED: - show(); - stateLabel_->setText("Call initiated..."); - break; - case WebRTCSession::State::OFFERSENT: - show(); - stateLabel_->setText("Calling..."); - break; - case WebRTCSession::State::CONNECTING: - show(); - stateLabel_->setText("Connecting..."); - break; - case WebRTCSession::State::CONNECTED: - show(); - callStartTime_ = QDateTime::currentSecsSinceEpoch(); - timer_->start(1000); - stateLabel_->setPixmap( - QIcon(":/icons/icons/ui/place-call.png").pixmap(QSize(buttonSize_, buttonSize_))); - durationLabel_->setText("00:00"); - durationLabel_->show(); - break; - case WebRTCSession::State::ICEFAILED: - case WebRTCSession::State::DISCONNECTED: - hide(); - timer_->stop(); - callPartyLabel_->setText(QString()); - stateLabel_->setText(QString()); - durationLabel_->setText(QString()); - durationLabel_->hide(); - setMuteIcon(false); - break; - default: - break; - } -} diff --git a/src/ActiveCallBar.h b/src/ActiveCallBar.h deleted file mode 100644 index 1e940227..00000000 --- a/src/ActiveCallBar.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include - -#include "WebRTCSession.h" - -class QHBoxLayout; -class QLabel; -class QTimer; -class Avatar; -class FlatButton; - -class ActiveCallBar : public QWidget -{ - Q_OBJECT - -public: - ActiveCallBar(QWidget *parent = nullptr); - -public slots: - void update(WebRTCSession::State); - void setCallParty(const QString &userid, - const QString &displayName, - const QString &roomName, - const QString &avatarUrl); - -private: - QHBoxLayout *layout_ = nullptr; - Avatar *avatar_ = nullptr; - QLabel *callPartyLabel_ = nullptr; - QLabel *stateLabel_ = nullptr; - QLabel *durationLabel_ = nullptr; - FlatButton *muteBtn_ = nullptr; - int buttonSize_ = 22; - bool muted_ = false; - qint64 callStartTime_ = 0; - QTimer *timer_ = nullptr; - - void setMuteIcon(bool muted); -}; diff --git a/src/CallManager.cpp b/src/CallManager.cpp index 7a8d2ca7..2c0f9d5a 100644 --- a/src/CallManager.cpp +++ b/src/CallManager.cpp @@ -52,7 +52,7 @@ CallManager::CallManager(QSharedPointer userSettings) emit newMessage(roomid_, CallInvite{callid_, sdp, 0, timeoutms_}); emit newMessage(roomid_, CallCandidates{callid_, candidates, 0}); QTimer::singleShot(timeoutms_, this, [this]() { - if (session_.state() == WebRTCSession::State::OFFERSENT) { + if (session_.state() == webrtc::State::OFFERSENT) { hangUp(CallHangUp::Reason::InviteTimeOut); emit ChatPage::instance()->showNotification( "The remote side failed to pick up."); @@ -99,13 +99,13 @@ CallManager::CallManager(QSharedPointer userSettings) turnServerTimer_.setInterval(ttl * 1000 * 0.9); }); - connect(&session_, &WebRTCSession::stateChanged, this, [this](WebRTCSession::State state) { + connect(&session_, &WebRTCSession::stateChanged, this, [this](webrtc::State state) { switch (state) { - case WebRTCSession::State::DISCONNECTED: + case webrtc::State::DISCONNECTED: playRingtone("qrc:/media/media/callend.ogg", false); clear(); break; - case WebRTCSession::State::ICEFAILED: { + case webrtc::State::ICEFAILED: { QString error("Call connection failed."); if (turnURIs_.empty()) error += " Your homeserver has no configured TURN server."; @@ -152,13 +152,6 @@ CallManager::sendInvite(const QString &roomid) generateCallID(); nhlog::ui()->debug("WebRTC: call id: {} - creating invite", callid_); - std::vector members(cache::getMembers(roomid.toStdString())); - const RoomMember &callee = - members.front().user_id == utils::localUser() ? members.back() : members.front(); - emit newCallParty(callee.user_id, - callee.display_name, - QString::fromStdString(roomInfo.name), - QString::fromStdString(roomInfo.avatar_url)); playRingtone("qrc:/media/media/ringback.ogg", true); if (!session_.createOffer()) { emit ChatPage::instance()->showNotification("Problem setting up call."); @@ -195,7 +188,7 @@ CallManager::hangUp(CallHangUp::Reason reason) bool CallManager::onActiveCall() { - return session_.state() != WebRTCSession::State::DISCONNECTED; + return session_.state() != webrtc::State::DISCONNECTED; } void @@ -259,11 +252,6 @@ CallManager::handleEvent(const RoomEvent &callInviteEvent) std::vector members(cache::getMembers(callInviteEvent.room_id)); const RoomMember &caller = members.front().user_id == utils::localUser() ? members.back() : members.front(); - emit newCallParty(caller.user_id, - caller.display_name, - QString::fromStdString(roomInfo.name), - QString::fromStdString(roomInfo.avatar_url)); - auto dialog = new dialogs::AcceptCall(caller.user_id, caller.display_name, QString::fromStdString(roomInfo.name), diff --git a/src/CallManager.h b/src/CallManager.h index 3a406438..1de8d2ae 100644 --- a/src/CallManager.h +++ b/src/CallManager.h @@ -41,10 +41,6 @@ signals: void newMessage(const QString &roomid, const mtx::events::msg::CallAnswer &); void newMessage(const QString &roomid, const mtx::events::msg::CallHangUp &); void turnServerRetrieved(const mtx::responses::TurnServer &); - void newCallParty(const QString &userid, - const QString &displayName, - const QString &roomName, - const QString &avatarUrl); private slots: void retrieveTurnServer(); diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 6008846a..59ae7ef2 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -22,7 +22,6 @@ #include #include -#include "ActiveCallBar.h" #include "AvatarProvider.h" #include "Cache.h" #include "Cache_p.h" @@ -40,7 +39,6 @@ #include "UserInfoWidget.h" #include "UserSettingsPage.h" #include "Utils.h" -#include "WebRTCSession.h" #include "ui/OverlayModal.h" #include "ui/Theme.h" @@ -129,12 +127,6 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) contentLayout_->addWidget(view_manager_->getWidget()); - activeCallBar_ = new ActiveCallBar(this); - contentLayout_->addWidget(activeCallBar_); - activeCallBar_->hide(); - connect( - &callManager_, &CallManager::newCallParty, activeCallBar_, &ActiveCallBar::setCallParty); - // Splitter splitter->addWidget(sideBar_); splitter->addWidget(content_); diff --git a/src/ChatPage.h b/src/ChatPage.h index a139b5fd..6b3916a2 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -41,7 +41,6 @@ #include "notifications/Manager.h" #include "popups/UserMentions.h" -class ActiveCallBar; class OverlayModal; class QuickSwitcher; class RoomList; @@ -235,7 +234,6 @@ private: SideBarActions *sidebarActions_; TextInputWidget *text_input_; - ActiveCallBar *activeCallBar_; QTimer connectivityTimer_; std::atomic_bool isConnected_; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 29abed86..330ba2a3 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -288,7 +288,7 @@ MainWindow::showChatPage() void MainWindow::closeEvent(QCloseEvent *event) { - if (WebRTCSession::instance().state() != WebRTCSession::State::DISCONNECTED) { + if (WebRTCSession::instance().state() != webrtc::State::DISCONNECTED) { if (QMessageBox::question(this, "nheko", "A call is in progress. Quit?") != QMessageBox::Yes) { event->ignore(); @@ -440,7 +440,7 @@ MainWindow::openLogoutDialog() { auto dialog = new dialogs::Logout(this); connect(dialog, &dialogs::Logout::loggingOut, this, [this]() { - if (WebRTCSession::instance().state() != WebRTCSession::State::DISCONNECTED) { + if (WebRTCSession::instance().state() != webrtc::State::DISCONNECTED) { if (QMessageBox::question( this, "nheko", "A call is in progress. Log out?") != QMessageBox::Yes) { diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index 4a25c4cf..a7d9797e 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -560,7 +560,7 @@ TextInputWidget::TextInputWidget(QWidget *parent) #ifdef GSTREAMER_AVAILABLE callBtn_ = new FlatButton(this); - changeCallButtonState(WebRTCSession::State::DISCONNECTED); + changeCallButtonState(webrtc::State::DISCONNECTED); connect(&WebRTCSession::instance(), &WebRTCSession::stateChanged, this, @@ -776,11 +776,11 @@ TextInputWidget::paintEvent(QPaintEvent *) } void -TextInputWidget::changeCallButtonState(WebRTCSession::State state) +TextInputWidget::changeCallButtonState(webrtc::State state) { QIcon icon; - if (state == WebRTCSession::State::ICEFAILED || - state == WebRTCSession::State::DISCONNECTED) { + if (state == webrtc::State::ICEFAILED || + state == webrtc::State::DISCONNECTED) { callBtn_->setToolTip(tr("Place a call")); icon.addFile(":/icons/icons/ui/place-call.png"); } else { diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 3aa05c39..f4ca980e 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -164,7 +164,7 @@ public slots: void openFileSelection(); void hideUploadSpinner(); void focusLineEdit() { input_->setFocus(); } - void changeCallButtonState(WebRTCSession::State); + void changeCallButtonState(webrtc::State); private slots: void addSelectedEmoji(const QString &emoji); diff --git a/src/WebRTCSession.cpp b/src/WebRTCSession.cpp index d8497833..8f877c41 100644 --- a/src/WebRTCSession.cpp +++ b/src/WebRTCSession.cpp @@ -1,4 +1,5 @@ #include +#include #include "Logging.h" #include "WebRTCSession.h" @@ -14,12 +15,22 @@ extern "C" } #endif -Q_DECLARE_METATYPE(WebRTCSession::State) +Q_DECLARE_METATYPE(webrtc::State) + +using webrtc::State; WebRTCSession::WebRTCSession() : QObject() -{ - qRegisterMetaType(); +{ + qRegisterMetaType(); + qmlRegisterUncreatableMetaObject( + webrtc::staticMetaObject, + "im.nheko", + 1, + 0, + "WebRTCState", + "Can't instantiate enum"); + connect(this, &WebRTCSession::stateChanged, this, &WebRTCSession::setState); init(); } @@ -247,11 +258,11 @@ iceGatheringStateChanged(GstElement *webrtc, if (isoffering_) { emit WebRTCSession::instance().offerCreated(localsdp_, localcandidates_); emit WebRTCSession::instance().stateChanged( - WebRTCSession::State::OFFERSENT); + State::OFFERSENT); } else { emit WebRTCSession::instance().answerCreated(localsdp_, localcandidates_); emit WebRTCSession::instance().stateChanged( - WebRTCSession::State::ANSWERSENT); + State::ANSWERSENT); } } } @@ -264,10 +275,10 @@ onICEGatheringCompletion(gpointer timerid) *(guint *)(timerid) = 0; if (isoffering_) { emit WebRTCSession::instance().offerCreated(localsdp_, localcandidates_); - emit WebRTCSession::instance().stateChanged(WebRTCSession::State::OFFERSENT); + emit WebRTCSession::instance().stateChanged(State::OFFERSENT); } else { emit WebRTCSession::instance().answerCreated(localsdp_, localcandidates_); - emit WebRTCSession::instance().stateChanged(WebRTCSession::State::ANSWERSENT); + emit WebRTCSession::instance().stateChanged(State::ANSWERSENT); } return FALSE; } @@ -285,7 +296,7 @@ addLocalICECandidate(GstElement *webrtc G_GNUC_UNUSED, localcandidates_.push_back({"audio", (uint16_t)mlineIndex, candidate}); return; #else - if (WebRTCSession::instance().state() >= WebRTCSession::State::OFFERSENT) { + if (WebRTCSession::instance().state() >= State::OFFERSENT) { emit WebRTCSession::instance().newICECandidate( {"audio", (uint16_t)mlineIndex, candidate}); return; @@ -314,11 +325,11 @@ iceConnectionStateChanged(GstElement *webrtc, switch (newState) { case GST_WEBRTC_ICE_CONNECTION_STATE_CHECKING: nhlog::ui()->debug("WebRTC: GstWebRTCICEConnectionState -> Checking"); - emit WebRTCSession::instance().stateChanged(WebRTCSession::State::CONNECTING); + emit WebRTCSession::instance().stateChanged(State::CONNECTING); break; case GST_WEBRTC_ICE_CONNECTION_STATE_FAILED: nhlog::ui()->error("WebRTC: GstWebRTCICEConnectionState -> Failed"); - emit WebRTCSession::instance().stateChanged(WebRTCSession::State::ICEFAILED); + emit WebRTCSession::instance().stateChanged(State::ICEFAILED); break; default: break; @@ -356,7 +367,7 @@ linkNewPad(GstElement *decodebin G_GNUC_UNUSED, GstPad *newpad, GstElement *pipe nhlog::ui()->error("WebRTC: unable to link new pad"); else { emit WebRTCSession::instance().stateChanged( - WebRTCSession::State::CONNECTED); + State::CONNECTED); } gst_object_unref(queuepad); } @@ -633,21 +644,17 @@ WebRTCSession::createPipeline(int opusPayloadType) } bool -WebRTCSession::toggleMuteAudioSrc(bool &isMuted) +WebRTCSession::toggleMuteAudioSource() { if (state_ < State::INITIATED) return false; GstElement *srclevel = gst_bin_get_by_name(GST_BIN(pipe_), "srclevel"); - if (!srclevel) - return false; - gboolean muted; g_object_get(srclevel, "mute", &muted, nullptr); g_object_set(srclevel, "mute", !muted, nullptr); gst_object_unref(srclevel); - isMuted = !muted; - return true; + return !muted; } void @@ -778,7 +785,7 @@ WebRTCSession::createPipeline(int) } bool -WebRTCSession::toggleMuteAudioSrc(bool &) +WebRTCSession::toggleMuteAudioSource() { return false; } diff --git a/src/WebRTCSession.h b/src/WebRTCSession.h index 653ec2cf..9593def9 100644 --- a/src/WebRTCSession.h +++ b/src/WebRTCSession.h @@ -9,23 +9,30 @@ typedef struct _GstElement GstElement; +namespace webrtc { +Q_NAMESPACE + +enum class State +{ + DISCONNECTED, + ICEFAILED, + INITIATING, + INITIATED, + OFFERSENT, + ANSWERSENT, + CONNECTING, + CONNECTED + +}; +Q_ENUM_NS(State) + +} + class WebRTCSession : public QObject { Q_OBJECT public: - enum class State - { - DISCONNECTED, - ICEFAILED, - INITIATING, - INITIATED, - OFFERSENT, - ANSWERSENT, - CONNECTING, - CONNECTED - }; - static WebRTCSession &instance() { static WebRTCSession instance; @@ -33,14 +40,14 @@ public: } bool init(std::string *errorMessage = nullptr); - State state() const { return state_; } + webrtc::State state() const { return state_; } bool createOffer(); bool acceptOffer(const std::string &sdp); bool acceptAnswer(const std::string &sdp); void acceptICECandidates(const std::vector &); - bool toggleMuteAudioSrc(bool &isMuted); + bool toggleMuteAudioSource(); void end(); void setStunServer(const std::string &stunServer) { stunServer_ = stunServer; } @@ -55,16 +62,16 @@ signals: void answerCreated(const std::string &sdp, const std::vector &); void newICECandidate(const mtx::events::msg::CallCandidates::Candidate &); - void stateChanged(WebRTCSession::State); // explicit qualifier necessary for Qt + void stateChanged(webrtc::State); private slots: - void setState(State state) { state_ = state; } + void setState(webrtc::State state) { state_ = state; } private: WebRTCSession(); bool initialised_ = false; - State state_ = State::DISCONNECTED; + webrtc::State state_ = webrtc::State::DISCONNECTED; GstElement *pipe_ = nullptr; GstElement *webrtc_ = nullptr; unsigned int busWatchId_ = 0; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index abb807b3..3a308be8 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -141,6 +141,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin isInitialSync_ = true; emit initialSyncChanged(true); }); + connect( + &WebRTCSession::instance(), &WebRTCSession::stateChanged, this, &TimelineViewManager::callStateChanged); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 1a98f64d..fdf1603d 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -13,6 +13,7 @@ #include "Logging.h" #include "TimelineModel.h" #include "Utils.h" +#include "WebRTCSession.h" #include "emoji/EmojiModel.h" #include "emoji/Provider.h" @@ -33,6 +34,8 @@ class TimelineViewManager : public QObject bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged) Q_PROPERTY( bool isNarrowView MEMBER isNarrowView_ READ isNarrowView NOTIFY narrowViewChanged) + Q_PROPERTY( + webrtc::State callState READ callState NOTIFY callStateChanged) public: TimelineViewManager(QSharedPointer userSettings, @@ -48,6 +51,8 @@ public: Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } bool isNarrowView() const { return isNarrowView_; } + webrtc::State callState() const { return WebRTCSession::instance().state(); } + Q_INVOKABLE bool toggleMuteAudioSource() { return WebRTCSession::instance().toggleMuteAudioSource(); } Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); Q_INVOKABLE QString escapeEmoji(QString str) const; @@ -72,6 +77,7 @@ signals: void inviteUsers(QStringList users); void showRoomList(); void narrowViewChanged(); + void callStateChanged(webrtc::State); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); From 44cfc8d22afd5b7522a13f52af67839de6a37dcc Mon Sep 17 00:00:00 2001 From: trilene Date: Tue, 22 Sep 2020 12:14:15 -0400 Subject: [PATCH 2/5] clang-format --- src/TextInputWidget.cpp | 3 +-- src/WebRTCSession.cpp | 20 ++++++-------------- src/timeline/TimelineViewManager.cpp | 6 ++++-- src/timeline/TimelineViewManager.h | 8 +++++--- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index a7d9797e..f0d23e21 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -779,8 +779,7 @@ void TextInputWidget::changeCallButtonState(webrtc::State state) { QIcon icon; - if (state == webrtc::State::ICEFAILED || - state == webrtc::State::DISCONNECTED) { + if (state == webrtc::State::ICEFAILED || state == webrtc::State::DISCONNECTED) { callBtn_->setToolTip(tr("Place a call")); icon.addFile(":/icons/icons/ui/place-call.png"); } else { diff --git a/src/WebRTCSession.cpp b/src/WebRTCSession.cpp index 8f877c41..b4eaadab 100644 --- a/src/WebRTCSession.cpp +++ b/src/WebRTCSession.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include "Logging.h" #include "WebRTCSession.h" @@ -21,15 +21,10 @@ using webrtc::State; WebRTCSession::WebRTCSession() : QObject() -{ +{ qRegisterMetaType(); qmlRegisterUncreatableMetaObject( - webrtc::staticMetaObject, - "im.nheko", - 1, - 0, - "WebRTCState", - "Can't instantiate enum"); + webrtc::staticMetaObject, "im.nheko", 1, 0, "WebRTCState", "Can't instantiate enum"); connect(this, &WebRTCSession::stateChanged, this, &WebRTCSession::setState); init(); @@ -257,12 +252,10 @@ iceGatheringStateChanged(GstElement *webrtc, nhlog::ui()->debug("WebRTC: GstWebRTCICEGatheringState -> Complete"); if (isoffering_) { emit WebRTCSession::instance().offerCreated(localsdp_, localcandidates_); - emit WebRTCSession::instance().stateChanged( - State::OFFERSENT); + emit WebRTCSession::instance().stateChanged(State::OFFERSENT); } else { emit WebRTCSession::instance().answerCreated(localsdp_, localcandidates_); - emit WebRTCSession::instance().stateChanged( - State::ANSWERSENT); + emit WebRTCSession::instance().stateChanged(State::ANSWERSENT); } } } @@ -366,8 +359,7 @@ linkNewPad(GstElement *decodebin G_GNUC_UNUSED, GstPad *newpad, GstElement *pipe if (GST_PAD_LINK_FAILED(gst_pad_link(newpad, queuepad))) nhlog::ui()->error("WebRTC: unable to link new pad"); else { - emit WebRTCSession::instance().stateChanged( - State::CONNECTED); + emit WebRTCSession::instance().stateChanged(State::CONNECTED); } gst_object_unref(queuepad); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 3a308be8..09ab8cf8 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -141,8 +141,10 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin isInitialSync_ = true; emit initialSyncChanged(true); }); - connect( - &WebRTCSession::instance(), &WebRTCSession::stateChanged, this, &TimelineViewManager::callStateChanged); + connect(&WebRTCSession::instance(), + &WebRTCSession::stateChanged, + this, + &TimelineViewManager::callStateChanged); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index fdf1603d..243927ef 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -34,8 +34,7 @@ class TimelineViewManager : public QObject bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged) Q_PROPERTY( bool isNarrowView MEMBER isNarrowView_ READ isNarrowView NOTIFY narrowViewChanged) - Q_PROPERTY( - webrtc::State callState READ callState NOTIFY callStateChanged) + Q_PROPERTY(webrtc::State callState READ callState NOTIFY callStateChanged) public: TimelineViewManager(QSharedPointer userSettings, @@ -52,7 +51,10 @@ public: Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } bool isNarrowView() const { return isNarrowView_; } webrtc::State callState() const { return WebRTCSession::instance().state(); } - Q_INVOKABLE bool toggleMuteAudioSource() { return WebRTCSession::instance().toggleMuteAudioSource(); } + Q_INVOKABLE bool toggleMuteAudioSource() + { + return WebRTCSession::instance().toggleMuteAudioSource(); + } Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); Q_INVOKABLE QString escapeEmoji(QString str) const; From 3f73853e4b71b67ab6c8ced2dd4eb1e7f473025e Mon Sep 17 00:00:00 2001 From: trilene Date: Fri, 25 Sep 2020 10:26:36 -0400 Subject: [PATCH 3/5] Move ActiveCallBar Qml to separate file --- resources/qml/ActiveCallBar.qml | 110 +++++++++++++++++++++ resources/qml/Avatar.qml | 2 +- resources/qml/TimelineView.qml | 137 +-------------------------- resources/res.qrc | 1 + src/CallManager.cpp | 13 ++- src/CallManager.h | 7 +- src/WebRTCSession.cpp | 15 ++- src/WebRTCSession.h | 3 +- src/timeline/TimelineViewManager.cpp | 10 +- src/timeline/TimelineViewManager.h | 15 ++- 10 files changed, 166 insertions(+), 147 deletions(-) create mode 100644 resources/qml/ActiveCallBar.qml diff --git a/resources/qml/ActiveCallBar.qml b/resources/qml/ActiveCallBar.qml new file mode 100644 index 00000000..8a63725e --- /dev/null +++ b/resources/qml/ActiveCallBar.qml @@ -0,0 +1,110 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.2 + +import im.nheko 1.0 + +Rectangle { + id: activeCallBar + visible: timelineManager.callState != WebRTCState.DISCONNECTED + color: "#2ECC71" + implicitHeight: rowLayout.height + 8 + + RowLayout { + id: rowLayout + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 8 + + Avatar { + width: avatarSize + height: avatarSize + + url: timelineManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") + displayName: timelineManager.callPartyName + } + + Label { + font.pointSize: fontMetrics.font.pointSize * 1.1 + text: " " + timelineManager.callPartyName + " " + } + + Image { + Layout.preferredWidth: 24 + Layout.preferredHeight: 24 + source: "qrc:/icons/icons/ui/place-call.png" + } + + Label { + id: callStateLabel + font.pointSize: fontMetrics.font.pointSize * 1.1 + } + + Connections { + target: timelineManager + function onCallStateChanged(state) { + switch (state) { + case WebRTCState.INITIATING: + callStateLabel.text = qsTr("Initiating...") + break; + case WebRTCState.OFFERSENT: + callStateLabel.text = qsTr("Calling...") + break; + case WebRTCState.CONNECTING: + callStateLabel.text = qsTr("Connecting...") + break; + case WebRTCState.CONNECTED: + callStateLabel.text = "00:00" + var d = new Date() + callTimer.startTime = Math.floor(d.getTime() / 1000) + break; + case WebRTCState.DISCONNECTED: + callStateLabel.text = "" + } + } + } + + Timer { + id: callTimer + property int startTime + interval: 1000 + running: timelineManager.callState == WebRTCState.CONNECTED + repeat: true + onTriggered: { + var d = new Date() + let seconds = Math.floor(d.getTime() / 1000 - startTime) + let s = Math.floor(seconds % 60) + let m = Math.floor(seconds / 60) % 60 + let h = Math.floor(seconds / 3600) + callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s) + } + + function pad(n) { + return (n < 10) ? ("0" + n) : n + } + } + + Item { + Layout.fillWidth: true + } + + ImageButton { + width: 24 + height: 24 + src: timelineManager.isMicMuted ? + "qrc:/icons/icons/ui/microphone-unmute.png" : + "qrc:/icons/icons/ui/microphone-mute.png" + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: timelineManager.isMicMuted ? qsTr("Unmute Mic") : qsTr("Mute Mic") + + onClicked: timelineManager.toggleMicMute() + } + + Item { + implicitWidth: 16 + } + } +} diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index a3943806..c030c843 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -14,7 +14,7 @@ Rectangle { Label { anchors.fill: parent - text: timelineManager.escapeEmoji(String.fromCodePoint(displayName.codePointAt(0))) + text: timelineManager.escapeEmoji(displayName ? String.fromCodePoint(displayName.codePointAt(0)) : "") textFormat: Text.RichText font.pixelSize: avatar.height/2 verticalAlignment: Text.AlignVCenter diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 4ea15f7b..07c5e1a4 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -498,144 +498,9 @@ Page { } } - Rectangle { - id: activeCallBar - visible: timelineManager.callState != WebRTCState.DISCONNECTED - + ActiveCallBar { Layout.fillWidth: true - implicitHeight: topLayout.height + 16 - color: "#2ECC71" z: 3 - - GridLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: 8 - anchors.verticalCenter: parent.verticalCenter - - Avatar { - Layout.column: 1 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - - width: avatarSize - height: avatarSize - - url: chat.model ? chat.model.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : "" - displayName: chat.model ? chat.model.roomName : qsTr("No room selected") - } - - Label { - Layout.column: 2 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - - font.pointSize: fontMetrics.font.pointSize * 1.1 - text: chat.model ? " " + chat.model.roomName + " " : "" - } - - Image { - Layout.column: 3 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: 23 - Layout.preferredHeight: 23 - source: "qrc:/icons/icons/ui/place-call.png" - } - - Connections { - target: timelineManager - function onCallStateChanged(state) { - switch (state) { - case WebRTCState.INITIATING: - callStateLabel.text = "Initiating call..." - break; - case WebRTCState.INITIATED: - callStateLabel.text = "Call initiated..." - break; - case WebRTCState.OFFERSENT: - callStateLabel.text = "Calling..." - break; - case WebRTCState.CONNECTING: - callStateLabel.text = "Connecting..." - break; - case WebRTCState.CONNECTED: - callStateLabel.text = "00:00" - var d = new Date() - callTimer.startTime = Math.floor(d.getTime() / 1000) - break; - } - } - } - - Label { - id: callStateLabel - Layout.column: 4 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - font.pointSize: fontMetrics.font.pointSize * 1.1 - } - - Timer { - id: callTimer - property int startTime - interval: 1000 - running: timelineManager.callState == WebRTCState.CONNECTED - repeat: true - onTriggered: { - var d = new Date() - let seconds = Math.floor(d.getTime() / 1000 - startTime) - let s = Math.floor(seconds % 60) - let m = Math.floor(seconds / 60) % 60 - let h = Math.floor(seconds / 3600) - callStateLabel.text = (h ? (pad(h) + ":") : "") + pad(m) + ":" + pad(s) - } - - function pad(n) { - return (n < 10) ? ("0" + n) : n - } - } - - Item { - Layout.column: 5 - Layout.fillWidth: true - } - - ImageButton { - Layout.column: 6 - Layout.row: 0 - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignVCenter - - width: 22 - height: 22 - src: "qrc:/icons/icons/ui/microphone-mute.png" - - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Mute Mic") - - onClicked: { - if (timelineManager.toggleMuteAudioSource()) { - src = "qrc:/icons/icons/ui/microphone-unmute.png" - ToolTip.text = qsTr("Unmute Mic") - } - else { - src = "qrc:/icons/icons/ui/microphone-mute.png" - ToolTip.text = qsTr("Mute Mic") - } - } - } - - Item { - Layout.column: 7 - implicitWidth: 16 - } - } } } } diff --git a/resources/res.qrc b/resources/res.qrc index b245f48f..63a40431 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -121,6 +121,7 @@ qtquickcontrols2.conf qml/TimelineView.qml + qml/ActiveCallBar.qml qml/Avatar.qml qml/ImageButton.qml qml/MatrixText.qml diff --git a/src/CallManager.cpp b/src/CallManager.cpp index 2c0f9d5a..b1d1a75a 100644 --- a/src/CallManager.cpp +++ b/src/CallManager.cpp @@ -152,6 +152,12 @@ CallManager::sendInvite(const QString &roomid) generateCallID(); nhlog::ui()->debug("WebRTC: call id: {} - creating invite", callid_); + std::vector members(cache::getMembers(roomid.toStdString())); + const RoomMember &callee = + members.front().user_id == utils::localUser() ? members.back() : members.front(); + callPartyName_ = callee.display_name.isEmpty() ? callee.user_id : callee.display_name; + callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); + emit newCallParty(); playRingtone("qrc:/media/media/ringback.ogg", true); if (!session_.createOffer()) { emit ChatPage::instance()->showNotification("Problem setting up call."); @@ -186,7 +192,7 @@ CallManager::hangUp(CallHangUp::Reason reason) } bool -CallManager::onActiveCall() +CallManager::onActiveCall() const { return session_.state() != webrtc::State::DISCONNECTED; } @@ -252,6 +258,9 @@ CallManager::handleEvent(const RoomEvent &callInviteEvent) std::vector members(cache::getMembers(callInviteEvent.room_id)); const RoomMember &caller = members.front().user_id == utils::localUser() ? members.back() : members.front(); + callPartyName_ = caller.display_name.isEmpty() ? caller.user_id : caller.display_name; + callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); + emit newCallParty(); auto dialog = new dialogs::AcceptCall(caller.user_id, caller.display_name, QString::fromStdString(roomInfo.name), @@ -364,6 +373,8 @@ void CallManager::clear() { roomid_.clear(); + callPartyName_.clear(); + callPartyAvatarUrl_.clear(); callid_.clear(); remoteICECandidates_.clear(); } diff --git a/src/CallManager.h b/src/CallManager.h index 1de8d2ae..640230a4 100644 --- a/src/CallManager.h +++ b/src/CallManager.h @@ -29,7 +29,9 @@ public: void sendInvite(const QString &roomid); void hangUp( mtx::events::msg::CallHangUp::Reason = mtx::events::msg::CallHangUp::Reason::User); - bool onActiveCall(); + bool onActiveCall() const; + QString callPartyName() const { return callPartyName_; } + QString callPartyAvatarUrl() const { return callPartyAvatarUrl_; } void refreshTurnServer(); public slots: @@ -40,6 +42,7 @@ signals: void newMessage(const QString &roomid, const mtx::events::msg::CallCandidates &); void newMessage(const QString &roomid, const mtx::events::msg::CallAnswer &); void newMessage(const QString &roomid, const mtx::events::msg::CallHangUp &); + void newCallParty(); void turnServerRetrieved(const mtx::responses::TurnServer &); private slots: @@ -48,6 +51,8 @@ private slots: private: WebRTCSession &session_; QString roomid_; + QString callPartyName_; + QString callPartyAvatarUrl_; std::string callid_; const uint32_t timeoutms_ = 120000; std::vector remoteICECandidates_; diff --git a/src/WebRTCSession.cpp b/src/WebRTCSession.cpp index b4eaadab..14f17030 100644 --- a/src/WebRTCSession.cpp +++ b/src/WebRTCSession.cpp @@ -636,7 +636,20 @@ WebRTCSession::createPipeline(int opusPayloadType) } bool -WebRTCSession::toggleMuteAudioSource() +WebRTCSession::isMicMuted() const +{ + if (state_ < State::INITIATED) + return false; + + GstElement *srclevel = gst_bin_get_by_name(GST_BIN(pipe_), "srclevel"); + gboolean muted; + g_object_get(srclevel, "mute", &muted, nullptr); + gst_object_unref(srclevel); + return muted; +} + +bool +WebRTCSession::toggleMicMute() { if (state_ < State::INITIATED) return false; diff --git a/src/WebRTCSession.h b/src/WebRTCSession.h index 9593def9..83cabf5c 100644 --- a/src/WebRTCSession.h +++ b/src/WebRTCSession.h @@ -47,7 +47,8 @@ public: bool acceptAnswer(const std::string &sdp); void acceptICECandidates(const std::vector &); - bool toggleMuteAudioSource(); + bool isMicMuted() const; + bool toggleMicMute(); void end(); void setStunServer(const std::string &stunServer) { stunServer_ = stunServer; } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 09ab8cf8..2b453e56 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -7,7 +7,6 @@ #include #include "BlurhashProvider.h" -#include "CallManager.h" #include "ChatPage.h" #include "ColorImageProvider.h" #include "DelegateChooser.h" @@ -145,6 +144,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin &WebRTCSession::stateChanged, this, &TimelineViewManager::callStateChanged); + connect( + callManager_, &CallManager::newCallParty, this, &TimelineViewManager::callPartyChanged); } void @@ -216,6 +217,13 @@ TimelineViewManager::escapeEmoji(QString str) const return utils::replaceEmoji(str); } +void +TimelineViewManager::toggleMicMute() +{ + WebRTCSession::instance().toggleMicMute(); + emit micMuteChanged(); +} + void TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const { diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 243927ef..9ff9adac 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -10,6 +10,7 @@ #include #include "Cache.h" +#include "CallManager.h" #include "Logging.h" #include "TimelineModel.h" #include "Utils.h" @@ -19,7 +20,6 @@ class MxcImageProvider; class BlurhashProvider; -class CallManager; class ColorImageProvider; class UserSettings; class ChatPage; @@ -35,6 +35,9 @@ class TimelineViewManager : public QObject Q_PROPERTY( bool isNarrowView MEMBER isNarrowView_ READ isNarrowView NOTIFY narrowViewChanged) Q_PROPERTY(webrtc::State callState READ callState NOTIFY callStateChanged) + Q_PROPERTY(QString callPartyName READ callPartyName NOTIFY callPartyChanged) + Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY callPartyChanged) + Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged) public: TimelineViewManager(QSharedPointer userSettings, @@ -51,10 +54,10 @@ public: Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } bool isNarrowView() const { return isNarrowView_; } webrtc::State callState() const { return WebRTCSession::instance().state(); } - Q_INVOKABLE bool toggleMuteAudioSource() - { - return WebRTCSession::instance().toggleMuteAudioSource(); - } + QString callPartyName() const { return callManager_->callPartyName(); } + QString callPartyAvatarUrl() const { return callManager_->callPartyAvatarUrl(); } + bool isMicMuted() const { return WebRTCSession::instance().isMicMuted(); } + Q_INVOKABLE void toggleMicMute(); Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); Q_INVOKABLE QString escapeEmoji(QString str) const; @@ -80,6 +83,8 @@ signals: void showRoomList(); void narrowViewChanged(); void callStateChanged(webrtc::State); + void callPartyChanged(); + void micMuteChanged(); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); From aab6cb88a58d3c070233298e199fd7653cf909df Mon Sep 17 00:00:00 2001 From: trilene Date: Fri, 25 Sep 2020 11:10:45 -0400 Subject: [PATCH 4/5] Fix build for those without GStreamer --- src/WebRTCSession.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/WebRTCSession.cpp b/src/WebRTCSession.cpp index 14f17030..9c8c032d 100644 --- a/src/WebRTCSession.cpp +++ b/src/WebRTCSession.cpp @@ -790,7 +790,13 @@ WebRTCSession::createPipeline(int) } bool -WebRTCSession::toggleMuteAudioSource() +WebRTCSession::isMicMuted() const +{ + return false; +} + +bool +WebRTCSession::toggleMicMute() { return false; } From e57199412aa34ad95e124cad6f8703add92063db Mon Sep 17 00:00:00 2001 From: trilene Date: Fri, 25 Sep 2020 12:09:22 -0400 Subject: [PATCH 5/5] Allow button colors override --- resources/qml/ActiveCallBar.qml | 7 ++++--- resources/qml/ImageButton.qml | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/qml/ActiveCallBar.qml b/resources/qml/ActiveCallBar.qml index 8a63725e..8d837c29 100644 --- a/resources/qml/ActiveCallBar.qml +++ b/resources/qml/ActiveCallBar.qml @@ -92,9 +92,10 @@ Rectangle { ImageButton { width: 24 height: 24 - src: timelineManager.isMicMuted ? - "qrc:/icons/icons/ui/microphone-unmute.png" : - "qrc:/icons/icons/ui/microphone-mute.png" + buttonTextColor: "#000000" + image: timelineManager.isMicMuted ? + ":/icons/icons/ui/microphone-unmute.png" : + ":/icons/icons/ui/microphone-mute.png" hoverEnabled: true ToolTip.visible: hovered diff --git a/resources/qml/ImageButton.qml b/resources/qml/ImageButton.qml index 0a33d376..54399ae7 100644 --- a/resources/qml/ImageButton.qml +++ b/resources/qml/ImageButton.qml @@ -2,8 +2,9 @@ import QtQuick 2.3 import QtQuick.Controls 2.3 AbstractButton { - property string image - property string src + property string image: undefined + property color highlightColor: colors.highlight + property color buttonTextColor: colors.buttonText width: 16 height: 16 id: button @@ -12,7 +13,7 @@ AbstractButton { id: buttonImg // Workaround, can't get icon.source working for now... anchors.fill: parent - source: src ? src : ("image://colorimage/" + image + "?" + (button.hovered ? colors.highlight : colors.buttonText)) + source: "image://colorimage/" + image + "?" + (button.hovered ? highlightColor : buttonTextColor) } MouseArea