Написал небольшой виджет (QPopup) на основе QListView с использованием QItemDelegate для самостоятельной отрисовки. Хотелось бы, чтобы ширина виджета зависела от ширины элементов. Однако sizeHint делегата на виджет никак не влияет. Есть ли выход?
C++ (Qt)
struct QCountry
{
QCountry (const QString & n, const QPixmap & i) : name (n), image (i), selected (false) {};
QCountry() {}; // QVariant (metaType) compatible
QCountry (const QString & n) : name (n) {}; // implicit ctor - convenient for indexOf
bool operator == (const QCountry & c) const // indexOf comparator
{ return name.compare (c.name, Qt::CaseInsensitive) == 0;
}
QString name;
QPixmap image;
bool selected;
};
typedef QList <QCountry> QCountryList;
class QPopup : public QListView
{
class QDelegate : public QItemDelegate
{
public:
QDelegate (QObject * parent) : QItemDelegate (parent) {};
void paint (QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const;
QSize sizeHint (const QStyleOptionViewItem &, const QModelIndex &) const;
const static int _margin = 4;
};
class QListModel : public QAbstractListModel
{
public:
QListModel (QObject * parent, QCountryList & list) : QAbstractListModel (parent),
_list (list) {};
int rowCount (const QModelIndex & parent = QModelIndex()) const {return _list.size();}
QVariant data (const QModelIndex & id, int /*role*/) const
{ return QVariant::fromValue (_list[id.row()]);
}
private:
QCountryList & _list;
};
public:
QPopup (QCountryView * parent, QCountryList & list);
private:
bool event (QEvent *);
QSize sizeHint() const {return QSize (140, 0);} // TEMP
};
...
QCountryView::QPopup::QPopup (QCountryView * parent, QCountryList & list) : QListView (parent)
{
setWindowFlags (Qt::Popup);
setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Expanding);
setModel (new QListModel (this, list));
setItemDelegate (new QDelegate (this));
connect (this,
SIGNAL (clicked (const QModelIndex &)),
parent,
SLOT (onPopupClick (const QModelIndex &)));
}
void QCountryView::QPopup::QDelegate::paint (QPainter * painter,
const QStyleOptionViewItem & option,
const QModelIndex & index) const
{
if(index.data().canConvert <QCountry> ())
{ QCountry country = index.data().value <QCountry> ();
QRect rect = option.rect;
QPoint imgPos = rect.topLeft() + QPoint (_margin, (rect.height() - country.image.height())/2);
painter->drawPixmap (imgPos, country.image);
rect.setLeft (rect.left() + country.image.width() + _margin * 2);
painter->setPen (country.selected ? Qt::black : Qt::lightGray);
painter->drawText (rect.adjusted (0, 1, 0, 0), Qt::AlignLeft, country.name);
}
}
QSize QCountryView::QPopup::QDelegate::sizeHint (const QStyleOptionViewItem & option,
const QModelIndex & index) const
{
if(index.data().canConvert <QCountry> ())
{ QCountry country = index.data().value <QCountry> ();
QFontMetrics fm (option.font);
int height = country.image.height() + 5;
int width = fm.width (country.name) + country.image.width() + _margin * 3;
return QSize (width, height);
}
return QSize();
}