qml listview improve speed of changing items - qt

i have a listview, how to change a speed of changing items, tried highlightMoveSpeed(highlightMoveDuration), but that does not working
Is there any way to increase the spped
slider.qml
import QtQuick 1.0
Rectangle {
id: slider
anchors.fill: parent
Component {
id: pageDelegate
Rectangle {
id: page
height: parent.height
Component.onCompleted: page.width = slider.width
Rectangle {
anchors.fill: parent
// anchors.margins: 15
Image{
anchors.top: parent.top
anchors.fill: parent
source: modelData
}
}
}
}
ListView {
id: list_model
anchors.fill: parent
model: modelData
delegate: pageDelegate
orientation: ListView.Horizontal
snapMode: ListView.SnapToItem
spacing: 5
highlightMoveSpeed: 10000000
}
}

You can either use the default highlight and set its speed, e.g.
highlightMoveDuration : 200
highlightMoveVelocity : 1000
or, in case you use your custom highlight, let the highlight component handle the behaviour. E.g.
// Set the highlight delegate. Note we must also set highlightFollowsCurrentItem
// to false so the highlight delegate can control how the highlight is moved.
highlightFollowsCurrentItem: false
highlight: Rectangle {
y: myListView.currentItem.y;
Behavior on y {
SmoothedAnimation {
easing.type: Easing.Linear
duration:200;
maximumEasingTime:300
velocity : 1000
}
}
}
Check the qt highlight example

A note about the other highlight move property: if you want to use highlightMoveDuration instead of highlightMoveVelocity (highlightMoveSpeed in Qt 4), you need to set the latter to -1:
highlightMoveDuration: 1000
highlightMoveVelocity: -1

Related

Qml: ScrollView containing a ListView does not adjust ScrollBar handle

I am trying to add vertical scrollbar, as needed, to a ListView. I have been told that the best approach is to place the ListView inside a ScrollView, instead of inserting a scrollbar into the ListView (like in this question), because that would make it more efficient for the GPU.
I inserted it, as in the example below - but no matter what I tried, if the scroll bar shows, its handle always takes the entire height and of course doesn't move.
I hope you can take a look at my sample and give me a suggestion, why the scroll bar is not showing up properly.
There are comments inside the code explaining what I did and why.
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Item
{
readonly property int parentWidth: 280
readonly property int parentMaxHeight: 400
// Main reason for doing this - the items are custom objects and
// their width does not automatically adjust for having the scroll bar or not
// But also, to set scroll bars because Qt.ScrollBarAsNeeded makes them not show
property bool scrollBarVisible: myListView.contentHeight > myListView.height
width: parentWidth
height: parentMaxHeight
Rectangle
{
id: myMenuRect
anchors.rightMargin: 2
anchors.leftMargin: 2
anchors.bottomMargin: 4
anchors.topMargin: 4
width: parentWidth
height: myListView.height
radius: 10
z: 2
color: "red" // Adding this to show why the height of the rectangle must match the listview
}
ScrollView
{
id: myScrollView
parent: myMenuRect
anchors.fill: parent
anchors.topMargin: 5
anchors.bottomMargin: 5
anchors.rightMargin: 5
frameVisible: false
// I have tried to set implicitHeight in many different ways,
// no matter what I do the scroll bar handle occupies the enire bar and doesn't move
// The Qt.ScrollBarAsNeeded didn't work... so I did this
verticalScrollBarPolicy: scrollBarVisible ? Qt.ScrollBarAlwaysOn : Qt.ScrollBarAlwaysOff
// Adding colors on scrollbar to show which part is showing
style: ScrollViewStyle
{
handle: Rectangle
{
implicitWidth: 10
implicitHeight: 2
radius: 10
anchors.leftMargin: 1
anchors.left: parent.left
color: "yellow"
}
scrollBarBackground: Rectangle
{
implicitWidth: 12
anchors.right: parent.right
color: "green"
}
}
ListView
{
id: myListView
parent: myScrollView
model: wifiComboListModel
focus: true
clip: true
interactive: false
width: parent.width
// I am trying to tell my view to take the minimum space it needs that is below
// a certain height. Ignore the "myListView." prefixes here, I know they are not needed but
// make it easier to move this outside if needed
height: (myListView.contentHeight > 0 ?
(myListView.contentHeight < parentMaxHeight ?
myListView.contentHeight : parentMaxHeight) : 0)
// I made this as simple as possible, without affecting "quality"
delegate: Text
{
text: _comboBoxText
height: 70
width: parent.width - 20
}
}
ListModel
{
id: wifiComboListModel
}
// I want to populate my model from outside, not be static. Not sure if this affects the bars
function populateComboBoxListModel()
{
wifiComboListModel.clear();
for (var itemIndex = 0; itemIndex < listItems.length; itemIndex++)
{
wifiComboListModel.append
({
_id: itemIndex,
_comboBoxText: listItems[itemIndex]
});
}
}
Component.onCompleted:
{
populateComboBoxListModel();
}
property var listItems: [
"This",
"annoying",
"list",
"view",
"does",
"not behave the way",
"I expect.",
"I",
"tried many",
"things,",
"now I am",
"begging for your",
"help",
"."
]
}
you have a binding loop for height in myMenuRect. This occurs because myMenuRect depends on height of the list view and vice versa. After fixing it seems to be working:
import QtQuick 2.0
import QtQuick.Controls 1.4
ApplicationWindow
{
readonly property int parentWidth: 280
readonly property int parentMaxHeight: 400
visible: true
// Main reason for doing this - the items are custom objects and
// their width does not automatically adjust for having the scroll bar or not
// But also, to set scroll bars because Qt.ScrollBarAsNeeded makes them not show
property bool scrollBarVisible: myListView.contentHeight > myListView.height
width: parentWidth
height: parentMaxHeight
Rectangle
{
id: myMenuRect
anchors.rightMargin: 2
anchors.leftMargin: 2
anchors.bottomMargin: 4
anchors.topMargin: 4
width: parentWidth
height: parentMaxHeight
radius: 10
z: 2
}
ScrollView
{
id: myScrollView
parent: myMenuRect
anchors.fill: parent
anchors.topMargin: 5
anchors.bottomMargin: 5
anchors.rightMargin: 5
frameVisible: false
// I have tried to set implicitHeight in many different ways,
// no matter what I do the scroll bar handle occupies the enire bar and doesn't move
// The Qt.ScrollBarAsNeeded didn't work... so I did this
verticalScrollBarPolicy: scrollBarVisible ? Qt.ScrollBarAlwaysOn : Qt.ScrollBarAlwaysOff
ListView
{
id: myListView
model: wifiComboListModel
focus: true
clip: true
interactive: false
width: parent.width
// I am trying to tell my view to take the minimum space it needs that is below
// a certain height. Ignore the "myListView." prefixes here, I know they are not needed but
// make it easier to move this outside if needed
height: (myListView.contentHeight > 0 ?
(myListView.contentHeight < parentMaxHeight ?
myListView.contentHeight : parentMaxHeight) : 0)
// I made this as simple as possible, without affecting "quality"
delegate: Text
{
text: _comboBoxText
height: 70
width: parent.width - 20
}
}
}
ListModel
{
id: wifiComboListModel
}
// I want to populate my model from outside, not be static. Not sure if this affects the bars
function populateComboBoxListModel()
{
wifiComboListModel.clear();
for (var itemIndex = 0; itemIndex < listItems.length; itemIndex++)
{
wifiComboListModel.append
({
_id: itemIndex,
_comboBoxText: listItems[itemIndex]
});
}
}
Component.onCompleted:
{
populateComboBoxListModel();
}
property var listItems: [
"This",
"annoying",
"list",
"view",
"does",
"not behave the way",
"I expect.",
"I",
"tried many",
"things,",
"now I am",
"begging for your",
"help",
"."
]
}
The reason why my ScrollView did not behave was parenthood :)
The issue: even though I set the parent in the ListView, it seems it did not take:
ListView
{
parent: myScrollView
What I had to do to make it work was actually nest the ListView inside the ScrollView.
ScrollView
{
id: myScrollView
parent: myMenuRect
anchors.fill: parent
ListView
{
id: myListView
model: wifiComboListModel
I think the "parent" property may not work well for all controls, and will remember that in the future.

Qt qml SwipeView change animation/transition speed

I'm using Qt 5.10 and i'm using a SwipeView. I want to change the swipe animation speed, but after reading the docs i could not see how. Is there some workaround to do this?
the reason i was trying to do this was because, don't know why, the swipe transition animation was very slow (see below)
this is my code:
ColumnLayout{
anchors.fill: parent
Item{
id:modulecontainer
Layout.fillHeight: true
Layout.fillWidth: true
SwipeView{
id: moduleview
anchors.fill: parent
interactive: loggedUser.role==User.AdminRole
clip: true
orientation: Qt.Horizontal
Item {
id: firstPage
Loader {
anchors.fill: parent
id:moduleLoader
}
}
Item {
id: secondPage
Rectangle{
anchors.fill: parent
color: "red"
}
}
}
}
}
I solved this issue just taking the code of contentItem implementation from the source code of SwipeView:
....
SwipeView{
id: moduleview
....
contentItem: ListView {
model: moduleview.contentModel
interactive: moduleview.interactive
currentIndex: moduleview.currentIndex
spacing: moduleview.spacing
orientation: moduleview.orientation
snapMode: ListView.SnapOneItem
boundsBehavior: Flickable.StopAtBounds
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: 0
preferredHighlightEnd: 0
highlightMoveDuration: 250
// min:10
maximumFlickVelocity: 4 * (moduleview.orientation ===
Qt.Horizontal ? width : height)
}
}
....
the result:
don't know why this solves the problem, but i'm sharing just in case others face the same problem. If more animation speed is wanted just replace the maximumFlickVelocity factor from 4 to a bigger value

How to limit the size of drop-down of a ComboBox in QML

I am using a ComboBox in QML and when populated with a lot of data it exceeds my main windows bottom boarder. From googling I have learned that the drop-down list of a ComboBox is put on top of the current application window and therefore it does not respect its boundaries.
Ideally I would want the ComboBox to never exceed the main applications boundary, but I can not find any property in the documentation.
A different approach would be to limit the number of visible items of the drop-down list so that it do not exceed the window limits for a given window geometry. I was not able to find this in the documentation either and I have run out of ideas.
Take a look to the ComboBox source code, the popup is of a Menu type and it doesn't have any property to limit its size. Moreover, the z property of the Menu is infinite, i.e. it's always on top.
If you Find no way but to use the ComboBox of Qt you can create two models one for visual purpose, I will call it visual model, you will show it in your ComboBox and the complete one , it will be the reference model. Items count in your VisualModel wil be equal to some int property maximumComboBoxItemsCount that you declare . you'll need o find a way that onHovered find the index under the mouse in the visualmodel if it's === to maximumComboBoxIemsCount you do visualModel.remove(0) et visualModel.add(referenceModel.get(maximum.. + 1) and you'll need another property minimumComboBoxIemsCount, same logic but for Scroll Up , I dont know if it will work. but it's an idea
I think there is no solution using the built-in component and you should create your own comboBox. You can start from the following code.
ComboBox.qml
import QtQuick 2.0
Item {
id: comboBox
property string initialText
property int maxHeight
property int selectedItem:0
property variant listModel
signal expanded
signal closed
// signal sgnSelectedChoice(var choice)
width: 100
height: 40
ComboBoxButton {
id: comboBoxButton
width: comboBox.width
height: 40
borderColor: "#fff"
radius: 10
margin: 5
borderWidth: 2
text: initialText
textSize: 12
onClicked: {
if (listView.height == 0)
{
listView.height = Math.min(maxHeight, listModel.count*comboBoxButton.height)
comboBox.expanded()
source = "qrc:/Images/iconUp.png"
}
else
{
listView.height = 0
comboBox.closed()
source = "qrc:/Images/iconDown.png"
}
}
}
Component {
id: comboBoxDelegate
Rectangle {
id: delegateRectangle
width: comboBoxButton.width
height: comboBoxButton.height
color: "#00000000"
radius: comboBoxButton.radius
border.width: comboBoxButton.borderWidth
border.color: comboBoxButton.borderColor
Text {
color: index == listView.currentIndex ? "#ffff00" : "#ffffff"
anchors.centerIn: parent
anchors.margins: 3
font.pixelSize: 12
text: value
font.bold: true
}
MouseArea {
anchors.fill: parent
onClicked: {
listView.height = 0
listView.currentIndex = index
comboBox.selectedItem = index
tools.writePersistence(index,5)
comboBoxButton.text = value
comboBox.closed()
}
}
}
}
ListView {
id: listView
anchors.top: comboBoxButton.bottom
anchors.left: comboBoxButton.left
width: parent.width
height: 0
clip: true
model: listModel
delegate: comboBoxDelegate
currentIndex: selectedItem
}
onClosed: comboBoxButton.source = "qrc:/Images/iconDown.png"
Component.onCompleted: {
var cacheChoice = tools.getPersistence(5);
listView.currentIndex = tools.toInt(cacheChoice)
selectedItem = listView.currentIndex
comboBoxButton.text = cacheModel.get(selectedItem).value
}
}
ComboBoxButton.qml
import QtQuick 2.0
Item {
id: container
signal clicked
property string text
property alias source : iconDownUp.source
property string color: "#ffffff"
property int textSize: 12
property string borderColor: "#00000000"
property int borderWidth: 0
property int radius: 0
property int margin: 0
Rectangle {
id: buttonRectangle
anchors.fill: parent
color: "#00000000"
radius: container.radius
border.width: container.borderWidth
border.color: container.borderColor
Image {
id: image
anchors.fill: parent
source: "qrc:/Images/buttonBackground.png"
Image {
id: iconDownUp
source: "qrc:/Images/iconDown.png"
sourceSize.height:20
sourceSize.width: 20
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
id:label
color: container.color
anchors.centerIn: parent
font.pixelSize: 10
text: container.text
font.bold: true
}
MouseArea {
id: mouseArea;
anchors.fill: parent
onClicked: {
container.clicked()
buttonRectangle.state = "pressed"
startTimer.start()
}
}
Timer{
id:startTimer
interval: 200
running: false;
repeat: false
onTriggered: buttonRectangle.state = ""
}
states: State {
name: "pressed"
when: mouseArea.pressed
PropertyChanges { target: image; scale: 0.7 }
PropertyChanges { target: label; scale: 0.7 }
}
transitions: Transition {
NumberAnimation { properties: "scale"; duration: 200; easing.type: Easing.InOutQuad }
}
}
}
I've used it in some software of mine, hence it is possible that It could not work "out of the box". I use it like this:
ComboBox{
id:cacheChoice
initialText: "None"
anchors.top: baseContainer.top
anchors.topMargin: 2
anchors.right: baseContainer.right
maxHeight: 500
listModel: cacheModel
onExpanded: {
cacheChoice.height = 500
}
onClosed: {
cacheChoice.height = 20
}
}
In case you are working with ComboBox from Qt Quick Controls 2, here's the source code for it:
https://github.com/qt/qtquickcontrols2/blob/5.12/src/imports/controls/ComboBox.qml
Based on that, this override of the behavior works to limit the height to something reasonable:
myComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
return Math.min(250, myComboBox.popup.contentItem.contentHeight);
});
It is possible to access the hidden MenuStyle within the ComboBoxStyle component. There you can use all the things and hidden things you have within a MenuStyle, including its maximum height.
The thing looks roughly like this.
Not pretty but it works well enough.
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
ComboBox {
id: comboBox
style: ComboBoxStyle {
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: 400
__menuItemType: "comboboxitem" //not 100% sure if this is needed
}
}
As it came up resonantly in our team, here is a updated version of the idea shown above. The new version restricts the size automatically to the size of your application.
ComboBox {
id: root
style: ComboBoxStyle {
id: comboBoxStyle
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: Math.max(55, //min value to keep it to a functional size even if it would not look nice
Math.min(400,
//limit the max size so the menu is inside the application bounds
comboBoxStyle.control.Window.height
- mapFromItem(comboBoxStyle.control, 0,0).y
- comboBoxStyle.control.height))
__menuItemType: "comboboxitem" //not 100% sure if this is needed
} //Component __dropDownStyle: MenuStyle
} //style: ComboBoxStyle
} //ComboBox

Combine 2 MouseAreas on GridView

I have code like this:
GridView {
// ... declarations ...
model: theModel
delegate: MouseArea {
id: cellMouseArea
onClicked: // open the cell
}
MouseArea {
id: gridViewMouseArea
// here process horizontal mouse press/release actions
}
}
with a MouseArea defined in each delegate and an overall MouseArea covering my GridView. In the cellMouseArea I want to perform an open item action whereas in the gridViewMouseArea I want to implement mouseX handle to open/close a sidebar. However, the two MouseAreas do not work together. How can I carry it out?
You can exploit propagateComposedEvents:
If propagateComposedEvents is set to true, then composed events will
be automatically propagated to other MouseAreas in the same location
in the scene. Each event is propagated to the next enabled MouseArea
beneath it in the stacking order, propagating down this visual
hierarchy until a MouseArea accepts the event. Unlike pressed events,
composed events will not be automatically accepted if no handler is
present.
You can set the property to true on the GridView MouseArea. In this way click events are propagated to the MouseAreas in the delegates whereas the outer MouseArea can implement other behaviours such as drag or hoven.
Here is an example in which outer MouseArea defines drag property to slide in/out a Rectangle ( simulating your sidebar) and thanks to the propagateComposedEvents clicks are managed by the single delegates.
import QtQuick 2.4
import QtQuick.Window 2.2
ApplicationWindow {
width: 300; height: 400
color: "white"
Component {
id: appDelegate
Item {
width: 100; height: 100
Text {
anchors.centerIn: parent
text: index
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
console.info("Index clicked: " + index)
}
}
}
}
Component {
id: appHighlight
Rectangle { width: 80; height: 80; color: "lightsteelblue" }
}
GridView {
anchors.fill: parent
cellWidth: 100; cellHeight: 100
highlight: appHighlight
focus: true
model: 12
delegate: appDelegate
MouseArea {
anchors.fill: parent
z:1
propagateComposedEvents: true // the key property!
drag.target: dragged
drag.axis: Drag.XAxis
drag.minimumX: - parent.width
drag.maximumX: parent.width / 2
onMouseXChanged: console.info(mouseX)
}
}
Rectangle{
id: dragged
width: parent.width
height: parent.height
color: "steelblue"
x: -parent.width
}
}

QML ListView scrolling does not produce any animation, when delegates have different width

I am creating a ListView with horizontal orientation. The highlight is set to one fixed position of the view so that the list elements scroll through the visible area when I increment/decrement the current Item. Here is my code for the view:
ListView {
anchors.fill: parent
model: ListModel{
ListElement{name:"x"}
ListElement{name:"y"}
ListElement{name:"z"}
}
delegate:
Rectangle {
property int viewW: ListView.view.width
property bool isCurrent: ListView.isCurrentItem
width: ListView.isCurrent? viewW * 0.4 : viewW * 0.3
Text {
anchors.fill: parent
text: name
}
}
orientation: Qt.Horizontal
highlight: Rectangle {color: "transparent"}
preferredHighlightBegin: 0
preferredHighlightEnd: width*0.4
highlightRangeMode: ListView.StrictlyEnforceRange
}
I want the delegate of the current item to have a greater width than all the other elements. However, when the width of all delegates is not identical, the list scrolling animation (e.g. you can see elements moving to next position, instead of just appearing on the new position) does not apply any more.
How can I have a ListView with the current element showing a different width than the rest of the other elements, while still being able to have the scrolling animations?
The currently selected item can be modified by combining the property to modify with the currentIndex/index properties. The former is the property contained in the ListView to indicate the selected item (as you already know). The latter is a property exposed in the delegate to represent the index of the corresponding item in the list. When we have that
ListView.view.currentIndex === index
we are in the currently selected item. Hence, in your delegate, you can write something like this:
width: ListView.view.currentIndex === index ? 60 : 30
Now the selected item will be twice as large as the other items. However the effect is a little bit ugly. I would go for the following one:
scale: ListView.view.currentIndex === index ? 1.5 : 0.5
Here you are saying that "when this item is the selected one it should grow by 50%, otherwise it should shrink by 50%".
The final code for the width could look like this:
import QtQuick 2.3
import QtQuick.Window 2.0
import QtQuick.Controls 1.2
Window {
id: container
width: 300
height: 150
visible: true
ListView {
id: list
anchors.fill: parent
spacing: 5
model: ListModel{
ListElement{name:"a"}
ListElement{name:"b"}
ListElement{name:"c"}
ListElement{name:"d"}
ListElement{name:"e"}
ListElement{name:"f"}
ListElement{name:"g"}
ListElement{name:"h"}
ListElement{name:"i"}
ListElement{name:"j"}
ListElement{name:"k"}
ListElement{name:"l"}
ListElement{name:"x"}
ListElement{name:"y"}
ListElement{name:"z"}
}
delegate:
Rectangle {
width: ListView.view.currentIndex === index ? 60 : 30 // the magnifying/shrinking
color: "steelblue"
height: ListView.view.height
Text {
anchors.centerIn: parent
text: name
font.pixelSize: 20
}
Behavior on width { // added to smooth the resizing
NumberAnimation { duration: 100 }
}
}
orientation: Qt.Horizontal
highlight: Rectangle {color: "transparent"}
preferredHighlightBegin: 0
preferredHighlightEnd: delegate.width
highlightRangeMode: ListView.StrictlyEnforceRange
}
}
And the effect is not that beautiful as said. I would go for the scale property, as follows:
import QtQuick 2.3
import QtQuick.Window 2.0
import QtQuick.Controls 1.2
Window {
id: container
width: 300
height: 150
visible: true
ListView {
id: list
anchors.fill: parent
spacing: 5
model: ListModel{
ListElement{name:"a"}
ListElement{name:"b"}
ListElement{name:"c"}
ListElement{name:"d"}
ListElement{name:"e"}
ListElement{name:"f"}
ListElement{name:"g"}
ListElement{name:"h"}
ListElement{name:"i"}
ListElement{name:"j"}
ListElement{name:"k"}
ListElement{name:"l"}
ListElement{name:"x"}
ListElement{name:"y"}
ListElement{name:"z"}
}
delegate:
Rectangle {
scale: ListView.view.currentIndex === index ? 1.5 : 0.5
color: "transparent"
width: 30
height: ListView.view.height
Text {
anchors.centerIn: parent
text: name
font.pixelSize: 20
}
Behavior on scale { // added to smooth the scaling
NumberAnimation { duration: 100 }
}
}
orientation: Qt.Horizontal
highlight: Rectangle {color: "steelblue"}
preferredHighlightBegin: 0
preferredHighlightEnd: delegate.width
highlightRangeMode: ListView.StrictlyEnforceRange
}
}
Note that the highlight it strictly maintained at the beginning of the list (i.e. at the left side) as required.

Resources