Qml Grid, Column, and RowLayout does not work with LayoutMirroring - c++

I am writing a qml application that should support RTL and LTR languages and the interface needs to be some how flexible, and anchors may not produce good UI
So I planned to use qml Grid, Column, and RowLayout, they work good but does not get mirrored when I use
LayoutMirroring.enabled: true
LayoutMirroring.childrenInherit: true
is there any way to use these layout components with LayoutMirroring.enabled: true
and if not how to set width and height for qml positioners (Row,Column,and Grid) to fill thier bounding item width and height

LayoutMirroring is not available for RowLayout, ColumnLayout or GridLayout. You can use Row[View], Column[View] or Grid[View] instead. See http://qt-project.org/doc/qt-5.1/qtquick/qml-qtquick2-layoutmirroring.html for more information.
Here is a quick example for qml positioners:
Rectangle {
width: 640
height: 480
color: "#303030"
Rectangle {
width: parent.width / 1.1
height: parent.height / 1.1
anchors.centerIn: parent
color: "white"
Grid {
id: grid
anchors.fill: parent
columns: 2
spacing: 6
columnSpacing: spacing
rowSpacing: spacing
property int rowCount: Math.ceil(visibleChildren.length / columns)
property real cellWidth: (width - (columns - 1) * columnSpacing) / columns
property real cellHeight: (height - (rowCount - 1) * rowSpacing) / rowCount
Rectangle {
color: "#aa6666"
width: grid.cellWidth
height: grid.cellHeight
}
Rectangle {
color: "#aaaa66"
width: grid.cellWidth
height: grid.cellHeight
}
Rectangle {
color: "#9999aa"
width: grid.cellWidth
height: grid.cellHeight
}
Rectangle {
color: "#6666aa"
width: grid.cellWidth
height: grid.cellHeight
}
}
}
}
Change the number of columns and add or remove some rectangles, to see that it works.

in Qt 5.2 RowLayout, ColumnLayout and GridLayout have layoutDirection property to support RTL layout

Related

QML label inflate background rect

Okay, I give up. How do I draw backing rectangle around Label?
Here's what I do
import QtQuick 2.0
import QtQuick.Controls 2.15
Label {
id: label
text: root.text
font {
pointSize: 24
bold: true
}
wrapMode: Text.WordWrap
background: Rectangle{
radius: 20
color: "lightgreen"
anchors.centerIn: parent
width: parent.width + 30
height: parent.height + 30
}
}
And that sure draws rounded rect around Label, but the size of the label stays the same, so layout can't position it right. How the heck do I make it work?
I think what you're looking for is padding. Don't add anything to the height/width of the background. Just add padding.
Label {
id: label
text: root.text
font {
pointSize: 24
bold: true
}
wrapMode: Text.WordWrap
padding: 15
background: Rectangle{
radius: 20
color: "lightgreen"
width: parent.width
height: parent.height
}
}

QML Unable to change position of item while being dragged by MouseArea

Context
I am trying to create a tiled map using QML without using QtLocation. I am trying to design the application such that only essential tiles are being loaded. Here is the strategy I have come up with.
Each tile is 256x256 px. The tiles are placed in a Grid class with nested Repeaters. The xOffset determines the tiles that should be loaded into the grid.
Grid{
id: mapgrid
rows: 6
columns: 9
spacing: 1
Repeater{
model: 6
Repeater{
model: 9
property int outerIndex: index
Tile{
imgsrc: "image://provider/" + maprect.zoomLevel + '/' + (index + maprect.xOffset) + '/' + (outerIndex+maprect.yOffset)
}
}
}
}
When the grid is shifted by 256 pixels left, a new set of tiles should be loaded in. This can be achieved by changing the offset values. I can then shift the grid by 256 pixels again so its back in the view.
onXChanged: {
if(x <= -512){
maprect.x = -256;
maprect.xOffset++;
}
}
Problem
The movement of my grid is controlled by a MouseArea with drag.target set to it. It appears that the MouseArea controls the coordinates until the mouse is released. Here is a simple example:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Text{
z:1
text: "X: " + rect.x
}
MouseArea{
id:marea
anchors.fill: rect
drag.target: rect
}
Rectangle{
id: rect
height: 256
width: 256
color: "red"
onXChanged: {
if(x > 100){
x = 0;
}
}
}
}
If you move the box right past x > 100, indeed x=0 is set. However that is because the actual value of the box is still 100+. If you move the box left again (x<100) without releasing, you'll find that the box is back at its actual x value. Sorry if it sounds confusing, its easier to understand to see it for yourself.
What I am looking for is an alternative way to load tiles or a fix to the MouseArea problem such that I can actually change the position of an item while its being dragged by the MouseArea.
Also, would it be possible for me to implement a different version of MouseArea so I can deal with this problem at a lower abstraction?
Update
I have been able to achieve the effect I wanted to a certain degree. It appears to be working fine but QML is detecting a binding loop.
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: wind
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle{
id: wrapper
height: wind.height
width: wind.width
MouseArea{
id:marea
anchors.fill: wrapper
//drag.target: rect
property int initialX: 0
property int iniMouseX;
onEntered: {
initialX = rect.x
iniMouseX = mouseX
console.log ("set")
}
}
Rectangle{
id: rect
height: 256
width: 256
border.color: "green"
x: marea.initialX + (marea.mouseX - marea.iniMouseX)
color: "red"
onXChanged: {
if(rect.x > 200){
marea.initialX -= 200;
}
}
}
}
}
https://imgur.com/a/zmu5eiO
The problem with the updated code is initialX bind with rect.x, so if rect.x be updated it will invoke onXChanged, and if rect.x > 200 will update initialX again, this will become an infinite loop.
So to fix it here is the solution, make the event goes like flow :
Window {
id: wind
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle{
id: wrapper
height: wind.height
width: wind.width
MouseArea{
id:marea
anchors.fill: wrapper
//drag.target: rect
property int initialX: 0
property int iniMouseX;
onEntered: {
initialX = rect.x
iniMouseX = mouseX
console.log ("set")
}
onMouseXChanged: {
if(rect.x > 200){
marea.initialX -= 200;
}
rect.x = marea.initialX + (marea.mouseX - marea.iniMouseX)
}
}
Rectangle{
id: rect
height: 256
width: 256
border.color: "green"
color: "red"
}
}
}

Hover effect is scrolling the list on a QML GridView

I have a QML GridView with a scrollbar and hover effect, when I move the curso to the page's bottom the hover make an automatic scrolling, I want to stop it. I tried to set the interactive property of Flickable to false "interactive: false" but it didn't work. How can I stop this behavior?
Obs: When I remove the hover effect the scroll behave in the expected way, just moving through the scrollbar.
GridView{
id: grid
anchors.margins: 20
anchors.fill: parent
cellHeight: 80
cellWidth: 80
model: MyModel{}
highlight: Rectangle {
color: "lightsteelblue"
height: parent.cellHeight
width: parent.cellWidth
z:2
opacity: 0.7
}
delegate: Column {
Rectangle {
color: myColor;
height: grid.cellHeight * 0.7
width: grid.cellWidth * 0.7
border.color: "white"
anchors.margins: 5
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
id: mouseRegion
anchors.fill: parent
hoverEnabled: true
onEntered: grid.currentIndex = model.index
}
}
Text {
text: name;
anchors.horizontalCenter: parent.horizontalCenter }
}
}
Here is the Link to a simple project that reproduces the behavior: https://drive.google.com/open?id=0B7WUSCDDdwtIbWVDWVFvMjM1djA
My workmate help me to figured out a solution to my problem. I was using currentIndex property to set the hover position in the gridview, as described on documentation this property will smoothly scroll the GridView in the way that the current item becomes visible if the highlightFollowsCurrentItemis set to true (default value) and will not scroll if highlightFollowsCurrentItem set to false.
However, after set highlightFollowsCurrentItem property to false, the automatic scroll stopped together with my hover effect, which is clearly unwanted. I couldn't figure out what was wrong and we came out with a different approach.
To make the hover work without the automatic scrolling provided by currentIndex behavior, we remove it from gridview and used onEntered and onExited to control hover behavior, changing the rectangle visibility (id:selectedItem) used to simulate the hover effect as showed below.
GridView{
id: grid
anchors.margins: 20
anchors.fill: parent
cellHeight: 80
cellWidth: 80
model: MyModel{}
delegate: Item{
height: grid.cellHeight * 0.9
width: grid.cellWidth * 0.7
Rectangle {
id: selectedItem
color: "lightsteelblue"
height: parent.height
width: parent.width
z:12
opacity: 0.7
visible: false
}
Rectangle {
id:rect
color: myColor;
height: parent.height-textName.height
width: parent.width
border.color: "white"
anchors.margins: 5
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
id: mouseRegion
anchors.fill: parent
hoverEnabled: true
onEntered: selectedItem.visible = true
onExited: selectedItem.visible = false
}
}
Text {
id: textName
text: name;
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
}
}
}
It allowed me to avoid automatic scroll and keep the hover effect work properly.

QML ScrollView with ColumnLayout

I am trying to create a scroll view around a ColumnLayout, unfortunately my current code doesn't work. I know about ListView, but in my case I need to create scrollable Layout, because it will contain heterogeneous elements.
ApplicationWindow {
id: mainwindow
title: qsTr("Hello World")
width: 300
height: 300
visible: true
ScrollView {
anchors.fill: parent
ColumnLayout {
width: mainwindow.width
Image {
anchors.bottomMargin: 10
source: "img/button.jpg"
width: parent.width
height: 400
}
Image {
source: "img/button.jpg"
width: parent.width
height: 500
}
}
}
}
This renders to this (which is clearly not what I want):
There are two problems:
Images are not stretched across the entire window width, parent.width is ignored. I want images to have exact same width as ScrollView (no horizontal scroll)
Image height property is ignored
What am I doing wrong?
I would go with a plain column and access the desired width property directly by id. As I understand these container elements are measuring their size depending on their content, that might be the reason why setting the ColumnLayouts width has no effect.
This works for me:
ScrollView
{
anchors.fill: parent
Column {
Repeater {
model: 4;
delegate: Item {
width: root.width;
height: image.sourceSize.height;
Image {
id: image;
anchors.centerIn: parent;
width: parent.width;
fillMode: Image.Stretch;
source: "img" + (index+1) + ".png"
}
}
}
}
}
In my case root is just the parent's id. Hope this helps!
Same problem on my side. This worked for me :
ScrollView {
width: parent.width
height : parent.height
contentWidth: column.width // The important part
contentHeight: column.height // Same
clip : true // Prevent drawing column outside the scrollview borders
Column {
id: column
width: parent.width
// Your items here
}
}

Resizing windows to match screen size in Qt

In OpenGL terms, what I want to do is modify the projection matrix of a Qt GUI.
Pretend the window is 480x640. It is displayed as normal, and rendered to a texture.
I then take that texture, and stretch it across the entire screen.
Does Qt have something like that? I don't want the GUI looking fine and having appropriately-sized text on a 480x640 tablet, but then it gets loaded up on a 1536x2048 tablet and you need a magnifying glass for the text.
I've written my own GUI in OpenGL before, calculating a vid.width/BASEWIDTH, vid.height/BASEHEIGHT ratio and multiplying the modelview matrix of elements to ensure that a GUI always fills a screen and stays the same size -- obviously this only works perfectly providing the aspect ratio is the same, but I digress.
I messed with layouts in Qt Quick for awhile, and it offers some nice anchoring options, but nothing for stuff like scaling up the text if the parent window is larger. Or am I missing something here?
An OpenGL GUI I wrote had a few options for control position coordinates:
Origin for transforms (Top, Center, Bottom, Left, Center, Right)
PosIsPercentage (specified whether the position coordinates were to be interpreted as a percentage of screen width/height)
This allowed you to set the position as a distance from any edge of the screen, or you could set PosIsPercentage = true and set the X value to 75 to have the coordinate always be at 3/4ths of whatever the screen size was.
There was also a SizeIsPercentage value, so you could set a button to be 10% of the screen width.
I see some of these options in the Qt Quick designer, but they aren't behaving as I expect.
I know this is hard to explain, so here is an image to demonstrate:
http://www.spaddlewit.com/QtLayoutProblem.png
(not what I'm using Qt for, but a good example of the problem I'm having)
Scaling items based on the width and height of the screen works well enough, except when you move to a high DPI device. A better method is to scale items based on the height of the default font. The default font size of a Text item, for example, will always be legible on platforms supported by Qt, no matter the DPI. You can use the same principle to scale font sizes; multiply the default font size by some amount.
Below I've done a quick mock up of the screenshot you linked to:
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
id: window
contentItem.implicitWidth: 640
contentItem.implicitHeight: 480
contentItem.minimumWidth: 640
contentItem.minimumHeight: 480
contentItem.maximumWidth: 1024
contentItem.maximumHeight: 768
/*
With Qt 5.4, you can also use the new FontMetrics item,
which saves you the overhead of creating a Text item:
For example:
FontMetrics {
id: fontMetrics
}
Then:
font.pixelSize: fontMetrics.font.pixelSize * 4
anchors.margins: fontMetrics.implicitHeight * 2
*/
Text {
id: defaultText
}
Image {
source: "http://cdn2.landscapehdwalls.com/wallpapers/1/perfect-green-hills-1197-1280x800.jpg"
}
Item {
id: container
anchors.fill: parent
anchors.margins: defaultText.implicitHeight * 2
Column {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: container.anchors.margins
Text {
id: yourGameText
text: "Your Game!"
font.pixelSize: defaultText.font.pixelSize * 4
wrapMode: Text.Wrap
}
ListView {
interactive: false
anchors.right: parent.right
width: yourGameText.width
height: container.height * 0.3
model: ["Play Game!", "Options", "Exit"]
delegate: Button {
text: modelData
width: ListView.view.width
}
}
}
Row {
anchors.left: parent.left
anchors.bottom: parent.bottom
spacing: container.anchors.margins
Image {
source: "http://www.facebookbrand.com/img/assets/asset.f.logo.lg.png"
width: defaultText.implicitHeight * 3
height: width
}
Image {
source: "http://g.twimg.com/Twitter_logo_white.png"
width: defaultText.implicitHeight * 3
height: width
}
Image {
source: "http://www.youtube.com/yt/brand/media/image/YouTube-logo-full_color.png"
width: defaultText.implicitHeight * 3
height: width
}
}
}
}
Window Size
The first thing I did was set the default, minimum and maximum size of the window.
Scaling
Next, I created an empty Text item which items and text sizes will be based off. It might seem hackish, and it is a bit, but it also works really well. As mentioned in the comment, in Qt 5.4 there will be a FontMetrics type which you can use instead of creating a Text item that will never actually be shown.
Another alternative is to use Screen's pixelDensity property.
Margins
You said you wanted to:
set the position as a distance from any edge of the screen
I did that by creating an Item that fills the window, and then setting the margins from the edges of the window as some factor of the default font's implicit height. This ensures that the content within the item will be the same physical distance (e.g., in millimetres) from the edge of the window regardless of the DPI of the device you're viewing it on. If you'd rather the distance be larger if the window is larger, you can do this instead:
anchors.margins: window.width * 0.1
Font Sizes
Take a look at the Text item within the Column. If you want to ensure the text is also the same physical size on the screen, you can set font.pixelSize to be the default font's size multiplied by some amount. Again, if you'd rather base it off the size of the screen rather than the DPI, you can do this instead:
font.pixelSize: window.height * 0.05
More Information
The Scalability documentation also gives a nice overview on this topic.
Below is a screenshot of the application running:
The following works, but it's annoying -- you have to create a scaleWidth and scaleHeight function and wrap any constant coordinates in them.
Font sizes scale along the shortest edge of the screen -- this application is a Portrait-only orientation, so it uses scaleWidth(pointSize) for font sizes.
Would be nice to find a solution that's compatible with the QML designer.. is there any way to automatically insert this calculation, maybe afterwards in C++ code at runtime?
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
ApplicationWindow {
id:window
visible: true
width: 480
height: 640
function scaleWidth(w)
{
return w * (width / 480.0)
}
function scaleHeight(h)
{
return h * (height / 640.0)
}
Text {
id: defaultText
}
Image {
id: image1
x: 0
y: 0
width: window.width
height: window.height
fillMode: Image.Stretch
source: "http://cdn2.landscapehdwalls.com/wallpapers/1/perfect-green-hills-1197-1280x800.jpg"
Label {
id: lblTitle
x: 0
y: scaleHeight(8)
text: qsTr("Welcome to the App")
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: scaleWidth(36)
horizontalAlignment: Text.AlignHCenter
}
Label {
id: lblSubtitle
x: 0
text: qsTr("Login to Continue")
font.pointSize: scaleWidth(24)
anchors.top: lblTitle.bottom
anchors.topMargin: scaleHeight(8)
anchors.horizontalCenter: lblTitle.horizontalCenter
}
Item {
id: itemCenterAlign
x: 0
y: 0
width: 0
height: 200
anchors.horizontalCenter: parent.horizontalCenter
}
Label {
id: lblUsername
x: 0
text: qsTr("Username:")
anchors.top: lblSubtitle.bottom
anchors.topMargin: scaleHeight(64)
font.bold: true
font.pointSize: scaleWidth(24)
anchors.right: itemCenterAlign.left
anchors.rightMargin: scaleWidth(8)
}
TextField {
id: txtUsername
width: scaleWidth(224)
height: scaleHeight(43)
anchors.left: itemCenterAlign.right
anchors.leftMargin: scaleWidth(8)
anchors.top: lblSubtitle.bottom
anchors.topMargin: scaleHeight(64)
font.pointSize: scaleWidth(24)
placeholderText: qsTr("Username")
}
Label {
id: lblPIN
x: 0
y: scaleWidth(-8)
text: qsTr("PIN:")
font.bold: true
font.pointSize: scaleWidth(24)
anchors.topMargin: scaleHeight(12)
anchors.right: itemCenterAlign.left
anchors.rightMargin: scaleWidth(8)
anchors.top: lblUsername.bottom
}
TextField {
id: txtPIN
x: 0
y: 0
width: scaleWidth(224)
height: scaleHeight(43)
placeholderText: qsTr("PIN")
font.pointSize: scaleWidth(24)
anchors.topMargin: scaleHeight(8)
anchors.leftMargin: scaleWidth(8)
anchors.left: itemCenterAlign.right
anchors.top: txtUsername.bottom
}
Row {
id: row1
x: 0
y: scaleHeight(277)
width: scaleWidth(464)
height: scaleHeight(115)
spacing: scaleWidth(8)
anchors.horizontalCenter: parent.horizontalCenter
Button {
id: cmdQuit
text: qsTr("Quit")
width: row1.width / 3 - row1.spacing / 2
height: row1.height
}
Button {
id: cmdGPSOnly
text: qsTr("GPS Only")
width: row1.width / 3 - row1.spacing / 2
height: row1.height
}
Button {
id: cmdLogin
text: qsTr("Login")
width: row1.width / 3 - row1.spacing / 2
height: row1.height
}
}
Button {
id: cmdAbout
width: cmdQuit.width
height: scaleHeight(44)
text: qsTr("About")
anchors.top: row1.bottom
anchors.topMargin: scaleHeight(8)
anchors.left: row1.left
anchors.leftMargin: 0
}
Label {
id: lblVersion
y: 619
text: qsTr("v3.0.0.0")
font.pointSize: scaleWidth(16)
anchors.bottom: parent.bottom
anchors.bottomMargin: scaleHeight(8)
anchors.left: parent.left
anchors.leftMargin: scaleWidth(8)
}
Label {
id: lblBooks
x: 0
y: lvBooks.y
text: qsTr("Books Loaded:")
horizontalAlignment: Text.AlignRight
font.pointSize: scaleWidth(24)
anchors.right: lvBooks.left
anchors.rightMargin: scaleWidth(8)
}
Rectangle
{
x: lvBooks.x
y: lvBooks.y
width: lvBooks.width
height: lvBooks.height
color: "white"
border.color: "black"
}
ListView {
id: lvBooks
x: 0
y: 0
width: scaleWidth(224)
height: scaleHeight(160)
anchors.bottom: parent.bottom
anchors.bottomMargin: scaleHeight(8)
anchors.right: parent.right
anchors.rightMargin: scaleWidth(8)
model: ListModel {
ListElement {
name: "Book1"
}
ListElement {
name: "Book2"
}
}
delegate: Item {
x: 5
width: scaleWidth(80)
height: scaleHeight(40)
Row {
Text {
text: name
font.bold: true
font.pointSize: scaleWidth(24)
anchors.verticalCenter: parent.verticalCenter
}
spacing: 0
}
}
}
}
}