Hi,
This is not a KDE post, but might help those that are converting KDE plasmoids from C++ to QML.
As i mentioned before, i had a lot of issues making dynamically added elements resizable and drag/droppable. So i started a new QML project to figure out exactly that. It turns out to be quite a challenge and certainly not straightforward. First an image of how it looks.

As you can see, it’s a very basic image. The functionality is as follows. The red rectangle can be used to resize the blue rectangle (vertically only). The red one will turn orange once you start resizing. The blue rectangle will then obviously change in size. By clicking the blue rectangle you can drag it around and drop it anywhere you want. Below is the code and after the code i will share the tricks i had to apply to even get this working.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
import QtQuick 1.1 Rectangle { id: root width: 600 height: 500 ListModel { id: testModel ListElement {} ListElement {} ListElement {} } Repeater { model: testModel delegate: delegateItem } Item { id: positionAnchorHook width: 0 height: 0 } Component { id: delegateItem Rectangle { x: Math.random() * root.width y: Math.random() * root.height width: 100 height: 100 color: "blue" id: delegateRect MouseArea { id: dragHandle anchors.top: topHandle.bottom anchors.bottom: parent.bottom width: parent.width drag.target: delegateRect onPressed: { dragColor.color = "orange" } onReleased: { dragColor.color = "transparent" } Rectangle { id: dragColor anchors.fill: parent color: "transparent" } } MouseArea { id: topHandle width: parent.width height: 20 anchors.top: parent.top drag.target: topHandle drag.axis: Drag.YAxis property int clickedYInHandle: 0 // used to keep the mouse pointed at the position you clicked onPressed: { // We only set the anchors for a second to put the anchor element on the right position. After that's done we release the hook to this element. positionAnchorHook.anchors.bottom = parent.bottom positionAnchorHook.anchors.left = parent.left positionAnchorHook.anchors.bottom = undefined positionAnchorHook.anchors.left = undefined // Now hook our object to the achor element parent.anchors.bottom = positionAnchorHook.bottom topHandleRect.color = "orange" clickedYInHandle = mouseY } onReleased: { topHandleRect.color = "red" parent.anchors.top = undefined parent.anchors.bottom = undefined } onPositionChanged: { var mouseToPlaceholder = mapToItem(positionAnchorHook, mouseX, mouseY) var newHeight = Math.abs(mouseToPlaceholder.y) + clickedYInHandle if(mouseToPlaceholder.y < 0) { // bottom anchored parent.anchors.top = undefined parent.anchors.bottom = positionAnchorHook.bottom topHandle.anchors.bottom = undefined topHandle.anchors.top = delegateRect.top console.log("bottom anchored") } else { // top anchored parent.anchors.bottom = undefined parent.anchors.top = positionAnchorHook.top topHandle.anchors.top = undefined topHandle.anchors.bottom = delegateRect.bottom console.log("top anchored") } delegateRect.height = newHeight } Rectangle { id: topHandleRect anchors.fill: parent color: "red" } } } } } |
In order to get this working i had to do some tricks. For instance, you can’t resize a QML element downwards by default so you have to anchor it to something that stays at a certain position, anchor to that item and resizing works both up and down. The way to get that working is a bit tricky. In the code above i’m defining an item with the “positionAnchorHook” id. It’s just an empty item with no size at all yet that item is very important.
Now the thing that happens once you press a red rectangle is the following:
- First i’m anchoring the “positionAnchorHook” to the bottom of the blue rectangle. That’s just to position the “positionAnchorHook” at the same location as the rectangle. I might as well use X and Y in this case.
- Right after they are anchored i reset the anchor points of “positionAnchorHook” so that it simply stays where i placed them regardless of what i do next. So what i basically did (with a detour) is setting the X and Y position… While typing this i’m considering of changing it in the code :p
- Now the bottom of the red rectangle is anchored to the “positionAnchorHook”. Doing this allows me to resize the element up and down till the “positionAnchorHook” y position. If i drop below that position i have the change the anchoring.
- The height is being calculated based on the “positionAnchorHook” y position and the current mouse y position.
- I’m also adding the mouse position from where you click in the red rectangle in order to avoid a height jump. (difficult to explain).
At first i was trying to simply use dragging of the red rectangle and binding the blue rectangle to that, but that didn’t really work out nicely.
Dragging the entire blue rectangle does work nicely. You also might notice the listmodel.. That’s done intentionally because that mimics the situation i had in my QML Calendar application.
So there you have it. Dragging/dropping and (vertically only) resizing in QML. It’s quite tricky to figure out how it should all work, so therefore i share the basic version in this blog post.
I hope this saves some others hours if not days of figuring out this stuff. It took me quite some time. I did this because i couldn’t continue my QML Calendar because of those issues. Now that they are fixed i can finally take some steps in there.
Cheers,
Mark

Exactly the same thing I do to test some functionality or figure out the mechanics of a certain api / library / language. Guess we all do.
One thing that stops me from using qml, how can you be sure about the order of calculation of the declarative expressions? Like, if there is a dependency chain, in which order are the variables updated? And other situations like that, is there any way to determine order of expression evaluation? E.g. in c++ we have teh standard and always know the order for different overload resolutions, you see. Maybe I’m overcomplicating things a bit?
I understand what you mean and i think you’re indeed overcomplicating things a little :) I certainly don’t worry about that. Remember, all it should do is “declare” the GUI. In my case that boils down to declaring the GUI and placing everything at the right positions. The actual data is being fetched on the C++ side and it being fed to qml as ready to display data.
Things do get a bit more complicated when i also want to update certain data with user input which is obviously going through QML first.
Hi Mark,
first of all, thank you for the post! This kind of “more advanced” usage of QML aren’t being very covered by the documentation ,so posts like this are very useful to expose some tricks and possibilites of QML.
Right after read your post I was wondering if there was another way to implement this example. So I rewrote it using a bit different approach. You can take a look at the code here [1]. I don’t know if it fits for you QML Calendar, but for the example of this post my solution seems to be a bit easier to understand.
[1] – http://pastebin.com/nVh1gEYn
Cheers,
Luís Gabriel
That’s truly awesome! It works quite a bit better then the stuff i came up with. I did try to make the “handle” using the “drag.” stuff, but i failed there. Don’t know why, but i am certainly glad that you posted this.
We really need a place where we can put QML Snippets like this since it will likely get lost over time.
I confess that I took some time to come up with this solution, but I think this is the bealty of QML, you always have a variety of ways to do what you want. ;)
And fully agree, it would be very good to have a place to keep QML Snippets.
Pingback: QML: drag, drop and resize dynamic element – Followup | Mark's KDE blog