summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp45
-rw-r--r--src/corelib/itemmodels/qabstractitemmodel.cpp298
-rw-r--r--src/corelib/itemmodels/qabstractitemmodel.h110
-rw-r--r--src/gui/itemmodels/qstandarditemmodel.cpp39
-rw-r--r--src/gui/itemmodels/qstandarditemmodel.h2
-rw-r--r--src/widgets/itemviews/qstyleditemdelegate.cpp66
-rw-r--r--tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp141
7 files changed, 672 insertions, 29 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp
index 2e055ec439..397f6f72af 100644
--- a/src/corelib/doc/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp
@@ -141,3 +141,48 @@ private:
};
//! [12]
+//! [13]
+QVariant text = model->data(index, Qt::DisplayRole);
+QVariant decoration = model->data(index, Qt::DecorationRole);
+QVariant checkState = model->data(index, Qt::CheckStateRole);
+// etc.
+//! [13]
+
+//! [14]
+std::array<QModelRoleData, 3> roleData = { {
+ QModelRoleData(Qt::DisplayRole),
+ QModelRoleData(Qt::DecorationRole),
+ QModelRoleData(Qt::CheckStateRole)
+} };
+
+// Usually, this is not necessary: A QModelRoleDataSpan
+// will be built automatically for you when passing an array-like
+// container to multiData().
+QModelRoleDataSpan span(roleData);
+
+model->multiData(index, span);
+
+// Use roleData[0].data(), roleData[1].data(), etc.
+//! [14]
+
+//! [15]
+void MyModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
+{
+ for (QModelRoleData &roleData : roleDataSpan) {
+ int role = roleData.role();
+
+ // ... obtain the data for index and role ...
+
+ roleData.setData(result);
+ }
+}
+//! [15]
+
+//! [16]
+QVariant MyModel::data(const QModelIndex &index, int role) const
+{
+ QModelRoleData roleData(role);
+ multiData(index, roleData);
+ return roleData.data();
+}
+//! [16]
diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp
index a686e918c3..8314ea8958 100644
--- a/src/corelib/itemmodels/qabstractitemmodel.cpp
+++ b/src/corelib/itemmodels/qabstractitemmodel.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -91,6 +92,226 @@ void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data)
}
/*!
+ \class QModelRoleData
+ \inmodule QtCore
+ \since 6.0
+ \ingroup model-view
+ \brief The QModelRoleData class holds a role and the data associated to that role.
+
+ QModelRoleData objects store an item role (which is a value from the
+ Qt::ItemDataRole enumeration, or an arbitrary integer for a custom role)
+ as well as the data associated with that role.
+
+ A QModelRoleData object is typically created by views or delegates,
+ setting which role they want to fetch the data for. The object
+ is then passed to models (see QAbstractItemModel::multiData()),
+ which populate the data corresponding to the role stored. Finally,
+ the view visualizes the data retrieved from the model.
+
+ \sa {Model/View Programming}, QModelRoleDataSpan
+*/
+
+/*!
+ QModelRoleData::QModelRoleData(int role) noexcept
+
+ Constructs a QModelRoleData object for the given \a role.
+
+ \sa Qt::ItemDataRole
+*/
+
+/*!
+ int QModelRoleData::role() const noexcept
+
+ Returns the role held by this object.
+
+ \sa Qt::ItemDataRole
+*/
+
+/*!
+ const QVariant &QModelRoleData::data() const noexcept
+
+ Returns the data held by this object.
+
+ \sa setData()
+*/
+
+/*!
+ QVariant &QModelRoleData::data() noexcept
+
+ Returns the data held by this object as a modifiable reference.
+
+ \sa setData()
+*/
+
+/*!
+ template <typename T> void QModelRoleData::setData(T &&value)
+
+ Sets the data held by this object to \a value.
+ \a value must be of a datatype which can be stored in a QVariant.
+
+ \sa data(), clearData(), Q_DECLARE_METATYPE
+*/
+
+/*!
+ void QModelRoleData::clearData() noexcept
+
+ Clears the data held by this object. Note that the role is
+ unchanged; only the data is cleared.
+
+ \sa data()
+*/
+
+/*!
+ \class QModelRoleDataSpan
+ \inmodule QtCore
+ \since 6.0
+ \ingroup model-view
+ \brief The QModelRoleDataSpan class provides a span over QModelRoleData objects.
+
+ A QModelRoleDataSpan is used as an abstraction over an array of
+ QModelRoleData objects.
+
+ Like a view, QModelRoleDataSpan provides a small object (pointer
+ and size) that can be passed to functions that need to examine the
+ contents of the array. A QModelRoleDataSpan can be constructed from
+ any array-like sequence (plain arrays, QVector, std::vector,
+ QVarLengthArray, and so on). Moreover, it does not own the
+ sequence, which must therefore be kept alive longer than any
+ QModelRoleDataSpan objects referencing it.
+
+ Unlike a view, QModelRoleDataSpan is a span, so it allows for
+ modifications to the underlying elements.
+
+ QModelRoleDataSpan's main use case is making it possible
+ for a model to return the data corresponding to different roles
+ in one call.
+
+ In order to draw one element from a model, a view (through its
+ delegates) will generally request multiple roles for the same index
+ by calling \c{data()} as many times as needed:
+
+ \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 13
+
+ QModelRoleDataSpan allows a view to request the same data
+ using just one function call.
+
+ This is achieved by having the view prepare a suitable array of
+ QModelRoleData objects, each initialized with the role that should
+ be fetched. The array is then wrapped in a QModelRoleDataSpan
+ object, which is then passed to a model's \c{multiData()} function.
+
+ \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 14
+
+ Views are encouraged to store the array of QModelRoleData objects
+ (and, possibly, the corresponding span) and re-use it in subsequent
+ calls to the model. This allows to reduce the memory allocations
+ related with creating and returning QVariant objects.
+
+ Finally, given a QModelRoleDataSpan object, the model's
+ responsibility is to fill in the data corresponding to each role in
+ the span. How this is done depends on the concrete model class.
+ Here's a sketch of a possible implementation that iterates over the
+ span and uses \c{setData()} on each element:
+
+ \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 15
+
+ \sa {Model/View Programming}, QAbstractItemModel::multiData()
+*/
+
+/*!
+ QModelRoleDataSpan::QModelRoleDataSpan() noexcept
+
+ Constructs an empty QModelRoleDataSpan. Its data() will be set to
+ \nullptr, and its length to zero.
+*/
+
+/*!
+ QModelRoleDataSpan::QModelRoleDataSpan(QModelRoleData &modelRoleData) noexcept
+
+ Constructs an QModelRoleDataSpan spanning over \a modelRoleData,
+ seen as a 1-element array.
+*/
+
+/*!
+ QModelRoleDataSpan::QModelRoleDataSpan(QModelRoleData *modelRoleData, qsizetype len)
+
+ Constructs an QModelRoleDataSpan spanning over the array beginning
+ at \a modelRoleData and with length \a len.
+
+ \note The array must be kept alive as long as this object has not
+ been destructed.
+*/
+
+/*!
+ template <typename Container> QModelRoleDataSpan::QModelRoleDataSpan(Container &c) noexcept
+
+ Constructs an QModelRoleDataSpan spanning over the container \a c,
+ which can be any contiguous container of QModelRoleData objects.
+ For instance, it can be a \c{QVector<QModelRoleData>},
+ a \c{std::array<QModelRoleData, 10>} and so on.
+
+ \note The container must be kept alive as long as this object has not
+ been destructed.
+*/
+
+/*!
+ qsizetype QModelRoleDataSpan::size() const noexcept
+
+ Returns the length of the span represented by this object.
+*/
+
+/*!
+ qsizetype QModelRoleDataSpan::length() const noexcept
+
+ Returns the length of the span represented by this object.
+*/
+
+/*!
+ QModelRoleData *QModelRoleDataSpan::data() const noexcept
+
+ Returns a pointer to the beginning of the span represented by this
+ object.
+*/
+
+/*!
+ QModelRoleData *QModelRoleDataSpan::begin() const noexcept
+
+ Returns a pointer to the beginning of the span represented by this
+ object.
+*/
+
+/*!
+ QModelRoleData *QModelRoleDataSpan::end() const noexcept
+
+ Returns a pointer to the imaginary element one past the end of the
+ span represented by this object.
+*/
+
+/*!
+ QModelRoleData &QModelRoleDataSpan::operator[](qsizetype index) const
+
+ Returns a modifiable reference to the QModelRoleData at position
+ \a index in the span.
+
+ \note \a index must be a valid index for this span (0 <= \a index < size()).
+*/
+
+/*!
+ const QVariant *QModelRoleDataSpan::dataForRole(int role) const
+
+ Returns the data associated with the first QModelRoleData in the
+ span that has its role equal to \a role. If such a QModelRoleData
+ object does not exist, the behavior is undefined.
+
+ \note Avoid calling this function from the model's side, as a
+ model cannot possibly know in advance which roles are in a given
+ QModelRoleDataSpan. This function is instead suitable for views and
+ delegates, which have control over the roles in the span.
+
+ \sa QModelRoleData::data()
+*/
+
+/*!
\class QPersistentModelIndex
\inmodule QtCore
\ingroup shared
@@ -423,6 +644,20 @@ QVariant QPersistentModelIndex::data(int role) const
return QVariant();
}
+
+/*!
+ Populates the given \a roleDataSpan for the item referred to by the
+ index.
+
+ \since 6.0
+ \sa Qt::ItemDataRole, QAbstractItemModel::setData()
+*/
+void QPersistentModelIndex::multiData(QModelRoleDataSpan roleDataSpan) const
+{
+ if (d)
+ d->index.multiData(roleDataSpan);
+}
+
/*!
\since 4.2
@@ -1109,6 +1344,14 @@ void QAbstractItemModel::resetInternalData()
*/
/*!
+ \fn void QModelIndex::multiData(QModelRoleDataSpan roleDataSpan) const
+ \since 6.0
+
+ Populates the given \a roleDataSpan for the item referred to by the
+ index.
+*/
+
+/*!
\fn Qt::ItemFlags QModelIndex::flags() const
\since 4.2
@@ -3401,6 +3644,61 @@ bool QAbstractItemModel::checkIndex(const QModelIndex &index, CheckIndexOptions
}
/*!
+ \since 6.0
+
+ Fills the \a roleDataSpan with the requested data for the given \a index.
+
+ The default implementation will call simply data() for each role in
+ the span. A subclass can reimplement this function to provide data
+ to views more efficiently:
+
+ \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 15
+
+ In the snippet above, \c{index} is the same for the entire call.
+ This means that accessing to the necessary data structures in order
+ to retrieve the information for \c{index} can be done only once
+ (hoisting the relevant code out of the loop).
+
+ The usage of QModelRoleData::setData(), or similarly
+ QVariant::setValue(), is encouraged over constructing a QVariant
+ separately and using a plain assignment operator; this is
+ because the former allow to re-use the memory already allocated for
+ the QVariant object stored inside a QModelRoleData, while the latter
+ always allocates the new variant and then destroys the old one.
+
+ Note that views may call multiData() with spans that have been used
+ in previous calls, and therefore may already contain some data.
+ Therefore, it is imperative that if the model cannot return the
+ data for a given role, then it must clear the data in the
+ corresponding QModelRoleData object. This can be done by calling
+ QModelRoleData::clearData(), or similarly by setting a default
+ constructed QVariant, and so on. Failure to clear the data will
+ result in the view believing that the "old" data is meant to be
+ used for the corresponding role.
+
+ Finally, in order to avoid code duplication, a subclass may also
+ decide to reimplement data() in terms of multiData(), by supplying
+ a span of just one element:
+
+ \snippet code/src_corelib_kernel_qabstractitemmodel.cpp 16
+
+ \note Models are not allowed to modify the roles in the span, or
+ to rearrange the span elements. Doing so results in undefined
+ behavior.
+
+ \note It is illegal to pass an invalid model index to this function.
+
+ \sa QModelRoleDataSpan, data()
+*/
+void QAbstractItemModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
+{
+ Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid));
+
+ for (QModelRoleData &d : roleDataSpan)
+ d.setData(data(index, d.role()));
+}
+
+/*!
\class QAbstractTableModel
\inmodule QtCore
\brief The QAbstractTableModel class provides an abstract model that can be
diff --git a/src/corelib/itemmodels/qabstractitemmodel.h b/src/corelib/itemmodels/qabstractitemmodel.h
index 325ff1df4a..9cda91e66e 100644
--- a/src/corelib/itemmodels/qabstractitemmodel.h
+++ b/src/corelib/itemmodels/qabstractitemmodel.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -49,6 +50,108 @@ QT_REQUIRE_CONFIG(itemmodel);
QT_BEGIN_NAMESPACE
+class QModelRoleData
+{
+ int m_role;
+ QVariant m_data;
+
+public:
+ explicit QModelRoleData(int role) noexcept
+ : m_role(role)
+ {}
+
+ constexpr int role() const noexcept { return m_role; }
+ constexpr QVariant &data() noexcept { return m_data; }
+ constexpr const QVariant &data() const noexcept { return m_data; }
+
+ template <typename T>
+ constexpr void setData(T &&value) noexcept(noexcept(m_data.setValue(std::forward<T>(value))))
+ { m_data.setValue(std::forward<T>(value)); }
+
+ void clearData() noexcept { m_data.clear(); }
+};
+
+Q_DECLARE_TYPEINFO(QModelRoleData, Q_MOVABLE_TYPE);
+
+class QModelRoleDataSpan;
+
+namespace QtPrivate {
+template <typename T, typename Enable = void>
+struct IsContainerCompatibleWithModelRoleDataSpan : std::false_type {};
+
+template <typename T>
+struct IsContainerCompatibleWithModelRoleDataSpan<T, std::enable_if_t<std::conjunction_v<
+ // lacking concepts and ranges, we accept any T whose std::data yields a suitable pointer ...
+ std::is_convertible<decltype( std::data(std::declval<T &>()) ), QModelRoleData *>,
+ // ... and that has a suitable size ...
+ std::is_convertible<decltype( std::size(std::declval<T &>()) ), qsizetype>,
+ // ... and it's a range as it defines an iterator-like API
+ std::is_convertible<
+ typename std::iterator_traits<decltype( std::begin(std::declval<T &>()) )>::value_type,
+ QModelRoleData
+ >,
+ std::is_convertible<
+ decltype( std::begin(std::declval<T &>()) != std::end(std::declval<T &>()) ),
+ bool>,
+ // Don't make an accidental copy constructor
+ std::negation<std::is_same<std::decay_t<T>, QModelRoleDataSpan>>
+ >>> : std::true_type {};
+} // namespace QtPrivate
+
+class QModelRoleDataSpan
+{
+ QModelRoleData *m_modelRoleData = nullptr;
+ qsizetype m_len = 0;
+
+ template <typename T>
+ using if_compatible_container = std::enable_if_t<QtPrivate::IsContainerCompatibleWithModelRoleDataSpan<T>::value, bool>;
+
+public:
+ constexpr QModelRoleDataSpan() noexcept {}
+
+ constexpr QModelRoleDataSpan(QModelRoleData &modelRoleData) noexcept
+ : m_modelRoleData(&modelRoleData),
+ m_len(1)
+ {}
+
+ constexpr QModelRoleDataSpan(QModelRoleData *modelRoleData, qsizetype len)
+ : m_modelRoleData(modelRoleData),
+ m_len(len)
+ {}
+
+ template <typename Container, if_compatible_container<Container> = true>
+ constexpr QModelRoleDataSpan(Container &c) noexcept(noexcept(std::data(c)) && noexcept(std::size(c)))
+ : m_modelRoleData(std::data(c)),
+ m_len(qsizetype(std::size(c)))
+ {}
+
+ constexpr qsizetype size() const noexcept { return m_len; }
+ constexpr qsizetype length() const noexcept { return m_len; }
+ constexpr QModelRoleData *data() const noexcept { return m_modelRoleData; }
+ constexpr QModelRoleData *begin() const noexcept { return m_modelRoleData; }
+ constexpr QModelRoleData *end() const noexcept { return m_modelRoleData + m_len; }
+ constexpr QModelRoleData &operator[](qsizetype index) const { return m_modelRoleData[index]; }
+
+ constexpr QVariant *dataForRole(int role) const
+ {
+#ifdef __cpp_lib_constexpr_algorithms
+ auto result = std::find_if(begin(), end(), [role](const QModelRoleData &roleData) {
+ return roleData.role() == role;
+ });
+#else
+ auto result = begin();
+ const auto e = end();
+ for (; result != e; ++result) {
+ if (result->role() == role)
+ break;
+ }
+#endif
+
+ return Q_ASSERT(result != end()), &result->data();
+ }
+};
+
+Q_DECLARE_TYPEINFO(QModelRoleDataSpan, Q_MOVABLE_TYPE);
class QAbstractItemModel;
class QPersistentModelIndex;
@@ -69,6 +172,7 @@ public:
inline QModelIndex siblingAtColumn(int column) const;
inline QModelIndex siblingAtRow(int row) const;
inline QVariant data(int role = Qt::DisplayRole) const;
+ inline void multiData(QModelRoleDataSpan roleDataSpan) const;
inline Qt::ItemFlags flags() const;
constexpr inline const QAbstractItemModel *model() const noexcept { return m; }
constexpr inline bool isValid() const noexcept { return (r >= 0) && (c >= 0) && (m != nullptr); }
@@ -132,6 +236,7 @@ public:
QModelIndex parent() const;
QModelIndex sibling(int row, int column) const;
QVariant data(int role = Qt::DisplayRole) const;
+ void multiData(QModelRoleDataSpan roleDataSpan) const;
Qt::ItemFlags flags() const;
const QAbstractItemModel *model() const;
bool isValid() const;
@@ -256,6 +361,8 @@ public:
Q_REQUIRED_RESULT bool checkIndex(const QModelIndex &index, CheckIndexOptions options = CheckIndexOption::NoOption) const;
+ virtual void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
+
Q_SIGNALS:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
const QList<int> &roles = QList<int>());
@@ -421,6 +528,9 @@ inline QModelIndex QModelIndex::siblingAtRow(int arow) const
inline QVariant QModelIndex::data(int arole) const
{ return m ? m->data(*this, arole) : QVariant(); }
+inline void QModelIndex::multiData(QModelRoleDataSpan roleDataSpan) const
+{ if (m) m->multiData(*this, roleDataSpan); }
+
inline Qt::ItemFlags QModelIndex::flags() const
{ return m ? m->flags(*this) : Qt::ItemFlags(); }
diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp
index cf905eae66..1d3dc1303f 100644
--- a/src/gui/itemmodels/qstandarditemmodel.cpp
+++ b/src/gui/itemmodels/qstandarditemmodel.cpp
@@ -968,13 +968,31 @@ void QStandardItem::clearData()
*/
QVariant QStandardItem::data(int role) const
{
+ QModelRoleData result(role);
+ multiData(result);
+ return result.data();
+}
+
+void QStandardItem::multiData(QModelRoleDataSpan roleDataSpan) const
+{
Q_D(const QStandardItem);
- const int r = (role == Qt::EditRole) ? Qt::DisplayRole : role;
- for (const auto &value : d->values) {
- if (value.role == r)
- return value.value;
+
+ const auto valuesBegin = d->values.begin();
+ const auto valuesEnd = d->values.end();
+
+ for (auto &roleData : roleDataSpan) {
+ const int role = (roleData.role() == Qt::EditRole) ? Qt::DisplayRole : roleData.role();
+ const auto hasSameRole = [role](const QStandardItemData &data)
+ {
+ return data.role == role;
+ };
+
+ auto dataIt = std::find_if(valuesBegin, valuesEnd, hasSameRole);
+ if (dataIt != valuesEnd)
+ roleData.setData(dataIt->value);
+ else
+ roleData.clearData();
}
- return QVariant();
}
/*!
@@ -2826,6 +2844,17 @@ QVariant QStandardItemModel::data(const QModelIndex &index, int role) const
/*!
\reimp
*/
+void QStandardItemModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const
+{
+ Q_D(const QStandardItemModel);
+ QStandardItem *item = d->itemFromIndex(index);
+ if (item)
+ item->multiData(roleDataSpan);
+}
+
+/*!
+ \reimp
+*/
Qt::ItemFlags QStandardItemModel::flags(const QModelIndex &index) const
{
Q_D(const QStandardItemModel);
diff --git a/src/gui/itemmodels/qstandarditemmodel.h b/src/gui/itemmodels/qstandarditemmodel.h
index 3e0374d741..02274839cb 100644
--- a/src/gui/itemmodels/qstandarditemmodel.h
+++ b/src/gui/itemmodels/qstandarditemmodel.h
@@ -66,6 +66,7 @@ public:
virtual ~QStandardItem();
virtual QVariant data(int role = Qt::UserRole + 1) const;
+ virtual void multiData(QModelRoleDataSpan roleDataSpan) const;
virtual void setData(const QVariant &value, int role = Qt::UserRole + 1);
void clearData();
@@ -331,6 +332,7 @@ public:
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool clearItemData(const QModelIndex &index) override;
diff --git a/src/widgets/itemviews/qstyleditemdelegate.cpp b/src/widgets/itemviews/qstyleditemdelegate.cpp
index 915a401a25..fb1d56464e 100644
--- a/src/widgets/itemviews/qstyleditemdelegate.cpp
+++ b/src/widgets/itemviews/qstyleditemdelegate.cpp
@@ -74,6 +74,7 @@
#include <qtableview.h>
#endif
+#include <array>
#include <limits.h>
QT_BEGIN_NAMESPACE
@@ -96,6 +97,16 @@ public:
}
QItemEditorFactory *factory;
+
+ mutable std::array<QModelRoleData, 7> modelRoleData = {
+ QModelRoleData(Qt::FontRole),
+ QModelRoleData(Qt::TextAlignmentRole),
+ QModelRoleData(Qt::ForegroundRole),
+ QModelRoleData(Qt::CheckStateRole),
+ QModelRoleData(Qt::DecorationRole),
+ QModelRoleData(Qt::DisplayRole),
+ QModelRoleData(Qt::BackgroundRole)
+ };
};
/*!
@@ -276,33 +287,39 @@ QString QStyledItemDelegate::displayText(const QVariant &value, const QLocale& l
void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
const QModelIndex &index) const
{
- QVariant value = index.data(Qt::FontRole);
- if (value.isValid() && !value.isNull()) {
- option->font = qvariant_cast<QFont>(value).resolve(option->font);
+ option->index = index;
+
+ Q_D(const QStyledItemDelegate);
+ QModelRoleDataSpan modelRoleDataSpan = d->modelRoleData;
+ index.multiData(modelRoleDataSpan);
+
+ const QVariant *value;
+ value = modelRoleDataSpan.dataForRole(Qt::FontRole);
+ if (value->isValid() && !value->isNull()) {
+ option->font = qvariant_cast<QFont>(*value).resolve(option->font);
option->fontMetrics = QFontMetrics(option->font);
}
- value = index.data(Qt::TextAlignmentRole);
- if (value.isValid() && !value.isNull())
- option->displayAlignment = Qt::Alignment(value.toInt());
+ value = modelRoleDataSpan.dataForRole(Qt::TextAlignmentRole);
+ if (value->isValid() && !value->isNull())
+ option->displayAlignment = Qt::Alignment(value->toInt());
- value = index.data(Qt::ForegroundRole);
- if (value.canConvert<QBrush>())
- option->palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
+ value = modelRoleDataSpan.dataForRole(Qt::ForegroundRole);
+ if (value->canConvert<QBrush>())
+ option->palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(*value));
- option->index = index;
- value = index.data(Qt::CheckStateRole);
- if (value.isValid() && !value.isNull()) {
+ value = modelRoleDataSpan.dataForRole(Qt::CheckStateRole);
+ if (value->isValid() && !value->isNull()) {
option->features |= QStyleOptionViewItem::HasCheckIndicator;
- option->checkState = static_cast<Qt::CheckState>(value.toInt());
+ option->checkState = static_cast<Qt::CheckState>(value->toInt());
}
- value = index.data(Qt::DecorationRole);
- if (value.isValid() && !value.isNull()) {
+ value = modelRoleDataSpan.dataForRole(Qt::DecorationRole);
+ if (value->isValid() && !value->isNull()) {
option->features |= QStyleOptionViewItem::HasDecoration;
- switch (value.userType()) {
+ switch (value->userType()) {
case QMetaType::QIcon: {
- option->icon = qvariant_cast<QIcon>(value);
+ option->icon = qvariant_cast<QIcon>(*value);
QIcon::Mode mode;
if (!(option->state & QStyle::State_Enabled))
mode = QIcon::Disabled;
@@ -319,18 +336,18 @@ void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
}
case QMetaType::QColor: {
QPixmap pixmap(option->decorationSize);
- pixmap.fill(qvariant_cast<QColor>(value));
+ pixmap.fill(qvariant_cast<QColor>(*value));
option->icon = QIcon(pixmap);
break;
}
case QMetaType::QImage: {
- QImage image = qvariant_cast<QImage>(value);
+ QImage image = qvariant_cast<QImage>(*value);
option->icon = QIcon(QPixmap::fromImage(image));
option->decorationSize = image.size() / image.devicePixelRatio();
break;
}
case QMetaType::QPixmap: {
- QPixmap pixmap = qvariant_cast<QPixmap>(value);
+ QPixmap pixmap = qvariant_cast<QPixmap>(*value);
option->icon = QIcon(pixmap);
option->decorationSize = pixmap.size() / pixmap.devicePixelRatio();
break;
@@ -340,13 +357,14 @@ void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
}
}
- value = index.data(Qt::DisplayRole);
- if (value.isValid() && !value.isNull()) {
+ value = modelRoleDataSpan.dataForRole(Qt::DisplayRole);
+ if (value->isValid() && !value->isNull()) {
option->features |= QStyleOptionViewItem::HasDisplay;
- option->text = displayText(value, option->locale);
+ option->text = displayText(*value, option->locale);
}
- option->backgroundBrush = qvariant_cast<QBrush>(index.data(Qt::BackgroundRole));
+ value = modelRoleDataSpan.dataForRole(Qt::BackgroundRole);
+ option->backgroundBrush = qvariant_cast<QBrush>(*value);
// disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
option->styleObject = nullptr;
diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp
index bf4f33fbd1..df3104eddf 100644
--- a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp
+++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp
@@ -35,6 +35,13 @@
#include "dynamictreemodel.h"
+// for testing QModelRoleDataSpan construction
+#include <QVarLengthArray>
+#include <array>
+#include <vector>
+#include <deque>
+#include <list>
+
/*!
Note that this doesn't test models, but any functionality that QAbstractItemModel should provide
*/
@@ -108,6 +115,10 @@ private slots:
void checkIndex();
+ void modelRoleDataSpanConstruction();
+ void modelRoleDataSpan();
+
+ void multiData();
private:
DynamicTreeModel *m_model;
};
@@ -2384,5 +2395,135 @@ void tst_QAbstractItemModel::checkIndex()
QVERIFY(!model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
}
+template <typename T>
+inline constexpr bool CanConvertToSpan = std::is_convertible_v<T, QModelRoleDataSpan>;
+
+void tst_QAbstractItemModel::modelRoleDataSpanConstruction()
+{
+ // Compile time test
+ static_assert(CanConvertToSpan<QModelRoleData &>);
+ static_assert(CanConvertToSpan<QModelRoleData (&)[123]>);
+ static_assert(CanConvertToSpan<QVector<QModelRoleData> &>);
+ static_assert(CanConvertToSpan<QVarLengthArray<QModelRoleData> &>);
+ static_assert(CanConvertToSpan<std::vector<QModelRoleData> &>);
+ static_assert(CanConvertToSpan<std::array<QModelRoleData, 123> &>);
+
+ static_assert(!CanConvertToSpan<QModelRoleData>);
+ static_assert(!CanConvertToSpan<QVector<QModelRoleData>>);
+ static_assert(!CanConvertToSpan<const QVector<QModelRoleData> &>);
+ static_assert(!CanConvertToSpan<std::vector<QModelRoleData>>);
+ static_assert(!CanConvertToSpan<std::deque<QModelRoleData>>);
+ static_assert(!CanConvertToSpan<std::deque<QModelRoleData> &>);
+ static_assert(!CanConvertToSpan<std::list<QModelRoleData> &>);
+ static_assert(!CanConvertToSpan<std::list<QModelRoleData>>);
+}
+
+void tst_QAbstractItemModel::modelRoleDataSpan()
+{
+ QModelRoleData data[3] = {
+ QModelRoleData(Qt::DisplayRole),
+ QModelRoleData(Qt::DecorationRole),
+ QModelRoleData(Qt::EditRole)
+ };
+ QModelRoleData *dataPtr = data;
+
+ QModelRoleDataSpan span(data);
+
+ QCOMPARE(span.size(), 3);
+ QCOMPARE(span.length(), 3);
+ QCOMPARE(span.data(), dataPtr);
+ QCOMPARE(span.begin(), dataPtr);
+ QCOMPARE(span.end(), dataPtr + 3);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(span[i].role(), data[i].role());
+
+ data[0].setData(42);
+ data[1].setData(QStringLiteral("a string"));
+ data[2].setData(123.5);
+
+ QCOMPARE(span.dataForRole(Qt::DisplayRole)->toInt(), 42);
+ QCOMPARE(span.dataForRole(Qt::DecorationRole)->toString(), "a string");
+ QCOMPARE(span.dataForRole(Qt::EditRole)->toDouble(), 123.5);
+}
+
+// model implementing data(), but not multiData(); check that the
+// default implementation of multiData() does the right thing
+class NonMultiDataRoleModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ int rowCount(const QModelIndex &) const override
+ {
+ return 1000;
+ }
+
+ // We handle roles <= 10. All such roles return a QVariant(int) containing
+ // the same value as the role, except for 10 which returns a string.
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid));
+
+ if (role < 10)
+ return QVariant::fromValue(role);
+ else if (role == 10)
+ return QVariant::fromValue(QStringLiteral("Hello!"));
+
+ return QVariant();
+ }
+};
+
+void tst_QAbstractItemModel::multiData()
+{
+ QModelRoleData data[] = {
+ QModelRoleData(1),
+ QModelRoleData(42),
+ QModelRoleData(5),
+ QModelRoleData(2),
+ QModelRoleData(12),
+ QModelRoleData(2),
+ QModelRoleData(10),
+ QModelRoleData(-123)
+ };
+
+ QModelRoleDataSpan span(data);
+
+ for (const auto &roledata : span)
+ QVERIFY(roledata.data().isNull());
+
+ NonMultiDataRoleModel model;
+ const QModelIndex index = model.index(0, 0);
+ QVERIFY(index.isValid());
+
+ const auto check = [&]() {
+ for (auto &roledata : span) {
+ const auto role = roledata.role();
+ if (role < 10) {
+ QVERIFY(!roledata.data().isNull());
+ QVERIFY(roledata.data().userType() == qMetaTypeId<int>());
+ QCOMPARE(roledata.data().toInt(), role);
+ } else if (role == 10) {
+ QVERIFY(!roledata.data().isNull());
+ QVERIFY(roledata.data().userType() == qMetaTypeId<QString>());
+ QCOMPARE(roledata.data().toString(), QStringLiteral("Hello!"));
+ } else {
+ QVERIFY(roledata.data().isNull());
+ }
+ }
+ };
+
+ model.multiData(index, span);
+ check();
+
+ model.multiData(index, span);
+ check();
+
+ for (auto &roledata : span)
+ roledata.clearData();
+
+ model.multiData(index, span);
+ check();
+}
+
QTEST_MAIN(tst_QAbstractItemModel)
#include "tst_qabstractitemmodel.moc"