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.
Related
I have a pages flow like this:
That i Implemented it with a "Loader" and a "StackView", "splash" and "authorize" load in "Loader", and "Dashboard" and other pages will load in "StackView", "StackView" is not nasted (load) in "Loader", Just i invisable it befor load "Dashboard" like:
Loader {
id:loader
anchors.fill: parent
visible: true // till authorization
source: "qrc:/pages/Splash.qml"
}
StackView {
id: stack_view
anchors.fill: parent
visible: false // till authorization done and load dashboard
}
StateGroup {
id: state_group
state: "SPLASH"
states: [
State {
name: "SPLASH"
PropertyChanges {target: loader; source: "qrc:/pages/Splash.qml"}
},
State {
name: "AOUTHORIZE"
PropertyChanges {target: loader; source: "qrc:/pages/Login.qml"}
},
State {
name: "STACKVIEW"
PropertyChanges {target: loader; visible: false}
PropertyChanges {target: stack_view; visible: true}
}
]
}
As diagram shows "Splash" and "Authorize" load once, "Dashboard" and other pages may load several times, I want to know the way i used "Loader" and "StackView" is good? or how and what components is beter to use?
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"
}
I have an object that I want to move from it's previously set position every time that particular state is set. I've tried making a separate property called xPos to get around the binding loop error which is set by the object's new position of x after the state is set, then entering a default state just to be able to switch back to that specific state again since calling the same state does nothing but it doesn't seem to work.
Here is a snippet of my code:
property int xPos: 0
states: [
State {
name: "nextStep"
PropertyChanges {
target: progressBar_Id
x: -1*(progressBar_Id.step.width) + xPos
}
},
State {
name: "previousStep"
PropertyChanges {
target: progressBar_Id
x: progressBar_Id.step.width + xPos
}
},
State {
name: "default"
PropertyChanges {
target: progressBar_Id
x: xPos
}
}
]
transitions: [
Transition {
from: "*"
to: "nextStep"
NumberAnimation {properties: "x"; easing.type: Easing.Linear; duration: 1000}
onRunningChanged: {
if(!running) {
xPos = progressBar_Id.x;
console.info("xPos = " + xPos);
state = "default";
}
}
},
Transition {
from: "*"
to: "previousStep"
NumberAnimation {properties: "x"; easing.type: Easing.Linear; duration: 1000}
onRunningChanged: {
if(!running) {
xPos = progressBar_Id.x;
console.info("xPos = " + xPos);
state = "default";
}
}
}
]
xPos seems to get set the first time from the console output but never applies the new xCoord to the object.
Explicitly specify the id of the item on which you wish to set the state, e.g:
idOfComponent.state = "default"
All QML Items and derivatives have a property called state, so the code needs to be more specific.
Actually came up with a much better alternative using a ListView.
ListView {
id: listView_Id
width: contentWidth
height: bubbleSize
anchors.centerIn: parent
layoutDirection: Qt.RightToLeft
orientation: ListView.Horizontal
spacing: 0
delegate: Item {
width: bubbleSize
height: width
ProgressBubble {
stateText: stateNumber
currentState: state
}
}
model: ListModel { id: progressModel_Id }
}
Another qml file:
progressModel.insert(0, {stateNumber: number++, state: "current"});
But I ran into another problem, is there anyway to change a state within a delegate after it's been added to the ListView?
I've already tried progressModel.set and progressModel.setProperty but doesn't seem to work.
state is a property for qml types, so when you are assigning "state" to "currentState" for "ProgressBubble", its taking the value of state in which " ProgressBubble" is currently present.
Rename "state" to something else like "presentState" and then try.
Moreover id of ListView model(progressModel_Id) and the one used to insert model elements(progressModel) in different file are different, both of them must refer to same id.
After these changes, you can try set property of model. Sample code snippet:
import QtQuick 2.0
Rectangle {
id: root
width: 360
height: 360
property int number: 1
ListView {
id: listView_Id
width: 100 //contentWidth // this creates property binding issue
height: 100 // bubbleSize // I was not sure about this value
anchors.centerIn: parent
layoutDirection: Qt.RightToLeft
orientation: ListView.Horizontal
spacing: 0
delegate: Item {
width: 100 // bubbleSize // I was not sure about this value
height: width
ProgressBubble {
state: "default"
stateText: stateNumber
currentState: presentState
MouseArea {
anchors.fill: parent
onClicked: {
progressModel_Id.set(index,{stateNumber: stateNumber, presentState: "not current"})
}
}
}
}
model: ListModel { id: progressModel_Id }
}
Component.onCompleted:
{
progressModel_Id.insert(0, {stateNumber: number++, presentState: "current"});
}
}
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 }
}
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"
}
}