QGIM: support gadgets and objects as rows and multi-role values
We can interpret the properties of a gadget or object in two ways: either as different columns in a fixed-width table, or as different role-values in a single item, matching the named properties against the role names. If used in a table structure, then the properties can only be interpreted as different role values, as we already have a row type with multiple columns. In a list it can be either. To disambiguate in this case, check whether there is a "display" property. If so, then we assume that the gadget is supposed to be a multi-role item. Change-Id: I99400304986f643506e590c5287f4d800654ec77 Reviewed-by: Artem Dyomin <artem.dyomin@qt.io>
This commit is contained in:
parent
ec5f4fe580
commit
8da1ec6a09
@ -107,6 +107,41 @@ namespace std {
|
||||
//! [tuple_protocol]
|
||||
#endif // __cpp_concepts && forward_like
|
||||
|
||||
namespace gadget {
|
||||
//! [gadget]
|
||||
class Book
|
||||
{
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString title READ title)
|
||||
Q_PROPERTY(QString author READ author)
|
||||
Q_PROPERTY(QString summary MEMBER m_summary)
|
||||
Q_PROPERTY(int rating READ rating WRITE setRating)
|
||||
public:
|
||||
Book(const QString &title, const QString &author);
|
||||
|
||||
// C++ rule of 0: destructor, as well as copy/move operations
|
||||
// provided by the compiler.
|
||||
|
||||
// read-only properties
|
||||
QString title() const { return m_title; }
|
||||
QString author() const { return m_author; }
|
||||
|
||||
// read/writable property with input validation
|
||||
int rating() const { return m_rating; }
|
||||
void setRating(int rating)
|
||||
{
|
||||
m_rating = qBound(0, rating, 5);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_title;
|
||||
QString m_author;
|
||||
QString m_summary;
|
||||
int m_rating = 0;
|
||||
};
|
||||
//! [gadget]
|
||||
} // namespace gadget
|
||||
|
||||
void color_map()
|
||||
{
|
||||
//! [color_map]
|
||||
@ -126,3 +161,45 @@ QListView list;
|
||||
list.setModel(&colorModel);
|
||||
//! [color_map]
|
||||
}
|
||||
|
||||
namespace multirole_gadget {
|
||||
//! [color_gadget_0]
|
||||
class ColorEntry
|
||||
{
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString display MEMBER m_colorName)
|
||||
Q_PROPERTY(QColor decoration READ decoration)
|
||||
Q_PROPERTY(QString toolTip READ toolTip)
|
||||
public:
|
||||
ColorEntry(const QString &color = {})
|
||||
: m_colorName(color)
|
||||
{}
|
||||
|
||||
QColor decoration() const
|
||||
{
|
||||
return QColor::fromString(m_colorName);
|
||||
}
|
||||
QString toolTip() const
|
||||
{
|
||||
return QColor::fromString(m_colorName).name();
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_colorName;
|
||||
};
|
||||
//! [color_gadget_0]
|
||||
|
||||
void color_list() {
|
||||
//! [color_gadget_1]
|
||||
const QStringList colorNames = QColor::colorNames();
|
||||
QList<QGenericItemModel::SingleColumn<ColorEntry>> colors;
|
||||
colors.reserve(colorNames.size());
|
||||
for (const QString &name : colorNames)
|
||||
colors << ColorEntry{name};
|
||||
|
||||
QGenericItemModel colorModel(colors);
|
||||
QListView list;
|
||||
list.setModel(&colorModel);
|
||||
//! [color_gadget_1]
|
||||
}
|
||||
} // namespace multirole_gadget
|
||||
|
@ -91,6 +91,24 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
\snippet qgenericitemmodel/main.cpp pair_int_QString
|
||||
|
||||
An easier and more flexible alternative to implementing the tuple protocol
|
||||
for a C++ type is to use Qt's \l{Meta-Object System}{meta-object system} to
|
||||
declare a type with \l{Qt's Property System}{properties}. This can be a
|
||||
value type that is declared as a \l{Q_GADGET}{gadget}, or a QObject subclass.
|
||||
|
||||
\snippet qgenericitemmodel/main.cpp gadget
|
||||
|
||||
Using QObject subclasses allows properties to be \l{Qt Bindable Properties}
|
||||
{bindable}, or to have change notification signals. However, using QObject
|
||||
instances for items has significant memory overhead.
|
||||
|
||||
Using Qt gadgets or objects is more convenient and can be more flexible
|
||||
than implementing the tuple protocol. Those types are also directly
|
||||
accessible from within QML. However, the access through the property system
|
||||
comes with some runtime overhead. For performance critical models, consider
|
||||
implementing the tuple protocol for compile-time generation of the access
|
||||
code.
|
||||
|
||||
\section2 Multi-role items
|
||||
|
||||
The type of the items that the implementations of data(), setData(),
|
||||
@ -118,6 +136,19 @@ QT_BEGIN_NAMESPACE
|
||||
\c{int}. When using \c{int}, itemData() returns the container as is, and
|
||||
doesn't have to create a copy of the data.
|
||||
|
||||
Gadgets and QObject types are also represented at multi-role items if they
|
||||
are the item type in a table. The names of the properties have to match the
|
||||
names of the roles.
|
||||
|
||||
\snippet qgenericitemmodel/main.cpp color_gadget_0
|
||||
|
||||
When used in a list, these types are ambiguous: they can be represented as
|
||||
multi-column rows, with each property represented as a separate column. Or
|
||||
they can be single items with each property being a role. To disambiguate,
|
||||
use the QGenericItemModel::SingleColumn wrapper.
|
||||
|
||||
\snippet qgenericitemmodel/main.cpp color_gadget_1
|
||||
|
||||
\section2 The C++ tuple protocol
|
||||
|
||||
As seen in the \c{numberNames} example above, the row type can be a tuple,
|
||||
|
@ -13,6 +13,9 @@ class Q_CORE_EXPORT QGenericItemModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
template <typename T>
|
||||
using SingleColumn = std::tuple<T>;
|
||||
|
||||
template <typename Range,
|
||||
QGenericItemModelDetails::if_is_range<Range> = true>
|
||||
explicit QGenericItemModel(Range &&range, QObject *parent = nullptr);
|
||||
@ -143,6 +146,11 @@ protected:
|
||||
using row_ptr = std::conditional_t<rows_are_pointers, row_type, row_type *>;
|
||||
using const_row_ptr = const std::remove_pointer_t<row_type> *;
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool has_metaobject =
|
||||
(QtPrivate::QMetaTypeForType<std::remove_pointer_t<T>>::flags() & QMetaType::IsGadget)
|
||||
|| (QtPrivate::QMetaTypeForType<T>::flags() & QMetaType::PointerToQObject);
|
||||
|
||||
using ModelData = QGenericItemModelDetails::ModelData<std::conditional_t<
|
||||
std::is_pointer_v<Range>,
|
||||
Range, std::remove_reference_t<Range>>
|
||||
@ -217,7 +225,14 @@ public:
|
||||
|
||||
Qt::ItemFlags f = Structure::defaultFlags();
|
||||
|
||||
if constexpr (static_column_count <= 0) {
|
||||
if constexpr (has_metaobject<row_type>) {
|
||||
if (index.column() < row_traits::fixed_size()) {
|
||||
const QMetaObject mo = std::remove_pointer_t<row_type>::staticMetaObject;
|
||||
const QMetaProperty prop = mo.property(index.column() + mo.propertyOffset());
|
||||
if (prop.isWritable())
|
||||
f |= Qt::ItemIsEditable;
|
||||
}
|
||||
} else if constexpr (static_column_count <= 0) {
|
||||
if constexpr (isMutable())
|
||||
f |= Qt::ItemIsEditable;
|
||||
} else if constexpr (std::is_reference_v<row_reference> && !std::is_const_v<row_reference>) {
|
||||
@ -244,7 +259,17 @@ public:
|
||||
return m_itemModel->QAbstractItemModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
if constexpr (static_column_count >= 1) {
|
||||
if constexpr (has_metaobject<row_type>) {
|
||||
using meta_type = std::remove_pointer_t<row_type>;
|
||||
if (row_traits::fixed_size() == 1) {
|
||||
const QMetaType metaType = QMetaType::fromType<meta_type>();
|
||||
result = QString::fromUtf8(metaType.name());
|
||||
} else if (section <= row_traits::fixed_size()) {
|
||||
const QMetaProperty prop = meta_type::staticMetaObject.property(
|
||||
section + meta_type::staticMetaObject.propertyOffset());
|
||||
result = QString::fromUtf8(prop.name());
|
||||
}
|
||||
} else if constexpr (static_column_count >= 1) {
|
||||
const QMetaType metaType = meta_type_at<row_type>(section);
|
||||
if (metaType.isValid())
|
||||
result = QString::fromUtf8(metaType.name());
|
||||
@ -257,11 +282,18 @@ public:
|
||||
QVariant data(const QModelIndex &index, int role) const
|
||||
{
|
||||
QVariant result;
|
||||
const auto readData = [this, &result, role](const auto &value) {
|
||||
const auto readData = [this, column = index.column(), &result, role](const auto &value) {
|
||||
Q_UNUSED(this);
|
||||
using value_type = q20::remove_cvref_t<decltype(value)>;
|
||||
using multi_role = QGenericItemModelDetails::is_multi_role<value_type>;
|
||||
if constexpr (multi_role::value) {
|
||||
if constexpr (has_metaobject<value_type>) {
|
||||
if (row_traits::fixed_size() <= 1) {
|
||||
result = readRole(role, value);
|
||||
} else if (column <= row_traits::fixed_size()
|
||||
&& (role == Qt::DisplayRole || role == Qt::EditRole)) {
|
||||
result = readProperty(column, value);
|
||||
}
|
||||
} else if constexpr (multi_role::value) {
|
||||
const auto it = [this, &value, role]{
|
||||
Q_UNUSED(this);
|
||||
if constexpr (multi_role::int_key)
|
||||
@ -347,10 +379,20 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
const auto writeData = [this, &data, role](auto &&target) -> bool {
|
||||
const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
|
||||
using value_type = q20::remove_cvref_t<decltype(target)>;
|
||||
using multi_role = QGenericItemModelDetails::is_multi_role<value_type>;
|
||||
if constexpr (multi_role::value) {
|
||||
if constexpr (has_metaobject<value_type>) {
|
||||
if (QMetaType::fromType<value_type>() == data.metaType()) {
|
||||
target = data.value<value_type>();
|
||||
return true;
|
||||
} else if (row_traits::fixed_size() <= 1) {
|
||||
return writeRole(role, target, data);
|
||||
} else if (column <= row_traits::fixed_size()
|
||||
&& (role == Qt::DisplayRole || role == Qt::EditRole)) {
|
||||
return writeProperty(column, target, data);
|
||||
}
|
||||
} else if constexpr (multi_role::value) {
|
||||
Qt::ItemDataRole roleToSet = Qt::ItemDataRole(role);
|
||||
// If there is an entry for EditRole, overwrite that; otherwise,
|
||||
// set the entry for DisplayRole.
|
||||
@ -476,20 +518,32 @@ public:
|
||||
Q_EMIT dataChanged(index, index, {});
|
||||
});
|
||||
|
||||
auto clearData = [column = index.column()](auto &&target) {
|
||||
if constexpr (has_metaobject<row_type>) {
|
||||
if (row_traits::fixed_size() <= 1) {
|
||||
// multi-role object/gadget: reset all properties
|
||||
return resetProperty(-1, target);
|
||||
} else if (column <= row_traits::fixed_size()) {
|
||||
return resetProperty(column, target);
|
||||
}
|
||||
} else { // normal structs, values, associative containers
|
||||
target = {};
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
row_reference row = rowData(index);
|
||||
if constexpr (dynamicColumns()) {
|
||||
*std::next(std::begin(row), index.column()) = {};
|
||||
success = true;
|
||||
success = clearData(*std::next(std::begin(row), index.column()));
|
||||
} else if constexpr (static_column_count == 0) {
|
||||
row = row_type{};
|
||||
success = true;
|
||||
success = clearData(row);
|
||||
} else if (QGenericItemModelDetails::isValid(row)) {
|
||||
for_element_at(row, index.column(), [&success](auto &&target){
|
||||
for_element_at(row, index.column(), [&clearData, &success](auto &&target){
|
||||
using target_type = decltype(target);
|
||||
if constexpr (std::is_lvalue_reference_v<target_type>
|
||||
&& !std::is_const_v<std::remove_reference_t<target_type>>) {
|
||||
target = {};
|
||||
success = true;
|
||||
success = clearData(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -649,6 +703,124 @@ protected:
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
QMetaProperty roleProperty(int role) const
|
||||
{
|
||||
const QMetaObject *mo = &ItemType::staticMetaObject;
|
||||
const QByteArray roleName = roleNames().value(role);
|
||||
if (const int index = mo->indexOfProperty(roleName.data()); index >= 0)
|
||||
return mo->property(index);
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
QVariant readRole(int role, ItemType *gadget) const
|
||||
{
|
||||
using item_type = std::remove_pointer_t<ItemType>;
|
||||
QVariant result;
|
||||
QMetaProperty prop = roleProperty<item_type>(role);
|
||||
if (!prop.isValid() && role == Qt::EditRole)
|
||||
prop = roleProperty<item_type>(Qt::DisplayRole);
|
||||
|
||||
if (prop.isValid())
|
||||
result = readProperty(prop, gadget);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
QVariant readRole(int role, const ItemType &gadget) const
|
||||
{
|
||||
return readRole(role, &gadget);
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
static QVariant readProperty(const QMetaProperty &prop, ItemType *gadget)
|
||||
{
|
||||
if constexpr (std::is_base_of_v<QObject, ItemType>)
|
||||
return prop.read(gadget);
|
||||
else
|
||||
return prop.readOnGadget(gadget);
|
||||
}
|
||||
template <typename ItemType>
|
||||
static QVariant readProperty(int property, ItemType *gadget)
|
||||
{
|
||||
using item_type = std::remove_pointer_t<ItemType>;
|
||||
const QMetaObject &mo = item_type::staticMetaObject;
|
||||
const QMetaProperty prop = mo.property(property + mo.propertyOffset());
|
||||
return readProperty(prop, gadget);
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
static QVariant readProperty(int property, const ItemType &gadget)
|
||||
{
|
||||
return readProperty(property, &gadget);
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
bool writeRole(int role, ItemType *gadget, const QVariant &data)
|
||||
{
|
||||
using item_type = std::remove_pointer_t<ItemType>;
|
||||
auto prop = roleProperty<item_type>(role);
|
||||
if (!prop.isValid() && role == Qt::EditRole)
|
||||
prop = roleProperty<item_type>(Qt::DisplayRole);
|
||||
|
||||
return writeProperty(prop, gadget, data);
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
bool writeRole(int role, ItemType &&gadget, const QVariant &data)
|
||||
{
|
||||
return writeRole(role, &gadget, data);
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
static bool writeProperty(const QMetaProperty &prop, ItemType *gadget, const QVariant &data)
|
||||
{
|
||||
if constexpr (std::is_base_of_v<QObject, ItemType>)
|
||||
return prop.write(gadget, data);
|
||||
else
|
||||
return prop.writeOnGadget(gadget, data);
|
||||
}
|
||||
template <typename ItemType>
|
||||
static bool writeProperty(int property, ItemType *gadget, const QVariant &data)
|
||||
{
|
||||
using item_type = std::remove_pointer_t<ItemType>;
|
||||
const QMetaObject &mo = item_type::staticMetaObject;
|
||||
return writeProperty(mo.property(property + mo.propertyOffset()), gadget, data);
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
static bool writeProperty(int property, ItemType &&gadget, const QVariant &data)
|
||||
{
|
||||
return writeProperty(property, &gadget, data);
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
static bool resetProperty(int property, ItemType *object)
|
||||
{
|
||||
using item_type = std::remove_pointer_t<ItemType>;
|
||||
const QMetaObject &mo = item_type::staticMetaObject;
|
||||
bool success = true;
|
||||
if (property == -1) {
|
||||
// reset all properties
|
||||
if constexpr (std::is_base_of_v<QObject, item_type>) {
|
||||
for (int p = mo.propertyOffset(); p < mo.propertyCount(); ++p)
|
||||
success = writeProperty(mo.property(p), object, {}) && success;
|
||||
} else { // reset a gadget by assigning a default-constructed
|
||||
*object = {};
|
||||
}
|
||||
} else {
|
||||
success = writeProperty(mo.property(property + mo.propertyOffset()), object, {});
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
template <typename ItemType>
|
||||
static bool resetProperty(int property, ItemType &&object)
|
||||
{
|
||||
return resetProperty(property, &object);
|
||||
}
|
||||
|
||||
// helpers
|
||||
const_row_reference rowData(const QModelIndex &index) const
|
||||
{
|
||||
|
@ -170,6 +170,25 @@ namespace QGenericItemModelDetails
|
||||
static constexpr int fixed_size() { return 0; }
|
||||
};
|
||||
|
||||
// Specialization for gadgets
|
||||
// clang doesn't accept multiple specializations using std::void_t directly
|
||||
template <class... Ts> struct make_void { using type = void; };
|
||||
|
||||
template <typename T>
|
||||
struct row_traits<T, typename make_void<decltype(T::staticMetaObject)>::type>
|
||||
{
|
||||
static constexpr int static_size = 0;
|
||||
static int fixed_size(){
|
||||
// Interpret a gadget in a list as a multi-column row item. To
|
||||
// disambiguate, stick it into a SingleColumn wrapper.
|
||||
static const int columnCount = []{
|
||||
const QMetaObject &mo = T::staticMetaObject;
|
||||
return mo.propertyCount() - mo.propertyOffset();
|
||||
}();
|
||||
return columnCount;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
[[maybe_unused]] static constexpr int static_size_v =
|
||||
row_traits<q20::remove_cvref_t<std::remove_pointer_t<T>>>::static_size;
|
||||
|
@ -12,15 +12,62 @@
|
||||
#include <QtTest/qabstractitemmodeltester.h>
|
||||
#endif
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__cpp_lib_ranges)
|
||||
#include <ranges>
|
||||
#endif
|
||||
|
||||
class Item
|
||||
{
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString display READ display WRITE setDisplay)
|
||||
Q_PROPERTY(QColor decoration READ decoration WRITE setDecoration)
|
||||
Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip)
|
||||
public:
|
||||
Item() = default;
|
||||
|
||||
Item(const QString &display, QColor decoration, const QString &toolTip)
|
||||
: m_display(display), m_decoration(decoration), m_toolTip(toolTip)
|
||||
{
|
||||
}
|
||||
|
||||
QString display() const { return m_display; }
|
||||
void setDisplay(const QString &display) { m_display = display; }
|
||||
QColor decoration() const { return m_decoration; }
|
||||
void setDecoration(QColor decoration) { m_decoration = decoration; }
|
||||
QString toolTip() const { return m_toolTip.isEmpty() ? display() : m_toolTip; }
|
||||
void setToolTip(const QString &toolTip) { m_toolTip = toolTip; }
|
||||
|
||||
private:
|
||||
QString m_display;
|
||||
QColor m_decoration;
|
||||
QString m_toolTip;
|
||||
};
|
||||
|
||||
class Object : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString string READ string WRITE setString)
|
||||
Q_PROPERTY(int number READ number WRITE setNumber)
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
QString string() const { return m_string; }
|
||||
void setString(const QString &string) { m_string = string; }
|
||||
int number() const { return m_number; }
|
||||
void setNumber(int number) { m_number = number; }
|
||||
|
||||
private:
|
||||
// note: default values need to be convertible to each other
|
||||
QString m_string = "1234";
|
||||
int m_number = 42;
|
||||
};
|
||||
|
||||
struct Row
|
||||
{
|
||||
QString m_item;
|
||||
Item m_item;
|
||||
int m_number;
|
||||
QString m_description;
|
||||
|
||||
@ -41,7 +88,7 @@ struct Row
|
||||
|
||||
namespace std {
|
||||
template <> struct tuple_size<Row> : std::integral_constant<size_t, 3> {};
|
||||
template <> struct tuple_element<0, Row> { using type = QString; };
|
||||
template <> struct tuple_element<0, Row> { using type = Item; };
|
||||
template <> struct tuple_element<1, Row> { using type = int; };
|
||||
template <> struct tuple_element<2, Row> { using type = QString; };
|
||||
}
|
||||
@ -110,9 +157,9 @@ private:
|
||||
std::array<int, 5> fixedArrayOfNumbers = {1, 2, 3, 4, 5};
|
||||
int cArrayOfNumbers[5] = {1, 2, 3, 4, 5};
|
||||
Row cArrayFixedColumns[3] = {
|
||||
{"red", 0xff0000, "The color red"},
|
||||
{"green", 0x00ff00, "The color green"},
|
||||
{"blue", 0x0000ff, "The color blue"}
|
||||
{{"red", Qt::red, "0xff0000"}, 0xff0000, "The color red"},
|
||||
{{"green", Qt::green, "0x00ff00"}, 0x00ff00, "The color green"},
|
||||
{{"blue", Qt::blue, "0x0000ff"}, 0x0000ff, "The color blue"}
|
||||
};
|
||||
|
||||
// dynamic number of rows, fixed number of columns
|
||||
@ -130,10 +177,27 @@ private:
|
||||
{31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
|
||||
{41, 42, 43, 44, 45, 46, 47, 48, 49, 50},
|
||||
};
|
||||
std::vector<Item> vectorOfGadgets = {
|
||||
{"red", Qt::red, "0xff0000"},
|
||||
{"green", Qt::green, "0x00ff00"},
|
||||
{"blue", Qt::blue, "0x0000ff"},
|
||||
};
|
||||
std::vector<QGenericItemModel::SingleColumn<Item>> listOfGadgets = {
|
||||
{{"red", Qt::red, "0xff0000"}},
|
||||
{{"green", Qt::green, "0x00ff00"}},
|
||||
{{"blue", Qt::blue, "0x0000ff"}},
|
||||
};
|
||||
std::vector<Row> vectorOfStructs = {
|
||||
{"red", 1, "one"},
|
||||
{"green", 2, "two"},
|
||||
{"blue", 3, "three"},
|
||||
{{"red", Qt::red, "0xff0000"}, 1, "one"},
|
||||
{{"green", Qt::green, "0x00ff00"}, 2, "two"},
|
||||
{{"blue", Qt::blue, "0x0000ff"}, 3, "three"},
|
||||
};
|
||||
|
||||
Object row1;
|
||||
Object row2;
|
||||
Object row3;
|
||||
std::list<Object *> listOfObjects = {
|
||||
&row1, &row2, &row3
|
||||
};
|
||||
|
||||
// bad (but legal) get() overload that never returns a mutable reference
|
||||
@ -152,8 +216,16 @@ private:
|
||||
{21.0, 22.0, 23.0, 24.0, 25.0},
|
||||
};
|
||||
|
||||
// item is pointer
|
||||
Item itemAsPointer = {"red", Qt::red, "0xff0000"};
|
||||
std::vector<std::vector<Item *>> tableOfPointers = {
|
||||
{&itemAsPointer, &itemAsPointer},
|
||||
{&itemAsPointer, &itemAsPointer},
|
||||
{&itemAsPointer, &itemAsPointer},
|
||||
};
|
||||
|
||||
// rows are pointers
|
||||
Row rowAsPointer = {"blue", 0x0000ff, "Blau"};
|
||||
Row rowAsPointer = {{"blue", Qt::blue, "0x0000ff"}, 0x0000ff, "Blau"};
|
||||
std::vector<Row *> tableOfRowPointers = {
|
||||
&rowAsPointer,
|
||||
&rowAsPointer,
|
||||
@ -275,17 +347,29 @@ void tst_QGenericItemModel::createTestData()
|
||||
<< int(std::tuple_size_v<Row>) << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
ADD_POINTER(vectorOfStructs)
|
||||
<< int(std::tuple_size_v<Row>) << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
|
||||
ADD_COPY(vectorOfConstStructs)
|
||||
<< int(std::tuple_size_v<ConstRow>) << ChangeActions(ChangeAction::ChangeRows);
|
||||
ADD_POINTER(vectorOfConstStructs)
|
||||
<< int(std::tuple_size_v<ConstRow>) << ChangeActions(ChangeAction::ChangeRows);
|
||||
|
||||
ADD_COPY(vectorOfGadgets)
|
||||
<< 3 << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
ADD_POINTER(vectorOfGadgets)
|
||||
<< 3 << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
ADD_COPY(listOfGadgets)
|
||||
<< 1 << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
ADD_POINTER(listOfGadgets)
|
||||
<< 1 << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
ADD_COPY(listOfObjects)
|
||||
<< 2 << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
|
||||
ADD_COPY(tableOfNumbers)
|
||||
<< 5 << ChangeActions(ChangeAction::All);
|
||||
ADD_POINTER(tableOfNumbers)
|
||||
<< 5 << ChangeActions(ChangeAction::All);
|
||||
// only adding as pointer, copy would operate on the same data
|
||||
ADD_POINTER(tableOfPointers)
|
||||
<< 2 << ChangeActions(ChangeAction::All);
|
||||
ADD_POINTER(tableOfRowPointers)
|
||||
<< int(std::tuple_size_v<Row>) << (ChangeAction::ChangeRows | ChangeAction::SetData);
|
||||
|
||||
@ -598,6 +682,7 @@ void tst_QGenericItemModel::insertRows()
|
||||
const QVariant lastValue = lastItem.data();
|
||||
QEXPECT_FAIL("tableOfPointersPointer", "No item created", Continue);
|
||||
QEXPECT_FAIL("tableOfRowPointersPointer", "No row created", Continue);
|
||||
QEXPECT_FAIL("listOfObjectsCopy", "No object created", Continue);
|
||||
|
||||
// associative containers are default constructed with no valid data
|
||||
ignoreFailureFromAssociativeContainers();
|
||||
|
Loading…
x
Reference in New Issue
Block a user