├── .gitignore ├── LICENSE ├── ListModel.pro ├── README.md ├── src ├── QCxxListModel.cpp ├── QCxxListModel.h ├── QCxxListModel.pri └── QCxxListModel_p.h └── test ├── auto ├── ListModel │ ├── ListModel.pro │ ├── ListModelTest.cpp │ ├── ListModelTest.h │ ├── ListModelTester.cpp │ ├── ListModelTester.h │ └── ListModelTester_p.h └── auto.pro ├── demo ├── DemoCore.cpp ├── DemoCore.h ├── DemoCore_p.h ├── DemoNode.cpp ├── DemoNode.h ├── DemoNode_p.h ├── demo.pro ├── main.cpp ├── main.qml └── qml.qrc └── test.pro /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | moc_*.h 24 | qrc_*.cpp 25 | ui_*.h 26 | Makefile* 27 | *build-* 28 | 29 | # QtCreator 30 | 31 | *.autosave 32 | 33 | # QtCtreator Qml 34 | *.qmlproject.user 35 | *.qmlproject.user.* 36 | 37 | # QtCtreator CMake 38 | CMakeLists.txt.user* 39 | 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 周珺 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ListModel.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += \ 4 | test 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 用于 QML 的 C++ 列表模型 2 | 3 | 本项目维护了一个用于 QML 工程的 C++ 列表模型 `QCxxListModel`。它继承自 `QList` 和 `QAbstractListModel`,这将提供一种新的方式:在 C++ 中,将其作为 `QList` 来使用;在 QML 中,将其作为 `QAbstractListModel` 来展示。 4 | 5 | ### 使用方式: 6 | 在 C++ 中,`QCxxListModel` 与 `QList` 的使用方式基本一致: 7 | ``` cpp 8 | QList nodeList; 9 | QCxxListModel nodeModel; 10 | ... 11 | nodeList.append(node); 12 | nodeModel.append(node); 13 | ... 14 | nodeList.removeOne(node); 15 | nodeModel.removeOne(node); 16 | ... 17 | DemoNode *node = nodeList.value(0); 18 | DemoNode *node = nodeModel.value(0); 19 | ... 20 | ``` 21 | 由于迭代器操作无法触发模型的方法、信号,列表模型屏蔽了所有与迭代器操作相关的方法。这也导致了 C++11 的 for 循环无法直接使用。如果需要遍历列表模型,可以通过 `QCxxListModel::ref()` 方法,获取一个列表容器的引用: 22 | ``` cpp 23 | for (DemoNode *node : nodeModel.ref()) { 24 | node ... 25 | } 26 | ``` 27 | 如果想将列表模型当做类属性,暴露给 QML,可以参考下面的写法: 28 | ```cpp 29 | class DemoCore : public QObject 30 | { 31 | Q_OBJECT 32 | Q_PROPERTY(QAbstractListModel *nodes READ nodes CONSTANT) 33 | ... 34 | QAbstractListModel *nodes() const 35 | { 36 | return const_cast *>(&nodeModel); 37 | } 38 | ... 39 | QCxxListModel nodeModel; 40 | }; 41 | ``` 42 | 43 | 这样,在 QML 中,我们就可以将其作为一个模型来使用了: 44 | ```qml 45 | Item { 46 | DemoCore { 47 | id: core 48 | } 49 | ListView { 50 | model: core.nodes 51 | delegate: Item { 52 | text: modelData.text // modelData -> DemoNode * 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/QCxxListModel.cpp: -------------------------------------------------------------------------------- 1 | #include "QCxxListModel.h" 2 | #include "QCxxListModel_p.h" 3 | 4 | namespace Internal { 5 | 6 | // class QCxxListModel 7 | 8 | QCxxListModel::QCxxListModel(QObject *parent) 9 | : QAbstractListModel(parent) 10 | , d_ptr(new QCxxListModelPrivate()) 11 | { 12 | d_ptr->q_ptr = this; 13 | } 14 | 15 | QCxxListModel::~QCxxListModel() 16 | { 17 | } 18 | 19 | QHash QCxxListModel::roleNames() const 20 | { 21 | #ifdef Q_COMPILER_INITIALIZER_LISTS 22 | QHash roleNames { { QCxxListModel::ModelDataRole, "modelData" } }; 23 | #else 24 | QHash roleNames; 25 | roleNames[QCxxListModel::ModelDataRole] = "modelData"; 26 | #endif // Q_COMPILER_INITIALIZER_LISTS 27 | 28 | return roleNames; 29 | } 30 | 31 | void QCxxListModel::_q_resetCount() 32 | { 33 | Q_D(QCxxListModel); 34 | 35 | if (d->countEnabled) { 36 | int count = rowCount(); 37 | if (count != d->count) { 38 | d->count = count; 39 | emit countChanged(); 40 | } 41 | } 42 | } 43 | 44 | bool QCxxListModel::isCountEnabled() const 45 | { 46 | Q_D(const QCxxListModel); 47 | 48 | return d->countEnabled; 49 | } 50 | 51 | void QCxxListModel::setCountEnabled(bool y) 52 | { 53 | Q_D(QCxxListModel); 54 | 55 | if (y != d->countEnabled) { 56 | d->countEnabled = y; 57 | if (!d->countEnabled) { 58 | d->count = -1; 59 | } 60 | emit countEnabledChanged(); 61 | } 62 | } 63 | 64 | // class QCxxListModelPrivate 65 | 66 | QCxxListModelPrivate::QCxxListModelPrivate() 67 | : q_ptr(nullptr) 68 | , countEnabled(false) 69 | , count(-1) 70 | { 71 | } 72 | 73 | QCxxListModelPrivate::~QCxxListModelPrivate() 74 | { 75 | } 76 | 77 | } // namespace Internal 78 | 79 | -------------------------------------------------------------------------------- /src/QCxxListModel.h: -------------------------------------------------------------------------------- 1 | #ifndef QCXXLISTMODEL_H 2 | #define QCXXLISTMODEL_H 3 | 4 | #include 5 | #include 6 | 7 | #if defined(QT_TESTLIB_LIB) 8 | # define QCXXLISTMODEL_ASSERT(x) 9 | #else 10 | # define QCXXLISTMODEL_ASSERT(x) Q_ASSERT(x) 11 | #endif 12 | 13 | namespace Internal { 14 | 15 | template 16 | struct QCxxListGuard 17 | { 18 | }; 19 | 20 | class QCxxListModelPrivate; 21 | class QCxxListModel : public QAbstractListModel 22 | { 23 | Q_OBJECT 24 | Q_DECLARE_PRIVATE(QCxxListModel) 25 | Q_PROPERTY(int count READ rowCount NOTIFY countChanged) 26 | Q_PROPERTY(bool countEnabled READ isCountEnabled NOTIFY countEnabledChanged) 27 | 28 | public: 29 | explicit QCxxListModel(QObject *parent = nullptr); 30 | virtual ~QCxxListModel(); 31 | protected: 32 | QScopedPointer d_ptr; 33 | 34 | public: 35 | enum ModelDataRoles { 36 | ModelDataRole = Qt::UserRole + 1, 37 | }; 38 | QHash roleNames() const override; 39 | 40 | signals: 41 | void countChanged(); 42 | protected: 43 | void _q_resetCount(); 44 | 45 | signals: 46 | void countEnabledChanged(); 47 | public: 48 | bool isCountEnabled() const; 49 | public: 50 | void setCountEnabled(bool y); 51 | }; 52 | 53 | } // namespace Internal 54 | 55 | template 56 | class QCxxListModel : public Internal::QCxxListModel, public QList 57 | { 58 | public: 59 | QCxxListModel(const QList &l, QObject *parent = nullptr) 60 | : Internal::QCxxListModel(parent), QList(l) { } 61 | explicit QCxxListModel(QObject *parent = nullptr) 62 | : Internal::QCxxListModel(parent) { } 63 | 64 | public: 65 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 66 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 67 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 68 | 69 | public: 70 | T value(const QModelIndex &index) const; 71 | T value(const QModelIndex &index, const T &defaultValue) const; 72 | 73 | T value(int i) const; 74 | T value(int i, const T &defaultValue) const; 75 | 76 | public: 77 | void append(const QList &l); 78 | 79 | void prepend(const T &t); 80 | void append(const T &t); 81 | 82 | void push_front(const T &t); 83 | void push_back(const T &t); 84 | 85 | void replace(int i, const T &t); 86 | void insert(int i, const T &t); 87 | 88 | bool removeOne(const T &t); 89 | int removeAll(const T &t); 90 | 91 | void pop_front(); 92 | void pop_back(); 93 | 94 | void removeAt(int i); 95 | void removeFirst(); 96 | void removeLast(); 97 | 98 | T takeAt(int i); 99 | T takeFirst(); 100 | T takeLast(); 101 | 102 | void swap(int i, int j); 103 | void swap(QList &list); 104 | void move(int from, int to); 105 | 106 | void clear(); 107 | 108 | public: // Extra methods 109 | void deleteAll(); 110 | 111 | public: // Disabled stl methods 112 | typedef typename QList::iterator Iterator; 113 | typedef typename QList::reverse_iterator ReverseIterator; 114 | 115 | Iterator begin(); // Not Implemented 116 | Iterator end(); // Not Implemented 117 | 118 | ReverseIterator rbegin(); // Not Implemented 119 | ReverseIterator rend(); // Not Implemented 120 | 121 | Iterator insert(Iterator before, const T &t); // Not Implemented 122 | Iterator erase(Iterator pos); // No Implemented 123 | Iterator erase(Iterator begin, Iterator end); // Not Implemented 124 | 125 | public: 126 | QCxxListModel &operator=(const QList &l); 127 | inline QList operator+(const QList &others) const 128 | { QList l = *this; l.append(others); return l; } 129 | 130 | inline bool operator==(const QList &other) const 131 | { return QList::operator==(other); } 132 | inline bool operator!=(const QList &other) const 133 | { return QList::operator!=(other); } 134 | 135 | inline QCxxListModel &operator+=(const QList &l) 136 | { append(l); return *this; } 137 | inline QCxxListModel &operator<<(const QList &l) 138 | { append(l); return *this; } 139 | 140 | inline QCxxListModel &operator+=(const T &t) 141 | { append(t); return *this; } 142 | inline QCxxListModel &operator<<(const T &t) 143 | { append(t); return *this; } 144 | 145 | public: 146 | inline const QList &ref() const { return *this; } 147 | }; 148 | 149 | template 150 | int QCxxListModel::rowCount(const QModelIndex &parent) const 151 | { 152 | return parent.isValid() ? 0 : QList::count(); 153 | } 154 | 155 | template 156 | QVariant QCxxListModel::data(const QModelIndex &index, int role) const 157 | { 158 | if ((ModelDataRole != role) 159 | || !index.isValid() 160 | || (index.model() != this) 161 | || (index.row() >= QList::count())) { 162 | return QVariant(); 163 | } 164 | 165 | return QVariant::fromValue(QList::at(index.row())); 166 | } 167 | 168 | template 169 | QModelIndex QCxxListModel::index(int row, int column, const QModelIndex &parent) const 170 | { 171 | if ((row < 0) 172 | || (column != 0) 173 | || (row >= QList::count()) 174 | || parent.isValid()) { 175 | return QModelIndex(); 176 | } 177 | 178 | return createIndex(row, 0); 179 | } 180 | 181 | template 182 | T QCxxListModel::value(const QModelIndex &index) const 183 | { 184 | QCXXLISTMODEL_ASSERT(index.model() == this); 185 | 186 | return QList::value(index.row()); 187 | } 188 | 189 | template 190 | T QCxxListModel::value(const QModelIndex &index, const T &defaultValue) const 191 | { 192 | QCXXLISTMODEL_ASSERT(index.model() == this); 193 | 194 | return QList::value(index.row(), defaultValue); 195 | } 196 | 197 | template 198 | T QCxxListModel::value(int i) const 199 | { 200 | return QList::value(i); 201 | } 202 | 203 | template 204 | T QCxxListModel::value(int i, const T &defaultValue) const 205 | { 206 | return QList::value(i, defaultValue); 207 | } 208 | 209 | template 210 | void QCxxListModel::append(const QList &l) 211 | { 212 | if (l.isEmpty()) 213 | return; 214 | 215 | int f = QList::count(); 216 | int t = f + l.count() - 1; 217 | beginInsertRows(QModelIndex(), f, t); 218 | QList::append(l); 219 | endInsertRows(); 220 | _q_resetCount(); 221 | } 222 | 223 | template 224 | void QCxxListModel::prepend(const T &t) 225 | { 226 | beginInsertRows(QModelIndex(), 0, 0); 227 | QList::prepend(t); 228 | endInsertRows(); 229 | _q_resetCount(); 230 | } 231 | 232 | template 233 | void QCxxListModel::append(const T &t) 234 | { 235 | int r = QList::count(); 236 | beginInsertRows(QModelIndex(), r, r); 237 | QList::append(t); 238 | endInsertRows(); 239 | _q_resetCount(); 240 | } 241 | 242 | template 243 | void QCxxListModel::push_front(const T &t) 244 | { 245 | beginInsertRows(QModelIndex(), 0, 0); 246 | QList::push_front(t); 247 | endInsertRows(); 248 | _q_resetCount(); 249 | } 250 | 251 | template 252 | void QCxxListModel::push_back(const T &t) 253 | { 254 | int r = QList::count(); 255 | beginInsertRows(QModelIndex(), r, r); 256 | QList::push_back(t); 257 | endInsertRows(); 258 | _q_resetCount(); 259 | } 260 | 261 | template 262 | void QCxxListModel::replace(int i, const T &t) 263 | { 264 | QCXXLISTMODEL_ASSERT(i >= 0); 265 | QCXXLISTMODEL_ASSERT(i < QList::count()); 266 | 267 | QModelIndex x = index(i, 0); 268 | if (!x.isValid()) { 269 | return; 270 | } 271 | 272 | QList::replace(i, t); emit dataChanged(x, x); 273 | } 274 | 275 | template 276 | void QCxxListModel::insert(int i, const T &t) 277 | { 278 | QCXXLISTMODEL_ASSERT(i >= 0); 279 | QCXXLISTMODEL_ASSERT(i <= QList::count()); 280 | 281 | beginInsertRows(QModelIndex(), i, i); 282 | QList::insert(i, t); 283 | endInsertRows(); 284 | _q_resetCount(); 285 | } 286 | 287 | template 288 | bool QCxxListModel::removeOne(const T &t) 289 | { 290 | typename QList::iterator p = QList::begin(); 291 | typename QList::iterator end = QList::end(); 292 | 293 | for (int i = 0; p != end; ++p, ++i) { 294 | if (*p == t) { 295 | beginRemoveRows(QModelIndex(), i, i); 296 | QList::erase(p); 297 | endRemoveRows(); 298 | _q_resetCount(); 299 | 300 | return true; 301 | } 302 | } 303 | 304 | return false; 305 | } 306 | 307 | template 308 | int QCxxListModel::removeAll(const T &t) 309 | { 310 | typename QList::iterator null; 311 | typename QList::iterator f = null; 312 | typename QList::iterator l = null; 313 | typename QList::iterator p = QList::begin(); 314 | typename QList::iterator end = QList::end(); 315 | 316 | int c = 0; 317 | int fpos = 0; 318 | int lpos = 0; 319 | 320 | for (int i = 0; p != end; ++p, ++i) { 321 | if (t == *p) { 322 | if (f == null) { 323 | fpos = i; 324 | f = p; 325 | } 326 | lpos = i; 327 | l = p; 328 | 329 | } else if (f != null) { 330 | beginRemoveRows(QModelIndex(), fpos - c, lpos - c); 331 | QList::erase(f, ++l); 332 | endRemoveRows(); 333 | 334 | c += lpos - fpos + 1; 335 | 336 | f = null; 337 | l = null; 338 | } 339 | } 340 | 341 | if (f != null) { 342 | beginRemoveRows(QModelIndex(), fpos - c, lpos - c); 343 | QList::erase(f, ++l); 344 | endRemoveRows(); 345 | 346 | c += lpos - fpos + 1; 347 | } 348 | 349 | _q_resetCount(); 350 | 351 | return c; 352 | } 353 | 354 | template 355 | void QCxxListModel::pop_front() 356 | { 357 | QCXXLISTMODEL_ASSERT(QList::count() > 0); 358 | 359 | beginRemoveRows(QModelIndex(), 0, 0); 360 | QList::pop_front(); 361 | endRemoveRows(); 362 | _q_resetCount(); 363 | } 364 | 365 | template 366 | void QCxxListModel::pop_back() 367 | { 368 | QCXXLISTMODEL_ASSERT(QList::count() > 0); 369 | 370 | int r = QList::count() - 1; 371 | beginRemoveRows(QModelIndex(), r, r); 372 | QList::pop_back(); 373 | endRemoveRows(); 374 | _q_resetCount(); 375 | } 376 | 377 | template 378 | void QCxxListModel::removeAt(int i) 379 | { 380 | QCXXLISTMODEL_ASSERT(i >= 0); 381 | QCXXLISTMODEL_ASSERT(i < QList::count()); 382 | 383 | beginRemoveRows(QModelIndex(), i, i); 384 | QList::removeAt(i); 385 | endRemoveRows(); 386 | _q_resetCount(); 387 | } 388 | 389 | template 390 | void QCxxListModel::removeFirst() 391 | { 392 | QCXXLISTMODEL_ASSERT(QList::count() > 0); 393 | 394 | beginRemoveRows(QModelIndex(), 0, 0); 395 | QList::removeFirst(); 396 | endRemoveRows(); 397 | _q_resetCount(); 398 | } 399 | 400 | template 401 | void QCxxListModel::removeLast() 402 | { 403 | QCXXLISTMODEL_ASSERT(QList::count() > 0); 404 | 405 | int r = QList::count() - 1; 406 | beginRemoveRows(QModelIndex(), r, r); 407 | QList::removeLast(); 408 | endRemoveRows(); 409 | _q_resetCount(); 410 | } 411 | 412 | template 413 | T QCxxListModel::takeAt(int i) 414 | { 415 | QCXXLISTMODEL_ASSERT(i >= 0); 416 | QCXXLISTMODEL_ASSERT(i < QList::count()); 417 | 418 | beginRemoveRows(QModelIndex(), i, i); 419 | T t = QList::takeAt(i); 420 | endRemoveRows(); 421 | _q_resetCount(); 422 | 423 | return t; 424 | } 425 | 426 | template 427 | T QCxxListModel::takeFirst() 428 | { 429 | QCXXLISTMODEL_ASSERT(QList::count() > 0); 430 | 431 | beginRemoveRows(QModelIndex(), 0, 0); 432 | T t = QList::takeFirst(); 433 | endRemoveRows(); 434 | _q_resetCount(); 435 | 436 | return t; 437 | } 438 | 439 | template 440 | T QCxxListModel::takeLast() 441 | { 442 | QCXXLISTMODEL_ASSERT(QList::count() > 0); 443 | 444 | int r = QList::count() - 1; 445 | beginRemoveRows(QModelIndex(), r, r); 446 | T t = QList::takeLast(); 447 | endRemoveRows(); 448 | _q_resetCount(); 449 | 450 | return t; 451 | } 452 | 453 | template 454 | void QCxxListModel::swap(int i, int j) 455 | { 456 | if (i == j) { 457 | return; 458 | } 459 | 460 | QCXXLISTMODEL_ASSERT(i >= 0); 461 | QCXXLISTMODEL_ASSERT(i < QList::count()); 462 | 463 | QCXXLISTMODEL_ASSERT(j >= 0); 464 | QCXXLISTMODEL_ASSERT(j < QList::count()); 465 | 466 | QList::swap(i, j); 467 | 468 | QModelIndex ii = index(i, 0); emit dataChanged(ii, ii); 469 | QModelIndex ji = index(j, 0); emit dataChanged(ji, ji); 470 | } 471 | 472 | template 473 | void QCxxListModel::swap(QList &l) 474 | { 475 | /* 476 | if (l.d == this->d) { 477 | return *this; 478 | } 479 | */ 480 | 481 | beginResetModel(); 482 | QList::swap(l); 483 | endResetModel(); 484 | _q_resetCount(); 485 | } 486 | 487 | template 488 | void QCxxListModel::move(int from, int to) 489 | { 490 | if (from == to) { 491 | return; 492 | } 493 | 494 | QCXXLISTMODEL_ASSERT(from >= 0); 495 | QCXXLISTMODEL_ASSERT(from < QList::count()); 496 | 497 | QCXXLISTMODEL_ASSERT(to >= 0); 498 | QCXXLISTMODEL_ASSERT(to < QList::count()); 499 | 500 | QModelIndex p; 501 | int t = (from < to) ? (to + 1) : to; 502 | if (beginMoveRows(p, from, from, p, t)) { 503 | QList::move(from, to); 504 | endMoveRows(); 505 | } 506 | } 507 | 508 | template 509 | void QCxxListModel::clear() 510 | { 511 | if (QList::isEmpty()) { 512 | return; 513 | } 514 | 515 | beginResetModel(); 516 | QList::clear(); 517 | endResetModel(); 518 | _q_resetCount(); 519 | } 520 | 521 | template 522 | void QCxxListModel::deleteAll() 523 | { 524 | if (QList::isEmpty()) { 525 | return; 526 | } 527 | 528 | beginResetModel(); 529 | qDeleteAll(ref()); 530 | QList::clear(); 531 | endResetModel(); 532 | _q_resetCount(); 533 | } 534 | 535 | template 536 | QCxxListModel &QCxxListModel::operator=(const QList &l) 537 | { 538 | /* 539 | if (l.d == this->d) { 540 | return *this; 541 | } 542 | */ 543 | 544 | beginResetModel(); 545 | QList tmp(l); 546 | tmp.swap(*this); 547 | endResetModel(); 548 | _q_resetCount(); 549 | 550 | return *this; 551 | } 552 | 553 | #endif // QCXXLISTMODEL_H 554 | -------------------------------------------------------------------------------- /src/QCxxListModel.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD 2 | 3 | HEADERS += \ 4 | $$PWD/QCxxListModel.h \ 5 | $$PWD/QCxxListModel_p.h 6 | SOURCES += \ 7 | $$PWD/QCxxListModel.cpp 8 | -------------------------------------------------------------------------------- /src/QCxxListModel_p.h: -------------------------------------------------------------------------------- 1 | #ifndef QCXXLISTMODEL_P_H 2 | #define QCXXLISTMODEL_P_H 3 | 4 | #include 5 | 6 | #include "QCxxListModel.h" 7 | 8 | namespace Internal { 9 | 10 | class QCxxListModelPrivate 11 | { 12 | Q_DECLARE_PUBLIC(QCxxListModel) 13 | 14 | public: 15 | QCxxListModelPrivate(); 16 | virtual ~QCxxListModelPrivate(); 17 | protected: 18 | QCxxListModel *q_ptr; 19 | 20 | protected: 21 | bool countEnabled; 22 | int count; 23 | }; 24 | 25 | } // namespace Internal 26 | 27 | #endif // QCXXLISTMODEL_P_H 28 | -------------------------------------------------------------------------------- /test/auto/ListModel/ListModel.pro: -------------------------------------------------------------------------------- 1 | QT += testlib 2 | QT -= gui 3 | 4 | CONFIG += qt console warn_on depend_includepath testcase 5 | CONFIG -= app_bundle 6 | 7 | TEMPLATE = app 8 | 9 | msvc { 10 | QMAKE_CXXFLAGS ~= s/-W3/-W4 11 | } 12 | 13 | HEADERS += \ 14 | ListModelTest.h \ 15 | ListModelTester.h \ 16 | ListModelTester_p.h 17 | SOURCES += \ 18 | ListModelTest.cpp \ 19 | ListModelTester.cpp 20 | 21 | include(../../../src/QCxxListModel.pri) 22 | -------------------------------------------------------------------------------- /test/auto/ListModel/ListModelTest.cpp: -------------------------------------------------------------------------------- 1 | #include "ListModelTest.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "QCxxListModel.h" 7 | #include "ListModelTester.h" 8 | 9 | // Macros 10 | 11 | #define InitModel() \ 12 | QCxxListModel model; \ 13 | model.setCountEnabled(true); \ 14 | auto tester = new ListModelTester(&model) 15 | 16 | #define VerifyCountSignal(value) QVERIFY(tester->count() == value) 17 | #define VerifyChangedSize(value) QVERIFY(tester->changedSize() == value) 18 | #define VerifyRowsChanged(i, from, to) QVERIFY(tester->isChanged(i, from, to)) 19 | 20 | ListModelTest::ListModelTest() 21 | { 22 | for (int i = 0; i < 100; ++i) { 23 | objects.append(new QObject(this)); 24 | } 25 | } 26 | 27 | void ListModelTest::appendListCase() 28 | { 29 | InitModel(); 30 | 31 | QObjectList list1 { objects[0], objects[1] }; 32 | QObjectList list2 { objects[2], objects[3] }; 33 | 34 | model.append(list1); 35 | VerifyCountSignal(1); 36 | model.append(list2); 37 | VerifyCountSignal(2); 38 | 39 | QVERIFY(model.count() == 4); 40 | 41 | QVERIFY(model.value(0) == objects[0]); 42 | QVERIFY(model.value(1) == objects[1]); 43 | QVERIFY(model.value(2) == objects[2]); 44 | QVERIFY(model.value(3) == objects[3]); 45 | } 46 | 47 | void ListModelTest::prependCase() 48 | { 49 | InitModel(); 50 | 51 | model.prepend(objects[0]); 52 | VerifyCountSignal(1); 53 | model.prepend(objects[1]); 54 | VerifyCountSignal(2); 55 | model.prepend(objects[2]); 56 | VerifyCountSignal(3); 57 | 58 | QVERIFY(model.count() == 3); 59 | 60 | QVERIFY(model.value(0) == objects[2]); 61 | QVERIFY(model.value(1) == objects[1]); 62 | QVERIFY(model.value(2) == objects[0]); 63 | } 64 | 65 | void ListModelTest::appendCase() 66 | { 67 | InitModel(); 68 | 69 | model.append(objects[0]); 70 | VerifyCountSignal(1); 71 | model.append(objects[1]); 72 | VerifyCountSignal(2); 73 | model.append(objects[2]); 74 | VerifyCountSignal(3); 75 | 76 | QVERIFY(model.count() == 3); 77 | 78 | QVERIFY(model.value(0) == objects[0]); 79 | QVERIFY(model.value(1) == objects[1]); 80 | QVERIFY(model.value(2) == objects[2]); 81 | } 82 | 83 | void ListModelTest::push_frontCase() 84 | { 85 | InitModel(); 86 | 87 | model.push_front(objects[0]); 88 | VerifyCountSignal(1); 89 | model.push_front(objects[1]); 90 | VerifyCountSignal(2); 91 | model.push_front(objects[2]); 92 | VerifyCountSignal(3); 93 | 94 | QVERIFY(model.count() == 3); 95 | 96 | QVERIFY(model.value(0) == objects[2]); 97 | QVERIFY(model.value(1) == objects[1]); 98 | QVERIFY(model.value(2) == objects[0]); 99 | } 100 | 101 | void ListModelTest::push_backCase() 102 | { 103 | InitModel(); 104 | 105 | model.push_back(objects[0]); 106 | VerifyCountSignal(1); 107 | model.push_back(objects[1]); 108 | VerifyCountSignal(2); 109 | model.push_back(objects[2]); 110 | VerifyCountSignal(3); 111 | 112 | QVERIFY(model.count() == 3); 113 | 114 | QVERIFY(model.value(0) == objects[0]); 115 | QVERIFY(model.value(1) == objects[1]); 116 | QVERIFY(model.value(2) == objects[2]); 117 | } 118 | 119 | void ListModelTest::replaceCase() 120 | { 121 | InitModel(); 122 | 123 | for (int i = 0; i < 3; ++i) { 124 | model.append(objects[i]); 125 | } 126 | tester->resetCount(); 127 | 128 | model.replace(0, objects[3]); 129 | VerifyCountSignal(0); 130 | QVERIFY(model.value(0) == objects[3]); 131 | model.replace(1, objects[4]); 132 | VerifyCountSignal(0); 133 | QVERIFY(model.value(1) == objects[4]); 134 | model.replace(2, objects[5]); 135 | VerifyCountSignal(0); 136 | QVERIFY(model.value(2) == objects[5]); 137 | 138 | QVERIFY(model.count() == 3); 139 | 140 | QVERIFY(model.value(0) == objects[3]); 141 | QVERIFY(model.value(1) == objects[4]); 142 | QVERIFY(model.value(2) == objects[5]); 143 | } 144 | 145 | void ListModelTest::insertCase() 146 | { 147 | InitModel(); 148 | 149 | for (int i = 0; i < 3; ++i) { 150 | model.append(objects[i]); 151 | } 152 | tester->resetCount(); 153 | 154 | model.insert(0, objects[3]); 155 | VerifyCountSignal(1); 156 | QVERIFY(model.value(0) == objects[3]); 157 | model.insert(2, objects[4]); 158 | VerifyCountSignal(2); 159 | QVERIFY(model.value(2) == objects[4]); 160 | model.insert(4, objects[5]); 161 | VerifyCountSignal(3); 162 | QVERIFY(model.value(4) == objects[5]); 163 | 164 | QVERIFY(model.count() == 6); 165 | 166 | QVERIFY(model.value(0) == objects[3]); 167 | QVERIFY(model.value(1) == objects[0]); 168 | QVERIFY(model.value(2) == objects[4]); 169 | QVERIFY(model.value(3) == objects[1]); 170 | QVERIFY(model.value(4) == objects[5]); 171 | QVERIFY(model.value(5) == objects[2]); 172 | } 173 | 174 | void ListModelTest::removeOneCase() 175 | { 176 | InitModel(); 177 | 178 | for (int i = 0; i < 6; ++i) { 179 | model.append(objects[i]); 180 | model.append(objects[i]); 181 | } 182 | tester->resetCount(); 183 | 184 | model.removeOne(objects[0]); 185 | VerifyCountSignal(1); 186 | model.removeOne(objects[2]); 187 | VerifyCountSignal(2); 188 | model.removeOne(objects[4]); 189 | VerifyCountSignal(3); 190 | 191 | QVERIFY(model.count() == 9); 192 | 193 | QVERIFY(model.value(0) == objects[0]); 194 | QVERIFY(model.value(1) == objects[1]); 195 | QVERIFY(model.value(2) == objects[1]); 196 | QVERIFY(model.value(3) == objects[2]); 197 | QVERIFY(model.value(4) == objects[3]); 198 | QVERIFY(model.value(5) == objects[3]); 199 | QVERIFY(model.value(6) == objects[4]); 200 | QVERIFY(model.value(7) == objects[5]); 201 | QVERIFY(model.value(8) == objects[5]); 202 | } 203 | 204 | void ListModelTest::removeAllCase() 205 | { 206 | InitModel(); 207 | 208 | for (int i = 0; i < 6; ++i) { 209 | model.append(objects[i]); 210 | } 211 | for (int i = 0; i < 6; ++i) { 212 | model.append(objects[i]); 213 | model.append(objects[i]); 214 | } 215 | for (int i = 0; i < 6; ++i) { 216 | model.append(objects[i]); 217 | model.append(objects[i]); 218 | model.append(objects[i]); 219 | } 220 | tester->resetCount(); 221 | 222 | model.removeAll(objects[0]); 223 | VerifyCountSignal(1); 224 | model.removeAll(objects[2]); 225 | VerifyCountSignal(2); 226 | model.removeAll(objects[4]); 227 | VerifyCountSignal(3); 228 | 229 | QVERIFY(model.count() == 18); 230 | 231 | QVERIFY(model.value( 0) == objects[1]); 232 | QVERIFY(model.value( 1) == objects[3]); 233 | QVERIFY(model.value( 2) == objects[5]); 234 | QVERIFY(model.value( 3) == objects[1]); 235 | QVERIFY(model.value( 4) == objects[1]); 236 | QVERIFY(model.value( 5) == objects[3]); 237 | QVERIFY(model.value( 6) == objects[3]); 238 | QVERIFY(model.value( 7) == objects[5]); 239 | QVERIFY(model.value( 8) == objects[5]); 240 | QVERIFY(model.value( 9) == objects[1]); 241 | QVERIFY(model.value(10) == objects[1]); 242 | QVERIFY(model.value(11) == objects[1]); 243 | QVERIFY(model.value(12) == objects[3]); 244 | QVERIFY(model.value(13) == objects[3]); 245 | QVERIFY(model.value(14) == objects[3]); 246 | QVERIFY(model.value(15) == objects[5]); 247 | QVERIFY(model.value(16) == objects[5]); 248 | QVERIFY(model.value(17) == objects[5]); 249 | } 250 | 251 | void ListModelTest::pop_frontCase() 252 | { 253 | InitModel(); 254 | 255 | for (int i = 0; i < 3; ++i) { 256 | model.append(objects[i]); 257 | } 258 | tester->resetCount(); 259 | 260 | model.pop_front(); 261 | VerifyCountSignal(1); 262 | QVERIFY(model.value(0) == objects[1]); 263 | QVERIFY(model.value(1) == objects[2]); 264 | model.pop_front(); 265 | VerifyCountSignal(2); 266 | QVERIFY(model.value(0) == objects[2]); 267 | model.pop_front(); 268 | VerifyCountSignal(3); 269 | 270 | QVERIFY(model.count() == 0); 271 | } 272 | 273 | void ListModelTest::pop_backCase() 274 | { 275 | InitModel(); 276 | 277 | for (int i = 0; i < 3; ++i) { 278 | model.append(objects[i]); 279 | } 280 | tester->resetCount(); 281 | 282 | model.pop_back(); 283 | VerifyCountSignal(1); 284 | QVERIFY(model.value(0) == objects[0]); 285 | QVERIFY(model.value(1) == objects[1]); 286 | model.pop_back(); 287 | VerifyCountSignal(2); 288 | QVERIFY(model.value(0) == objects[0]); 289 | model.pop_back(); 290 | VerifyCountSignal(3); 291 | 292 | QVERIFY(model.count() == 0); 293 | } 294 | 295 | void ListModelTest::removeAtCase() 296 | { 297 | InitModel(); 298 | 299 | for (int i = 0; i < 6; ++i) { 300 | model.append(objects[i]); 301 | } 302 | tester->resetCount(); 303 | 304 | model.removeAt(0); 305 | VerifyCountSignal(1); 306 | model.removeAt(1); 307 | VerifyCountSignal(2); 308 | model.removeAt(2); 309 | VerifyCountSignal(3); 310 | 311 | QVERIFY(model.count() == 3); 312 | 313 | QVERIFY(model.value(0) == objects[1]); 314 | QVERIFY(model.value(1) == objects[3]); 315 | QVERIFY(model.value(2) == objects[5]); 316 | } 317 | 318 | void ListModelTest::removeFirstCase() 319 | { 320 | InitModel(); 321 | 322 | for (int i = 0; i < 3; ++i) { 323 | model.append(objects[i]); 324 | } 325 | tester->resetCount(); 326 | 327 | model.removeFirst(); 328 | VerifyCountSignal(1); 329 | QVERIFY(model.value(0) == objects[1]); 330 | QVERIFY(model.value(1) == objects[2]); 331 | model.removeFirst(); 332 | VerifyCountSignal(2); 333 | QVERIFY(model.value(0) == objects[2]); 334 | model.removeFirst(); 335 | VerifyCountSignal(3); 336 | 337 | QVERIFY(model.count() == 0); 338 | } 339 | 340 | void ListModelTest::removeLastCase() 341 | { 342 | InitModel(); 343 | 344 | for (int i = 0; i < 3; ++i) { 345 | model.append(objects[i]); 346 | } 347 | tester->resetCount(); 348 | 349 | model.removeLast(); 350 | VerifyCountSignal(1); 351 | QVERIFY(model.value(0) == objects[0]); 352 | QVERIFY(model.value(1) == objects[1]); 353 | model.removeLast(); 354 | VerifyCountSignal(2); 355 | QVERIFY(model.value(0) == objects[0]); 356 | model.removeLast(); 357 | VerifyCountSignal(3); 358 | 359 | QVERIFY(model.count() == 0); 360 | } 361 | 362 | void ListModelTest::takeAtCase() 363 | { 364 | InitModel(); 365 | 366 | for (int i = 0; i < 6; ++i) { 367 | model.append(objects[i]); 368 | } 369 | tester->resetCount(); 370 | 371 | QVERIFY(model.takeAt(0) == objects[0]); 372 | VerifyCountSignal(1); 373 | QVERIFY(model.takeAt(1) == objects[2]); 374 | VerifyCountSignal(2); 375 | QVERIFY(model.takeAt(2) == objects[4]); 376 | VerifyCountSignal(3); 377 | 378 | QVERIFY(model.count() == 3); 379 | 380 | QVERIFY(model.value(0) == objects[1]); 381 | QVERIFY(model.value(1) == objects[3]); 382 | QVERIFY(model.value(2) == objects[5]); 383 | } 384 | 385 | void ListModelTest::takeFirstCase() 386 | { 387 | InitModel(); 388 | 389 | for (int i = 0; i < 3; ++i) { 390 | model.append(objects[i]); 391 | } 392 | tester->resetCount(); 393 | 394 | QVERIFY(model.takeFirst() == objects[0]); 395 | VerifyCountSignal(1); 396 | QVERIFY(model.value(0) == objects[1]); 397 | QVERIFY(model.value(1) == objects[2]); 398 | 399 | QVERIFY(model.takeFirst() == objects[1]); 400 | VerifyCountSignal(2); 401 | QVERIFY(model.value(0) == objects[2]); 402 | 403 | QVERIFY(model.takeFirst() == objects[2]); 404 | VerifyCountSignal(3); 405 | 406 | QVERIFY(model.count() == 0); 407 | } 408 | 409 | void ListModelTest::takeLastCase() 410 | { 411 | InitModel(); 412 | 413 | for (int i = 0; i < 3; ++i) { 414 | model.append(objects[i]); 415 | } 416 | tester->resetCount(); 417 | 418 | QVERIFY(model.takeLast() == objects[2]); 419 | VerifyCountSignal(1); 420 | QVERIFY(model.value(0) == objects[0]); 421 | QVERIFY(model.value(1) == objects[1]); 422 | 423 | QVERIFY(model.takeLast() == objects[1]); 424 | VerifyCountSignal(2); 425 | QVERIFY(model.value(0) == objects[0]); 426 | 427 | QVERIFY(model.takeLast() == objects[0]); 428 | VerifyCountSignal(3); 429 | 430 | QVERIFY(model.count() == 0); 431 | } 432 | 433 | void ListModelTest::swapCase() 434 | { 435 | InitModel(); 436 | 437 | for (int i = 0; i < 6; ++i) { 438 | model.append(objects[i]); 439 | } 440 | tester->resetCount(); 441 | 442 | model.swap(0, 1); 443 | QVERIFY(model.value(0) == objects[1]); 444 | QVERIFY(model.value(1) == objects[0]); 445 | model.swap(1, 2); 446 | QVERIFY(model.value(1) == objects[2]); 447 | QVERIFY(model.value(2) == objects[0]); 448 | model.swap(4, 5); 449 | QVERIFY(model.value(4) == objects[5]); 450 | QVERIFY(model.value(5) == objects[4]); 451 | 452 | VerifyCountSignal(0); 453 | 454 | QVERIFY(model.value(0) == objects[1]); 455 | QVERIFY(model.value(1) == objects[2]); 456 | QVERIFY(model.value(2) == objects[0]); 457 | QVERIFY(model.value(3) == objects[3]); 458 | QVERIFY(model.value(4) == objects[5]); 459 | QVERIFY(model.value(5) == objects[4]); 460 | 461 | QVERIFY(model.count() == 6); 462 | } 463 | 464 | void ListModelTest::swapListCase() 465 | { 466 | InitModel(); 467 | 468 | QObjectList list { objects[0], objects[1], objects[2] }; 469 | 470 | for (int i = 0; i < 6; ++i) { 471 | model.append(objects[i]); 472 | } 473 | tester->resetCount(); 474 | 475 | model.swap(list); 476 | 477 | VerifyCountSignal(1); 478 | 479 | QVERIFY(list.value(0) == objects[0]); 480 | QVERIFY(list.value(1) == objects[1]); 481 | QVERIFY(list.value(2) == objects[2]); 482 | QVERIFY(list.value(3) == objects[3]); 483 | QVERIFY(list.value(4) == objects[4]); 484 | QVERIFY(list.value(5) == objects[5]); 485 | 486 | QVERIFY(model.value(0) == objects[0]); 487 | QVERIFY(model.value(1) == objects[1]); 488 | QVERIFY(model.value(2) == objects[2]); 489 | 490 | QVERIFY(model.count() == 3); 491 | } 492 | 493 | void ListModelTest::moveCase() 494 | { 495 | InitModel(); 496 | 497 | for (int i = 0; i < 6; ++i) { 498 | model.append(objects[i]); 499 | } 500 | tester->resetCount(); 501 | 502 | model.move(0, 1); 503 | QVERIFY(model.value(0) == objects[1]); 504 | QVERIFY(model.value(1) == objects[0]); 505 | model.move(1, 2); 506 | QVERIFY(model.value(1) == objects[2]); 507 | QVERIFY(model.value(2) == objects[0]); 508 | model.move(4, 5); 509 | QVERIFY(model.value(4) == objects[5]); 510 | QVERIFY(model.value(5) == objects[4]); 511 | 512 | model.move(5, 4); 513 | QVERIFY(model.value(4) == objects[4]); 514 | QVERIFY(model.value(5) == objects[5]); 515 | model.move(2, 1); 516 | QVERIFY(model.value(1) == objects[0]); 517 | QVERIFY(model.value(2) == objects[2]); 518 | model.move(1, 0); 519 | QVERIFY(model.value(0) == objects[0]); 520 | QVERIFY(model.value(1) == objects[1]); 521 | 522 | VerifyCountSignal(0); 523 | 524 | QVERIFY(model.value(0) == objects[0]); 525 | QVERIFY(model.value(1) == objects[1]); 526 | QVERIFY(model.value(2) == objects[2]); 527 | QVERIFY(model.value(3) == objects[3]); 528 | QVERIFY(model.value(4) == objects[4]); 529 | QVERIFY(model.value(5) == objects[5]); 530 | 531 | QVERIFY(model.count() == 6); 532 | } 533 | 534 | void ListModelTest::clearCase() 535 | { 536 | InitModel(); 537 | 538 | int destroies = 0; 539 | for (int i = 0; i < 6; ++i) { 540 | model.append(objects[i]); 541 | connect(objects[i], &QObject::destroyed, 542 | this, [&] { ++destroies; }); 543 | } 544 | tester->resetCount(); 545 | 546 | model.clear(); 547 | VerifyCountSignal(1); 548 | 549 | QTRY_COMPARE(destroies, 0); 550 | QVERIFY(model.count() == 0); 551 | } 552 | 553 | void ListModelTest::deleteAllCase() 554 | { 555 | InitModel(); 556 | 557 | int destroies = 0; 558 | for (int i = 0; i < 6; ++i) { 559 | QObject *o = new QObject(this); 560 | model.append(o); 561 | connect(o, &QObject::destroyed, 562 | this, [&] { ++destroies; }); 563 | } 564 | tester->resetCount(); 565 | 566 | model.deleteAll(); 567 | VerifyCountSignal(1); 568 | 569 | QTRY_COMPARE(destroies, 6); 570 | QVERIFY(model.count() == 0); 571 | } 572 | 573 | void ListModelTest::operatorAssignmentCase() 574 | { 575 | InitModel(); 576 | 577 | QObjectList list { objects[0], objects[1], objects[2] }; 578 | 579 | for (int i = 0; i < 6; ++i) { 580 | model.append(objects[i]); 581 | } 582 | tester->resetCount(); 583 | 584 | model = list; 585 | 586 | VerifyCountSignal(1); 587 | 588 | QVERIFY(list.value(0) == objects[0]); 589 | QVERIFY(list.value(1) == objects[1]); 590 | QVERIFY(list.value(2) == objects[2]); 591 | 592 | QVERIFY(model.value(0) == objects[0]); 593 | QVERIFY(model.value(1) == objects[1]); 594 | QVERIFY(model.value(2) == objects[2]); 595 | 596 | QVERIFY(model.count() == 3); 597 | } 598 | 599 | void ListModelTest::operatorAdditionCase() 600 | { 601 | InitModel(); 602 | 603 | QObjectList list { objects[0], objects[1], objects[2] }; 604 | 605 | for (int i = 0; i < 3; ++i) { 606 | model.append(objects[i]); 607 | } 608 | tester->resetCount(); 609 | 610 | list = model + list; 611 | 612 | VerifyCountSignal(0); 613 | 614 | QVERIFY(list.value(0) == objects[0]); 615 | QVERIFY(list.value(1) == objects[1]); 616 | QVERIFY(list.value(2) == objects[2]); 617 | QVERIFY(list.value(3) == objects[0]); 618 | QVERIFY(list.value(4) == objects[1]); 619 | QVERIFY(list.value(5) == objects[2]); 620 | 621 | QVERIFY(list.count() == 6); 622 | } 623 | 624 | void ListModelTest::operatorEqualToCase() 625 | { 626 | InitModel(); 627 | 628 | QObjectList list { objects[0], objects[1], objects[2] }; 629 | 630 | for (int i = 0; i < 3; ++i) { 631 | model.append(objects[i]); 632 | } 633 | tester->resetCount(); 634 | 635 | QVERIFY(model == list); 636 | VerifyCountSignal(0); 637 | } 638 | 639 | void ListModelTest::operatorNotEqualCase() 640 | { 641 | InitModel(); 642 | 643 | QObjectList list { objects[1], objects[1], objects[2] }; 644 | 645 | for (int i = 0; i < 3; ++i) { 646 | model.append(objects[i]); 647 | } 648 | tester->resetCount(); 649 | 650 | QVERIFY(model != list); 651 | VerifyCountSignal(0); 652 | } 653 | 654 | void ListModelTest::operatorAdditionAssignmentListCase() 655 | { 656 | InitModel(); 657 | 658 | QObjectList list { objects[0], objects[1], objects[2] }; 659 | 660 | model += list; 661 | VerifyCountSignal(1); 662 | model += list; 663 | VerifyCountSignal(2); 664 | 665 | QVERIFY(model.value(0) == objects[0]); 666 | QVERIFY(model.value(1) == objects[1]); 667 | QVERIFY(model.value(2) == objects[2]); 668 | QVERIFY(model.value(3) == objects[0]); 669 | QVERIFY(model.value(4) == objects[1]); 670 | QVERIFY(model.value(5) == objects[2]); 671 | 672 | QVERIFY(model.count() == 6); 673 | } 674 | 675 | void ListModelTest::operatorLeftShiftListCase() 676 | { 677 | InitModel(); 678 | 679 | QObjectList list { objects[0], objects[1], objects[2] }; 680 | 681 | model << list; 682 | VerifyCountSignal(1); 683 | model << list; 684 | VerifyCountSignal(2); 685 | 686 | QVERIFY(model.value(0) == objects[0]); 687 | QVERIFY(model.value(1) == objects[1]); 688 | QVERIFY(model.value(2) == objects[2]); 689 | QVERIFY(model.value(3) == objects[0]); 690 | QVERIFY(model.value(4) == objects[1]); 691 | QVERIFY(model.value(5) == objects[2]); 692 | 693 | QVERIFY(model.count() == 6); 694 | } 695 | 696 | void ListModelTest::operatorAdditionAssignmentCase() 697 | { 698 | InitModel(); 699 | 700 | model += objects[0]; 701 | VerifyCountSignal(1); 702 | model += objects[1]; 703 | VerifyCountSignal(2); 704 | 705 | QVERIFY(model.value(0) == objects[0]); 706 | QVERIFY(model.value(1) == objects[1]); 707 | 708 | QVERIFY(model.count() == 2); 709 | } 710 | 711 | void ListModelTest::operatorLeftShiftCase() 712 | { 713 | InitModel(); 714 | 715 | model << objects[0]; 716 | VerifyCountSignal(1); 717 | model << objects[1]; 718 | VerifyCountSignal(2); 719 | 720 | QVERIFY(model.value(0) == objects[0]); 721 | QVERIFY(model.value(1) == objects[1]); 722 | 723 | QVERIFY(model.count() == 2); 724 | } 725 | 726 | void ListModelTest::modelMethodsCase() 727 | { 728 | InitModel(); 729 | Q_UNUSED(tester); 730 | 731 | QObject *o = (QObject *)1; 732 | 733 | for (int i = 0; i < 3; ++i) { 734 | model.append(objects[i]); 735 | } 736 | model.append(nullptr); 737 | model.append(o); 738 | 739 | QModelIndex index0 = model.index(0, 0); 740 | QModelIndex index1 = model.index(1, 0); 741 | QModelIndex index2 = model.index(2, 0); 742 | QModelIndex index3 = model.index(3, 0); 743 | QModelIndex index4 = model.index(4, 0); 744 | QModelIndex index5 = model.index(5, 0); 745 | 746 | QVERIFY(index0.row() == 0); 747 | QVERIFY(index1.row() == 1); 748 | QVERIFY(index2.row() == 2); 749 | QVERIFY(index3.row() == 3); 750 | QVERIFY(index4.row() == 4); 751 | QVERIFY(index5.row() ==-1); 752 | 753 | QVERIFY(index0.column() == 0); 754 | QVERIFY(index1.column() == 0); 755 | QVERIFY(index2.column() == 0); 756 | QVERIFY(index3.column() == 0); 757 | QVERIFY(index4.column() == 0); 758 | QVERIFY(index5.column() ==-1); 759 | 760 | QVERIFY(model.value(index0) == objects[0]); 761 | QVERIFY(model.value(index1) == objects[1]); 762 | QVERIFY(model.value(index2) == objects[2]); 763 | QVERIFY(model.value(index3) == nullptr); 764 | QVERIFY(model.value(index4) == o); 765 | QVERIFY(model.value(index5) == nullptr); 766 | 767 | QVERIFY(model.value(index0, nullptr) == objects[0]); 768 | QVERIFY(model.value(index1, nullptr) == objects[1]); 769 | QVERIFY(model.value(index2, nullptr) == objects[2]); 770 | QVERIFY(model.value(index3, nullptr) == nullptr); 771 | QVERIFY(model.value(index4, nullptr) == o); 772 | QVERIFY(model.value(index5, nullptr) == nullptr); 773 | 774 | QVERIFY(model.value(0) == objects[0]); 775 | QVERIFY(model.value(1) == objects[1]); 776 | QVERIFY(model.value(2) == objects[2]); 777 | QVERIFY(model.value(3) == nullptr); 778 | QVERIFY(model.value(4) == o); 779 | QVERIFY(model.value(5) == nullptr); 780 | 781 | QVERIFY(model.value(0, nullptr) == objects[0]); 782 | QVERIFY(model.value(1, nullptr) == objects[1]); 783 | QVERIFY(model.value(2, nullptr) == objects[2]); 784 | QVERIFY(model.value(3, nullptr) == nullptr); 785 | QVERIFY(model.value(4, nullptr) == o); 786 | QVERIFY(model.value(5, nullptr) == nullptr); 787 | } 788 | 789 | QTEST_APPLESS_MAIN(ListModelTest) 790 | -------------------------------------------------------------------------------- /test/auto/ListModel/ListModelTest.h: -------------------------------------------------------------------------------- 1 | #ifndef LISTMODELTEST_H 2 | #define LISTMODELTEST_H 3 | 4 | #include 5 | 6 | class ListModelTest : public QObject 7 | { 8 | Q_OBJECT 9 | 10 | public: 11 | ListModelTest(); 12 | 13 | private slots: 14 | void appendListCase(); 15 | 16 | void prependCase(); 17 | void appendCase(); 18 | 19 | void push_frontCase(); 20 | void push_backCase(); 21 | 22 | void replaceCase(); 23 | void insertCase(); 24 | 25 | void removeOneCase(); 26 | void removeAllCase(); 27 | 28 | void pop_frontCase(); 29 | void pop_backCase(); 30 | 31 | void removeAtCase(); 32 | void removeFirstCase(); 33 | void removeLastCase(); 34 | 35 | void takeAtCase(); 36 | void takeFirstCase(); 37 | void takeLastCase(); 38 | 39 | void swapCase(); 40 | void swapListCase(); 41 | void moveCase(); 42 | 43 | void clearCase(); 44 | 45 | private slots: // Extra methods 46 | void deleteAllCase(); 47 | 48 | private slots: // Operators 49 | void operatorAssignmentCase(); 50 | void operatorAdditionCase(); 51 | 52 | void operatorEqualToCase(); 53 | void operatorNotEqualCase(); 54 | 55 | void operatorAdditionAssignmentListCase(); 56 | void operatorLeftShiftListCase(); 57 | 58 | void operatorAdditionAssignmentCase(); 59 | void operatorLeftShiftCase(); 60 | 61 | private slots: 62 | void modelMethodsCase(); 63 | 64 | private: 65 | QObjectList objects; 66 | }; 67 | 68 | #endif // LISTMODELTEST_H 69 | -------------------------------------------------------------------------------- /test/auto/ListModel/ListModelTester.cpp: -------------------------------------------------------------------------------- 1 | #include "ListModelTester.h" 2 | #include "ListModelTester_p.h" 3 | 4 | #include 5 | 6 | // class ListModelTester 7 | 8 | ListModelTester::ListModelTester(Internal::QCxxListModel *model, QObject *parent) 9 | : QObject(parent ? parent : model) 10 | , d_ptr(new ListModelTesterPrivate()) 11 | { 12 | d_ptr->q_ptr = this; 13 | Q_CHECK_PTR(model); 14 | 15 | new QAbstractItemModelTester(model, this); 16 | 17 | connect(model, &Internal::QCxxListModel::countChanged, 18 | this, &ListModelTester::onCountChanged); 19 | connect(model, &Internal::QCxxListModel::dataChanged, 20 | this, &ListModelTester::onDataChanged); 21 | } 22 | 23 | ListModelTester::~ListModelTester() 24 | { 25 | } 26 | 27 | int ListModelTester::count() const 28 | { 29 | Q_D(const ListModelTester); 30 | 31 | return d->count; 32 | } 33 | 34 | void ListModelTester::resetCount() 35 | { 36 | Q_D(ListModelTester); 37 | 38 | d->count = 0; 39 | } 40 | 41 | void ListModelTester::onCountChanged() 42 | { 43 | Q_D(ListModelTester); 44 | 45 | ++d->count; 46 | } 47 | 48 | int ListModelTester::changedSize() const 49 | { 50 | Q_D(const ListModelTester); 51 | 52 | return d->changedList.count(); 53 | } 54 | 55 | bool ListModelTester::isChanged(int index, int from, int to) const 56 | { 57 | Q_D(const ListModelTester); 58 | 59 | ListModelChanged changed = d->changedList.at(index); 60 | return ((from == changed.first) && (to == changed.second)); 61 | } 62 | 63 | void ListModelTester::resetChangedList() 64 | { 65 | Q_D(ListModelTester); 66 | 67 | d->changedList.clear(); 68 | } 69 | 70 | void ListModelTester::onDataChanged(const QModelIndex &tl, const QModelIndex &br, const QVector &roles) 71 | { 72 | Q_D(ListModelTester); 73 | 74 | Q_UNUSED(roles); 75 | 76 | Q_ASSERT(tl.column() == 0); 77 | Q_ASSERT(br.column() == 0); 78 | 79 | d->changedList.append(ListModelChanged(tl.row(), br.row())); 80 | } 81 | 82 | // class ListModelTesterPrivate 83 | 84 | ListModelTesterPrivate::ListModelTesterPrivate() 85 | : q_ptr(nullptr) 86 | , model(nullptr) 87 | , count(0) 88 | { 89 | } 90 | 91 | ListModelTesterPrivate::~ListModelTesterPrivate() 92 | { 93 | } 94 | -------------------------------------------------------------------------------- /test/auto/ListModel/ListModelTester.h: -------------------------------------------------------------------------------- 1 | #ifndef LISTMODELTESTER_H 2 | #define LISTMODELTESTER_H 3 | 4 | #include 5 | 6 | #include "QCxxListModel.h" 7 | 8 | class ListModelTesterPrivate; 9 | class ListModelTester : public QObject 10 | { 11 | Q_OBJECT 12 | Q_DECLARE_PRIVATE(ListModelTester) 13 | 14 | public: 15 | ListModelTester(Internal::QCxxListModel *model, QObject *parent = nullptr); 16 | protected: 17 | QScopedPointer d_ptr; 18 | public: 19 | virtual ~ListModelTester(); 20 | 21 | public: 22 | int count() const; 23 | 24 | void resetCount(); 25 | 26 | private: 27 | void onCountChanged(); 28 | 29 | public: 30 | int changedSize() const; 31 | bool isChanged(int index, int from, int to) const; 32 | void resetChangedList(); 33 | 34 | private: 35 | void onDataChanged(const QModelIndex &tl, const QModelIndex &br, const QVector &roles); 36 | }; 37 | 38 | #endif // LISTMODELTESTER_H 39 | -------------------------------------------------------------------------------- /test/auto/ListModel/ListModelTester_p.h: -------------------------------------------------------------------------------- 1 | #ifndef LISTMODELTESTER_P_H 2 | #define LISTMODELTESTER_P_H 3 | 4 | #include 5 | 6 | #include "ListModelTester.h" 7 | 8 | typedef QPair ListModelChanged; 9 | 10 | class ListModelTesterPrivate 11 | { 12 | Q_DECLARE_PUBLIC(ListModelTester) 13 | 14 | public: 15 | ListModelTesterPrivate(); 16 | virtual ~ListModelTesterPrivate(); 17 | protected: 18 | ListModelTester *q_ptr; 19 | 20 | protected: 21 | Internal::QCxxListModel *model; 22 | QList changedList; 23 | int count; 24 | }; 25 | 26 | #endif // LISTMODELTESTER_P_H 27 | -------------------------------------------------------------------------------- /test/auto/auto.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += \ 4 | ListModel 5 | -------------------------------------------------------------------------------- /test/demo/DemoCore.cpp: -------------------------------------------------------------------------------- 1 | #include "DemoCore.h" 2 | #include "DemoCore_p.h" 3 | 4 | #include 5 | 6 | // class DemoCore 7 | 8 | DemoCore::DemoCore(QObject *parent) 9 | : QObject(parent) 10 | , d_ptr(new DemoCorePrivate()) 11 | { 12 | d_ptr->q_ptr = this; 13 | Q_D(DemoCore); 14 | 15 | for (int i = 0; i < 3; ++i) { 16 | DemoNode *node = new DemoNode(this); 17 | node->setText(QString::number(i)); 18 | d->nodes.append(node); 19 | 20 | d->points.append(QPointF(i * 100, i * 120)); 21 | } 22 | 23 | QTimer *timer = new QTimer(this); 24 | timer->setSingleShot(false); 25 | timer->setInterval(500); 26 | connect(timer, &QTimer::timeout, 27 | this, [d] { 28 | int x = qrand() % 200; 29 | int y = qrand() % 200; 30 | 31 | d->points.replace(qrand() % 3, QPointF(x, y)); 32 | }); 33 | timer->start(); 34 | } 35 | 36 | DemoCore::~DemoCore() 37 | { 38 | } 39 | 40 | QAbstractListModel *DemoCore::nodes() const 41 | { 42 | Q_D(const DemoCore); 43 | 44 | return const_cast *>(&d->nodes); 45 | } 46 | 47 | QAbstractListModel *DemoCore::points() const 48 | { 49 | Q_D(const DemoCore); 50 | 51 | return const_cast *>(&d->points); 52 | } 53 | 54 | void DemoCore::move(int from, int to) 55 | { 56 | Q_D(DemoCore); 57 | 58 | d->nodes.move(from, to); 59 | } 60 | 61 | // class DemoCorePrivate 62 | 63 | DemoCorePrivate::DemoCorePrivate() 64 | : q_ptr(nullptr) 65 | { 66 | } 67 | 68 | DemoCorePrivate::~DemoCorePrivate() 69 | { 70 | } 71 | -------------------------------------------------------------------------------- /test/demo/DemoCore.h: -------------------------------------------------------------------------------- 1 | #ifndef DEMOCORE_H 2 | #define DEMOCORE_H 3 | 4 | #include 5 | #include 6 | 7 | class DemoNode; 8 | class DemoCorePrivate; 9 | class DemoCore : public QObject 10 | { 11 | Q_OBJECT 12 | Q_DECLARE_PRIVATE(DemoCore) 13 | 14 | Q_PROPERTY(QAbstractListModel *nodes READ nodes CONSTANT) 15 | Q_PROPERTY(QAbstractListModel *points READ points CONSTANT) 16 | 17 | public: 18 | explicit DemoCore(QObject *parent = nullptr); 19 | virtual ~DemoCore(); 20 | protected: 21 | QScopedPointer d_ptr; 22 | 23 | public: 24 | QAbstractListModel *nodes() const; 25 | QAbstractListModel *points() const; 26 | 27 | public slots: 28 | void move(int from, int to); 29 | }; 30 | 31 | #endif // DEMOCORE_H 32 | -------------------------------------------------------------------------------- /test/demo/DemoCore_p.h: -------------------------------------------------------------------------------- 1 | #ifndef DEMOCORE_P_H 2 | #define DEMOCORE_P_H 3 | 4 | #include "DemoCore.h" 5 | #include "QCxxListModel.h" 6 | #include "DemoNode.h" 7 | 8 | class DemoCorePrivate 9 | { 10 | Q_DECLARE_PUBLIC(DemoCore) 11 | 12 | public: 13 | DemoCorePrivate(); 14 | virtual ~DemoCorePrivate(); 15 | protected: 16 | DemoCore *q_ptr; 17 | 18 | protected: 19 | QCxxListModel nodes; 20 | QCxxListModel points; 21 | }; 22 | 23 | #endif // DEMOCORE_P_H 24 | -------------------------------------------------------------------------------- /test/demo/DemoNode.cpp: -------------------------------------------------------------------------------- 1 | #include "DemoNode.h" 2 | #include "DemoNode_p.h" 3 | 4 | // class DemoNode 5 | 6 | DemoNode::DemoNode(QObject *parent) 7 | : QObject(parent) 8 | , d_ptr(new DemoNodePrivate()) 9 | { 10 | d_ptr->q_ptr = this; 11 | } 12 | 13 | DemoNode::~DemoNode() 14 | { 15 | } 16 | 17 | void DemoNode::setText(const QString &text) 18 | { 19 | Q_D(DemoNode); 20 | 21 | if (text != d->text) { 22 | d->text = text; 23 | emit textChanged(); 24 | } 25 | } 26 | 27 | QString DemoNode::text() const 28 | { 29 | Q_D(const DemoNode); 30 | 31 | return d->text; 32 | } 33 | 34 | // class DemoNodePrivate 35 | 36 | DemoNodePrivate::DemoNodePrivate() 37 | : q_ptr(nullptr) 38 | { 39 | } 40 | 41 | DemoNodePrivate::~DemoNodePrivate() 42 | { 43 | } 44 | -------------------------------------------------------------------------------- /test/demo/DemoNode.h: -------------------------------------------------------------------------------- 1 | #ifndef DEMONODE_H 2 | #define DEMONODE_H 3 | 4 | #include 5 | 6 | class DemoNodePrivate; 7 | class DemoNode : public QObject 8 | { 9 | Q_OBJECT 10 | Q_DECLARE_PRIVATE(DemoNode) 11 | 12 | Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) 13 | 14 | public: 15 | explicit DemoNode(QObject *parent = nullptr); 16 | virtual ~DemoNode(); 17 | protected: 18 | QScopedPointer d_ptr; 19 | 20 | signals: 21 | void textChanged(); 22 | public: 23 | void setText(const QString &text); 24 | public: 25 | QString text() const; 26 | }; 27 | 28 | #endif // DEMONODE_H 29 | -------------------------------------------------------------------------------- /test/demo/DemoNode_p.h: -------------------------------------------------------------------------------- 1 | #ifndef DEMONODE_P_H 2 | #define DEMONODE_P_H 3 | 4 | #include "DemoNode.h" 5 | 6 | class DemoNodePrivate 7 | { 8 | Q_DECLARE_PUBLIC(DemoNode) 9 | 10 | public: 11 | DemoNodePrivate(); 12 | virtual ~DemoNodePrivate(); 13 | protected: 14 | DemoNode *q_ptr; 15 | 16 | protected: 17 | QString text; 18 | }; 19 | 20 | #endif // DEMONODE_P_H 21 | -------------------------------------------------------------------------------- /test/demo/demo.pro: -------------------------------------------------------------------------------- 1 | QT += quick 2 | 3 | DEFINES += QT_DEPRECATED_WARNINGS 4 | DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 5 | 6 | HEADERS += \ 7 | DemoCore.h \ 8 | DemoCore_p.h \ 9 | DemoNode.h \ 10 | DemoNode_p.h 11 | SOURCES += \ 12 | DemoCore.cpp \ 13 | DemoNode.cpp 14 | 15 | SOURCES += \ 16 | main.cpp 17 | 18 | RESOURCES += qml.qrc 19 | 20 | include(../../src/QCxxListModel.pri) 21 | -------------------------------------------------------------------------------- /test/demo/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "DemoCore.h" 5 | #include "DemoNode.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 10 | 11 | QGuiApplication app(argc, argv); 12 | 13 | qmlRegisterType("ListModelDemo", 1, 0, "DemoCore"); 14 | qmlRegisterUncreatableType("ListModelDemo", 1, 0, "DemoNode", "Internal Element"); 15 | 16 | QQmlApplicationEngine engine; 17 | const QUrl url(QStringLiteral("qrc:/main.qml")); 18 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, 19 | &app, [url](QObject *obj, const QUrl &objUrl) { 20 | if (!obj && url == objUrl) 21 | QCoreApplication::exit(-1); 22 | }, Qt::QueuedConnection); 23 | engine.load(url); 24 | 25 | return app.exec(); 26 | } 27 | -------------------------------------------------------------------------------- /test/demo/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Controls 2.12 4 | 5 | import ListModelDemo 1.0 6 | 7 | Window { 8 | title: qsTr("ListModelDemo") 9 | 10 | width: 1280 11 | height: 720 12 | 13 | visible: true 14 | 15 | DemoCore { 16 | id: core 17 | } 18 | 19 | Repeater { 20 | anchors { 21 | centerIn: parent 22 | } 23 | 24 | model: core.points 25 | delegate: Rectangle { 26 | x: 200 + modelData.x 27 | y: 200 + modelData.y 28 | 29 | width: 100 30 | height: 100 31 | color: "red" 32 | 33 | opacity: 0.5 34 | 35 | Behavior on x { 36 | NumberAnimation { 37 | duration: 750 38 | easing { 39 | type: Easing.OutCubic 40 | } 41 | } 42 | } 43 | Behavior on y { 44 | NumberAnimation { 45 | duration: 750 46 | easing { 47 | type: Easing.OutCubic 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | ListView { 55 | id: listView 56 | anchors { 57 | fill: parent 58 | } 59 | 60 | model: core.nodes 61 | delegate: Item { 62 | width: listView.width 63 | height: 60 64 | 65 | Rectangle { 66 | anchors { 67 | left: parent.left 68 | right: parent.right 69 | bottom: parent.bottom 70 | } 71 | height: 1 72 | color: "red" 73 | } 74 | 75 | Text { 76 | id: label 77 | anchors { 78 | verticalCenter: parent.verticalCenter 79 | left: parent.left 80 | leftMargin: 8 81 | } 82 | 83 | text: index + ":" + modelData.text 84 | font { 85 | pixelSize: 16 86 | } 87 | } 88 | 89 | Row { 90 | anchors { 91 | verticalCenter: parent.verticalCenter 92 | left: label.right 93 | leftMargin: 8 94 | } 95 | 96 | Button { 97 | text: "Move up" 98 | onClicked: { 99 | if (index - 1 >= 0) { 100 | core.move(index, index - 1); 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /test/demo/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/test.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += \ 4 | auto \ 5 | demo 6 | --------------------------------------------------------------------------------