0

I want to develop a qml application that starts with the main window and three buttons are available in the main window. By pressing each button a new page(Layout) should show up, but after returning to the main window, the state of the previous page should not change. I have tried ListView but it did not work, because it destroys the Item after pop(). Here is my Code(PagesOne.qml is a sample for three pages that I want to open):

main.qml

    ApplicationWindow {
    id: root
    visible: true
    width: 8 * Screen.width / 10
    height: 8 * Screen.height / 10
    color: "#154c79"
    // Keep the window on the given x,y on starting
    Component.onCompleted: {
        x: Screen.width / 10
        y: Screen.height / 10
    }

    minimumWidth: 700
    minimumHeight: 400


    // Current Working Layout
    Component {
        id: pageOne
        PageOne{}
    }

    // Empty Layout
    Component {
        id: pageTwo
        PageTwo{}
    }

    // Empty Layout
    Component {
        id: pageThree
        PageThree{}
    }

    property variant items: [pageOne.createObject(), pageTwo.createObject(), pageThree.createObject()]

    StackView {
        id: stack
        anchors.fill: parent
        // initialItem: pageOne
        Component.onCompleted: {
            stack.push(items[2], {"destroyOnPop": false})
            stack.push(items[1], {"destroyOnPop": false})
            stack.push(items[0], {"destroyOnPop": false})
        }
    }

    // change layout on call
    function load_layout(layout){

        console.log("#############",  stack.depth, "#############")

        switch (layout){
            case 'one':
                stack.pop(0, {"destroyOnPop": false})
                break;
            case 'two':
                stack.pop(1, {"destroyOnPop": false})
                break;
            case 'three':
                stack.pop(2, {"destroyOnPop": false})
                break;
            default:
                stack.pop(0, {"destroyOnPop": false})
        }
    }
}

PageOne.qml:

    Item{
    id: root
    // anchors.fill: parent
    // Empty rect
    Rectangle{
        color: "#03fc88"
        anchors.fill: parent

        TextField {
            anchors.right: parent.right
            anchors.top: parent.top
            width: 120
            height: 60
            placeholderText: qsTr("Enter: ")
        }

        Label{
            anchors.centerIn: parent
            text:  "Page two"
        }

        // return button
        RoundButton {
            id: btnBack
            text: "\u003C"
            onClicked: {
                if(text === "\u003C"){
                    load_layout('one')
                    }
                }
            }

    }

}

Is there any suggestion that helps me?

  • According to your code you mean `page` or `frame`, not `window`. what does `the state of the previous window should not change` mean? please clarify the question. Also check [this note](https://doc.qt.io/qt-6/qml-qtquick-controls2-stackview.html#push-method) from the doc: _Only items that StackView created itself (from a Component or url) will be destroyed when popped_ – folibis Nov 15 '22 at 08:19
  • @folibis By state of window (page) I mean if there is a textfield in the `pageTwo` and user type something, after going back to main window and selecting `pageTwo` again the textField keeps the data. – siamak mirifar Nov 15 '22 at 09:00
  • I guess if you will push an item, not component it will keep the state i.e. will not be destroyed by QtQuick. – folibis Nov 15 '22 at 09:03
  • Another option is using `replace()` instead of `push()` – folibis Nov 15 '22 at 09:41
  • @folibis Because of **Only items that StackView created itself (from a Component or URL) will be destroyed when popped** I have `createObject()` from my component and then push it to StackView. I tried also to use Item instead of Component (I just replace it) and still StackView destroy it. I am not sure now if StackView is the correct choice in my case for handling multiple pages.`replace()` is the same it will destroy the previous state of the window. As I said I want all the information in windows to stay as the user change between pages. – siamak mirifar Nov 15 '22 at 10:36

2 Answers2

0

A simple example:

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQuick.Controls 2.15
    import QtQuick.Layouts 1.0
    
    Window {
        height: 800
        width: 600
        visible: true
        title: qsTr("Stack test")
    
        ColumnLayout {
            anchors.fill: parent
            spacing: 0
            StackView {
                id: stack
                Layout.fillHeight: true
                Layout.fillWidth: true
                initialItem: screen1
                Rectangle {
                    id: screen1
                    color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
                }
                Rectangle {
                    id: screen2
                    color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
                }
                Rectangle {
                    id: screen3
                    color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
                }
            }
            RowLayout {
                id: bar
                Layout.fillWidth: true
                Layout.preferredHeight: 50
                spacing: 0
                Button {
                    Layout.preferredWidth: 300
                    Layout.preferredHeight: 50
                    text: "Change color"
                    onClicked: {
                        stack.currentItem.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
                    }
                }
    
                Button {
                    Layout.preferredWidth: 300
                    Layout.preferredHeight: 50
                    text: "Next"
                    onClicked: {
                        switch(stack.currentItem)
                        {
                        case screen1:
                            stack.replace(screen2)
                            break;
                        case screen2:
                            stack.replace(screen3)
                            break;
                        case screen3:
                            stack.replace(screen1)
                            break;
                        }
                    }
                }
            }
        }
    }
folibis
  • 12,048
  • 6
  • 54
  • 97
  • this code works for me and it keeps the page's state. I will try to load my **PageOne.qml* file in code to see how it works in that manner. Thank you. – siamak mirifar Nov 15 '22 at 10:50
  • It works perfectly when I load (read) another .qml file and the state of the pages did not change. Just I had some anchors problem which was not a big deal. – siamak mirifar Nov 15 '22 at 14:09
0

Because, in your main page, you use Component, by definition, QML can create and destroy as many instances of the page corresponding to push/pull on the stack. So, the feature you described is as per design.

If you want to maintain an instance that is persistent, do not use Component. Instead, declare an instance of the pages but keep them invisible.

In the following example, I declare Pages each with a different TextField. As you switch between the pages, you can resume editing the TextField on that page exactly where you left it.

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    StackView {
        id: stackView
        anchors.fill: parent
        initialItem: "MenuPage.qml"
    }

    PageOne {
        id: pageOne
        visible: false
    }
    PageTwo {
        id: pageTwo
        visible: false
    }
    PageThree {
        id: pageThree
        visible: false
    }
}

// MenuPage.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    header: Label { text: "MainPage" }
    ColumnLayout {
        Button {
            text: qsTr("Page One")
            onClicked: stackView.push(pageOne)
        }
        Button {
            text: qsTr("Page Two")
            onClicked: stackView.push(pageTwo)
        }
        Button {
            text: qsTr("Page Three")
            onClicked: stackView.push(pageThree)
        }
    }
}

// PageOne.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    header: Label { text: "Page One" }
    ColumnLayout {
        TextField {
            text: "sheep"
        }
        Button {
            text: qsTr("Back")
            onClicked: stackView.pop()
        }
    }
}

// PageTwo.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    header: Label { text: "Page Two" }
    ColumnLayout {
        TextField {
            text: "magic"
        }
        Button {
            text: qsTr("Back")
            onClicked: stackView.pop()
        }
    }
}

// PageThree.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
    header: Label { text: "Page Three" }
    ColumnLayout {
        TextField {
            text: "xyzzy"
        }
        Button {
            text: qsTr("Back")
            onClicked: stackView.pop()
        }
    }
}

You can Try it Online!

Stephen Quan
  • 21,481
  • 4
  • 88
  • 75
  • yes you are right. I found the problem. I used this approach in my several tries as well, but as my anchors was not correct the pages did not show up.So I though something is wrong with using StackView, but the problem was anchors as well... . – siamak mirifar Nov 15 '22 at 21:14
  • StackView should be positioned properly. The pages will inherit the StackView geometry. In my example, I use `anchors.fill: parent` on the StackView. – Stephen Quan Nov 15 '22 at 21:25