Reputation: 630
I have QComboBox
in my Qt application and i want it to have few default, non-editable items, but i also want it to have one editable item, which has default text, but is replaced once edited and some confirmation button is pressed (enter, by default).
Here's what i've tried:
QComboBox* combo_1 = new QComboBox();
combo_1->setEditable(true);
combo_1->addItems(QStringList()<<"Option_1."<<"Option_2."<<"Option_3."<<"Option_4."<<"Other...");
This way all the items are editable and once i edit any item and press enter, it stays the same, but new item with edited text is inserted to the box.
How can i achieve the behaviour i want? Any help is appreciated!
P.s. i indicated my goal as to have only one editable item, but i'd also like to know how to insert endless(conditionally) amount of new items the same way.
Upvotes: 0
Views: 1459
Reputation: 1122
Solution is using model-view pattern for combobox and subclassing QComboBox
.
1: Implement custom model. In my case I have editable item in row = 2 (QString m_strEditableValue
) and fixed items in 0,1 rows.
class MyModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit MyModel(QObject *parent = nullptr);
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
Qt::ItemFlags flags(const QModelIndex &index) const;
private:
QString m_strEditableValue;
};
MyModel::MyModel(QObject *parent) : QAbstractItemModel(parent)
{
m_strEditableValue = "default value";
}
QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const
{
return createIndex(row, column);
}
QModelIndex MyModel::parent(const QModelIndex &child) const
{
return QModelIndex();
}
int MyModel::rowCount(const QModelIndex &parent) const
{
return 3;
}
int MyModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
if (index.row() == 0) {
return tr("First fixed value");
}
if (index.row() == 1) {
return tr("Second fixed value");
}
if (index.row() == 2) {
return m_strEditableValue;
}
}
return QVariant();
}
bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role == Qt::EditRole) {
if (index.row() == 2) {
m_strEditableValue = value.toString();
return true;
}
}
return false;
}
Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if (index.row() == 2) {
//mark editable only for row 2
f = f | Qt::ItemIsEditable;
}
return f;
}
2: Subclass QComboBox
to change standard behavior
class MyCombobox : public QComboBox
{
Q_OBJECT
public:
explicit MyCombobox(QWidget *parent = nullptr);
private slots:
void OnEditTextChanged(const QString& text);
void OnCurrentIndexChanged(int index);
public slots:
};
MyCombobox::MyCombobox(QWidget *parent) : QComboBox(parent)
{
connect(this, &QComboBox::editTextChanged, this, &MyCombobox::OnEditTextChanged);
connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MyCombobox::OnCurrentIndexChanged);
}
void MyCombobox::OnEditTextChanged(const QString &text)
{
if (model()) {
//set data to model immediately
model()->setData(model()->index(currentIndex(), 0), text);
}
}
void MyCombobox::OnCurrentIndexChanged(int index)
{
if (model())
{
//disable editing if model disable it
Qt::ItemFlags flags = model()->flags(model()->index(index, 0));
if (flags & Qt::ItemIsEditable) {
lineEdit()->setReadOnly(false);
} else {
lineEdit()->setReadOnly(true);
}
}
}
3: Using
MyModel *cbModel = new MyModel(this);
ui->cbEditable->setModel(cbModel);
Upvotes: 1