qtqmlqtquick2overlapflickable

Click \ flick through emptry area of a Flickable that partially overlaps another Flickable


I have a scene "editor" flickable on the bottom, and docked at its right side, a scene "outliner" flickable on top of it, which shows a tree of the scene structure.

  Flickable {
    id: editor
    anchors.fill: parent
    contentWidth: 5000
    contentHeight: 5000
    Repeater {
      model: 500
      delegate: Rectangle {
        width: Math.random() * 200 + 50
        height: width
        x: Math.random() * editor.contentWidth
        y: Math.random() * editor.contentHeight
        color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
        border.color: "black"
      }
    }
  }

  Flickable {
    id: outliner
    anchors.right: editor.right
    width: contentWidth
    height: parent.height
    contentWidth: contentItem.childrenRect.width
    contentHeight: contentItem.childrenRect.height
    Column {
      Repeater {
        model: 500
        delegate: Rectangle {
          width: Math.random() * 200 + 50
          x: outliner.contentWidth - width
          height: 50
          color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
          border.color: "black"
        }
      }
    }
  }

Naturally, I want to be able to flick both to navigate both, since they both do get larger than the available screen real estate.

The problem is that the outliner flickable will simply block the editor flickable, even if I flick from a position that the outliner items do not occupy. I don't want that, what I want is to flick the outliner only if the flick happens on top of an outliner item, if not I want to skip through to the editor, so I can click and flick it from the area to the right.

I haven't been able to do it, due to the way Flickable works. It will come in first to check for a drag, and only if the click doesn't exceed the drag threshold will it let the click down to underlying elements. So I can't think of a way to flick only if there is an object in that position.

Is there any way I can get the event handling to do what I want it to?


Solution

  • Mkay, I think I did it, the key was to disable the interactivity for the outliner, then intercept input via a MouseArea in the delegate, then carefully time and measure the manual dragging delta and if fast enough, schedule a manual flick on release.

    Getting it to both stop an ongoing flick on press and subsequently launching another flick from the next drag turned out to be problematic after a flick has just been cancelled, but by delaying each flick to the next event loop cycle I got it working.

    I also added wheel support and such.

    If anyone experiences problems with it on any platform, do let me know in the comments.

    // the editor code is the same from the OP
      Flickable {
        id: outliner
        anchors.right: editor.right
        width: contentWidth
        height: parent.height
        contentWidth: contentItem.childrenRect.width
        contentHeight: contentItem.childrenRect.height
        interactive: false
        property int ly: 0
        property real lt: 0
        property int dy: 0
        function go(yy) {
          dy = contentY - (contentY -= yy - ly)
          ly = yy
          lt = Date.now()
        }
        Timer { 
          id: trigger
          interval: 1
          onTriggered: outliner.flick(0, outliner.dy * 150)
        }
        Column {
          Repeater {
            model: 500
            delegate: Rectangle {
              width: Math.random() * 200 + 50
              x: outliner.contentWidth - width
              height: 50
              color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
              border.color: "black"
              MouseArea {
                anchors.fill: parent
                Drag.active: drag.active
                drag.target: Item { id: dummy }
                onPressed: {
                  dummy.y = 0
                  if (outliner.flicking) outliner.cancelFlick()
                }
                onWheel: outliner.flick(0, wheel.angleDelta.y * 10)
                onPositionChanged: {
                  if (drag.active) outliner.go(dummy.y)
                }
                onReleased: {
                  if (Date.now() - outliner.lt < 50) trigger.start()
                  outliner.ly = 0
                  outliner.returnToBounds()
                }
              }
            }
          }
        }
      }