Is there a way to make unselectable a "list item" in ListView? - qt

I am new to Qt. Wondering if there a possibility to make an item "unselectable" in ListView.
I see there are a lot of other things, e.g: collapsing , expanding, etc.
**
I have not find any simple example for this problem. **
Can you provide some minimalistic examples to make a specific item in the list unselectable?
I have the following minimalistic example. How can I set list item index 2 to be unselectable?
Window {
id: mainWindow
width: 130
height: 240
visible: truetitle: qsTr("Hello")
Rectangle {
id: bg
color: "#ffffff"
anchors.fill: parent
ListModel {
id: nameModel
ListElement { name: "Alice" }
ListElement { name: "Bob" }
ListElement { name: "Jay" }
ListElement { name: "Kate" }
}
Component {
id: nameDelegate
Text {
text: model.name
font.pixelSize: 24
}
}
ListView {
anchors.fill: parent
model: nameModel
delegate: nameDelegate
clip: true
highlight: Rectangle {
anchors { left: parent.left; right: parent.right }
//height: parent.height
color: "lightgrey"
}
}
}
}

I found numerous issues with your code snippet, so I attempted to address them all:
I made use of Page and set Page.background instead of declaring an outer Rectangle. This removes a level of indentation
I refactored NameComponent.qml to reduce the complexity of your main program
I change the delegate from Text to ItemDelegate so that it is clickable, and, it being clickable, I can (1) make the ListView have active focus so that it can receive keyboard events, (2) change the current item in the ListView => I think this achieves your criteria of being able to select a different item
I removed unnecessary anchoring from your highlight - your highlight will default anchor to your selected item
I set the width of your delegate to listView.width - I also made use of the ListView.view attached property so that your delegate and access properties from the ListView
Finally, I added a 20 pixel width vertical ScrollBar
import QtQuick
import QtQuick.Controls
Page {
background: Rectangle { color: "#ffffff" }
ListModel {
id: nameModel
ListElement { name: "Alice" }
ListElement { name: "Bob" }
ListElement { name: "Jay" }
ListElement { name: "Kate" }
}
ListView {
anchors.fill: parent
model: nameModel
delegate: NameDelegate { }
clip: true
highlight: Rectangle {
color: "lightgrey"
}
ScrollBar.vertical: ScrollBar {
width: 20
policy: ScrollBar.AlwaysOn
}
}
}
// NameDelegate.qml
import QtQuick
import QtQuick.Controls
ItemDelegate {
property ListView listView: ListView.view
width: listView.width - 20
text: model.name
font.pixelSize: 24
onClicked: {
listView.forceActiveFocus();
if (listView.currentIndex === index) {
listView.currentIndex = -1;
} else {
listView.currentIndex = index;
}
}
}
You can Try it Online!

Related

Is it possible to make a cycle swipe items in qml?

I use swipeview in qml. I can swipe items from first to final and back. Is it possible to swipe from final to first immediatly ? I didn't find any information in docs.
You can do that with PathView. Qt Quick Controls 2's Tumbler can also be made to wrap because it uses PathView internally.
You can use PathView. Some codes like this:
import QtQuick 2.0
Rectangle {
width: 200
height: 200
ListModel {
id: model
ListElement {
color: "red"
}
ListElement {
color: "green"
}
ListElement {
color: "blue"
}
}
Component {
id: delegate
Rectangle {
id: wrapper
width: view.width
height: view.height
color: model.color
Text {
anchors.centerIn: parent
font.pointSize: 26
font.bold: true
color: "white"
text: index
}
}
}
PathView {
id: view
anchors.fill: parent
snapMode: PathView.SnapOneItem
highlightRangeMode: PathView.StrictlyEnforceRange
currentIndex: -1
model: model
delegate: delegate
path: Path {
startX: -view.width / 2 // let the first item in left
startY: view.height / 2 // item's vertical center is the same as line's
PathLine {
relativeX: view.width * view.model.count // all items in lines
relativeY: 0
}
}
}
}

How to fill a ListView manually in QML?

I have a Listview which will hold material list. My element is a text and an image. Text holds material name, image is a delete button. When I click add button, I want to add my element to the listview. Then if I click delete button, I want to delete record from Listview.
How can I do that?
I suppose you have a ListModel in your code. ListModel provides some methods to insert and remove items.
You mentioned your ListElement contains a text and a delete image. So I think you have something like the following code, where you can see an example of appending and removing elements.
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
width: 500
height: 500
Rectangle {
width: 250
height: 400
Component {
id: listDelegate
Item {
width: 250; height: 50
Row {
Column {
width: 200
Text { text: 'mytext: ' + mytext }
}
Column {
width: 50
Image {
id: deleteButton
source: "delete.jpg"
MouseArea {
anchors.fill: parent;
onClicked:{
console.debug("clicked:"+ index);
listModel.remove(index);
}
}
}
}
}
}
}
ListModel {
id: listModel
ListElement {
mytext: "AAA"
}
ListElement {
mytext: "BBB"
}
}
ListView {
id: listView
anchors.fill: parent
model: listModel
delegate: listDelegate
focus: true
}
}
Button {
y: 450
text: "add"
onClicked: {
listModel.append({"mytext": "XXX"})
}
}
}

How to access the property of a GridView - or ListView - element

Here is the code, I create 4 buttons. When one is clicked I wanna that its color changes to red and the color of all the others change to black.
But looks like I could not access the color property.
Rectangle {
id: root
width: 200; height: 100
DelegateModel {
id: visualModel
model: ListModel {
ListElement { my_color: "red" }
ListElement { my_color: "black" }
ListElement { my_color: "black" }
ListElement { my_color: "black" }
}
groups: [
DelegateModelGroup { name: "selected" }
]
delegate: Rectangle {
id: item
height: 25
width: 200
color:my_color
MouseArea {
anchors.fill: parent
onClicked: {
console.log(visualModel.items.get(index).color)
for (var i = 0; i < root.count; i++){
if(index == i)
visualModel.items.get(i).color = "red";
else
visualModel.items.get(i).color = "black";
}
}
}
}
}
ListView {
anchors.fill: parent
model: visualModel
}
}
I advice you to use ExclusiveGroup from QML controls. Usually it is used for Action but it's possible to use it for any other Item. From the Qt docs:
It is possible to add support for ExclusiveGroup for an object or
control. It should have a checked property, and either a
checkedChanged, toggled(), or toggled(bool) signal.
So all we need is to add suitable property. Small example:
import QtQuick 2.5
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
Window {
width: 200
height: 400
ExclusiveGroup { id: exclusiveGroup }
ListView {
anchors.fill: parent
anchors.margins: 5
spacing: 2
model: 10
delegate: Rectangle {
id: myItem
property bool checked: false // <-- this is necessary
height: 30
width: parent.width
color: myItem.checked ? "lightblue" : "#DEDEDE"
border { width: 1; color: "#999" }
radius: 5
Text { text: "item" + (index + 1); anchors.centerIn: parent}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: myItem.checked = !myItem.checked;
}
Component.onCompleted: {
exclusiveGroup.bindCheckable(myItem);
}
}
}
}

Different delegates for QML ListView

I would like to know if it's possible to use (several) different delegates for a QML ListView.
Depending on the individual object in the ListView model, I would like to visualize the objects with different delegates.
This piece of code explains what I want to achieve:
main.qml
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"
position: "Engineer"
}
ListElement {
name: "John Brown"
position: "Engineer"
}
ListElement {
name: "Sam Wise"
position: "Manager"
}
}
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: {
if (position == "Engineer") return Employee; //<--- depending on condition, load Contact{}
else if (position == "Manager") return Manager; //<--- depending on condition, load Person{}
}
}
}
Employee.qml (One possible Component which I would like to use as a delegate)
import QtQuick 2.4
Rectangle{
width: 200
height: 50
color: ListView.isCurrentItem ? "#003366" : "#585858"
border.color: "gray"
border.width: 1
Text{
anchors.centerIn: parent
color: "white"
text: name
}
}
Manager.qml (other Component I would like to use as a delegate)
import QtQuick 2.4
Rectangle{
width: 200
height: 50
color: "red"
border.color: "blue"
border.width: 1
Text{
anchors.centerIn: parent
color: "white"
text: name
}
}
I would appreciate any advice!
Thanks!
I've had the same problem, the Qt documentation is providing a pretty good answer: http://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate
The easiest solution is an inline Component with a Loader to set a source file:
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: Component {
Loader {
source: switch(position) {
case "Engineer": return "Employee.qml"
case "Manager": return "Manager.qml"
}
}
}
}
Any attempt to use Loader.srcComponent will result in missing any variable from the model (including index). The only way for the variables to be present is the children Component to be inside the main Component, but then only one can be present, so it is useless.
I believe it would be better to implement one base delegate for all kind of position which loads concrete implementation depending on position or any other data properties using Loader
BaseDelegate {
property var position
Loader {
sourceComponent: {
switch(position) {
case "Engineer": return engineerDelegate
}
}
}
Component {
id: engineerDelegate
Rectangle {
Text { }
}
}
}
I implemented it as follow:
ListView {
id: iranCitiesList
model: sampleModel
delegate: Loader {
height: childrenRect.height
width: parent.width
sourceComponent: {
switch(itemType) {
case "image" :
return imageDel;
case "video":
return videoDel;
}
}
}
ImageDelegate { id: imageDel }
VideoDelegate { id: videoDel }
}
ImageDelegate.qml
Component {
Image { /*...*/ }
}
VideoDelegate.qml
Component {
Item { /*....*/ }
}
Last note, check width and height of delegates. In my case, I had to set width and height of my delegate in Loader again.
Good luck - Mousavi
The simplest way to do this now is using DelegateChooser. This also allows you to edit the properties of the delegates, which is something that is more difficult to do with Loader!
Example inspired from the docs:
import QtQuick 2.14
import QtQuick.Controls 2.14
import Qt.labs.qmlmodels 1.0
ListView {
width: 640; height: 480
ListModel {
id: contactsModel
ListElement {
name: "Bill Smith"
position: "Engineer"
}
ListElement {
name: "John Brown"
position: "Engineer"
}
ListElement {
name: "Sam Wise"
position: "Manager"
}
}
DelegateChooser {
id: chooser
role: "position"
DelegateChoice { roleValue: "Manager"; Manager { ... } }
DelegateChoice { roleValue: "Employee"; Employee { ... } }
}
model: contractsModel
delegate: chooser
}
Sure, it's possible. ListView.delegate is a kind of pointer to a Component which will draw the items so you can change it.
For example:
Employee { id: delegateEmployee }
Manager { id: delegateManager}
...
ListView {
property string position
delegate: position == "Engineer" ? delegateEmployee : delegateManager
}
As far as you have only two types, the following code is as easy to maintain as easy to understand:
delegate: Item {
Employee { visible = position === "Engineer" }
Manager { visible = position === "Manager" }
}
In case the number of types will grow, it is not a suitable solution for it easily leads to an hell of if statement.
Because position is either "Manager" or "Engineer" and the delegates are saved in Manager.qml or Engineer.qml we can use a clever expression for Loader.source:
Loader {
source: position + ".qml"
}
Here's the full source:
import QtQuick
import QtQuick.Controls
Page {
ListModel {
id: contactsModel
ListElement { name: "Bill Smith"; position: "Engineer" }
ListElement { name: "John Brown"; position: "Engineer" }
ListElement { name: "Sam Wise"; position: "Manager" }
}
ListView {
id: listView
anchors.fill: parent
model: contactsModel
delegate: Loader {
width: ListView.view.width
source: position + ".qml"
}
}
}
//Engineer.qml
import QtQuick
import QtQuick.Controls
Rectangle {
property bool isCurrentItem: listView.currentIndex === index
height: 50
color: isCurrentItem ? "#0033cc" : "#585858"
border.color: "gray"
border.width: 1
Text {
anchors.centerIn: parent
color: "white"
text: name
}
}
//Manager.qml
import QtQuick
import QtQuick.Controls
Rectangle {
property bool isCurrentItem: listView.currentIndex === index
height: 50
color: isCurrentItem ? "#cc3300" : "#661100"
border.color: "blue"
border.width: 1
Text {
anchors.centerIn: parent
color: "white"
text: name
}
}
You can Try it Online!

Qt QML dropdown list like in HTML

Simple thing, but can't get find it. I want a simple dropdown selection box with a couple of selections. Like in HTML
<select>
<option>1</option>
<option>2</option>
</select>
what is the code for QML for that ?
Here's a simple example that could maybe be used as a starting point:
import QtQuick 1.0
Rectangle {
width:400;
height: 400;
Rectangle {
id:comboBox
property variant items: ["Item 1", "Item 2", "Item 3"]
property alias selectedItem: chosenItemText.text;
property alias selectedIndex: listView.currentIndex;
signal comboClicked;
width: 100;
height: 30;
z: 100;
smooth:true;
Rectangle {
id:chosenItem
radius:4;
width:parent.width;
height:comboBox.height;
color: "lightsteelblue"
smooth:true;
Text {
anchors.top: parent.top;
anchors.left: parent.left;
anchors.margins: 8;
id:chosenItemText
text:comboBox.items[0];
font.family: "Arial"
font.pointSize: 14;
smooth:true
}
MouseArea {
anchors.fill: parent;
onClicked: {
comboBox.state = comboBox.state==="dropDown"?"":"dropDown"
}
}
}
Rectangle {
id:dropDown
width:comboBox.width;
height:0;
clip:true;
radius:4;
anchors.top: chosenItem.bottom;
anchors.margins: 2;
color: "lightgray"
ListView {
id:listView
height:500;
model: comboBox.items
currentIndex: 0
delegate: Item{
width:comboBox.width;
height: comboBox.height;
Text {
text: modelData
anchors.top: parent.top;
anchors.left: parent.left;
anchors.margins: 5;
}
MouseArea {
anchors.fill: parent;
onClicked: {
comboBox.state = ""
var prevSelection = chosenItemText.text
chosenItemText.text = modelData
if(chosenItemText.text != prevSelection){
comboBox.comboClicked();
}
listView.currentIndex = index;
}
}
}
}
}
Component {
id: highlight
Rectangle {
width:comboBox.width;
height:comboBox.height;
color: "red";
radius: 4
}
}
states: State {
name: "dropDown";
PropertyChanges { target: dropDown; height:40*comboBox.items.length }
}
transitions: Transition {
NumberAnimation { target: dropDown; properties: "height"; easing.type: Easing.OutExpo; duration: 1000 }
}
}
}
For new users, there is a built in ComboBox in Qt5.3 QtQuick.Controls ComboBox - Reference.
Example from Documentation:
import QtQuick 2.2
import QtQuick.Controls 1.2
ComboBox {
id: combo
editable: true
model: ListModel {
id: model
ListElement { text: "Banana"; color: "Yellow" }
ListElement { text: "Apple"; color: "Green" }
ListElement { text: "Coconut"; color: "Brown" }
}
onAccepted: {
if (combo.find(currentText) === -1) {
model.append({text: editText})
currentIndex = combo.find(editText)
}
}
}
Note: I had to post it as an answer, since the text is too long for a comment.
I've been using approaches with ComboBoxStyle (customization capabilities limited) and completely custom implementations, but they have a lot of limitations with focus management and z-index management.
I've end up with the implementation of ComboBox which consists of 2 parts: a header which you actually put somewhere and a dropdown component which you create dynamically. The latter consists of an Item covering everything (and intercepting mouse activity) and a dropdown which is carefully positioned below the header.
Code is pretty massive to included here so you can see details in my blogpost with all the code
If you targeting Nokia devices (Symbian or Meego), you could use high-level Qt Quick components. I believe Menu is something similar to html's select.
See http://doc.qt.nokia.com/qt-components-symbian/qml-menu.html or http://harmattan-dev.nokia.com/docs/library/html/qt-components/qt-components-meego-menu.html
If you are targeting Symbian, there is SelectionListItem and SelectionDialog:
http://doc.qt.nokia.com/qt-components-symbian/qml-selectionlistitem.html

Resources