From 06c3ffa6cee0a62e713acafb7cd4b32df5d4da69 Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Fri, 2 Feb 2024 17:22:46 -0500
Subject: [PATCH] testing: add a configuration tool to the dev-testing window

This will allow the addition of custom parameters to a second anchored window.

"Conversation ID" and "Force local preview" are implemented.

Change-Id: I2366b57e6bb36efb568b06e40ef124a440a39397
---
 src/app/ComponentTestWindow.qml             | 142 ++++++++++++++++++++
 src/app/calloverlaymodel.cpp                |  32 +++--
 src/app/calloverlaymodel.h                  |   4 +-
 src/app/mainview/components/MainOverlay.qml |  12 +-
 src/app/net/jami/Constants/JamiQmlUtils.qml |   6 +
 5 files changed, 184 insertions(+), 12 deletions(-)

diff --git a/src/app/ComponentTestWindow.qml b/src/app/ComponentTestWindow.qml
index 7729d5863..a072ce828 100644
--- a/src/app/ComponentTestWindow.qml
+++ b/src/app/ComponentTestWindow.qml
@@ -17,8 +17,19 @@
 
 import QtQuick
 import QtQuick.Controls
+import QtQuick.Layouts
+import Qt.labs.qmlmodels
 
+import net.jami.Models 1.1
 import net.jami.Adapters 1.1
+import net.jami.Enums 1.1
+import net.jami.Helpers 1.1
+import net.jami.Constants 1.1
+
+import "mainview"
+import "mainview/components"
+import "wizardview"
+import "commoncomponents"
 
 // A window into which we can load a QML file for testing.
 ApplicationWindow {
@@ -62,4 +73,135 @@ ApplicationWindow {
 
     // Closing this window should always exit the application.
     onClosing: Qt.quit()
+
+    // A window to modify properties for Jamified components.
+    // Sometimes we need to modify properties including current conversation ID, account ID, etc.
+    // This window should have a simple layout: a list of editable parameters within a scroll view.
+    Window {
+        id: configTool
+        width: 400
+        height: 400
+        title: "Config tool"
+
+        visible: true
+        // Cannot be closed.
+        flags: Qt.SplashScreen
+
+        // Anchor the window to the right of the parent window.
+        x: appWindow.x + appWindow.width
+        y: appWindow.y
+
+        color: "lightgray"
+
+        Page {
+            anchors.fill: parent
+            header: Control {
+                contentItem: Text {
+                    horizontalAlignment: Text.AlignHCenter
+                    text: "Config tool"
+                }
+                background: Rectangle { color: configTool.color }
+            }
+            contentItem: Control {
+                background: Rectangle { color: Qt.lighter(configTool.color, 1.1) }
+                padding: 10
+                contentItem: ListView {
+                    // Declare types of controls. TODO: add as needed.
+                    Component {
+                        id: checkComponent
+                        CheckBox {
+                            text: label
+                            onCheckedChanged: checkChangedCb(checked)
+                        }
+                    }
+                    Component {
+                        id: comboComponent
+                        Control {
+                            contentItem: RowLayout {
+                                Text { text: label }
+                                ComboBox {
+                                    id: comboBox
+                                    displayText: CurrentConversation.title || "undefined"
+                                    model: getDataModel()
+                                    delegate: ItemDelegate {
+                                        highlighted: comboBox.highlightedIndex === index
+                                        width: parent.width
+                                        text: JamiQmlUtils.getModelData(comboBox.model, index, displayRole)
+                                    }
+                                    onCurrentIndexChanged: onIndexChanged(model, currentIndex)
+                                }
+                            }
+                        }
+                    }
+                    spacing: 5
+                    model: ListModel {
+                        ListElement {
+                            label: "Conversation ID"
+                            type: "combobox"
+                            getDataModel: () => ConversationsAdapter.convListProxyModel
+                            displayRole: ConversationList.Title
+                            onIndexChanged: function(model, index) {
+                                const convUid = JamiQmlUtils.getModelData(model, index, ConversationList.UID);
+                                LRCInstance.selectConversation(convUid);
+                            }
+                        }
+                        ListElement {
+                            label: "Force local preview"
+                            type: "checkbox"
+                            value: false
+                            checkChangedCb: function(checked) {
+                                // Find any child component of type `LocalVideo` and start it.
+                                const localVideo = findChild(loader.item, LocalVideo, "type");
+                                if (localVideo) {
+                                    if (checked) {
+                                        localVideo.startWithId(VideoDevices.getDefaultDevice());
+                                    } else {
+                                        localVideo.startWithId("");
+                                    }
+                                } else {
+                                    console.error("LocalVideo not found");
+                                }
+                            }
+                        }
+                    }
+                    delegate: DelegateChooser {
+                        role: "type"
+                        DelegateChoice {
+                            roleValue: "checkbox"
+                            delegate: checkComponent
+                        }
+                        DelegateChoice {
+                            roleValue: "combobox"
+                            delegate: comboComponent
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // From TestCase.qml, refactored to find a child by type or name.
+    function findChild(parent, searchValue, searchBy = "name") {
+        if (!parent || parent.children === undefined) {
+            console.error("No children found");
+            return null;
+        }
+        // Search directly under the given parent
+        for (var i = 0; i < parent.children.length; ++i) {
+            var child = parent.children[i];
+            var match = false;
+            if (searchBy === "name" && child.objectName === searchValue) {
+                match = true;
+            } else if (searchBy === "type" && child instanceof searchValue) {
+                match = true;
+            }
+            if (match) return child;
+        }
+        // Recursively search in child objects
+        for (i = 0; i < parent.children.length; ++i) {
+            var found = findChild(parent.children[i], searchValue, searchBy);
+            if (found) return found;
+        }
+        return null;
+    }
 }
diff --git a/src/app/calloverlaymodel.cpp b/src/app/calloverlaymodel.cpp
index 44962f205..5e6555216 100644
--- a/src/app/calloverlaymodel.cpp
+++ b/src/app/calloverlaymodel.cpp
@@ -19,6 +19,8 @@
 
 #include "calloverlaymodel.h"
 
+#include "global.h"
+
 #include <QEvent>
 #include <QMouseEvent>
 #include <QQuickWindow>
@@ -360,23 +362,37 @@ CallOverlayModel::clearControls()
 }
 
 void
-CallOverlayModel::registerFilter(QQuickWindow* object, QQuickItem* item)
+CallOverlayModel::registerFilter(QObject* object, QQuickItem* item)
 {
-    if (!object || !item || watchedItems_.contains(item))
+    QQuickWindow* window = qobject_cast<QQuickWindow*>(object);
+    if (!window || !item) {
+        C_WARN << "Attempting to register an invalid object or item" << object << item;
         return;
+    }
+    if (watchedItems_.contains(item)) {
+        C_DBG << "Item already registered" << item;
+    }
     watchedItems_.push_back(item);
-    if (watchedItems_.size() == 1)
-        object->installEventFilter(this);
+    if (watchedItems_.size() == 1) {
+        window->installEventFilter(this);
+    }
 }
 
 void
-CallOverlayModel::unregisterFilter(QQuickWindow* object, QQuickItem* item)
+CallOverlayModel::unregisterFilter(QObject* object, QQuickItem* item)
 {
-    if (!object || !item || !watchedItems_.contains(item))
+    QQuickWindow* window = qobject_cast<QQuickWindow*>(object);
+    if (!window || !item) {
+        C_WARN << "Attempting to unregister an invalid object or item" << object << item;
         return;
+    }
+    if (!watchedItems_.contains(item)) {
+        C_DBG << "Item not registered" << item;
+    }
     watchedItems_.removeOne(item);
-    if (watchedItems_.size() == 0)
-        object->removeEventFilter(this);
+    if (watchedItems_.size() == 0) {
+        window->removeEventFilter(this);
+    }
 }
 
 bool
diff --git a/src/app/calloverlaymodel.h b/src/app/calloverlaymodel.h
index 934802bc2..c940896ad 100644
--- a/src/app/calloverlaymodel.h
+++ b/src/app/calloverlaymodel.h
@@ -137,8 +137,8 @@ public:
     Q_INVOKABLE QVariant overflowHiddenModel();
     Q_INVOKABLE QVariant pendingConferenceesModel();
 
-    Q_INVOKABLE void registerFilter(QQuickWindow* object, QQuickItem* item);
-    Q_INVOKABLE void unregisterFilter(QQuickWindow* object, QQuickItem* item);
+    Q_INVOKABLE void registerFilter(QObject* object, QQuickItem* item);
+    Q_INVOKABLE void unregisterFilter(QObject* object, QQuickItem* item);
     bool eventFilter(QObject* object, QEvent* event) override;
 
 Q_SIGNALS:
diff --git a/src/app/mainview/components/MainOverlay.qml b/src/app/mainview/components/MainOverlay.qml
index 455ecb347..9bc7863ba 100644
--- a/src/app/mainview/components/MainOverlay.qml
+++ b/src/app/mainview/components/MainOverlay.qml
@@ -20,9 +20,11 @@
  */
 import QtQuick
 import QtQuick.Layouts
+
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
+
 import "../../commoncomponents"
 
 Item {
@@ -61,9 +63,15 @@ Item {
 
     // (un)subscribe to an app-wide mouse move event trap filtered
     // for the overlay's geometry
-    onVisibleChanged: {
-        visible ? CallOverlayModel.registerFilter(appWindow, this) : CallOverlayModel.unregisterFilter(appWindow, this);
+    function setupFilter() {
+        if (visible) {
+            CallOverlayModel.registerFilter(appWindow, this);
+        } else {
+            CallOverlayModel.unregisterFilter(appWindow, this);
+        }
     }
+    Component.onCompleted: setupFilter()
+    onVisibleChanged: setupFilter()
 
     Connections {
         target: CallOverlayModel
diff --git a/src/app/net/jami/Constants/JamiQmlUtils.qml b/src/app/net/jami/Constants/JamiQmlUtils.qml
index eb272fb93..2051903c6 100644
--- a/src/app/net/jami/Constants/JamiQmlUtils.qml
+++ b/src/app/net/jami/Constants/JamiQmlUtils.qml
@@ -34,6 +34,12 @@ Item {
         return accountCreationInputParaObject;
     }
 
+    // For list models (1 column).
+    function getModelData(model, index, role) {
+        const modelIndex = model.index(index, 0);
+        return model.data(modelIndex, role);
+    }
+
     // MessageBar buttons in mainview points
     property var mainViewRectObj
     property var messageBarButtonsRowObj
-- 
GitLab