Make graphical item stay together with the mouse cursor - qt

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.

Related

Set a variable to a fix value in QML

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.

How can I get the drop effect of a DragArea to animate towards the DropArea that received it?

Hope this makes some sense as a question. In my app, I have a DragArea defined which I use to start dragging things over top of various Rectangles that each contain a DropArea. Everything is working fine in my code except for a cosmetic effect that I would like to change.
In QML, when you start dragging from a DragArea and eventually drop, the animation effect is such that the thing you're dragging animates (while fading out) back to the spot from which you started dragging. This happens even when you drop over a DropArea that successfully captures the drop.
What I would like to do is have the drop effect animate towards the DropArea that received the drop - so that it appears I am dragging-and-dropping things into the Rectangle. Is there any way to do this?
I'm guessing that this in some way involves the .source and .target properties of these areas, but no luck so far in having any effect on where the drop animation goes.
By default, QML will give you no cosmetic behavior for drag and drop whatsoever. The drag target will begin at the drag start location, and will end wherever it is dropped, regardless of whether the drag is accepted or not.
Thus I assume the behavior you describe is implemented in your user code, which you have not disclosed. Regardless, what you want to do is quite easy, it involves tracking the position the drag originates at and it ends at, so you can use the two coordinates to animate the position.
In the following example the red rectangle can be dragged, and if dropped outside of a drop area it will animate from its current to its initial position, whereas if dropped in the yellow rectangle, it will animate from its initial to its drop position.
Window {
width: 600
height: 600
visible: true
Rectangle {
width: 200
height: 200
color: "yellow"
DropArea {
anchors.fill: parent
onEntered: drag.source.accepted = true
onExited: drag.source.accepted = false
}
}
Rectangle {
id: rect
width: 50
height: 50
color: "red"
x: parent.width * 0.5
y: parent.height * 0.5
Drag.active: mouseArea.drag.active
property point begin
property point end
property bool accepted : false
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: parent
onPressed: rect.begin = Qt.point(rect.x, rect.y)
onReleased: {
rect.end = Qt.point(rect.x, rect.y)
aX.from = rect.accepted ? rect.begin.x : rect.end.x
aX.to = rect.accepted ? rect.end.x : rect.begin.x
aY.from = rect.accepted ? rect.begin.y : rect.end.y
aY.to = rect.accepted ? rect.end.y : rect.begin.y
anim.start()
}
ParallelAnimation {
id: anim
NumberAnimation { id: aX; target: rect; property: "x"; duration: 200 }
NumberAnimation { id: aY; target: rect; property: "y"; duration: 200 }
}
}
}
}

QML: referencing root window by parent reference is unreliable

Qt/QML question. Using Qt 5.7.
Take the following simple QML program that displays a red rectangle and a blue rectangle aligned vertically. Click handlers for both rectangles attempt to change the color of the parent host window. But with a subtle difference. The red rectangle references the host window directly by it's id (rootWindow). The blue click handler changes color via a parent reference.
The former case works fine. The latter case does not work. It seems like the root window is treated specially and isn't directly part of the parent/child hierarchy, even if the Rectangles are logically nested in the code that way.
Can someone explain the rule around this?
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: rootWindow
color: "#ffffee"
Rectangle {
id: rect1; width: 50; height: 50; color:"red"
MouseArea {
anchors.fill: parent;
onClicked: {
print("rect1 clicked");
rootWindow.color = "green"; // works fine
}
}
}
Rectangle {
id: rect2; width: 50; height: 50; color:"blue"
anchors.top: rect1.bottom
MouseArea {
anchors.fill: parent;
onClicked: {
print("rect2 clicked");
rect2.parent.color = "pink"; // does not work
}
}
}
}
If you add the following line to the onClicked handler, you'll see that its parent isn't the Window:
print(rect2.parent)
Output:
qml: QQuickRootItem(0x18b18147bc0)
This is explained not-so-visibly in the documentation for Window:
If you assign an Item to the data list, it becomes a child of the Window's contentItem, so that it appears inside the window. The item's parent will be the window's contentItem, which is the root of the Item ownership tree within that Window.
The window itself isn't an item, so it uses contentItem instead so that child items can have a parent.
However, in Qt 5.7, Window got an attached property that can be used to access the window of an item:
rect2.Window.window.color = "pink";
Whichever item comes before the Window.window part will be the item that the attached property is used on. You could use it on any item in this scene (e.g. the MouseArea), as they all belong to the same window.
Note that attached properties create a QObject-derived object for each unique item they're used on, so be mindful of how you use them, especially in items that are created in very large numbers.

New drag-and-drop mechanism does not work as expected in Qt-Quick (Qt 5.3)

I've tried to implement drag and drop in Qt 5.3 using the new QML types Drag, DragEvent and DropArea. This is the original example from the documentation of the QML Drag type with some small modifications:
import QtQuick 2.2
Item {
width: 800; height: 600
DropArea {
width: 100; height: 100; anchors.centerIn: parent
Rectangle {
anchors.fill: parent
color: parent.containsDrag ? "red" : "green"
}
onEntered: print("entered");
onExited: print("exited");
onDropped: print("dropped");
}
Rectangle {
x: 15; y: 15; width: 30; height: 30; color: "blue"
Drag.active: dragArea.drag.active
// Drag.dragType: Drag.Automatic
Drag.onDragStarted: print("drag started");
Drag.onDragFinished: print("drag finished");
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
}
}
}
Expected behaviour: The small blue rectangle (drag target) can be dragged around with the mouse. If dragged over the larger green rectangle in the center of the window, this rectangle turns red and back to green when leaving. In addition, the signals dragStarted, entered, exited, dropped and dragFinished are emitted in time and the corresponding signal handlers print out their messages.
Experienced behaviour:
Depends on Drag.dragType (see commented line above):
Drag.dragType is NOT set (default is Drag.Internal):
Drag and drop works as described, but only the signals entered and exited are emitted. The other signals (dragStarted, dragFinished and dropped) are suppressed. So there is no way to react to the drop in the DropArea.
Drag.dragType is set to Drag.Automatic:
All of the signals are emitted now, but the blue rectangle (drag target) does not move with the mouse. Instead, the mouse cursor changes its shape to visualize possible drop targets. After the mouse has been released, the blue rectangle jumps to the latest mouse position.
Neither of these two variants are pleasing. How can I get all signals and still be able to drag around the drag target? Unfortunately the documentation is everything but clear about drag-and-drop in QML, especially about the ominous Drag.dragType.
If you open the QQuickDrag source code and look at the differences between start(), which is used by Drag.Internal, and startDrag() which is used by Drag.Automatic, the difference is pretty obvious. start() sets up an event change listener, which it then uses to update the position of the attached object. startDrag() doesn't do this.
Why does it work this way? I have no idea! The QtQuick 2 drag and drop documentation certainly has room for improvement here.
There is a fairly simple workaround: take the best from both worlds. Use Drag.Automatic, but instead of setting Drag.active, call start() and drop() manually. It won't invoke Drag.onDragStarted() and Drag.onDragFinished() but you essentially get those for free anyway by listening for a change in the MouseArea's drag.active.
Here's the concept in action:
import QtQuick 2.0
Item {
width: 800; height: 600
DropArea {
width: 100; height: 100; anchors.centerIn: parent
Rectangle {
anchors.fill: parent
color: parent.containsDrag ? "red" : "green"
}
onEntered: print("entered");
onExited: print("exited");
onDropped: print("dropped");
}
Rectangle {
x: 15; y: 15; width: 30; height: 30; color: "blue"
// I've added this property for simplicity's sake.
property bool dragActive: dragArea.drag.active
// This can be used to get event info for drag starts and
// stops instead of onDragStarted/onDragFinished, since
// those will neer be called if we don't use Drag.active
onDragActiveChanged: {
if (dragActive) {
print("drag started")
Drag.start();
} else {
print("drag finished")
Drag.drop();
}
}
Drag.dragType: Drag.Automatic
// These are now handled above.
//Drag.onDragStarted: print("drag started");
//Drag.onDragFinished: print("drag finished");
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
}
}
}
I realize it's not a completely satisfying solution, but it does match your expected behavior.
This solution offers:
Notifications for all of the desired events: drag started, drag finished, enter drag area, exit drag area, and dropped in drag area.
The drag animation is automatically handled by QtQuick. The square doesn't freeze in place like it does when running the sample code with Drag.Automatic.
What it doesn't offer:
An explanation as to why QtQuick's drag and drop functionality works this way, or whether it's even the intended behavior by the developers. The current documentation seems ambiguous.
Just ran into this myself (using Qt 5.2, but the same problem exists there). I've got a 'slider box' on the X-axis and just wanted to know when the drag was finished... instead of responding to every position change along the way. My workaround involved hacking the states/transitions, with a ScriptAction to provide the logic. This is the simplified version for mimicking a response to the "onDragFinished" signal. So while it doesn't cover all your drag/drop signals, it might get you pointed in the right direction.
Rectangle {
id: sliderControl
height: coordinates.height
width: 80
color: "#F78181"
border.color: "#FE2E2E"
border.width: 1
opacity: 0.4
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: sliderControl
drag.axis: Drag.XAxis
drag.minimumX: 0
drag.maximumX: view.width - sliderControl.width
hoverEnabled: true
}
states: [
State {
name: "dragging"
when: mouseArea.drag.active
},
State {
name: "finished_dragging"
when: !mouseArea.drag.active
}
]
transitions: [
Transition {
from: "dragging"
to: "finished_dragging"
ScriptAction {
script: console.log("finished dragging script");
}
}
]
}
ps - I know that such a 'workaround' doesn't qualify for the bounty parameters, but I was pretty bummed to find only your question (no solutions) when I searched for help on the issue. Hopefully anyone else stumbling down this path will find this useful. Unfortunately, I've got no clue what's going on with QML's Drag.dragType either.

QML OnHighligt movement completed

I have this ListView:
ListView {
id: topList
focus: true
width: parent.width
height: parent.height
preferredHighlightBegin: height - 70
preferredHighlightEnd: height
highlightMoveSpeed: 150
highlight: Rectangle { width: theListView.width; height: 22; color: "yellow" }
highlightRangeMode: ListView.StrictlyEnforceRange
}
Now, when I use up or down arrow the list moves nice and fine. However, when the movement is done, I would like to trigger another animation. Have tried listening on onMovementEnded and onFlickEnded but neither of them seems to be triggered when the animation is done.
The QtQuick examples include an example of rolling your own: http://doc.qt.nokia.com/4.7-snapshot/declarative-modelviews-listview-highlight-qml.html
The basic idea is to prevent the view from moving the highlight itself:
highlightFollowsCurrentItem: false
and use an animation to make the highlight follow the currentItem. You will probably want to stick to SmoothedAnimation or SpringAnimation since they handle the target position being moved before the animation is complete.
I believe it is not possible. highlightPosAnimator and highlightSizeAnimator are part of QDeclarativeListView private implementation, and not accesible by "user" code.
If you need custom/combined highlight animation, looks like you have to roll your own.

Resources