I have a 'base' component which controls the aspect ratio of the item 'childItem':
//AspectRatio.qml
import QtQuick 2.12
Rectangle
{
property real targetAspectRatio: 16 / 9
color: "black"
anchors.fill: parent
onWidthChanged:
{
var _ratio = parent.width / parent.height
var _width = 0
if(_ratio > targetAspectRatio) //Too wide
{
_width = parent.height * targetAspectRatio
}
else
{
_width = parent.width
}
childItem.width = _width
}
onHeightChanged:
{
var _ratio = parent.width / parent.height
var _height = 0
if(_ratio > targetAspectRatio) //Too wide
{
_height = parent.height
}
else
{
_height = parent.width / targetAspectRatio
}
childItem.height = _height
}
Item
{
id: childItem
anchors.centerIn: parent
}
}
I want to use AspectRatio.qml as a generic component and override 'childItem', depending on the context the component is used. How can 'childItem' be overridden, like this?
//child.qml
AspectRatio
{
childItem : Rectangle
{
color: "red"
}
}
This mechanism is also used in standard qml components, like here. But it's unclear to me how to achieve this.
CodePudding user response:
There are multiple solutions for your problem.
You could create a childItem property and manually put it in the children property of Item
like so:
// AspectRatio.qml
Rectangle {
property real targetAspectRatio: 16 / 9
property Item childItem
children: [childItem]
color: "black"
anchors.fill: parent
onWidthChanged: // ...
onHeightChanged: // ...
}
// Usage
AspectRatio {
childItem : Rectangle {
color: "red"
}
}
Alternatively you could do it the other way and bind the childItem to the children and not have an explicit setter and your property just being a "view" into children
:
readonly property Item childItem: children[0]
// Usage
AspectRatio {
Rectangle { /* ... */ }
}
To be able to use both syntax you could use a default property, overriding the Item's children default property:
default property Item childItem
children: [childItem]
// Usage
AspectRatio {
Rectangle { /* ... */ }
}
// OR
AspectRatio {
childItem: Rectangle { /* ... */ }
}
I'd say this one is my preferred solution.
You didn't ask but I would replace your imperative code in onWidth/heightChanged by declarative bindings (and move the anchors outside):
Rectangle {
id: root
property real targetAspectRatio: 16 / 9
default property Item childItem
children: [childItem]
color: "black"
Binding {
target: root.childItem
property: "width"
value: Math.min(root.height * root.targetAspectRatio, root.width)
}
Binding {
target: root.childItem
property: "height"
value: Math.min(root.width / root.targetAspectRatio, root.height)
}
}
CodePudding user response:
You can't "override" an item, but you can create a property that points to the child list of an item. This is how things like the background
property that you mentioned on controls work. By assigning to this property, you're essentially inserting an object as a child of whatever the containing Item is.
//AspectRatio.qml
Rectangle
{
property alias childItem: childContainer.data
Item
{
id: childContainer
anchors.centerIn: parent
}
}
//child.qml
AspectRatio
{
childItem : Rectangle
{
width: 100
height: 100
color: "red"
}
}