QML: how to add scrollbar into ListView - qt

I have a simple ListView program and therein trying to attach scrollbar. There is no movement on list while scrolling up/ down, here, list should be moving accordingly. Seems, I am not able to set contentItem correctly. Looking for some hints.
Please find my sample core below, thereupon I added a vertical scrollbar to listView.rolesListModel is my model.
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
width: 400
height: 300
visible: true
ListModel
{
id:rolesListModel
ListElement
{
t:"One"
}
ListElement
{
t:"Two"
}
ListElement
{
t:"Three"
}
ListElement
{
t:"Five"
}
ListElement
{
t:"Six"
}
ListElement
{
t:"Seven"
}
ListElement
{
t:"Eight"
}
ListElement
{
t:"Nine"
}
ListElement
{
t:"Ten"
}
}
ListView {
id: listView
width: 150
height: 100
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.StopAtBounds
model: rolesListModel
clip: true
delegate: listRect
ScrollBar {
id: vbar
active: true
orientation: Qt.Vertical
size: listView.height / listView.contentHeight
position: listView.currentItem
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}
Component
{
id:listRect
Rectangle
{
id:listElementRect
height:20
width: 100
Text
{
id:elementText
width:parent.width
height:parent.height
text:t
horizontalAlignment: "AlignHCenter"
}
}
}
}

Newer way
You're using an older version of QML, QtQuick is now at 2.14. If you were okay moving from ScrollBar to a ScrollView, then the code for an arbitrary ListView would look like this:
import QtQuick 2.14
import QtQuick.Controls 2.14
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Scroll")
ScrollView {
anchors.fill: parent
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
ListView {
width: parent.width
model: 20
delegate: ItemDelegate {
text: "Item " + (index + 1)
width: parent.width
}
}
}
}
Please note the essential scrollbar configuration line ScrollBar.vertical.policy: ScrollBar.AlwaysOn
https://doc.qt.io/qt-5/qml-qtquick-controls2-scrollview.html#scroll-bars
Older way
If, however, you would like to continue with your code, then the fix is to specifically declare that the vertical scrollbar property is bound to your scrollbar
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
width: 400
height: 300
visible: true
ListModel
{
id:rolesListModel
ListElement
{
t:"One"
}
ListElement
{
t:"Two"
}
ListElement
{
t:"Three"
}
ListElement
{
t:"Five"
}
ListElement
{
t:"Six"
}
ListElement
{
t:"Seven"
}
ListElement
{
t:"Eight"
}
ListElement
{
t:"Nine"
}
ListElement
{
t:"Ten"
}
}
ListView {
id: listView
width: 150
height: 100
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.StopAtBounds
model: rolesListModel
clip: true
delegate: listRect
ScrollBar.vertical: vbar
ScrollBar {
id: vbar
active: true
orientation: Qt.Vertical
size: listView.height / listView.contentHeight
position: listView.currentItem
policy: ScrollBar.AlwaysOn
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
}
}
Component
{
id:listRect
Rectangle
{
id:listElementRect
height:20
width: 100
Text
{
id:elementText
width:parent.width
height:parent.height
text:t
horizontalAlignment: "AlignHCenter"
}
}
}
}
Please note the crucial ScrollBar.vertical: vbar line, which assigns your ScrollBar component vbar to the ListView.
https://doc.qt.io/qt-5/qml-qtquick-controls2-scrollbar.html#details

Related

Dragging from Listview to a droparea

I am following this example here Using drag and drop with ListView to create an inventory UI. However, I have broken down the components into a main section, Listview and a droparea. Everything is working except, I cannot get the ReferenceError: listView is not defined. Please help
Main area
import QtQuick 2.15
import QtQuick.Controls 2.15
Page{
id:dragRect
Rectangle {
id: root
width: 400
height: 400
ListViewElem{
}
DropAreaElem{}
}
}
ListViewElem
import QtQuick 2.15
ListViewElem {
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: dragRect2
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: dragRect2
drag.onActiveChanged: {
if (mouseArea.drag.active) {
listView.dragItemIndex = index;
}
dragRect2.Drag.drop();
}
}
states: [
State {
when: dragRect2.Drag.active
ParentChange {
target: dragRect2
parent: root
}
AnchorChanges {
target: dragRect2
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
}
}
]
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: dragRect2.width / 2
Drag.hotSpot.y: dragRect2.height / 2
}
}
}
DropAreaElem
import QtQuick 2.15
Rectangle {
width: parent.width / 2
height: parent.height
anchors.right: parent.right
color: "#aaff0011"
DropArea {
id: dropArea
anchors.fill: parent
onDropped: {
listView.model.remove(listView.dragItemIndex);
listView.dragItemIndex = -1;
}
}
}
You should provide id to your components
import QtQuick 2.15
import QtQuick.Controls 2.15
Page{
id:dragRect
Rectangle {
id: root
width: 400
height: 400
ListViewElem{
id: listView
}
DropAreaElem{
id: dropArea
}
}
}

QML DelegateModel: Access DelegateModel from delegate

I'm trying to create a ListView with different delegates and a drag'n'drop functionality. The delegates shall be loaded with a Loader.
The QML Documentation provides a working example for a ListView without a Loader:
http://doc.qt.io/qt-5/qtquick-tutorials-dynamicview-dynamicview3-example.html
However, using the Loader I get the error: Cannot read property 'DelegateModel' of undefined
I do not understand how I can access the DelegateModel from the Loader.
A hint to the solution is highly appreciated!
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import QtQml.Models 2.3
Window {
id: mainroot
visible: true
width: 640
height: 480
Rectangle{
id:viewContainer
anchors.fill: parent
DelegateModel {
id: visualModel
model: ListModel{
id:m_model
ListElement{
type:1
m_text :"Text1"
}
ListElement{
type:1
m_text :"Text2"
}
}
delegate: Loader{
id:idLoader
width: view.width
height: childrenRect.height
Component.onCompleted: {
switch(type){
case 1:
idLoader.setSource("TestDelegate.qml", {"m_text": m_text})
break;
}
}
}
}
ListView{
id: view
anchors.fill: parent
spacing: 5
model: visualModel
}
}
}
TestDelegate.qml:
import QtQuick 2.7
MouseArea {
id: dragArea
property bool held: false
property string m_text
anchors { left: parent.left; right: parent.right }
height: 50
width: view.width
drag.target: held ? content : undefined
drag.axis: Drag.YAxis
onPressAndHold: held = true
onReleased: held = false
Rectangle {
id: content
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
width: dragArea.width
height: textfield.implicitHeight
Drag.active: dragArea.held
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
border.width: 1
border.color: "lightsteelblue"
color: dragArea.held ? "lightsteelblue" : "white"
Behavior on color { ColorAnimation { duration: 100 } }
radius: 2
states: State {
when: dragArea.held
ParentChange { target: content; parent: viewContainer }
AnchorChanges {
target: content
anchors { horizontalCenter: undefined; verticalCenter: undefined }
}
}
Text{
id: textfield
anchors.centerIn: parent
text: m_text
}
}
DropArea {
anchors { fill: parent; margins: 10 }
onEntered: {
visualModel.items.move(
idLoader.item.drag.source.DelegateModel.itemsIndex,
idLoader.item.dragArea.DelegateModel.itemsIndex)
}
}
}
The items defined in the file loaded with Loader or in general with any other .qml file that is imported should not depend directly on the main file since the ids have a scope, it is better to expose properties, in your case:
╭------------------------------------------╮
| bool held ------┿--->
| TestDelegate string m_text ------┿--->
| ============ DelegateModel md ------┿--->
| int index ------┿--->
╰------------------------------------------╯
Considering the above, the solution is the following:
main.qml
import QtQuick 2.7
import QtQuick.Window 2.0
import QtQml.Models 2.3
Window {
id: mainroot
visible: true
width: 640
height: 480
Rectangle{
id:viewContainer
anchors.fill: parent
DelegateModel {
id: visualModel
model: ListModel{
id:m_model
Component.onCompleted: {
for(var i=0; i< 40; i++){
m_model.append({"type": 1, "m_text": "Text" + i})
}
}
}
delegate:
Loader{
id: idLoader
width: view.width
height: childrenRect.height
property int index: DelegateModel.itemsIndex
onIndexChanged: if(status == Loader.Ready) idLoader.item.index = index
Component.onCompleted: {
switch(type){
case 1:
idLoader.setSource("TestDelegate.qml", {
"m_text": m_text,
"index": index,
"md": visualModel
})
break;
}
}
}
}
ListView{
id: view
anchors.fill: parent
spacing: 5
model: visualModel
}
}
}
TestDelegate.qml
import QtQuick 2.7
import QtQml.Models 2.3
MouseArea {
id: dragArea
property bool held: false
property string m_text
property DelegateModel md: null
property int index : -1;
anchors { left: parent.left; right: parent.right }
height: 50
width: view.width
drag.target: held ? content : undefined
drag.axis: Drag.YAxis
onPressAndHold: held = true
onReleased: held = false
Rectangle {
id: content
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
width: dragArea.width
height: textfield.implicitHeight
Drag.active: dragArea.held
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
border.width: 1
border.color: "lightsteelblue"
color: dragArea.held ? "lightsteelblue" : "white"
Behavior on color { ColorAnimation { duration: 100 } }
radius: 2
states: State {
when: dragArea.held
ParentChange { target: content; parent: viewContainer }
AnchorChanges {
target: content
anchors { horizontalCenter: undefined; verticalCenter: undefined }
}
}
Text{
id: textfield
anchors.centerIn: parent
text: m_text
}
}
DropArea {
anchors { fill: parent; margins: 10 }
onEntered: {
if(md !== null)
md.items.move(drag.source.index, dragArea.index)
}
}
}

Error of 'TypeError: Cannot read property 'name' of undefined', it comes start to end or vice-versa of application window by moving mouse wheel scroll

I am working on qml, found found an error during moving mouse wheel scrolling i.e.
"TypeError: Cannot read property 'name' of undefined".
It only appear when either mouse wheel scrolling or close the application while during execution and beginning of the application didn't show this error.
One more thing, I am getting accurate data/result whatever I want but due to this error the performance of application get affected.
Please let me know how can I get rid of this issue?
also find below example to see error.
My code is..
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
TableView {
anchors.fill: parent
visible: true
backgroundVisible: false
alternatingRowColors: false
sortIndicatorVisible: true
clip: true
highlightOnFocus: false
width: parent.width
height: parent.height
TableViewColumn{ role: "code" ; title: "cde" ; width: 200;
delegate: Component {
id: codeDelegate
Item{
Text {
color: "lightgray"
elide: styleData.elideMode
text: styleData.value //modelSettingData.get(styleData.row).name
font.family: "Arial"
font.pixelSize: 18
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Text {
id: metaData
color: "red"
width: parent.width
// height: 20
anchors.bottom: parent.bottom
anchors.bottomMargin: -parent.height/1.5
font.weight: Font.ExtraLight
//elide: Text.ElideMiddle
text: "Time: " + modelSettingData.get(styleData.row).name //Error comes when this part added but I need to use it with the same form
}
}
}
}
}
TableViewColumn{ role: "name" ; title: "nme" ; width: 200
delegate: Component {
id: nameDelegate
Text {
color: "yellow"
elide: styleData.elideMode
text: styleData.value
font.family: "Arial"
font.pixelSize: 18
}
}
}
model: ListModel {
id: modelSettingData
ListElement {
name: "aaaaaaaaaa"
code: "3042666666666"
}
ListElement {
name: "bbbbbb"
code: "32235"
}
ListElement {
name: "ccccccc"
code: "32638"
}
ListElement {
name: "ddddddddddd"
code: "00000000000"
}
ListElement {
name: "eeeeeeeeeeee"
code: "111111111111"
}
ListElement {
name: "ffffffffffff"
code: "222222222222"
}
ListElement {
name: "ggggggggggggg"
code: "3333333333333"
}
ListElement {
name: "hhhhhhhhhhhhh"
code: "4444444444444"
}
ListElement {
name: "iiiiiiiiiiiii"
code: "5555555555555"
}
}
rowDelegate: Rectangle {
property bool selected : styleData.selected
width: parent.width-2
height: 100
color: styleData.selected? "black" : "black"
Rectangle {
width: parent.width
height: 1
anchors.bottom: parent.bottom
visible: parent.selected
color: "yellow"
}
}
}
}

QML fill width of parent element

I have a problem, which can be seen on the attached screenshot
There is ApplicationWindow, which has header and ListView which is used in horizontal layout. Each item of list should be one page of application. Unfortunatelly, the width of base page is not set correctly to fill width of its parent (white background, not the grey one).
Here is the code of main page, it should load Login page - it is shown on the image.
ApplicationWindow {
id: root_window
title: Style.applicationName
visible: true
color: "white"
width: Style.windowWidth
height: Style.windowHeight
ColumnLayout {
id: root_layout
spacing: 0
width: root_window.width
height: root_window.height
SmonHeader {
id: smon_user_app_header
height: Style.headerHeight
anchors.top: parent.top
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
}
Component.onCompleted: {
console.log("Main width: " + width);
}
ListView {
id: navigation
width: parent.width
height: parent.height
orientation: ListView.Horizontal
interactive: true // disable manual pageChange
snapMode: ListView.SnapOneItem // while moving to right, finish move
highlightRangeMode: ListView.StrictlyEnforceRange // mouse gesture make currentIndex change
highlightMoveDuration: 400 // speed up pages change (swap)
model: ObjectModel {
/* First page with login capabilities */
Login {
id: login_module
width: root_layout.width
height: root_layout.height
}
}
}
}
/* Private function definition*/
function init_database()
{
var database = Storage.LocalStorage.openDatabaseSync(Style.databaseName, Style.databaseVersion, Style.databaseDescr, Style.databaseSize);
smonDatabase.startDatabase(Style.databaseName);
}
Component.onCompleted: {
init_database();
}
}
Here is base of Login page
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import "../"
import "./common"
Rectangle {
id: login_page
// why parent.width is not set ?
anchors.fill: parent
//width: parent.width
//Layout.fillWidth: true
property string credentials_title: qsTr("Přístupové údaje")
property string available_devices: qsTr("Dostupné servery")
property string identity_title: qsTr("Identita")
property string password_title: qsTr("Heslo")
property string domain_title: qsTr("Doména")
property string infoLine_title: qsTr("Zapamatovat přihlašovací údaje")
property string username_title: qsTr("Jméno");
Component.onCompleted: {
console.log("Login width: " + login_page.width);
control.cancelEnabled = false
}
ColumnLayout{
id: navigation
spacing: Style.spacing
anchors.left: parent.left
anchors.leftMargin: Style.defaultAnchors
Layout.fillWidth: true
anchors.fill: parent
width: parent.width
Text {
id: title
//anchors.top: parent.top
//anchors.left: parent.left
font.pointSize: Style.fontSizeHeading
text: credentials_title
}
ColumnLayout{
id: navigationLogin
spacing: Style.spacing
anchors.left: parent.left
anchors.leftMargin: Style.defaultAnchors
Layout.fillWidth: true
Layout.bottomMargin: Style.bottomMargin
width: (parent.width - 4*Style.defaultAnchors)
GridLayout {
id: input_login
rowSpacing: Style.denseSpacing
columns: 2
columnSpacing: Style.spacing
anchors.left: parent.left
anchors.leftMargin: Style.defaultAnchors
width: 200
Text {
id: user_name
font.pointSize: Style.fontSizeSubHeading
text: username_title
}
SmonComboBox {
id: user
width: parent.width
value: smonRole.user
object: smonRole
prop: "user"
isEditable: true
dataModel: smonRole.userData
selectedIndex: smonRole.userNameSelected
}
Text {
id: password_name
font.pointSize: Style.fontSizeSubHeading
text: password_title
}
SmonTextField {
id: password
width: parent.width
type: "password"
object: smonRole
prop: "pass"
value: smonRole.pass
onEnterPressed: {
user.enabled = false
password.enabled = false
//control.okEnabled = false
control.okEnabled = false
control.cancelEnabled = true
smonRole.save();
smonCommunication.connect();
}
onValueChanged: {
if(password.value !== "")
{
control.okEnabled = true
}
else
{
control.okEnabled = false
}
}
}
}
ColumnLayout {
id: scanning
spacing: Style.denseSpacing
anchors.left: parent.left
//Layout.fillWidth: true
RowLayout {
id: slider_component
Text {
id: scanningHeader
font.pointSize: Style.fontSizeSubHeading
text: qsTr("Perioda vyhledávání zařízení");
}
Text {
id: value
font.pointSize: Style.fontSizeInfo
anchors.left: scanningHeader.right
anchors.leftMargin: Style.defaultAnchors
width: 30
text: slider.value
}
}
Slider {
id: slider
minimumValue: 2
maximumValue: 30
Layout.fillWidth: true
stepSize: 1
value: smonCommunication.scanPeriod
onValueChanged: {
smonCommunication.scanPeriod = slider.value;
}
}
}
SmonControlPanel {
id: control
width: parent.width
okEnabled: smonRole.user != "" && smonRole.pass != ""
okVisible: true
cancelVisible: true
onSignalOk: {
// hide content
user.enabled = false
password.enabled = false
control.okEnabled = false
control.cancelEnabled = true
smonRole.save();
smonCommunication.connect();
}
onSignalCancel: {
// show content again
password.enabled = true
user.enabled = true
//domain.enabled = true
control.cancelEnabled = false
control.okEnabled = true
//smonConnection.logout();
smonCommunication.disconnect();
smonRole.disconnected();
}
}
}
Text {
id: favourite
font.pointSize: Style.fontSizeHeading
text: available_devices
}
ListView{
id: servers
Layout.fillHeight: true
width: parent.width
model: smonCommunication.devicesList
delegate: Rectangle {
id: serverList
height: 80
width: parent.width
ColumnLayout{
Text {
id: serverName
text: modelData.bluetooth_name
}
Text {
id: rssi
text: modelData.bluetooth_rssi
}
}
MouseArea {
id: bt_device
anchors.fill: parent
onClicked: {
if(smonCommunication.btCanConnect === true)
smonCommunication.connect(index);
}
}
}
}
}
MessageDialog {
id: errorDialog
standardButtons: StandardButton.Cancel | StandardButton.OK
visible: false;
informativeText: smonCommunication.errorMessage
onInformativeTextChanged: {
errorDialog.visible = true;
}
}
}
Is there problem on the main page or on the page which is loaded ? Thanks for help...
Your problem lies with the anchors.fill: parent bit in your ObjectModel.
The parent here, is not the ListView, but the ListView's contentItem, which happens to have an implicit width of 100px.
In your minimal example, something like this should work:
model: ObjectModel {
/* First page with login capabilities */
Rectangle{
id: one
//anchors.fill: parent <- parent is not navigation
width: navigation.width
height: 50
color: "red"
}
}
Generally speaking, you should not use the parent property in your delegates.
So, after answers from ddriver and Kevin Krammer (thanks) I made a minimal working example.
I stopped using ColumnLayout, and set everything as best as I can.
Here is the code
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
import QtQml.Models 2.1
ApplicationWindow {
id: root_window
title: "Hello world"
visible: true
color: "white"
width: 480
height: 520
Rectangle {
id: smon_user_app_header
height: 50
color: "blue"
width: parent.width
}
ListView {
id: navigation
orientation: ListView.Horizontal
interactive: true // disable manual pageChange
snapMode: ListView.SnapOneItem // while moving to right, finish move
highlightRangeMode: ListView.StrictlyEnforceRange // mouse gesture make currentIndex change
highlightMoveDuration: 400 // speed up pages change (swap)
anchors.top: smon_user_app_header.bottom
anchors.bottom: root_window.bottom
width: parent.width
height: 400
model: ObjectModel {
/* First page with login capabilities */
Rectangle{
id: one
anchors.fill: parent
color: "red"
}
}
}
}
And here is how it looks

Propagate Click from QML Combobox

I have a Listmodel where the delegates get selected/highlighted when they are clicked on. However when I click on a Combobox, which is part of the delegate, the delegate does not get selected.
Is there something like propagateComposedEvents which could propagate the click to the MouseArea of the delegate?
What would be the best way to also select the delegate when I click on it's containing Combobox?
Here is a screenshot
Here is the example code
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
ListModel {
id: contactsModel
ListElement {
name: "Bill Smith"
}
ListElement {
name: "John Brown"
}
ListElement {
name: "Sam Wise"
}
}
ListModel {
id: roleModel
ListElement {
text: "Employee"
}
ListElement {
text: "Manager"
}
ListElement {
text: "Big Boss"
}
}
ListView{
id: contactsView
anchors.left: parent.left
anchors.top: parent.top
width: parent.width
height: parent.height
orientation: Qt.Vertical
spacing: 10
model: contactsModel
delegate: contactsDelegate
}
Component{
id: contactsDelegate
Rectangle{
width: 200
height: 50
color: ListView.isCurrentItem ? "#003366" : "#585858"
border.color: "gray"
border.width: 1
MouseArea{
anchors.fill: parent
onClicked: {
contactsView.currentIndex = index;
}
}
Column{
Text {
color: "white"
text: name
}
ComboBox{
currentIndex: 0
model: roleModel
}
}
}
}
}
ComboBox{
currentIndex: 0
model: roleModel
onPressedChanged: if (pressed) contactsView.currentIndex = index
}
It is not exactly propagating, but it does the trick.

Resources