iOS App in QML how to navigate backward through StackView via swipe gestures



  • Hi together,

    I am developing an crossplatform compatible (iOS, Android; maybe later also windows phone) app prototype for our company. I am completely new to QML programming.

    In my main.qml file I use a StackView which is used for the basic navigation through views in the app. For the android like backward navigation I can simply listen to the "Keys.onReleased" event to pop the current view.

    For the iOS implementation of the app I want to use the swipe gesture for navigating to the previous view. What way would you suggest to achieve such functionality?

    I currently have an (not completely working approach) where I am using an "MouseArea" as a child of my StackView (with z: 100) to detect the swipe gesture. The navigation works, but I am in trouble to use the controls (like buttons, list, ...) underneath the MouseArea.

    My current StackView looks like that:

    StackView {
            id: mainStackLayout
            anchors.fill: parent
            focus: true
    
            //handle the android back button
            Keys.onReleased: {
                if (event.key === Qt.Key_Back && mainStackLayout.depth > 1) {
                    mainStackLayout.pop()
                    event.accepted = true
                }
            }
    
            //and the ios back swipe
            MouseArea {
                id: globalMouseArea
                anchors.fill: parent
                hoverEnabled: true
                z: 100
                //propagateComposedEvents: true
                preventStealing: true
                property real velocity: 0.0
                property int xStart: 0
                property int xPrev: 0
                property bool tracing: false
    
                onPressed: {
                    if(mainStackLayout.depth > 1) {
                        xStart = mouse.x
                        xPrev = mouse.x
                        velocity = 0
                        tracing = true
                        mouse.accepted = true
                    } else {
                        mouse.accepted = false
                    }
                }
                onPositionChanged: {
                    print('position changed')
                    //this routine sets the velocity which is checked in the onReleased handler
                    if (!tracing) {
                        mouse.accepted = false
                        return
                    }
                    var currVel = (mouse.x-xPrev)
                    velocity = (velocity + currVel)/2.0
                    xPrev = mouse.x
                    mouse.accepted = true
                }
                onReleased: {
                    print('released')
                    if(!tracing) {
                        mouse.accepted = false
                        return
                    }
                    print('velocity: ' + velocity)
                    tracing = false
                    if (velocity > 15 && mouse.x > parent.width * 0.2 ) {
                        print('Detect swipe')
                        mouse.accepted = true
                        mainStackLayout.pop()
                    }
                }
            }
        }
    

    But as I said it steals the input events from the controls underneath. I tried to get rid of this problem due simulating mouse clicks in case of the user releases the mouse without making an valid gesture, but I failed to implement such a mouse simulating. I tried to send mouseEvents from c++ to several QML objects, but they never received an event.

    For simulating the MouseEvents I tried things like

    QCoreApplication::instance()->sendEvent(findApplicationWindow(), &mouseEvent);
    

    where the mouseEvent is an left click and findApplicationWindow() returns the instance of the "QQuickApplicationWindow". In the net I found something like

        QMouseEvent mouseEvent(QEvent::MouseButtonPress, QPointF(105, 25), Qt::LeftButton, 0, Qt::NoModifier);
    
        QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
        pressEvent.setScenePos(QPointF(105, 25));
        pressEvent.setButton(Qt::LeftButton);
        pressEvent.setButtons(Qt::LeftButton);
        QApplication::sendEvent(object, &pressEvent);
    
        QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
        releaseEvent.setScenePos(QPointF(105, 25));
        releaseEvent.setButton(Qt::LeftButton);
        releaseEvent.setButtons(Qt::LeftButton);
        QApplication::sendEvent(object, &releaseEvent);
    

    Which is although not working.

    But, all in all, I think it is a bad solution to simulate mouse clicks in my case.

    Maybe someone out here has already an working solution for this case or an advise for me. That would really be helpful :-)

    Thank you in advance!

    Greetings
    eumel1990


  • Moderators

    Hi @eumel1990

    I currently have an (not completely working approach) where I am using an "MouseArea" as a child of my StackView (with z: 100) to detect the swipe gesture.

    Instead of maintaining the z order you can make MouseArea as the root element and make StackView child of it so that you propagate only those events beneath which you require.

    MouseArea {
       ...
       StackView {
       ...
       }
    }
    

    Can SwipeView be an alternative to StackView in your case ?

    Also Since Qt 5 there is no relation between QGraphics* classes and QtQuick. The backend implemenation is now scenegraph based. So sending events in that way wont work.



  • Hi p3c0

    that's brilliant. I just did a short test until now, but changing the parent-child relation like you suggested worked for me.

    Now my StackView looks like that:

    MouseArea {
            id: globalMouseArea
            anchors.fill: parent
            hoverEnabled: true
            //z: 100
            //propagateComposedEvents: true
            preventStealing: true
            property real velocity: 0.0
            property int xStart: 0
            property int xPrev: 0
            property bool tracing: false
    
            onPressed: {
               // mouse.accepted = false
    
                //mouseEventSender.sendMouseEvent(mouse.x, mouse.y, globalMouseArea)
    
                if(mainStackLayout.depth > 1) {
                    xStart = mouse.x
                    xPrev = mouse.x
                    velocity = 0
                    tracing = true
                    mouse.accepted = true
                } else {
                    mouse.accepted = false
                }
            }
            onPositionChanged: {
                print('position changed')
                //this routine sets the velocity which is checked in the onReleased handler
                if (!tracing) {
                    mouse.accepted = false
                    return
                }
                var currVel = (mouse.x-xPrev)
                velocity = (velocity + currVel)/2.0
                xPrev = mouse.x
                mouse.accepted = true
            }
            onReleased: {
                print('released')
                if(!tracing) {
                    mouse.accepted = false
                    return
                }
                print('velocity: ' + velocity)
                tracing = false
                if (velocity > 15 && mouse.x > parent.width * 0.2 ) {
                    print('Detect swipe')
                    mouse.accepted = true
                    mainStackLayout.pop()
                } /*else {
                    mouse.accepted = false
                }*/
            }
    
            StackView {
                id: mainStackLayout
                anchors.fill: parent
                focus: true
    
                //handle the android back button
                Keys.onReleased: {
                    if (event.key === Qt.Key_Back && mainStackLayout.depth > 1) {
                        mainStackLayout.pop()
                        event.accepted = true
                    }
                }
            }
        }
    

    I know the SwipeView, but as far as I understood, it's navigating in a line (like if you swiping through pictures). I am using the StackView for the complete App and, for later implementation steps, the backward swipe should only be enabled for the iOS implementation.

    p3c0 you really helped me a lot, I am really grateful, Thank you!!

    Greetings
    eumel1990


  • Moderators

    @eumel1990 You're Welcome :)



  • Sry, but I have to ask again. What is the reason for this behaviour? Why does the change of the parent/child relation of MouseArea/StackView cause that change of the input event mechanics?

    Can you give an advise for an modern book about QML and/or App development with Qt? I already have "Getting started with Qt Quick" from Paolo Sereno (it's about Qt 5.6) which is like a short tutorial.

    Thanks in advise

    greetings
    eumel1990


  • Moderators

    @eumel1990

    Sry, but I have to ask again. What is the reason for this behaviour? Why does the change of the parent/child relation of MouseArea/StackView cause that change of the input event mechanics?

    It is better to have a the main Item to listen to mouse events and then propagate those to individual items rather than each item grab the events and propagate them to other child or siblings.

    Can you give an advise for an modern book about QML and/or App development with Qt? I already have "Getting started with Qt Quick" from Paolo Sereno (it's about Qt 5.6) which is like a short tutorial.

    Well I guess most of us here have learned Qt/QML through the excellent official documentation so I would first suggest the docs itself. Another one is
    http://qmlbook.github.io/



  • Ok. Thank you for this informations :-)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.