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

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"
}
}

Related

QT QML error : can't apply a state change as part of a state definition

I'am struggling with the issue indicated in the title.
When trying to switch state after a custom signal made from C++ was triggered, i have this error coming from my QML script : can't apply a state change as part of a state definition.
I have barely found information about that same error on internet and i have struggling for days trying to fix this without success. Regarding the error, it's assumed that i'm trying to change state in a state definition but it's definitely not the case.
Here is a sample of my code:
Connections {
target: timer
function onTriggered() {rectangle.state = "welcomeState"}
}
Connections {
target: testBack
function onPrintDataReady() {rectangle.state = "printState"}
}
Connections {
target: boutonOui
function onPressed() {rectangle.state = "endState";testBack.printFile() }
}
Connections {
target: boutonNon
function onPressed() {rectangle.state = "endState"}
}
And the states are defined here:
states: [
State {
name: "welcomeState"
PropertyChanges {
target: logo_2
visible: false
}
PropertyChanges {
target: messageFin
visible: false
}
PropertyChanges {
target: boutonOui
visible: false
}
PropertyChanges {
target: boutonNon
visible: false
}
PropertyChanges {
target: impressionTicket
visible: false
}
},
State {
name: "printState"
PropertyChanges {
target: boutonOui
visible: true
text: qsTr("OUI")
}
PropertyChanges {
target: boutonNon
visible: true
text: "NON"
}
PropertyChanges {
target: logo_1
visible: false
}
PropertyChanges {
target: impressionTicket
visible: true
}
PropertyChanges {
target: logo_2
visible: true
}
PropertyChanges {
target: messageFin
visible: false
text: qsTr("Merci de votre visite. A bientôt.")
}
},
State {
name: "endState"
PropertyChanges {
target: logo_1
visible: false
}
PropertyChanges {
target: logo_2
visible: true
}
PropertyChanges {
target: messageFin
visible: true
text: qsTr("Merci de votre visite. A bientôt.")
anchors.verticalCenterOffset: 1
anchors.horizontalCenterOffset: 1
}
PropertyChanges {
target: boutonNon
visible: false
text: qsTr("NON")
}
PropertyChanges {
target: impressionTicket
visible: false
}
PropertyChanges {
target: boutonOui
visible: false
}
PropertyChanges {
target: timer
running: true
triggeredOnStart: false
interval: 6000
}
}
]
I can't switch to the state "printState" for the target "testBack". "testBack" is a C++ object made accessible in QML imported from C++ and so, onPrintDataReady() is a custom signal.
And the first state is "welcomeState":
Rectangle {
id: rectangle
width: 800
height: Constants.height
color: "#eb5a2d"
state: "welcomeState"
property alias messageFin: messageFin
property alias logo_1: logo_1
property bool property0: true
That transition is the only one that is not working among the 4 transition that are indicated.
Any help would be appreciated.
I made sure that the onPrintDataReady() was fired by changing the color of the rectangle instead of the state and it worked.
I tried to made the connection directly in the item "testBack" but the issue was still here.
I set the variable STATECHANGE_DEBUG to 1 but didn't give me any interesting information for my problem.
Tried the other transitions and it's all working except the one that is in testBack
And now i'm running out of idea.
Finally found a turnaroud.
Instead of going fromwelcomeState to printState through the onPrintDataReady signal, i activated a timer when printerDataReady was triggered and when receiving the trigger signal from this timer, i did the transition from welcomeState to printState so this is what i have now :
Connections {
target: timer3
function onTriggered() {testBack.waitForPrint()}
}
Connections {
target: timer2
function onTriggered() {rectangle.state = "welcomeState"}
}
Connections {
target: testBack
function onPrintDataReadyChanged() {timer.start()}
}
Connections {
target: timer
function onTriggered() {rectangle.state = "printState"}
}
Connections {
target: boutonOui
function onPressed() {rectangle.state = "endState";testBack.printFile()}
}
Connections {
target: boutonNon
function onPressed() {rectangle.state = "endState"}
}

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"
}

Drag after long press

I want to drag my custom buttons QML after a long press over them. I've implemented that behaviour, however the problem is that after enabling drag, I need to press button once again to actually start dragging. How should I implement this mechanism if I want to move buttons without releasing after long press?
Here is my button code (onReleased and onLongPressed are my own signals):
ButtonWidget.SoftButtonUI
{
id:softButtonDelegate2
x:500
y:300
labelText: "button"
iconImageSource: path
isGrayedOut: false
Drag.active: dragArea2.drag.active
Drag.hotSpot.x: 10
Drag.hotSpot.y: 10
onReleased:
{
console.log("onClicked")
}
onLongPressed:
{
console.log("onLongPressed")
dragArea2.enabled = true
}
MouseArea {
id: dragArea2
enabled: false
anchors.fill: parent
drag.target: parent
onReleased: parent.Drag.drop()
onClicked: {
console.log("MouseArea onClicked")
}
onPressAndHold: {
console.log("MouseArea onPressAndHold")
}
}
}
Any idea?
Generally speaking you can connect different signals and concatenate operations as discussed in this page. You should have a look at it since it is full of nice and useful information.
However, when it comes to mouse events, an interesting approach to events concatenation is given by MouseEvents acceptation. Documentation says about MouseEvent::accepted:
Setting accepted to true prevents the mouse event from being
propagated to items below this item. Generally, if the item acts on
the mouse event then it should be accepted so that items lower in the
stacking order do not also respond to the same event.
In this case we can take the opposite approach by not accepting the event. This way the pressed event can be used to both activate the drag and actually perform it. Then the MouseEvent can be accepted (implicitly) in the release event, occurring at the end of the drag.
Here is a simple example following this approach. As the mouse is pressed and hold the drag.target is set and drag can start, whereas when the mouse is released the drag.target is removed, removing the dragging behaviour. To test it, just press and hold the mouse over the rectangle and when it changes color just drag it.
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
width: 300
height: 300
visible: true
Rectangle {
id: item
border.width: 2
x: 100
y: 100
width: 100
height: 100
state: "BASE"
states: [
State {
name: "BASE"
PropertyChanges { target: mouseArea; drag.target: undefined}
PropertyChanges { target: item; color: "steelblue"}
},
State {
name: "DRAGGABLE"
PropertyChanges { target: mouseArea; drag.target: item}
PropertyChanges { target: item; color: "darkblue"}
}
]
MouseArea {
id: mouseArea
anchors.fill: parent
drag{
// target: NOT SET HERE
minimumX: 0
minimumY: 0
maximumX: parent.parent.width - parent.width
maximumY: parent.parent.height - parent.height
smoothed: true
}
onPressAndHold: {
item.state = "DRAGGABLE"
mouse.accepted = false // mouse event is USED but NOT CONSUMED...
}
onReleased: {
item.state = "BASE" // mouse event acceptation occurs here!
}
}
}
}
This simple approach should work perfectly also with your custom signals.

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.

QML zoom from a clicked rectangle to another UI element

I have 9:9 matrix of Rectangle elements on the main QML form with Repeater. What I want to implement is if user clicks on one of rectangles, it zooms to TextEdit widget which on Esc press zooms back.
Is it possible with QML?
If yes, how am I supposed to turn Rectangle to TextEdit and zoom this TextEdit to fill the parent?
Just starting to work with QML and can't quite get an answer from http://doc.qt.nokia.com/4.7-snapshot/qdeclarativeanimation.html yet.
Thank you.
1) Sure thing! This is more or less what QML is made for.
2) This is an example of how you can do what you want (not the only way to do it):
Rectangle {
id: parentRect
width: 500; height: 500
// Every item in the grid should look like this
Rectangle {
id: singleItem
color: "red"
state: "closed"
// Hidden text input, shown when user clicks
TextInput {
id: textInput
anchors.fill: parent
text: "Input here"
cursorVisible: true
}
// MouseArea that will catch the user click
MouseArea {
anchors.fill: parent
onClicked: singleItem.state = "open"
}
// Item states
states: [
State {
name: "closed"
PropertyChanges {target: singleItem; width: 25; height: 25}
PropertyChanges {target: textInput; opacity: 0}
},
State {
name: "open"
PropertyChanges {target: singleItem; width: parentRect.width; height: parentRect.height}
PropertyChanges {target: textInput; opacity: 1; focus: true}
}
]
// Transitions between states
transitions: Transition {
ParallelAnimation {
NumberAnimation {
target: singleItem
properties: "width,height"
duration: 1000
}
NumberAnimation {
target: textInput
property: "opacity"
duration: 1000
}
}
}
}
}
Even I'm new to qt-quick. I don't think it is possible to zoom unless we write our code to do such. I'm not sure though. :-)
This effect is good and it will b nice to see in coming versions. Try to give a feature request to the community <3

Resources