From c977c732f2753b710e6e9d2446a03ccc49eeb6dd Mon Sep 17 00:00:00 2001 From: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com> Date: Mon, 21 Nov 2022 17:46:41 -0500 Subject: [PATCH] feature: improve advanced call information overlay GitLab: #925 Change-Id: Id75b14bf431ac421b135beb75918dbf37a81d53c --- build.py | 0 daemon | 2 +- src/app/avadapter.cpp | 46 +++- src/app/avadapter.h | 12 +- src/app/calladapter.cpp | 74 ++++- src/app/calladapter.h | 10 +- src/app/constant/JamiStrings.qml | 13 + src/app/constant/JamiTheme.qml | 4 +- .../components/CallInformationOverlay.qml | 255 ++++++++++++++++++ .../components/CallInformationWindow.qml | 213 --------------- src/app/mainview/components/CallOverlay.qml | 12 +- .../components/CallViewContextMenu.qml | 2 +- src/app/qmlregister.cpp | 5 + src/libclient/CMakeLists.txt | 4 + src/libclient/api/avmodel.h | 13 +- src/libclient/api/callmodel.h | 4 + src/libclient/avmodel.cpp | 37 ++- src/libclient/callInformationListModel.cpp | 114 ++++++++ src/libclient/callInformationListModel.h | 68 +++++ src/libclient/callmodel.cpp | 37 ++- src/libclient/renderer.cpp | 2 +- src/libclient/renderer.h | 2 +- .../rendererinformationlistmodel.cpp | 114 ++++++++ src/libclient/rendererinformationlistmodel.h | 62 +++++ 24 files changed, 853 insertions(+), 252 deletions(-) mode change 100644 => 100755 build.py create mode 100644 src/app/mainview/components/CallInformationOverlay.qml delete mode 100644 src/app/mainview/components/CallInformationWindow.qml create mode 100644 src/libclient/callInformationListModel.cpp create mode 100644 src/libclient/callInformationListModel.h create mode 100644 src/libclient/rendererinformationlistmodel.cpp create mode 100644 src/libclient/rendererinformationlistmodel.h diff --git a/build.py b/build.py old mode 100644 new mode 100755 diff --git a/daemon b/daemon index 9d76cf5cc..eb9c52cee 160000 --- a/daemon +++ b/daemon @@ -1 +1 @@ -Subproject commit 9d76cf5cc767e33ab06054bfa40ee45f671002bd +Subproject commit eb9c52cee45660f68334adb32a23d6f743d6bcf4 diff --git a/src/app/avadapter.cpp b/src/app/avadapter.cpp index 8e072d57a..14edd4fe0 100644 --- a/src/app/avadapter.cpp +++ b/src/app/avadapter.cpp @@ -37,19 +37,27 @@ AvAdapter::AvAdapter(LRCInstance* instance, QObject* parent) : QmlAdapterBase(instance, parent) + , rendererInformationListModel_(std::make_unique<RendererInformationListModel>()) { + set_renderersInfoList(QVariant::fromValue(rendererInformationListModel_.get())); connect(&lrcInstance_->avModel(), &lrc::api::AVModel::audioDeviceEvent, this, &AvAdapter::onAudioDeviceEvent); + // QueuedConnection mandatory to avoid deadlock connect(&lrcInstance_->avModel(), &lrc::api::AVModel::rendererStarted, this, - &AvAdapter::onRendererStarted); + &AvAdapter::onRendererStarted, + Qt::QueuedConnection); connect(&lrcInstance_->avModel(), - &lrc::api::AVModel::onRendererInfosUpdated, + &lrc::api::AVModel::rendererStopped, this, - &AvAdapter::setRenderersInfoList); + &AvAdapter::onRendererStopped); + connect(&lrcInstance_->avModel(), + &lrc::api::AVModel::onRendererFpsChange, + this, + &AvAdapter::updateRenderersFPSInfo); } // The top left corner of primary screen is (0, 0). @@ -323,6 +331,12 @@ AvAdapter::onRendererStarted(const QString& id, const QSize& size) if (callId.isEmpty()) { return; } + + // update renderer Information list + auto& avModel = lrcInstance_->avModel(); + auto rendererInfo = avModel.getRenderersInfo(id)[0]; + rendererInformationListModel_->addElement(qMakePair(id, rendererInfo)); + auto callModel = lrcInstance_->getCurrentCallModel(); auto renderDevice = callModel->getCurrentRenderedDevice(callId); if (!id.contains("://")) @@ -331,6 +345,12 @@ AvAdapter::onRendererStarted(const QString& id, const QSize& size) set_currentRenderingDeviceType(renderDevice.type); } +void +AvAdapter::onRendererStopped(const QString& id) +{ + rendererInformationListModel_->removeElement(id); +} + bool AvAdapter::isSharing() const { @@ -448,7 +468,23 @@ AvAdapter::setHardwareAcceleration(bool accelerate) } void -AvAdapter::setRenderersInfoList(QVariantList renderersInfo) +AvAdapter::resetRendererInfo() +{ + rendererInformationListModel_->reset(); +} + +void +AvAdapter::setRendererInfo() +{ + auto& avModel = lrcInstance_->avModel(); + for (auto rendererInfo : avModel.getRenderersInfo()) { + rendererInformationListModel_->addElement( + qMakePair(rendererInfo["RENDERER_ID"], rendererInfo)); + } +} + +void +AvAdapter::updateRenderersFPSInfo(QPair<QString, QString> fpsInfo) { - set_renderersInfoList(renderersInfo); + rendererInformationListModel_->updateFps(fpsInfo.first, fpsInfo.second); } diff --git a/src/app/avadapter.h b/src/app/avadapter.h index c5ed95761..abd011296 100644 --- a/src/app/avadapter.h +++ b/src/app/avadapter.h @@ -26,6 +26,8 @@ #include <QString> #include <qtutils.h> +#include "rendererinformationlistmodel.h" + class AvAdapter final : public QmlAdapterBase { Q_OBJECT @@ -36,7 +38,7 @@ class AvAdapter final : public QmlAdapterBase QML_PROPERTY(bool, muteCamera) QML_RO_PROPERTY(QStringList, windowsNames) QML_RO_PROPERTY(QList<QVariant>, windowsIds) - QML_RO_PROPERTY(QVariantList, renderersInfoList) + QML_RO_PROPERTY(QVariant, renderersInfoList) public: explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr); @@ -103,14 +105,18 @@ protected: Q_INVOKABLE void increaseCodecPriority(unsigned int id, bool isVideo); Q_INVOKABLE void decreaseCodecPriority(unsigned int id, bool isVideo); + Q_INVOKABLE void resetRendererInfo(); + Q_INVOKABLE void setRendererInfo(); + // TODO: to be removed Q_INVOKABLE bool getHardwareAcceleration(); Q_INVOKABLE void setHardwareAcceleration(bool accelerate); private Q_SLOTS: - void setRenderersInfoList(QVariantList renderersInfo); + void updateRenderersFPSInfo(QPair<QString, QString> fpsInfo); void onAudioDeviceEvent(); void onRendererStarted(const QString& id, const QSize& size); + void onRendererStopped(const QString& id); private: // Get screens arrangement rect relative to primary screen. @@ -118,4 +124,6 @@ private: // Get the screen number int getScreenNumber(int screenId = 0) const; + + std::unique_ptr<RendererInformationListModel> rendererInformationListModel_; }; diff --git a/src/app/calladapter.cpp b/src/app/calladapter.cpp index 90313c0bc..3c5d99bf2 100644 --- a/src/app/calladapter.cpp +++ b/src/app/calladapter.cpp @@ -40,7 +40,10 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent) : QmlAdapterBase(instance, parent) , systemTray_(systemTray) + , callInformationListModel_(std::make_unique<CallInformationListModel>()) { + set_callInformationList(QVariant::fromValue(callInformationListModel_.get())); + timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation); @@ -127,7 +130,6 @@ CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId); auto& callModel = accInfo.callModel; const auto call = callModel->getCall(callId); - auto to = lrcInstance_->accountModel().bestNameForAccount(accountId); const auto& convInfo = lrcInstance_->getConversationFromCallId(callId, accountId); if (convInfo.uid.isEmpty() || call.isOutgoing) @@ -150,7 +152,7 @@ CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId auto from = accInfo.conversationModel->title(convInfo.uid); auto notifId = QString("%1;%2").arg(accountId).arg(convInfo.uid); systemTray_->showNotification(notifId, - tr("%1 missed call").arg(to), + tr("Missed call"), tr("Missed call with %1").arg(from), NotificationType::CHAT, Utils::QImageToByteArray(convAvatar)); @@ -170,6 +172,7 @@ CallAdapter::onParticipantAdded(const QString& callId, int index) try { if (lrcInstance_->get_selectedConvUid().isEmpty()) return; + const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid( lrcInstance_->get_selectedConvUid()); if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) { @@ -191,6 +194,7 @@ CallAdapter::onParticipantRemoved(const QString& callId, int index) try { if (lrcInstance_->get_selectedConvUid().isEmpty()) return; + const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid( lrcInstance_->get_selectedConvUid()); if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) { @@ -223,6 +227,27 @@ CallAdapter::onParticipantUpdated(const QString& callId, int index) } } +void +CallAdapter::onCallStarted(const QString& callId) +{ + if (lrcInstance_->get_selectedConvUid().isEmpty()) + return; + auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_); + auto& callModel = accInfo.callModel; + // update call Information list by adding the new information related to the callId + callInformationListModel_->addElement( + qMakePair(callId, callModel->advancedInformationForCallId(callId))); +} + +void +CallAdapter::onCallEnded(const QString& callId) +{ + if (lrcInstance_->get_selectedConvUid().isEmpty()) + return; + // update call Information list by removing information related to the callId + callInformationListModel_->removeElement(callId); +} + void CallAdapter::onCallStatusChanged(const QString& callId, int code) { @@ -570,7 +595,6 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid) { auto& accInfo = lrcInstance_->getAccountInfo(accountId); auto title = accInfo.conversationModel->title(convUid); - auto to = lrcInstance_->accountModel().bestNameForAccount(accountId); auto preferences = accInfo.conversationModel->getConversationPreferences(convUid); // Ignore notifications for this conversation @@ -581,7 +605,7 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid) auto convAvatar = Utils::conversationAvatar(lrcInstance_, convUid, QSize(50, 50), accountId); auto notifId = QString("%1;%2").arg(accountId).arg(convUid); systemTray_->showNotification(notifId, - tr("%1 incoming call").arg(to), + tr("Incoming call"), tr("%1 is calling you").arg(title), NotificationType::CALL, Utils::QImageToByteArray(convAvatar)); @@ -620,6 +644,18 @@ CallAdapter::connectCallModel(const QString& accountId) &CallAdapter::onParticipantUpdated, Qt::UniqueConnection); + connect(accInfo.callModel.get(), + &CallModel::callStarted, + this, + &CallAdapter::onCallStarted, + Qt::UniqueConnection); + + connect(accInfo.callModel.get(), + &CallModel::callEnded, + this, + &CallAdapter::onCallEnded, + Qt::UniqueConnection); + connect(accInfo.callModel.get(), &CallModel::callStatusChanged, this, @@ -1071,13 +1107,39 @@ CallAdapter::getCallDurationTime(const QString& accountId, const QString& convUi return QString(); } +void +CallAdapter::resetCallInfo() +{ + callInformationListModel_->reset(); +} + +void +CallAdapter::setCallInfo() +{ + try { + auto& callModel = lrcInstance_->accountModel().getAccountInfo(accountId_).callModel; + for (auto callId : callModel->getCallIds()) { + callInformationListModel_->addElement( + qMakePair(callId, callModel->advancedInformationForCallId(callId))); + } + + } catch (const std::exception& e) { + qWarning() << e.what(); + } +} + void CallAdapter::updateAdvancedInformation() { try { auto& callModel = lrcInstance_->accountModel().getAccountInfo(accountId_).callModel; - if (callModel) - set_callInformation(QVariantList::fromList(callModel->getAdvancedInformation())); + for (auto callId : callModel->getCallIds()) { + if (!callInformationListModel_->addElement( + qMakePair(callId, callModel->advancedInformationForCallId(callId)))) { + callInformationListModel_->editElement( + qMakePair(callId, callModel->advancedInformationForCallId(callId))); + } + } } catch (const std::exception& e) { qWarning() << e.what(); } diff --git a/src/app/calladapter.h b/src/app/calladapter.h index cf6d3eec9..e83387e6a 100644 --- a/src/app/calladapter.h +++ b/src/app/calladapter.h @@ -31,13 +31,15 @@ #include <QVariant> #include <QSystemTrayIcon> +#include "callInformationListModel.h" + class SystemTray; class CallAdapter final : public QmlAdapterBase { Q_OBJECT QML_PROPERTY(bool, hasCall) - QML_RO_PROPERTY(QVariantList, callInformation) + QML_RO_PROPERTY(QVariant, callInformationList) public: QTimer* timer; @@ -92,6 +94,8 @@ public: const QString& accountId = {}, bool forceCallOnly = false); Q_INVOKABLE QString getCallDurationTime(const QString& accountId, const QString& convUid); + Q_INVOKABLE void resetCallInfo(); + Q_INVOKABLE void setCallInfo(); Q_INVOKABLE void updateAdvancedInformation(); Q_SIGNALS: @@ -112,6 +116,8 @@ public Q_SLOTS: void onParticipantAdded(const QString& callId, int index); void onParticipantRemoved(const QString& callId, int index); void onParticipantUpdated(const QString& callId, int index); + void onCallStarted(const QString& callId); + void onCallEnded(const QString& callId); private: void showNotification(const QString& accountId, const QString& convUid); @@ -126,4 +132,6 @@ private: QScopedPointer<CallOverlayModel> overlayModel_; QScopedPointer<CallParticipantsModel> participantsModel_; VectorString currentConfSubcalls_; + + std::unique_ptr<CallInformationListModel> callInformationListModel_; }; diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml index 510237180..f69be343e 100644 --- a/src/app/constant/JamiStrings.qml +++ b/src/app/constant/JamiStrings.qml @@ -299,6 +299,19 @@ Item { property string raiseHand: qsTr("Raise hand") property string layoutSettings: qsTr("Layout settings") + //advanced information + property string renderersInformation: qsTr("Renderers information") + property string callInformation: qsTr("Call information") + property string peerNumber: qsTr("Peer number") + property string callId: qsTr("Call id") + property string sockets: qsTr("Sockets") + property string videoCodec: qsTr("Video codec") + property string hardwareAcceleration: qsTr("Hardware acceleration") + property string videoBitrate: qsTr("Video bitrate") + property string audioCodec: qsTr("Audio codec") + property string rendererId: qsTr("Renderer id") + property string fps_short: qsTr("Fps") + // Share location/position property string shareLocation: qsTr("Share location") property string stopSharingLocation: qsTr("Stop sharing") diff --git a/src/app/constant/JamiTheme.qml b/src/app/constant/JamiTheme.qml index 1b68a3e8c..80c71c474 100644 --- a/src/app/constant/JamiTheme.qml +++ b/src/app/constant/JamiTheme.qml @@ -376,9 +376,11 @@ Item { //Call information property real textFontPointSize: calcSize(10) property real titleFontPointSize: calcSize(13) - property color callInfoColor: chatviewTextColor + property color callInfoColor: whiteColor property int callInformationElementsSpacing: 5 property int callInformationBlockSpacing: 25 + property int callInformationlayoutMargins: 10 + // Jami switch property real switchIndicatorRadius: 30 diff --git a/src/app/mainview/components/CallInformationOverlay.qml b/src/app/mainview/components/CallInformationOverlay.qml new file mode 100644 index 000000000..c5b06c014 --- /dev/null +++ b/src/app/mainview/components/CallInformationOverlay.qml @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2022 Savoir-faire Linux Inc. + * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +import net.jami.Models 1.1 +import net.jami.Adapters 1.1 +import net.jami.Constants 1.1 +import Qt5Compat.GraphicalEffects + +import "../../commoncomponents" + +Popup { + id: root + + property real maxHeight: parent.height * 40 / 100 + property real maxTextWidth: parent.width * 30 / 100 + + property var advancedList + property var fps + + width: container.width + height: container.height + closePolicy: Popup.NoAutoClosed + + onClosed: { + CallAdapter.stopTimerInformation() + } + + onOpened: { + AvAdapter.resetRendererInfo() + CallAdapter.resetCallInfo() + CallAdapter.setCallInfo() + AvAdapter.setRendererInfo() + } + + background: Rectangle { + color: JamiTheme.transparentColor + } + + Rectangle { + id: container + + color: JamiTheme.blackColor + opacity: 0.85 + radius: 10 + width: windowContent.width + height: windowContent.height + + PushButton { + id: closeButton + + anchors.top: container.top + anchors.topMargin: 5 + anchors.right: container.right + anchors.rightMargin: 5 + normalColor: JamiTheme.transparentColor + imageColor: JamiTheme.callInfoColor + source: JamiResources.round_close_24dp_svg + circled: false + toolTipText: JamiStrings.close + + onClicked: { + root.close() + } + } + + RowLayout { + id: windowContent + + ColumnLayout { + spacing: JamiTheme.callInformationBlockSpacing + Layout.margins: JamiTheme.callInformationlayoutMargins + Layout.preferredWidth: callInfoListview.width + Layout.alignment: Qt.AlignTop + + Text{ + id: textTest + color: JamiTheme.callInfoColor + text: JamiStrings.callInformation + font.pointSize: JamiTheme.titleFontPointSize + } + + ListView { + id: callInfoListview + + model: advancedList + Layout.preferredWidth: root.maxTextWidth + Layout.preferredHeight: contentItem.childrenRect.height < root.maxHeight ? contentItem.childrenRect.height : root.maxHeight + spacing: JamiTheme.callInformationBlockSpacing + clip: true + + delegate: Column { + spacing: JamiTheme.callInformationElementsSpacing + + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.callId + ": " + CALL_ID + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: callInfoListview.width + } + + Text { + function stringWithoutRing(peerNumber){ + return peerNumber.replace("@ring.dht","") ; + } + color: JamiTheme.callInfoColor + text: JamiStrings.peerNumber + ": " + stringWithoutRing(PEER_NUMBER) + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: callInfoListview.width + } + Column { + id: socketLayout + + property bool showAll: false + width: callInfoListview.width + + RowLayout { + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.sockets + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: socketLayout.width + } + + PushButton { + source: socketLayout.showAll ? JamiResources.expand_less_24dp_svg : JamiResources.expand_more_24dp_svg + normalColor: JamiTheme.transparentColor + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + imageColor: JamiTheme.callInfoColor + onClicked: { + socketLayout.showAll = !socketLayout.showAll + } + } + } + + Text { + color: JamiTheme.callInfoColor + text: SOCKETS + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + visible: socketLayout.showAll + width: socketLayout.width + } + } + + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.videoCodec + ": " + VIDEO_CODEC + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: callInfoListview.width + } + + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.audioCodec + ": " + AUDIO_CODEC + " " + AUDIO_SAMPLE_RATE + " Hz" + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: callInfoListview.width + } + + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.hardwareAcceleration + ": " + HARDWARE_ACCELERATION + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: callInfoListview.width + } + + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.videoBitrate + ": " + VIDEO_BITRATE + " bps" + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: callInfoListview.width + } + } + } + } + + ColumnLayout { + spacing: JamiTheme.callInformationBlockSpacing + Layout.margins: JamiTheme.callInformationlayoutMargins + Layout.preferredWidth: renderersInfoListview.width + Layout.alignment: Qt.AlignTop + + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.renderersInformation + font.pointSize: JamiTheme.titleFontPointSize + } + + ListView { + id: renderersInfoListview + + Layout.preferredWidth: root.maxTextWidth + Layout.preferredHeight: contentItem.childrenRect.height < root.maxHeight ? contentItem.childrenRect.height : root.maxHeight + spacing: JamiTheme.callInformationBlockSpacing + model: fps + clip: true + + delegate: Column { + spacing: JamiTheme.callInformationElementsSpacing + + Text{ + color: JamiTheme.callInfoColor + text: JamiStrings.rendererId + ": " + RENDERER_ID + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: renderersInfoListview.width + } + + Text { + id: testText + color: JamiTheme.callInfoColor + text: JamiStrings.fps_short + ": " + FPS + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: renderersInfoListview.width + } + + Text { + color: JamiTheme.callInfoColor + text: JamiStrings.resolution + ": " + RES + font.pointSize: JamiTheme.textFontPointSize + wrapMode: Text.WrapAnywhere + width: renderersInfoListview.width + } + } + } + } + } + } +} diff --git a/src/app/mainview/components/CallInformationWindow.qml b/src/app/mainview/components/CallInformationWindow.qml deleted file mode 100644 index 533434950..000000000 --- a/src/app/mainview/components/CallInformationWindow.qml +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2022 Savoir-faire Linux Inc. - * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls - -import net.jami.Models 1.1 -import net.jami.Adapters 1.1 -import net.jami.Constants 1.1 -import Qt5Compat.GraphicalEffects - -import "../../commoncomponents" - -Window { - id: root - - width: parent.width * 2 / 3 - height: parent.height * 2 / 3 - property var advancedList - property var fps - - onClosing: { - CallAdapter.stopTimerInformation() - } - - Rectangle { - id: container - - anchors.fill: parent - color: JamiTheme.secondaryBackgroundColor - - RowLayout { - id: windowContent - - anchors.fill: parent - - ColumnLayout { - spacing: JamiTheme.callInformationBlockSpacing - - Text{ - color: JamiTheme.callInfoColor - text: "Call information" - font.pointSize: JamiTheme.titleFontPointSize - } - - Item { - id: itemCallInformation - - Layout.fillHeight: true - Layout.fillWidth: true - clip: true - - ListView { - model: advancedList - width: parent.width - height: root.height - spacing: JamiTheme.callInformationBlockSpacing - - delegate: Column { - spacing: JamiTheme.callInformationElementsSpacing - - Text { - color: JamiTheme.callInfoColor - text: "Call id: " + modelData.CALL_ID - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Video codec: " + modelData.VIDEO_CODEC - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Audio codec: " + modelData.AUDIO_CODEC - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - function stringWithoutRing(peerNumber){ - return peerNumber.replace("@ring.dht","") ; - } - color: JamiTheme.callInfoColor - text: "PEER_NUMBER: " + stringWithoutRing(modelData.PEER_NUMBER) - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Hardware acceleration: " + modelData.HARDWARE_ACCELERATION - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Video min bitrate: " + modelData.VIDEO_MIN_BITRATE - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Video max bitrate: " + modelData.VIDEO_MAX_BITRATE - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Video bitrate: " + modelData.VIDEO_BITRATE - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Sockets: " + modelData.SOCKETS - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemCallInformation.width - } - - } - } - } - } - - ColumnLayout { - spacing: JamiTheme.callInformationBlockSpacing - - Text { - color: JamiTheme.callInfoColor - text: "Renderers information" - font.pointSize: JamiTheme.titleFontPointSize - } - - Item { - id: itemParticipantInformation - - Layout.fillHeight: true - Layout.fillWidth: true - clip: true - - - ListView { - width: parent.width - height: root.height - spacing: JamiTheme.callInformationBlockSpacing - model: fps - - delegate: Column { - spacing: JamiTheme.callInformationElementsSpacing - - Text{ - color: JamiTheme.callInfoColor - text: "Renderer id: " + modelData.ID - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemParticipantInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Fps: " + modelData.FPS - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemParticipantInformation.width - } - - Text { - color: JamiTheme.callInfoColor - text: "Resolution: " + modelData.RES - font.pointSize: JamiTheme.textFontPointSize - wrapMode: Text.WrapAnywhere - width: itemParticipantInformation.width - } - - } - } - } - } - } - } -} diff --git a/src/app/mainview/components/CallOverlay.qml b/src/app/mainview/components/CallOverlay.qml index 1a869aa11..a506609ed 100644 --- a/src/app/mainview/components/CallOverlay.qml +++ b/src/app/mainview/components/CallOverlay.qml @@ -46,7 +46,7 @@ Item { SelectScreenWindowCreation.destroySelectScreenWindow() ScreenRubberBandCreation.destroyScreenRubberBandWindow() PluginHandlerPickerCreation.closePluginHandlerPicker() - callInformationWindow.close() + callInformationOverlay.close() } // x, y position does not need to be translated @@ -71,12 +71,16 @@ Item { y: root.height / 2 - sipInputPanel.height / 2 } - CallInformationWindow { - id: callInformationWindow + CallInformationOverlay { + id: callInformationOverlay visible: false - advancedList: CallAdapter.callInformation + advancedList: CallAdapter.callInformationList fps: AvAdapter.renderersInfoList + + Component.onDestruction: { + CallAdapter.stopTimerInformation(); + } } JamiFileDialog { diff --git a/src/app/mainview/components/CallViewContextMenu.qml b/src/app/mainview/components/CallViewContextMenu.qml index 179df492f..8f27ed55b 100644 --- a/src/app/mainview/components/CallViewContextMenu.qml +++ b/src/app/mainview/components/CallViewContextMenu.qml @@ -192,7 +192,7 @@ ContextMenuAutoLoader { onClicked: { CallAdapter.startTimerInformation(); - callInformationWindow.show() + callInformationOverlay.open() } } ] diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp index d42c8cc5b..1c203dbb1 100644 --- a/src/app/qmlregister.cpp +++ b/src/app/qmlregister.cpp @@ -45,6 +45,8 @@ #include "smartlistmodel.h" #include "conversationlistmodelbase.h" #include "filestosendlistmodel.h" +#include "callInformationListModel.h" +#include "rendererinformationlistmodel.h" #include "qrimageprovider.h" #include "avatarimageprovider.h" @@ -170,6 +172,9 @@ registerTypes(QQmlEngine* engine, QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel); QML_REGISTERTYPE(NS_MODELS, SmartListModel); QML_REGISTERTYPE(NS_MODELS, MessageListModel); + QML_REGISTERTYPE(NS_MODELS, CallInformationListModel); + QML_REGISTERTYPE(NS_MODELS, RendererInformationListModel); + // Roles & type enums for models QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList"); diff --git a/src/libclient/CMakeLists.txt b/src/libclient/CMakeLists.txt index 4c0db810c..d69a28152 100644 --- a/src/libclient/CMakeLists.txt +++ b/src/libclient/CMakeLists.txt @@ -261,6 +261,8 @@ set(LIBCLIENT_SOURCES behaviorcontroller.cpp datatransfermodel.cpp messagelistmodel.cpp + callInformationListModel.cpp + rendererinformationlistmodel.cpp # communication dbus/configurationmanager.cpp @@ -289,6 +291,8 @@ set(LIBCLIENT_HEADERS vcard.h namedirectory.h messagelistmodel.h + callInformationListModel.h + rendererinformationlistmodel.h # interfaces interfaces/pixmapmanipulatori.h diff --git a/src/libclient/api/avmodel.h b/src/libclient/api/avmodel.h index 1e0a782f7..198f5e3af 100644 --- a/src/libclient/api/avmodel.h +++ b/src/libclient/api/avmodel.h @@ -276,17 +276,22 @@ public: QSize getRendererSize(const QString& id); video::Frame getRendererFrame(const QString& id); bool useDirectRenderer() const; + /** + * Get Renderers information + * @param id (optional) : for a specific renderer or for all renderers + */ + QList<MapStringString> getRenderersInfo(QString id = {}); /** * Update renderers information list */ - Q_SLOT void updateRenderersInfo(); + Q_SLOT void updateRenderersFPSInfo(QString rendererId); Q_SIGNALS: /** - * Emitted after an update of renderers information - * @param renderersInfoList Information on all renderers (RES, ID, FPS) + * Emitted after an update of renderer's fps + * @param pair of renderer id and its fps value */ - void onRendererInfosUpdated(QVariantList renderersInfoList); + void onRendererFpsChange(QPair<QString, QString> fpsInfo); /** * Emitted when a renderer is started * @param id of the renderer diff --git a/src/libclient/api/callmodel.h b/src/libclient/api/callmodel.h index 2103a8161..282c300e8 100644 --- a/src/libclient/api/callmodel.h +++ b/src/libclient/api/callmodel.h @@ -397,6 +397,10 @@ public: */ QList<QVariant> getAdvancedInformation() const; + MapStringString advancedInformationForCallId(QString callId) const; + + QStringList getCallIds() const; + Q_SIGNALS: /** diff --git a/src/libclient/avmodel.cpp b/src/libclient/avmodel.cpp index 63aa950ce..a33dec1b2 100644 --- a/src/libclient/avmodel.cpp +++ b/src/libclient/avmodel.cpp @@ -173,21 +173,36 @@ AVModel::~AVModel() } } -void -AVModel::updateRenderersInfo() +QList<MapStringString> +AVModel::getRenderersInfo(QString id) { - QVariantList renderersInfoList; - + QList<MapStringString> infoList; + std::lock_guard<std::mutex> lk(pimpl_->renderers_mtx_); for (auto r = pimpl_->renderers_.begin(); r != pimpl_->renderers_.end(); r++) { - QVariantMap qmap; + MapStringString qmap; auto& rend = r->second; MapStringString mapInfo = rend->getInfos(); - qmap.insert(rend->RES, mapInfo["RES"]); - qmap.insert(rend->ID, mapInfo["ID"]); - qmap.insert(rend->FPS, mapInfo["FPS"]); - renderersInfoList.append(qmap); + if (id.isEmpty() || mapInfo["RENDERER_ID"] == id) { + qmap.insert(rend->RES, mapInfo["RES"]); + qmap.insert(rend->RENDERER_ID, mapInfo["RENDERER_ID"]); + qmap.insert(rend->FPS, mapInfo["FPS"]); + infoList.append(qmap); + } } - Q_EMIT onRendererInfosUpdated(renderersInfoList); + return infoList; + return {}; +} + +void +AVModel::updateRenderersFPSInfo(QString rendererId) +{ + auto it = std::find_if(pimpl_->renderers_.begin(), + pimpl_->renderers_.end(), + [&rendererId](const auto& c) { + return rendererId == c.second->getInfos()["RENDERER_ID"]; + }); + if (it != pimpl_->renderers_.end()) + Q_EMIT onRendererFpsChange(qMakePair(rendererId, it->second->getInfos()["FPS"])); } bool @@ -925,7 +940,7 @@ AVModelPimpl::addRenderer(const QString& id, const QSize& res, const QString& sh renderer, &Renderer::fpsChanged, this, - [this, id](void) { Q_EMIT linked_.updateRenderersInfo(); }, + [this, id](void) { Q_EMIT linked_.updateRenderersFPSInfo(id); }, Qt::DirectConnection); connect( renderer, diff --git a/src/libclient/callInformationListModel.cpp b/src/libclient/callInformationListModel.cpp new file mode 100644 index 000000000..b27d04d4b --- /dev/null +++ b/src/libclient/callInformationListModel.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "callInformationListModel.h" + +CallInformationListModel::CallInformationListModel(QObject* parent) + : QAbstractListModel(parent) +{} + +int +CallInformationListModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return callsInfolist_.size(); +} + +QVariant +CallInformationListModel::data(const QModelIndex& index, int role) const +{ + using namespace InfoList; + if (role == Role::CALL_ID) + return callsInfolist_[index.row()].first; + switch (role) { +#define X(var) \ + case Role::var: \ + return callsInfolist_[index.row()].second[#var]; + CALLINFO_ROLES +#undef X + } + + return QVariant(); +} + +bool +CallInformationListModel::addElement(QPair<QString, MapStringString> callInfo) +{ + // check element existence + auto callId = callInfo.first; + auto it = std::find_if(callsInfolist_.begin(), callsInfolist_.end(), [&callId](const auto& c) { + return callId == c.first; + }); + // if element doesn't exist + if (it == callsInfolist_.end()) { + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + callsInfolist_.append(callInfo); + endInsertRows(); + return true; + } + return false; +} + +void +CallInformationListModel::editElement(QPair<QString, MapStringString> callInfo) +{ + auto it = std::find_if(callsInfolist_.begin(), + callsInfolist_.end(), + [&callInfo](const auto& c) { return callInfo.first == c.first; }); + if (it != callsInfolist_.end()) { + // update infos + auto index = std::distance(callsInfolist_.begin(), it); + QModelIndex modelIndex = QAbstractListModel::index(index, 0); + it->second = callInfo.second; + Q_EMIT dataChanged(modelIndex, modelIndex); + } +} + +QHash<int, QByteArray> +CallInformationListModel::roleNames() const +{ + using namespace InfoList; + QHash<int, QByteArray> roles; +#define X(var) roles[var] = #var; + CALLINFO_ROLES +#undef X + return roles; +} + +void +CallInformationListModel::reset() +{ + beginResetModel(); + callsInfolist_.clear(); + endResetModel(); +} + +void +CallInformationListModel::removeElement(QString callId) +{ + auto it = std::find_if(callsInfolist_.begin(), callsInfolist_.end(), [&callId](const auto& c) { + return callId == c.first; + }); + if (it != callsInfolist_.end()) { + auto elementIndex = std::distance(callsInfolist_.begin(), it); + beginRemoveRows(QModelIndex(), elementIndex, elementIndex); + callsInfolist_.remove(elementIndex); + endRemoveRows(); + } +} diff --git a/src/libclient/callInformationListModel.h b/src/libclient/callInformationListModel.h new file mode 100644 index 000000000..176e34f93 --- /dev/null +++ b/src/libclient/callInformationListModel.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include "api/interaction.h" + +#include <QAbstractListModel> + +#define CALLINFO_ROLES \ + X(CALL_ID) \ + X(PEER_NUMBER) \ + X(SOCKETS) \ + X(VIDEO_CODEC) \ + X(AUDIO_CODEC) \ + X(AUDIO_SAMPLE_RATE) \ + X(HARDWARE_ACCELERATION) \ + X(VIDEO_BITRATE) + +namespace InfoList { + +Q_NAMESPACE +enum Role { + DummyRole = Qt::UserRole + 1, +#define X(role) role, + CALLINFO_ROLES +#undef X +}; +Q_ENUM_NS(Role) +} // namespace InfoList + +class CallInformationListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + CallInformationListModel(QObject* parent = 0); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + Q_INVOKABLE QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + bool addElement(QPair<QString, MapStringString> callInfo); + void editElement(QPair<QString, MapStringString> callInfo); + QHash<int, QByteArray> roleNames() const override; + void reset(); + void removeElement(QString callId); + +protected: + using Role = InfoList::Role; + +private: + QList<QPair<QString, MapStringString>> callsInfolist_; +}; diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp index 17bbded66..0c425dfe8 100644 --- a/src/libclient/callmodel.cpp +++ b/src/libclient/callmodel.cpp @@ -24,6 +24,8 @@ #include "api/avmodel.h" #include "api/behaviorcontroller.h" #include "api/conversationmodel.h" +#include "api/codecmodel.h" + #include "api/contact.h" #include "api/contactmodel.h" #include "api/pluginmodel.h" @@ -56,6 +58,7 @@ using namespace libjami::Media; constexpr static const char HARDWARE_ACCELERATION[] = "HARDWARE_ACCELERATION"; +constexpr static const char AUDIO_CODEC[] = "AUDIO_CODEC"; constexpr static const char CALL_ID[] = "CALL_ID"; static std::uniform_int_distribution<int> dis {0, std::numeric_limits<int>::max()}; @@ -133,6 +136,9 @@ public: ~CallModelPimpl(); QVariantList callAdvancedInformation(); + MapStringString advancedInformationForCallId(QString callId); + + QStringList getCallIds(); /** * Send the profile VCard into a call @@ -389,7 +395,7 @@ CallModel::createCall(const QString& uri, bool isAudioOnly, VectorMapStringStrin } #ifdef ENABLE_LIBWRAP auto callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); -#else // dbus +#else // dbus // do not use auto here (QDBusPendingReply<QString>) QString callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); #endif // ENABLE_LIBWRAP @@ -419,6 +425,18 @@ CallModel::getAdvancedInformation() const return pimpl_->callAdvancedInformation(); } +MapStringString +CallModel::advancedInformationForCallId(QString callId) const +{ + return pimpl_->advancedInformationForCallId(callId); +} + +QStringList +CallModel::getCallIds() const +{ + return pimpl_->getCallIds(); +} + void CallModel::emplaceConversationConference(const QString& confId) { @@ -1026,6 +1044,23 @@ CallModelPimpl::callAdvancedInformation() return advancedInformationList; } +MapStringString +CallModelPimpl::advancedInformationForCallId(QString callId) +{ + MapStringString infoMap = CallManager::instance().getCallDetails(linked.owner.id, callId); + if (lrc.getAVModel().getHardwareAcceleration()) + infoMap[HARDWARE_ACCELERATION] = "True"; + else + infoMap[HARDWARE_ACCELERATION] = "False"; + return infoMap; +} + +QStringList +CallModelPimpl::getCallIds() +{ + return CallManager::instance().getCallList(linked.owner.id); +} + void CallModelPimpl::initCallFromDaemon() { diff --git a/src/libclient/renderer.cpp b/src/libclient/renderer.cpp index e79ceb51f..538070264 100644 --- a/src/libclient/renderer.cpp +++ b/src/libclient/renderer.cpp @@ -62,7 +62,7 @@ MapStringString Renderer::getInfos() const { MapStringString map; - map[ID] = id(); + map[RENDERER_ID] = id(); map[FPS] = QString::number(fps()); map[RES] = QString::number(size().width()) + " * " + QString::number(size().height()); return map; diff --git a/src/libclient/renderer.h b/src/libclient/renderer.h index f850e2f77..d1f37db21 100644 --- a/src/libclient/renderer.h +++ b/src/libclient/renderer.h @@ -36,7 +36,7 @@ class Renderer : public QObject { Q_OBJECT public: - constexpr static const char ID[] = "ID"; + constexpr static const char RENDERER_ID[] = "RENDERER_ID"; constexpr static const char FPS[] = "FPS"; constexpr static const char RES[] = "RES"; constexpr static const int FPS_RATE_SEC = 1; diff --git a/src/libclient/rendererinformationlistmodel.cpp b/src/libclient/rendererinformationlistmodel.cpp new file mode 100644 index 000000000..099726b55 --- /dev/null +++ b/src/libclient/rendererinformationlistmodel.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "rendererinformationlistmodel.h" + +RendererInformationListModel::RendererInformationListModel(QObject* parent) + : QAbstractListModel(parent) +{} + +int +RendererInformationListModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return renderersInfoList_.size(); +} + +QVariant +RendererInformationListModel::data(const QModelIndex& index, int role) const +{ + using namespace RendererInfoList; + + if (role == Role::RENDERER_ID) + return renderersInfoList_[index.row()].first; + + switch (role) { +#define X(var) \ + case Role::var: \ + return renderersInfoList_[index.row()].second[#var]; + RENDERERINFO_ROLES +#undef X + } + + return QVariant(); +} + +void +RendererInformationListModel::updateFps(QString rendererId, QString fps) +{ + auto it = std::find_if(renderersInfoList_.begin(), + renderersInfoList_.end(), + [&rendererId](const auto& c) { return rendererId == c.first; }); + if (it != renderersInfoList_.end()) { + // update fps + auto index = std::distance(renderersInfoList_.begin(), it); + QModelIndex modelIndex = QAbstractListModel::index(index, 0); + it->second["FPS"] = fps; + Q_EMIT dataChanged(modelIndex, modelIndex, {Role::FPS}); + } +} + +void +RendererInformationListModel::addElement(QPair<QString, MapStringString> rendererInfo) +{ + // check element existence + auto rendererId = rendererInfo.first; + auto it = std::find_if(renderersInfoList_.begin(), + renderersInfoList_.end(), + [&rendererId](const auto& c) { return rendererId == c.first; }); + // if element doesn't exist + if (it == renderersInfoList_.end()) { + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + renderersInfoList_.append(rendererInfo); + endInsertRows(); + } +} + +void +RendererInformationListModel::removeElement(QString rendererId) +{ + auto it = std::find_if(renderersInfoList_.begin(), + renderersInfoList_.end(), + [&rendererId](const auto& c) { return rendererId == c.first; }); + if (it != renderersInfoList_.end()) { + auto elementIndex = std::distance(renderersInfoList_.begin(), it); + beginRemoveRows(QModelIndex(), elementIndex, elementIndex); + renderersInfoList_.remove(elementIndex); + endRemoveRows(); + } +} + +QHash<int, QByteArray> +RendererInformationListModel::roleNames() const +{ + using namespace RendererInfoList; + QHash<int, QByteArray> roles; +#define X(var) roles[var] = #var; + RENDERERINFO_ROLES +#undef X + return roles; +} + +void +RendererInformationListModel::reset() +{ + beginResetModel(); + renderersInfoList_.clear(); + endResetModel(); +} diff --git a/src/libclient/rendererinformationlistmodel.h b/src/libclient/rendererinformationlistmodel.h new file mode 100644 index 000000000..a07bf2b29 --- /dev/null +++ b/src/libclient/rendererinformationlistmodel.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 Savoir-faire Linux Inc. + * + * Author: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include "api/interaction.h" + +#include <QAbstractListModel> + +#define RENDERERINFO_ROLES \ + X(RENDERER_ID) \ + X(RES) \ + X(FPS) + +namespace RendererInfoList { +Q_NAMESPACE +enum Role { + DummyRole = Qt::UserRole + 1, +#define X(role) role, + RENDERERINFO_ROLES +#undef X +}; +Q_ENUM_NS(Role) +} // namespace RendererInfoList + +class RendererInformationListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + RendererInformationListModel(QObject* parent = 0); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + Q_INVOKABLE QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + void updateFps(QString rendererId, QString fps); + void addElement(QPair<QString, MapStringString> rendererInfo); + void removeElement(QString rendererId); + QHash<int, QByteArray> roleNames() const override; + void reset(); + +protected: + using Role = RendererInfoList::Role; + +private: + QList<QPair<QString, MapStringString>> renderersInfoList_; +}; -- GitLab