Garbage collector issue
-
Hello,
I have an issue similar to this one:
https://forum.qt.io/topic/105749/remove-dynamically-created-item-from-a-stacklayoutI'm using a StackLayout to show a set of pages dynamically. The pages can be added, deleted or selected on runtime by the user, so there is no way to know in advance when these actions will happen. For that reason the most appropriate component to own the pages is a StackLayout (due to the possible random access to any item), and AFAIK I cannot use another one.
Also, each page provides an unique identifier to be recognized in the list.
My issue happens when I need to select another page after one was deleted, because this enters in conflict with the Garbage Collector.
Here is the problematic code:
function onShowSelectedPage(uid) { // if no unique identifier defined, don't show page if (!uid || !uid.length) { slPageStack.currentIndex = -1; return; } let idx = 0; console.log("--------------> 1 - " + slPageStack.children.length); console.log("--------------> 2 - " + slPageStack.count); // FIXME for Jean: Possible unsafe code, check better how the garbage collector is working // iterate through page view stack until find the matching page unique identifier for (var i = 0; i < slPageStack.children.length; ++i) { // is child a page? if (slPageStack.children[i].m_Type === "TSP_PageView") { // is page deleted but still not collected by the garbage collector? if (slPageStack.children[i].m_Deleted) { console.log("--------------> 3 - STILL USED!!!"); continue; } // found page to show? if (slPageStack.children[i].pageProxy.uid === uid) { // use manually counted idx instead of i, because it may be shifted // if a deleting page was still not removed by the garbage collector slPageStack.currentIndex = idx; break; } } ++idx; } console.log("--------------> 4 - " + slPageStack.children.length); console.log("--------------> 5 - " + slPageStack.count); } }
This code is problematic for several reasons:
- I'm aware that this code is over-complicated but I don't know how to simplify it. For example I needed to add a
m_Deleted
flag, which I set to true on delete, to certify that the page I try to access is not currently deleted, but still not cleaned by the Garbage Collector, and should no longer be considered. - Even with the current loop, I'm not sure that the Garbage Collector will not perform the cleanup while my loop is executed, which may cause a very hard to detect bug later.
In the above post I mentioned, the first answer (change to
StackView
and usepush
/pop
) cannot be applied in my case, due to the random access to the items I said above.The second solution seems promising, but I have no idea about how to implement it in my case.
My questions are:
- How may I use a Repeater which take care of completely dynamic objects, managed randomly in a random view during the life cycle of my application?
- Or how may I at least guarantee that the Garbage Collector doesn't interfere with my loop
Also a simple example of using a
StackLayout
with aRepeater
managing dynamic components would be welcome. - I'm aware that this code is over-complicated but I don't know how to simplify it. For example I needed to add a
-
main.qml:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import QtQuick.Layouts 1.15 Window { property int m_PageCount: 0 // common properties id: wnMain width: 640 height: 480 visible: true title: qsTr("Dynamic page creation") Rectangle { // common properties id: rcButtons anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right height: 45 color: "gray" Button { // common properties id: btAdd anchors.left: parent.left anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Add page" onClicked: { lmViewModel.append({color: "lightblue", "pageNb": ++m_PageCount}); slView.currentIndex = slView.count - 1; } } Button { // common properties id: btDel anchors.left: btAdd.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Delete page" onClicked: { if (!slView.count) return; lmViewModel.remove(slView.currentIndex); } } Button { // common properties id: btPrev anchors.left: btDel.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "< Prev" onClicked: slView.prevPage() } Button { // common properties id: btNext anchors.left: btPrev.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Next >" onClicked: slView.nextPage() } } ListModel { // common properties id: lmViewModel } StackLayout { // common properties id: slView anchors.left: parent.left anchors.top: rcButtons.bottom anchors.right: parent.right anchors.bottom: parent.bottom Repeater { property int pageNb // common properties id: rpView model: lmViewModel //delegate: PageItem PageItem { // bind color property contained in Page item to this loader /* Binding { // common properties target: rpView property: "color" value: color } */ // you could have kept name of property as color inside PageItem and then // accessed color here as: color: model.color // I prefer to keep names distinct m_color: color m_Index: index // index provided by Repeater m_PageNb: pageNb /// Called when the component was opened Component.onCompleted: { // index provided by Repeater //index = Qt.binding(function() { return slView.currentIndex; }); // link remove signal between list model and page item remove.connect(lmViewModel.remove); } } /* Loader { // common properties source: "PageItem.qml" // bind color property contained in Page item to this loader Binding { // common properties target: item property: "color" value: color } // bind pageNb property contained in Page item to this loader Binding { // common properties target: item property: "m_PageNb" value: pageNb } // bind index property contained in Page child item to this loader Binding { // common properties target: item property: "m_Index" value: index } /// Called when the component was opened Component.onCompleted: { // link remove signal between list model and page item item.remove.connect(lmViewModel.remove); console.log("Page Nb. - " + pageNb + " - " + item.m_PageNb); } } */ onPageNbChanged: { console.log("Page Nb. Changed - " + pageNb + " - " + item.m_PageNb); } } function prevPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; --index; if (index < 0) index = count - 1; currentIndex = index; } function nextPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; ++index; if (index >= count) index = 0; currentIndex = index; } } Component.onCompleted: { lmViewModel.append({"color": "pink", "pageNb": ++m_PageCount}); } }
PageItem.qml:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Templates 2.15 as T T.Control { // aliases property alias m_color: rcBackground.color // advanced properties property int m_PageNb property int m_Index // common properties id: ctPage signal remove(int index) Rectangle { // common properties id: rcBackground anchors.fill: parent Text { // common properties id: txText anchors.centerIn: parent text: "Page %1 - Index %2".arg(m_PageNb).arg(m_Index) } MouseArea { // common properties anchors.fill: parent onClicked: { remove(m_Index) } } } }
Just to show how it looks using only PageItem as child of Repeater.
I renamed color to m_color. I have a comment about how that could be different. -
Do what @GrecKo said in that other post and use a model:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import QtQuick.Layouts 1.15 Window { width: 640 height: 480 visible: true title: qsTr("Stack Layout") ListModel { id: listmodel Component.onCompleted: { append({ctype: type1, bcolor:"pink"}) append({ctype: type1, bcolor:"lightblue"}) append({ctype: type2, bcolor:"green"}) } } Column { Button { text: "inc layout" onClicked: stacklayout.rotateLayout() } StackLayout { id: stacklayout width: 200 height: 200 function rotateLayout(){ let ind = currentIndex ind++ if(ind >= count){ ind = 0 } currentIndex = ind } Repeater { model: listmodel Loader { sourceComponent: ctype Binding { target: item property: "color" value: bcolor } Binding { target: item property: "index" value: index } Component.onCompleted: { item.remove.connect(listmodel.remove) } } } } } Component { id: type1 Rectangle { property int index signal remove(int ind) Text { id: type1_text anchors.centerIn: parent text: "type1 %1".arg(index) } MouseArea { anchors.fill: parent onClicked: { remove(index) } } } } Component { id: type2 Item { property alias color: type2_text.color property int index signal remove(int ind) Rectangle { anchors.fill: parent color: "lightgrey" border.width: 1 border.color: parent.color } Text { id: type2_text anchors.centerIn: parent text: "type2 %1".arg(index) } MouseArea { anchors.fill: parent onClicked: { remove(index) } } } } }
-
@fcarney Thank you for your answer.
Based on the above fcarney code, I tried to write my own code to load dynamically a child component and to add it on a stack view. But it doesn't work and I cannot figure out how to achieve that correctly.
Below is the code I tried to write, can someone point me what I'm doing wrong, and explain me with further details how to use a
Repeater
/Loader
to load and manage dynamic components in a view?import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import QtQuick.Layouts 1.15 Window { property int m_GenID: 0 width: 640 height: 480 visible: true title: qsTr("Stack Layout") ListModel { id: listmodel Component.onCompleted: { /* append({ctype: type1, bcolor:"pink"}) append({ctype: type1, bcolor:"lightblue"}) append({ctype: type2, bcolor:"green"}) */ } } Column { Button { text: "add layout" onClicked: { } } Button { text: "inc layout" onClicked: stacklayout.rotateLayout() } StackLayout { id: stacklayout width: 200 height: 200 function rotateLayout() { let ind = currentIndex ind++ if (ind >= count) ind = 0 currentIndex = ind } Repeater { model: listmodel Loader { sourceComponent: ctype // bind color property contained in ChildComponent child item to this repeater Binding { target: item property: "color" value: bcolor } // bind index property contained in ChildComponent child item to this repeater Binding { target: item property: "index" value: index } /// Called when the component was opened Component.onCompleted: { // link remove signal between list model and the ChildComponent child item item.remove.connect(listmodel.remove); } } } } } /*REM Component { id: type1 ChildComponent {} } Component { id: type2 ChildComponent {} } */ Component.onCompleted: { let component = Qt.createComponent('ChildComponent.qml'); // succeeded? if (component.status !== Component.Ready) { console.error("Add child - an error occurred while the item was created - " + component.errorString()); return; } // build page identifier const pageId = "ccChild_" + m_GenID; // create and show new item object let item = component.createObject(listmodel.ctype, {"id": pageId, "objectName": pageId, //REM "ctype": pageId, "color": "pink"}); // succeeded? if (!item) { console.error("Add child - an error occurred while the item was added to view"); return; } listmodel.append(item); ++m_GenID } }
Here is the ChildComponent.qml file:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Templates 2.15 as T T.Control { property alias color: rcRect.color property int index id: ctChild signal remove(int ind) Rectangle { id: rcRect anchors.fill: parent Text { id: txText anchors.centerIn: parent text: "type1 %1".arg(index) } MouseArea { anchors.fill: parent onClicked: { remove(index) } } } }
-
@jeanmilost said in Garbage collector issue:
let component = Qt.createComponent('ChildComponent.qml');
// succeeded? if (component.status !== Component.Ready) { console.error("Add child - an error occurred while the item was created - " + component.errorString()); return; } // build page identifier const pageId = "ccChild_" + m_GenID; // create and show new item object let item = component.createObject(listmodel.ctype, {"id": pageId, "objectName": pageId, //REM "ctype": pageId, "color": "pink"}); // succeeded? if (!item) { console.error("Add child - an error occurred while the item was added to view"); return; }
I don't understand why you are doing any of this. The Repeater makes the object via the Loader. Just fill out a JS structure to define what component type gets loaded. The whole point of using a Loader with a Repeater is so you don't have to use the dynamic gen functions.
Also, there is a "better" way to use Loader that maybe I should have pointed out. Use the "source" property and give it the qml file path. Then use Component.onCompleted to select what gets loaded based upon a value fed from the model:
Loader { // this makes it so you don't have to use Binding property var passedIn: ({ name: name, param: param }) Component.onCompleted: { switch(type){ case "type1": setSource("SomeQMLFile.qml", passedIn) break; case "type2: setSource("AnotherQMLFile.qml", passedIn) break; } } }
I just used this latter version of a Loader with a ListView. It works very nicely and cleans up the code.
-
@fcarney Once again, thank you for your answer, it was really useful for me. Indeed my code isn't very optimal, and it was the reason of my above questions. I felt that the
Repeater
/Loader
association was a better concept from what I'm tried to achieve, but I could not understand how to use them in my particular situation. And as I am self-taught, I needed to start from my imperfect concept to try to understand how to do better :-)So I played around your code, and I think I understood how the things work. I have a new code (below) which is close to what I wanted to achieve on the beginning. I have an unique remaining issue: In the below code, the binding around the
pageNb
property is not working. On runtime, this property seems correctly linked between the item and the loader (The value changes in the item if I hadrcode it in the loader), but when I try to execute the following code:
lmViewModel.append({color: "lightblue", pageNb: 5678});
The value doesn't change in the item. Also theonPageNbChanged
signal is never called. Can you please explain me what I'm doing wrong?import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import QtQuick.Layouts 1.15 Window { property int m_GenID: 0 // common properties id: wnMain width: 640 height: 480 visible: true title: qsTr("Dynamic page creation") Rectangle { // common properties id: rcButtons anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right height: 45 color: "gray" Button { // common properties id: btAdd anchors.left: parent.left anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Add page" onClicked: { lmViewModel.append({color: "lightblue", pageNb: 5678}); slView.currentIndex = slView.count - 1; } } Button { // common properties id: btDel anchors.left: btAdd.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Delete page" onClicked: { if (!slView.count) return; lmViewModel.remove(slView.currentIndex); } } Button { // common properties id: btPrev anchors.left: btDel.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "< Prev" onClicked: slView.prevPage() } Button { // common properties id: btNext anchors.left: btPrev.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Next >" onClicked: slView.nextPage() } } ListModel { // common properties id: lmViewModel } StackLayout { // common properties id: slView anchors.left: parent.left anchors.top: rcButtons.bottom anchors.right: parent.right anchors.bottom: parent.bottom Repeater { // common properties id: rpView model: lmViewModel Loader { property int pageNb // common properties source: "PageItem.qml" // bind color property contained in Page item to this loader Binding { // common properties target: item property: "color" value: color } // bind pageNb property contained in Page item to this loader Binding { // common properties target: item property: "pageNb" value: pageNb } // bind index property contained in Page child item to this loader Binding { // common properties target: item property: "index" value: index } onPageNbChanged: { console.log("Page Nb. Changed - " + pageNb + " - " + item.pageNb); } /// Called when the component was opened Component.onCompleted: { // link remove signal between list model and page item item.remove.connect(lmViewModel.remove); console.log("Page Nb. - " + pageNb + " - " + item.pageNb); } } } function prevPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; --index; if (index < 0) index = count - 1; currentIndex = index; } function nextPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; ++index; if (index >= count) index = 0; currentIndex = index; } } Component.onCompleted: { lmViewModel.append({color: "pink"}); } }
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Templates 2.15 as T T.Control { property alias color: rcBackground.color property int pageNb property int index // common properties id: ctPage signal remove(int index) Rectangle { // common properties id: rcBackground anchors.fill: parent Text { // common properties id: txText anchors.centerIn: parent text: "Page %1 - Index %2".arg(pageNb).arg(index) } MouseArea { // common properties anchors.fill: parent onClicked: { remove(index) } } } }
-
@jeanmilost said in Garbage collector issue:
Component.onCompleted:
{
lmViewModel.append({color: "pink"});
}This must have pageNb defined if you add later entries to your model.
There is a way to turn that requirement off in the ListModel, but I don't recommend that.
Aren't you getting an error when you click the add button? It should be showing an error about having pageNb when you didn't have that in the first entry added during onComplete.Make sure everything that you add to the model has the proper entries.
One way to do that is write a function that you call for this:ListModel { id: lmodel function addNewThing(color, ind){ lmodel.append({color:color, ind:ind}) } } This ensures everything has parity.
Loader { property int pageNb
Don't define pageNb here. It should be coming from your model. This probably occluding what gets sent from the model. I am not 100% sure though. Maybe test that a bit. I know there is syntax to ensure you get something from a model that involves properties using the term "required". But I don't see you using that. Again, play with this to see if it occludes it or not.
Edit: If you get stuck just say so. I will look later this weekend when I get a chance.
-
@fcarney This solution (below) seems to work for me. I just moved the
pageNb
property to theRepeater
level and I encapsulated the property name in quotes, and voila!On the other hand, I also tried to replace the
Loader
byPageItem
directly, as I have indeed only one type of child item, but without success. You can take a look to the commented code (but it is experimental).Anyway, thank you very much for your help.
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import QtQuick.Layouts 1.15 Window { property int m_PageCount: 0 // common properties id: wnMain width: 640 height: 480 visible: true title: qsTr("Dynamic page creation") Rectangle { // common properties id: rcButtons anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right height: 45 color: "gray" Button { // common properties id: btAdd anchors.left: parent.left anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Add page" onClicked: { lmViewModel.append({color: "lightblue", "pageNb": ++m_PageCount}); slView.currentIndex = slView.count - 1; } } Button { // common properties id: btDel anchors.left: btAdd.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Delete page" onClicked: { if (!slView.count) return; lmViewModel.remove(slView.currentIndex); } } Button { // common properties id: btPrev anchors.left: btDel.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "< Prev" onClicked: slView.prevPage() } Button { // common properties id: btNext anchors.left: btPrev.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Next >" onClicked: slView.nextPage() } } ListModel { // common properties id: lmViewModel } StackLayout { // common properties id: slView anchors.left: parent.left anchors.top: rcButtons.bottom anchors.right: parent.right anchors.bottom: parent.bottom Repeater { property int pageNb // common properties id: rpView model: lmViewModel /*REM //delegate: PageItem PageItem { // bind color property contained in Page item to this loader Binding { // common properties target: rpView property: "color" value: color } /// Called when the component was opened Component.onCompleted: { index = Qt.binding(function() { return slView.currentIndex; }); // link remove signal between list model and page item remove.connect(lmViewModel.remove); } } */ Loader { // common properties source: "PageItem.qml" // bind color property contained in Page item to this loader Binding { // common properties target: item property: "color" value: color } // bind pageNb property contained in Page item to this loader Binding { // common properties target: item property: "m_PageNb" value: pageNb } // bind index property contained in Page child item to this loader Binding { // common properties target: item property: "m_Index" value: index } /// Called when the component was opened Component.onCompleted: { // link remove signal between list model and page item item.remove.connect(lmViewModel.remove); console.log("Page Nb. - " + pageNb + " - " + item.m_PageNb); } } onPageNbChanged: { console.log("Page Nb. Changed - " + pageNb + " - " + item.m_PageNb); } } function prevPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; --index; if (index < 0) index = count - 1; currentIndex = index; } function nextPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; ++index; if (index >= count) index = 0; currentIndex = index; } } Component.onCompleted: { lmViewModel.append({"color": "pink", "pageNb": ++m_PageCount}); } }
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Templates 2.15 as T T.Control { // aliases property alias color: rcBackground.color // advanced properties property int m_PageNb property int m_Index // common properties id: ctPage signal remove(int index) Rectangle { // common properties id: rcBackground anchors.fill: parent Text { // common properties id: txText anchors.centerIn: parent text: "Page %1 - Index %2".arg(m_PageNb).arg(m_Index) } MouseArea { // common properties anchors.fill: parent onClicked: { remove(m_Index) } } } }
-
main.qml:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Window 2.15 import QtQuick.Layouts 1.15 Window { property int m_PageCount: 0 // common properties id: wnMain width: 640 height: 480 visible: true title: qsTr("Dynamic page creation") Rectangle { // common properties id: rcButtons anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right height: 45 color: "gray" Button { // common properties id: btAdd anchors.left: parent.left anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Add page" onClicked: { lmViewModel.append({color: "lightblue", "pageNb": ++m_PageCount}); slView.currentIndex = slView.count - 1; } } Button { // common properties id: btDel anchors.left: btAdd.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Delete page" onClicked: { if (!slView.count) return; lmViewModel.remove(slView.currentIndex); } } Button { // common properties id: btPrev anchors.left: btDel.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "< Prev" onClicked: slView.prevPage() } Button { // common properties id: btNext anchors.left: btPrev.right anchors.leftMargin: 5 anchors.top: parent.top anchors.topMargin: 5 anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: "Next >" onClicked: slView.nextPage() } } ListModel { // common properties id: lmViewModel } StackLayout { // common properties id: slView anchors.left: parent.left anchors.top: rcButtons.bottom anchors.right: parent.right anchors.bottom: parent.bottom Repeater { property int pageNb // common properties id: rpView model: lmViewModel //delegate: PageItem PageItem { // bind color property contained in Page item to this loader /* Binding { // common properties target: rpView property: "color" value: color } */ // you could have kept name of property as color inside PageItem and then // accessed color here as: color: model.color // I prefer to keep names distinct m_color: color m_Index: index // index provided by Repeater m_PageNb: pageNb /// Called when the component was opened Component.onCompleted: { // index provided by Repeater //index = Qt.binding(function() { return slView.currentIndex; }); // link remove signal between list model and page item remove.connect(lmViewModel.remove); } } /* Loader { // common properties source: "PageItem.qml" // bind color property contained in Page item to this loader Binding { // common properties target: item property: "color" value: color } // bind pageNb property contained in Page item to this loader Binding { // common properties target: item property: "m_PageNb" value: pageNb } // bind index property contained in Page child item to this loader Binding { // common properties target: item property: "m_Index" value: index } /// Called when the component was opened Component.onCompleted: { // link remove signal between list model and page item item.remove.connect(lmViewModel.remove); console.log("Page Nb. - " + pageNb + " - " + item.m_PageNb); } } */ onPageNbChanged: { console.log("Page Nb. Changed - " + pageNb + " - " + item.m_PageNb); } } function prevPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; --index; if (index < 0) index = count - 1; currentIndex = index; } function nextPage() { if (!count) { currentIndex = -1; return; } let index = currentIndex; ++index; if (index >= count) index = 0; currentIndex = index; } } Component.onCompleted: { lmViewModel.append({"color": "pink", "pageNb": ++m_PageCount}); } }
PageItem.qml:
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Templates 2.15 as T T.Control { // aliases property alias m_color: rcBackground.color // advanced properties property int m_PageNb property int m_Index // common properties id: ctPage signal remove(int index) Rectangle { // common properties id: rcBackground anchors.fill: parent Text { // common properties id: txText anchors.centerIn: parent text: "Page %1 - Index %2".arg(m_PageNb).arg(m_Index) } MouseArea { // common properties anchors.fill: parent onClicked: { remove(m_Index) } } } }
Just to show how it looks using only PageItem as child of Repeater.
I renamed color to m_color. I have a comment about how that could be different. -
@fcarney Wow excellent, this work very well. Thank you very much for your help