Git commit 9948ebd5c3790617be86fa5679c17f9985ca5418 by Robby Stephenson. Committed on 18/03/2024 at 01:31. Pushed by rstephenson into branch 'master'.
Add gui class for multi-value Choice fields Taken from the QGIS project at https://api.qgis.org/api/qgscheckablecombobox_8h_source.html BUG:483831 FIXED-IN:4.0 M +4 -0 ChangeLog M +2 -2 doc/details.docbook M +11 -6 src/collection.cpp M +13 -9 src/collectionfieldsdialog.cpp M +3 -4 src/entry.cpp M +1 -0 src/gui/CMakeLists.txt A +241 -0 src/gui/checkablecombobox.cpp [License: GPL (v2/3)] A +104 -0 src/gui/checkablecombobox.h [License: GPL (v2/3)] M +79 -28 src/gui/choicefieldwidget.cpp M +9 -0 src/gui/choicefieldwidget.h https://invent.kde.org/office/tellico/-/commit/9948ebd5c3790617be86fa5679c17f9985ca5418 diff --git a/ChangeLog b/ChangeLog index 3605776a0..6c78614f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-03-17 Robby Stephenson <ro...@periapsis.org> + + * Updated Choice fields to allow multiple values (Bug 483831). + 2024-03-16 Robby Stephenson <ro...@periapsis.org> * Updated FilmAffinity data source. diff --git a/doc/details.docbook b/doc/details.docbook index 772174715..5774185dc 100644 --- a/doc/details.docbook +++ b/doc/details.docbook @@ -82,8 +82,8 @@ summaries, or reviews should be entered using this field type.</para> <para> When a field should be limited to a few preset values, a <emphasis>Choice</emphasis> type is used. The acceptable values are -presented in a drop down box for selection. Obviously, multiple values -are not applicable. Fields such as bibliography type or personal rating +presented in a drop down box for selection. Multiple values can be allowed. +Fields such as bibliography type or personal rating are <emphasis>Choice</emphasis>-type fields.</para> <para>Semi-colons should be used to separated the allowed values.</para> diff --git a/src/collection.cpp b/src/collection.cpp index 5095d2b32..7e7fb1f5a 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -637,19 +637,24 @@ bool Collection::isAllowed(const QString& field_, const QString& value_) const { return true; } - // find the field with a name of 'key_' FieldPtr field = fieldByName(field_); - - // if the type is not multiple choice or if value_ is allowed, return true - if(field && (field->type() != Field::Choice || field->allowed().contains(value_))) { + if(!field) return false; + // non-choice or single-value choice fields with alowed values + if(field->type() != Field::Choice || + (!field->hasFlag(Field::AllowMultiple) && field->allowed().contains(value_))) { return true; } - return false; + // now have to split the text into separate values and check each one + const auto values = FieldFormat::splitValue(value_, FieldFormat::StringSplit); + for(const auto& value : values) { + if(!field->allowed().contains(value)) return false; + } + + return true; } Tellico::Data::EntryGroupDict* Collection::entryGroupDictByName(const QString& name_) { -// myDebug() << name_; m_lastGroupField = name_; // keep track, even if it's invalid if(name_.isEmpty() || !m_entryGroupDicts.contains(name_) || m_entries.isEmpty()) { return nullptr; diff --git a/src/collectionfieldsdialog.cpp b/src/collectionfieldsdialog.cpp index 6b5f479e3..d375c29ed 100644 --- a/src/collectionfieldsdialog.cpp +++ b/src/collectionfieldsdialog.cpp @@ -575,25 +575,29 @@ void CollectionFieldsDialog::slotTypeChanged(const QString& type_) { m_allowEdit->setEnabled(type == Data::Field::Choice); // paragraphs, tables, and images are their own category - bool isCategory = (type == Data::Field::Para || type == Data::Field::Table || + bool isCategory = (type == Data::Field::Para || + type == Data::Field::Table || type == Data::Field::Image); m_catCombo->setEnabled(!isCategory); // formatting is only applicable when the type is simple text or a table - bool isText = (type == Data::Field::Line || type == Data::Field::Table); // formatNone is the default - m_formatCombo->setEnabled(isText); + m_formatCombo->setEnabled(type == Data::Field::Line || + type == Data::Field::Table); - // multiple is only applicable for simple text and number - isText = (type == Data::Field::Line || type == Data::Field::Number); - m_multiple->setEnabled(isText); + // multiple is only applicable for simple text, number, and choice + m_multiple->setEnabled(type == Data::Field::Line || + type == Data::Field::Number || + type == Data::Field::Choice); // completion is only applicable for simple text, number, and URL - isText = (isText || type == Data::Field::URL); - m_complete->setEnabled(isText); + m_complete->setEnabled(type == Data::Field::Line || + type == Data::Field::Number || + type == Data::Field::URL); // grouping is not possible with paragraphs or images - m_grouped->setEnabled(type != Data::Field::Para && type != Data::Field::Image); + m_grouped->setEnabled(type != Data::Field::Para && + type != Data::Field::Image); } void CollectionFieldsDialog::slotHighlightedChanged(int index_) { diff --git a/src/entry.cpp b/src/entry.cpp index f7af90e09..583949f08 100644 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -241,15 +241,14 @@ bool Entry::setFieldImpl(const QString& name_, const QString& value_) { return false; } - // the string store is probable only useful for fields with auto-completion or choice/number/bool + // the string store is probably only useful for fields with auto-completion or choice/number/bool bool shareType = f->type() == Field::Choice || f->type() == Field::Bool || f->type() == Field::Image || f->type() == Field::Rating || f->type() == Field::Number; - if(!(f->hasFlag(Field::AllowMultiple)) && - (shareType || - (f->type() == Field::Line && (f->flags() & Field::AllowCompletion)))) { + if(!f->hasFlag(Field::AllowMultiple) && + (shareType || (f->type() == Field::Line && f->hasFlag(Field::AllowCompletion)))) { m_fieldValues.insert(Tellico::shareString(name_), Tellico::shareString(value_)); } else { m_fieldValues.insert(Tellico::shareString(name_), value_); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index b19cbbaa5..0157293a5 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -2,6 +2,7 @@ SET(gui_STAT_SRCS boolfieldwidget.cpp + checkablecombobox.cpp choicefieldwidget.cpp collectiontemplatedialog.cpp collectiontypecombo.cpp diff --git a/src/gui/checkablecombobox.cpp b/src/gui/checkablecombobox.cpp new file mode 100644 index 000000000..cf04f3d03 --- /dev/null +++ b/src/gui/checkablecombobox.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + Copyright (C) 2024 Robby Stephenson <ro...@periapsis.org> + Adapted from code (C) 2017 by Alexander Bruy + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + * * + ***************************************************************************/ + +#include "checkablecombobox.h" + +#include <QEvent> +#include <QMouseEvent> +#include <QLineEdit> +#include <QPoint> +#include <QAbstractItemView> + +using Tellico::GUI::CheckableItemModel; +using Tellico::GUI::CheckBoxDelegate; +using Tellico::GUI::CheckableComboBox; + +CheckableItemModel::CheckableItemModel(QObject* parent_) + : QStandardItemModel(0, 1, parent_) { +} + +Qt::ItemFlags CheckableItemModel::flags(const QModelIndex& index_) const { + return index_.row() > 0 ? QStandardItemModel::flags(index_) | Qt::ItemIsUserCheckable : + QStandardItemModel::flags(index_); +} + +QVariant CheckableItemModel::data(const QModelIndex& index_, int role_) const { + QVariant value = QStandardItemModel::data(index_, role_); + if(index_.isValid() && role_ == Qt::CheckStateRole && !value.isValid()) { + value = Qt::Unchecked; + } + return value; +} + +bool CheckableItemModel::setData(const QModelIndex& index_, const QVariant& value_, int role_) { + const bool ok = QStandardItemModel::setData(index_, value_, role_); + if(ok && role_ == Qt::CheckStateRole) { + emit itemCheckStateChanged(index_); + } + emit dataChanged(index_, index_); + return ok; +} + +/**************************************************************/ + +CheckBoxDelegate::CheckBoxDelegate(QObject* parent_) + : QStyledItemDelegate(parent_) { +} + +void CheckBoxDelegate::paint(QPainter* painter_, const QStyleOptionViewItem& option_, const QModelIndex& index_) const { + QStyleOptionViewItem& nonConstOpt = const_cast<QStyleOptionViewItem &>(option_); + nonConstOpt.showDecorationSelected = false; + QStyledItemDelegate::paint(painter_, nonConstOpt, index_); +} + +/**************************************************************/ + +CheckableComboBox::CheckableComboBox(QWidget* parent_) + : QComboBox(parent_) + , m_separator(QStringLiteral(", ")) { + setModel(new CheckableItemModel(this)); + setItemDelegate(new CheckBoxDelegate(this)); + + auto lineEdit = new QLineEdit(this); + lineEdit->setReadOnly(true); + setLineEdit(lineEdit); + lineEdit->installEventFilter(this); + view()->viewport()->installEventFilter(this); + + connect(model(), &QStandardItemModel::rowsInserted, this, [=](const QModelIndex&, int, int) { updateDisplayText(); }); + connect(model(), &QStandardItemModel::rowsRemoved, this, [=](const QModelIndex&, int, int) { updateDisplayText(); }); + connect(model(), &QStandardItemModel::dataChanged, this, [=](const QModelIndex&, const QModelIndex&, const QVector<int> &) { updateDisplayText(); }); + + connect(this, &QComboBox::editTextChanged, + this, &CheckableComboBox::setCheckedDataText); +} + +QString CheckableComboBox::separator() const { + return m_separator; +} + +void CheckableComboBox::setSeparator(const QString& separator_) { + if(m_separator != separator_) { + m_separator = separator_; + updateDisplayText(); + } +} + +QStringList CheckableComboBox::checkedItems() const { + QStringList items; + if(auto* lModel = model()) { + const QModelIndex index = lModel->index(0, modelColumn(), rootModelIndex()); + const QModelIndexList indexes = lModel->match(index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly); + for(const QModelIndex& index : indexes) { + items += index.data().toString(); + } + } + return items; +} + +QVariantList CheckableComboBox::checkedItemsData() const { + QVariantList data; + if(auto* lModel = model()) { + const QModelIndex index = lModel->index(0, modelColumn(), rootModelIndex()); + const QModelIndexList indexes = lModel->match(index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly); + for(const QModelIndex& index : indexes) { + data += index.data(Qt::UserRole).toString(); + } + } + return data; +} + +Qt::CheckState CheckableComboBox::itemCheckState(int index_) const { + return static_cast<Qt::CheckState>(itemData(index_, Qt::CheckStateRole).toInt()); +} + +void CheckableComboBox::setAllCheckState(Qt::CheckState checkState_) { + for(int i = 0; i < count(); i++) { + setItemData(i, checkState_, Qt::CheckStateRole); + } + updateCheckedItems(); +} + +void CheckableComboBox::hidePopup() { + if(!m_skipHide) { + QComboBox::hidePopup(); + } + m_skipHide = false; +} + +bool CheckableComboBox::eventFilter(QObject* obj_, QEvent* ev_) { + if(obj_ == lineEdit()) { + if(ev_->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent*>(ev_)->button() == Qt::LeftButton) { + m_skipHide = true; + showPopup(); + } + } else if((ev_->type() == QEvent::MouseButtonPress || ev_->type() == QEvent::MouseButtonRelease) + && obj_ == view()->viewport()) { + m_skipHide = true; + if(ev_->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent*>(ev_)->button() == Qt::RightButton) { + return true; + } + + if(ev_->type() == QEvent::MouseButtonRelease) { + const QModelIndex index = view()->indexAt(static_cast<QMouseEvent*>(ev_)->pos()); + if(index.isValid()) { + auto item = static_cast<CheckableItemModel*>(model())->itemFromIndex(index); + // uncheck the first item, if a different item was checked + // uncheckk all other items if the first item was checked + // remember the chech state hasn't been changed yet. Do this check first so that the state changed signal + // gets fired after all items are updated + blockSignals(true); + if(item->checkState() == Qt::Unchecked) { + if(index.row() == 0) { + for(int i = 1; i < count(); i++) { + setItemData(i, Qt::Unchecked, Qt::CheckStateRole); + } + } else { + setItemData(0, Qt::Unchecked, Qt::CheckStateRole); + } + } + item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked); + blockSignals(false); + updateCheckedItems(); + } + return true; + } + } + + return QComboBox::eventFilter(obj_, ev_); +} + +void CheckableComboBox::setCheckedData(const QStringList& values_) { + blockSignals(true); + setItemData(0, values_.isEmpty() ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + for(int i = 1; i < count(); i++) { + setItemData(i, values_.contains(itemData(i).toString()) ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + } + blockSignals(false); + updateCheckedItems(); +} + +// have to update the checked item state after setting the custom text +void CheckableComboBox::setCheckedDataText(const QString& text_) { + blockSignals(true); + const auto values = text_.split(m_separator); + setItemData(0, text_.isEmpty() ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + for(int i = 1; i < count(); i++) { + const bool hasValue = values.contains(itemData(i).toString()); + setItemData(i, hasValue ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + } + blockSignals(false); + updateCheckedItems(); +} + +void CheckableComboBox::resizeEvent(QResizeEvent* ev_) { + QComboBox::resizeEvent(ev_); + updateDisplayText(); +} + +void CheckableComboBox::updateCheckedItems() { + const QStringList items = checkedItems(); + updateDisplayText(); + emit checkedItemsChanged(items); +} + +void CheckableComboBox::updateDisplayText() { + // There is only a line edit if the combobox is in editable state + if(!lineEdit()) return; + + const QString oldText = lineEdit()->text(); + QString text; + QStringList items = checkedItems(); + items.removeOne(QString()); // skip empty string + if(!items.isEmpty()) { + text = items.join(m_separator); + } + if(oldText != text) { + setEditText(text); + } +} diff --git a/src/gui/checkablecombobox.h b/src/gui/checkablecombobox.h new file mode 100644 index 000000000..908c9246b --- /dev/null +++ b/src/gui/checkablecombobox.h @@ -0,0 +1,104 @@ +/*************************************************************************** + Copyright (C) 2024 Robby Stephenson <ro...@periapsis.org> + Adapted from code (C) 2017 by Alexander Bruy + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + * * + ***************************************************************************/ + +#ifndef TELLICO_GUI_CHECKABLECOMBOBOX_H +#define TELLICO_GUI_CHECKABLECOMBOBOX_H + +#include <QComboBox> +#include <QMenu> +#include <QStandardItemModel> +#include <QStyledItemDelegate> + +class QEvent; + +namespace Tellico { + namespace GUI { + +class CheckableItemModel : public QStandardItemModel { +Q_OBJECT + +public: + CheckableItemModel(QObject* parent = nullptr); + + Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE; + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + bool setData(const QModelIndex& index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE; + +Q_SIGNALS: + void itemCheckStateChanged(const QModelIndex& index); +}; + +class CheckBoxDelegate : public QStyledItemDelegate { +Q_OBJECT + +public: + CheckBoxDelegate(QObject* parent = nullptr); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE; +}; + +class CheckableComboBox : public QComboBox { +Q_OBJECT + +public: + CheckableComboBox(QWidget* parent = nullptr); + + QString separator() const; + void setSeparator(const QString& separator); + + QStringList checkedItems() const; + QVariantList checkedItemsData() const; + + Qt::CheckState itemCheckState(int index) const; + void setAllCheckState(Qt::CheckState checkState); + + void hidePopup() override; + bool eventFilter(QObject* object, QEvent* event) Q_DECL_OVERRIDE; + +Q_SIGNALS: + void checkedItemsChanged(const QStringList& items); + +public Q_SLOTS: + void setCheckedDataText(const QString& text); + void setCheckedData(const QStringList& values); + +protected: + void resizeEvent(QResizeEvent* event) override; + +protected: + +private: + void updateCheckedItems(); + void updateDisplayText(); + + QString m_separator; + bool m_skipHide = false; +}; + + } // end namespace +} //end namespace + +#endif diff --git a/src/gui/choicefieldwidget.cpp b/src/gui/choicefieldwidget.cpp index 2efecd153..7cfcb434c 100644 --- a/src/gui/choicefieldwidget.cpp +++ b/src/gui/choicefieldwidget.cpp @@ -23,11 +23,12 @@ ***************************************************************************/ #include "choicefieldwidget.h" +#include "checkablecombobox.h" #include "../field.h" -#include <QComboBox> #include <QGuiApplication> #include <QScreen> +#include <QBoxLayout> namespace { const double MAX_FRACTION_SCREEN_WIDTH = 0.4; @@ -36,11 +37,31 @@ namespace { using Tellico::GUI::ChoiceFieldWidget; ChoiceFieldWidget::ChoiceFieldWidget(Tellico::Data::FieldPtr field_, QWidget* parent_) - : FieldWidget(field_, parent_), m_comboBox(nullptr) { + : FieldWidget(field_, parent_), m_comboBox(nullptr), m_checkableComboBox(nullptr) { + if(field_->hasFlag(Data::Field::AllowMultiple)) { + initCheckableComboBox(); + } else { + initComboBox(); + } + initCommon(field_); + + registerWidget(); +} + +void ChoiceFieldWidget::initComboBox() { m_comboBox = new QComboBox(this); void (QComboBox::* indexChanged)(int) = &QComboBox::currentIndexChanged; connect(m_comboBox, indexChanged, this, &ChoiceFieldWidget::checkModified); +} + +void ChoiceFieldWidget::initCheckableComboBox() { + m_checkableComboBox = new CheckableComboBox(this); + m_checkableComboBox->setSeparator(FieldFormat::delimiterString()); + connect(m_checkableComboBox, &CheckableComboBox::checkedItemsChanged, this, &ChoiceFieldWidget::checkModified); +} + +void ChoiceFieldWidget::initCommon(Tellico::Data::FieldPtr field_) { m_maxTextWidth = MAX_FRACTION_SCREEN_WIDTH * QGuiApplication::primaryScreen()->size().width(); QStringList values = field_->allowed(); @@ -48,54 +69,84 @@ ChoiceFieldWidget::ChoiceFieldWidget(Tellico::Data::FieldPtr field_, QWidget* pa values.removeAll(QString()); const QFontMetrics& fm = fontMetrics(); - m_comboBox->addItem(QString(), QString()); + auto cb = comboBox(); // everything here applies to either type of combobox; + cb->addItem(QString(), QString()); foreach(const QString& value, values) { - m_comboBox->addItem(fm.elidedText(value, Qt::ElideMiddle, m_maxTextWidth), value); + cb->addItem(fm.elidedText(value, Qt::ElideMiddle, m_maxTextWidth), value); } - - m_comboBox->setMinimumWidth(5*fm.maxWidth()); - registerWidget(); + cb->setMinimumWidth(5*fm.maxWidth()); } QString ChoiceFieldWidget::text() const { - return m_comboBox->currentData().toString(); + return isMultiSelect() ? comboBox()->currentText() + : comboBox()->currentData().toString(); } void ChoiceFieldWidget::setTextImpl(const QString& text_) { - int idx = m_comboBox->findData(text_); - if(idx < 0) { - m_comboBox->addItem(fontMetrics().elidedText(text_, Qt::ElideMiddle, m_maxTextWidth), text_); - m_comboBox->setCurrentIndex(m_comboBox->count()-1); + auto cb = comboBox(); + if(isMultiSelect()) { + // first uncheck all the boxes + m_checkableComboBox->setAllCheckState(Qt::Unchecked); + const auto values = FieldFormat::splitValue(text_, FieldFormat::StringSplit); + for(const auto& value : qAsConst(values)) { + int idx = cb->findData(value); + if(idx < 0) { + m_checkableComboBox->addItem(fontMetrics().elidedText(text_, Qt::ElideMiddle, m_maxTextWidth), text_); + idx = m_checkableComboBox->count()-1; + } + m_checkableComboBox->setItemData(idx, Qt::Checked, Qt::CheckStateRole); + } } else { - m_comboBox->setCurrentIndex(idx); + int idx = cb->findData(text_); + if(idx < 0) { + m_comboBox->addItem(fontMetrics().elidedText(text_, Qt::ElideMiddle, m_maxTextWidth), text_); + m_comboBox->setCurrentIndex(m_comboBox->count()-1); + } else { + m_comboBox->setCurrentIndex(idx); + } } } void ChoiceFieldWidget::clearImpl() { - m_comboBox->setCurrentIndex(0); // first item is empty + comboBox()->setCurrentIndex(0); // first item is empty editMultiple(false); } void ChoiceFieldWidget::updateFieldHook(Tellico::Data::FieldPtr, Tellico::Data::FieldPtr newField_) { - const QString oldValue = text(); - int idx = m_comboBox->currentIndex(); - m_comboBox->blockSignals(true); - m_comboBox->clear(); + bool wasMultiSelect = isMultiSelect(); + bool nowMultiSelect = newField_->hasFlag(Data::Field::AllowMultiple); - QStringList values = newField_->allowed(); - values.removeAll(QString()); + const QString oldValue = text(); - const QFontMetrics& fm = fontMetrics(); - // always have empty choice - m_comboBox->addItem(QString(), QString()); - foreach(const QString& value, values) { - m_comboBox->addItem(fm.elidedText(value, Qt::ElideMiddle, m_maxTextWidth), value); + const int widgetIndex = layout()->indexOf(widget()); + Q_ASSERT(widgetIndex > -1); + + if(wasMultiSelect && !nowMultiSelect) { + layout()->removeWidget(m_checkableComboBox); + delete m_checkableComboBox; + m_checkableComboBox = nullptr; + initComboBox(); + } else if(!wasMultiSelect && nowMultiSelect) { + layout()->removeWidget(m_comboBox); + delete m_comboBox; + m_comboBox = nullptr; + initCheckableComboBox(); + } else { + // remove existing values + comboBox()->clear(); } - m_comboBox->setCurrentIndex(idx); - m_comboBox->blockSignals(false); + initCommon(newField_); + + static_cast<QBoxLayout*>(layout())->insertWidget(widgetIndex, widget(), 1 /*stretch*/); + widget()->show(); + setTextImpl(oldValue); // set back to previous value } +QComboBox* ChoiceFieldWidget::comboBox() const { + return isMultiSelect() ? m_checkableComboBox : m_comboBox; +} + QWidget* ChoiceFieldWidget::widget() { - return m_comboBox; + return comboBox(); } diff --git a/src/gui/choicefieldwidget.h b/src/gui/choicefieldwidget.h index ac97973a5..e06071e7c 100644 --- a/src/gui/choicefieldwidget.h +++ b/src/gui/choicefieldwidget.h @@ -34,6 +34,8 @@ class FieldWidgetTest; namespace Tellico { namespace GUI { +class CheckableComboBox; + /** * @author Robby Stephenson */ @@ -57,7 +59,14 @@ protected: virtual void updateFieldHook(Data::FieldPtr oldField, Data::FieldPtr newField) Q_DECL_OVERRIDE; private: + QComboBox* comboBox() const; + bool isMultiSelect() const { return (m_checkableComboBox != nullptr); } + void initComboBox(); + void initCheckableComboBox(); + void initCommon(Data::FieldPtr field); + QComboBox* m_comboBox; + CheckableComboBox* m_checkableComboBox; int m_maxTextWidth; };