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!


  • Moderators

    @peteritv

    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?



  • AAAAAHHHHHHH!!!!!!!!!!

    I removed the menuBar from the main QML, and now IT IS WORKING!!!

    Is this a BUG or am I just newbie enough to not understand?



  • There is still the matter of the "Unable to assign [undefined] to bool" though...

    Any suggestions?



  • 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



  • Should I make that a new Topic?

    I already had a topic for that, but deleted it now, due to new knowledge :)

    So how to resize the SplitView when the handle has been touched, and the container resizes?



  • BTW: I think that Layout.fillWidth and Layout.fillHeight should BOTH accept "undefined", and keep their current value, or is it just me who thinks so?



  • The higher layer programming languages get, the more things are being taken for granted! I still dream of the times that every bit and byte had to be accounted for when programming in direct machine language :)



  • Maybe I can get a vote here?

    Who thinks Layout.fillWidth and Layout.fillHeight should accept "undefined" and keep there current value?



  • Vote + if you agree, vote - otherwise...



  • I'll continue this on a new Topic...

    This topic has reached its goal, and so I will will mark it is such.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.