TabView without lazy loading



  • I have items in a TabView that the parent needs to interact with (e.g. forwarding text from a TextField to C++ for processing then back to QML for displaying in a TextArea) and also update when not the current tab. I understand that the Tab item inherits from Loader and therefore it's children are sort of "sandboxed". Unfortunately, this hinders precisely what I am trying to accomplish. In the QWidget world, all of the items in the TabWidget always exist. Is TabView the best option or should I create my own QTabWidget-like QML component that switches the visibility of items and creates the illusion of "Tabs".


  • Moderators

    @ZergedU Hi! I find this really annoying, too. After trying to work around it for some time I eventually implemented my own tabview component.


  • Moderators

    Hi @ZergedU,
    It can be done from C++ but it involves some child items processing which is fine IMO.

    I understand that the Tab item inherits from Loader and therefore it's children are sort of "sandboxed".

    Not exactly. These can be extracted easily.

    Unfortunately, this hinders precisely what I am trying to accomplish. In the QWidget world, all of the items in the TabWidget always exist.

    You have the property active for Tab which forces Item to exist on load.

    Here' a minimal example:

    main.qml

    Item {
        width: 200
        height: 200
        TabView {
            objectName: "tabview"
            anchors.fill: parent
            Tab {
                active: true
                title: "Tab1"
                TextField {
                    text: "TextField-One"
                }
            }
            Tab {
                active: true
                title: "Tab2"
                TextField {
                    text: "TextField-Two"
                }
            }
        }
    }
    

    main.cpp
    This is where you find the TabView by objectname. ProxyObject is just simple class to which we pass the found TabView object for further processing. Note that we have used contentItem property as you can see here that all Tabs are actually parented to it. So,

    QQuickItem *item = view.rootObject()->findChild<QQuickItem*>("tabview");
    QQuickItem *contentItem = qvariant_cast<QQuickItem*>(item->property("contentItem"));
    ProxyObject *obj = new ProxyObject(contentItem);
    

    ProxyObject.cpp
    This is where we iterate and get the Tab and its child contents for eg. TextField objects. Once we get hold of TextField object, we can connect its signals for eg. accepted() in this case. The once its correponding slot is invoked we can easily find who the sender was and then further extract its properties.

    ProxyObject::ProxyObject(QQuickItem *parent) : QObject(parent) {
        m_item = parent;
        foreach (QQuickItem *tab, m_item->childItems()) {
            foreach (QQuickItem *tabitem, tab->childItems()) {
                connect(tabitem, SIGNAL(accepted()), this, SLOT(onAccepted()));
            }
        }
    }
    
    void ProxyObject::onAccepted()
    {
        QQuickItem *item = qobject_cast<QQuickItem*>(sender());
        qDebug() << item->property("text").toString();
    }
    

    Hope this helps you in some way :)



  • Thanks for the suggestions everyone! I was really hoping to avoiding having to get involved with C++ just for creating the type of tabview I was looking for. I actually came up with what I think is a pretty neat solution. I used a stackview that I let be only 1 layer deep and disabled the transition animation. Then I connected a custom tab bar to push/pop items onto the stackview. These items to be pushed/popped I stored in a list, meaning they always exist and lazy loading is not used. Below is my attempt to include the code I used. Thanks again everyone!

    // Custom components go here, using rectangles just as an example
    property list<Item> stackViewItems: [
            Rectangle{
                color: "blue"
            },
            Rectangle{
                color: "red"
            },
            Rectangle {
                color: "green"
            }
        ]
    
        RowLayout {
    
            anchors.fill: parent
    
            // Custom component that emits the "tab" index when clicked
            NavigationMenu {
                id: navigationMenu
    
                // Layout
                Layout.preferredWidth: 100
                Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
    
                // Slots
                onClicked: {
                    stackView.pop()
                    stackView.pushHelper(index)
                }
            }
    
            StackView {
                id: stackView
    
                // Layout
                Layout.fillWidth: true
                Layout.fillHeight: true
    
                // Initial Item
                initialItem: stackViewItems[0]
    
                // Helper
                function pushHelper(index) {
                    if (index >= 0 && index < stackViewItems.length) {
                        push(stackViewItems[index])
                    }
                }
    
                // Transitions
                delegate: StackViewDelegate {
                    pushTransition: StackViewTransition {}
                    popTransition: StackViewTransition {}
                    replaceTransition: StackViewTransition {}
                }
            }
        }
    

Log in to reply
 

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