Qt Quick: Code redundancy when creating Transition - qt

My code:
states: [
State {
name: "pressed"; when: mouseArea.pressed
PropertyChanges {
target: foo
prop1: 10
prop2: 10
prop3: 10
}
},
State {
name: "notPressed"; when: !mouseArea.pressed
PropertyChanges {
target: foo
prop1: 1
prop2: 1
prop3: 1
}
}
]
transitions: [
Transition {
to: "*"
NumberAnimation {
target: foo
properties: "prop1,prop2,prop3"
duration: 1000
}
}
]
This works, but requires me to redundantly specify properties: "prop1,prop2,prop3" when the properties to change are already specified in the PropertyChanges elements. Also, I need to redundantly specify target: foo in NumberAnimation when it's already specified in the PropertyChanges elements.
Can this redundancy be avoided? If not, why not?

A property change does not necessary mean it will be animated. And it is not necessary that all property changes will have the same animation either.
I don't see a redundancy here, if the behavior you want was default you wouldn't be able to have fine grained control over what happens. You will be stuck with the same behavior for all property changes, which might suit your particular need, but will actually be quite problematic in all other scenarios.

As #ddriver said, this is needed to have a fine control of what's animated during state changes.
However here if all your properties are changed to the same value like in your example, you could refactor that and bind them to a common property.
Like so :
property int bar: mouseArea.pressed ? 10 : 1
prop1: bar
prop2: bar
prop3: bar
states: [
State {
name: "pressed"; when: mouseArea.pressed
PropertyChanges {
target: foo
bar: 10
}
},
State {
name: "notPressed"; when: !mouseArea.pressed
PropertyChanges {
target: foo
bar: 1
}
}
]
transitions: [
Transition {
to: "*"
NumberAnimation {
target: foo
property: "bar"
duration: 1000
}
}
]
Alternatively if your transition is symmetric (using the same animation going from state A -> state B and going from B -> A), you could use a Behavior to simplify your code :
property int bar: mouseArea.pressed ? 10 : 1
prop1: bar
prop2: bar
prop3: bar
Behavior on bar {
NumberAnimation { duration: 1000 }
}

Related

QML: show only certain Components depending on a State

I have several ui.qml files, let's say A.ui.qml, B.ui.qml, C.ui.qml.
Then I have to create, depending on a State, a window that shows A+B, or B+A, or B+C, etc.
In my main.qml, I've actually written the following:
...
Item {
id: contentArea
state: "state1"
AForm {
id: aForm
}
BForm {
id: bForm
}
states: [
State {
name: "state1"
PropertyChanges { target: aForm; x: 0 }
PropertyChanges { target: bForm; x: 400 }
},
State {
name: "state2"
PropertyChanges { target: aForm; x: 400 }
PropertyChanges { target: bForm; x: 0 }
}
]
...
So, when setting contentArea.state = "state2", A and B forms are swapped.
But this is just an example to see if it works.
Now I need to create all the forms (I guess using Component) and show them only if the state is a certain one.
How to do that?
You replace AForm and BForm by Loaders, and set the source-property of this Loaders to the right file name, or the sourceComponent to the right Component id.
Loader {
id: topLoader
}
Loader {
id: bottomLoader
y: 200
}
states: [
State {
name: "state1"
PropertyChanges { target: topLoader; source: "AForm.ui.qml" }
PropertyChanges { target: bottomLoader; source: "BForm.ui.qml" }
},
...
]
This process will destroy and recreate the instances of the ui.qml-files whenever the state changes.
You can also use e.g. a ListView with snapMode: ListView.SnapToItem, interactive: false, and change the currentItem when the state changes.
You could also create all the objects, and only parent the two you want to show to Items that serve as frames, and the rest is parented to null.
Or you use three or four Loaders of which you only show two, that you shift around as necessary, only loading files that shall be shown. Here you will need to write a bit more logic.

QML transition changes immediately, not according to 'duration'

I have the following QML file. I wan't the rectangle myRect to slide in from the right when the root item is clicked (simplified setup). What actually happens is that myRect appears immediately when the root item is clicked.
I checked the running property on the transition and that seems to be fine. It logs true when I click the root item, and then false after 2 seconds.
Does anyone know why the x property doesn't gradually change?
import QtQuick 2.7
Item{
id: root
MouseArea{
anchors.fill: parent
onClicked: {
myRect.state = "visible"
}
}
Rectangle{
id: myRect
width: root.width
height: root.height
state: "hidden"
color: "yellow"
states: [
State {
name: "hidden"
PropertyChanges{
target: myRect
x: myRect.width
}
},
State {
name: "visible"
PropertyChanges{
target: myRect
x: 0
}
}
]
transitions: [
Transition {
NumberAnimation{
duration: 2000
}
onRunningChanged: {
console.log("Running:", running)
}
}
]
}
}
You have to indicate the property, in your case "x"
NumberAnimation{
duration: 2000
properties: "x"
}

How to manage Focus with States in my custom QML component?

I´ve created a draggable custom Component in order to manage the geometry of individual Quick Controls Components.
The componet has 2 parts:
The "Manipulator" which is a draggable and resizable Rectangle
The inner component which is in the center of the manipulator
Description of the behavior:
No focus: the default state, the Manipulator is invisible
and you can only see the inner component
Focused: When you click the component (or try to drag it) you enter
this state and the Manipulator becomes visible but you can´t access
the inner component. Disabled pressing Escape or clicking outside the component (goes to state 1)
Inner Focus: when you double click on the component The Manipulator
keeps visible and you can still still resize but the the inner
component has the main focus (for example a TextEdit now could be
editable). Disabled pessing Escape (goes to state 2) or clicking outside the component (goes to state 1)
Example of the Component when the Manipulator area is visible
The logic of this component would be similar to the logic of a folder in a Desktop Enviroment (except for resizing) The manipulator would be the folder itself and the inner component is its name.
analogy with folder
Here I post a simplified version of my manipulator, I´m sure it will help to construct an answer, (I tried a lot of variations for several hours, this is one of those not functional attempts)
FocusScope{
id: root
width: 175; height: 25;
focus: true
states: [
State {
name: "noFocus"
when: !manipulator.activeFocus && !innerComp.activeFocus
PropertyChanges {
target: innerComp
enabled: false
}
PropertyChanges {
target: manipulator
visible: false
}
},
State {
name: "focused"
when: manipulator.activeFocus
PropertyChanges {
target: innerComp
enabled: false
}
PropertyChanges {
target: manipulator
visible: true
}
},
State {
name: "innerFocus"
when: innerComp.activeFocus
PropertyChanges {
target: innerComp
enabled: true
}
PropertyChanges {
target: manipulator
visible: true
}
}
]
//visual area of manipulation (drag, redimension, etc)
MouseArea{
id: manipulator
anchors.fill: parent
onDoubleClicked: forceActiveFocus(innerComp) //go to state 3 "innerFocus"
drag.target: manipulator
Keys.onEscapePressed: forceActiveFocus(root) //I don´t think this is the correct to loose focus but I don´t know how to do that
Rectangle {
id: background
anchors.fill: parent
color: "lightsteelblue";
}
}
//Inner Component (TextField for example)
InnerComp {
id: innerComp
anchors.fill: parent
Keys.onEscapePressed: forceActiveFocus(manipulator) //return state 2 "focused"
}
}
I finally found the solution, as someone in a qt forum sugested:
Maybe reverse the dependency, i.e. make the focus depend on the state, not the state depend on the focus?
So I changed my code and now it works!
I post the solution here for those who could be interested in it (as I said this is a simplified version of the real code):
Item {
id: root
width: 175; height: 25;
states: [
State {
name: "noFocus"
PropertyChanges {
target: innerComp; enabled: false
}
PropertyChanges {
target: background; visible: false
}
PropertyChanges {
target: manipulator; focus: true
}
},
State {
name: "focused"
PropertyChanges {
target: innerComp; enabled: false
}
PropertyChanges {
target: background; visible: true
}
PropertyChanges {
target: manipulator; focus: true
}
},
State {
name: "innerFocus"
PropertyChanges {
target: innerComp; enabled: true
}
PropertyChanges {
target: background; visible: true
}
PropertyChanges {
target: manipulator; focus: true
}
}
]
state: "noFocus"
//visual area of manipulation (drag, redimension, etc)
MouseArea{
id: manipulator
anchors.fill: parent
onPressed: {
root.state = "focused"
forceActiveFocus(manipulator) //this prevents loosing focus in some especific situations
}
onDoubleClicked: root.state = "innerFocus"
Keys.onEscapePressed: root.state = "noFocus"
}
Rectangle {
id: background
anchors.fill: parent
color: "lightsteelblue";
}
//Inner Component (TextField for example)
InnerComp {
id: innerComp
anchors.fill: parent
Keys.onEscapePressed: root.state = "focused"
}
}

Behavior on scale often is not working

I would like to have an application, where always when new image is loaded, it is appeared by scaling from 0 size to default size. This behavior is often not working. In this image I am also using animation for bouncing when mouse enters to image. Is it possible, that this two animations are not loving themselves and that is, why scaling up is often not working?
I am using Linux Mint 13, Qt 5.3
Here is my Image element:
Image {
id: pic1
width: appWindow.height*0.4
height: appWindow.height*0.4
smooth: { enabled = true
pic1MouseArea.containsMouse
}
states: [ "mouseIn", "mouseOut" ]
state: "mouseOut"
transitions: [
Transition {
from: "*"
to: "mouseIn"
NumberAnimation {
target: pic1
properties: "scale"
from: 0.95
to: 1
duration: 400
easing.type: Easing.OutBounce
}
}
]
scale: {
status === Image.Ready ? 1 : 0
}
Behavior on scale {
NumberAnimation{
from: 0
to: 1
duration: 1000
easing.type: Easing.OutBounce
}
}
MouseArea{
id: pic1MouseArea
hoverEnabled: true
anchors.fill: parent
onContainsMouseChanged: {
pic1.state = containsMouse ? "mouseIn" : "mouseOut"
}
onClicked: {
MyScript.getRandomFile()
}
}
}
First of all, read this doc. The states property must be defined as list<State>, not as an array of strings. Also, the State element defines some state when a property or set of properties changes from default configuration. In your example states define nothing. Read more about State type.
Finally, here is a small example to help you getting on:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Window 2.2
Window {
width: 600
height: 400
visible: true
Image {
id: img
source: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"
anchors.centerIn: parent
opacity: 1
state: "mouseOut"
states: [
State {
name: "mouseIn"
PropertyChanges { target: img; opacity: 0 }
},
State {
name: "mouseOut"
PropertyChanges { target: img; opacity: 1 }
}
]
transitions: Transition {
PropertyAnimation {
target: img
property: "opacity"
easing.type: Easing.InCirc
duration: 1000
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: img.state = "mouseIn"
onExited: img.state = "mouseOut"
}
}
}
Sure, you can replace transitions with Behavior, if you need exactly this functionality, as shown below:
Behavior on opacity {
PropertyAnimation {
duration: 1000
easing.type: Easing.InCirc
}
}

How to stop the transition animation in QML?

There is window, its layout designed by states and transition. we know that when the state change, the transition-animation will start automatically, but when the transition animation doesn't finished, i change the state, it make troubles. just like slow in reacting; how to fix it? thank you...
it something like this :
Flickable {
id: content
anchors.fill: parent
flickableDirection: Flickable.HorizontalFlick
contentWidth: width * 2
contentHeight: height
clip: true
onFlickStarted: {
if(horizontalVelocity > 0) {
regAndFind.state = "Find"
}
else {
regAndFind.state = "Register"
}
} .......
}
states: [
State {
name: "Register"
PropertyChanges {
target: slider
x: 0
}
PropertyChanges {
target: content
contentX: 0
}
},
State {
name: "Find"
PropertyChanges {
target: slider
x: parent.width / 2
}
PropertyChanges {
target: content
contentX: parent.width
}
}
]
transitions: [
Transition {
NumberAnimation {
target: slider
property: "x"
duration: 600
}
NumberAnimation {
target: content
property: "contentX"
duration: 600
}
}
]
Read about the animation element in Qml.
Before you move to other state, you can call the Animation::stop () function to stop the animation in between. Note that it will stop the animation immediately, and the animation will have no further effect on the property values.

Resources