StackView - Popped screen gets destroyed after previous screen is made visible - qt

StackView has screen 1 as initial item. Screen 2 is pushed. Screen 2 is now popped.
Screen 2 gets destroyed after screen 1 is made visible.
Is this expected behavior? Why does it work this way?
I expected the popped screen to get destroyed before the previous screen is made visible.
Code to demonstrate the same:
StackView {
id: stack
initialItem: mainView1
anchors.fill: parent
}
Component {
id: mainView1
Text {
text: "1"
onVisibleChanged: console.log(text, visible)
Component.onCompleted: console.log(text, "completed")
Component.onDestruction: console.log(text, "destroyed")
}
}
Component {
id: mainView2
Text {
text: "2"
onVisibleChanged: console.log(text, visible)
Component.onCompleted: console.log(text, "completed")
Component.onDestruction: console.log(text, "destroyed")
}
}
Console output:
qml: 1 completed
qml: Push
qml: 2 completed
qml: 1 false
qml: Pop
qml: 1 true
qml: 2 false
qml: 2 destroyed
Motivation behind the question:
I need to control a C++ timer from QML. Timer needs to be started when widget is created/visible and stopped when widget is destroyed/hidden.
The above depicted behavior of StackView prevents me from achieving it.
When I pop screen 2, screen 1 is first made visible, so my timer starts. Screen 2 then gets destroyed, as a result, my timer is stopped.

The reason why this occurs is due to StackView animation. Both views must exist during the default push and pop animations as they both appear on screen at the same time.
You likely can solve your problem using the StackView attached signals:
https://doc.qt.io/qt-5/qml-qtquick-controls2-stackview.html#attached-signals
These are signals that fire on your views regarding their individual status as the StackView pushes and pops.
If you haven't used attached signals before, here's some documentation on how they work:
https://doc.qt.io/qt-5/qtqml-syntax-objectattributes.html#attached-properties-and-attached-signal-handlers

Related

Stackview Push and Pop destroys and creates a new page

I have many pages and when a button is clicked I make transitions between these pages with a StackView, push and pop. And in these pages when corresponding buttons are clicked I make these buttons red. However, when I pop and re-opened the same page with a push, button is no longer red. So that makes me think that pop and push destroys and creates a new page which is the opposite what is written in docs:
This means that any item pushed onto a StackView will never be
destroyed by the StackView; only items that StackView creates from
Components or URLs are destroyed by the StackView.
Here is the code for stackview:
Window{
id:main
property bool isAbsOffRoad: false
StackView{
id:contentFrame
initialItem:Qt.resolvedUrl("qrc:/MainPage.qml")
Connections{
target:contentFrame.currentItem
onBackButtonPressed:{
contentFrame.pop() }
}}
And then here is how I push:
Item {
id:backgroundItem
LeftButtons{
id:buttonSettings
MultiPointTouchArea{
onPressed{
contentFrame.push("qrc:/SettingsPage.qml")}
}}
I cannot see a reason why the page doesn't preserves it's state when popped and pushed back. What might be the reason?
Another question is: I get a
QML Connections: Cannot assign to non-existent property
"onBackButtonPressed".
However, back buttons work. Why I get that error?
The documentation you quote gives you the answer.
This means that any item pushed onto a StackView will never be destroyed by the StackView; only items that StackView creates from Components ->or URLs<- are destroyed by the StackView.
If a StackView creates an item from a URL, it will have ownership of it, and therefore feel free to destroy it.
Your code shows this:
initialItem:Qt.resolvedUrl("qrc:/MainPage.qml")
So you're giving the StackView a URL to your QML. If you don't want it to do that, try doing something like this instead:
initialItem: MainPage {}
That way, the StackView will be given a fully constructed item, and it won't try to destroy it.
For your second question, I'm guessing that your MainPage.qml does not define that signal. You could create that signal in MainPage just to remove the warning, or you can try adding the ignoreUnknownSignals property to your Connections object.
UPDATE:
You can still use push and pop. You just have to provide a created instance of your item, not just the item type. You could try something like this, for example:
component SomePage: Rectangle {
signal clicked()
MouseArea {
anchors.fill: parent
onClicked: parent.clicked()
}
Component.onCompleted: {
console.log("Created: " + color);
}
Component.onDestruction: {
console.log("Destroyed: " + color);
}
}
SomePage {
id: bluePage
color: "blue"
visible: false
onClicked: contentFrame.push(redPage)
}
SomePage {
id: redPage
color: "red"
visible: false
onClicked: contentFrame.pop();
}
StackView {
id: contentFrame
anchors.fill: parent
initialItem: bluePage
}

Stackview calling slots for previous screen

I am implementing stackview in my application.
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: showfooter.currentIndex
DashboardListView{
id:dashboard
}
Settings{
id:setting
}
Cart{
id:cart
}
}
StackView {
id: stackView
initialItem: Pane {
id: pane
}
}
When i am loading some other screen(like SightDescription.qml) from DashboardListView using push method and cliking somewhere on that screen its calling slots for DashboardListView. DashboardListView Screen controls are getting onclick signal. Is there any setting related to stack view that I need to do, I read stackview's documentation but did not find anything to restrict this behavior.
It seems like the Pane is usually intercepting the mouse events, so the lower Items cannot receive them.
When you push the new item on the StackView the Pane becomes visible: false and therefore does not care for input anymore. If the new Item does not handle the mouse events, they will propagate to the lower Item.
To prevent that, you have various options:
Make sure that all Items pushed on the StackView will handle mouse events, e.g. by making a Pane or a MouseArea the root item.
Place a MouseArea directly below the StackView that is only enabled when there are Items on the StackView
Some more... e.g. installing EventFilters in C++ e.t.c. but I think 1 and 2 should be suffice and be easy to implement.

QML: Buttons of invisible screens still active?

I am developing a qml application for a touchscreen. The app is organized into different screens
e.g. in my main.qml looks like this
ApplicationWindow {
visible: true
width: 1024
height: 600
Screen1 {
id: screen1
visible: true
}
Screen2 {
id: screen2
visible: false
}
I switch between these screens, through buttons, e.g. on Screen1 there would be a mouseArea which does
onClicked: {
screen1.visible=false
screen2.visible=true
}
I now wonder whether this is the way to do it, as I observe a strange behaviour: I have a button on screen2 which is at the same position as a button on screen1 (which triggers Qt.quit(). The button on screen2 most of the time does what it should. However, if I click several times on it, sometimes the quit button on screen1 is triggered (I explicitly checked with a log.console writeout), although this screen is invisible.
Why does this happen and how can I avoid this behaviour?

Catching mouse events from QML

I want to create a QML item which disappears when the mouse moves outside of it. Here is my code:
Item {
id: disappearing_element
ListView { ... }
MouseArea {
id: collapser
anchors.fill: parent
propagateComposedEvents: true
hoverEnabled: true
onExited: {
disappearing_element.visible = false
}
}
}
It works well, but MouseArea propagates events like onClicked() onDoubleClicked() only (as said in Qt docs).
Is there a way to notify disappearing_element's childrens about mouse enter and mouse exit events (without using a Popup element)?
I think this is one of the common needs when developing QtQuick apps. One solution we currently use quite often is to add MouseArea in each of the children that need check mouse containment, and emit signals (and catch these signals in your main item) when the mouse enters or exits.
Things go a bit complicated when the children items also need such mechanism to manage their children. However, for common usage, this approach is enough for us right now.

QML: rendering problems after showMinimized()

Encountered the following problem with rendering in QML. I have implemented the 'minimize window' button:
Image {
source: "minimize.png"
scale: mouse.pressed ? 0.8 : 1.0
smooth: mouse.pressed
MouseArea {
id: mouse
anchors.fill: parent
anchors.margins: -5
onClicked: {
console.log("MinimizeButton clicked");
viewer.showMinimized();
}
}
}
where 'viewer' is the object inherited from QDeclarativeView which represents the main application window.
The button shrinks when user clicks the mouse onto it and window has been minimized. But button stays shrinked when window is restored.
Tried to add the timer which prints 'mouse.pressed' every 1 sec:
Timer {
repeat: true
interval: 1000
running: true
onTriggered: {
console.log("mouse.pressed =",mouse.pressed);
}
}
It always prints mouse not pressed. But button is scaled to 0.8, not 1.0.
"viewer.showMinimized()" appears to be guilty: button is rendered OK if it is commented out.
Any suggestions to solve the problem?

Resources