2
votes

I'm trying to implement the following GUI in QML and having trouble understanding how to properly navigate through different pages of the application.

enter image description here

There are 3 buttons in the main menu. When the user clicks on the 'actor' button the UI switches to 'actor view' where the user can toggle between Thumbnail view and List View. When the user clicks on one of the actors the UI switches to Actor Detail view: A view that has a movie view 'nested in it' which lists all the actors movies.

I'm trying to implement this using StackView.

So my StackView lives in the main menu screen (main.qml) when the user clicks one of the buttons the onClicked event pushes the correct view on to the stack.

ActorsView.qml consists of an internal StackView (Most likely a bad idea) and 2 buttons that switch between Thumb and Detail view. This is done by pushing either Thumb or Detail view onto the local stack.

DetailView.qml and ThumbView.qml function exactly the same though look different. Here is where I ran into trouble. I want the main view to be notified when a click event occurs in either Detail or Thumb view. So that it could (based on the event passed information) know what view push onto the main stack. For example when the user clicks on Actor1, the main menu could push 'actor detail view for actor 1' onto the stack.

Sadly I don't know how to 'catch' events that are firing in nested components in the parent element.

I've started playing around with QML and QT just a few weeks ago, and would be happy to hear that my approach is all wrong and that there is a much better way to achieve what I want. Sadly this is the only viable option I found this far.

main.qml:

ApplicationWindow {
    title: qsTr("Hello World")
    width: 1280
    height: 720
    visible: true
    id: mainWindow

    Component{
        id: homeScreen
        Rectangle{
            height: 500
            width: 500
            color:"blue"
            anchors.centerIn: mainWindow

            Text {
                anchors.centerIn: parent
                text: qsTr("Home")
                font.pixelSize: 40
            }
        }
    }


    Component{
        id: actorsView
        ActorsView{
            view: stack
        }
    }


    Component{
        id: moviesView
        MoviesView{
            view: stack
        }
    }

    ColumnLayout{
        RowLayout{
            Layout.fillWidth: true


            Button{
                text: "Back"
                onClicked: stack.pop()

            }


            Button{
                text: "actor view"
                onClicked: stack.push(actorView)
            }

            Button{
                text: "movie view"
                onClicked: stack.push(moviesView)

            }

        }

        StackView {
            id: stack
            initialItem: homeScreen
            Layout.fillHeight: true
            Layout.fillWidth: true

        }

    }
}

ActorsView.qml:

Item {
    property StackView view

    Component {
        id: actorDetailView
        DetailView {
            name: "actorDetailView"
            text: "Actor"
        }
    }

    Component {
        id: actorThumbView
        ThumbView {
            name: "actorThumbView"
            text: "Actor"
        }
    }

    ColumnLayout {

        RowLayout {

            Text {
                text: "Actor view"
                Layout.fillWidth: true
                horizontalAlignment: Text.AlignHCenter
            }

            Button {
                text: "Detail"
                onClicked: internalStack.push(actorDetailView)
            }
            Button {
                text: "Thumb"
                onClicked: internalStack.push(actorThumbView)
            }

            Button {
                text: "back"
                onClicked: internalStack.pop()
            }

            Button {
                text: "depth: " + internalStack.depth

            }
        }

        StackView {
            id: internalStack
            initialItem: {
                console.log(internalStack.depth)
                internalStack.initialItem = actorThumbView
            }
            Layout.fillHeight: true
            Layout.fillWidth: true
        }
    }
}

ThumbView.qml:

Item {
    property string name: "thumbView"
    property string text
    property int counter: 0
    id:thumbView
    signal thumbPressed (string pressedName)

    GridLayout {
        columnSpacing: 10
        rowSpacing: 10
        width: parent.width

        Repeater {
            model: 16
            Rectangle {

                width: 200
                height: 300
                color: "grey"
                Text {
                    id: lable
                    text: text
                    anchors.centerIn: parent
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        var tag = lable.text
                        console.log("You have clicked " + tag)

                        thumbView.thumbPressed(tag)
                    }
                }

                Component.onCompleted: {
                    counter = counter + 1
                    lable.text = text + " " + counter
                }
            }
        }
    }
}
1

1 Answers

3
votes

That's actually a common approach to structure a QML application, so no it's not all bad ! Nested StackViews are a powerful way to manage sub-content of a page, but surely add a level in your app structure. It's made easier by creating your own Page item, redefining the navigation and interaction as you wish.

There's different ways to handle signal in nested components. The easiest: call an identified item up in hierarchy. Local and parent elements in QML are accessible from their id directly, even if those are not in the same QML file. Which allowThis of course has the drawback of inducing coupling between your pages or components and the rest of your application.

ApplicationWindow {
    id: mainWindow

    function pushPage(page) {
        stack.push(page)
    }
    function showActor(id) {
        ...
    }

    // ...
}

In your page simply...

MouseArea {
    onClicked: {
        mainWindow.showActor(index)
    }
}

To achieve something more modular, you can rely StackView currentItem, signals, Connections and Binding elements to name a few, or implement an interface in QML and/or C++ to manage your navigation.

There's definitely a lot of possibilities depending on your goal architecture, trying & learning makes it perfect !