#include "ecombobox.h" #include "clineedit.h" #include "qabstractitemview.h" #include "qad_types.h" #include "qwidget.h" #include #include #include #include #include #include class EModel: public QStandardItemModel { public: EModel(QObject * parent = nullptr): QStandardItemModel(parent) { #if QT_VERSION < 0x050000 setSupportedDragActions(Qt::MoveAction); #endif } protected: virtual Qt::ItemFlags flags(const QModelIndex & index) const override { Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; if (!index.isValid()) f |= Qt::ItemIsDropEnabled; return f; } #if QT_VERSION >= 0x050000 Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; } Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; } #endif }; EComboBox::EComboBox(QWidget * parent) : QComboBox(parent) , iv(new QTreeView(this)) , header(new QWidget(this)) , icon(new QLabel(this)) , filter(new CLineEdit(this)) { setView(iv); setModel(new EModel()); iv->setTextElideMode(Qt::ElideMiddle); iv->setRootIsDecorated(false); iv->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); iv->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); iv->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); iv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); iv->setMinimumHeight(100); icon->setPixmap(QPixmap(":/icons/edit-find.png")); icon->setScaledContents(true); icon->setFixedSize(preferredIconSize(1.2, this)); ifont = nfont = font(); ifont.setItalic(true); #if QT_VERSION >= 0x040700 filter->setPlaceholderText(tr("Filter")); filter->setFont(ifont); #endif header->setAutoFillBackground(true); header->setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); header->layout()->setSpacing(2); header->layout()->setContentsMargins(2, 0, 0, 0); header->layout()->addWidget(icon); header->layout()->addWidget(filter); header->setParent(iv->header()); connect(filter, &CLineEdit::textChanged, this, [this](const QString & text) { EComboBox::filterChanged(text, false); }); connect(model(), &EModel::layoutChanged, this, &EComboBox::rowsChanged); connect(filter, &QLineEdit::returnPressed, this, [this]() { if (rows_visible != 1) return; setCurrentIndex(first_index.row()); }); } QSize EComboBox::sizeHint() const { QSize s = QComboBox::sizeHint(); s.setWidth(s.width() + 16); return s; } void EComboBox::showPopup() { iv->setDragDropMode(isEditable() ? QAbstractItemView::InternalMove : QAbstractItemView::NoDragDrop); filterChanged(filter->text(), true); QComboBox::showPopup(); QRect r = iv->header()->rect(); header->setGeometry(r.x(), r.y(), r.width(), r.height() - 1); filter->setFocus(); filter->selectAll(); } void EComboBox::changeEvent(QEvent * e) { QComboBox::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: #if QT_VERSION >= 0x040700 filter->setPlaceholderText(tr("Filter")); #endif break; default: break; } } void EComboBox::filterChanged(const QString & text, bool first) { if (filter->text().isEmpty()) { filter->setFont(ifont); } else { filter->setFont(nfont); } iv->hide(); QModelIndex pi = iv->rootIndex(); int row_count = iv->model()->rowCount(); rows_visible = 0; if (text.isEmpty()) { for (int i = 0; i < row_count; ++i) { iv->setRowHidden(i, pi, false); iv->model()->setData(iv->model()->index(i, 0), iv->model()->index(i, 0, pi).data().toString(), Qt::ToolTipRole); } iv->show(); if (first) return; hidePopup(); showPopup(); qApp->processEvents(); QRect r = iv->header()->rect(); header->setGeometry(r.x(), r.y(), r.width(), r.height() - 1); return; } for (int i = 0; i < row_count; ++i) { bool hidden = !iv->model()->index(i, 0, pi).data().toString().contains( #if QT_VERSION_MAJOR <= 5 QRegExp(text, Qt::CaseInsensitive) #else QRegularExpression(text, QRegularExpression::CaseInsensitiveOption) #endif ); if (!hidden) { if (rows_visible == 0) first_index = iv->model()->index(i, 0); ++rows_visible; } iv->setRowHidden(i, pi, hidden); iv->model()->setData(iv->model()->index(i, 0), iv->model()->index(i, 0, pi).data().toString(), Qt::ToolTipRole); } iv->show(); qApp->processEvents(); QRect r = iv->header()->rect(); header->setGeometry(r.x(), r.y(), r.width(), r.height() - 1); }