From 100756b02d5a697c8faba4ecaa5c6f0db80e2627 Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Tue, 26 Sep 2023 15:06:29 -0400
Subject: [PATCH] viewmanager: allow creating more than 1 instance of a view

Previously, managed views were always stored in a dictionary using non-unique keys. This commit adds a method to the ViewManager component allowing for automatic unique key generation if the base key (QML component path) is the same. This is used for dialogs because we need to possibly open nested modal dialogs.

Gitlab: #1367
Change-Id: I94e0ef0e0ae79689445be0409dd902dc74554e43
---
 src/app/ViewCoordinator.qml |  2 +-
 src/app/ViewManager.qml     | 56 +++++++++++++++++++++++++++++--------
 2 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/src/app/ViewCoordinator.qml b/src/app/ViewCoordinator.qml
index 6939b539f..6a14c49c8 100644
--- a/src/app/ViewCoordinator.qml
+++ b/src/app/ViewCoordinator.qml
@@ -74,7 +74,7 @@ QtObject {
     // Create, present, and return a dialog object.
     function presentDialog(parent, path, props = {}) {
         // Open the dialog once the object is created
-        return viewManager.createView(path, parent, function (obj) {
+        return viewManager.createUniqueView(path, parent, function (obj) {
                 const doneCb = function () {
                     viewManager.destroyView(path);
                 };
diff --git a/src/app/ViewManager.qml b/src/app/ViewManager.qml
index 24cf22973..5a66358aa 100644
--- a/src/app/ViewManager.qml
+++ b/src/app/ViewManager.qml
@@ -36,36 +36,70 @@ QtObject {
         }
     }
 
+    // Create a view from a path only if it doesn't already exist. This is used
+    // by the view coordinator to create views that are not self-destructing
+    // (main views) and only exist once per instance of the app.
     function createView(path, parent = null, cb = null, props = {}) {
-        if (views.hasOwnProperty(path)) {
-            // an instance of <path> already exists
+        const component = Qt.createComponent(Qt.resolvedUrl(path));
+        return createViewFromComponent(component, path, parent, cb, props);
+    }
+
+    // Create a new view. Useful when we want to create multiple views that are
+    // self-destructing (dialogs).
+    function createUniqueView(path, parent = null, cb = null, props = {}) {
+        const component = Qt.createComponent(Qt.resolvedUrl(path));
+        return createViewFromComponent(component, getViewName(path), parent, cb,
+                                       props);
+    }
+
+    // Create a new view from a component. If a view with the same path already
+    // exists, it is returned instead.
+    function createViewFromComponent(component, viewName, parent = null,
+                                     cb = null, props = {}) {
+        if (views.hasOwnProperty(viewName)) {
+            // an instance of the view already exists
             if (cb !== null) {
-                cb(views[path])
+                cb(views[viewName])
             }
-            return views[path]
+            return views[viewName]
         }
-        const component = Qt.createComponent(Qt.resolvedUrl(path))
         if (component.status === Component.Ready) {
             const obj = component.createObject(parent, props)
             if (obj === null) {
-                print("error creating object")
+                console.error("error creating object")
                 return null
             }
-            views[path] = obj
+            views[viewName] = obj
             // Set the view name to the object name if it has one.
-            const viewName = obj.objectName.toString() !== '' ? obj.objectName : path.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "")
-            viewPaths[viewName] = path
+            const friendlyName = obj.objectName.toString() !== '' ?
+                obj.objectName :
+                viewName.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "")
+            viewPaths[friendlyName] = viewName
             if (cb !== null) {
                 cb(obj)
             }
-            return views[path]
+            return views[viewName]
         }
-        print("error creating component", path)
+        console.error("error creating component", component.url)
         console.error(component.errorString())
         Qt.exit(1)
         return null
     }
 
+    // Finds a unique view name for a given path by appending a number to the
+    // base name. For example, if a view named "MyView" already exists, the next
+    // view will be named "MyView_1".
+    function getViewName(path) {
+        const baseName = path.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, "")
+        let viewName = baseName
+        let suffix = 1
+        while (views.hasOwnProperty(viewName)) {
+            viewName = `${baseName}_${suffix}`
+            suffix++
+        }
+        return viewName
+    }
+
     function destroyView(path) {
         // The view may already have been destroyed.
         if (!views.hasOwnProperty(path)) {
-- 
GitLab