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. ListView with MouseArea under to cach swipe gesture
Forum Updated to NodeBB v4.3 + New Features

ListView with MouseArea under to cach swipe gesture

Scheduled Pinned Locked Moved Solved QML and Qt Quick
7 Posts 2 Posters 3.5k Views 2 Watching
  • 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.
  • M Offline
    M Offline
    Marek
    wrote on last edited by
    #1

    Hi all,
    I have ListView which uses delegate for each item, this "AndroidDelegate" should get mouseEvent clicked, pressed, released.
    I also need MouseArea "under" ListView to catch swipe gesture (to exit current page).
    I was testing MouseArea inside ListView, and outside, also changing Z value.
    In the code below it almost works, there are two problems:

    1. First time app is started and first press and drag on ListView - ListView does not scroll items, instead "clicked" is fired from delegate on which I stop dragging, all subsequent press and drag works fine, ListView is scrolled.
    2. "AndroidDelegate" does not receive "pressed" signal, and I would like to change its color when pressed

    If I get rid of additional MouseArea, ListView and delegate works fine, but I don't have the swipe signal.
    How to achieve working ListView with delegates and swipe signal (to change the page)

    ListView {
        id: catalogListView
        anchors.top: phraseInput.bottom
        anchors.bottom: parent.bottom
        anchors.topMargin: Style.catalogFontSize
        width: parent.width
        clip: true
        z: 5
        model: CatalogModel
    
    
        delegate: AndroidDelegate {
            catalog_id: id_role
            text: name_role
            isDir: (dir_role ? 1 : 0)
            back_visible: back_visible_role
            onClicked: {
                if(dir_role) {
                    catalogDirClicked(catalog_id)
                } else {
                    catalogItemClicked(catalog_id,1)
                }
            }
        }
        MouseArea {
            id: catalogMouseArea
            z:4
            anchors.fill: parent
            preventStealing: false
            propagateComposedEvents: true
            property real velocity: 0.0
            property int xStart: 0
            property int xPrev: 0
            property bool tracing: false
            property int swiping
            onPressed: {
                xStart = mouse.x
                xPrev = mouse.x
                velocity = 0
                tracing = true
                swiping = false
                console.log("catalog mouse pressed");
            }
            onPositionChanged: {
                if ( !tracing ) return
                var currVel = (mouse.x-xPrev)
                velocity = (velocity + currVel)/2.0
                xPrev = mouse.x
                console.log("vel:"+velocity)
                if ( velocity < -15 ) {
                    tracing = false
                    swiping=true
                    console.log("left swipe detected");
                }
                if ( velocity > 15 ) {
                    tracing = false
                    swiping=true
                    console.log("right swipe detected")
                    catalogDirBack()
                }
            }
            onReleased: {
                tracing = false
            }
        }
    }
    //AndroidDelegate.qml
    Rectangle {
        id: root
        width: parent.width
        height: Style.catalogFontSize*2
    
        property alias text: textitem.text
        property int catalog_id
        property int back_visible;
        signal clicked
    
        Rectangle {
            anchors.fill: parent
            color: "black"
            visible: mouse.pressed
        }
    
        Text {
            id: textitem
            color: "black"
            font.pixelSize: Style.catalogFontSize
            fontSizeMode: Text.Fit
            verticalAlignment: Text.AlignVCenter
    
            anchors.left: parent.left
            anchors.leftMargin: backImageItem.width
            width: (isDir ? parent.width-100 : parent.width-20)
            height: parent.height
            elide: Text.ElideRight
        }
        MouseArea {
            id: mouse
            anchors.fill: parent
            property int moved: 0
            property int last_x: 0
            onClicked: {
                console.log("delegate clicked");
                if(!catalogMouseArea.swiping) {
                    catalogListView.in_x_from= catalogRect.width
                    catalogListView.in_x_to= 0
                    catalogListView.out_x_from= 0
                    catalogListView.out_x_to= -catalogRect.width
                    catalogListView.direction=1
                    root.clicked()
                }
            }
            onPressed: {
                console.log("delegate pressed");
            }
            onPressAndHold: {
                console.log("delegate pressAndHold")
            }
            onReleased: {
                console.log("delegate released");
            }
            onPositionChanged: {
                console.log("delegate positionChanged")
            }
        }
    }
    
    1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      Put your ListView inside a SwipeView.

      (Z(:^

      M 1 Reply Last reply
      0
      • sierdzioS sierdzio

        Put your ListView inside a SwipeView.

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

        @sierdzio Thanks for answer.

        Well, I can't do that. My ListView is like a Tree, for instance a view of file system. My model is QAbstractListModel exposed from C++. At first it shows all dirs from path "/". When delegate is clicked, I send signal to model which reloads its data and then shows items from "/selectedDir" directory, next delegate clicked - "/selectedDir/nextDir" and so on.
        It's hard to believe that I need to implement "back" button to navigate ListView back, I would like to do that by swiping.
        In widgets application based on QGraphicsView I would catch that "swipe" gesture from underlying QGraphicsScene.

        here z order for mouseArea doesn't work in ListView someone is doing similar thing but with clicked event which I suppose is propagated because this is composedEvent, but press, positionChanged, and release are not.

        Best Regards
        Marek

        1 Reply Last reply
        0
        • sierdzioS Offline
          sierdzioS Offline
          sierdzio
          Moderators
          wrote on last edited by
          #4

          Oh, I see. That indeed complicates matters. It is quite ridiculous that QtQuick does not support custom gestures well... even though it was created specifically for mobile, touch-enabled devices.

          What about using SwipeDelegate, then? The example in docs provides a SwipeDelegate for ListView. Looks like you could use the swipe.left property to implement your use case.

          Note: I've never used SwipeDelegate myself, so it is a kind of shot in the dark ;-)

          (Z(:^

          M 1 Reply Last reply
          0
          • sierdzioS sierdzio

            Oh, I see. That indeed complicates matters. It is quite ridiculous that QtQuick does not support custom gestures well... even though it was created specifically for mobile, touch-enabled devices.

            What about using SwipeDelegate, then? The example in docs provides a SwipeDelegate for ListView. Looks like you could use the swipe.left property to implement your use case.

            Note: I've never used SwipeDelegate myself, so it is a kind of shot in the dark ;-)

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

            @sierdzio I will give it a try, just to check how it works. However I could implement swiping gesture in my custom delegate, no problem, but what with such a case: ListView spans across the phone screen and it has only one item at the top (in currentDir there is only one file). I need to perform swipe gesture on this element only, I mean, with SwipeDelegate or without, swipe won't work on an empty part of ListView.
            Hmm... maybe I can check whether contentHeight is less than screenHeight or parentHeight and then create last delegate that is high enough to cover empty part of ListView?

            M 1 Reply Last reply
            0
            • M Marek

              @sierdzio I will give it a try, just to check how it works. However I could implement swiping gesture in my custom delegate, no problem, but what with such a case: ListView spans across the phone screen and it has only one item at the top (in currentDir there is only one file). I need to perform swipe gesture on this element only, I mean, with SwipeDelegate or without, swipe won't work on an empty part of ListView.
              Hmm... maybe I can check whether contentHeight is less than screenHeight or parentHeight and then create last delegate that is high enough to cover empty part of ListView?

              M Offline
              M Offline
              Marek
              wrote on last edited by
              #6

              Excellent ;) it works!
              The only drawback is that I can't swipe across items, but I can live with that.
              My intention was also that the last "stretched" item would look like the other items, in other words stretching is invisible., and the item is resizable when I set different fontSize.
              Working example below (If one would like to test it, one needs to provide Model, maybe static model defined in QML would do)

              import QtQuick 2.9
              import QtGraphicalEffects 1.0
              import "style"
              
              Rectangle {
                  id: catalogRect
                  width: parent.width
              
                  ListView {
                      id: catalogTestListView
                      anchors.fill: parent
                      width: parent.width
                      clip: true
              
                      property int direction: 1
                      property int in_x_from: catalogRect.width
                      property int in_x_to: 0
                      property int out_x_from: 0
                      property int out_x_to: -catalogRect.width
                      model: CatalogModel
              
              
                      delegate: TestDelegate {
                          catalog_id: id_role
                          title: name_role
                          latin: name_role
                          isDir: (dir_role ? 1 : 0)
                          back_visible: back_visible_role
                          last: (index==(CatalogModel.rowCount()-1) ? 1 : 0)
                          onClicked: {
                              if(dir_role) {
                                  console.log("dir catalog_id:"+catalog_id+" clicked");
                                  catalogDirClicked(catalog_id)
                              } else {
                                  console.log("item catalog_id:"+catalog_id+" clicked");
                                  catalogItemClicked(catalog_id,1)
                              }
                          }
                      }
                      add: Transition {
                          id: addTrans
                          ScriptAction {
                              script: {
                                  if(catalogTestListView.direction==1) {
                                      addTrans.ViewTransition.item.x=catalogRect.width
                                  }
                                  if(catalogTestListView.direction==-1) {
                                      addTrans.ViewTransition.item.x=-catalogRect.width
                                  }
                                  console.log("addTrans direction:"+catalogTestListView.direction);
                              }
                          }
                          SequentialAnimation {
                              PauseAnimation {
                                  duration: (addTrans.ViewTransition.index) * 50
                              }
                              NumberAnimation {
                                  properties: "x"; from: catalogTestListView.in_x_from; to: catalogTestListView.in_x_to; duration: 200
                              }
                          }
                      }
              
                      remove: Transition {
                          id: removeTrans
                          SequentialAnimation {
                              PauseAnimation {
                                  duration: (removeTrans.ViewTransition.index) * 50
                              }
                              NumberAnimation {
                                  properties: "x"; from: catalogTestListView.out_x_from; to: catalogTestListView.out_x_to; duration: 200
                              }
                          }
                      }
                  }
              }
              //TestDelegate.qml
              import QtQuick 2.9
              import QtGraphicalEffects 1.0
              import "style"
              
              Rectangle {
                  id: root
                  width: parent.width
              
                  property alias title: titleItem.text
                  property alias isDir: imageItem.visible
                  property int catalog_id
                  property int back_visible;
                  property string latin
                  property int last
                  property int defaultHeight: Style.catalogFontSize*2.5
                  signal clicked
              
                  height: {
                      if(last && catalogRect.height>((CatalogModel.rowCount()-1)*defaultHeight)) {
                          if(catalogRect.height-((CatalogModel.rowCount()-1)*defaultHeight)<defaultHeight)
                              defaultHeight
                          else
                              catalogRect.height-((CatalogModel.rowCount()-1)*defaultHeight)
                      }
                      else {
                          defaultHeight
                      }
                  }
              
                  Rectangle {
                      anchors.top: parent.top
                      anchors.left: parent.left
                      anchors.right: parent.right
                      height: defaultHeight
                      anchors.leftMargin: 15
                      anchors.rightMargin: 15
                      anchors.topMargin: 5
                      anchors.bottomMargin: 5
                      radius:15
                      color: "#c7ffc3"
                      visible: ((mouse.pressed && mouse.mouseY<defaultHeight) ? true : false)
                  }
              
                  Text {
                      id: titleItem
                      color: "#525252"
                      font.pixelSize: Style.catalogFontSize
                      fontSizeMode: Text.Fit
                      verticalAlignment: Text.AlignBottom
                      anchors.top: parent.top
                      anchors.left: parent.left
                      anchors.leftMargin: backImageItem.width
                      width: (isDir ? parent.width-imageItem.width*2 : parent.width-20)
                      height: defaultHeight*0.6666
                      elide: Text.ElideRight
              
                      style: Text.Raised; styleColor: "gray"
                  }
                  Text {
                      id: latinItem
                      color: "gray"
                      text:"("+latin+")"
                      font.pixelSize: 2*Style.catalogFontSize/3
                      font.italic: true
                      fontSizeMode: Text.Fit
                      verticalAlignment: Text.AlignTop
                      anchors.top: titleItem.bottom
                      anchors.left: titleItem.left
                      anchors.leftMargin: backImageItem.width
                      width: parent.width
                      height: defaultHeight/2
                      elide: Text.ElideRight
                      visible: !isDir
                  }
              
              
                  Rectangle {
                      id: bottomLine
                      anchors.top: parent.top
                      anchors.topMargin: defaultHeight
                      anchors.left: parent.left
                      anchors.right: parent.right
                      anchors.leftMargin: 15
                      anchors.rightMargin: 15
                      height: 1
                      color: "#424246"
                      layer.enabled: true
                      layer.effect: DropShadow {
                          verticalOffset: -1
                          horizontalOffset: 0
                          samples: 10
                          spread: 0.5
                      }
                  }
              
                  Image {
                      id: imageItem
                      height: defaultHeight*0.5
                      anchors.right: parent.right
                      anchors.top: parent.top
                      anchors.topMargin: (defaultHeight-imageItem.height)/2
                      source: "../images/navigation_next_item.png"
                      fillMode: Image.PreserveAspectFit
                      smooth: true
                  }
                  Image {
                      id: backImageItem
                      height: defaultHeight*0.5
                      anchors.left: parent.left
                      anchors.top: parent.top
                      anchors.topMargin: (defaultHeight-imageItem.height)/2
                      source: "../images/navigation_prev_item.png"
                      visible: parent.back_visible
                      fillMode: Image.PreserveAspectFit
                      smooth: true
                  }
              
                  MouseArea {
                      id: mouse
                      anchors.fill: parent
                      property real velocity: 0.0
                      property int xStart: 0
                      property int xPrev: 0
                      property bool tracing: false
                      property bool swiping: false
                      onPressed: {
                          xStart = mouse.x
                          xPrev = mouse.x
                          velocity = 0
                          tracing = true
                          swiping = false
                          console.log("delegate item pressed");
                      }
                      onPositionChanged: {
                          if ( !tracing ) return
                          swiping = true
                          var currVel = (mouse.x-xPrev)
                          velocity = (velocity + currVel)/2.0
                          xPrev = mouse.x
                          console.log("vel:"+velocity)
                          if ( velocity < -5 ) {
                              tracing = false
                              console.log("left swipe detected mouse.y:"+mouse.y+" defaultHeight:"+defaultHeight);
                              if(mouse.y<defaultHeight) {
                                  changeTransitionDirection(1)
                                  root.clicked()
                              }
                          }
                          if ( velocity > 5 ) {
                              tracing = false
                              console.log("right swipe detected")
                              changeTransitionDirection(-1)
                              catalogDirBack()
                          }
                      }
                      onClicked: {
                          console.log("delegate clicked");
                          if(!swiping) {
                              changeTransitionDirection(1)
                              root.clicked()
                          }
                      }
                      onReleased: {
                          tracing=false
                          console.log("delegate released");
                      }
                      function changeTransitionDirection(direction) {
                          if(direction==1) {
                              catalogTestListView.in_x_from= catalogRect.width
                              catalogTestListView.in_x_to= 0
                              catalogTestListView.out_x_from= 0
                              catalogTestListView.out_x_to= -catalogRect.width
                              catalogTestListView.direction=1
                          }
                          else {
                              catalogTestListView.in_x_from=-catalogRect.width;
                              catalogTestListView.in_x_to=0;
                              catalogTestListView.out_x_from=0;
                              catalogTestListView.out_x_to=catalogRect.width;
                              catalogTestListView.direction=-1;
                          }
                      }
                  }
              }
              

              Best Regards
              Marek

              1 Reply Last reply
              1
              • sierdzioS Offline
                sierdzioS Offline
                sierdzio
                Moderators
                wrote on last edited by
                #7

                Cool, great news. And thanks for sharing the example! :-)

                (Z(:^

                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