Center QML ListView highlighted item - qt

In the picture Test, Test 1 and Test 2 are in the ListView. In this case Test element is highlighted. How can I modify view behavior to ensure that current (highlighted) item stays always in the middle of the list? What I want to achieve is represented in the following picture:

You just need highlightRangeMode with preferredHighlightBegin and preferredHighlightEnd. From the documentation:
These properties affect the position of the current item when the list is scrolled. For example, if the currently selected item should stay in the middle of the list when the view is scrolled, set the preferredHighlightBegin and preferredHighlightEnd values to the top and bottom coordinates of where the middle item would be. If the currentItem is changed programmatically, the list will automatically scroll so that the current item is in the middle of the view. Furthermore, the behavior of the current item index will occur whether or not a highlight exists.
Here is a full example of an horizontal ListView with the current item positioned at the center.
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
width: 300
height: 150
visible: true
ListView {
anchors.fill: parent
spacing: 5
model: 20
delegate:
Rectangle {
width: 30
color: ListView.view.currentIndex === index ? "red" : "steelblue"
height: ListView.view.height
Text {
anchors.centerIn: parent
text: index
font.pixelSize: 20
}
}
orientation: Qt.Horizontal
preferredHighlightBegin: 150 - 15
preferredHighlightEnd: 150 + 15
highlightRangeMode: ListView.StrictlyEnforceRange
}
}

You could have a look at ListView's positionViewAtIndex method and see if that helps.

Related

Setting header on ListView prevents scrolling when the delegate has MouseArea

I have a ListView and its delegate has a MouseArea. If the ListView doesn't have a header, everything works well. However, if I set a header, I can only click on the delegates but can't scroll.
What's is more interesting is that when I set the header's width to half the width of the window, I can scroll normally on the right side of the screen (where there's no header), but can't scroll on the left side under the header.
EDIT: Experimented with this a bit and found another thing. The delegate's height is 80 and if I set the header's height to, say, 30, then I can't swipe when the mouse lands on the top 30 pixels of a delegate as if the header is attached to each item in the list?
[edited code] This the full code that recreates the problem for me (can't scroll on the left side but can on the right). I'm using qt 2.15
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: root
width: 640
height: 600
visible: true
ListView {
id: list
anchors.fill: parent
model: 20
delegate: Rectangle {
signal clicked();
implicitHeight: 70
width: root.width
MouseArea {
anchors.fill: parent
z: 2
onClicked: console.log("clicked");
}
Text {
anchors.centerIn: parent
text: "text"
}
}
// Works fine when I comment this out
headerPositioning: ListView.OverlayHeader
header: Rectangle {
z: 2
implicitHeight: 100
implicitWidth: root.width / 2
color: "blue"
}
}
}
Example (the header is blue):
https://bugreports.qt.io/browse/QTBUG-101727 was written up about this; probably the same as QTBUG-89409. And we fixed it already in 5.15.7. It's also OK in Qt 6.
Note that if you press on the header itself and try to flick, you won't be able to, in any version; that's intentional: https://bugreports.qt.io/browse/QTBUG-74046
The problem turns out to be too specific and can't be recreated so I guess I should close the question.
(SO asks me to wait for 18 hours...)

QtQuick - what is meant by "parent item"?

The following QML code:
Window {
id: window
width: 450
height: 700
visible: true
StackView {
id: mainStack
property Item itemTest: Item {
id: itemTest
ColumnLayout {
id: mainLayout
width: mainStack.width
ScrollView {
id: scrollview
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ColumnLayout{
id: colLayout
anchors.fill: scrollview
}
}
}
}
initialItem: itemTest
anchors.fill: parent
}
}
outputs "QML ColumnLayout: Cannot anchor to an item that isn't a parent or sibling."
Replacing "anchors.fill: scrollview" by "anchors.fill: parent" in the above code makes this message disappear but then the ColumnLayout does not seem to fill the ScrollView.
Given this behaviour, I come to the conclusion that the ScrollView in this QML file isn't actually the parent of "colLayout", which comes against my first intuition about the way parenting works in QML.Can someone explain to me what is meant exactly by the keyword "parent" in QML ? Many thanks in advance.
The issue is that Controls use the concept of a contentItem. While the ScrollView itself is a Control which itself is in turn an Item, the children are parented to a different Item called contentItem.
More info here:
https://doc.qt.io/qt-5/qml-qtquick-controls2-control.html#contentItem-prop
See also the comment there:
Note: Most controls use the implicit size of the content item to calculate the implicit size of the control itself. If you replace the content item with a custom one, you should also consider providing a sensible implicit size for it (unless it is an item like Text which has its own implicit size).
You don't want to grow your ColumnLayout to match the contentItem, the contentItem will automatically resize to fit the implicit size of the ColumnLayout.
If the effect you are trying to get is to match the size of the ColumnLayout to that of the ScrollView, then use something like:
ColumnLayout{
id: colLayout
implicitWidth: scrollview.width
implicitHeight: scrollview.height
height: implicitHeight
width: implicitWidth
}
But in that case, why bother using a ScrollView? Normally you would allow the ColumnLayout to manage its implicit size normally based on it's children. When the contentItem ends up overflowing the ScrollView, then it starts to automatically scroll.

Qml Combobox not closing when delegate of the listview goes out of scope

I have made a delegate for listview that contains Combobox. If I open the Combobox and scroll the listview, the Combobox popup is moving with the delegate position, that's ok. But when the delegate goes out of the listview area(Ref the sample image attached), Combobox popup continues to moves even out of the listview area.
How to close the Combobox when the corresponding delegate goes out of the listview area.
Thanks in advance...
Code goes here
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Column {
spacing: 0
anchors.fill: parent
Item {
width: parent.width
height: parent.height * 0.4
Image {
anchors.fill: parent
anchors.margins: 10
source: "https://lh4.googleusercontent.com/proxy/cITVCAj9KJ5Hfwd5iuNDhzdB2pSrMQv2rzTl-vvg23Ifhe2qdCisZBG-MzV35y_r2zijC9X4QOpda9eHzr_hA"
}
}
ListView {
width: parent.width
height: parent.height * 0.7
model: 10
spacing: 5
clip: true
delegate: Rectangle {
width: parent.width
height: 50
color: index % 2 == 0 ? "lightsteelblue" : "steelblue"
Row {
spacing: 25
anchors.centerIn: parent
Label {
text: qsTr("%1").arg(index)
anchors.verticalCenter: parent.verticalCenter
}
ComboBox {
anchors.verticalCenter: parent.verticalCenter
model: ["a", "b", "c"]
}
}
}
}
}
}
If there is no particular goal to keep ComboBox popup opened when scrolling, then add the following property to your ListView:
highlightRangeMode: ListView.StrictlyEnforceRange
This will close ComboBox popup when ListView is being scrolled.
P.S.
Also, resolves your issue with the ComboBox list getting out of view area.
UPDATE on issue with header element hiding below other list items:
Accordingly to the description ListView.StrictlyEnforceRange - the highlight never moves outside of the range. The current item changes if a keyboard or mouse action would cause the highlight to move outside of the range. when an item goes out of range the list changes next item and that makes ComboBox closing its popup, but since header item is below the other ListView items itself (see this paragraph https://doc.qt.io/qt-5/qml-qtquick-listview.html#stacking-order-in-listview , delegate is always above a header) it makes impossible displaying default header here at the top of the other items. I'd suggest you implement your own header beyond the list. Sorry, I might not have known Qt so good to find the other solution.

How to align items in a ListView in QtQuick

I have the following horizontal list:
BulletsListDelegate.qml
import QtQuick 2.0
Rectangle {
width: 8
height: 8
color: "#808080"
radius: width * 0.5
}
main.qml
import QtQuick 2.0
Item {
width: 256
height: 256
ListModel {
id: bulletsListModel
ListElement {
a: 'example'
}
ListElement {
a: 'example'
}
...
}
ListView {
id: bulletsList
spacing: 8
orientation: ListView.Horizontal
delegate: BulletsListDelegate {}
model: bulletsListModel
anchors.bottom: parent.bottom
width: parent.width
}
}
And the elements are shown like this (they are the grey bullets)
I want them to show in the horizontal center of the black box above them (its parent).
Is there any form of centralize or justify the items of the list?
So, if I understand your question correctly, you want to have alignment for instances of your items in a ListView. Using a ListView, that is not so easy to achieve. If you have an uneven number of items, you can do it by using preferredHighlightBegin and preferredHighlightEnd to have a 1-item sized region in the center of your ListView, and then setting hightlightRangeMode to ListView.StrictlyEnforceRange. You can set the currentIndex to point to index so that the middle item will be considered current. That puts it within the range you defined, and thus in the center. That does not work when you have an even number of items though, so it's a hack with limited value.
My question is: do the items have to be positioned using a ListView? It looks like you don't actually need much of the functionality of the ListView at all? If you don't need the other features from ListView (like scrolling), you can just use a Repeater instead. That allows you to simply put the items in a Row positioner, which you can width of count*(delegateWidth+spacing)-spacing and a height equal to your delegate height. Then, you can use acnhors to position the Row centered to whatever you like.
André's answer suggested me to use Repeater and Rows instead of ListView, and it completely solved my problem. But... didn't find a way to align an actual ListView yet.
Row {
spacing: 8
Repeater {
id: bulletsRepeater
model: 5
BulletsDelegate { }
}
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}

How to scroll list view to the current item?

I have a list view. If i click on a cell, that specific cell need to move to the center with a small scrolling animation. With my code, the clicked cell will comes to the center without any animation.
Is it possible to add animation for that ?
Am putting my code below :
ListView {
id: source_list
width: 1080
height: 480
spacing: 50
model: mediaSongsModel
delegate: mediaSongsDelegate
focus: true
interactive: true
clip: true
highlightMoveDuration: 50
snapMode: ListView.SnapToItem
boundsBehavior:Flickable.StopAtBounds
preferredHighlightBegin: 260/scaleFactor
preferredHighlightEnd: 260/scaleFactor
highlightRangeMode: ListView.StrictlyEnforceRange
}
Component {
id: mediaSongsDelegate
Item {
id: wrapper
width: 1080
height: 200
MouseArea {
anchors.fill: parent
onClicked: {
source_list.currentIndex = index
source_list.positionViewAtIndex(index,ListView.Center)
}
}
}
}
If the current item should always be in the center or within a specific are of the view, then you can use the preferredHighlightBegin and preferredHightlightEnd properties to define that area.
The value of highlightRangeMode controls if and if yes, how strictly this applies. E.g. if the first item is the current item and the value is StriclyEnforeRange then the item will be within the specified area even if that means scrolling further up than a normal "scroll to top" would.
Something like that should work
ListView {
preferredHighlightBegin: height / 2 - 100 // 100 being item height / 2
preferredHighlightEnd: height / 2 + 100 // 100 being item height / 2
highlightRangeMode: ListView.StrictlyEnforceRange
}
I've solved my issue by removing the following line of code:
source_list.positionViewAtIndex(index,ListView.Center);
The animation correctly worked.

Resources