5
votes

I have rectangles with a certain aspect ratio and rectangles with the inverse aspect ratio. I would like to arrange them in a grid layout of my choosing (doesn't need to be a regular grid, on the contrary: I'd prefer a solution where I can build up RowLayouts and ColumnLayouts at will).

I know I can have scaling items in my Layouts using Layout.fillHeight and Layout.fillWidth. Unfortunately, I can find no way to properly define aspect ratios for my Rectangles. I know QML Image can do it (through its fillMode property) but I see no simple way of doing it nicely.

Any help or pointers in the right direction would be much appreciated!

Note I'm assuming QML Layouts is the way to go, but if there is a functional solution with just anchors or a plain Row/Column setup, I'm all for it!

Also note that I would prefer to keep the area of te two types of Rectangle the same, as it seems while experimenting, this is not so trivial...

EDIT

An attempt of what I mean, minus the equal area constraint. The rectangles fill the width, but leave space in the height because they are constrained by their aspect ratio and the filled width. Same should go for the height, but I'm failing at combining the two.

1

3
Could you provide an image describing what you want to achieve ? It'll help understanding and hopefully answering your question.GrecKo
@GrecKo an attempt that covers about 1/3 of my constraints. I'm showing you the one that's satisfied ;-).rubenvb
As a picture I meant one that fills all your constraints, it could be done in paint, and doesn't have to be done in QML, otherwise you wouldn't need much help ;)GrecKo
Well, I'd need several of them to actually show all of them. I'll try to make them later!rubenvb
You could take a look at github.com/Ableton/aqt-cassowary if what you want can't be done with layouts. I haven't really used it so I can't provide more details, but it seems like a good lead.GrecKo

3 Answers

2
votes

I have been struggling for few days on the proper way to do that. I could not found any working exemple so I wrote my own.

This rectangle will always respect its aimedRatio and will keeps its margins. The trick here is to treat to different cases : Either the parents ratio is larger that the aimed one or not. In one case you bind the width to parent and set height according to. In the other case you do it the other way.

Rectangle {
    color  : 'green'

    // INPUTS
    property double  rightMargin    : 20
    property double  bottomMargin   : 20
    property double  leftMargin     : 20
    property double  topMargin      : 20
    property double  aimedRatio     : 3/4

    // SIZING
    property double  availableWidth  : parent.width  - rightMargin  - leftMargin
    property double  availableHeight : parent.height - bottomMargin - topMargin

    property bool    parentIsLarge   : parentRatio > aimedRatio

    property double  parentRatio     : availableHeight / availableWidth

    height : parentIsLarge ? width * aimedRatio :  availableHeight
    width  : parentIsLarge ? availableWidth     :  height / aimedRatio

    anchors.top        : parent.top
    anchors.topMargin  : topMargin
    anchors.left       : parent.left
    anchors.leftMargin : leftMargin
}

Hope it will help out people who arrive here with google search !

0
votes

You can use property binding to bind the width of the rectangle with height using aspect ratio as follows,

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.0

Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    property int minWidth: 150
    property int maxWidth: 300
    property int minHeight: 150
    property int maxHeight: 250
    property int rowWidth: root.width/3 //Change accordingly the width of each child item w.r.t. the width of the root.
    Row {
        id: layout
        anchors.fill: parent
        spacing: 6
        Rectangle {
            color: 'red'
            // 4/3 is the aspect ratio of the first Rectangle
            height: (3*width/4)<root.minHeight ? root.minHeight : ( (3*width/4)>root.maxHeight ? root.maxHeight : 3*width/4 )
            width: (root.rowWidth) < root.minWidth ? root.minWidth : ( root.rowWidth > root.maxWidth ? root.maxWidth : root.rowWidth)
            Text {
                anchors.centerIn: parent
                text: parent.width + 'x' + parent.height
            }
        }
        Rectangle {
            color: 'green'
            // 3/4 is the aspect ratio of the second Rectangle
            height: (4*width/3)<root.minHeight ? root.minHeight : ( (4*width/3)>root.maxHeight ? root.maxHeight : 4*width/3 )
            width: (root.rowWidth) < root.minWidth ? root.minWidth : ( root.rowWidth > root.maxWidth ? root.maxWidth : root.rowWidth)
            Text {
                anchors.centerIn: parent
                text: parent.width + 'x' + parent.height
            }
        }
    }
}
0
votes

I did upvote the answer of @CharlesSALA (because it works!), but thought I would also offer an alternative answer based on layouts.

The AspectItem is responsible for providing the correct margin to maintain the width, using the implicitWidth. The Layout is responsible for providing the correct margin to maintain the height, using the Layout.maximumHeight.

The same will work in a GridLayout in the place of the ColumnLayout & RowLayout. The result is slightly different, because the columns must stay aligned in all of the rows, but in both cases the aspect ratio of the rectangles is preserved as required.

AspectItem.qml

import QtQuick 2.11

Item {
    id: container
    property real aimedRatio: 3/4
    property alias color: content.color
    implicitWidth: height*container.aimedRatio
    implicitHeight: width/container.aimedRatio

    Rectangle {
        id: content
        anchors.horizontalCenter: container.horizontalCenter

        height: container.height
        implicitWidth: height*container.aimedRatio
    }
}

main.qml

import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11

ApplicationWindow {
    id: window
    visible: true
    width: 800
    height: 800
    y: 0

    ColumnLayout {
        anchors.fill: parent

        RowLayout {
            width: parent.width
            AspectItem {
                color: "darkseagreen"
                aimedRatio: 1/2
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }

            AspectItem {
                color: "seagreen"
                aimedRatio: 2/1
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }

            AspectItem {
                color: "lightseagreen"
                aimedRatio: 1/2
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }
        }

        RowLayout {
            width: parent.width
            AspectItem {
                color: "darkcyan"
                aimedRatio: 2/1
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }

            AspectItem {
                color: "cyan"
                aimedRatio: 1/2
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }

            AspectItem {
                color: "lightcyan"
                aimedRatio: 2/1
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }
        }

        RowLayout {
            width: parent.width
            AspectItem {
                color: "darksalmon"
                aimedRatio: 1/2
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }

            AspectItem {
                color: "salmon"
                aimedRatio: 2/1
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }

            AspectItem {
                color: "lightsalmon"
                aimedRatio: 1/2
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.maximumHeight: implicitHeight
            }
        }
    }
}

Result

At launch:

At Launch

Stretched narrow:

Stretched narrow

Stretched wide:

Stretched wide