[Interest] Awkwardness of delegates in heterogenous ListViews

Elvis Stansvik elvstone at gmail.com
Thu Jun 9 10:22:50 CEST 2016


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!

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