how to draw a line between items of a QCompleter as a separator?

814 views Asked by At

I have a QCompleter and a QStringListModel that is set to QCompleter. Now how can I draw a line as separator between items of StringList that is set to QStringListModel. Finally, QCompleter will be set to a QLineEdit.

enter image description here

3

There are 3 answers

1
László Papp On

OK, so I am trying to provide more details than in my initial hint as I thought that would be enough. I fear that people would start asking further, so here goes it:

completerseparatordelegate.h

#include <QItemDelegate>
#include <QSize>
#include <QModelIndex>
#include <QStyleOptionViewItem>

class CompleterSeparatorDelegate : public QItemDelegate
{
    Q_OBJECT

public:
    CompleterSeparatorDelegate(QObject *parent = 0);
    ~CompleterSeparatorDelegate();

protected:    
    void paint(QPainter *painter, const QStyleOptionViewItem &option,
               const QModelIndex &index) const;
    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index) const;
};

completerseparatordelegate.cpp

#include "completerseparatordelegate.h"

#include <QPainter>
#include <QString>

CompleterSeparatorDelegate::CompleterSeparatorDelegate(QObject *parent)
    : QItemDelegate(parent)
{
}

CompleterSeparatorDelegate::~CompleterSeparatorDelegate()
{
}

void CompleterSeparatorDelegate::paint(QPainter *painter,
                          const QStyleOptionViewItem &option,
                          const QModelIndex &index) const
{
    if(index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator"))
    {
        painter->setPen(Qt::red);
        painter->drawLine(option.rect.left(), option.rect.center().y(), option.rect.right(), option.rect.center().y());
    } else {
        QItemDelegate::paint(painter, option, index);
    }
}

QSize CompleterSeparatorDelegate::sizeHint(const QStyleOptionViewItem &option,
                                           const QModelIndex &index) const
{
    QString type = index.data(Qt::AccessibleDescriptionRole).toString();
    if(type == QLatin1String("separator"))
        return QSize(0, 2);
    return QItemDelegate::sizeHint( option, index );
}

mainwindow.cpp

#include "completerseparatordelegate.h"

...
listView->setItemDelegate(new CompleterSeparatorDelegate);
completer->setPopup(listView);
...

I have not tried to compile this code as I am just writing it on the fly as raw text, but the main idea is basically coming from here with minor adaptation.

0
hassan deldar On

Create a custom model with new type and a custom delegate for your widget in delegate paint you can paint any thing in your new type and allow the default type to paint in parent... see this example for combobox

0
marcbf On

A bit late to the party, but here's a "semi-working" solution, which I've just implemented myself.

You need to change the internal item delegate of the completer, either by subclassing QCompleter or just calling the respective functions. This example uses subclassing.

MyCompleter::MyCompleter(QObject* parent) : QCompleter(parent)
{
  QAbstractItemDelegate* delegate = popup()->itemDelegate();
  popup()->setItemDelegate(new MyCompleterItemDelegate(popup()));
  delete delegate;
}

MyCompleterItemDelegate can be implemented like this:

MyCompleterItemDelegate::MyCompleterItemDelegate(QAbstractItemView* view)
 : QItemDelegate(view), m_view(view)
{
}

void MyCompleterItemDelegate::paint(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
  if (index.data(Qt::UserRole).toInt() == SEPARATOR) // this is where you check if the current index is a separator
  {
    QRect rect = option.rect;
    rect.setWidth(m_view->viewport()->width());
    QStyleOption opt;
    opt.rect = rect;
    m_view->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, p, m_view);
  }
  else
  {
    QStyleOptionViewItem optCopy = option;
    optCopy.showDecorationSelected = true;
    if (m_view->currentIndex() == index)
      optCopy.state |= QStyle::State_HasFocus;
    QItemDelegate::paint(p, optCopy, index);
  }
}

Note, that this doesn't solve the issue with showing the filtered items with separators. For that you would need to somehow alter the completion (proxy) model as returned by QCompleter::completionModel(). I haven't figured that one out yet. Also, you need to somehow tell the completer/delegate what a separator is. In my case I have a custom model. With a QStringList perhaps a certain trigger word would do the trick?

Hope this helps someone.