[Interest] Awkwardness of delegates in heterogenous ListViews
Elvis Stansvik
elvstone at gmail.com
Thu Jun 9 10:34:51 CEST 2016
2016-06-09 10:22 GMT+02:00 Elvis Stansvik <elvstone at gmail.com>:
> Okay, this thread is getting long, but I've now found a solution I
> think I'm happy with, I thought I'd share it:
>
> First, my ColorDelegate, a delegate for items of type "color":
>
> ColorDelegate.qml:
>
> Row {
> readonly property alias color: colorDialog.color
>
> ColorButton {
> color: value
> onClicked: {
> colorDialog.open()
> colorDialog.currentColor = color
> }
> }
>
> Text {
> text: name
> color: Style.foregroundColor
> anchors.verticalCenter: parent.verticalCenter
> }
>
> ColorDialog {
> id: colorDialog
> title: name
> showAlphaChannel: true
> }
> }
>
> I'm here aliasing the color property of the ColorDialog I use to let
> the user choose the color. Note that this delegate has no references
> to ListView attached properties of its parent (or itself), so it makes
> no assumptions about how it is used (nested in a delegate or as a
> delegate itself), so it can be used in both scenarios (e.g. when I
> have a heterogenous list model, or a model where I know all items will
> be of type "color").
>
> Then my "super"-delegate ItemDelegate:
>
> ItemDelegate.qml:
>
> Item {
> readonly property alias color: colorDelegate.color
>
> height: 2 * TextSingleton.implicitHeight
> width: parent.width
>
> ColorDelegate {
> id: colorDelegate
> visible: type === "color"
> }
>
> Text {
> visible: type === "flag"
> text: "TODO: delegate for flag items"
> }
>
> Text {
> visible: type === "choice"
> text: "TODO: delegate for choice items"
> }
> }
>
> Here I'm using Jason's suggestion of simply making the constituent
> delegates visible/invisible based on the type of the item, and again
> I'm aliasing the color property upwards, so the "super"-delegate has a
> color property.
>
> Then, in my actual ListView I have:
>
> ListView {
> model: themeModel
> anchors.fill: parent
>
> section.property: "section"
> section.delegate: SectionDelegate { }
>
> delegate: ItemDelegate {
> onColorChanged: themeModel.setColor(index, color)
> }
> }
>
> Here, I know the model being used (themeModel), and handle changes in
> color (which equates to changes in color of the ColorDelegate further
> down) by calling my model's setColor slot.
>
> I'll now do it similarly for other types of items. I think this can
> work for me, since my item types are quite simple and "mutually
> exclusive" so to speak, so I won't have conflicting properties to
> alias.
>
> Thanks for all the input!
And as soon as I said that, I realize that this solution is bust: I
can't use the visible/invisible trick since the various delegates will
then make illegal uses of the items. I must use the Loader approach.
But if I use the loader approach, then my solution with propagating
the properties upwards with property aliases won't work, since the
Loader can't alias a property of the delegates since they're now
wrapped in Component.
Oh well, I think i'll just give up on having these delegates drop-in
usable in both scenarios.
Elvis
>
> Elvis
>
> 2016-06-09 9:38 GMT+02:00 Elvis Stansvik <elvstone at gmail.com>:
>> 2016-06-09 8:20 GMT+02:00 Elvis Stansvik <elvstone at gmail.com>:
>>> 2016-06-09 7:54 GMT+02:00 Elvis Stansvik <elvstone at gmail.com>:
>>>> 2016-06-08 23:59 GMT+02:00 Jason H <jhihn at gmx.com>:
>>>>> I'm coming into this late, but can't you just set up:
>>>>>
>>>>> Item { // delegate
>>>>>
>>>>> Item {
>>>>> id: view1
>>>>> visible: some_data_item_property == true
>>>>> }
>>>>> Item {
>>>>> id: view2
>>>>> visible: !view1.visible
>>>>> }
>>>>> }
>>>>>
>>>>> Then make view1 and view2 your different delegates? You're just going after
>>>>> two diferent views, right?
>>>>
>>>> Well it's ~5, but indeed that seems to work great! A little test:
>>>>
>>>>
>>>> import QtQuick 2.4
>>>> import QtQuick.Window 2.2
>>>>
>>>> import "."
>>>>
>>>> Window {
>>>> title: "Test"
>>>> width: 600
>>>> height: 900
>>>> visible: true
>>>>
>>>> ListModel {
>>>> id: listModel
>>>> dynamicRoles: true
>>>> Component.onCompleted: {
>>>> append({
>>>> "type": "color",
>>>> "name": "foo",
>>>> "value": "red"
>>>> });
>>>> append({
>>>> "type": "flag",
>>>> "name": "bar",
>>>> "value": false
>>>> });
>>>> append({
>>>> "type": "choice",
>>>> "name": "bar",
>>>> "value": "Foo",
>>>> "choices": ["Foo", "Bar"]
>>>> });
>>>> }
>>>> }
>>>>
>>>> ListView {
>>>> anchors.fill: parent
>>>>
>>>> model: listModel
>>>> delegate: Rectangle {
>>>> implicitHeight: {
>>>> if (type === "color")
>>>> return colorDelegate.implicitHeight
>>>> if (type === "flag")
>>>> return flagDelegate.implicitHeight
>>>> if (type === "choice")
>>>> return choiceDelegate.implicitHeight
>>>> }
>>>> Text {
>>>> id: colorDelegate
>>>> text: value + " (color)"
>>>> visible: type === "color"
>>>> }
>>>> Text {
>>>> id: flagDelegate
>>>> text: value + " (flag)"
>>>> visible: type === "flag"
>>>> }
>>>> Text {
>>>> id: choiceDelegate
>>>> text: value + " (choice)"
>>>> visible: type === "choice"
>>>> }
>>>> }
>>>> }
>>>> }
>>>>
>>>
>>> It does not really solve the "problem" though. Say one of my delegates
>>> is a ColorDelegate for when the item represents a color, and I want to
>>> provide editing (my model has a setColor(index, color)).
>>>
>>> I will then have to use parent.ListView.view.model.setColor(index,
>>> color), thereby tying the color delegate to this way of working: It
>>
>> It seems there is actually something called just "model" available in
>> the delegate and its children, but it's of type
>> QQmlDMAbstractItemModelData, and I get a TypeError if trying to treat
>> it as my custom model (e.g. calling my setColor(index, color) slot).
>>
>> Elvis
>>
>>> can't be reused in another context where I have a model known to
>>> consist of only colors (well not without artificially nesting it
>>> inside something else, like an invisible Rectangle, so that the
>>> reference to setColor works).
>>>
>>> On the C++ side, it's a bit easier to make delegates that are reusable
>>> in these two situations, since you work with createEditor and friends
>>> instead.
>>>
>>> Elvis
>>>
>>>>
>>>> I wonder why I haven't seen this approach before while searching around.
>>>>
>>>> Elvis
>>>>
>>>>>
>>>>> Sent: Wednesday, June 08, 2016 at 11:49 AM
>>>>> From: "Elvis Stansvik" <elvstone at gmail.com>
>>>>> To: "Kristoffersen, Even (NO14)" <Even.Kristoffersen at honeywell.com>,
>>>>> "interest at qt-project.org Interest" <interest at qt-project.org>
>>>>>
>>>>> Subject: Re: [Interest] Awkwardness of delegates in heterogenous ListViews
>>>>>
>>>>> 2016-06-08 15:41 GMT+02:00 Kristoffersen, Even (NO14)
>>>>> <Even.Kristoffersen at honeywell.com>:
>>>>>> Have you tried something like this in the delegate?
>>>>>>
>>>>>> property alias some_descriptive_name: parent
>>>>>
>>>>> It seems it's not allowed to use parent as the target of an alias (?):
>>>>>
>>>>> file:///home/estan/qte/qte/qml/ItemDelegate.qml:5 Invalid alias reference.
>>>>> Unable to find id "parent"
>>>>>
>>>>> And this would still tie my "subdelegates" to this structure. They could not
>>>>> be reused in other places, where lists are homogenous and I'm not using the
>>>>> Loader approach.
>>>>>
>>>>> But I guess there's really no way to make a delegate that can be used in
>>>>> both the "normal" situation and in the indirect-through-Loader approach.
>>>>>
>>>>> Elvis
>>>>>
>>>>>>
>>>>>> (Not tested, just an idea)
>>>>>>
>>>>>> -Even
>>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Interest
>>>>>> [mailto:interest-bounces+even.kristoffersen=honeywell.com at qt-project.org] On
>>>>>> Behalf Of Elvis Stansvik
>>>>>> Sent: 8. juni 2016 15:34
>>>>>> To: interest at qt-project.org Interest <interest at qt-project.org>
>>>>>> Subject: Re: [Interest] Awkwardness of delegates in heterogenous ListViews
>>>>>>
>>>>>> 2016-06-08 15:32 GMT+02:00 Elvis Stansvik <elvstone at gmail.com>:
>>>>>>> Hi all,
>>>>>>>
>>>>>>> I'm currently using a "type" role in my items and then a Loader as
>>>>>>> delegate, to give a level of indirection and let me choose the actual
>>>>>>> delegate based on the "type" of the item.
>>>>>>>
>>>>>>> This explains it better (from my ListView):
>>>>>>>
>>>>>>> delegate: Loader {
>>>>>>> sourceComponent: {
>>>>>>> switch (type) {
>>>>>>> case "color":
>>>>>>> return colorDelegate
>>>>>>> case "flag":
>>>>>>> return flagDelegate
>>>>>>> ...
>>>>>>> }
>>>>>>> }
>>>>>>>
>>>>>>> Component {
>>>>>>> id: colorDelegate
>>>>>>>
>>>>>>> ColorDelegate { }
>>>>>>> }
>>>>>>>
>>>>>>> Component {
>>>>>>> id: flagDelegate
>>>>>>>
>>>>>>> FlagDelegate { }
>>>>>>> }
>>>>>>>
>>>>>>> ...
>>>>>>> }
>>>>>>>
>>>>>>> What I don't like with this approach is that inside my delegates, e.g.
>>>>>>> ColorDelegate.qml, I have to use parent.ListView.view.model to get a
>>>>>>
>>>>>> I should clarify: I have to use this notation since it is the parent (the
>>>>>> Loader) which is the real delegate and has the ListView attached properties.
>>>>>>
>>>>>> Elvis
>>>>>>
>>>>>>> reference to the model (needed for editing operations). The use of
>>>>>>> "parent" here assumes a certain layout, but I see no other way :(
>>>>>>>
>>>>>>> How have others solved the issue of delegates for heterogenous lists?
>>>>>>>
>>>>>>> Best regards,
>>>>>>> Elvis
>>>>>> _______________________________________________
>>>>>> Interest mailing list
>>>>>> Interest at qt-project.org
>>>>>> http://lists.qt-project.org/mailman/listinfo/interest
>>>>>
>>>>> _______________________________________________ Interest mailing list
>>>>> Interest at qt-project.org
>>>>> http://lists.qt-project.org/mailman/listinfo/interest
More information about the Interest
mailing list