How to make GridView to display items with different cellWidth and cellHeight - qt

I have a model which contains counting number. I want to show that model data in given layout. Please find attached image.

You can use the GridLayout to span an item across multiple rows/columns
import QtQuick 2.0
import QtQuick.Layouts 1.3
GridLayout {
columnSpacing: 10
rowSpacing: 10
columns: 4
Repeater {
model: ListModel {
ListElement { colSpan: 2; rowSpan: 3 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 2; rowSpan: 3 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
ListElement { colSpan: 1; rowSpan: 1 }
}
Rectangle {
Layout.columnSpan: model.colSpan
Layout.rowSpan: model.rowSpan
Layout.fillHeight: true
Layout.fillWidth: true
color: "red"
Text {
anchors.fill: parent
text: index
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
}
}
}
}

Related

QML: how to scroll scrollview using the mouse wheel

I need to ensure that the ListView scrolls not only when hovering over it, but also in the area around it. As in the picture
I tried to do it like this, but scrolling with the mouse wheel still doesn't work
import QtQuick 2.0
import QtQuick.Controls 2.3
Rectangle {
height: 400
width: 300
color: "steelblue"
ScrollView {
anchors.fill: parent
wheelEnabled: true
ListView {
id: listView
anchors { fill: parent; margins: 40}
clip: true
model: appModel
delegate: Rectangle {
height: 50
width: listView.width
color: colorR
}
}
}
ListModel {
id: appModel
ListElement { colorR: "red"}
ListElement { colorR: "green"}
ListElement { colorR: "blue"}
ListElement { colorR: "cyan"}
ListElement { colorR: "yellow"}
ListElement { colorR: "blue"}
ListElement { colorR: "lightgray"}
ListElement { colorR: "red"}
ListElement { colorR: "green"}
ListElement { colorR: "blue"}
ListElement { colorR: "cyan"}
ListElement { colorR: "yellow"; }
ListElement { colorR: "lightgray"}
}
}
You can use WheelHandler outside the ListView
import QtQuick
import QtQuick.Controls
Rectangle {
height: 400
width: 300
color: "green"
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
WheelHandler {
onWheel: (event)=>{listView.flick(0, event.angleDelta.y*event.y)}
}
ListView {
id: listView
anchors { fill: parent; margins: 40}
model: 50
spacing: 10
delegate: Rectangle {
height: 50
width: listView.width
color: getRandomColor()
}
}
}
You can try this online.

QML Repeater and QML Grid Layout with variable number of columns

I have an model that consist of objects with some properties, e.g.:
ListModel {
id: fruitModel
ListElement {
name: "Apple"
color: "green"
cost: 2.45
}
ListElement {
name: "Orange"
color: "orange"
cost: 3.25
}
ListElement {
name: "Banana"
color: "yellow"
cost: 1.95
}
}
And now I want to display this model using GridLayout. For each property I want to use one element inside GridLayout, e.g:
GridLayout {
columns: 3
Text { text: "Apple" }
Rectangle { color: "green" }
SpinBox { value: 2.45 }
Text { text: "Orange" }
Rectangle { color: "orange" }
SpinBox { value: 3.25 }
Text { text: "Banana" }
Rectangle { color: "yellow" }
SpinBox { value: 1.95 }
}
The point is that I can easily change the columns property of GridLayout and make my layout narrower (e.g. to fit small screens). I can use Repeater to fill GridLayout. However such approach will fill my GridLayout in wrong order:
GridLayout {
columns: 3
Repeater {
model: fruitModel
Text { text: name }
}
Repeater {
model: fruitModel
Rectangle { color: color }
}
Repeater {
model: fruitModel
SpinBox { value: value }
}
}
And it is waste to use Layout.column and Layout.row attached properties since I want to easily change number of columns in GridLayout.
Is there any way to fill GridLayout with data from model row by row?
UPD1:
Behaviour that I want to get:
GridLayout {
columns: parent.width > 235 ? 3 : 1
Text { text: "Apple" }
Rectangle { color: "green"; width: 40; height: 40 }
SpinBox { value: 2 }
Text { text: "Orange" }
Rectangle { color: "orange"; width: 40; height: 40 }
SpinBox { value: 3 }
Text { text: "Banana" }
Rectangle { color: "yellow"; width: 40; height: 40 }
SpinBox { value: 1 }
}
UPD2:
Modified variant from #m7913d:
GridLayout {
id: layout
property int maxColumns: 3
columns: parent.width > 235 ? maxColumns : 1
Repeater {
model: fruitModel
Text {
text: name
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index)
Layout.column: 0
}
}
Repeater {
model: fruitModel
Rectangle {
Layout.preferredWidth: 30
Layout.preferredHeight: 30
color: col
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index + 1)
Layout.column: layout.columns == maxColumns ? 1 : 0
}
}
Repeater {
model: fruitModel
SpinBox {
value: val
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index + 2)
Layout.column: layout.columns == maxColumns ? 2 : 0
}
}
}
It's working but not easy-to-modify solution plus sometimes during layout resize there is messages QGridLayoutEngine::addItem: Cell (1, 0) already taken.
I would use a Column filled by either Rows or Columns (or any other arrangement) as delegates.
Column {
id: rootCol
anchors.fill: parent
Repeater {
model: fruitModel
delegate: rootCol.width > 300 ? rowDel : colDel
}
Component {
id: rowDel
Row {
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.cost }
}
}
Component {
id: colDel
Column {
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.cost}
}
}
}
Or maybe:
Column {
id: rootCol
anchors.fill: parent
Repeater {
model: fruitModel
delegate: Flow {
anchors {
left: parent.left
right: parent.right
}
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.value }
}
}
}
Here is yet another solution:
The Idea: add the children in the right order
For that we will need to make sure that all Items for a model entry.
GridLayout {
id: gl
columns: parent.width > 235 ? 3 : 1
}
Instantiator {
model: fruitModel
delegate: QtObject {
property Item text: Text { parent: gl; text: model.name }
property Item rect: Rectangle { parent: gl; color: model.color; width: 50; height: 50; }
property Item spin: SpinBox { parent: gl; value: model.cost; }
}
}
Note: This will fail when entries in the model will be inserted or reordered, as in this solution the entries are always appended.
Other than that, it will automatically support column changes
A possible approach is to extend this method with condition column/row indexes:
GridLayout {
id:gridLayout
columns: parent.width > 235 ? 3 : 1
Repeater {
model: fruitModel
implicitHeight: 50
Text {
text: name
Layout.row: gridLayout.columns == 3 ? index : 3 * index
Layout.column: 0
}
}
Repeater {
model: fruitModel
Rectangle {
color: model.color
implicitWidth: 50
implicitHeight: 50
Layout.row: gridLayout.columns == 3 ? index : 3 * index + 1
Layout.column: gridLayout.columns == 3 ? 1 : 0
}
}
Repeater {
model: fruitModel
SpinBox {
value: value
Layout.row: gridLayout.columns == 3 ? index : 3 * index + 2
Layout.column: gridLayout.columns == 3 ? 2 : 0
}
}
}

Animate `positionViewAtIndex`

I have ListViewshowing a Model, in which, at random positions I might add a entry.
Now I want to see my new entry, and try to move the ListView to it, with the
positionViewAtIndex(newIndex, ListView.Visible)
But this change somtimes feeles very hard. Is there a posibility to smooth it with som animation?
A somewhat lengthy example to play with:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow
{
width: 1024
height: 800
visible: true
property int lastAddedIndex: -1
onLastAddedIndexChanged: lv.positionViewAtIndex(lastAddedIndex, ListView.Contain)
Button {
property int num: 10
text: 'Add element: ' + num
onClicked: {
lastAddedIndex = Math.floor(Math.random(num) * lm.count)
lm.insert(lastAddedIndex, { num : this.num })
num++
}
}
ListModel {
id: lm
ListElement { num: 0 }
ListElement { num: 1 }
ListElement { num: 2 }
ListElement { num: 3 }
ListElement { num: 4 }
ListElement { num: 5 }
ListElement { num: 6 }
ListElement { num: 7 }
ListElement { num: 8 }
ListElement { num: 9 }
}
ListView {
id: lv
model: lm
y: 150
width: 800
height: 100
spacing: 2
clip: true
orientation: ListView.Horizontal
delegate: Rectangle {
width: 150
height: 100
border.color: 'black'
color: lastAddedIndex === index ? 'purple' : 'white'
Text {
anchors.centerIn: parent
text: index + ': ' + num
}
}
add: Transition { NumberAnimation { property: 'width'; from: 0; to: 150; duration: 600 } }
}
}
You can do it by adding addition animation element for contentX (as you have your element horizontally placed), and running it in order to animate changing view position at a given index, it will give you smooth animation:
source
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow
{
width: 1024
height: 800
visible: true
property int lastAddedIndex: -1
onLastAddedIndexChanged: gotoIndex(lastAddedIndex)
function gotoIndex(idx) {
anim.running = false;
var pos = lv.contentX;
var destPos;
lv.positionViewAtIndex(idx, ListView.Contain);
destPos = lv.contentX;
anim.from = pos;
anim.to = destPos;
anim.running = true;
}
NumberAnimation { id: anim; target: lv; property: "contentX"; duration: 500 }
Button {
property int num: 10
text: 'Add element: ' + num
onClicked: {
lastAddedIndex = Math.floor(Math.random(num) * lm.count)
lm.insert(lastAddedIndex, { num : this.num })
num++
}
}
ListModel {
id: lm
ListElement { num: 0 }
ListElement { num: 1 }
ListElement { num: 2 }
ListElement { num: 3 }
ListElement { num: 4 }
ListElement { num: 5 }
ListElement { num: 6 }
ListElement { num: 7 }
ListElement { num: 8 }
ListElement { num: 9 }
}
ListView {
id: lv
model: lm
y: 150
width: 800
height: 100
spacing: 2
clip: true
orientation: ListView.Horizontal
delegate: Rectangle {
width: 150
height: 100
border.color: 'black'
color: lastAddedIndex === index ? 'purple' : 'white'
Text {
anchors.centerIn: parent
text: index + ': ' + num
}
}
add: Transition { NumberAnimation { property: 'width'; from: 0; to: 150; duration: 600 } }
}
}

Horizontal listview navigation - visible item

I have more horizontal listview. For example let's take two of them. Items of each list are not the same length, when one list is scrolling - the other one does, too. I've solved that with contentX property. But, when I want to navigate between each other (when I press key "down" I need to force focus second listview). The problem is that focus is on horizontalna2.currentIndex that is remembered, and I want to go on first visible item in listview.
In android is that very simple, but to solve that here, huh..
Here is sketch of the code:
Rectangle {
width: 500
height: 200
ListModel {
id: model1
ListElement { itemwidth: 100 }
ListElement { itemwidth: 200 }
ListElement { itemwidth: 50 }
ListElement { itemwidth: 70 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
}
ListModel {
id: model2
ListElement { itemwidth: 300 }
ListElement { itemwidth: 50 }
ListElement { itemwidth: 70 }
ListElement { itemwidth: 100 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 30 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
}
ListView {
clip: true
id: horizontalna
boundsBehavior: Flickable.StopAtBounds
width: 500
height: 60;
focus: true
model: model1
orientation: ListView.Horizontal
KeyNavigation.down: horizontalna2
onContentXChanged: {
if (horizontalna.activeFocus === true)
{
horizontalna2.contentX = horizontalna.contentX
}
}
delegate: Item {
id: containerHorizontal
width: itemwidth; height: 60;
Rectangle {
id: contentHorizontal
anchors.centerIn: parent; width: containerHorizontal.width; height: containerHorizontal.height - 10
color: "transparent"
antialiasing: true
Rectangle { id: insideConHorizontal; anchors.fill: parent; anchors.margins: 3; color: "grey"; antialiasing: true; radius: 5
Text {
id: labelHorizontal
text: "name"
color: "white"
}
}
}
states: State {
name: "active"; when: containerHorizontal.activeFocus
PropertyChanges { target: contentHorizontal; color: "#FFFF00"; scale: 1}
PropertyChanges { target: insideConHorizontal; color: "#F98F06" }
PropertyChanges { target: labelHorizontal; color: "#0E2687"}
}
}
}
ListView {
id: horizontalna2
anchors.top: horizontalna.bottom
boundsBehavior: Flickable.StopAtBounds
width: 500
height: 60;
focus: true
model: model2
orientation: ListView.Horizontal
onContentXChanged: {
if (horizontalna2.activeFocus === true)
{
horizontalna.contentX = horizontalna2.contentX
}
}
delegate: Item {
id: containerHorizontal2
width: itemwidth; height: 60;
Rectangle {
id: contentHorizontal2
anchors.centerIn: parent; width: containerHorizontal2.width; height: containerHorizontal2.height - 10
color: "transparent"
antialiasing: true
Rectangle { id: insideConHorizontal2; anchors.fill: parent; anchors.margins: 3; color: "grey"; antialiasing: true; radius: 5
Text {
id:labelHorizontal2
color: "white"
text: "name"
}
}
}
states: State {
name: "active"; when: containerHorizontal2.activeFocus
PropertyChanges { target: contentHorizontal2; color: "#FFFF00"; scale: 1}
PropertyChanges { target: insideConHorizontal2; color: "#F98F06" }
PropertyChanges { target: labelHorizontal2; color: "#0E2687"}
}
}
}
}
EDIT[SOLVED] :
Get index of first visible item of second list depending on contentX --> function indexAt()
onCurrentIndexChanged: {
if (horizontalna.activeFocus === true)
{
horizontalna2.currentIndex = horizontalna2.indexAt((horizontalna2.contentX),0)
}
}
Get index of first visible item of second list depending on contentX --> function indexAt()
onCurrentIndexChanged: {
if (horizontalna.activeFocus === true)
{
horizontalna2.currentIndex = horizontalna2.indexAt((horizontalna2.contentX),0)
}
}

Is there easy way to duplicate a RowLayout in QtQuick?

In my ui.qml I have:
RowLayout {
id: rowLayout2
x: 0
y: 397
width: 640
height: 50
clip: false
Text {
id: text4
width: 105
height: 32
text: qsTr("房间类型")
font.pixelSize: 17
wrapMode: Text.WordWrap
}
ComboBox {
id: comboBox1
activeFocusOnPress: false
model: ListModel {
id: cbItems
ListElement { text: "标准间"; color: "Yellow" }
ListElement { text: "三人间"; color: "Green" }
ListElement { text: "大床房"; color: "Brown" }
ListElement { text: "豪华套房"; color: "Blue" }
}
}
}
And I want to make a button that when clicked on duplicates this RowLayout below the original one, how do I do that?
Put it inside a Repeater and increment the model count when the button is clicked.
Button {
onClicked: {
repeater.model += 1;
}
}
...
Column {
Repeater {
model: 1
// Your rowLayout2 code
}
}

Resources