3

I am in the process of porting a application based on QtWidgets over to QtQuick2.

I am trying to figure out what QtQuick layout items I should use.

I am stuck at QFormLayout. I simply cannot find an equivalent. The best I could find is a GridLayout, but I want the labels to be generated automatically (like in QFormLayout).

QHBoxLayout converts to RowLayout
QVBoxLayout converts to ColumnLayout
QGridLayout converts to GridLayout
QFormLayout converts to ???

feedc0de
  • 3,646
  • 8
  • 30
  • 55
  • As I am not familiar with the `QFormLayout` could you please explain, what you are trying to do, and how you plan on using it? – derM - not here for BOT dreams Jul 11 '17 at 13:32
  • I am trying to implement a very simple dialog with multiple input fields. Every input field should have its own label left to it. If a user accidentally clicks on the label instead of the input item, I want the input item to be focused (buddy functionality). I think it would be better If I wouldn't have to create labels on my own and let a FormLayout do that (same as in QtWidgets). I just wanted to use the most canonical way. – feedc0de Jul 11 '17 at 13:51

2 Answers2

3

If you are satisfied with the GridLayout, only missing the automatic label generation, you can create yourself some small helper class, in which you encapsulate the Label and hold a property for the control.

// FormControl.qml

import QtQuick 2.0
import QtQuick.Controls 2.0

Item {
    id: root
    property alias label: myLabel

    Label {
        id: myLabel
        parent: root.parent

        Layout.fillHeight: true
        Layout.fillWidth: true

        verticalAlignment: Qt.AlignVCenter

        MouseArea {
            anchors.fill: parent
            onClicked: root.control.forceActiveFocus()
        }
    }

    property Item control

    Row {
        id: content
        parent: myLabel.parent // parent it to myLabel.parent, to make sure, that one is added first.
        children: [control]
    }
}

The usage is simple:

import QtQuick 2.7
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0

ApplicationWindow {
    id: myWindow
    visible: true
    width: 600
    height: 600
    color: 'white'

    GridLayout {
        columns: 2

        FormControl {
            label.text: 'test1'
            control: ComboBox {
                model: ['hello', 'world', 'and', 'bye']
            }
        }

        FormControl {
            label.text: 'Text'
            control: TextField {
            }
        }

        FormControl {
            label.text: 'Something Long'
            control: TextField {

            }
        }
    }
}

You might omit the control when you declare it the default property Item control in FormControl.qml. Then however you might acidently add multiple controls, where the first will be lost.

I use the Row to benefit from the implicit height and width, but you could also use a Item and set the width and height to it's childrenRect.width/height. I am not sure, however, if this is robust.

  • I don't know why but FormControl are displayed in reverse order: https://imgur.com/a/dPabstE – uni Oct 13 '20 at 12:44
1

As far as I know there is no equivalent for QFormLayout in QML.

You'll have to define the component yourself with a Repeater and GridLayout.

You can find some details here : Populate GridLayout with Repeater

Example of a FormLayout.qml :

import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.0

GridLayout {
    id: grid

    // Emitted when text has changed
    signal inputTextChanged(int index, string text)

    columns: 2
    rowSpacing: 5
    columnSpacing: 5

    property var labels: []

    Repeater {
        model: grid.labels
        Text {
            Layout.row: index
            Layout.column: 0
            Layout.preferredWidth: 100
            Layout.preferredHeight: 100
            text: modelData
            verticalAlignment: Text.AlignVCenter
        }
    }

    Repeater {
        model: grid.labels
        TextField {
            Layout.row: index
            Layout.column: 1
            Layout.preferredWidth: 100
            Layout.preferredHeight: 40
            onTextChanged: inputTextChanged(index,text)
        }
    }
}

main.qml :

import QtQuick 2.5
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.2

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")


    FormLayout {
        anchors {
            fill: parent
            margins: 5
        }

        labels: ["label1","label2"]
        onInputTextChanged: console.log(index + "/" + text)
    }
}

labels can be any model that qml accept (JS array, ListModel, or AbstractItemModel from c++).

qformlayout

Blabdouze
  • 1,041
  • 6
  • 12
  • Can I somehow use an array for the items itself? They are different from row to row and I also need to set properties on them. Can I somehow enable the buddy functionality where Qt knows which label belongs to which item? – feedc0de Jul 11 '17 at 14:37
  • Yes you can, see @derM solution it is more appropriate for you. As for the buddy functionality, I don't think there is an equivalent. But I doubt you'll need it in QML ! – Blabdouze Jul 11 '17 at 15:01
  • You can, at least set give it active focus. Add a `MouseArea` to the `Text` in your first repeater. `onClicked: secondRepeater.itemAt(index).forceActiveFocus()`. It won't open the PopUp for `ComboBox`es though. – derM - not here for BOT dreams Jul 11 '17 at 15:37