Expandable ListView delegate and other questions
-
Hello all
This is my first question in this subforum/category and I'm afraid it won't be last since I'm just starting with QML/Quick.
I've written simple example with which I'm having problems (explanations below):
import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.0 ApplicationWindow { id: app visible: true width: 340 height: 480 title: qsTr("Simple test") Component { id: delComp Rectangle { width: view.width height: 80 border.color: "red" border.width: 1 Row { id: row anchors { top: parent.top horizontalCenter: parent.horizontalCenter } Rectangle { width: 100 height: 40 color: "lightgray" Text { anchors.centerIn: parent text: value } } CheckBox { text: "Active" checked: active onCheckedChanged: { active = checked } } Text { text: "\u2630" font.pixelSize: 20 anchors.verticalCenter: parent.verticalCenter MouseArea { anchors.fill: parent onClicked: { console.log("Changing visible:" + details.visible) details.visible = !details.visible // console.log("Opening popup") // schedDialog.row = index // schedDialog.auto = auto // schedDialog.open() } } } } Rectangle { id: details visible: false anchors { top: row.bottom bottom: parent.bottom horizontalCenter: parent.horizontalCenter } height: 80 Text { text: "Details" } } Connections { target: schedDialog onAccepted: { if (index === schedDialog.row) { console.log("Delegate accept") auto = schedDialog.auto } } } } } Dialog { id: schedDialog x: (parent.width - implicitWidth)/2 y: (parent.height - implicitHeight)/2 standardButtons: Dialog.Cancel | Dialog.Ok modal: true property int row: 0 property alias auto: chkBox.checked // property int start: 0 // property int stop: 0 ColumnLayout { CheckBox { id: chkBox text: "Enable schedule" } } // onAccepted: { // // XXX - how can I update model here??? // } } ListView { id: view anchors.fill: parent model: simpleModel delegate: delComp } ListModel { id: simpleModel ListElement { value: 1023 active: true auto: false start: 0 // TODO: What is QML equivalent of QTime stop: 0 } ListElement { value: 0 active: false auto: true start: 10 stop: 20 } ListElement { value: 5421 active: true auto: true start: 1000 stop: 2020 } onDataChanged: { console.log("Data changed " + topLeft) } } }
So this is a simple list view with some items and I want to show each item with simplified interface (not showing all details). This is quite simple and I've added "heaven" button which originally (now commented) was opening popup dialog with the remaining details for given item.
First question(s):
- if I have Dialog as part of delegate then there is no problem with access/modification of model's data but how can I position dialog popup from delegate relative to the app window (and not relative to the delegate) - setting y to
(app.height - implicitHeight)/2
seems to position dialog correct distance but not from app.top but from the delegate.top - if I get this then below questions are only for my curiosity - if the dialog is outside of the delegate how can I access given model item data, any attached properties, modelData? Right now I copy values to Dialog.
- if the dialog is outside of the delegate what would be the best method to update model - right now I have all delegates connected to the Dialog's
accepted
signal and they checkrow
in order to see if they need to update model with Dialog's values or not
Since I had problems with this dialog I thought about having different design and now I want to create list view in which delegate will be expanding a bit and showing hidden content. This is not really similar to "expandingdelegates" from the Qt Quick 'Views' example coming with Qt. In there the delegate expands to the whole screen and I want to have delegate expand a bit showing more controls and pushing other delegates down.
I thought about adding second Rectangle (details
) and changing its visibility but that does not work. The "Details" text visibility is changing as needed but the delegate is not sizing according to its visibility.How can I achieve that effect?
For example setting height of the top level Rectangle in delComp to:
details.visible ? row.height + details.height : row.height
does not do anything. Is it possible to have list view with each delegate having different height? If so how can I achieve that?My apologies for a lengthy post, and thank you for reading it.
Best regards
Andrzej - if I have Dialog as part of delegate then there is no problem with access/modification of model's data but how can I position dialog popup from delegate relative to the app window (and not relative to the delegate) - setting y to
-
I've just found out that if instead of specifying height of the top level Rectangle in delComp to:
height: details.visible ? row.height + details.height : row.height
I do it as:
height: row.height + (details.visible ? details.height : 0)
then the delegate is expanding as I wanted. Could somebody explain the difference?
Best regards
Andrzej -
@AndrzejO said in Expandable ListView delegate and other questions:
- if I have Dialog as part of delegate then there is no problem with access/modification of model's data but how can I position dialog popup from delegate relative to the app window (and not relative to the delegate) - setting y to
(app.height - implicitHeight)/2
seems to position dialog correct distance but not from app.top but from the delegate.top
You must translate the coordinates. See Item's mapTo... and mapFrom... methods. They are difficult to use so I don't even try to give any code. Having one dialog per delegate object is of course inefficient, one dialog is better.
You don't access a delegate's properties from outside that delegate object. Instead you communicate through the model or set things from the delegate. Here it's done through the model, modifying your code:
MouseArea { anchors.fill: parent onClicked: { schedDialog.row = index schedDialog.open() } }
Dialog { id: schedDialog x: (parent.width - implicitWidth)/2 y: (parent.height - implicitHeight)/2 standardButtons: Dialog.Close modal: true property int row: 0 ColumnLayout{ CheckBox { id: chkB text: "Enable schedule" } } onOpened: chkB.checked = simpleModel.get(row).auto onClosed: simpleModel.get(row).auto = chkB.checked // or use onCheckedChanged of chkB }
Basically you use whatever means are available for manipulating the model data. ListModel has this get() method which lets you handle one item as a javascript object. C++ models have their own methods.
I've just found out that if instead of specifying height of the top level Rectangle in delComp to:
height: details.visible ? row.height + details.height : row.height
I do it as:
height: row.height + (details.visible ? details.height : 0)
then the delegate is expanding as I wanted. Could somebody explain the difference?At least I can't. Very interesting.
- if I have Dialog as part of delegate then there is no problem with access/modification of model's data but how can I position dialog popup from delegate relative to the app window (and not relative to the delegate) - setting y to
-
Thanks @Eeli-K for taking a look at it.
Would youronClosed
work the same if the model was on the C++ side? I mean if the model is defined in C++ and e.g. set as a context property could you callmodel.get(row).auto = chkB.checked
on it, or is it just the interface ofListModel
QML type. If the latter how would you define function withQ_INVOKED
that would provide such interface?Best regards
Andrzej -
@AndrzejO As I said, "C++ models have their own methods". In the documentation of QAbstractItemModel you find insert..., move..., remove... and set... methods or you can implement your own custom interface as long as you send the corresponding signals which tell the listeners when the model is changed. For example you have Q_INVOKABLE changeSomeDataValueOnEveryRow(int argumentForCalculatingNewValues). You change all wanted data values there and choose the signal to send - dataChanged for each row, or maybe even modelAboutToBeReset and modelReset. Learn how to subclass QAbstractItemModel.