Reputation: 385
I'm looking for how to input for Loader inside Component. Here is my problem.
// CustomSplitView.qml
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Pane {
function addItem(text, item) {
/**
Here is the problem part I think.
"headerText" is correctly applied to the 'template' object,
but "content" is not.
**/
splitView.addItem(template.createObject(null, {"headerText": text, "content": item}))
}
Component {
id: template
Pane {
property alias headerText: label.text
property alias content: loader.sourceComponent
ColumnLayout { anchors.fill: parent
Label { id: label }
Loader {
id: loader
Layout.fillWidth: true; Layout.fillHeight: true
sourceComponent: Rectangle { color: "Green" }
} // Loader
} // ColumnLayout
} // Pane
} // Component
SplitView {
id: splitView
anchors.fill: parent
} // SplitView
} // Pane
// Usage
Pane {
Component {
id: redRect
Rectangle { color: "Red" }
} // Component
Column { anchors.fill: parent
Button {
text: "add"
onClicked: customSplitView.addItem("RED", redRect.createObject())
} // Button
CustomSplitView { id: customSplitView }
} // Column
} // Pane
Result: When "add" button clicked, adding item inside of the split view has "RED" text but green rectangle appears, not red.
It's not a size issue. (detailed resizing code has been omitted for code simplicity) Any advise will helps a lot, for what I missed, or other approaches.
Upvotes: 0
Views: 191
Reputation: 26309
Whenever you want to dynamically create (and, possibly destroy) components, I would highly recommend using a model
with a delegate
. Because your delegate
appears to be dynamic, I recommend using DelegateChooser
. This will remove the pain of managing the dynamic creation and destruction of your objects.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
header: Frame {
RowLayout {
Button {
text: "add"
onClicked: customSplitView.addRandomItem()
}
Button {
text: "clear"
onClicked: customSplitView.clear()
}
}
}
CustomSplitView {
id: customSplitView
}
}
// CustomSplitView.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.qmlmodels
Pane {
anchors.fill: parent
SplitView {
anchors.fill: parent
Repeater {
id: repeater
model: ListModel {
id: listModel
}
delegate: DelegateChooser {
role: "typ"
DelegateChoice {
roleValue: "label"
delegate: ColumnLayout {
SplitView.preferredWidth: w
SplitView.preferredHeight: h
Label {
Layout.fillWidth: true
text: txt
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: lbl
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
}
}
DelegateChoice {
roleValue: "rect"
delegate: ColumnLayout {
SplitView.preferredWidth: w
SplitView.preferredHeight: h
Label {
Layout.fillWidth: true
text: txt
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Qt.rgba(r, g, b, 1)
}
}
}
}
}
}
function addRandomItem() {
if (Math.random() < 0.5) {
listModel.append( {
typ: "label",
txt: "Label",
lbl: "Label " + Math.random(),
w: 200,
h: 50,
r: 0,
g: 0,
b: 0
} );
} else {
listModel.append( {
typ: "rect",
txt: "Rect",
lbl: "",
w: 50,
h: 50,
r: Math.random(),
g: Math.random(),
b: Math.random()
} );
}
}
function clear() {
listModel.clear();
}
Component.onCompleted: addRandomItem()
}
You can Try it Online!
If, on the other hand, you really want fully adhoc QML components controlled by a string. You should consider using createQmlObject
or Loader
with data-uri. Below is a demonstration of the latter:
import QtQuick
import QtQuick.Controls
Page {
id: page
Repeater {
model: [
{ qml: "Rectangle { }",
obj: { width: 100,
height: 100,
color: "red" } },
{ qml: "Rectangle { radius: height * 0.5 }",
obj: { width: 50,
height: 50,
color: "orange" } },
{ qml: "Button { }",
obj: { text: "Click Me" } }
]
delegate: Loader {
x: Math.random() * page.width
y: Math.random() * page.height
Component.onCompleted: setSource(
`data:text/plain,import QtQuick
import QtQuick.Controls
` + modelData.qml,
modelData.obj
);
}
}
}
You can Try it Online!
Upvotes: 1
Reputation: 385
Thanks for insight of Stephen Quan, I could instantiate Component
type as an argument of function.
// CustomSplitView.qml
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
Pane {
function addItem(text, component) {
listModel.append( {text, component} )
} // addItem
SplitView { id: splitView
anchors.fill: parent
Repeater {
model: ListModel { id: listModel } // ListModel
delegate: Pane {
property string _headerText: text /* input "text" */
property var _component: component /* input "component" */
ColumnLayout { anchors.fill: parent
Label { text: _headerText }
Loader { Layout.fillWidth: true; Layout.fillHeight: true
sourceComponent: _component
} // Loader
} // ColumnLayout
} // Loader
} // Repeater
} // SplitView
} // Pane
// Usage
Window {
Component { id: rectComponent
Rectangle {
color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
} // Rectangle
} // Component
Component { id: labelComponent
Label {
text: "Label " + Math.random()
} // Label
} // Component
Pane { anchors.fill: parent
ColumnLayout { anchors.fill: parent
CustomSplitView { id: customSplitView
Layout.fillWidth: true; Layout.fillHeight: true
orientation: Qt.Horizontal
} // customSplitView
Button {
text: "Add"
onClicked: {
customSplitView.addItem(
"Label", Math.random() < 0.5 ? rectComponent : labelComponent)
} // onClicked
} // Button
} // ColumnLayout
} // Pane
} // Window
Now the "add" button generates random color of rectangle or random label randomly.
Upvotes: 0