I am trying to use QML layouts to size sections of a UI within a ScrollView
and having trouble understanding why one of the nested GridLayouts
is being clipped. To understand it better, please see the picture below and look specifically at the bottom of "Section 1":
I think, basically, the issue is that "Section 2" implicit height(the smaller of the two side-by-side sections) is driving the sizing for that row in the magenta GridLayout instead of the other way around, as I would hope. I'm trying to get the code down to something allowed in the length of the question and will edit as soon as I do.
CodePudding user response:
Instead of using ScrollView
. I highly recommend you look at components that have a ScrollBar
property first such as Flickable
and ListView
.
To demonstrate, I mocked up the following example demonstrating how you can use ListView
with the ScrollBar.vertical
property set to recreate the UI/UX shown in your question.
For the delegate
I use a DelegateChooser
so each row can have a different delegate. For the model
I use a ListModel
which is used to populate the different input types.
For each delegate, I add the following code to ensure that when that control is being edited, its focus is forced to be visible. i.e. it avoids the control being clipped.
onActiveFocusChanged: listView.currentIndex = index
Here's a full working example:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
background: Rectangle { color: "black" }
Frame {
anchors.fill: parent
anchors.margins: 10
background: Rectangle {
color: "black"
border.color: "#f0f"
border.width: 2
}
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
SectionInput {
title: "SECTION 1"
model: Data1 { }
}
SectionInput {
title: "SECTION 2"
model: Data2 { }
}
}
SectionInput {
title: "SECTION 3"
model: Data3 { }
}
}
}
}
// SectionInput.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.qmlmodels
Frame {
id: sectionInput
property string title
property ListModel model
Layout.fillWidth: true
Layout.fillHeight: true
background: Rectangle {
color: "black"
border.color: "yellow"
border.width: 2
}
ColumnLayout {
anchors.fill: parent
Text {
text: title
color: "cyan"
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: sectionInput.model
clip: true
spacing: 10
snapMode: ListView.SnapOneItem
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
delegate: DelegateChooser {
id: chooser
role: "typ"
DelegateChoice { roleValue: "combo"; MyCombo { } }
DelegateChoice { roleValue: "text"; MyText { } }
DelegateChoice { roleValue: "switch"; MySwitch { } }
}
}
}
}
// MyCombo.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
RowLayout {
property ListView listView: ListView.view
width: listView.width - 20
Text {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: lab
color: "white"
horizontalAlignment: Qt.AlignRight
}
ComboBox {
Layout.fillWidth: true
Layout.preferredWidth: 100
model: dat.split(",")
onAccepted: val = currentText
onActiveFocusChanged: listView.currentIndex = index
Component.onCompleted: currentIndex = find(val)
}
}
// MyText.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
RowLayout {
property ListView listView: ListView.view
width: listView.width - 20
Text {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: lab
color: "white"
horizontalAlignment: Qt.AlignRight
}
TextField {
Layout.fillWidth: true
Layout.preferredWidth: 100
background: Rectangle { color: "#ddd" }
text: val
placeholderText: pla
onAccepted: val = text
onActiveFocusChanged: listView.currentIndex = index
}
}
// MySwitch.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
RowLayout {
property ListView listView: ListView.view
width: listView.width - 20
Text {
Layout.fillWidth: true
Layout.preferredWidth: 100
text: lab
color: "white"
horizontalAlignment: Qt.AlignRight
}
Frame {
Layout.fillWidth: true
Layout.preferredWidth: 100
background: Item { }
Switch {
checked: val === "true"
onToggled: val = JSON.stringify(checked)
onActiveFocusChanged: listView.currentIndex = index
}
}
}
// Data1.qml
import QtQuick
import QtQuick.Controls
ListModel {
ListElement { lab: "Field 1"; typ: "combo"; val: "F1"; dat: "F0,F1,F2,F3" }
ListElement { lab: "Field 2"; typ: "text"; pla: "xxx.xxx.xxx.xxx" }
ListElement { lab: "Field 3"; typ: "text"; pla: "xx,xxx" }
ListElement { lab: "Field 4"; typ: "text"; pla: "x,xxx" }
ListElement { lab: "Field 5"; typ: "text"; pla: "xxx" }
ListElement { lab: "Field 6"; typ: "text"; pla: "xx" }
ListElement { lab: "Field 7"; typ: "text" }
}
// Data2.qml
import QtQuick
import QtQuick.Controls
ListModel {
ListElement { lab: "Field 1"; typ: "combo"; val: "TTL"; dat: "TTL,XYZ,ABC" }
ListElement { lab: "Field 2"; typ: "combo"; val: "50"; dat: "5,50,500" }
ListElement { lab: "Field 3"; typ: "combo"; val: "50"; dat: "40,50,60" }
ListElement { lab: "Field 4"; typ: "combo"; val: "Normal"; dat: "Normal,Not Normal" }
ListElement { lab: "Field 5"; typ: "combo"; val: "Normal"; dat: "Normal,Not Normal" }
}
// Data3.qml
import QtQuick
import QtQuick.Controls
ListModel {
ListElement { lab: "Field 1"; typ: "switch"; val: "true" }
ListElement { lab: "Field 2"; typ: "combo"; val: "OFF"; dat: "ON,OFF" }
ListElement { lab: "Field 3"; typ: "switch"; val: "false" }
ListElement { lab: "Field 4"; typ: "text" }
ListElement { lab: "Field 5"; typ: "text" }
ListElement { lab: "Field 6"; typ: "text" }
}
You can Try it Online!
CodePudding user response:
I ended up resolving the sizing issue with a test for which "Section" Rectangle
was vertically larger (implicitHeight
) before setting the Layout.preferredHeight
for both of the "Sections".
Rectangle { //Section 1 rectangle
color: "black"
clip: true
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: widthMainControlCommon
Layout.preferredHeight: { layoutSec1.implicitHeight > layoutSec2.implicitHeight ?
layoutSec1.implicitHeight : layoutSec2.implicitHeight}
GridLayout {
id: layoutSec1
columns: 2
anchors.fill: parent
}
//..
Rectangle { //Section 2 rectangle
color: "black"
clip: true
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: widthMainControlCommon
Layout.preferredHeight: { layoutSec1.implicitHeight > layoutSec2.implicitHeight ?
layoutSec1.implicitHeight : layoutSec2.implicitHeight}
GridLayout {
id: layoutSec2
columns: 2
anchors.fill: parent
}
I don't understand the benefits of the Layouts
like I thought I did, or the logic that drives sizing when they're nested. Needing to set a preferredHeight
in order to get the Layout
to size to make it visible seems strange to me.