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.
  • J Offline
    J Offline
    JordanHarris
    wrote on last edited by
    #2

    Here's some other examples that might help you out. I'm also curious about this, so I'll have to give it a try in a little while to see if I can do it. I'll try to report any findings.

    Shape-Shifting Delegates
    Expanding Delegates

    1 Reply Last reply
    0
    • 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