Long live QAIM::multiData!
Views / delegates absolutely *adore* hammering data(). A simple QListView showing a couple of dozens entries can call data() a hundred of times on the first show. Back of the hand calculation, * 2 times per visible item (sizeHint() + paint()), * times 9 roles used by the default delegate, * times 20 visible items = 360 as a bare minimum, assuming the view doesn't redraw twice accidentally. Move the mouse over the view, and that'll cause a full update with certain styles: 360 calls to data() per update. This has an overhead visible in profilers. The model's data() has to re-fetch the index from its data structure and extract the requested field every time. Also, QVariant is used for the data interexchange, meaning anything that won't fit in one is also a memory allocation. This problem will likely be gone in Qt6Variant as that will store sizeof(void*) * 3, meaning QImage/QPixmap and similar polymorphic classes will fit in a QVariant now... So I'm trying to to remove part of that overhead by allowing views to request all the data they need in one go. For now, one index a a time. A view might also store the data returned. The idea is that the same role on different indexes will _very likely_ return variants of the same type. So a model could move-assign the data into the variant, avoiding the memory allocation /deallocation for the variant's private. This patch: 1) Introduces QModelRoleData as a holder for role+data. 2) Introduces QModelRoleDataSpan as a span over QModelRoleData. The idea of a span type is twofold. First and foremost, we are in no position to choose which kind of container a view should use to store the QModelRoleData objects for a multiData() call; a span abstracts any contiguous sequence, leaving the view free to do whatever it wants (statically allocate, use a vector, etc.). It also solves the problem of efficient passing the roles and gathering the returned variants from multiData(). 3) Add multiData(), which populates a span of roles for a given model index. The main advantage here is that a model can fetch all the needed information for a given index just once, then iterate on the span and provide data for each requested role. Cf. this with data(), where every call has to re-fetch the information for the index. A couple of models have been ported to multiData(), as well as QStyledItemDelegate. [ChangeLog][QtCore][QModelRoleData] New class. [ChangeLog][QtCore][QModelRoleDataSpan] New class. [ChangeLog][QtCore][QAbstractItemModel] Added the multiData() function. Change-Id: Icce0d108ad4e156c9fb05c83ce6df5f58f99f118 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
19874d6a63
commit
39e07ebf64
@ -141,3 +141,48 @@ private:
|
|||||||
};
|
};
|
||||||
//! [12]
|
//! [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]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** 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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the QtCore module of the Qt Toolkit.
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
@ -90,6 +91,226 @@ void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data)
|
|||||||
delete data;
|
delete 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
|
\class QPersistentModelIndex
|
||||||
\inmodule QtCore
|
\inmodule QtCore
|
||||||
@ -423,6 +644,20 @@ QVariant QPersistentModelIndex::data(int role) const
|
|||||||
return QVariant();
|
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
|
\since 4.2
|
||||||
|
|
||||||
@ -1108,6 +1343,14 @@ void QAbstractItemModel::resetInternalData()
|
|||||||
index.
|
index.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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
|
\fn Qt::ItemFlags QModelIndex::flags() const
|
||||||
\since 4.2
|
\since 4.2
|
||||||
@ -3400,6 +3643,61 @@ bool QAbstractItemModel::checkIndex(const QModelIndex &index, CheckIndexOptions
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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
|
\class QAbstractTableModel
|
||||||
\inmodule QtCore
|
\inmodule QtCore
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** 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/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the QtCore module of the Qt Toolkit.
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
@ -49,6 +50,108 @@ QT_REQUIRE_CONFIG(itemmodel);
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
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 QAbstractItemModel;
|
||||||
class QPersistentModelIndex;
|
class QPersistentModelIndex;
|
||||||
@ -69,6 +172,7 @@ public:
|
|||||||
inline QModelIndex siblingAtColumn(int column) const;
|
inline QModelIndex siblingAtColumn(int column) const;
|
||||||
inline QModelIndex siblingAtRow(int row) const;
|
inline QModelIndex siblingAtRow(int row) const;
|
||||||
inline QVariant data(int role = Qt::DisplayRole) const;
|
inline QVariant data(int role = Qt::DisplayRole) const;
|
||||||
|
inline void multiData(QModelRoleDataSpan roleDataSpan) const;
|
||||||
inline Qt::ItemFlags flags() const;
|
inline Qt::ItemFlags flags() const;
|
||||||
constexpr inline const QAbstractItemModel *model() const noexcept { return m; }
|
constexpr inline const QAbstractItemModel *model() const noexcept { return m; }
|
||||||
constexpr inline bool isValid() const noexcept { return (r >= 0) && (c >= 0) && (m != nullptr); }
|
constexpr inline bool isValid() const noexcept { return (r >= 0) && (c >= 0) && (m != nullptr); }
|
||||||
@ -132,6 +236,7 @@ public:
|
|||||||
QModelIndex parent() const;
|
QModelIndex parent() const;
|
||||||
QModelIndex sibling(int row, int column) const;
|
QModelIndex sibling(int row, int column) const;
|
||||||
QVariant data(int role = Qt::DisplayRole) const;
|
QVariant data(int role = Qt::DisplayRole) const;
|
||||||
|
void multiData(QModelRoleDataSpan roleDataSpan) const;
|
||||||
Qt::ItemFlags flags() const;
|
Qt::ItemFlags flags() const;
|
||||||
const QAbstractItemModel *model() const;
|
const QAbstractItemModel *model() const;
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
@ -256,6 +361,8 @@ public:
|
|||||||
|
|
||||||
Q_REQUIRED_RESULT bool checkIndex(const QModelIndex &index, CheckIndexOptions options = CheckIndexOption::NoOption) const;
|
Q_REQUIRED_RESULT bool checkIndex(const QModelIndex &index, CheckIndexOptions options = CheckIndexOption::NoOption) const;
|
||||||
|
|
||||||
|
virtual void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
|
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
|
||||||
const QList<int> &roles = QList<int>());
|
const QList<int> &roles = QList<int>());
|
||||||
@ -421,6 +528,9 @@ inline QModelIndex QModelIndex::siblingAtRow(int arow) const
|
|||||||
inline QVariant QModelIndex::data(int arole) const
|
inline QVariant QModelIndex::data(int arole) const
|
||||||
{ return m ? m->data(*this, arole) : QVariant(); }
|
{ 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
|
inline Qt::ItemFlags QModelIndex::flags() const
|
||||||
{ return m ? m->flags(*this) : Qt::ItemFlags(); }
|
{ return m ? m->flags(*this) : Qt::ItemFlags(); }
|
||||||
|
|
||||||
|
@ -967,14 +967,32 @@ void QStandardItem::clearData()
|
|||||||
as referring to the same data.
|
as referring to the same data.
|
||||||
*/
|
*/
|
||||||
QVariant QStandardItem::data(int role) const
|
QVariant QStandardItem::data(int role) const
|
||||||
|
{
|
||||||
|
QModelRoleData result(role);
|
||||||
|
multiData(result);
|
||||||
|
return result.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QStandardItem::multiData(QModelRoleDataSpan roleDataSpan) const
|
||||||
{
|
{
|
||||||
Q_D(const QStandardItem);
|
Q_D(const QStandardItem);
|
||||||
const int r = (role == Qt::EditRole) ? Qt::DisplayRole : role;
|
|
||||||
for (const auto &value : d->values) {
|
const auto valuesBegin = d->values.begin();
|
||||||
if (value.role == r)
|
const auto valuesEnd = d->values.end();
|
||||||
return value.value;
|
|
||||||
|
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -2823,6 +2841,17 @@ QVariant QStandardItemModel::data(const QModelIndex &index, int role) const
|
|||||||
return item ? item->data(role) : QVariant();
|
return item ? item->data(role) : QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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
|
\reimp
|
||||||
*/
|
*/
|
||||||
|
@ -66,6 +66,7 @@ public:
|
|||||||
virtual ~QStandardItem();
|
virtual ~QStandardItem();
|
||||||
|
|
||||||
virtual QVariant data(int role = Qt::UserRole + 1) const;
|
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);
|
virtual void setData(const QVariant &value, int role = Qt::UserRole + 1);
|
||||||
void clearData();
|
void clearData();
|
||||||
|
|
||||||
@ -331,6 +332,7 @@ public:
|
|||||||
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
|
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) 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 setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||||
bool clearItemData(const QModelIndex &index) override;
|
bool clearItemData(const QModelIndex &index) override;
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@
|
|||||||
#include <qtableview.h>
|
#include <qtableview.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
@ -96,6 +97,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QItemEditorFactory *factory;
|
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,
|
void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
|
||||||
const QModelIndex &index) const
|
const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
QVariant value = index.data(Qt::FontRole);
|
option->index = index;
|
||||||
if (value.isValid() && !value.isNull()) {
|
|
||||||
option->font = qvariant_cast<QFont>(value).resolve(option->font);
|
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);
|
option->fontMetrics = QFontMetrics(option->font);
|
||||||
}
|
}
|
||||||
|
|
||||||
value = index.data(Qt::TextAlignmentRole);
|
value = modelRoleDataSpan.dataForRole(Qt::TextAlignmentRole);
|
||||||
if (value.isValid() && !value.isNull())
|
if (value->isValid() && !value->isNull())
|
||||||
option->displayAlignment = Qt::Alignment(value.toInt());
|
option->displayAlignment = Qt::Alignment(value->toInt());
|
||||||
|
|
||||||
value = index.data(Qt::ForegroundRole);
|
value = modelRoleDataSpan.dataForRole(Qt::ForegroundRole);
|
||||||
if (value.canConvert<QBrush>())
|
if (value->canConvert<QBrush>())
|
||||||
option->palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
|
option->palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(*value));
|
||||||
|
|
||||||
option->index = index;
|
value = modelRoleDataSpan.dataForRole(Qt::CheckStateRole);
|
||||||
value = index.data(Qt::CheckStateRole);
|
if (value->isValid() && !value->isNull()) {
|
||||||
if (value.isValid() && !value.isNull()) {
|
|
||||||
option->features |= QStyleOptionViewItem::HasCheckIndicator;
|
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);
|
value = modelRoleDataSpan.dataForRole(Qt::DecorationRole);
|
||||||
if (value.isValid() && !value.isNull()) {
|
if (value->isValid() && !value->isNull()) {
|
||||||
option->features |= QStyleOptionViewItem::HasDecoration;
|
option->features |= QStyleOptionViewItem::HasDecoration;
|
||||||
switch (value.userType()) {
|
switch (value->userType()) {
|
||||||
case QMetaType::QIcon: {
|
case QMetaType::QIcon: {
|
||||||
option->icon = qvariant_cast<QIcon>(value);
|
option->icon = qvariant_cast<QIcon>(*value);
|
||||||
QIcon::Mode mode;
|
QIcon::Mode mode;
|
||||||
if (!(option->state & QStyle::State_Enabled))
|
if (!(option->state & QStyle::State_Enabled))
|
||||||
mode = QIcon::Disabled;
|
mode = QIcon::Disabled;
|
||||||
@ -319,18 +336,18 @@ void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
|
|||||||
}
|
}
|
||||||
case QMetaType::QColor: {
|
case QMetaType::QColor: {
|
||||||
QPixmap pixmap(option->decorationSize);
|
QPixmap pixmap(option->decorationSize);
|
||||||
pixmap.fill(qvariant_cast<QColor>(value));
|
pixmap.fill(qvariant_cast<QColor>(*value));
|
||||||
option->icon = QIcon(pixmap);
|
option->icon = QIcon(pixmap);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QMetaType::QImage: {
|
case QMetaType::QImage: {
|
||||||
QImage image = qvariant_cast<QImage>(value);
|
QImage image = qvariant_cast<QImage>(*value);
|
||||||
option->icon = QIcon(QPixmap::fromImage(image));
|
option->icon = QIcon(QPixmap::fromImage(image));
|
||||||
option->decorationSize = image.size() / image.devicePixelRatio();
|
option->decorationSize = image.size() / image.devicePixelRatio();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QMetaType::QPixmap: {
|
case QMetaType::QPixmap: {
|
||||||
QPixmap pixmap = qvariant_cast<QPixmap>(value);
|
QPixmap pixmap = qvariant_cast<QPixmap>(*value);
|
||||||
option->icon = QIcon(pixmap);
|
option->icon = QIcon(pixmap);
|
||||||
option->decorationSize = pixmap.size() / pixmap.devicePixelRatio();
|
option->decorationSize = pixmap.size() / pixmap.devicePixelRatio();
|
||||||
break;
|
break;
|
||||||
@ -340,13 +357,14 @@ void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value = index.data(Qt::DisplayRole);
|
value = modelRoleDataSpan.dataForRole(Qt::DisplayRole);
|
||||||
if (value.isValid() && !value.isNull()) {
|
if (value->isValid() && !value->isNull()) {
|
||||||
option->features |= QStyleOptionViewItem::HasDisplay;
|
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)
|
// disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
|
||||||
option->styleObject = nullptr;
|
option->styleObject = nullptr;
|
||||||
|
@ -35,6 +35,13 @@
|
|||||||
|
|
||||||
#include "dynamictreemodel.h"
|
#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
|
Note that this doesn't test models, but any functionality that QAbstractItemModel should provide
|
||||||
*/
|
*/
|
||||||
@ -108,6 +115,10 @@ private slots:
|
|||||||
|
|
||||||
void checkIndex();
|
void checkIndex();
|
||||||
|
|
||||||
|
void modelRoleDataSpanConstruction();
|
||||||
|
void modelRoleDataSpan();
|
||||||
|
|
||||||
|
void multiData();
|
||||||
private:
|
private:
|
||||||
DynamicTreeModel *m_model;
|
DynamicTreeModel *m_model;
|
||||||
};
|
};
|
||||||
@ -2384,5 +2395,135 @@ void tst_QAbstractItemModel::checkIndex()
|
|||||||
QVERIFY(!model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
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)
|
QTEST_MAIN(tst_QAbstractItemModel)
|
||||||
#include "tst_qabstractitemmodel.moc"
|
#include "tst_qabstractitemmodel.moc"
|
||||||
|
Loading…
Reference in New Issue
Block a user