QML SplitView - handleDelegate
-
Hi all!
I use a horizontal SplitView in QML with 2 children (let's say: Rectangles).
When the SplitView handle is dragged, I want to calculate the left/right ratio and save the value for further processing (store it in settings or so).The documentation (http://doc.qt.io/qt-5/qml-qtquick-controls-splitview.html#details) mentions something like "handleDelegate Component" which seems to be of some use, but it only mentions readonly properties. Is it so that the delegate will be activated on any SplitView event, and I use the properties to find out what exactly happened?
The editor also suggests the existance of an event called "on__HandlesChanged: ". Maybe that is the way to go?
Does anyone know how to implement such a thing?
Maybe some example?T.I.A!
-
Is it so that the delegate will be activated on any SplitView event, and I use the properties to find out what exactly happened?
There is already a default one activated i.e the thin vertical 1 width line which you click and drag.
You can override it and create new one as:handleDelegate: Rectangle { width: 20 color: "red" }
The editor also suggests the existance of an event called "on__HandlesChanged: ". Maybe that is the way to go?
Not sure. This is a private property and may change in future and so cant be relied on.
Does anyone know how to implement such a thing?
When the handle is moved the items which it holds are resized too. So you can monitor these size change in your
Rectangles
? -
@p3c0 : Thanx for your reply!
Now I know how that delegate works :)
But now for the resizing stuff, this is my simplified QML so far:
Item { id: mainItem anchors.fill: parent SplitView { id: splitMain orientation: Qt.Horizontal anchors.fill: parent property var ratio: [ 0.6, 0.4 ] Rectangle { width: splitMain.width * splitMain.ratio[0] } Rectangle { width: splitMain.width * splitMain.ratio[1] } handleDelegate: Rectangle { width: 4; color: "red" } } }
I gave the splitview a dynamic property "ratio" which is used to calculate the width of children. Children will be created dynamiccally later on, so this code just illustrates.
Point is that the width of a child is calculated from ratio, so using a child event to change the ratio will cause a circular reference, right?
So I need some kind of event at the splitMain level that signals the user is fiddling with the handles. I tried onChildrenChanged and onChildrenRectChanged, but those do not seem to be fired.
To indicate what I want, I tried the child.onWidthChanged anyway, and surprisingly it kinda works, besides the fact that the first initialization immediately resets ratio to 0.
Item { id: mainItem anchors.fill: parent SplitView { id: splitMain orientation: Qt.Horizontal anchors.fill: parent property var ratio: [ 0.6, 0.4 ] Rectangle { id: r1 width: splitMain.width * splitMain.ratio[0]; Text { text: splitMain.ratio[0] } onWidthChanged: { if (splitMain.resizing ) splitMain.ratioChanged() } } Rectangle { id: r2 width: splitMain.width * splitMain.ratio[1]; Text { text: splitMain.ratio[1] } } handleDelegate: Rectangle { width: 4; color: "red" } onRatioChanged: { ratio[0] = r1.width / width ratio[1] = (r2.width + 4) / width } } }
Any ideas on how to fix this?
-
A few hours later, and after a lot of trial & error, I came up with this result, that is supposed to work for both horizontal as vertical SplitView, and with just 2 children:
import QtQuick 2.6 import QtQuick.Controls 1.5 import QtQuick.Layouts 1.3 Item { id: mainItem anchors.fill: parent SplitView { id: splitMain orientation: Qt.Vertical anchors.fill: parent signal handleChanged() property real ratio: 0.6 Rectangle { id: r1 width: if (splitMain.orientation == Qt.Horizontal) { splitMain.width * splitMain.ratio } height: if (splitMain.orientation == Qt.Vertical) { splitMain.height * splitMain.ratio } Text { text: splitMain.ratio.toFixed(2) } onWidthChanged: { if ( (splitMain.orientation == Qt.Horizontal) && splitMain.resizing ) splitMain.handleChanged() } onHeightChanged: { if ( (splitMain.orientation == Qt.Vertical) && splitMain.resizing ) splitMain.handleChanged() } } Rectangle { id: r2 Layout.fillWidth: true Layout.fillHeight: true } handleDelegate: Rectangle { width: (splitMain.orientation == Qt.Horizontal) ? 4 : 0 height: (splitMain.orientation == Qt.Vertical) ? 4 : 0 color: "red" } onHandleChanged: { ratio = (splitMain.orientation == Qt.Horizontal) ? r1.width / width : r1.height / height console.log("handle changed to ratio " + ratio.toFixed(2)) } } }
As you can see, I declared an event "handleChanged" that is triggered when width/height (depending on SplitView orientation) of the children changes.
Now I am 95% happy!
In horizontal orientation this works like a charm, but when changed to vertical (splitMain.orientation = Qt.Vertical) somehow the initial ratio get's lost?Please???? Any help will be GREATLY appreciated!
-
OK, I dug a little deeper into the problem with this code:
Item { id: mainItem anchors.fill: parent SplitView { id: splitMain orientation: Qt.Horizontal anchors.fill: parent signal handleChanged property real ratio: 0.6 Rectangle { id: r1 width: { if (splitMain.orientation == Qt.Horizontal) { console.log("width: " + splitMain.width) return splitMain.width * splitMain.ratio } } height: { if (splitMain.orientation == Qt.Vertical) { console.log("height: " + splitMain.height) return splitMain.height * splitMain.ratio } } Text { text: splitMain.ratio.toFixed(2) } onWidthChanged: { if ((splitMain.orientation == Qt.Horizontal) && splitMain.resizing) splitMain.handleChanged() } onHeightChanged: { if ((splitMain.orientation == Qt.Vertical) && splitMain.resizing) splitMain.handleChanged() } } Rectangle { id: r2 Layout.fillWidth: if (splitMain.orientation == Qt.Horizontal) true Layout.fillHeight: if (splitMain.orientation == Qt.Vertical) true } handleDelegate: Rectangle { width: (splitMain.orientation == Qt.Horizontal) ? 4 : 0 height: (splitMain.orientation == Qt.Vertical) ? 4 : 0 color: "red" } onHandleChanged: { ratio = (splitMain.orientation == Qt.Horizontal) ? r1.width / width : r1.height / height console.log("handle changed to ratio " + ratio.toFixed(2)) } } }
Now on horizontal orientation, I get this output after initialization:
Starting /home/peter/V-PlaySDK/5.7/gcc_64/bin/qmlscene... qml: width: 0 file:///home/peter/Projects/BridgeComponents/MainForm.ui.qml:48:32: Unable to assign [undefined] to bool qml: width: 800
On vertical orientation I get this:
Starting /home/peter/V-PlaySDK/5.7/gcc_64/bin/qmlscene... qml: height: 0 qml: height: -21 file:///home/peter/Projects/BridgeComponents/MainForm.ui.qml:46:31: Unable to assign [undefined] to bool
and then it stops...
Now I start to think there is more to this problem than meets the eye, so here is my main QML:
import QtQuick 2.6 import QtQuick.Controls 1.5 ApplicationWindow { visible: true width: 800 height: 600 title: qsTr("Bridge layout") menuBar: MenuBar { Menu { title: qsTr("File") MenuItem { text: qsTr("&Open") onTriggered: console.log("Open action triggered"); } MenuItem { text: qsTr("Exit") onTriggered: Qt.quit(); } } } MainForm { anchors.fill: parent } }
Now does that not CLEARLY state that the default height is 600?
So how come the default width is being handled OK, but the height not?
And both (horizontal and vertical) complain about "Unable to assign [undefined] to bool"? What is that about?
-
OK, solved that too!
Layout.fillWidth and Layout.fillHeight do not like to be unassigned when you call on them, so this code solves that problem:
Rectangle { id: r2 Layout.fillWidth: (splitMain.orientation == Qt.Horizontal) ? true : false Layout.fillHeight: (splitMain.orientation == Qt.Vertical) ? true : false }
I guess I am happy now!
But I am not closing this topic yet, because the main reason why I started this topic was:
When you DO NOT touch the SplitView handle, and you resize the Window, then the SplitView resizes with it, keeping initial aspect ratio. However, when u touch the handle, all bets or off and resizing the Window does no longer keep aspect ratio.
Now I am sure there are some real QML guru's out there that can explain?
Greetz,
Peter