Showing posts with label Item delegate. Show all posts
Showing posts with label Item delegate. Show all posts

Friday, March 4, 2011

Creating custom ListView delegate in QML

Currently I am trying to port one of my application's view to QML. During porting activity I need to create custom ListView delegate. In any case it looks like in QML ListView dose not provide any default delegate so you need to create one.

So I wanted to put button on my every row of ListView and wanted to emit signal when that button is pressed.

Something like this.(This is final out put of my QML code)


So I began with simple ListView with basic delegate as shown in below code.

import Qt 4.7

Rectangle {
    width: 250
    height: 300

    Component {
        id: listDelegate
        Item {
        width: 250; height: 50
        
            Row {
             Column {
                 width: 200
                 Text { text: 'Name: ' + name }
                 Text { text: 'Cost:' + cost }
                }
            }
        }
    }

    ListModel {
             id: listModel

             ListElement {
                 name: "Apple"; cost: 2.45
             }
             ListElement {
                 name: "Banana"; cost: 1.95
             }
     }

    ListView {
          id: listView
          anchors.fill: parent; anchors.margins: 5
          model: listModel
          delegate: listDelegate
          focus: true
    }
}

Out put will look like below. In above code delegate is created to display one colum with two text element.



Now I need to add image in above item delegate which will act as button and will propogate click event with index of item being clicked.

To add image to item delegate I am adding another column to row element and adding image element in that, also handling various mouse event to simulate button.
Component {
             id: listDelegate

             Item {
                 width: 250; height: 50

                 Row {
                     Column {
                         width: 200
                         Text { text: 'Name: ' + name }
                         Text { text: 'Cost:' + cost }
                     }
                     Column {
                        width: 50
                        Image {
                          id: itemBtn
                          source: "btn1.png"
                          MouseArea {
                              anchors.fill: parent;
                              onEntered: {
                                  itemBtn.source= "btn2.png";
                              }

                              onExited: {
                                    itemBtn.source= "btn1.png";
                              }

                              onCanceled: {
                                    itemBtn.source= "btn1.png";
                              }

                              onClicked:{
                                  console.debug("clicked:"+ index);
                                  listView.currentIndex = index;
                              }
                          }
                       }
                    }
                }
             }
         }  

So it will look like below.

This is all. In case you want to add highlight to list then you can use following code. You need to add this code to Rectangle element of qml file. Please note that in above code, highlight only moves when you click button or you use keyboard arrow key.
Component {
          id: highlightBar
          Rectangle {
              width: 245; height: 40
              radius: 5
              color: "lightsteelblue"
              y: listView.currentItem.y;
              x: listView.currentItem.x-3;
              Behavior on y { PropertyAnimation {} }
          }
      }


And also you will need to change ListView element to set highlight like below.
ListView {
          id: listView
          anchors.fill: parent; anchors.margins: 5
          model: listModel
          delegate: listDelegate
          highlight: highlightBar
          highlightFollowsCurrentItem: false
          focus: true
    }

Thats all, hope this helps.

Friday, December 10, 2010

Creating custom QItemDelegate with QPushButton in Qt

Recently I was working on Audiobook Reader application for maemo. For this application I have created one custom item delegate for QListView which contains two clickable item and generate signal accordingly.

For application I was using some different mechanism to create button inside custom item delegate, but now I found better way to create custom item delegate with QPushButton. So thought to share code.

Following is code for custom item delegate derived from QItemDelegate.

Update: I have uploaded following code to gitorous, Please visit this link if you want a working sample code.

#include <QItemDelegate>
class CustomItemDelegate : public QItemDelegate
{
    Q_OBJECT
public:
    CustomItemDelegate(QObject *parent = 0);
    virtual void paint(QPainter *painter,
                       const QStyleOptionViewItem &option,
                       const QModelIndex &index) const ;

    virtual QSize sizeHint(const QStyleOptionViewItem &option,
                           const QModelIndex &index) const ;

    bool editorEvent(QEvent *event, QAbstractItemModel *model, 
                           const QStyleOptionViewItem &option, 
                           const QModelIndex &index);

signals:
    void buttonClicked(const QModelIndex &index);
private:
    QStyle::State  _state;
};

#include "customitemdelegate.h"
...

CustomItemDelegate::CustomItemDelegate(QObject *parent) :
    QItemDelegate(parent)
{
    _state =  QStyle::State_Enabled;
}

void CustomItemDelegate::paint(QPainter *painter,
                   const QStyleOptionViewItem &option,
                   const QModelIndex &index) const
{
   const QStandardItemModel* model = 
   static_cast<const QStandardItemModel*>(index.model());
   QStandardItem* item = model->item(index.row());

   QString text = item->text();
   QRect rect = option.rect;

    QRect textRect( rect);
    textRect.setHeight( 30);
    painter->drawText(textRect,text);

    QRect buttonRect( rect);
    buttonRect.setY(textRect.y()+ 35);
    buttonRect.setHeight( 30);
    QStyleOptionButton button;
    button.rect = buttonRect;
    button.text = text;
    button.state = _state | QStyle::State_Enabled;

    QApplication::style()->drawControl
        (QStyle::CE_PushButton, &button, painter);
}

QSize CustomItemDelegate::sizeHint(const QStyleOptionViewItem &/*option*/,
                       const QModelIndex &/*index*/) const
{
    //hard coding size for test purpose, 
    //actual size hint can be calculated from option param
    return QSize(800,70);
}

bool CustomItemDelegate::editorEvent(QEvent *event, 
    QAbstractItemModel *model, 
    const QStyleOptionViewItem &option, 
    const QModelIndex &index)
{
    if( event->type() == QEvent::MouseButtonPress ||
        event->type() == QEvent::MouseButtonRelease ) {
    } else {
         //ignoring other mouse event and reseting button's state
         _state = QStyle::State_Raised;
        return true;
    }

    QRect buttonRect( option.rect);
    buttonRect.setY(option.rect.y()+ 35);
    buttonRect.setHeight( 30);

    QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
    if( !buttonRect.contains( mouseEvent->pos()) ) {
        _state = QStyle::State_Raised;
        return true;
    }

    if( event->type() == QEvent::MouseButtonPress) {            
        _state = QStyle::State_Sunken;
    } else if( event->type() == QEvent::MouseButtonRelease) {
        _state = QStyle::State_Raised;
        emit buttonClicked( index);
    }    
    return true;
}
Basically in above code, I am calculating rect where I want to draw my button and drawing QPushButton on list item using QStyleOptionButton class.

And in editor event on mouse press and release event, I am checking if mouse position on click falls into my button's rect or not. If it falls inside my button's rect then I am emitting signal.

I am using item's signal as shown in below code.
CustomList::CustomList(QWidget *parent) :
    QWidget(parent),_view(0)
{
    _view = new QListView();
    _view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    _view->setEditTriggers(QAbstractItemView::NoEditTriggers);

    //creating custom item delegate and setting  it to view
    CustomItemDelegate* itemDelegate = new CustomItemDelegate(_view);
    _view->setItemDelegate( itemDelegate );

    _view->setModel(&_model); 
   
    //connecting delegate's signal to this class's slot
    connect(itemDelegate,SIGNAL(buttonClicked(QModelIndex)),
    this,SLOT(listButtonClicked(QModelIndex)));

    QHBoxLayout* mainLayout = new QHBoxLayout(this);
    mainLayout->addWidget( _view);

    //creating and adding data to model
    QStandardItem* item = new QStandardItem;
    item->setText("testing");

    QStandardItem* item1 = new QStandardItem;
    item1->setText("testing1");

    _model.appendRow(item);
    _model.appendRow(item1);
}

// following slot will be invoked when delegate's button is clicked
void CustomList::listButtonClicked(const QModelIndex &index)
{
    qDebug() << "######### listbutton clicked ######### " << index.row();
}
Following is snap of how custom item delegate looks.