Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set currentIndex of SwipeView to currentIndex of TabBar "by reference" after going to specific page?

I'm getting started with QtQuick Controls 2.0. I have experience with C++ and a small amount of experience with Qt, but I have not worked with QML before.

I have a TabBar and a SwipeView that are linked to each other. What I mean by this is that when you select a page on the TabBar, the SwipeView goes to that page. When you swipe to a page from the SwipeView, the TabBar updates itself to reflect that.

As a learning exercise, I decided to create a button that would send the user to the second page. The issue is that I can't seem to find a way to do so without messing up the link between the TabBar and the SwipeView.

The following code is the best I've come up with. It correctly goes to the second page, and when I change the current page with the TabBar the SwipeView still updates. However, swiping to a new page no longer updates the TabBar. It appears that setting tabBar.currentIndex to swipeView.currentIndex only has the effect of setting by reference when done with the colon to initialize. Doing so with an equals sign sets by value. How can I move to a specific page while still maintaining the invariant that swipeView.currentIndex == tabBar.currentIndex?

// main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    SwipeView {
        id: swipeView
        anchors.fill: parent
        currentIndex: tabBar.currentIndex

        Page {
            Button {
                text: qsTr("Continue to Page 2")
                onClicked: {
                    tabBar.currentIndex = 1;
                    // this next line merely sets tabBar.currentIndex to 1
                    tabBar.currentIndex = swipeView.currentIndex
                }
                anchors.centerIn: parent
                width: text.implicitWidth
                height: text.implicitHeight
            }
        }

        Page {
            Label {
                text: qsTr("Second page")
                anchors.centerIn: parent
            }
        }
    }

    footer: TabBar {
        id: tabBar
        currentIndex: swipeView.currentIndex
        TabButton {
            text: qsTr("First")
        }
        TabButton {
            text: qsTr( "Second")
        }
    }
}

The C++ code is simply the default that Qt Creator provided for me:

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));

    return app.exec();
}
like image 736
Jon McClung Avatar asked Apr 24 '17 00:04

Jon McClung


2 Answers

In order to move to the next or the previous page without breaking the currentIndex bindings, you can call incrementCurrentIndex() or decrementCurrentIndex(), respectively. These methods were introduced in Qt Quick Controls 2.1 in Qt 5.8.

Given that the currentIndex setter aka. QQuickContainer::setCurrentIndex() is a slot, you can also call setCurrentIndex() from QML to jump to an arbitrary page. This will not break the existing bindings either.

Button {
    onClicked: tabBar.setCurrentIndex(1)
}
like image 98
jpnurmi Avatar answered Oct 13 '22 17:10

jpnurmi


You could be looking for Signals. From the Qt documentation:

Property Change Signal Handlers

A signal is automatically emitted when the value of a QML property changes. This type of signal is a property change signal and signal handlers for these signals are written in the form on<Property>Changed where <Property> is the name of the property, with the first letter capitalized.

Thus, for your example:

SwipeView {
    id: swipeView
    ...
    currentIndex: tabBar.currentIndex
    onCurrentIndexChanged: {
        tabBar.currentIndex = currentIndex
    }
...
footer: TabBar {
    id: tabBar
    currentIndex: swipeView.currentIndex
    onCurrentIndexChanged: {
        swipeView.currentIndex = currentIndex
    }
    ...

And that way you can simply set the currentIndex of either and rest assured that they will remain "linked" due to the signal handlers.

Edit:

As pointed out by jpnurmi, you can also use Qt.binding like so:

onClicked: {
    swipeView.currentIndex = 1;
    swipeView.currentIndex = Qt.binding(function() {return tabBar.currentIndex})
}

This will restore the binding after the static value has been assigned.

like image 2
Jon McClung Avatar answered Oct 13 '22 16:10

Jon McClung