QML - RowLayout - not even spacing - qt

WHAT I'M TRYING TO DO:
I have a RowLayer where I've added 3 custom buttons but for some reason, I can not obtain an even spacing between them, and I'm not sure if I'm doing something wrong on the RowLayer side or the implementation of a custom button is wrong from some whatever reason.
MY LAYOUT
import QtQuick 2.0
import QtQuick.Layouts 1.14
Item {
width: 600
height: 200
RowLayout {
anchors.fill: parent
spacing: 50
CustomButton{
id: returnToPlaylistID
Layout.preferredWidth: width
iconSource: "Images/IMG.png"
textSource: "Return back"
iconHeight: parent.width/20
iconWidth: parent.width/20
padding: 0
}
CustomButton{
id: playButton
iconSource: "Images/IMG.png"
textSource: ""
padding: 0
Layout.preferredWidth: width
iconHeight: parent.width/20
iconWidth: parent.width/20
}
CustomButton{
id: stopButton
iconSource: "Images/IMG.png"
textSource: ""
padding: 0
Layout.preferredWidth: width
iconHeight: parent.width/20
iconWidth: parent.width/20
}
}
}
MY CUSTOM BUTTON
import QtQuick 2.4
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
Item{
id: customButtonID
property var isPressed: false
property var iconSource: ""
property var textSource: ""
property var radiusValue: 20
property var borderColor: "aqua"
property var borderWidth: 5
property var backgroundColor: "#ffffff"
property var textColor: "#141414"
property var spacing: row.width/10 * 1.2
property var fontSize: 20
property var fontBold: true
property var padding: 15
property var iconWidth: 0
property var iconHeight: 0
signal clicked
property var _heigh: 0
width: row.width
height: textID.height
scale: 0.8
Rectangle{
id: rectangle
color: backgroundColor
radius: radiusValue
anchors.fill: parent
anchors.margins: padding * -1
border.color: borderColor
border.width: customButtonID.isPressed ? borderWidth : 0
Row{
id: row
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: customButtonID.spacing
Image{
id: iconID
source: iconSource
fillMode: Image.PreserveAspectFit
width: iconWidth
height: iconHeight
}
Text {
id: textID
fontSizeMode: Text.Fit
font.pixelSize: fontSize
font.bold: fontBold
text: "<font color='"+textColor+"'>" + textSource + "</font>"
}
}
}
MouseArea{
anchors.margins: padding * -1
anchors.fill: parent
onPressed: isPressed = true
onReleased: isPressed = false
onClicked: customButtonID.clicked()
}
}

It is about your textSource. Spacing is even, it starts when your layout item ends. You need to check your Text {} width to fix

You can't set width property to row, it grows dynamically based on the items you add to it, but define the height. Opposite is true for column.
Second thing is you can't horizontally center a row.
With the following modifications even spacing should works. Please check.
Note: if you want to center the elements then try with the item that holds the buttons.
CustomButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
Rectangle{
id: rectangle
property var isPressed: false
property var iconSource: ""
property var textSource: ""
property var radiusValue: 20
property var borderColor: "aqua"
property var borderWidth: 5
property var backgroundColor: "#ffffff"
property var textColor: "#141414"
property var spacing: row.width/10 * 1.2
property var fontSize: 20
property var fontBold: true
property var padding: 15
property var iconWidth: 0
property var iconHeight: 0
signal clicked
property var _heigh: 0
// width: row.width
height: textID.height
scale: 0.8
color: backgroundColor
radius: radiusValue
border.color: borderColor
border.width: rectangle.isPressed ? borderWidth : 0
Row{
id: row
// you can't horizonally center a row
// anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: rectangle.spacing
height: 50
Image{
id: iconID
source: iconSource
fillMode: Image.PreserveAspectFit
width: iconWidth
height: iconHeight
}
Text {
id: textID
fontSizeMode: Text.Fit
font.pixelSize: fontSize
font.bold: fontBold
text: "<font color='"+textColor+"'>" + textSource + "</font>"
}
}
MouseArea{
anchors.margins: padding * -1
anchors.fill: parent
onPressed: isPressed = true
onReleased: isPressed = false
onClicked: rectangle.clicked()
}
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item {
width: 600
height: 200
RowLayout {
anchors.fill: parent
spacing: 50
// lets have some left and right margins
anchors.leftMargin: 20
anchors.rightMargin: 20
CustomButton{
id: returnToPlaylistID
Layout.preferredWidth: width
iconSource: "Images/IMG.png"
textSource: "Return back"
iconHeight: parent.width/20
iconWidth: parent.width/20
padding: 0
}
CustomButton{
id: playButton
iconSource: "Images/IMG.png"
textSource: ""
padding: 0
Layout.preferredWidth: width
iconHeight: parent.width/20
iconWidth: parent.width/20
}
CustomButton{
id: stopButton
iconSource: "Images/IMG.png"
textSource: ""
padding: 0
Layout.preferredWidth: width
iconHeight: parent.width/20
iconWidth: parent.width/20
}
}
}
}

What's happening is the RowLayout is set to fill the parent. It then has three children to layout. However, they do not have implicit widths. Therefore it will stretch one of them to force them all to fit.
If you want even spacing between them, you actually need to add the equivalent of spacers between them:
Item {
Layout.fillWidth: true
}
You'll probably want to set spacing: 0 on the RowLayout if you use that technique.
And you will likely want to set an implicit width on the CustomButton to ensure they don't grow:
implicitWidth: row.width

Related

QT QML how to create a reusable button component

I have some simulated buttons in on rectangle. These buttons I need at different places in the app. Is it possible to make a kind of component out of it?
This is how it is currently
RowLayout {
anchors.fill: parent
Rectangle {
id: button1
height: _buttonsHeight * 0.6
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
anchors.centerIn: parent
source: "image://iconProvider/icons/128/button1.png"
sourceSize.height: parent.height * 0.8
}
MouseArea {
anchors.fill: parent
onPressed: btnHome.border.width = 2
onReleased: btnHome.border.width = 0
onClicked: userInputDevice.buttonClicked("button1")
}
}
Rectangle {
id: button2
height: _buttonsHeight * 0.6
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
anchors.centerIn: parent
source: "image://iconProvider/icons/128/button2.png"
sourceSize.height: parent.height * 0.8
}
MouseArea {
anchors.fill: parent
onPressed: btnHome.border.width = 2
onReleased: btnHome.border.width = 0
onClicked: userInputDevice.buttonClicked("button2")
}
}
}
Something like this would be the goal
RowLayout {
anchors.fill: parent
Button {id = "button1", height = _buttonsHeight * 0.6, icon = "button1.png", command = "button1", parent = this}
Button {id = "button2", height = _buttonsHeight * 0.6, icon = "button2.png", command = "button2", parent = this}
}
Is something like this feasible ?
Thanks and best regards
Arne
You should read the documentation about QML defining types.
Don't use the assignment operator, but the colon to create bindings. No need to set any parent.
For the nested Image.source property you should create an alias property in the root of your component like so property alias imageSource: <imageID>.source to be able to set it from the outside.
import QtQuick
import QtQuick.Layouts
Window {
id: root
width: 320
height: 240
visible: true
property int btnHeight: 80
component CustomButton : Rectangle {
property alias icon: image.source
signal pressed
signal released
signal clicked
id: buttonRoot
height: 20
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
id: image
anchors.centerIn: parent
source: "https://upload.wikimedia.org/wikipedia/commons/7/72/Variable_Resistor.svg"
fillMode: Image.PreserveAspectFit
width: buttonRoot.height / Math.sqrt(2)
height: image.width
}
MouseArea {
anchors.fill: parent
onPressed: buttonRoot.pressed()
onReleased: buttonRoot.released()
onClicked: buttonRoot.clicked()
}
}
RowLayout {
anchors.centerIn: parent
spacing: 20
CustomButton {
id: button1
height: root.btnHeight
onClicked: console.log("Button 1 clicked")
}
CustomButton {
id: button2
height: root.btnHeight
icon: "https://upload.wikimedia.org/wikipedia/commons/f/fd/Crystal_Clear_app_download_manager.svg"
onClicked: console.log("Button 2 clicked")
}
}
}
You can create a new qml file let's name it MyButton.qml
MyButton is a custom button with icon and text you can change it to fit your needs.
MyButton.qml:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt.labs.platform 1.0
import QtLocation 5.12
import QtQuick.Controls.Material 2.15
MouseArea {
id: control
width: control.w
height: control.h
property string icon_btn
property bool icon_visible: true
property string text
property int border: 3
property string borderColor: "white"
property string colorr: "#222222"
property int w: 50
property int h: 50
property bool text_visible: true
property bool clicked: false
property int radius: 12
property int contentLeftMargin: 0
property string buttonColor: enabled ? control.colorr : "grey"
cursorShape: Qt.PointingHandCursor
Rectangle {
border.color: control.borderColor
border.width: control.border
radius: control.radius
anchors.fill: parent
color: control.buttonColor
RowLayout{
anchors.fill: parent
Item { Layout.fillWidth: true }
Image{
id: img
visible: control.icon_visible
source: control.icon_btn
Layout.preferredWidth: 35
Layout.preferredHeight: 35
//Layout.leftMargin: control.contentLeftMargin
}
Text{
id: txt
text: control.text
font.pointSize: 17
font.bold: true
color: "white"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: control.text_visible ? x + width + 15 < control.width : 0
//Layout.leftMargin: control.contentLeftMargin
}
Item { Layout.fillWidth: true }
}
}
Layout.bottomMargin: 10
}
Then use it in your main qml file.
For example:
Window {
MyButton{
id: btn_refresh
icon_btn: "qrc:/Icons/outline_refresh_white_48pt_3x.png"
onClicked: {
//do something on click
}
}
}

How to customize Popup closing behavior?

I created a control, and when I click it, the popup will be displayed on the left and right sides. Like:
There may be many other buttons on the main interface. I hope that when I click the button, the popup will not close, but when I click other external areas, the popup will close.
I thought of using closePolicy, but when I set Popup.NoAutoClose, no matter I click the outer area or the button control, the popup will not be closed. When I set Popup.CloseOnPressOutside, the popup will be closed.
So how to customize the closing behavior of the popup? Or is there any other custom control way to achieve such a requirement(may not be popup)?
Edit
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
MenuSpinner{
id: menuId
x: 100
y: 50
}
Column{
x: 500
spacing: 10
Repeater{
model: 3
Button{
width: 100
height: 50
text: index
onPressed: {
console.log("pressed" + index)
}
}
}
}
}
MenuSpinner.qml
import QtQuick 2.0
import QtQuick.Controls 2.5
Rectangle{
width: 300
height: 50
property bool bTextClicked: false
onBTextClickedChanged: {
if(bTextClicked) popup.open()
else popup.close()
}
Rectangle{
width: 100
height: parent.height
x: rect1.width
border.color: "blue"
Text {
id: text
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: qsTr("value")
font.pixelSize: 16
}
MouseArea{
anchors.fill: parent
onClicked: {
bTextClicked = !bTextClicked
}
}
}
Popup {
id: popup
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
width: parent.width
height: parent.height
background: Rectangle {
anchors.fill: parent
color: "transparent"
//border.color: "black"
}
Rectangle{
id: rect1
width: 100
height: 50
Text {
text: qsTr("pop1")
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 16
}
color: "transparent"
border.color: "blue"
}
Rectangle{
id: rect2
x: parent.width - rect1.width
width: 100
height: 50
Text {
text: qsTr("pop2")
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 16
}
color: "transparent"
border.color: "blue"
}
onClosed: {
bTextClicked = false
console.log("close")
}
}
}
One way to do it is to catch mouse clicks on the window itself. Add a function or signal to MenuSpinner that can be called to close the popup. Any clicks to the other buttons should still work.
main.qml
Window {
MouseArea {
anchors.fill: parent
onClicked: {
menuId.closePopup();
}
}
MenuSpinner{
id: menuId
}
}
MenuSpinner.qml
Rectangle{
function closePopup() {
popup.close();
}
Popup {
id: popup
closePolicy: Popup.NoAutoClose
}
}

Can QML StackLayout detect drag from a component outside itself

I have a StackLayout with 3 stack items (2 static and one dynamically generated) which switches on button click. I want to make the second and third stack items a Droparea (third one is dynamically created) where I want to drag items from a Listview which is outside the StackLayout. Is it possible or I am doing something wrong?
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page{
id: pageid
width: parent.width
height: parent.height
Row{
id: row1
Button{
text: "0"
onClicked: layout.currentIndex = 0
}
Button{
text: "1"
onClicked: layout.currentIndex = 1
}
Button{
text: "2"
onClicked:{
var str = 'import QtQuick 2.15; Rectangle {id: rect2; color: "red"; DropArea{anchors.fill: parent; onEntered: {rect2.color = "silver"}}}'
var comp = Qt.createQmlObject(str,layout,"dynamicSnippet1")
onClicked: layout.currentIndex = 2
}
}
}
// Stacklayout block
StackLayout {
id: layout
anchors.top: row1.bottom
height: parent.height - row1.height - dragger.height
width: parent.width
currentIndex: 0
// Component 0
Rectangle {
id: rect0
color: 'teal'
}
// Component 1
Rectangle {
id:rect1
color: 'plum'
DropArea{
anchors.fill: parent
onEntered: {rect1.color = "gold"}
}
}
}
// Drag rectangles
ListView{
id: dragger
anchors.top: layout.bottom
height: 30
width: parent.width
orientation: Qt.Horizontal
model: 3
delegate: Rectangle{
id: xrect
height: 30
width: 60
color:"grey"
border.width: 1
border.color: "orange"
MouseArea{
id: ma
anchors.fill: parent
onReleased: parent.Drag.drop()
drag.target: parent
}
}
}
}
You forgot to set property Drag.active for your draggable target, e.g. put statement
Drag.active: ma.drag.active
into your xrect to make it work.
In addition you could check more dragging signals in your DropArea:
onDropped: console.error("# dropped")
onContainsDragChanged: console.error("# containsDrag", containsDrag)

QML pieMenu on draggable object

I've got a draggable object that is created by a Javascript, which is working fine. But when I create a PieMenu inside it, the object isn't created/visible in the Javascript context:
import QtQuick 2.8
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4
import QtQml.Models 2.2
Rectangle {
id: rev
width: 100
height: 80
color: "transparent"
antialiasing: false
Drag.active: dragArea.drag.active
MouseArea {
id: dragArea
width: parent.width
height: parent.height + 10 // easier to get
anchors.centerIn: parent
drag.target: parent
drag.axis: Drag.XAndYAxis
onClicked: pieMenu.popup(mouseX, mouseY), console.log("clicked")
}
PieMenu {
id: pieMenu
MenuItem {
text: "Add vertical bar"
onTriggered: print("Action 2")
}
}
Gauge {
id: revgauge
anchors.fill: parent
anchors.margins: 10
orientation : Qt.Horizontal
minorTickmarkCount: 4
tickmarkStepSize : 5000
minimumValue: 0
maximumValue: 10000
Behavior on value {
NumberAnimation {
duration: 5
}
}
Text {
font.pixelSize: (parent.height / 3)
anchors.top : parent.top
font.bold: true
font.family: "Eurostile"
color: "white"
anchors.horizontalCenter: parent.horizontalCenter
}
style: GaugeStyle {
valueBar: Rectangle {
implicitWidth: rev.height /3
color: Qt.rgba(revgauge.value / revgauge.maximumValue, 0, 1 - revgauge.value / revgauge.maximumValue, 1)
}
}
}
}
Can Mousearea handle dragging and a PieMenu at once? If not how can it be solved?
Consider QML PieMenu boundingItem. It addresses an exact issue with MouseArea you presented.

qt qml how can I create a list view feed item containing an icon and description?

I created an example. I do not know why I get a crash when I try to resize the window horizontally. Crash happens only when iconVisible is true
here is the qml code from the main, paste this in a hello world qt quick controls 2 project to test:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import QtQuick.XmlListModel 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Page {
anchors.fill: parent
XmlListModel{
id : modelId
namespaceDeclarations: "declare namespace media = 'http://search.yahoo.com/mrss/';"
source: "http://api.flickr.com/services/feeds/photos_public.gne?format=rss2&tags="
query: "//item[title and media:thumbnail and media:content]"
}
ListView {
anchors.fill: parent
model: modelId
Layout.fillWidth: true
delegate: itemDelegateId
ScrollBar.vertical: ScrollBar {}
}
Component {
id: itemDelegateId
Rectangle {
property bool iconVisible: true
property string contentText: "contentaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
property double itemHeight: 100
property int titleTextSize: 32
property int columnLayoutLeftMargin: (iconVisible)? 20 : 0
property string borderColor: "black"
property int iconSize: (iconVisible)? 40 : 0
height : itemHeight
anchors {
left: parent.left
right: parent.right
}
border.color: borderColor
border.width: 1
RowLayout {
anchors {
fill: parent
}
Rectangle {
id: notificationIconId
visible: iconVisible
anchors {
top: parent.top
}
height: iconSize
width: iconSize
Image {
anchors.fill: parent
fillMode: Image.PreserveAspectFit
}
}
ColumnLayout {
id: columnLayoutId
anchors {
top: parent.top
bottom: parent.bottom
left: notificationIconId.right
right: parent.right
}
Text {
id: contentId
anchors {
top: parent.top
bottom: parent.bottom
}
Layout.fillWidth: true
font.pixelSize: 20
elide: Text.ElideRight
maximumLineCount: 3
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: "black"
text: contentText
}
}
}
}
}
}
}
There are a few issues here. The first is that Layout.fillWidth: true has no effect, as Page is not a layout:
ListView {
anchors.fill: parent
model: modelId
Layout.fillWidth: true // remove
delegate: itemDelegateId
ScrollBar.vertical: ScrollBar {}
}
The second is that you shouldn't specify width and height for items in a layout:
Rectangle {
id: notificationIconId
visible: iconVisible
anchors {
top: parent.top
}
height: iconSize // Should be Layout.preferredHeight: iconSize
width: iconSize // Should be Layout.preferredWidth: iconSize
Image {
anchors.fill: parent
fillMode: Image.PreserveAspectFit
}
}
You can't use horizontal anchors on an item managed by a horizontal layout:
ColumnLayout {
id: columnLayoutId
anchors {
top: parent.top
bottom: parent.bottom
// This ColumnLayout is managed by a RowLayout, so these two lines have to go
left: notificationIconId.right
right: parent.right
}
}
Same thing for the Text item:
Text {
id: contentId
// Can't use vertical anchors on an item that is managed by a ColumnLayout
anchors {
top: parent.top
bottom: parent.bottom
}
Layout.fillWidth: true
font.pixelSize: 20
elide: Text.ElideRight
maximumLineCount: 3
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: "black"
text: contentText
}
With all of those issues fixed, it seems to work as expected:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import QtQuick.XmlListModel 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Page {
anchors.fill: parent
XmlListModel{
id : modelId
namespaceDeclarations: "declare namespace media = 'http://search.yahoo.com/mrss/';"
source: "http://api.flickr.com/services/feeds/photos_public.gne?format=rss2&tags="
query: "//item[title and media:thumbnail and media:content]"
}
ListView {
anchors.fill: parent
model: modelId
delegate: itemDelegateId
ScrollBar.vertical: ScrollBar {}
}
Component {
id: itemDelegateId
Rectangle {
property bool iconVisible: true
property string contentText: "contentaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
property double itemHeight: 100
property int titleTextSize: 32
property int columnLayoutLeftMargin: (iconVisible)? 20 : 0
property string borderColor: "black"
property int iconSize: (iconVisible)? 40 : 0
height : itemHeight
anchors {
left: parent.left
right: parent.right
}
border.color: borderColor
border.width: 1
RowLayout {
anchors {
fill: parent
}
Rectangle {
id: notificationIconId
visible: iconVisible
anchors {
top: parent.top
}
Layout.preferredHeight: iconSize
Layout.preferredWidth: iconSize
Image {
anchors.fill: parent
fillMode: Image.PreserveAspectFit
}
}
ColumnLayout {
id: columnLayoutId
anchors {
top: parent.top
bottom: parent.bottom
}
Text {
id: contentId
Layout.fillWidth: true
font.pixelSize: 20
elide: Text.ElideRight
maximumLineCount: 3
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
color: "black"
text: contentText
}
}
}
}
}
}
}

Resources