ListView dynamic anchoring - qt

Let us suppose I have a card made using Rectangle and I want to show buttons on top of it when clicked. I'm calling showMenu() function to do that and for buttons I'm using an ListView with dynamic ListModel. The problem with such is that the button gets added bellow the Rectangle instead of the top of it. The anchor is not updating after appending an item to the model. Here is my code
Item {
width: 120
height: 120
Rectangle {
id: card
width: 50
height: 100
color: "pink"
anchors.bottom: parent.bottom
Item {
id: rec
width: 50
anchors.bottom: parent.top // This anchor is not updating after appending an item to the list.
ListModel {
id: menuListModel
}
Component {
id: delegate
Rectangle {
width: 120
height: 20
color: "blue"
Text {
anchors.fill: parent
anchors.centerIn: parent
text: commandText
}
}
}
ListView {
anchors.fill: parent
model:menuListModel
delegate: delegate
interactive: false
}
}
MouseArea {
anchors.fill: parent
onClicked: menuListModel.append({"commandText" : "Normal Summon"});
}
}
}

This is more or less a duplicate of this question. The Item needs a height. As mentioned in the answer to that question, you can add debug statements to the code when things like this happen. In this situation, you can also add a Rectangle as a child of the Item and make sure that it's visible:
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: "darkorange"
}
If it's not visible, you know that the problem lies with that (parent) item.

Related

Looking for alternate function to QML ListView's itemAtIndex due to version conflict

I'm using Qt 5.12, so I can't access ListView's itemAtIndex which was introduced in Qt 5.13.
I can't upgrade Qt due to my project/platform related restrictions. Is there a way to find the item at a given index for ListView with the Qt versions prior to 5.13?
Otherwise, is there a way to get mouse positions of an item based on index?
I'm having a listview with adjacent items having different width(alternate items have same width). I'm trying to access listview's item which is of less width compared to the adjacent item. The space between two items in the above picture is also an item which is marked as dummy. I'm able to get the index of each item (both actual & dummy), but the x position I get seems to be incorrect as the rectangle cursor is not getting placed in the intended item's position.
Please suggest alternatives that gives the similar functionality as itemAtIndex. Thanks.
In the following example, I declare a MouseArea in each delegate. So, once the mouse hovers over that delegate, we trigger MouseArea.onEntered and can know which item, because that delegate will have the corresponding index value:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
background: Rectangle { color: "#848895" }
ColumnLayout {
anchors.fill: parent
ListView {
id: listView
Layout.fillWidth: true
Layout.preferredHeight: 150
model: 20
orientation: ListView.Horizontal
delegate: MyDelegate { }
ScrollBar.horizontal: ScrollBar {
height: 20
policy: ScrollBar.AlwaysOn
}
highlight: Item {
z: 2
Rectangle {
width: 10
height: parent.height
color: "lightsteelblue"
border.color: "black"
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Frame {
anchors.centerIn: parent
background: Rectangle { }
Text {
text: qsTr("ListView.currentIndex = %1").arg(listView.currentIndex)
}
}
}
}
}
// MyDelegate.qml
import QtQuick
import QtQuick.Controls
Rectangle {
property ListView listView: ListView.view
width: 120
height: listView.height - 20
implicitWidth: width
implicitHeight: height
color: "transparent"
Rectangle {
border.color: "grey"
color: "white"
y: 20
height: parent.height - y * 2
width: parent.width
Text {
anchors.centerIn: parent
text: qsTr("Item %1").arg(modelData + 1)
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: listView.currentIndex = index
}
}
You can Try it Online!

How to drag an item outside a ListView in QML

I am developing a QML application which basically contains two ListView. I would like to copy a QML item from one ListView to another. I tried to handle this by setting Drag property in the delegate but the item cannot go outside the view when I drag the item, I think the Flickable container handles mouse events.
So, I want to try the following:
create a mousearea which overlaps the to ListView
create a new object by calling **createComponent() / createObject()**
reparent this object to the mousearea
handle mouse events in the mousearea till drop
This solution seems to me a little complicated, so do you have a better way to achieve this ?
This was a bad idea and too much complicated. I think I got a way to achieve this:
each delegate of the ListView has a hidden Item which can be dragged,
as my ListView are in a reusable component, I use a property to pass a higher item (a Rectangle here and NOT a **MouseArea**) which can be used as parent for dragged items,
the higher item contains the two ListView (and maybe more in the future),
when the drag begins, the item is set to visible and reparented using a **State**
So, I missed the point that set the parent should solve my problem.
Next code is just an idea, but the key is to have a MouseArea inside a delegate for the first ListView so the user can drag the items and drop them into a DropArea which belongs to the second ListView.
In this example, model is very simple, just a number. And when the item is dropped, it is removed from the first ListView:
listView.model.remove(listView.dragItemIndex)
Just remove that line of code to copy the item instead of removing.
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 600
height: 600
Rectangle {
id: root
width: 400
height: 400
ListView {
id: listView
width: parent.width / 2
height: parent.height
property int dragItemIndex: -1
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < 10; ++i) {
append({value: i});
}
}
}
delegate: Item {
id: delegateItem
width: listView.width
height: 50
Rectangle {
id: dragRect
width: listView.width
height: 50
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "salmon"
border.color: Qt.darker(color)
Text {
anchors.centerIn: parent
text: modelData
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: dragRect
drag.onActiveChanged: {
if (mouseArea.drag.active) {
listView.dragItemIndex = index;
}
dragRect.Drag.drop();
}
}
states: [
State {
when: dragRect.Drag.active
ParentChange {
target: dragRect
parent: root
}
AnchorChanges {
target: dragRect
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
}
}
]
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: dragRect.width / 2
Drag.hotSpot.y: dragRect.height / 2
}
}
}
ListView {
id: listView2
width: parent.width / 2
height: parent.height
anchors.right: parent.right
property int dragItemIndex: -1
DropArea {
id: dropArea
anchors.fill: parent
onDropped: {
listView2.model.append(listView.model.get(listView.dragItemIndex))
listView.model.remove(listView.dragItemIndex)
listView.dragItemIndex = -1;
}
}
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < 1; ++i) {
append({value: i});
}
}
}
delegate: Item {
id: delegateItem2
width: listView2.width
height: 50
Rectangle {
id: dragRect2
width: listView2.width
height: 50
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "salmon"
border.color: Qt.darker(color)
Text {
anchors.centerIn: parent
text: modelData
}
}
}
}
}
}

Why does anchors.fill does not work in a QML ListView's delegate's subviews, and what is a nice solution?

I encountered this:
ListView {
id: listView
model: ["Lorem","Ipsum"]
delegate: Item {
height: 20
Text {
z: 2
text: modelData
anchors.fill: parent
}
Rectangle {
z: 1
color: "red"
// this does not work:
anchors.fill: parent
// this works, but I have mixed feelings about it:
// height: 20; width: listView.width
}
}
}
So, apparently, anchors do not work in a delegate's subitem (in this case, Rectangle is not displayed at all). I would like to understand the mechanism behind this. Also, I'd like to ask what is the preferred way to deal with this situation?
Thank You!
Item has an implicitWidth and implicitHeight of zero, so making your Rectangle and Text fill it will result in them having no size as well.
There are two things wrong with your code:
The ListView has no width or height specified.
Your delegate has no width specified.
Here's one way of doing it correctly:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
width: 300
height: 300
visible: true
ListView {
id: listView
anchors.fill: parent
model: ["Lorem","Ipsum"]
delegate: Item {
width: listView.width
height: textItem.implicitHeight
Text {
id: textItem
z: 2
text: modelData
width: parent.width
}
Rectangle {
z: 1
color: "red"
anchors.fill: parent
}
}
}
}
The documentation of ListView has more information.

MouseArea event is propagated to its parent sibling. Why?

Here's a code snippet with current form of the code
Rectangle
{
id: menu
GridLayout
{
id: layout
columns: 4
rows: 3
Repeater
{
model: ListModel {}
ToolButton {}
}
Rectangle
{
x: -3
y: -33
width: menu.width - 2
height: menu.height + 33
border.color: "red"
border.width: 3
color: "blue"
MouseArea
{
x: mapToItem(menu, -5, -35).x
y: mapToItem(menu, -5, -35).y
width: menu.width
height: menu.height + 35
hoverEnabled: true
preventStealing: true
onEntered:console.log("onEntered")
onExited:console.log("onExited menu mous area")
}
}
}
}
The MouseArea hover event is propagated down to the ToolButtons in the layout. I don't get why. Hence, the onEntered and onExited events do not work as expected, because onExited happen inside the MouseArea when the ToolButtons are 'hovered' and tooltips are shown. In the end I need the MouseArea to be a bit wider and longer than its parent Rectangle so that once onExited is emitted the menu gets invisible. After the test with Rectangle is successfull it will make sense to make C++ type Polygon.
In your example, onExited must emits when entering ToolButton. According to MouseArea.exited():
Rectangle {
width: 400; height: 400
MouseArea {
id: mouseArea1
anchors.fill: parent
hoverEnabled: true
}
MouseArea {
id: mouseArea2
width: 100; height: 100
anchors.centerIn: parent
hoverEnabled: true
}
}
Moving the mouse into mouseArea2 from mouseArea1 will cause mouseArea1 to emit the exited signal.
If you do not want the exited signal to be emitted,
If instead you give the two MouseAreas a parent-child relationship, moving the mouse into mouseArea2 from mouseArea1 will not cause mouseArea1 to emit exited. Instead, they will both be considered to be simultaneously hovered.
That is, place ToolButton (and all related components) within the MouseArea. For example,
Rectangle {
id: menu
Rectangle {
//some properties
MouseArea {
hoverEnabled: true
//some properties
onEntered:console.log("onEntered")
onExited:console.log("onExited menu mous area")
GridLayout {
id: layout
Repeater {
ToolButton {}
}
}
}
}
}

How do I put an image on items of ListView and change it dynamically?

I'm trying to make a color picker in QML. The color details are stored in a ListModel and I've tried to create a horizontal row of colors to choose from.
Something like this:
The problem is, I'm unable to add a button image over the selected cell. I want to highlight the cell the user has selected by placing an image at the center of that cell.
The following is the code snippet I've written
ListView{
id: list
height: 30
width: 500
anchors.bottom: main.bottom
anchors.horizontalCenter: main.horizontalCenter
anchors.bottomMargin: 40
orientation: ListView.Horizontal
model: myModel
delegate:
Item {
id: cell
property alias cellColor: rectangle.color
signal clicked(color cellColor)
width: 500/myModel.count; height: 25
Rectangle {
id: rectangle
color: clr
anchors.fill: parent
}
onClicked:{
dynamicRect.color=cellColor
list.currentIndex=index
console.log(list.currentIndex)
}
MouseArea {
anchors.fill: parent
z: 1
onClicked: {
cell.clicked(cell.cellColor)
}
}
}
highlight:
Image {
id: button
height: 20
width: 20
source: "button.png"
anchors.horizontalCenter: list.currentItem.horizontalCenter
}
}
Any help will be highly appreciated.
Thanks!

Resources