[Qt-qml] Section header 'always on top'

tapani.mikola at nokia.com tapani.mikola at nokia.com
Fri Aug 13 10:10:25 CEST 2010


Hi,

Wanted to share one exciting (at least IMO) piece of code.

Some existing devices have this fancy visual feature that when scrolling 
a list the section header stays on top and only when the next section 
header scrolls in it 'pushes' the old one away. (See the attached picture)
Unfortunately QML in Qt 4.7 does not support that kind of property for 
ListView. Section headers always scroll along with the list items. But 
no worries, as the following example (extended from the ListView 
documentation example) does the trick. Hopefully the comments in line 
the code are enough for documentation. Post any comments or questions to 
this list.

As continuation, I am planning to create a couple of JIRA items:
* To have a section.alwaysOnTop -property (or something similar) for 
ListView (the below trick wouldn't be needed at all)
* To have a 'ListView.previousSection et al' attached properties 
available also to the section delegate (currently only available for 
item delegate). These would make these kind of things easier to do. Now 
the code has to refer to list & model through their id:s

First a component to draw the section header:

---- Code snippet starts <SectionHeader.qml> ----

import Qt 4.7

Rectangle {
     color: "lightsteelblue"
     height: childrenRect.height
     property alias text: hdrText.text
     opacity:  0.85

     Text {
         id: hdrText
         font.bold: true
         font.pixelSize:26
     }
}

---- Code snippet ends <SectionHeader.qml> ----

and then the main qml file:

--- Code snippet starts <alwaysonsectionheader.qml> ----

import Qt 4.7

Rectangle {
      id: container
      width: 200
      height: 250
      gradient: Gradient {
          GradientStop { position:  0.0; color: "#555555" }
          GradientStop { position:  1.0; color: "#eeeeee" }
      }
      Rectangle {
          x: 180
          width: 20
          height: parent.height
          gradient: Gradient {
              GradientStop { position:  0.0; color: "#eeeeee" }
              GradientStop { position:  1.0; color: "#555555" }
          }
      }

      ListModel {
          id: animalsModel
          ListElement { name: "Parrot"; size: "Small" }
          ListElement { name: "Guinea pig"; size: "Small" }
          ListElement { name: "Mouse"; size: "Small" }
          ListElement { name: "Sparrow"; size: "Small" }
          ListElement { name: "Dog"; size: "Medium" }
          ListElement { name: "Cat"; size: "Medium" }
          ListElement { name: "Dolphin"; size: "Medium" }
          ListElement { name: "Seal"; size: "Medium" }
          ListElement { name: "Elephant"; size: "Large" }
          ListElement { name: "Blue whale"; size: "Large" }
          ListElement { name: "Rhino"; size: "Large" }
          ListElement { name: "Ostrich"; size: "Large" }
          ListElement { name: "Sperm whale"; size: "Large" }
          ListElement { name: "Giraffe"; size: "Large" }
      }

      // The section header delegate
      Component {
          id: sectionDelegate
          Item {
              id: sectionItem
              height:  childrenRect.height
              property real myPos: y - animalsList.contentY

              // For some reason, this does not work
              //visible: myPos >= 0

              // The visible -property setting(s) below are needed only, 
if SectionHeader has transparency (opacity < 1.0)
              onMyPosChanged: {
                  if (animalsList.contentY >= 0) {
                      if (myPos < height*2 && myPos > -height*2) {
                          // In the 'hot' area

                          // Set the correct text.
                          if (myPos < 0) {
                              sectionHeader.text = section
                              visible = false
                          }
                          else {
                              // Oooh, would it be nice to have 
'previousSection' property

                              // One can do this, if there is a 
cacheBuffer for the list, if not I sometimes get undefined from get() as
                              // the listitem above the section header 
is already deleted
                              //sectionHeader.text = y >= 1 ? 
animalsModel.get(animalsList.indexAt(0, y-1)).size : 
animalsModel.get(0).size

                              // Safer version that does not require 
cacheBuffer for the animalsList
                              var nextIndex = animalsList.indexAt(0, 
y+height+1)
                              sectionHeader.text = nextIndex >= 1 ? 
animalsModel.get(nextIndex-1).size : animalsModel.get(0).size
                              visible = true
                          }

                          // Then set y of the section header overlay to 
give an illusion of 'push'
                          if (myPos < height && myPos >= 0)
                              sectionHeader.y = 0 - (height - myPos) // 
push the old header away -effect
                          else
                              sectionHeader.y = 0 // clean up after push
                      }
                      else visible = true
                  }
                  else visible = true
              }
              SectionHeader {
                  width: animalsList.width
                  text: section
              }
          }
      }

      Item {
          anchors.fill: parent
          ListView {
              id: animalsList
              anchors.fill: parent
              model: animalsModel
              delegate: Text { text: name; font.pixelSize: 24 }

              section.property: "size"
              section.criteria: ViewSection.FullString
              section.delegate: sectionDelegate
          }

          // The section header item (rectangle+text overlayed on top of 
the ListView)
          SectionHeader {
              id: sectionHeader
              visible: !animalsList.atYBeginning // not wanted when list 
is at beginning (list can bounce and the section delegate is at right place)
              width: parent.width
              text: animalsList.currentSection
          }
      }
  }

---- Code snippet ends <alwaysonsectionheader.qml> ----

-------------- next part --------------
A non-text attachment was scrubbed...
Name: listviewsectiontrick.png
Type: image/png
Size: 13644 bytes
Desc: listviewsectiontrick.png
Url : http://lists.qt.nokia.com/pipermail/qt-qml/attachments/20100813/35d381fd/attachment.png 


More information about the Qt-qml mailing list