Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QML Expandable Menu with SubItems
Forum Updated to NodeBB v4.3 + New Features

QML Expandable Menu with SubItems

Scheduled Pinned Locked Moved Solved QML and Qt Quick
13 Posts 3 Posters 10.6k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • QMLNewbieQ QMLNewbie

    I'm pretty new to QML and am trying to make a menu with expandable sections. I have been trying to solve this for a couple days, but am stuck on how best to approach this. If anyone has any suggestions to point me in the right direction, I'd be very grateful.

    Here's a snazzy graphic to demonstrate what I am trying to do. http://pasteboard.co/xPyl3oy.jpg. I've got an expandable component already and was trying to make it work with a ListView and a ListModel.

    I tried to use the ListView with sections, however, unfortunately the Listview delegates are not children of the section delegates (like I need them to to be expandable), but are siblings.

    Item{
        id:root
        width: 1406; height: 536
    
    
        ListModel {
            id: animalsModel
            ListElement { name: "Puss in Boots"; type: "Cats" }
            ListElement { name: "Bengal"; type: "Cats" }
            ListElement { name: "Pug"; type: "Dogs" }
            ListElement { name: "German Shepherd"; type: "Dogs" }
            ListElement { name: "Parrot"; type: "Birds" }
        }
    
        Component {
            id: sectionHeader
    
            Rectangle {
                width: 181
                color:"green"
                height: 50
    
                Text {
                    text: section
                    anchors.centerIn: parent
                }
            }
        }
    
    
        ListView {
            id: listing
            width: 181
            height: parent.height
            model: animalsModel
    
            delegate: listdelegate
    
            section.property: "type"
            section.criteria: ViewSection.FullString
            section.delegate: sectionHeader
        }
    
        Component {
            id: listdelegate
    
            Rectangle {
                id: menuItem
                width: 181
                height: 55
                color: ListView.isCurrentItem?"light blue":"white"
    
    
                Text {
                    id: text
                    text: name
                    anchors.centerIn: parent
                }
    
                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        listing.currentIndex = index
                    }
                }
            }
        }
    }
    
    

    I then found this example: https://gist.github.com/elpuri/3753756

    However, in this one, the SubItems have the same index numbers, (the first subitem of each category is index 0) so I cannot use this for navigation.

    Does anyone have any other ideas? What am I missing?

    M Offline
    M Offline
    medyakovvit
    wrote on last edited by medyakovvit
    #3

    @QMLNewbie
    Try this:

    Item{
            id: root
            width: 1406; height: 536
    
            ListModel {
                id: animalsModel
                ListElement { name: "Puss in Boots"; type: "Cats"; aVisible: false}
                ListElement { name: "Bengal"; type: "Cats"; aVisible: false }
                ListElement { name: "Pug"; type: "Dogs"; aVisible: false }
                ListElement { name: "German Shepherd"; type: "Dogs"; aVisible: false }
                ListElement { name: "Parrot"; type: "Birds"; aVisible: false }
            }
    
            Component {
                id: sectionHeader
    
                Rectangle {
                    width: 181
                    color:"green"
                    height: 50
    
                    Text {
                        text: section
                        anchors.centerIn: parent
                    }
    
                    MouseArea{
                        anchors.fill: parent
                        onClicked: {
                            console.log("clicked");
                            for(var i=0; i<animalsModel.count; i++)
                            {
                                var animal = animalsModel.get(i);
                                if(animal.type === section)
                                    animal.aVisible = true;
                                else
                                    animal.aVisible = false;
                            }
                        }
                    }
                }
            }
    
    
            ListView {
                id: listing
                width: 181
                height: parent.height
                model: animalsModel
    
                delegate: listdelegate
    
                section.property: "type"
                section.criteria: ViewSection.FullString
                section.delegate: sectionHeader
            }
    
            Component {
                id: listdelegate
    
                Rectangle {
                    id: menuItem
                    width: 181
                    //height: 55
                    color: ListView.isCurrentItem ? "lightblue" : "white"
                    visible: aVisible
    
                    onVisibleChanged: {
                        if(visible)
                            height = 55;
                        else
                            height = 0;
                    }
    
                    Behavior on height {
                        NumberAnimation { duration: 1000 }
                    }
    
                    Text {
                        id: text
                        text: name
                        anchors.centerIn: parent
                    }
    
                    MouseArea {
                        anchors.fill: parent
                        onClicked: {
                            listing.currentIndex = index;
                        }
                    }
                }
            }
        }
    

    It's looking a little bit ugly. But maybe will be helpful.

    1 Reply Last reply
    0
    • QMLNewbieQ Offline
      QMLNewbieQ Offline
      QMLNewbie
      wrote on last edited by
      #4

      @JordanHarris: Thanks - I had seen the Expanding Delegates example, but not the shape shifting one. The problem is not the expanding mechanism, but how to populate the subItems in those two examples. If they are not all from one ListModel, I don't know how to use them for navigating - as in, currentIndex onClicked goes to this page, the other one goes to that page etc.

      @medyakovvit : That's really great, thank you! It is indeed quite ugly, and I am hesitant about writing to a bool variable in a ListModel, but it does seem to work for now. I'm gonna play around with it for a little bit more.

      M 1 Reply Last reply
      0
      • QMLNewbieQ QMLNewbie

        @JordanHarris: Thanks - I had seen the Expanding Delegates example, but not the shape shifting one. The problem is not the expanding mechanism, but how to populate the subItems in those two examples. If they are not all from one ListModel, I don't know how to use them for navigating - as in, currentIndex onClicked goes to this page, the other one goes to that page etc.

        @medyakovvit : That's really great, thank you! It is indeed quite ugly, and I am hesitant about writing to a bool variable in a ListModel, but it does seem to work for now. I'm gonna play around with it for a little bit more.

        M Offline
        M Offline
        medyakovvit
        wrote on last edited by
        #5

        @QMLNewbie
        it would be easier, if we have some Item that wraps items of section. But it's look like we haven't.

        1 Reply Last reply
        0
        • J Offline
          J Offline
          JordanHarris
          wrote on last edited by JordanHarris
          #6

          I attempted this and it was much harder than I expected. The way I attempted it was to modify the gist mentioned in the OP by making the sub item delegate another ListView. The reason I don't want to use sections like you do is because I would want top level items to be selectable, like normal items. Then I would want to be able to expand the sub items by clicking, tapping, or pressing a key (maybe enter or space). Then when it's expended, I'd like the sub ListView to gain focus and have keyboard navigation. I haven't gotten the keyboard and focus done yet, but I could at least make it all look right. I'll keep working on it and post my solution of I can get it working.

          1 Reply Last reply
          0
          • QMLNewbieQ Offline
            QMLNewbieQ Offline
            QMLNewbie
            wrote on last edited by
            #7

            I've been playing around with @medyakovvit 's example some more, but I am running into one big problem: I need the section delegate's color to change when the section is collapsed. This works fine if you collapse a section by clicking on it's own header. If you click on another section - which opens that section and collapses everything else - the other section delegates do not change color. I can not find any way of accessing them at all. The "section" property is just a string.

            I'm starting to think that this is simply not possible to do this!

            import QtQuick 2.6
            
            Item{
                id: root
                width: 1406; height: 536
                
                ListModel {
                    id: animalsModel
                    ListElement { name: "Puss in Boots"; type: "Cats"; aVisible: false}
                    ListElement { name: "Bengal"; type: "Cats"; aVisible: false }
                    ListElement { name: "Pug"; type: "Dogs"; aVisible: false }
                    ListElement { name: "German Shepherd"; type: "Dogs"; aVisible: false }
                    ListElement { name: "Parrot"; type: "Birds"; aVisible: false }
                }
                
                Component {
                    id: sectionHeader
                    
                    Rectangle {
                        id: testRect
                        width: 181
                        color:"green"
                        height: 50
                        
                        Text {
                            text: section
                            anchors.centerIn: parent
                        }
                        
                        MouseArea{
                            anchors.fill: parent
                            onClicked: {
                                console.log("clicked");
                                for(var i=0; i<animalsModel.count; i++)
                                {
                                    var animal = animalsModel.get(i);
                                    if(animal.type === section && animal.aVisible == false) {
                                        animal.aVisible = true;
                                        testRect.color = "red"
                                    }
                                    else if (animal.type === section && animal.aVisible == true) {
                                        animal.aVisible = false;
                                        testRect.color = "green"
                                    }
                                    
                                    else {
                                        animal.aVisible = false;
                                        //                            testRect.color = "green" //makes everything green
                                    }
                                }
                            }
                        }
                    }
                }
                
                
                ListView {
                    id: listing
                    width: 181
                    height: parent.height
                    model: animalsModel
                    
                    delegate: listdelegate
                    
                    section.property: "type"
                    section.criteria: ViewSection.FullString
                    section.delegate: sectionHeader
                }
                
                Component {
                    id: listdelegate
                    
                    Rectangle {
                        id: menuItem
                        width: 181
                        //height: 55
                        color: ListView.isCurrentItem ? "lightblue" : "white"
                        visible: aVisible
                        
                        onVisibleChanged: {
                            if(visible)
                                height = 55;
                            else
                                height = 0;
                        }
                        
                        Behavior on height {
                            NumberAnimation { duration: 300 }
                        }
                        
                        Text {
                            id: text
                            text: name
                            anchors.centerIn: parent
                        }
                        
                        MouseArea {
                            anchors.fill: parent
                            onClicked: {
                                listing.currentIndex = index;
                            }
                        }
                    }
                }
            }
            
            M 1 Reply Last reply
            0
            • QMLNewbieQ QMLNewbie

              I've been playing around with @medyakovvit 's example some more, but I am running into one big problem: I need the section delegate's color to change when the section is collapsed. This works fine if you collapse a section by clicking on it's own header. If you click on another section - which opens that section and collapses everything else - the other section delegates do not change color. I can not find any way of accessing them at all. The "section" property is just a string.

              I'm starting to think that this is simply not possible to do this!

              import QtQuick 2.6
              
              Item{
                  id: root
                  width: 1406; height: 536
                  
                  ListModel {
                      id: animalsModel
                      ListElement { name: "Puss in Boots"; type: "Cats"; aVisible: false}
                      ListElement { name: "Bengal"; type: "Cats"; aVisible: false }
                      ListElement { name: "Pug"; type: "Dogs"; aVisible: false }
                      ListElement { name: "German Shepherd"; type: "Dogs"; aVisible: false }
                      ListElement { name: "Parrot"; type: "Birds"; aVisible: false }
                  }
                  
                  Component {
                      id: sectionHeader
                      
                      Rectangle {
                          id: testRect
                          width: 181
                          color:"green"
                          height: 50
                          
                          Text {
                              text: section
                              anchors.centerIn: parent
                          }
                          
                          MouseArea{
                              anchors.fill: parent
                              onClicked: {
                                  console.log("clicked");
                                  for(var i=0; i<animalsModel.count; i++)
                                  {
                                      var animal = animalsModel.get(i);
                                      if(animal.type === section && animal.aVisible == false) {
                                          animal.aVisible = true;
                                          testRect.color = "red"
                                      }
                                      else if (animal.type === section && animal.aVisible == true) {
                                          animal.aVisible = false;
                                          testRect.color = "green"
                                      }
                                      
                                      else {
                                          animal.aVisible = false;
                                          //                            testRect.color = "green" //makes everything green
                                      }
                                  }
                              }
                          }
                      }
                  }
                  
                  
                  ListView {
                      id: listing
                      width: 181
                      height: parent.height
                      model: animalsModel
                      
                      delegate: listdelegate
                      
                      section.property: "type"
                      section.criteria: ViewSection.FullString
                      section.delegate: sectionHeader
                  }
                  
                  Component {
                      id: listdelegate
                      
                      Rectangle {
                          id: menuItem
                          width: 181
                          //height: 55
                          color: ListView.isCurrentItem ? "lightblue" : "white"
                          visible: aVisible
                          
                          onVisibleChanged: {
                              if(visible)
                                  height = 55;
                              else
                                  height = 0;
                          }
                          
                          Behavior on height {
                              NumberAnimation { duration: 300 }
                          }
                          
                          Text {
                              id: text
                              text: name
                              anchors.centerIn: parent
                          }
                          
                          MouseArea {
                              anchors.fill: parent
                              onClicked: {
                                  listing.currentIndex = index;
                              }
                          }
                      }
                  }
              }
              
              M Offline
              M Offline
              medyakovvit
              wrote on last edited by
              #8

              @QMLNewbie
              Maybe this workaround is suits you:

              Item{
                      id: root
                      width: 1406; height: 536
              
                      ListModel {
                          id: animalsModel
                          ListElement { name: "Puss in Boots"; type: "Cats"; aVisible: false}
                          ListElement { name: "Bengal"; type: "Cats"; aVisible: false }
                          ListElement { name: "Pug"; type: "Dogs"; aVisible: false }
                          ListElement { name: "German Shepherd"; type: "Dogs"; aVisible: false }
                          ListElement { name: "Parrot"; type: "Birds"; aVisible: false }
                      }
              
                      Component {
                          id: sectionHeader          
              
                          Rectangle {
                              id: sectionHeaderRect
                              width: 181
                              color:"green"
                              height: 50
              
                              property var view: ListView.view
                              property var viewCurrentItem: ListView.view.currentItem
              
                              onViewCurrentItemChanged: {
                                  if(viewCurrentItem){
                                      if(viewCurrentItem.section === section)
                                          color = "blue";
                                      else
                                          color = "green";
                                  }
                              }
              
                              Text {
                                  id: sectionHeaderText
                                  text: section
                                  anchors.centerIn: parent
                              }
              
                              MouseArea{
                                  anchors.fill: parent
                                  onClicked: {
                                      var firstInSection = false;
                                      for(var i=0; i<animalsModel.count; i++)
                                      {
                                          var animal = animalsModel.get(i);
                                          if(animal.type === section)
                                          {
                                              animal.aVisible = true;
                                              if(!firstInSection)
                                              {
                                                  firstInSection = true;
                                                  sectionHeaderRect.view.currentIndex = i;
                                              }
                                          }
                                          else
                                              animal.aVisible = false;
                                      }
                                  }
                              }
                          }
                      }
              
              
                      ListView {
                          id: listing
                          width: 181
                          height: parent.height
                          model: animalsModel
              
                          delegate: listdelegate
              
                          section.property: "type"
                          section.criteria: ViewSection.FullString
                          section.delegate: sectionHeader
                      }
              
                      Component {
                          id: listdelegate
              
                          Rectangle {
                              id: menuItem
                              width: 181
                              //height: 55
                              color: ListView.isCurrentItem ? "lightblue" : "white"
                              property var section: ListView.section
                              visible: aVisible
              
                              onVisibleChanged: {
                                  if(visible)
                                      height = 55;
                                  else
                                      height = 0;
                              }
              
                              Behavior on height {
                                  NumberAnimation { duration: 1000 }
                              }
              
                              Text {
                                  id: text
                                  text: name
                                  anchors.centerIn: parent
                              }
              
                              MouseArea {
                                  anchors.fill: parent
                                  onClicked: {
                                      listing.currentIndex = index;
                                  }
                              }
                          }
                      }
                  }
              
              1 Reply Last reply
              0
              • QMLNewbieQ Offline
                QMLNewbieQ Offline
                QMLNewbie
                wrote on last edited by
                #9

                Thanks @medyakovvit - I've spent the morning playing with this, but unfortunately every time you click on a Section Header, the currentIndex of the list changes. Since I am using the currentIndex to navigate between pages, that means the pages jump around every time you open or collapse a section.

                M 1 Reply Last reply
                0
                • QMLNewbieQ QMLNewbie

                  Thanks @medyakovvit - I've spent the morning playing with this, but unfortunately every time you click on a Section Header, the currentIndex of the list changes. Since I am using the currentIndex to navigate between pages, that means the pages jump around every time you open or collapse a section.

                  M Offline
                  M Offline
                  medyakovvit
                  wrote on last edited by
                  #10

                  @QMLNewbie
                  Ok, can you describe the behavior of listview one more time?

                  1. How many sections can be collapsed at the same time?
                  2. Do you need to highlight current section?

                  It's look like you need view that behaves like TreeView but with custom appearance?

                  1 Reply Last reply
                  0
                  • QMLNewbieQ Offline
                    QMLNewbieQ Offline
                    QMLNewbie
                    wrote on last edited by
                    #11

                    @medyakovvit : Thanks so much for helping me out, really appreciated.

                    Only one section can be open at a time. All the other sections should be collapsed. If you click on another section header, that one should open and the current open one should close. However, the currentIndex is independent of that. The currentIndex only changes when you actually click on a subitem. One you click on a subitem, currentIndex and therefore the content page changes.

                    So you could have subitem2 in section 1 selected and the corresponding page is open. If you then click on section2, that section opens, but subitem2 from section1 and it's page stay selected until you click on a different item.

                    As soon as you click on a section header, I need that section header to be highlighted (you can see in the original graphic here: http://pasteboard.co/xPyl3oy.jpg that an open section has the arrow pointing upwards instead of downwards).

                    I'll have a look at TreeView to see if that one would work! =)

                    M 1 Reply Last reply
                    0
                    • QMLNewbieQ QMLNewbie

                      @medyakovvit : Thanks so much for helping me out, really appreciated.

                      Only one section can be open at a time. All the other sections should be collapsed. If you click on another section header, that one should open and the current open one should close. However, the currentIndex is independent of that. The currentIndex only changes when you actually click on a subitem. One you click on a subitem, currentIndex and therefore the content page changes.

                      So you could have subitem2 in section 1 selected and the corresponding page is open. If you then click on section2, that section opens, but subitem2 from section1 and it's page stay selected until you click on a different item.

                      As soon as you click on a section header, I need that section header to be highlighted (you can see in the original graphic here: http://pasteboard.co/xPyl3oy.jpg that an open section has the arrow pointing upwards instead of downwards).

                      I'll have a look at TreeView to see if that one would work! =)

                      M Offline
                      M Offline
                      medyakovvit
                      wrote on last edited by
                      #12

                      @QMLNewbie

                      One more try:)

                      Item{
                              id: root
                              width: 1406; height: 536
                      
                              ListModel {
                                  id: animalsModel
                                  ListElement { name: "Puss in Boots"; type: "Cats"; aVisible: false}
                                  ListElement { name: "Bengal"; type: "Cats"; aVisible: false }
                                  ListElement { name: "Pug"; type: "Dogs"; aVisible: false }
                                  ListElement { name: "German Shepherd"; type: "Dogs"; aVisible: false }
                                  ListElement { name: "Parrot"; type: "Birds"; aVisible: false }
                              }
                      
                              Component {
                                  id: sectionHeader
                      
                                  Rectangle {
                                      id: sectionHeaderRect
                                      width: 181
                                      color:"green"
                                      height: 50
                      
                                      property bool isExpanded: false
                                      property string currentExpandedSection: ListView.view.expandedSection
                      
                                      onCurrentExpandedSectionChanged: {
                                          if(currentExpandedSection === section)
                                              isExpanded = true;
                                          else
                                              isExpanded = false;
                                      }
                      
                                      onIsExpandedChanged: {
                                          if(isExpanded){
                                              color = "blue";
                                              ListView.view.expandedSection = section;
                                          }
                                          else
                                              color = "green";
                                          for(var i=0; i<animalsModel.count; i++){
                                              var animal = animalsModel.get(i);
                                              if(section === animal.type)
                                                  animal.aVisible = sectionHeaderRect.isExpanded;
                                          }
                                      }
                      
                                      Text {
                                          id: sectionHeaderText
                                          text: section
                                          anchors.centerIn: parent
                                      }
                      
                                      MouseArea{
                                          anchors.fill: parent
                                          onClicked: {
                                              sectionHeaderRect.isExpanded = !sectionHeaderRect.isExpanded;
                                          }
                                      }
                                  }
                              }
                      
                      
                              ListView {
                                  id: listing
                                  width: 181
                                  height: parent.height
                                  model: animalsModel
                      
                                  property string expandedSection: ""
                      
                                  delegate: listdelegate
                      
                                  section.property: "type"
                                  section.criteria: ViewSection.FullString
                                  section.delegate: sectionHeader
                      
                              }
                      
                              Component {
                                  id: listdelegate
                      
                                  Rectangle {
                                      id: menuItem
                                      width: 181
                                      //height: 55
                                      color: ListView.isCurrentItem ? "lightblue" : "white"
                                      visible: aVisible
                      
                                      onVisibleChanged: {
                                          if(visible)
                                              height = 55;
                                          else
                                              height = 0;
                                      }
                      
                                      Behavior on height {
                                          NumberAnimation { duration: 1000 }
                                      }
                      
                                      Text {
                                          id: text
                                          text: name
                                          anchors.centerIn: parent
                                      }
                      
                                      MouseArea {
                                          anchors.fill: parent
                                          onClicked: {
                                              listing.currentIndex = index;
                                          }
                                      }
                                  }
                              }
                          }
                      
                      1 Reply Last reply
                      1
                      • QMLNewbieQ Offline
                        QMLNewbieQ Offline
                        QMLNewbie
                        wrote on last edited by
                        #13

                        @medyakovvit : EEEEK that's it!! Thank you so much! Took me a while to understand what you have done, but it works perfectly. Super thrilled that I can now move on to other things and don't have to worry about this one anymore, phew.

                        Thanks for helping a girl out! =)

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved