Height of QML rectangle depending on child contents (SailfishOS app) - qt

I have a rectangle which displays some information in the center of my app, like so:
Rectangle {
id: info
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width * 0.8
height: 500
Column {
spacing: Theme.paddingLarge
anchors.top: parent.top
anchors.topMargin: Theme.paddingLarge
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 2*Theme.paddingLarge
Row {
spacing: Theme.paddingLarge * 2
anchors.horizontalCenter: parent.horizontalCenter
Text {
anchors.verticalCenter: parent.verticalCenter
font.pointSize: Theme.fontSizeLarge
text: info.brand
}
Image {
width: 64
height: 64
source: "qrc:///graphics/other.png"
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: Theme.fontSizeTiny
wrapMode: Text.WordWrap
width: parent.width
text: info.priceString
}
Row {
spacing: Theme.paddingMedium
anchors.horizontalCenter: parent.horizontalCenter
Rectangle {
width: 60
height: 30
}
Text {
anchors.verticalCenter: parent.verticalCenter
font.pointSize: Theme.fontSizeTiny
text: info.description
}
Image {
source: "qrc:///graphics/locked.png"
width: 64
height: 64
}
}
Row {
spacing: Theme.paddingMedium
anchors.horizontalCenter: parent.horizontalCenter
Button {
text: qsTr("Find")
width: (scooterInfo.width -2*Theme.paddingLarge) / 2 - Theme.paddingMedium
visible: scooterInfo.mode == "show"
}
Button {
text: qsTr("Missing")
width: (scooterInfo.width -2*Theme.paddingLarge) / 2 - Theme.paddingMedium
visible: scooterInfo.mode == "show"
}
}
}
}
Now since its a SailfishOS app, the font sizes as well as the padding will differ according to the theme. This leaves me unable to set a fixed height for the Rectangle with id info.
It already makes a difference when running in emulator and when running on my device. I can make only one of both fit, not both at the same time.
So I need a way to have the rectangle automatically pick an appropriate height according to its contents, including padding. How can I achieve this?

I tested your code and got it to work by removing the top and bottom anchors from your Column object. Columns will automatically resize themselves based on their children, so you don't need to manually restrict their height.
Rectangle {
id: info
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width * 0.8
height: childrenRect.height // Resize to fit children
Column {
spacing: Theme.paddingLarge
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 2*Theme.paddingLarge
...
}
}

Related

Why does item disappear when anchors changed?

Here is a Tab in a TabView in my qml file:
Tab {
title: "Lab"
Rectangle {
width: 300
height: 600
anchors.margins: 60
color: "gray"
// anchors.top: parent.top
// anchors.bottom: parent.verticalCenter
// anchors.margins: 10
Column {
id : buttons
// anchors.top: parent.top
// anchors.bottom: parent.verticalCenter
anchors.fill: parent
anchors.margins: 10
spacing: 2
Repeater {
model: ["Add entity", "Add Unique Comp", "Add light", "Add Prefab", "Add entity to prefab", "None"]
Rectangle {
width: parent.width - 20
height: 24
anchors.horizontalCenter: parent.horizontalCenter
radius: 3
color: "lightBlue"
Button {
width: parent.width * 0.5
height: parent.height * 0.8
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: index +": "+modelData
onClicked: {
console.log("clicked ", modelData);
}
}
}
}
}
}
}
Here a TabView is used, above is one of the tabs. x y are not specified.
If I remove anchors.fill: parent, the Rectangle will disappear on the screen. Why did this happen? Or how could I debug this?

ListView item covered by header after scrolling

I am trying to use a ListView with a simple header that stays on top of the list. It is working fine for most situations. When I scroll all the way to the top manually the top item is located below the header:
When I set the current index from outside the list to highlight an item the list scrolls to the highlighted item. This is expected and desired behavior. However the list scrolls in a way, that the highlighted item will be on the same height (y) as the header. Therefore the header partially covers the item. In the picture the header is transparent, the highlight is light green:
How can I set up the list so that the list content always starts below the header? Or as a workaround, how can I set the height of the current item after the automatic scroll on selection?
For completeness here is the current code of my list.
ListView {
id: particleList
anchors.fill: parent
model: particleModel
clip: true
highlight: Rectangle { color: Material.color(Material.Green); opacity: 0.2}
highlightMoveDuration: Style.animationDurationMedium
headerPositioning: ListView.OverlayHeader
header: Item {
height: 40
anchors.left: parent.left
anchors.right: parent.right
Row {
anchors.fill: parent
MediumText {
horizontalAlignment: Text.AlignHCenter
width: parent.width / 3
text: qsTr("Width")
}
MediumText {
horizontalAlignment: Text.AlignHCenter
width: parent.width / 3
text: qsTr("Height")
}
MediumText {
horizontalAlignment: Text.AlignHCenter
width: parent.width / 3
text: qsTr("Area")
}
}
}
footer: SmallText {
anchors.left: parent.left
anchors.right: parent.right
text: particleModel.count
}
populate: Transition {
NumberAnimation { properties: "x,y"; duration: 1000 }
}
delegate: Item {
height: 40
anchors.left: parent.left
anchors.right: parent.right
Row {
anchors.fill: parent
MediumText {
anchors.verticalCenter: parent.verticalCenter
width: parent.width / 3
text: model.width
horizontalAlignment: Text.AlignRight
rightPadding: 20
}
MediumText {
anchors.verticalCenter: parent.verticalCenter
width: parent.width / 3
text: model.height
horizontalAlignment: Text.AlignRight
rightPadding: 20
}
MediumText {
anchors.verticalCenter: parent.verticalCenter
width: parent.width / 3
text: model.area
horizontalAlignment: Text.AlignRight
rightPadding: 20
}
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 1
visible: model.index !== 0
color: Material.color(Material.Grey)
}
}
}
The Listview rows always scroll behind the header.
So make it opaque (e.g. Rectangle with background instead of item) and increase the z value to have it on top.
For the scrolling use highlightRangeMode, preferredHighlightBegin and preferredHighlightEnd.

Qml Text Size based on Container

I am using Qt Quick to develop an app (Qt 5.8). I have text that appears in multiple places so I created a component to use to display the text. The areas the text can vary in placement and size. How can I adjust my text so that the data displays in 1 line horizontally and all text is the same size if I simply want to display the following?
FLAPS 1
GEAR DOWN
TRIM -1.0
I used Text and was able to get close, however since GEAR DOWN has more characters the text was smaller, than flaps and trim. So I moved on to using Label. Can someone provide better insight on how to have text size based on its container's width or height?
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
id: windowRoot
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle{
height: windowRoot.height * .80
width: windowRoot.width * .80
color: "green"
anchors.centerIn: parent
Rectangle {
id: rect1
opacity: .5
anchors.top: parent.top
anchors.left: parent.left
color: "lime"
border.color: "orange"
height: parent.height * 1/2
width: parent.width * 1/2
TextHolder {
anchors.top: parent.top
anchors.left: parent.left
width: parent.width * 1/4
height: parent.height
}
}
Rectangle {
id: rect2
anchors.top: parent.top
anchors.left: rect1.right
color: "yellow"
border.color: "blue"
height: parent.height * 1/2
width: parent.width * 1/2
}
Rectangle {
id: rect3
anchors.top: rect1.bottom
anchors.left: parent.left
color: "pink"
border.color: "red"
height: parent.height * 1/2
width: parent.width * 1/2
TextHolder {
anchors.top: parent.top
anchors.left: parent.left
width: parent.width * 1/4
height: parent.height * 1/2
}
}
Rectangle {
id: rect4
anchors.top: rect2.bottom
anchors.left: rect3.right
color: "orange"
border.color: "lime"
height: parent.height * 1/2
width: parent.width * 1/2
TextHolder {
anchors.bottom: parent.bottom
height: parent.height * 1/4
width: parent.width * 1/4
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
TextHolder.qml
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.1
Rectangle {
color: "purple"
border.color: "steelblue"
border.width: 3
property color colorOfText: "white"
property real textSize: 48
Item {
id: inputs
property int flapHndl: 1
property int gearHndl: 1
property real trim: -1.0
}
clip: true
ColumnLayout {
anchors.top: parent.top
width: parent.width
height: parent.height
spacing: 5
Label {
id: flapLabel
text: "FLAPS " + inputs.flapHndl
color: colorOfText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: textSize
fontSizeMode: Text.HorizontalFit
Layout.fillWidth: true
}
Label {
id: gearLabel
text: {
if (inputs.gearHndl === 1)
return "GEAR DOWN"
else
return "GEAR UP"
}
color: colorOfText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: textSize
fontSizeMode: Text.HorizontalFit
Layout.fillWidth: true
}
Label {
id: trimLabel
text: "TRIM " + inputs.trim.toFixed(1)
color: colorOfText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: textSize
fontSizeMode: Text.HorizontalFit
Layout.fillWidth: true
}
}
}
Can someone provide better insight on how to have text size based on
its container's width or height?
For evaluating the text width and height we can definitely use QML type TextMetrics and having the metrics we can then attempt to fit the text in. If the text still won't fit we can then try to adjust the font size. For that, some logic might need to be implemented with JavaScript. So, the below code is an example of the "feedback" type of solution.
Rectangle {
property string textToShow
property int widthLimit: 400
onTextToShowChanged: {
while (txtMeter.width > widthLimit) {
txtMeter.font.pixelSize --;
}
}
TextMetrics {
id: txtMeter
font.family: "Courier"
font.pixelSize: 25
text: textToShow
}
Text {
font: txtMeter.font
text: textToShow
}
}
P.S. This is just a sketchy idea and your actual implementation may differ
There is fontSizeMode property of Text
Text {
id: goToParentFolderText
anchors.fill: parent
font.family: fontAwesomeSolid.name
text: "\uf060"
fontSizeMode: Text.Fit
font.pointSize: 100
color: Material.accent
}
I was able to get #Alexander V's answer to work, with a minor change. The outer textToShow property was processing its update before the TextMetrics block was updated (which caused the width calculation to be incorrect). You can fix this issue by triggering onTextChanged inside the TextMetrics blocks instead.
Rectangle {
property string textToShow
property int widthLimit: 400
TextMetrics {
id: txtMeter
font.family: "Courier"
font.pixelSize: 25
text: textToShow
onTextChanged: {
while (txtMeter.width > widthLimit) {
txtMeter.font.pixelSize --;
}
}
}
Text {
font: txtMeter.font
text: textToShow
}
}

qml using Row to align components in the center

I am using Row to layout some buttons on a Rectangle which is my custom toolbar implementation. The problem is no matter what I do, the components are always aligned from the left. I would like them to be aligned with the center of the row and flowing outwards towards the edges. The code looks as follows:
Rectangle {
id: toolbar
color: "green"
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 100
Row
{
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
spacing: 60
ToolButton {
height: parent.height
Image {
anchors.verticalCenter: parent.verticalCenter
source: "../images/image.png"
}
}
ToolButton {
height: parent.height
Image {
anchors.verticalCenter: parent.verticalCenter
source: "../images/image.png"
}
}
}
}
My buttons are always laid out starting from the left side of the row. Rather I would like to have them laid out relative to the center of the toolbar. I thought specifying this line anchors.horizontalCenter: parent.horizontalCenter should achieve that but no matter what I try, the components are laid out from the left boundary.
If you set your Row's alignments to center in the parent object and then make the Row's width adjust to the childrenRect's width then you can have items expand from the center of the object. Note: you may need to set the widths of the ToolButton's in order for the childrenRect to have it's width value populated.
Rectangle {
id: toolbar
color: "green"
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 100
Row
{
anchors{
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
height: parent.height
width: childrenRect.width
spacing: 60
ToolButton {
height: parent.height
width: 50
Image {
anchors.verticalCenter: parent.verticalCenter
source: "../images/image.png"
}
}
ToolButton {
height: parent.height
width: 50
Image {
anchors.verticalCenter: parent.verticalCenter
source: "../images/image.png"
}
}
}
}
You've set anchors.fill: parent on the Row so it will of course fill its parent. Instead, you should remove this, and only set height: parent.height on the Row.
Documentation citation:
since a Row automatically positions its children horizontally, a child item within a Row should not set its x position or horizontally anchor itself using the left, right, anchors.horizontalCenter, fill or centerIn anchors. If you need to perform these actions, consider positioning the items without the use of a Row.
Row is only for positioning it`s children horizontally. Without any 'flows' or centering. It is for automatic positioning in a row which let you exclude defines of anchors and margins inside item when you need to perform that simple task.
But if you need something more complicated, you need to do it with anchors and margins manually. For example. Centering items and spreading them from center to the edges might look like this:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: toolbar
color: "green"
anchors.centerIn: parent
height: 100
width: parent.width
Rectangle {
id: toolbutton1
height: parent.height
anchors {
right: toolbutton2.left
margins: 20
}
width: 100
color: "blue"
}
Rectangle {
id: toolbutton2
height: parent.height
anchors {
right: parent.horizontalCenter
margins: 10
}
width: 100
color: "magenta"
}
Rectangle {
id: toolbutton3
height: parent.height
anchors {
left: parent.horizontalCenter
margins: 10
}
width: 100
color: "red"
}
Rectangle {
id: toolbutton4
height: parent.height
anchors {
left: toolbutton3.right
margins: 20
}
width: 100
color: "yellow"
}
}
}

QML does not center the component

I have a simple component which is designed to show an image with some text under it as follows:
Rectangle{
id: functionView
RowLayout {
spacing: 350
anchors.centerIn: parent
Item {
Image {
id: upload_pic
source: "http://icons.iconarchive.com/icons/custom-icon-design/mono-general-4/128/upload-icon.png"
Text {
text: "Upload Images"
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: -20
}
}
Layout.fillWidth: true
}
Item {
Image {
id: workflow_pic
source: "http://icons.iconarchive.com/icons/icons8/windows-8/128/Data-Workflow-icon.png"
Text {
text: "Upload Workflow"
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: -20
}
}
Layout.fillWidth: true
}
}
}
However, this is not aligned (either vertically or horizontally) on the component window. This can be verified using qmlscene. The images are directly linked in the component source.
You are trying to center your RowLayout in its parent, which is a Rectangle that doesn't have its width property set.
Set the rectangle's width to some value or to its parent width for example by width: parent.width or by setting its anchors like for example:
anchors.left: parent.left
anchors.right: parent.right

Resources