Qt Quick QML Customize ComboBox Popup - qt

I am trying to customize the QML 2.14 ComboBox.
I did follow the below link but I am not able to customize the ComboBox -> Popup -> ListView -> "delegate"
https://doc.qt.io/qt-5/qtquickcontrols2-customize.html#customizing-combobox
I want to have diffrent Text Color for the list item displayed inside the ComboBox Popup.
ComboBox {
id: myComboBox
model: ["First", "Second", "Third"]
popup: Popup {
y: myComboBox.height - 1
width: parent.width
implicitHeight: contentItem.implicitHeight
contentItem: ListView {
clip: true
anchors.fill: parent
model: myComboBox.popup.visible ? myComboBox.delegateModel : null
ScrollIndicator.vertical: ScrollIndicator {}
delegate: Text {
width: parent.width
height: 30
text: "Test" // How to access flat model, modelData is null and model.get(index) is not allowed in .ui.qml
color: "#ffffff"
anchors.horizontalCenter: parent.horizontalCenter
anchors.left: parent.left
}
highlight: Rectangle { color: "yellow" }
}
}
}
But I always see some default list with black text, in the combobox popup.
Also not able to apply the highlight to selected line in the popup.

The Combobox's model is a DelegateModel. That means the delegate is attached to the model. So trying to set the ListView's delegate will not have any effect. But the Combobox has its own delegate property that you can set.
ComboBox {
delegate: Text {
width: parent.width
height: 30
text: modelData
color: "#ffffff"
anchors.horizontalCenter: parent.horizontalCenter
}
}

Related

Qt5 QML: swap two Items in ListView

i'm using model view in qml.
ListView{
id: targetParameter
width: parent.width
height: parent.height
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 20
spacing: 10
orientation: ListView.Horizontal
interactive: false
model: proxyModelCharacterization
delegate: ParameterChangeTarget {
paramWidht: {
if(name === "NAME"){
targetParameter.width * 0.11
}else{
targetParameter.width * 0.42
}
}
paramHeight: targetParameter.height * 0.95
}
}
with this list view, I have three elements (for example Rectangle).
rect1, rect2, rect3.
i want to swap rect2 and rect3, but i can't change the order in which they are instantiated on my controller.
how can i swap two element on list view?
every kind of help or suggestion are greatly appreciated.
You can use the .move() method to move an element in a ListModel.
In the following example, the elements are added in the ListModel once, in the Component.onCompleted signal. You can then move them up or down (in the model and hence, the view) by clicking on the corresponding buttons. The buttons use the move() method of ListModel.
Example:
ListModel {
id: listModel
}
ListView {
id: listView
anchors.fill: parent
spacing: 10
model: listModel
delegate: Rectangle {
height: btnMoveUp.height
width: listView.width
color: "red"
Button {
id: btnMoveUp
anchors.left: parent.left
text: "Move Up"
enabled: index !== 0
onClicked: listModel.move(index, index-1,1)
}
Button {
id: btnMoveDown
anchors.left: btnMoveUp.right
text: "Move Down"
enabled: index !== listModel.count - 1
onClicked: listModel.move(index, index+1,1)
}
Text {
anchors {
left: btnMoveDown.right
right: parent.right
verticalCenter: parent.verticalCenter
}
text: model.name
}
}
}
Component.onCompleted: {
listModel.append({name: "Item 1"});
listModel.append({name: "Item 2"});
listModel.append({name: "Item 3"});
listModel.append({name: "Item 4"});
listModel.append({name: "Item 5"});
}

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
}
}
}
}
}
}

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!

Qt, QML ListView and Desktop App

My question is kind of a two part conditional question. I have a desktop application I'm writing in C++/Qt. In the app I have a couple lists that I want to decorate and add list items with icons and rich text.
I first attempted to do this with the QWidget world but the more I looked into it, the more I thought QML might be a better option. But now I'm wondering about that as well since it seems that QML Is more geared toward touch screen devices. Not to mention that my progress with QML has been frusating. Give them QML below, I cannot figure out how to: (1) get an item to highlight when I click it and (2) add a scroll bar:
import QtQuick 1.0
Item
{
width: 300
height: 200
ListModel
{
id: myModel2
ListElement { text: "List Item 1" }
ListElement { text: "List Item 2" }
ListElement { text: "List Item 3" }
ListElement { text: "List Item 4" }
ListElement { text: "List Item 5" }
ListElement { text: "List Item 6" }
}
Component
{
id: beerDelegate
Rectangle
{
id: beerDelegateRectangle
height: beerDelegateText.height * 1.5
width: parent.width
Text
{
id: beerDelegateText
text: "<b>" + modelData + "</b> <i>(" + modelData + ")</i>"
}
MouseArea
{
anchors.fill: parent
onClicked:
{
console.log("clicked: " + modelData + " at index: " + index);
beerList.currentIndex = index;
}
}
}
}
ListView
{
id: beerList
anchors.fill: parent
model: myModel2
delegate: beerDelegate
highlightFollowsCurrentItem: true
highlight: Rectangle
{
width: parent.width
color: "red"
}
focus: true
}
}
How can I accomplish what I'm looking for given this QML? Or is using QML in a QWidget desktop app just a bad idea all around?
For the first question (highlight):
Your list actually draws the highlight, however, your item delegate overpaints this with a white rectangle! Just replace the rectangle with an item and it works:
Component
{
id: beerDelegate
Item
{
...
}
}
For the second question (scroll bars):
As far as I know, QML doesn't provide scroll bars out of the box. There is however the Qt Desktop Components project (git repository) which gives you access to most of the widgets in the QML world. Among them, there is a ScrollArea.
It is no longer necessary to implement the Scrollbars yourself. There is the ScrollView-Item since Qt 5.1. Simply surround a Flickable-Item (e.g. the ListView-Item you use, is also "Flickable") with the ScrollView-Item and you'll be fine:
ScrollView {
ListView {
id: beerList
anchors.fill: parent
model: myModel2
delegate: beerDelegate
highlightFollowsCurrentItem: true
highlight: Rectangle
{
width: parent.width
color: "red"
}
focus: true
}
}
For the second question. i.e Scroll-bar on ListView:
I have created code for scroll bar on ListView. It also can work on the GridView
ScrollBar.qml
import Qt 4.7
Item {
property variant target
width: 8
anchors.top: target.top
anchors.bottom: target.bottom
anchors.margins: 1
anchors.rightMargin: 2
anchors.bottomMargin: 2
anchors.right: target.right
visible: (track.height == slider.height) ? false : true
Image {
id: scrollPath
width: 2
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
source: "qrc:/resources/buttons/slider2.png"
}
Item {
anchors.fill: parent
Timer {
property int scrollAmount
id: timer
repeat: true
interval: 20
onTriggered: {
target.contentY = Math.max(0, Math.min(target.contentY + scrollAmount,
target.contentHeight - target.height));
}
}
Item {
id: track
anchors.top: parent.top
anchors.topMargin: 1
anchors.bottom: parent.bottom
width: parent.width
MouseArea {
anchors.fill: parent
onPressed: {
timer.scrollAmount = target.height * (mouseY < slider.y ? -1 : 1)
timer.running = true;
}
onReleased: {
timer.running = false;
}
}
Image {
id:slider
anchors.horizontalCenter: parent.horizontalCenter
source: "qrc:/resources/buttons/slider.png"
width: parent.width
height: Math.min(target.height / target.contentHeight * track.height, track.height) < 20 ? 20 : Math.min(target.height / target.contentHeight * track.height, track.height)
y: target.visibleArea.yPosition * track.height
MouseArea {
anchors.fill: parent
drag.target: parent
drag.axis: Drag.YAxis
drag.minimumY: 0
drag.maximumY: track.height - height
onPositionChanged: {
if (pressedButtons == Qt.LeftButton) {
target.contentY = slider.y * target.contentHeight / track.height;
}
}
}
}
}
}
}
And I used scroll bar item with ListView in MyListView.qml as:
MyListView.qml
ListView {
id: list
clip: true
anchors.margins: 5
anchors.fill: parent
model: 10
delegate: trackRowDelegate
interactive: contentHeight > height
}
ScrollBar {
id: verticalScrollBar
target: list
clip: true
}
This ScrollBar item can be used with GridView as
GridView {
id: grid
clip: true
anchors.margins: 5
anchors.fill: parent
cellWidth:100
cellHeight: 100
model: items
interactive: contentHeight > height
snapMode: GridView.SnapToRow
delegate: myDelegate
}
ScrollBar {
id: verticalScrollBar
target: grid
clip: true
visible: grid.interactive
}

QML Listview selected item highlight on click

Hi I want to put this code :
highlight: Rectangle {
color: "black"
radius: 5
opacity: 0.7
focus: true
}
into mouseArea in onclick handler:
MouseArea {
id: mouse_area1
z: 1
hoverEnabled: false
anchors.fill: parent
onClicked: {
}
This is all listView:
ListView {
id: listview1
x: 0
y: 82
// width: 574
// height: 967
width: window.width
height: window.height
visible: true
keyNavigationWraps: false
boundsBehavior: Flickable.DragAndOvershootBounds
opacity: 1
maximumFlickVelocity: 2500
anchors.leftMargin: 0
highlightMoveSpeed: 489
contentWidth: 0
preferredHighlightEnd: 2
spacing: 5
highlightRangeMode: ListView.NoHighlightRange
snapMode: ListView.SnapToItem
anchors.bottomMargin: 0
anchors.rightMargin: 0
anchors.topMargin: 82
anchors.fill: parent
model: myModel
delegate:Component {
//id: contactDelegate
Item {
property variant myData: model
width: 574; height: 90
Column {
x: 12
y: 0
width: 562
height: 90
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 12
anchors.topMargin: 0
anchors.fill: parent
spacing: 2
Text { text: '<b>ID: </b> ' + id_user ; verticalAlignment: Text.AlignTop; wrapMode: Text.NoWrap; horizontalAlignment: Text.AlignHCenter; color:"steelblue"; font.family: "Helvetica"; font.pointSize: 10 }
Text { text: '<b>Name: </b> ' + user_name; horizontalAlignment: Text.AlignHCenter; color:"steelblue"; font.family: "Helvetica"; font.pointSize: 10 }
Text { text: '<b>Lastname: </b> ' + user_lastname; horizontalAlignment: Text.AlignHCenter; color:"steelblue"; font.family: "Helvetica"; font.pointSize: 10 }
Text { height: 16; text: '<b>Tel number: </b> ' + user_number; verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignHCenter; color:"steelblue"; font.family: "Helvetica"; font.pointSize: 10 }
Text { text: '<b>Address: </b> ' + user address; horizontalAlignment: Text.AlignHCenter; color:"steelblue"; font.family: "Helvetica"; font.pointSize: 10 }
MouseArea {
id: mouse_area1
z: 1
hoverEnabled: false
anchors.fill: parent
onClicked:
Item
{
}
}
}
}
}
//delegate: contactDelegate
highlight: Rectangle
{
color:"black"
radius: 5
opacity: 0.7
focus: true
}
}
For now highlight is working only when using arrows, bbut this will be app for android so I need on touch that same effect, and SECOND question is how to read certain data from selected item in listview?
Inside I have like id,name,lastname,number and adress.
I want to put those values into text_input boxes.
Thank you
It appears you need two solutions to your question:
You want to be able to set the current item of the ListView when it's clicked
You want to be able to know when the current selection changes
The Qt5 documentation says this about ListView mouse and touch handling:
The views handle dragging and flicking of their content, however they do not handle touch interaction with the individual delegates. In order for the delegates to react to touch input, e.g. to set the currentIndex, a MouseArea with the appropriate touch handling logic must be provided by the delegate.
Key input will work out-of-the-box but you'll need to explicitly catch the mouse/touch event on the delegate, and change the ListView.currentIndex value based on the index value of the selected delegate item.
Here's a full example:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
width: 640
height: 480
visible: true
ListModel {
id: model
ListElement {
name:'abc'
number:'123'
}
ListElement {
name:'efg'
number:'456'
}
ListElement {
name:'xyz'
number:'789'
}
}
ListView {
id: list
anchors.fill: parent
model: model
delegate: Component {
Item {
width: parent.width
height: 40
Column {
Text { text: 'Name:' + name }
Text { text: 'Number:' + number }
}
MouseArea {
anchors.fill: parent
onClicked: list.currentIndex = index
}
}
}
highlight: Rectangle {
color: 'grey'
Text {
anchors.centerIn: parent
text: 'Hello ' + model.get(list.currentIndex).name
color: 'white'
}
}
focus: true
onCurrentItemChanged: console.log(model.get(list.currentIndex).name + ' selected')
}
}
It does the following things:
creates a simple list and model
uses a MouseArea item within the item delegate to update set the list.currentIndex = index which is a local var and unique to the selected item
listens for the onCurrentItemChanged event of the ListView to show how to access the current model item values
binds the text value of the currently selected item to the highlight item to show using the currently selected values elsewhere
Answer provided by denoth: You need to add this line:
listview1.currentIndex = index
ListView provides so called "attached properties", i.e. properties available in the delegate for the list. Among them Listview.view is a reference to the list itself. It can be used to access currentIndex property and update it. Hence, to solve your issue just:
Uncomment //id: contactDelegate.
Set contactDelegate.ListView.view.currentIndex = index in the
OnClick even handler.
For those who use highlighting on a ListView with a specific height (being: not 100% height filled):
Be sure to enable the clip property of the ListView, as else the highlight will still be visible outside the ListView's borders while scrolling.
ListView
{
clip: true
}
As discussed here:
Hide the highlight of a ListView while scrolling
Simplest than ever, you can use: onCurrentItemChanged
ListView{
id: listViewMainMenu
signal Myselect(int playmode)
onCurrentItemChanged: {
Myselect(listViewMainMenu.currentIndex)
console.log("index changed see this " + currentIndex)
}
// ...
}
// do not forget to connect to this signal
otheritem.connect(thisitem.Myselect) //used in drag and works also with pathview
The answer is indeed listView.currentIndex = index.
Whilst playing around with this answer, I found that the ListView may not have keyboard focus, so, I found it may be necessary to call listView.forceActiveFocus() so that up and down arrow key presses are handled.
I found the delegate, particularly the usage of Text in the ListView delegate to be verbose and cumbersome. To clean that up, I refactored an AppInfo component for rendering the contact in a nice manner.
To polish the answer, I provided some sample data for the contacts ListModel and cleaned up the highlight mechanism:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
ListView {
id: listView
anchors.fill: parent
model: contacts
clip: true
focus: true
delegate: Item {
width: frame.width
height: frame.height
Frame {
id: frame
background: Item { }
ColumnLayout {
id: columnLayout
AppInfo { label: "ID"; value: id_user }
AppInfo { label: "Name"; value: user_name }
AppInfo { label: "Last Name"; value: user_lastname }
AppInfo { label: "Tel number"; value: user_number }
AppInfo { label: "Address"; value: user_address }
}
}
MouseArea {
anchors.fill: parent
onClicked: {
listView.currentIndex = index;
listView.forceActiveFocus();
}
}
}
highlight: Rectangle {
border.color: "black"
radius: 5
opacity: 0.7
focus: true
}
}
ListModel {
id: contacts
ListElement {
id_user: "bgates"
user_name: "Bill"
user_lastname: "Gates"
user_number: "555-Microsoft"
user_address: "1 Microsoft Way"
}
ListElement {
id_user: "sjobs"
user_name: "Steve"
user_lastname: "Jobs"
user_number: "555-Apple"
user_address: "1 Apple St"
}
ListElement {
id_user: "jbezos"
user_name: "Jeff"
user_lastname: "Bezos"
user_number: "555-Amazon"
user_address: "1 Amazon Ave"
}
}
}
//AppInfo.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
RowLayout {
property string label: "ID"
property string value: "id"
Text {
Layout.preferredWidth: 100
text: label
verticalAlignment: Text.AlignTop
wrapMode: Text.NoWrap
horizontalAlignment: Text.AlignRight
color: "steelblue"
font.family: "Helvetica"
font.pointSize: 10
font.bold: true
}
Text {
Layout.preferredWidth: 100
text: value
verticalAlignment: Text.AlignTop
wrapMode: Text.NoWrap
horizontalAlignment: Text.AlignLeft
font.family: "Helvetica"
font.pointSize: 10
}
}
You can Try it Online!
There's ItemDelegate since Qt 5.7. It reacts to mouse clicks by default.
import QtQuick
import QtQuick.Controls
ListView {
model: ListModel {
ListElement {
name: "Item 1"
}
ListElement {
name: "Item 2"
}
ListElement {
name: "Item 3"
}
}
delegate: ItemDelegate {
text: name
}
}

Resources