In QML, I want to create a text moving when the mouse in on it. When the mouse is not on it anymore, it should go back to its original position. The value of the variable 'toogle' in the code is true when my mouse is on the text, false when its not.
property real distance: myText.x
...
Text {
id: myText
property bool toogle
x:toogle?distance+2:distance
}
The problem is obviously that the value of distance will be increased when the mouse is on the text and that it will create a loop: the text will be always moving as long as the mouse is on it.
How can I save the value of the original x position of the text when it's created, and keep it unmodified to avoid having this undesired loop?
You could define a property and set it to a fixed value whenever the component loading is completed:
// Keep track of the original position.
property real originalPosition;
Component.onCompleted: {
originalPosition = myText.x;
}
I am a bit confused with your question though, do you want the text to keep moving or not whenever the mouse hovers over the text? The code you posted already contains a binding loop.
To detect mouse hovers you can define a MouseArea within your Text element and listen to the 'containsMouse' property to be able to reset the text's position:
MouseArea {
id: mouseArea
width: parent.width
height: parent.height
hoverEnabled: true
onContainsMouseChanged: {
console.log("Changed: " + containsMouse);
if (!containsMouse) {
myText.x = myText.originalPosition;
} else {
myText.x = mouseArea.containsMouse ? myText.originalPosition+2: myText.originalPosition;
}
}
}
This last implementation will only move the text 2 pixels whenever the text is hovered and back to the original position whenever the mouse stops hovering. It will NOT continuously move the text 2 pixels when hovered.
Related
I have a graphical item with x and y bound to the mouse position. When I move the mouse fast, the item goes after the cursor, smoothly animating towards the current position. I'd like it to keep the same position as the cursor all the time. Is it possible?
Current code:
Item{
width: 800; height: 600;
Rectangle{
width: 50; height: 50; color: "red";
x: area.mouseX;
y: area.mouseY;
}
MouseArea{
id: area
anchors.fill: parent
hoverEnable: true
}
}
Partial solution...
I included a cursor picture in the graphical item and made the real mouse cursor invisible (cursorShape: Qt.BlankCursor).
This fake cursor has the same latency to user input as I was already noticing in the bound item, but at least all parts stay together now.
Is there anyway to change the replaceEnter/Exit Transition animation dynamically depending on the next QML file to be loaded in the stack view.
Situation:
I have a Centre QML file having 4 buttons on the 4 sides of the screen. There are other 4 QML files namely Top, Bottm, Right and Left. On press of top button on the Centre QML the Top qml file should transitioned from top-to-bottom and replace the centre one. Similarly on press of left button on the centre QML the left QML should enter there display area from left to right and replace the centre one.
I tried using replaceEnter/Exit property. But not able to understand how to change it dynamically depending on the next QML to be displayed.
take a look at the doc for infos about customizing transitions for Stackview.
If you need more than one transition you can define them separately and then assign them just before they are used. Here is an example:
StackView {
id: control
pushEnter: topTransition
Transition {
id: topTransition
XAnimator {
from: (control.mirrored ? -1 : 1) * -control.width
to: 0
duration: 400
easing.type: Easing.OutCubic
}
}
Transition {
id: bottomTransition
XAnimator {
from: 0
to: (control.mirrored ? -1 : 1) * control.width
duration: 400
easing.type: Easing.OutCubic
}
}
Button {
text: "Push page from bottom"
onClicked: {
control.pushEnter = bottomTransition
control.push(bottomPage)
}
}
}
You will have to explicitly set all push/pop/replace transitions you need before each button click.
Does setting an component's width or height to zero has the same effect as setting its visible property to false?
An example use-case:
I have an item, which slides into the window. The sliding happens by animating its height from 0 to x and when I dismiss this item from x to 0. Don't want to go in depth why I am not animating the position of the item instead. When the item has 0 height, should I set its visible property to false or it doesn't make any difference?
Not really, unless you clip. And it is better to avoid clipping as much as possible.
An Item with zero size will still have its children visible.
Whereas setting visible to false will hide the entire object tree.
In your particular case it seems like it doesn't matter as long as it doesn't cause you to have unwanted visible leftovers. You certainly do not want to have a binding such as visible: height as that would needlessly execute on every step of the animation.
Just to be on the safe side, you can install handlers on the animation to toggle visibility:
// in the animation
onStarted: if (!item.height) item.visible = true // show if start at 0
onStopped: if (!item.height) item.visible = false // hide if end at 0
This will avoid the continuous reevaluations you'd get if you bind visibility to height directly, but will still ensure visibility on before your object begins expanding and off after it has finished contracting.
As dtech already pointed out, the dimensions of the root node of a component do not automatically represent the dimensions of the underlying object tree. As an example take this:
Item {
id: root
Text {
id: txt
text: 'some text produces implicit width'
}
}
In this example the text of txt will be shown, though the dimensions of root are width: 0; height: 0.
As dtech already mentioned, you might set clip to true, but this is not advisable, as then it would be passed to the renderer, which renders the Item and its tree and finally applies clipping to it - in a seperate batch.
If you have something like that:
Item {
Rectangle {
anchors.fill: parent
color: 'red'
}
}
The renderer would do nothing extra when rendering, as it could be processed in the same batch as the rest. However as a developer it is hard to tell, whether something is visible when the size is set to 0 or not. Therefore it is adivsable to always set visible properly.
We might simply set
visible: width > 0 && height > 0 && opacity > 0
which works fine, as long as we don't animate on any of those properties or change them frequently. At least for animations we might have good knowledge, when the any of those properties might become 0 and use this information to reduce the amount of evaluations.
The nice thing about QML is, that the logical expression is evaluated from the left to the right, which means in our last example:
If width === 0 and height changes, it wont trigger reevaluation
If height === 0 and width changes, each change triggers reevaluation.
This means, we need to put the most stable condition first. This might be our information about when any of those values might change. I propose, using the animation.running property, to prevent reevaluation of the binding, while the animation is running.
Let's take a more complete example: Upon click, this Rectangle will shrink from width: 800 to width: 0 - which shall set it invisible.
Or three additional properties binding1, binding2, binding3 are bound to expressions, that we might use to set visible. When ever a particular part of the binding is reeavluated, we log this.
Rectangle {
id: rect
color: 'red'
width: 800
height: 600
NumberAnimation {
id: ani1
target: rect
property: 'width'
from: 800
to: 0
duration: 3000
}
}
MouseArea {
anchors.fill: parent
onClicked: ani1.running = true
}
property bool binding1: {console.log("1", !rect.width); return !rect.width}
property bool binding2: {!ani1.running && (function() { console.log("2", !rect.width); return !rect.width })()}
property bool binding3: {(function() { console.log("3", !rect.width); return !rect.width })() && !ani1.running}
// equivalent, stripped of the logging:
// property bool binding1: !rect.width
// property bool binding2: !ani1.running && !rect.width
// property bool binding3: !rect.width && !ani1.running
As we can see, binding1 is constantly reevaluated, when ever the width changes. This is not desirable.
We can see, that binding2 is only evaluated once at creation, and whenever ani stops running.
In binding3 we have it the other way around and we first evaluate the width, and then whether the ani is running. This means, we have a reevaluation whenever the width is changing.
We could also use the signal handlers ani.onStarted and ani.onStopped and explicitly set the visiblity then, but that would not be declarative and QML encourages you to always strive to stay declarativ.
So in testing my program, I discovered the strangest thing.
So, I have a ListView element with a custom C++ model, and a fairly simple delegate. Each delegate is a MyButton class, which is simply a Text class(z:2), an Image class(z:1), and a MouseArea class. That Image class is the background, and contains a translucent image which becomes opaque when MouseArea is onPressed().
Now the strange part.
When the ListView has 4 elements, it operates normally -except- when the user selects entry #3, then entry #2 or #1.
When the selected entry goes from #3->#1, the text in entry #2 grays out as opposed to its normal white.
When the selected entry goes from #3->#2, the text in entry #2 completely disappears.
After hours of testing and banging head against desk, I've uncovered a bit more:
The opacity of MyButton or any of its children never changes.
The color of MyButton's text element never changes
The content of MyButton's test element never changes
After offsetting the text partially outside of MyButton, this abnormal behavior only affects the text remaining inside the bounds of MyButton's Image child.
The Z level of MyButton or any of its children never changes, though it appears as if MyButton's Image is being placed on top of its Text.
Another image is never placed on top of a MyButton element. If this was the case, when going from #3->#1 you would see the image of entry #2 become darker.
When the ListView is scrolled, everything returns to normal.
When ListView contains 4 elements, below are the abnormalities:
when #4->#1: #2 and #3 gray out
when #4->#2: #2 disappears
when #4->#3: #3 disappears
when #3->#2: #2 disappears
when #3->#1: #2 grays out
This is consistent to the image and text inside the MyButton class being re-ordered, placing the image a Z level above the text. However the z levels are forced in the MyButton definition, and an onZChanged signal is never created when these events happen.
Below is the relevant code:
//MyButton:
import QtQuick 2.0
Item {
id: button
property string source: ""
property string source_toggled: source
property string button_text_alias: ""
signal pressed
width: button_image.sourceSize.width
height: button_image.sourceSize.height
property bool toggled: false
Image{
id: button_image
z: 1
source: toggled ? parent.source_toggled : parent.source
}
MyText{
z: 2
text_alias: button_text_alias
anchors.centerIn: parent
}
MouseArea {
id: button_mouse
anchors.fill: parent
onPressed: button.pressed()
}
}
//ListView:
Component{
id: p_button
MyButton{
source: picture_path + "bar.png"
source_toggled: picture_path + "bar_selected.png"
toggled: model.isCurrent
onClicked: {
profile_model.setCurrent(model.index)
}
button_text_alias: model.display
}
}
ListView{
id: p_list
width: 623
height: count*74 -1
spacing: 1
interactive: false
model: p_model
delegate: p_button
}
I can't think of -anything- that could cause this behavior.. any ideas?
I was able to solve this error by breaking up my delegate into:
Component{
id: p_button
Item{
property bool toggled: model.isCurrent
width: button_image.sourceSize.width
height: button_image.sourceSize.height
Image{
id: button_image
visible: !toggled
source: picture_path + "bar.png"
}
Image{
visible: toggled
source: picture_path + "bar_selected.png"
}
MouseArea{
anchors.fill: parent
onClicked: p_model.setCurrent(model.index)
}
MyText{
text_alias: model.display
anchors.centerIn: parent
}
}
}
So instead of swapping the source of an object, there are two objects which become visible/invisible based on a boolean value. This prevented the issue, though I still don't know the cause.
I want to set the activeFocus for a FocusScope by clicking anywhere within an Item.
Is there a way to achieve this without having a MouseArea over the entire Item? Because it would have to overlay all elements within the Item, making them unclickable.
I'm pretty new to QtQuick/QML and have troubles understanding how to properly implement FocusScopes. I've read about propagating click signals, but couldn't get it to work.
Assuming I have something like this (no FocusScopes for readability):
Rectangle
{
id: outerRectangle
width: 1000
height: 1000
// various controls in here
Rectangle
{
id: innerRectangle
anchors.centerIn: parent
width: 200
height: 200
// even more controls in here
}
}
I want the outerRectangle to get the activeFocus when I click anywhere on the outerRectangle and vice-versa for the innerRectangle. But all controls on both Rectangles still have to work properly.
How can I achieve this?
Surround your Item with FocusScope:
FocusScope {
Item {
focus: true
}
}
See Qt Doc