From 085b71bd76209e6ae5cc6ee305783187f48fbd6f Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Thu, 4 Aug 2022 17:31:21 -0400
Subject: [PATCH] videodevices: replace C++ filter proxy model with QML-SFPM

Change-Id: I348cd7acc4cccee4b5a6784cc2430156bb73667b
---
 .../commoncomponents/SettingParaCombobox.qml  |  11 +-
 src/app/mainview/components/CallActionBar.qml |   9 +-
 .../settingsview/components/VideoSettings.qml |  57 ++++++-
 src/app/videodevices.cpp                      | 145 ++++--------------
 src/app/videodevices.h                        |  64 ++------
 5 files changed, 99 insertions(+), 187 deletions(-)

diff --git a/src/app/commoncomponents/SettingParaCombobox.qml b/src/app/commoncomponents/SettingParaCombobox.qml
index 50215e63e..64e8eaac9 100644
--- a/src/app/commoncomponents/SettingParaCombobox.qml
+++ b/src/app/commoncomponents/SettingParaCombobox.qml
@@ -46,19 +46,16 @@ ComboBox {
         width: root.width
 
         contentItem: Text {
-
             text: {
-                if (index >= 0) {
-                    var currentItem = root.delegateModel.items.get(index)
-                    return currentItem.model[root.textRole].toString()
-                }
-                return ""
+                if (index < 0) return ''
+                var currentItem = root.delegateModel.items.get(index)
+                const value = currentItem.model[root.textRole]
+                return value === undefined ? '' : value.toString()
             }
 
             color: hovered ? JamiTheme.comboboxTextColorHovered : JamiTheme.textColor
             elide: Text.ElideRight
             verticalAlignment: Text.AlignVCenter
-
         }
 
         background: Rectangle {
diff --git a/src/app/mainview/components/CallActionBar.qml b/src/app/mainview/components/CallActionBar.qml
index f70faf8c9..6eecc9638 100644
--- a/src/app/mainview/components/CallActionBar.qml
+++ b/src/app/mainview/components/CallActionBar.qml
@@ -236,14 +236,9 @@ Control {
             id: videoInputMenuAction
             enabled: VideoDevices.listSize !== 0
             text: JamiStrings.selectVideoDevice
-            property var listModel: VideoDevices.devicesSourceModel()
+            property var listModel: VideoDevices.deviceSourceModel
             function accept(index) {
-                if (VideoDevices.listSize < 1)
-                    return
-                // TODO: change it when we can suppot showing default and
-                //       current rendering device at the same time and
-                //       start and stop preview logic in here should be in LRC
-                VideoDevices.setDefaultDevice(index, true)
+                VideoDevices.setDefaultDevice(index)
             }
         }
     ]
diff --git a/src/app/settingsview/components/VideoSettings.qml b/src/app/settingsview/components/VideoSettings.qml
index f03cfb257..2a8593986 100644
--- a/src/app/settingsview/components/VideoSettings.qml
+++ b/src/app/settingsview/components/VideoSettings.qml
@@ -21,6 +21,8 @@ import QtQuick.Controls
 import QtQuick.Layouts
 import Qt5Compat.GraphicalEffects
 
+import SortFilterProxyModel 0.2
+
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Enums 1.1
@@ -118,13 +120,27 @@ ColumnLayout {
         tipText: JamiStrings.selectVideoDevice
         placeholderText: JamiStrings.noVideoDevice
         currentSelectionText: VideoDevices.defaultName
-        comboModel: VideoDevices.devicesFilterModel()
+
+        comboModel: SortFilterProxyModel {
+            id: filteredDevicesModel
+            sourceModel: SortFilterProxyModel {
+                id: deviceSourceModel
+                sourceModel: VideoDevices.deviceSourceModel
+            }
+            filters: ValueFilter {
+                roleName: "DeviceName"
+                value: VideoDevices.defaultName
+                inverted: true
+                enabled: deviceSourceModel.count > 1
+            }
+        }
         role: "DeviceName"
 
         onActivated: {
             // TODO: start and stop preview logic in here should be in LRC
             VideoDevices.stopDevice(previewWidget.deviceId)
-            VideoDevices.setDefaultDevice(modelIndex)
+            VideoDevices.setDefaultDevice(
+                        filteredDevicesModel.mapToSource(modelIndex))
             startPreviewing()
         }
     }
@@ -145,10 +161,24 @@ ColumnLayout {
         labelText: JamiStrings.resolution
         currentSelectionText: VideoDevices.defaultRes
         tipText: JamiStrings.selectVideoResolution
-        comboModel: VideoDevices.resFilterModel()
+
+        comboModel: SortFilterProxyModel {
+            id: filteredResModel
+            sourceModel: SortFilterProxyModel {
+                id: resSourceModel
+                sourceModel: VideoDevices.resSourceModel
+            }
+            filters: ValueFilter {
+                roleName: "Resolution"
+                value: VideoDevices.defaultRes
+                inverted: true
+                enabled: resSourceModel.count > 1
+            }
+        }
         role: "Resolution"
 
-        onActivated: VideoDevices.setDefaultDeviceRes(modelIndex)
+        onActivated: VideoDevices.setDefaultDeviceRes(
+                         filteredResModel.mapToSource(modelIndex))
     }
 
     SettingsComboBox {
@@ -167,10 +197,23 @@ ColumnLayout {
         tipText: JamiStrings.selectFPS
         labelText: JamiStrings.fps
         currentSelectionText: VideoDevices.defaultFps.toString()
-        comboModel: VideoDevices.fpsFilterModel()
+        comboModel: SortFilterProxyModel {
+            id: filteredFpsModel
+            sourceModel: SortFilterProxyModel {
+                id: fpsSourceModel
+                sourceModel: VideoDevices.fpsSourceModel
+            }
+            filters: ValueFilter {
+                roleName: "FPS"
+                value: VideoDevices.defaultFps
+                inverted: true
+                enabled: fpsSourceModel.count > 1
+            }
+        }
         role: "FPS"
 
-        onActivated: VideoDevices.setDefaultDeviceFps(modelIndex)
+        onActivated: VideoDevices.setDefaultDeviceFps(
+                         filteredFpsModel.mapToSource(modelIndex))
     }
 
     ToggleSwitch {
@@ -264,7 +307,7 @@ ColumnLayout {
         comboModel: ListModel { id: screenSharingFpsModel }
         role: "FPS"
         Component.onCompleted: {
-            var elements = VideoDevices.getScreenSharingFpsModel()
+            var elements = VideoDevices.sharingFpsSourceModel
             for (var item in elements) {
                 screenSharingFpsModel.append({"FPS": elements[item]})
             }
diff --git a/src/app/videodevices.cpp b/src/app/videodevices.cpp
index d0ab7cc4e..fe7c1251f 100644
--- a/src/app/videodevices.cpp
+++ b/src/app/videodevices.cpp
@@ -1,4 +1,4 @@
-/*!
+/*
  * Copyright (C) 2020-2022 Savoir-faire Linux Inc.
  * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
  *
@@ -119,10 +119,8 @@ VideoFormatResolutionModel::roleNames() const
 int
 VideoFormatResolutionModel::getCurrentIndex() const
 {
-    QString currentDeviceId = videoDevices_->get_defaultId();
     QString currentResolution = videoDevices_->get_defaultRes();
     auto resultList = match(index(0, 0), Resolution, QVariant(currentResolution));
-
     return resultList.size() > 0 ? resultList[0].row() : 0;
 }
 
@@ -183,21 +181,10 @@ VideoFormatFpsModel::getCurrentIndex() const
 VideoDevices::VideoDevices(LRCInstance* lrcInstance, QObject* parent)
     : QObject(parent)
     , lrcInstance_(lrcInstance)
-    , devicesFilterModel_(new CurrentItemFilterModel(this))
-    , resFilterModel_(new CurrentItemFilterModel(this))
-    , fpsFilterModel_(new CurrentItemFilterModel(this))
 {
-    devicesSourceModel_ = new VideoInputDeviceModel(lrcInstance, this);
-    resSourceModel_ = new VideoFormatResolutionModel(lrcInstance, this);
-    fpsSourceModel_ = new VideoFormatFpsModel(lrcInstance, this);
-
-    devicesFilterModel_->setSourceModel(devicesSourceModel_);
-    resFilterModel_->setSourceModel(resSourceModel_);
-    fpsFilterModel_->setSourceModel(fpsSourceModel_);
-
-    devicesFilterModel_->setFilterRole(VideoInputDeviceModel::DeviceName);
-    resFilterModel_->setFilterRole(VideoFormatResolutionModel::Resolution);
-    fpsFilterModel_->setFilterRole(VideoFormatFpsModel::FPS);
+    deviceListModel_ = new VideoInputDeviceModel(lrcInstance, this);
+    resListModel_ = new VideoFormatResolutionModel(lrcInstance, this);
+    fpsListModel_ = new VideoFormatFpsModel(lrcInstance, this);
 
     connect(&lrcInstance_->avModel(),
             &lrc::api::AVModel::deviceEvent,
@@ -206,67 +193,28 @@ VideoDevices::VideoDevices(LRCInstance* lrcInstance, QObject* parent)
 
     auto displaySettings = lrcInstance_->avModel().getDeviceSettings(DEVICE_DESKTOP);
 
-    auto desktopfpsSource = lrcInstance_->avModel().getDeviceCapabilities(DEVICE_DESKTOP);
-    if (desktopfpsSource.contains(CHANNEL_DEFAULT) && !desktopfpsSource[CHANNEL_DEFAULT].empty()) {
-        desktopfpsSourceModel_ = desktopfpsSource[CHANNEL_DEFAULT][0].second;
-        if (desktopfpsSourceModel_.indexOf(displaySettings.rate) >= 0)
+    auto desktopFpsSource = lrcInstance_->avModel().getDeviceCapabilities(DEVICE_DESKTOP);
+    if (desktopFpsSource.contains(CHANNEL_DEFAULT) && !desktopFpsSource[CHANNEL_DEFAULT].empty()) {
+        sharingFpsListModel_ = desktopFpsSource[CHANNEL_DEFAULT][0].second;
+        if (sharingFpsListModel_.indexOf(displaySettings.rate) >= 0)
             set_screenSharingDefaultFps(displaySettings.rate);
     }
     updateData();
 }
 
-VideoDevices::~VideoDevices() {}
-
-QVariant
-VideoDevices::devicesFilterModel()
-{
-    return QVariant::fromValue(devicesFilterModel_);
-}
-
-QVariant
-VideoDevices::devicesSourceModel()
-{
-    return QVariant::fromValue(devicesSourceModel_);
-}
-
-QVariant
-VideoDevices::resFilterModel()
-{
-    return QVariant::fromValue(resFilterModel_);
-}
-
-QVariant
-VideoDevices::resSourceModel()
-{
-    return QVariant::fromValue(resSourceModel_);
-}
-
-QVariant
-VideoDevices::fpsFilterModel()
-{
-    return QVariant::fromValue(fpsFilterModel_);
-}
-
-QVariant
-VideoDevices::fpsSourceModel()
-{
-    return QVariant::fromValue(fpsSourceModel_);
-}
-
 void
-VideoDevices::setDefaultDevice(int index, bool useSourceModel)
+VideoDevices::setDefaultDevice(int index)
 {
+    if (!listSize_) {
+        return;
+    }
+
     QString deviceId {};
     auto callId = lrcInstance_->getCurrentCallId();
 
-    if (useSourceModel)
-        deviceId = devicesSourceModel_
-                       ->data(devicesSourceModel_->index(index, 0), VideoInputDeviceModel::DeviceId)
-                       .toString();
-    else
-        deviceId = devicesFilterModel_
-                       ->data(devicesFilterModel_->index(index, 0), VideoInputDeviceModel::DeviceId)
-                       .toString();
+    deviceId = deviceListModel_
+                   ->data(deviceListModel_->index(index, 0), VideoInputDeviceModel::DeviceId)
+                   .toString();
 
     lrcInstance_->avModel().setDefaultDevice(deviceId);
 
@@ -279,11 +227,10 @@ VideoDevices::setDefaultDevice(int index, bool useSourceModel)
 const QString
 VideoDevices::getDefaultDevice()
 {
-    auto idx = devicesSourceModel_->getCurrentIndex();
+    auto idx = deviceListModel_->getCurrentIndex();
     auto rendererId = QString("camera://")
-                      + devicesSourceModel_
-                            ->data(devicesSourceModel_->index(idx, 0),
-                                   VideoInputDeviceModel::DeviceId)
+                      + deviceListModel_
+                            ->data(deviceListModel_->index(idx, 0), VideoInputDeviceModel::DeviceId)
                             .toString();
     return rendererId;
 }
@@ -318,8 +265,8 @@ VideoDevices::setDefaultDeviceRes(int index)
 {
     auto& channelCaps = get_defaultResRateList();
     auto settings = lrcInstance_->avModel().getDeviceSettings(get_defaultId());
-    settings.size = resFilterModel_
-                        ->data(resFilterModel_->index(index, 0),
+    settings.size = resListModel_
+                        ->data(resListModel_->index(index, 0),
                                VideoFormatResolutionModel::Resolution)
                         .toString();
 
@@ -339,8 +286,8 @@ VideoDevices::setDefaultDeviceFps(int index)
 {
     auto settings = lrcInstance_->avModel().getDeviceSettings(get_defaultId());
     settings.size = get_defaultRes();
-    settings.rate = fpsFilterModel_
-                        ->data(fpsFilterModel_->index(index, 0), VideoFormatFpsModel::FPS_Float)
+    settings.rate = fpsListModel_
+                        ->data(fpsListModel_->index(index, 0), VideoFormatFpsModel::FPS_Float)
                         .toFloat();
 
     lrcInstance_->avModel().setDeviceSettings(settings);
@@ -358,12 +305,6 @@ VideoDevices::setDisplayFPS(const QString& fps)
     set_screenSharingDefaultFps(fps.toInt());
 }
 
-QVariant
-VideoDevices::getScreenSharingFpsModel()
-{
-    return QVariant::fromValue(desktopfpsSourceModel_.toList());
-}
-
 void
 VideoDevices::updateData()
 {
@@ -377,31 +318,12 @@ VideoDevices::updateData()
                                                        ? CHANNEL_DEFAULT
                                                        : defaultDeviceSettings.channel];
         lrc::api::video::FrameratesList fpsList;
-
         for (int i = 0; i < currentResRateList.size(); i++) {
             if (currentResRateList[i].first == defaultDeviceSettings.size) {
                 fpsList = currentResRateList[i].second;
             }
         }
 
-        if (deviceOpen_ && defaultId_ != defaultDeviceSettings.id) {
-            auto callId = lrcInstance_->getCurrentCallId();
-            if (!callId.isEmpty()) {
-                auto callId = lrcInstance_->getCurrentCallId();
-                auto callInfos = lrcInstance_->getCallInfo(callId,
-                                                           lrcInstance_->get_currentAccountId());
-                for (const auto& media : callInfos->mediaList) {
-                    if (media["MUTED"] == "false" && media["ENABLED"] == "true"
-                        && media["SOURCE"] == getDefaultDevice()) {
-                        /*lrcInstance_->avModel().switchInputTo("camera://" +
-                           defaultDeviceSettings.id, callId);*/
-                        // startDevice("camera://" + defaultDeviceSettings.id);
-                        break;
-                    }
-                }
-            }
-        }
-
         set_defaultChannel(defaultDeviceSettings.channel);
         set_defaultId(defaultDeviceSettings.id);
         set_defaultName(defaultDeviceSettings.name);
@@ -409,10 +331,6 @@ VideoDevices::updateData()
         set_defaultFps(defaultDeviceSettings.rate);
         set_defaultResRateList(currentResRateList);
         set_defaultFpsList(fpsList);
-
-        devicesFilterModel_->setCurrentItemFilter(defaultDeviceSettings.name);
-        resFilterModel_->setCurrentItemFilter(defaultDeviceSettings.size);
-        fpsFilterModel_->setCurrentItemFilter(static_cast<int>(defaultDeviceSettings.rate));
     } else {
         set_defaultChannel("");
         set_defaultId("");
@@ -421,23 +339,22 @@ VideoDevices::updateData()
         set_defaultFps(0);
         set_defaultResRateList({});
         set_defaultFpsList({});
-
-        devicesFilterModel_->setCurrentItemFilter("");
-        resFilterModel_->setCurrentItemFilter("");
-        fpsFilterModel_->setCurrentItemFilter(0);
     }
 
-    devicesSourceModel_->reset();
-    resSourceModel_->reset();
-    fpsSourceModel_->reset();
+    deviceListModel_->reset();
+    resListModel_->reset();
+    fpsListModel_->reset();
+
+    set_deviceSourceModel(QVariant::fromValue(deviceListModel_));
+    set_resSourceModel(QVariant::fromValue(resListModel_));
+    set_fpsSourceModel(QVariant::fromValue(fpsListModel_));
+    set_sharingFpsSourceModel(QVariant::fromValue(sharingFpsListModel_.toList()));
 }
 
 void
 VideoDevices::onVideoDeviceEvent()
 {
     auto& avModel = lrcInstance_->avModel();
-    auto* callModel = lrcInstance_->getCurrentCallModel();
-    auto defaultDevice = avModel.getDefaultDevice();
     QString callId = lrcInstance_->getCurrentCallId();
 
     // Decide whether a device has plugged, unplugged, or nothing has changed.
diff --git a/src/app/videodevices.h b/src/app/videodevices.h
index 2dee808dd..aee88af88 100644
--- a/src/app/videodevices.h
+++ b/src/app/videodevices.h
@@ -1,4 +1,4 @@
-/*!
+/*
  * Copyright (C) 2020-2022 Savoir-faire Linux Inc.
  * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
  *
@@ -23,41 +23,10 @@
 
 #include "api/devicemodel.h"
 
-#include <QSortFilterProxyModel>
 #include <QObject>
 
 class VideoDevices;
 
-class CurrentItemFilterModel final : public QSortFilterProxyModel
-{
-    Q_OBJECT
-
-public:
-    explicit CurrentItemFilterModel(QObject* parent = nullptr)
-        : QSortFilterProxyModel(parent)
-
-    {}
-
-    void setCurrentItemFilter(const QVariant& filter)
-    {
-        currentItemFilter_ = filter;
-    }
-
-    virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override
-    {
-        // Do not filter if there is only one item.
-        if (currentItemFilter_.isNull() || sourceModel()->rowCount() == 1)
-            return true;
-
-        // Exclude current item filter.
-        auto index = sourceModel()->index(sourceRow, 0, sourceParent);
-        return index.data(filterRole()) != currentItemFilter_ && !index.parent().isValid();
-    }
-
-private:
-    QVariant currentItemFilter_ {};
-};
-
 class VideoInputDeviceModel : public QAbstractListModel
 {
     Q_OBJECT
@@ -155,21 +124,16 @@ class VideoDevices : public QObject
     QML_RO_PROPERTY(int, defaultFps)
     QML_PROPERTY(int, screenSharingDefaultFps)
 
+    QML_RO_PROPERTY(QVariant, deviceSourceModel)
+    QML_RO_PROPERTY(QVariant, resSourceModel)
+    QML_RO_PROPERTY(QVariant, fpsSourceModel)
+    QML_RO_PROPERTY(QVariant, sharingFpsSourceModel)
+
 public:
     explicit VideoDevices(LRCInstance* lrcInstance, QObject* parent = nullptr);
-    ~VideoDevices();
-
-    Q_INVOKABLE QVariant devicesFilterModel();
-    Q_INVOKABLE QVariant devicesSourceModel();
-
-    Q_INVOKABLE QVariant resFilterModel();
-    Q_INVOKABLE QVariant resSourceModel();
+    ~VideoDevices() = default;
 
-    Q_INVOKABLE QVariant fpsFilterModel();
-    Q_INVOKABLE QVariant fpsSourceModel();
-    Q_INVOKABLE QVariant getScreenSharingFpsModel();
-
-    Q_INVOKABLE void setDefaultDevice(int index, bool useSourceModel = false);
+    Q_INVOKABLE void setDefaultDevice(int index);
     Q_INVOKABLE const QString getDefaultDevice();
     Q_INVOKABLE QString startDevice(const QString& deviceId, bool force = false);
     Q_INVOKABLE void stopDevice(const QString& deviceId, bool force = false);
@@ -198,17 +162,13 @@ private:
 
     LRCInstance* lrcInstance_;
 
-    CurrentItemFilterModel* devicesFilterModel_;
-    CurrentItemFilterModel* resFilterModel_;
-    CurrentItemFilterModel* fpsFilterModel_;
-
-    VideoInputDeviceModel* devicesSourceModel_;
-    VideoFormatResolutionModel* resSourceModel_;
-    VideoFormatFpsModel* fpsSourceModel_;
+    VideoInputDeviceModel* deviceListModel_;
+    VideoFormatResolutionModel* resListModel_;
+    VideoFormatFpsModel* fpsListModel_;
 
     lrc::api::video::ResRateList defaultResRateList_;
     lrc::api::video::FrameratesList defaultFpsList_;
-    lrc::api::video::FrameratesList desktopfpsSourceModel_;
+    lrc::api::video::FrameratesList sharingFpsListModel_;
 
     constexpr static const char DEVICE_DESKTOP[] = "desktop";
     constexpr static const char CHANNEL_DEFAULT[] = "default";
-- 
GitLab