Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Solved Need help to create a tumbler from scratch

    QML and Qt Quick
    2
    7
    1293
    Loading More Posts
    • 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.
    • D
      DavidM29 last edited by

      Hello,
      I'm coding an app for a device that does not support QuickControls above 1.3 so I don't have the QML tumbler.
      I'm trying to make my own I found some code on the internet that I change a bit but there is something I don't like about it and I don't know how to change it.
      The thing I don't like is that once I have reached the end of my list I do have the begining of my list as the next element like in the QuickControls Tumbler.
      Here is an image of my actual Tumbler :
      0_1529499134220_4bc8d9de-1feb-48f4-88bd-efa42282fbbb-image.png
      As you can see the fisrt tumbler stop at 23 and has no next element.
      The last tumbler start at 0 but has no previous element.
      I would like something like that :
      0_1529499274940_12ffec5b-9c3c-497e-83dd-237f894c3698-image.png

      The tumbler values are based on a ListView.
      Here is the code I have for the moment :

      CustomTumbler.qml :

      import QtQuick 2.3
      
      Rectangle {
          id: root
          height: 200
          width: 200
      
          property var values: [0, 1, 2, 3, 4, 5]
          property var value: 0
          property string suffix: ""
          property int itemHeight: 60
          property int textSize: 30
          property string textColor: "#000000"
          property string indicatorColor: "#grey"
          property int indicatorSize: 2
      
          Component.onCompleted: {
              reloadModel()
          }
      
          onValuesChanged: {
              reloadModel()
          }
      
          onValueChanged: {
              setActive(findItemIndex(root.value), true)
          }
      
          onSuffixChanged: {
              reloadModel()
          }
      
          onTextSizeChanged: {
              reloadModel()
          }
      
          onTextColorChanged: {
              reloadModel()
          }
      
          onIndicatorColorChanged: {
              reloadModel()
          }
      
          onIndicatorSizeChanged: {
              reloadModel()
          }
      
          function findItemIndex(val)
          {
              for(var i = 0; i < tumblerModel.length; i++)
              {
                  if(tumblerModel.get(i).val === val && tumblerModel.get(i).hide === false)
                      return i
              }
      
              return -1
          }
      
          function reloadModel()
          {
              var scrollToIndex = 0
      
              tumblerModel.clear()
      
              tumblerModel.append({"val":null,"active":false,"hide":true})
      
              for(var i = 0; i < values.length; i++)
              {
                  if(root.value === root.values[i])
                  {
                      tumblerModel.append({"val":root.values[i],"active":true,"hide":false})
                      scrollToIndex = i
                  }
                  else
                  {
                      tumblerModel.append({"val":root.values[i],"active":false,"hide":false})
                  }
              }
      
              tumblerModel.append({"val":null,"active":false,"hide":true})
      
              tumblerMain.positionViewAtIndex(scrollToIndex, ListView.Center)
          }
      
          function setActive(index, reposition)
          {
              if(index === -1)
                  return
              else if(index === 0)
                  index = 1
              else if(index === tumblerModel.count - 1)
                  index = tumblerModel.count-2
      
              for(var i = 0; i < tumblerModel.count; i++)
              {
                  tumblerModel.setProperty(i, "active", false)
              }
      
              tumblerModel.setProperty(index, "active", true)
      
              root.value = root.values[index]
      
              if(reposition)
                  tumblerMain.positionViewAtIndex(index, ListView.Center)
          }
      
          ListModel {
              id: tumblerModel
          }
      
          Component {
              id: tumblerDelegate
      
              Rectangle {
                  anchors.left: parent.left
                  anchors.right: parent.right
                  height: root.itemHeight + (hide ? (root.height / 2 - root.itemHeight - root.itemHeight / 2) : 0)
                  color: "transparent"
                  visible: !hide
      
                  Text {
                      text: val + suffix
                      color: root.textColor
                      font.pointSize: root.textSize
                      anchors.centerIn: parent
                      opacity: active ? 1 : 0.3
                  }
              }
          }
      
          Rectangle {
              id: indicatorTop
              width: parent.width/1.5
              height: root.indicatorSize
              anchors.centerIn: parent
              anchors.verticalCenterOffset: -(root.itemHeight / 2)
              color: root.indicatorColor
          }
      
          Rectangle {
              id: indicatorBottom
              width: parent.width/1.5
              height: root.indicatorSize
              anchors.centerIn: parent
              anchors.verticalCenterOffset: root.itemHeight / 2
              color: root.indicatorColor
          }
      
          ListView {
              id: tumblerMain
              model: tumblerModel
              delegate: tumblerDelegate
              anchors.fill: parent
              clip: true
              onFlickEnded: {
                  console.log("FlickEnd")
                  var pos = contentY
                  setActive(indexAt(0, contentY + root.height / 2), false)
                  positionViewAtIndex(indexAt(0, contentY + root.height / 2), ListView.Center)
                  var destPos
                  destPos = contentY;
                  anim.from = pos;
                  anim.to = destPos;
                  anim.running = true;
              }
              onDragEnded: {
                  console.log("DragEnd")
                  var pos = contentY
                  console.log(pos)
                  console.log(root.height/2)
                  console.log(indexAt(0, contentY + root.height / 2))
                  setActive(indexAt(0, contentY + root.height / 2), false)
                  positionViewAtIndex(indexAt(0, contentY + root.height / 2), ListView.Center)
                  var destPos
                  destPos = contentY;
                  anim.from = pos;
                  anim.to = destPos;
                  anim.running = true;
              }
          }
      
          NumberAnimation { id: anim; target: tumblerMain; property: "contentY"; duration: 100}
      }
      
      

      Main.qml :

      import QtQuick 2.9
      import QtQuick.Window 2.2
      import QtQuick.Controls 2.2
      
      Window {
          visible: true
          width: 640
          height: 480
          title: qsTr("Hello World")
      
          CustomTumbler {
                  id: t1
                  radius: 0
                  width: 120
                  suffix: ""
                  value: 0
      
                  Component.onCompleted: {
                      var vals = []
                      for(var i = 0; i < 24; i++)
                          vals[i] = i
      
                      values = vals
                  }
              }
      
              CustomTumbler {
                  id: t2
                  anchors.left: t1.right
                  radius: 0
                  width: 120
                  suffix: ""
                  value: 0
      
                  Component.onCompleted: {
                      var vals = []
                      for(var i = 0; i < 60; i++)
                          vals[i] = i
      
                      values = vals
                  }
              }
      
              CustomTumbler {
                  id: t3
                  anchors.left: t2.right
                  radius: 0
                  width: 120
                  suffix: ""
                  value: 0
      
                  Component.onCompleted: {
                      var vals = []
                      for(var i = 0; i < 60; i++)
                          vals[i] = i
      
                      values = vals
                  }
              }
      }
      

      Does anybody know how could I make a loop on my values ?

      raven-worx 1 Reply Last reply Reply Quote 0
      • raven-worx
        raven-worx Moderators @DavidM29 last edited by

        @DavidM29
        The "official" QML Tumbler has a wrap property which does what you want.

        --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
        If you have a question please use the forum so others can benefit from the solution in the future

        1 Reply Last reply Reply Quote 0
        • D
          DavidM29 last edited by

          @raven-worx
          I can not use the "official" QML Tumbler because the device I programming for does not support Qt QuickControls 2 only Qt QuickControls 1.3. That is the reasons why I'm trying to make my own Tumbler

          raven-worx 1 Reply Last reply Reply Quote 0
          • raven-worx
            raven-worx Moderators @DavidM29 last edited by

            @DavidM29 said in Need help to create a tumbler from scratch:

            does not support Qt QuickControls 2

            why actually?
            Are you limited by the Qt version available?

            --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
            If you have a question please use the forum so others can benefit from the solution in the future

            1 Reply Last reply Reply Quote 0
            • D
              DavidM29 last edited by

              It is an embedded device which work only with Qt 5.4 for now. And according to the company providing this device it will not be change soon. So I'm making a few controls of my own.

              1 Reply Last reply Reply Quote 0
              • D
                DavidM29 last edited by

                I'm actually trying to change my ListView to a Pathview which seems to have the feature I want but I'm not able to make it work for now. So if anybody have a clue please share it with me.

                1 Reply Last reply Reply Quote 0
                • D
                  DavidM29 last edited by DavidM29

                  From now I have made this which is close to a tumbler there a fiew things to change but it works quite well :

                  Rectangle {
                  
                      id: container
                      width: 100
                      height: 200
                  
                      Rectangle{  //This rectangle is optional it is the lower bar on the middle of the View
                          anchors.centerIn: parent
                          anchors.verticalCenterOffset: (container.height / 10)
                          width: parent.width/2
                          height: 2
                          color: "black"
                      }
                  
                      Rectangle{ //This rectangle is optional it is the upper bar on the middle of the View
                          anchors.centerIn: parent
                          anchors.verticalCenterOffset: -(container.height / 10)
                          width: parent.width/2
                          height: 2
                          color: "black"
                      }
                  
                      ListModel {
                          id: tumblerModel
                          ListElement{ num:"01" }
                          ListElement{ num:"02" }
                          ListElement{ num:"03" }
                          ListElement{ num:"04" }
                          ListElement{ num:"05" }
                          ListElement{ num:"06" }
                          ListElement{ num:"07" }
                          ListElement{ num:"08" }
                          ListElement{ num:"09" }
                          ListElement{ num:"10" }
                      }
                  
                      Component {
                          id: tumblerDelegate
                  
                          Item {
                              id: itm
                              anchors.left: parent.left
                              anchors.right: parent.right
                              height: parent.height/5
                              scale: PathView.iconScale
                              opacity: PathView.iconOpacity
                  
                              Text {
                                  text: num;
                                  color: itm.PathView.isCurrentItem ? "red" : "black"
                                  font.pointSize: 12
                                  anchors.centerIn: parent
                                  //opacity: active ? 1 : 0.3
                              }
                              MouseArea{
                                  id: itemMouseArea
                                  anchors.fill: parent
                                  onClicked: {
                                      pathView.currentIndex = index
                  
                                  }
                              }
                          }
                      }
                  
                      PathView {
                          id: pathView
                          anchors.fill: parent
                          model: tumblerModel
                          pathItemCount: 5  //Show only five item from the Model
                          preferredHighlightBegin: 0.5  //It is used to center the selected item 
                          preferredHighlightEnd: 0.5  //It is used to center the selected item 
                          highlightRangeMode: PathView.StrictlyEnforceRange
                          delegate: tumblerDelegate
                          path: Path {   //This part could probably be optimized I do not really understand everything of what I'm doing here but it works 
                              startX: parent.width/2; startY: 0
                              PathAttribute { name: "iconScale"; value: 0.7 }
                              PathAttribute { name: "iconOpacity"; value: 0.2 }
                              PathLine {x: parent.width/2; y: container.height/2 }
                              PathAttribute { name: "iconScale"; value: 2 }
                              PathAttribute { name: "iconOpacity"; value: 1 }
                              PathLine {x: parent.width/2; y: container.height }
                          }
                      }
                  }
                  

                  You will need to implement the selected value retrieving and the variable model and then it should be working.
                  Here is the actual result :
                  0_1529572117470_a548e4ae-c59e-4907-8884-50bb39f70cb8-image.png

                  1 Reply Last reply Reply Quote 0
                  • First post
                    Last post