Welcome back to Programming with Qt
and QML. I'm Jesper Pedersen from KDAB. I'm gonna show you a small trick in this
video. It's not really all about ListView. It's not really all about...well, it's
really just a small trick. One day you might need exactly this, and then
remember you saw it here first. We saw in the previous video that we had in the
ListView the concept of sections and now the question that might arise to you: Can
I collapse those sections, and if I do, well...how would I do that? That's what
we're gonna see here. So, if you have a look at the code, or look at the user
interface, you can see clicking here on the items or the sections will collapse
and expand these sections. So how did we go about doing that? Let's
investigate the code. First off, I got a ListView.
I got a property var collapsed here. We'll get back to what that does in a
second. I set up width and a height. I said focus: true, flip: true, all those
things that we do every time we have a list view. The model - remember list
views are associated with models - so the model here is an instance of name model...f2 to jump to that one. It's just a list model, just like we've seen before. Name and
team for specifying the items in here. Then we got a delegate. Remember the
delegate is what is painting each of the items in our list view. So let's see what
that one...again this time I've taken my delegate and put it into a separate
class or separate element called name delegate. It's not very complex. It's a
text element. Clipping is turned on. The text is model.name. So, that's a name
for the given element. The font size is 24 pixels. I got a new property here
called expanded and I'll use that property for the height of the element.
That's where the core of this whole collapsing ends, showing you again it comes from mainly that the height of the element depends on expanded. If it's not
expanded, the height would be zero pixels, meaning your element is going to be
rather small. And observe clip: true - had I not had clip true I would still have seen the text despite it being zero pixels high, so that's really required
here. And the implicit height of my element is the other alternative. So when
it's expanded, it should be what the text element itself thinks is a good size,
dependent on the pixel size up here. I also have this behavior on height, and
the behavior on height is what makes it slowly collapse. Well, 200 milliseconds is
what it takes for it to collapse. Without that it would just go [mimics two quick collapsing sounds]...showing up and
collapsing. So that's an example of this behavior that we've seen in a previous
video. So that is a nameDelegate element itself, but it's also customized
from the usage of it. I anchor it...there's nothing new
there. The expanded - that was that property that it had that we just
discussed - that one comes from the ListView itself. Its section expanded and
if I scroll down here you can see I got four functions for configuring all of
this. So we'll see those after we've discussed the other things. So its
section expanded with model.team. So I go to the model for this particular
element that I'm creating here. I ask is that particular team expanded,
and if it is well then my property expander will be true. Otherwise it will be
false. Just a small observation in here,
my read-only property list view here, we saw that in a previous video. That is the
list view that this delegate is connected to, but I could just as well have
referred to list view and I actually do refer to it inconsistently
throughout this code here. Just so that you will not get too confused about
that. My mouse area that is sitting inside of each of the delegates, whenever
I click on it it will toggle the current index. It has nothing
with the expansion or not. I got a highlight...similar, has nothing to do with
what we're talking about here. That's just the set up of this element in
general. Now we've got a section here, that is the section for each of the
elements. Remember, I got my each of the elements set up with a delegate and then
the text at the top of each section is set up with the element that
is specified in my section property. So my section property
here says that it's...just... so the section...yeah...I asked...see that's interesting I was looking for section: here, but it's of
course a combined properties of section dot property team section dot criteria view
section full string. And section dot delegate, that's the
delegate of the section. Section delegate is again an element that is created
here on its own. It's a rectangle. It has a color, it has an implicit height here. It
got the text which is just an alias down to the text element down here. And it has
a mouse area building. Whenever I click on the item, it says parent.clicked. That's the signal that I have up there. So the signal clicked will be
emitted from the section delegate. I set up the text of my section delegate using
the section that comes from my element and unclicked. So whenever I click on
that section delegate it will emit its unclicked signal, which will then go to the
view. So that's where we are. Inconsistent with the previous, we have the
underscore underscore lv, but here I just go directly to the view. View.toggleSection, with this name. So that's each of the building blocks. Now we just need to
see the JavaScript that connects all this together.
Up here at the top, I have a property var collapsed that is an object map. The
syntax here is slightly weird. I'll put a link in for you to read up more on that
particular syntax. But it's a JavaScript object and so it's basically just what you
from Qt think of a Qmap or a Qhash. And this section
expanded. All it does is that it takes this section and looks.
Does my object actually have that key? And if it does, that means that it's collapsed. And if it doesn't, it means
that it's expanded. So my...is section expanded...I use that on my delegate here...
expanded...this section expanded for that particular element or that particular
section. Then whenever I clicked on a section here, we executed the toggle section, and what
toggle section does is test whether the section is expanded. If it is then
hide the section, otherwise show the section. And hide the section show the section simply take...the show the section takes the element and removes it from the collapsed map and the height one
puts it in. So it's a double negation that can be difficult to explain. And
then the last little thing here is that I emit. There's no emit keyword on
the QML side. But this is emitting that signal called collapsedChanged. Remember, I got a property up here that's called collapsed. Whenever I
have a property, there's a changed signal for it. And this is the first time. This is the very first time in QML that you've seen that you actually need to emit signals like that yourself. Of course, if I created a signal myself I would
need to emit it. But the changed signal - usually whenever you assign to a
property the changed signal will be emitted on its own. So why do we need to do it here? Well, the key thing is that what we have here is a map and it's one of the
items inside the map that is changing. It's not the whole map, but that's what
we want. We want the changed signal for that whole map to emit it. That's why we need to run this signal ourselves - to emit the collapsed changed signal ourselves. And once we do that, everything fits together. I am toggling.
I'm double clicking on an item. The item goes down to either...well it goes down to
toggle selection, which will either hide or show, which will either remove it or put
it into the collapsed map based on that map, or after that my collapsed
changed signal will be emitted. When the collapse changed signal is emitted all
of my expanded here will be re-evaluated because they are is section
expanded, which is a JavaScript function that is depending on that particular collapsed. So again, our property binding is a beautiful example of how the property binding implicitly works in our
favor. I just say it I wanted to be expanded if this item is in this map. And
then whenever I added that, then my property binding will be re-evaluated
because the changed signal was emitted. And then the element or the property binding is
re-evaluated, which means that the element for setting that up will now change its
height and because of my behavior element in there it will semi-slowly collapse or expand - truly beautiful, if you ask me. Let's end on
the high note.