How to access the QML object inside the Loader's sourceComponent? - qt

I may need to read or write to some of the properties of the Loader's sourceComponent from some outside function.
What is the way to access the property x of the object inside this Loader's sourceComponent?
import QtQuick 2.0
Item {
width: 200; height: 200
Loader {
anchors.fill: parent
sourceComponent: rect
}
Component {
id: rect
Rectangle
{
width: 50
height: 50
color: "red"
property int x
}
}
}

When you need to expose an inner object/property to the outside, you should create an alias to it.
import QtQuick 2.0
Item {
width: 200; height: 200
property alias loaderItem: loader.item
Loader {
id: loader
anchors.fill: parent
sourceComponent: rect
}
Component {
id: rect
Rectangle
{
width: 50
height: 50
color: "red"
property int x
}
}
}

Related

How can i overwrite items in a data property

I'm trying to make some qml components that are more general but have the flexibility of defaults but for items. Here are some example components
A.qml
import QtQuick 2.15
Rectangle {
property alias inner: inside.data
color: "red"
width: 40
height: 40
Item {
id: inside
}
}
B.qml
import shared.debug 1.0 as Debug
import QtQuick 2.15
Debug.A {
inner: Rectangle {
width: 30
height: 30
color: "blue"
}
}
C.qml
import shared.debug 1.0 as Debug
import QtQuick 2.15
Debug.B {
inner: Rectangle {
width: 20
height: 20
color: "black"
}
}
qmldir
module shared.debug
A 1.0 A.qml
B 1.0 B.qml
C 1.0 C.qml
Used here
Column {
spacing: 10
Debug.A {}
Debug.B {}
Debug.C {}
}
Im looking for a way to make the Debug.C show just the red and black squares, i understand why the blue is showing but i was hoping i could make a straight forward default and then have it be removed and overwritten when used by a child element.
Ive tried to make properties that dont use alias but thats the same problem. Ive also tried just hiding the original content but that feels roundabout in that the original items are still there just not visible.
I would suggest using a Loader instead of an Item in A.qml:
import QtQuick 2.15
Rectangle {
property alias inner: inside.sourceComponent
color: "red"
width: 40
height: 40
Loader {
id: inside
}
}
Then you can replace that inside field with a Component of your choosing:
B.qml:
Debug.A {
inner: Component {
Rectangle {
width: 30
height: 30
color: "blue"
}
}
}
The data property is of type list<QtObject>. It seems like binding to it multiple times will append.
Column {
spacing: 10
A { Component.onCompleted: console.log("A", inner.length) } // 0
B { Component.onCompleted: console.log("B", inner.length) } // 1
C { Component.onCompleted: console.log("C", inner.length) } // 2
}
Similar like making the alias to the data property the default which means all child items of component A would be appended to the list.
component A: Rectangle {
default property alias inner: inside.data
color: "red"; width: 80; height: 80
Item {
id: inside
}
}
component B: A {
Rectangle { width: 40; height: 40; color: "blue" }
}
component C: B {
Rectangle { width: 20; height: 20; color: "black" }
}
To circumvent this you could make inner an Item and bind it to the data or children property.
DANGER Keep in mind that this is a "hack". You should only use it if you know what you're doing. You can't add any other item into the inner Item because the binding on data will always overwrite it.
component A: Rectangle {
property Item inner
color: "red"; width: 80; height: 80
Item {
data: [inner]
}
}
component B: A {
inner: Rectangle { width: 40; height: 40; color: "blue" }
}
component C: B {
inner: Rectangle { width: 20; height: 20; color: "black" }
}
Column {
spacing: 10
A {}
B {}
C {}
}

QML Row: How to extend qml containers (Row or Column) from other file

It seems should have a solution for sure.
Suppose I have a Test.qml file containing this:
import QtQuick 2.0
Rectangle {
color: "green"
Row {
id: row
spacing: 10
anchors.fill: parent
Rectangle {
color: "red";
width: 100;
height: 100;
}
Rectangle {
color: "red";
width: 100;
height: 100;
}
Rectangle {
color: "red";
width: 100;
height: 100;
}
}
}
Now suppose we want to use this Test.qml within another file like main.qml:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Test {
anchors.fill: parent;
// I want to be able to add new items (rects) to the row inside Test.qml
}
}
Now suppose we want to extend items to the row object in Test.qml, But we want to add from main.qml. How we can do that? is that even possible?
(FYI: The application of this feature would be to develop a placeholder form and fill the items in the other items so we can skip duplicate codes. )
You can do this without creating objects dynamically. You need to use a default property that is aliased to the contents of your Row. A default property means Items that get added to your object will actually be assigned to that property instead. In Test.qml, add this:
Rectangle {
color: "green"
default property alias contents: row.data
Row {
id: row
...
}
}
Now you can add other items to it from main.qml, like this:
Test {
anchors.fill: parent;
// Automatically gets added to 'row'
Rectangle {
color: "blue"
width: 100
height: 100
}
}
You can create objects dynamically:
MyRow.qml:
Row {
id: row
spacing: 10
anchors.fill: parent
Rectangle {
color: "red";
width: 100;
height: 100;
}
}
main.qml:
MyRow{
id: myRow
Component.onCompleted: Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "green"; width: 100; height: 100}', myRow)
}

Generic QML delegate, but non-generic View attached properties?

A View attaches properties to a delegate. For a ListView, the delegate could access the ListView.view.width and ListView.isCurrentItem properties :
Rectangle {
width: ListView.view.width
height: 40
color: ListView.isCurrentItem ? "gray" : "lightGray"
Text {
anchors.centerIn: parent
text: index
}
}
By referring to the View by its type name, it seems like the Delegate loses its genericity.
What if I want to use the same delegate within a GridView ?
You should make a Component from your delegate and set property isCurrentItem during its instantiation. In other words, create new qml file and name it e.g. "Delegate.qml" and add property bool isCurrentItem:
import QtQuick 2.4
Rectangle {
property bool isCurrentItem: false
width: parent.width
height: 20
color: isCurrentItem ? "gray" : "lightGray"
Text {
anchors.centerIn: parent
text: index
}
}
than you can use it in ListView like:
ListView {
model: 10
width: 40
height: 200
delegate: Delegate {
isCurrentItem: ListView.isCurrentItem
}
}
and similarly in GridView:
GridView {
model: 10
width: 40
height: 200
delegate: Delegate {
isCurrentItem: ListView.isCurrentItem
}
}
You could do it same way to provide width of ListView/GridView to delegate, but in this case parent.width will also work the way you want.

Better way to reparent visual items in QML

It would seem that in the design of QML user reparent was not really "envisioned", because even though it is possible, it involves creating and changing states, which is just not convenient to add to each and every item.
import QtQuick 1.0
Item {
width: 200; height: 100
Rectangle {
id: redRect
width: 100; height: 100
color: "red"
}
Rectangle {
id: blueRect
x: redRect.width
width: 50; height: 50
color: "blue"
states: State {
name: "reparented"
ParentChange { target: blueRect; parent: redRect; x: 10; y: 10 }
}
MouseArea { anchors.fill: parent; onClicked: blueRect.state = "reparented" }
}
}
I was wondering if there is a more elegant way to reparent items without polluting items with unnecessary states?
not certain if you need to use QtQuick 1.0, but with 2.0 this also works and is imo more straight forward.
import QtQuick 2.0
Item {
width: 200; height: 100
Rectangle {
id: redRect
width: 100; height: 100
color: "red"
}
Rectangle {
id: blueRect
x: redRect.width
width: 50; height: 50
color: "blue"
MouseArea { anchors.fill: parent; onClicked:
{ blueRect.parent = redRect; blueRect.x = 10; blueRect.y = 10 }
}
}
}

QtQuick2: Handle onWheel event inside of a ScrollView

I have to put component X inside of a ScrollView. Component X has to handle mouse wheel event, but ScrollView handles it. So, following example (simplified) doesn't work.
How to let Rectangle's mouse area handle OnWheel event?
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
width: 640
height: 480
ScrollView {
height: 100
width: 100
ColumnLayout{
Rectangle {
color: "red"
width: 50
height: 50
MouseArea {
anchors.fill: parent
onWheel: {
console.log("onWheel"); // it doesn't work
}
onClicked: {
console.log("onClicked"); // it works
}
}
}
}
}
}
This as actually a bug in Qt:
https://bugreports.qt.io/browse/QTBUG-38083
This is being resolved in:
https://codereview.qt-project.org/#change,82572
https://codereview.qt-project.org/#change,82576
I find a way to solve it, but I can't properly explain it. :(
This document illustrates the concept of visual parent and object parent, but it dosen't tell how they affect the event propagation.
Hope someone would give a clear explaination.
ApplicationWindow {
width: 640
height: 480
ScrollView {
id: scroll // add an id
height: 100
width: 100
ColumnLayout{
Rectangle {
id: rect // add an id
color: "red"
width: 50
height: 50
MouseArea {
parent: scroll // specify the `visual parent`
anchors.fill: rect // fill `object parent`
onWheel: {
console.log("onWheel"); // now it works
}
onClicked: {
console.log("onClicked"); // it works
}
}
}
Repeater {
model: 30
Text{ text: index }
}
}
}
}

Resources