1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-07-31 04:45:39 +02:00

feature: improve advanced call information overlay

GitLab: #925

Change-Id: Id75b14bf431ac421b135beb75918dbf37a81d53c
This commit is contained in:
Nicolas Vengeon 2022-11-21 17:46:41 -05:00 committed by Sébastien Blin
parent a28d5c5c55
commit c977c732f2
24 changed files with 853 additions and 252 deletions

0
build.py Normal file → Executable file
View file

2
daemon

@ -1 +1 @@
Subproject commit 9d76cf5cc767e33ab06054bfa40ee45f671002bd Subproject commit eb9c52cee45660f68334adb32a23d6f743d6bcf4

View file

@ -37,19 +37,27 @@
AvAdapter::AvAdapter(LRCInstance* instance, QObject* parent) AvAdapter::AvAdapter(LRCInstance* instance, QObject* parent)
: QmlAdapterBase(instance, parent) : QmlAdapterBase(instance, parent)
, rendererInformationListModel_(std::make_unique<RendererInformationListModel>())
{ {
set_renderersInfoList(QVariant::fromValue(rendererInformationListModel_.get()));
connect(&lrcInstance_->avModel(), connect(&lrcInstance_->avModel(),
&lrc::api::AVModel::audioDeviceEvent, &lrc::api::AVModel::audioDeviceEvent,
this, this,
&AvAdapter::onAudioDeviceEvent); &AvAdapter::onAudioDeviceEvent);
// QueuedConnection mandatory to avoid deadlock
connect(&lrcInstance_->avModel(), connect(&lrcInstance_->avModel(),
&lrc::api::AVModel::rendererStarted, &lrc::api::AVModel::rendererStarted,
this, this,
&AvAdapter::onRendererStarted); &AvAdapter::onRendererStarted,
Qt::QueuedConnection);
connect(&lrcInstance_->avModel(), connect(&lrcInstance_->avModel(),
&lrc::api::AVModel::onRendererInfosUpdated, &lrc::api::AVModel::rendererStopped,
this, 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). // 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()) { if (callId.isEmpty()) {
return; 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 callModel = lrcInstance_->getCurrentCallModel();
auto renderDevice = callModel->getCurrentRenderedDevice(callId); auto renderDevice = callModel->getCurrentRenderedDevice(callId);
if (!id.contains("://")) if (!id.contains("://"))
@ -331,6 +345,12 @@ AvAdapter::onRendererStarted(const QString& id, const QSize& size)
set_currentRenderingDeviceType(renderDevice.type); set_currentRenderingDeviceType(renderDevice.type);
} }
void
AvAdapter::onRendererStopped(const QString& id)
{
rendererInformationListModel_->removeElement(id);
}
bool bool
AvAdapter::isSharing() const AvAdapter::isSharing() const
{ {
@ -448,7 +468,23 @@ AvAdapter::setHardwareAcceleration(bool accelerate)
} }
void void
AvAdapter::setRenderersInfoList(QVariantList renderersInfo) AvAdapter::resetRendererInfo()
{ {
set_renderersInfoList(renderersInfo); 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)
{
rendererInformationListModel_->updateFps(fpsInfo.first, fpsInfo.second);
} }

View file

@ -26,6 +26,8 @@
#include <QString> #include <QString>
#include <qtutils.h> #include <qtutils.h>
#include "rendererinformationlistmodel.h"
class AvAdapter final : public QmlAdapterBase class AvAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
@ -36,7 +38,7 @@ class AvAdapter final : public QmlAdapterBase
QML_PROPERTY(bool, muteCamera) QML_PROPERTY(bool, muteCamera)
QML_RO_PROPERTY(QStringList, windowsNames) QML_RO_PROPERTY(QStringList, windowsNames)
QML_RO_PROPERTY(QList<QVariant>, windowsIds) QML_RO_PROPERTY(QList<QVariant>, windowsIds)
QML_RO_PROPERTY(QVariantList, renderersInfoList) QML_RO_PROPERTY(QVariant, renderersInfoList)
public: public:
explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr); explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr);
@ -103,14 +105,18 @@ protected:
Q_INVOKABLE void increaseCodecPriority(unsigned int id, bool isVideo); Q_INVOKABLE void increaseCodecPriority(unsigned int id, bool isVideo);
Q_INVOKABLE void decreaseCodecPriority(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 // TODO: to be removed
Q_INVOKABLE bool getHardwareAcceleration(); Q_INVOKABLE bool getHardwareAcceleration();
Q_INVOKABLE void setHardwareAcceleration(bool accelerate); Q_INVOKABLE void setHardwareAcceleration(bool accelerate);
private Q_SLOTS: private Q_SLOTS:
void setRenderersInfoList(QVariantList renderersInfo); void updateRenderersFPSInfo(QPair<QString, QString> fpsInfo);
void onAudioDeviceEvent(); void onAudioDeviceEvent();
void onRendererStarted(const QString& id, const QSize& size); void onRendererStarted(const QString& id, const QSize& size);
void onRendererStopped(const QString& id);
private: private:
// Get screens arrangement rect relative to primary screen. // Get screens arrangement rect relative to primary screen.
@ -118,4 +124,6 @@ private:
// Get the screen number // Get the screen number
int getScreenNumber(int screenId = 0) const; int getScreenNumber(int screenId = 0) const;
std::unique_ptr<RendererInformationListModel> rendererInformationListModel_;
}; };

View file

@ -40,7 +40,10 @@
CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent) CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent)
: QmlAdapterBase(instance, parent) : QmlAdapterBase(instance, parent)
, systemTray_(systemTray) , systemTray_(systemTray)
, callInformationListModel_(std::make_unique<CallInformationListModel>())
{ {
set_callInformationList(QVariant::fromValue(callInformationListModel_.get()));
timer = new QTimer(this); timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation); 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& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
auto& callModel = accInfo.callModel; auto& callModel = accInfo.callModel;
const auto call = callModel->getCall(callId); const auto call = callModel->getCall(callId);
auto to = lrcInstance_->accountModel().bestNameForAccount(accountId);
const auto& convInfo = lrcInstance_->getConversationFromCallId(callId, accountId); const auto& convInfo = lrcInstance_->getConversationFromCallId(callId, accountId);
if (convInfo.uid.isEmpty() || call.isOutgoing) 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 from = accInfo.conversationModel->title(convInfo.uid);
auto notifId = QString("%1;%2").arg(accountId).arg(convInfo.uid); auto notifId = QString("%1;%2").arg(accountId).arg(convInfo.uid);
systemTray_->showNotification(notifId, systemTray_->showNotification(notifId,
tr("%1 missed call").arg(to), tr("Missed call"),
tr("Missed call with %1").arg(from), tr("Missed call with %1").arg(from),
NotificationType::CHAT, NotificationType::CHAT,
Utils::QImageToByteArray(convAvatar)); Utils::QImageToByteArray(convAvatar));
@ -170,6 +172,7 @@ CallAdapter::onParticipantAdded(const QString& callId, int index)
try { try {
if (lrcInstance_->get_selectedConvUid().isEmpty()) if (lrcInstance_->get_selectedConvUid().isEmpty())
return; return;
const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid( const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid(
lrcInstance_->get_selectedConvUid()); lrcInstance_->get_selectedConvUid());
if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) { if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) {
@ -191,6 +194,7 @@ CallAdapter::onParticipantRemoved(const QString& callId, int index)
try { try {
if (lrcInstance_->get_selectedConvUid().isEmpty()) if (lrcInstance_->get_selectedConvUid().isEmpty())
return; return;
const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid( const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid(
lrcInstance_->get_selectedConvUid()); lrcInstance_->get_selectedConvUid());
if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) { 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 void
CallAdapter::onCallStatusChanged(const QString& callId, int code) 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& accInfo = lrcInstance_->getAccountInfo(accountId);
auto title = accInfo.conversationModel->title(convUid); auto title = accInfo.conversationModel->title(convUid);
auto to = lrcInstance_->accountModel().bestNameForAccount(accountId);
auto preferences = accInfo.conversationModel->getConversationPreferences(convUid); auto preferences = accInfo.conversationModel->getConversationPreferences(convUid);
// Ignore notifications for this conversation // 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 convAvatar = Utils::conversationAvatar(lrcInstance_, convUid, QSize(50, 50), accountId);
auto notifId = QString("%1;%2").arg(accountId).arg(convUid); auto notifId = QString("%1;%2").arg(accountId).arg(convUid);
systemTray_->showNotification(notifId, systemTray_->showNotification(notifId,
tr("%1 incoming call").arg(to), tr("Incoming call"),
tr("%1 is calling you").arg(title), tr("%1 is calling you").arg(title),
NotificationType::CALL, NotificationType::CALL,
Utils::QImageToByteArray(convAvatar)); Utils::QImageToByteArray(convAvatar));
@ -620,6 +644,18 @@ CallAdapter::connectCallModel(const QString& accountId)
&CallAdapter::onParticipantUpdated, &CallAdapter::onParticipantUpdated,
Qt::UniqueConnection); 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(), connect(accInfo.callModel.get(),
&CallModel::callStatusChanged, &CallModel::callStatusChanged,
this, this,
@ -1071,13 +1107,39 @@ CallAdapter::getCallDurationTime(const QString& accountId, const QString& convUi
return QString(); 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 void
CallAdapter::updateAdvancedInformation() CallAdapter::updateAdvancedInformation()
{ {
try { try {
auto& callModel = lrcInstance_->accountModel().getAccountInfo(accountId_).callModel; auto& callModel = lrcInstance_->accountModel().getAccountInfo(accountId_).callModel;
if (callModel) for (auto callId : callModel->getCallIds()) {
set_callInformation(QVariantList::fromList(callModel->getAdvancedInformation())); if (!callInformationListModel_->addElement(
qMakePair(callId, callModel->advancedInformationForCallId(callId)))) {
callInformationListModel_->editElement(
qMakePair(callId, callModel->advancedInformationForCallId(callId)));
}
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
qWarning() << e.what(); qWarning() << e.what();
} }

View file

@ -31,13 +31,15 @@
#include <QVariant> #include <QVariant>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include "callInformationListModel.h"
class SystemTray; class SystemTray;
class CallAdapter final : public QmlAdapterBase class CallAdapter final : public QmlAdapterBase
{ {
Q_OBJECT Q_OBJECT
QML_PROPERTY(bool, hasCall) QML_PROPERTY(bool, hasCall)
QML_RO_PROPERTY(QVariantList, callInformation) QML_RO_PROPERTY(QVariant, callInformationList)
public: public:
QTimer* timer; QTimer* timer;
@ -92,6 +94,8 @@ public:
const QString& accountId = {}, const QString& accountId = {},
bool forceCallOnly = false); bool forceCallOnly = false);
Q_INVOKABLE QString getCallDurationTime(const QString& accountId, const QString& convUid); Q_INVOKABLE QString getCallDurationTime(const QString& accountId, const QString& convUid);
Q_INVOKABLE void resetCallInfo();
Q_INVOKABLE void setCallInfo();
Q_INVOKABLE void updateAdvancedInformation(); Q_INVOKABLE void updateAdvancedInformation();
Q_SIGNALS: Q_SIGNALS:
@ -112,6 +116,8 @@ public Q_SLOTS:
void onParticipantAdded(const QString& callId, int index); void onParticipantAdded(const QString& callId, int index);
void onParticipantRemoved(const QString& callId, int index); void onParticipantRemoved(const QString& callId, int index);
void onParticipantUpdated(const QString& callId, int index); void onParticipantUpdated(const QString& callId, int index);
void onCallStarted(const QString& callId);
void onCallEnded(const QString& callId);
private: private:
void showNotification(const QString& accountId, const QString& convUid); void showNotification(const QString& accountId, const QString& convUid);
@ -126,4 +132,6 @@ private:
QScopedPointer<CallOverlayModel> overlayModel_; QScopedPointer<CallOverlayModel> overlayModel_;
QScopedPointer<CallParticipantsModel> participantsModel_; QScopedPointer<CallParticipantsModel> participantsModel_;
VectorString currentConfSubcalls_; VectorString currentConfSubcalls_;
std::unique_ptr<CallInformationListModel> callInformationListModel_;
}; };

View file

@ -299,6 +299,19 @@ Item {
property string raiseHand: qsTr("Raise hand") property string raiseHand: qsTr("Raise hand")
property string layoutSettings: qsTr("Layout settings") 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 // Share location/position
property string shareLocation: qsTr("Share location") property string shareLocation: qsTr("Share location")
property string stopSharingLocation: qsTr("Stop sharing") property string stopSharingLocation: qsTr("Stop sharing")

View file

@ -376,9 +376,11 @@ Item {
//Call information //Call information
property real textFontPointSize: calcSize(10) property real textFontPointSize: calcSize(10)
property real titleFontPointSize: calcSize(13) property real titleFontPointSize: calcSize(13)
property color callInfoColor: chatviewTextColor property color callInfoColor: whiteColor
property int callInformationElementsSpacing: 5 property int callInformationElementsSpacing: 5
property int callInformationBlockSpacing: 25 property int callInformationBlockSpacing: 25
property int callInformationlayoutMargins: 10
// Jami switch // Jami switch
property real switchIndicatorRadius: 30 property real switchIndicatorRadius: 30

View file

@ -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
}
}
}
}
}
}
}

View file

@ -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
}
}
}
}
}
}
}
}

View file

@ -46,7 +46,7 @@ Item {
SelectScreenWindowCreation.destroySelectScreenWindow() SelectScreenWindowCreation.destroySelectScreenWindow()
ScreenRubberBandCreation.destroyScreenRubberBandWindow() ScreenRubberBandCreation.destroyScreenRubberBandWindow()
PluginHandlerPickerCreation.closePluginHandlerPicker() PluginHandlerPickerCreation.closePluginHandlerPicker()
callInformationWindow.close() callInformationOverlay.close()
} }
// x, y position does not need to be translated // x, y position does not need to be translated
@ -71,12 +71,16 @@ Item {
y: root.height / 2 - sipInputPanel.height / 2 y: root.height / 2 - sipInputPanel.height / 2
} }
CallInformationWindow { CallInformationOverlay {
id: callInformationWindow id: callInformationOverlay
visible: false visible: false
advancedList: CallAdapter.callInformation advancedList: CallAdapter.callInformationList
fps: AvAdapter.renderersInfoList fps: AvAdapter.renderersInfoList
Component.onDestruction: {
CallAdapter.stopTimerInformation();
}
} }
JamiFileDialog { JamiFileDialog {

View file

@ -192,7 +192,7 @@ ContextMenuAutoLoader {
onClicked: { onClicked: {
CallAdapter.startTimerInformation(); CallAdapter.startTimerInformation();
callInformationWindow.show() callInformationOverlay.open()
} }
} }
] ]

View file

@ -45,6 +45,8 @@
#include "smartlistmodel.h" #include "smartlistmodel.h"
#include "conversationlistmodelbase.h" #include "conversationlistmodelbase.h"
#include "filestosendlistmodel.h" #include "filestosendlistmodel.h"
#include "callInformationListModel.h"
#include "rendererinformationlistmodel.h"
#include "qrimageprovider.h" #include "qrimageprovider.h"
#include "avatarimageprovider.h" #include "avatarimageprovider.h"
@ -170,6 +172,9 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel); QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel);
QML_REGISTERTYPE(NS_MODELS, SmartListModel); QML_REGISTERTYPE(NS_MODELS, SmartListModel);
QML_REGISTERTYPE(NS_MODELS, MessageListModel); QML_REGISTERTYPE(NS_MODELS, MessageListModel);
QML_REGISTERTYPE(NS_MODELS, CallInformationListModel);
QML_REGISTERTYPE(NS_MODELS, RendererInformationListModel);
// Roles & type enums for models // Roles & type enums for models
QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList"); QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList");

View file

@ -261,6 +261,8 @@ set(LIBCLIENT_SOURCES
behaviorcontroller.cpp behaviorcontroller.cpp
datatransfermodel.cpp datatransfermodel.cpp
messagelistmodel.cpp messagelistmodel.cpp
callInformationListModel.cpp
rendererinformationlistmodel.cpp
# communication # communication
dbus/configurationmanager.cpp dbus/configurationmanager.cpp
@ -289,6 +291,8 @@ set(LIBCLIENT_HEADERS
vcard.h vcard.h
namedirectory.h namedirectory.h
messagelistmodel.h messagelistmodel.h
callInformationListModel.h
rendererinformationlistmodel.h
# interfaces # interfaces
interfaces/pixmapmanipulatori.h interfaces/pixmapmanipulatori.h

View file

@ -276,17 +276,22 @@ public:
QSize getRendererSize(const QString& id); QSize getRendererSize(const QString& id);
video::Frame getRendererFrame(const QString& id); video::Frame getRendererFrame(const QString& id);
bool useDirectRenderer() const; 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 * Update renderers information list
*/ */
Q_SLOT void updateRenderersInfo(); Q_SLOT void updateRenderersFPSInfo(QString rendererId);
Q_SIGNALS: Q_SIGNALS:
/** /**
* Emitted after an update of renderers information * Emitted after an update of renderer's fps
* @param renderersInfoList Information on all renderers (RES, ID, 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 * Emitted when a renderer is started
* @param id of the renderer * @param id of the renderer

View file

@ -397,6 +397,10 @@ public:
*/ */
QList<QVariant> getAdvancedInformation() const; QList<QVariant> getAdvancedInformation() const;
MapStringString advancedInformationForCallId(QString callId) const;
QStringList getCallIds() const;
Q_SIGNALS: Q_SIGNALS:
/** /**

View file

@ -173,21 +173,36 @@ AVModel::~AVModel()
} }
} }
void QList<MapStringString>
AVModel::updateRenderersInfo() 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++) { for (auto r = pimpl_->renderers_.begin(); r != pimpl_->renderers_.end(); r++) {
QVariantMap qmap; MapStringString qmap;
auto& rend = r->second; auto& rend = r->second;
MapStringString mapInfo = rend->getInfos(); MapStringString mapInfo = rend->getInfos();
qmap.insert(rend->RES, mapInfo["RES"]); if (id.isEmpty() || mapInfo["RENDERER_ID"] == id) {
qmap.insert(rend->ID, mapInfo["ID"]); qmap.insert(rend->RES, mapInfo["RES"]);
qmap.insert(rend->FPS, mapInfo["FPS"]); qmap.insert(rend->RENDERER_ID, mapInfo["RENDERER_ID"]);
renderersInfoList.append(qmap); 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 bool
@ -925,7 +940,7 @@ AVModelPimpl::addRenderer(const QString& id, const QSize& res, const QString& sh
renderer, renderer,
&Renderer::fpsChanged, &Renderer::fpsChanged,
this, this,
[this, id](void) { Q_EMIT linked_.updateRenderersInfo(); }, [this, id](void) { Q_EMIT linked_.updateRenderersFPSInfo(id); },
Qt::DirectConnection); Qt::DirectConnection);
connect( connect(
renderer, renderer,

View file

@ -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();
}
}

View file

@ -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_;
};

View file

@ -24,6 +24,8 @@
#include "api/avmodel.h" #include "api/avmodel.h"
#include "api/behaviorcontroller.h" #include "api/behaviorcontroller.h"
#include "api/conversationmodel.h" #include "api/conversationmodel.h"
#include "api/codecmodel.h"
#include "api/contact.h" #include "api/contact.h"
#include "api/contactmodel.h" #include "api/contactmodel.h"
#include "api/pluginmodel.h" #include "api/pluginmodel.h"
@ -56,6 +58,7 @@
using namespace libjami::Media; using namespace libjami::Media;
constexpr static const char HARDWARE_ACCELERATION[] = "HARDWARE_ACCELERATION"; constexpr static const char HARDWARE_ACCELERATION[] = "HARDWARE_ACCELERATION";
constexpr static const char AUDIO_CODEC[] = "AUDIO_CODEC";
constexpr static const char CALL_ID[] = "CALL_ID"; constexpr static const char CALL_ID[] = "CALL_ID";
static std::uniform_int_distribution<int> dis {0, std::numeric_limits<int>::max()}; static std::uniform_int_distribution<int> dis {0, std::numeric_limits<int>::max()};
@ -133,6 +136,9 @@ public:
~CallModelPimpl(); ~CallModelPimpl();
QVariantList callAdvancedInformation(); QVariantList callAdvancedInformation();
MapStringString advancedInformationForCallId(QString callId);
QStringList getCallIds();
/** /**
* Send the profile VCard into a call * Send the profile VCard into a call
@ -389,7 +395,7 @@ CallModel::createCall(const QString& uri, bool isAudioOnly, VectorMapStringStrin
} }
#ifdef ENABLE_LIBWRAP #ifdef ENABLE_LIBWRAP
auto callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); auto callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList);
#else // dbus #else // dbus
// do not use auto here (QDBusPendingReply<QString>) // do not use auto here (QDBusPendingReply<QString>)
QString callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList); QString callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList);
#endif // ENABLE_LIBWRAP #endif // ENABLE_LIBWRAP
@ -419,6 +425,18 @@ CallModel::getAdvancedInformation() const
return pimpl_->callAdvancedInformation(); return pimpl_->callAdvancedInformation();
} }
MapStringString
CallModel::advancedInformationForCallId(QString callId) const
{
return pimpl_->advancedInformationForCallId(callId);
}
QStringList
CallModel::getCallIds() const
{
return pimpl_->getCallIds();
}
void void
CallModel::emplaceConversationConference(const QString& confId) CallModel::emplaceConversationConference(const QString& confId)
{ {
@ -1026,6 +1044,23 @@ CallModelPimpl::callAdvancedInformation()
return advancedInformationList; 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 void
CallModelPimpl::initCallFromDaemon() CallModelPimpl::initCallFromDaemon()
{ {

View file

@ -62,7 +62,7 @@ MapStringString
Renderer::getInfos() const Renderer::getInfos() const
{ {
MapStringString map; MapStringString map;
map[ID] = id(); map[RENDERER_ID] = id();
map[FPS] = QString::number(fps()); map[FPS] = QString::number(fps());
map[RES] = QString::number(size().width()) + " * " + QString::number(size().height()); map[RES] = QString::number(size().width()) + " * " + QString::number(size().height());
return map; return map;

View file

@ -36,7 +36,7 @@ class Renderer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: 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 FPS[] = "FPS";
constexpr static const char RES[] = "RES"; constexpr static const char RES[] = "RES";
constexpr static const int FPS_RATE_SEC = 1; constexpr static const int FPS_RATE_SEC = 1;

View file

@ -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();
}

View file

@ -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_;
};