Revision control

++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ #include <QtCore/qrangemodeladapter.h>
++
++ #ifndef QT_NO_WIDGETS
++
++ using namespace Qt::StringLiterals;
++
++ #include <QtWidgets/qlistview.h>
++ #include <QtWidgets/qtableview.h>
++ #include <QtWidgets/qtreeview.h>
++ #include <vector>
++
++ 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:
++ enum Roles
++ {
++ TitleRole = Qt::UserRole,
++ AuthorRole,
++ SummaryRole,
++ RatingRole,
++ };
++
++ Book() = default;
++ 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;
++ };
++
++ template <> struct QRangeModel::RowOptions<Book>
++ {
++ static constexpr auto rowCategory = QRangeModel::RowCategory::MultiRoleItem;
++ };
++
++ struct TreeRow;
++ using Tree = QList<TreeRow *>;
++
++ struct TreeRow
++ {
++ QString firstColumn;
++ int secondColumn = 0;
++
++ TreeRow() = default;
++ explicit TreeRow(const QString &first, int second, std::optional<Tree> children = std::nullopt)
++ : firstColumn(first), secondColumn(second), m_children(children)
++ {}
++
++ TreeRow *parentRow() const { return m_parent; }
++ void setParentRow(TreeRow *parent) { m_parent = parent; }
++ const std::optional<Tree> &childRows() const { return m_children; }
++ std::optional<Tree> &childRows() { return m_children; }
++
++ private:
++ TreeRow *m_parent;
++ std::optional<Tree> m_children;
++
++ template <size_t I> friend inline decltype(auto) get(const TreeRow &row) {
++ static_assert(I < 2);
++ return false;
++ }
++ };
++
++ namespace std {
++ template<> struct tuple_size<TreeRow> : integral_constant<int, 2> {};
++ template<> struct tuple_element<0, TreeRow> { using type = QString; };
++ template<> struct tuple_element<1, TreeRow> { using type = int; };
++ }
++
++ void construct_and_use()
++ {
++ //! [construct]
++ std::vector<int> data = {1, 2, 3, 4, 5};
++ QRangeModelAdapter adapter(&data);
++ //! [construct]
++
++ //! [class-member-rvalue]
++ class Backend
++ {
++ using Adapter = decltype(QRangeModelAdapter(std::vector<Book>{}));
++ Adapter adapter = Adapter(std::vector<Book>{});
++ //! [class-member-rvalue]
++ };
++
++ class Backend2
++ {
++ //! [class-member-refwrapper]
++ QList<Book> books = { /* ... */ };
++ using Adapter = decltype(QRangeModelAdapter(std::ref(books)));
++ Adapter adapter = Adapter(std::ref(books));
++ };
++ //! [class-member-refwrapper]
++
++ //! [use-model]
++ QListView listView;
++ listView.setModel(adapter.model());
++ //! [use-model]
++ }
++
++ void get_and_set()
++ {
++ QListView tableView;
++ //! [get-range]
++ QList<Book> books = {
++ // ...
++ };
++ QRangeModelAdapter adapter(books);
++ tableView.setModel(adapter.model());
++
++ // show UI and where the user can modify the list
++
++ QList<Book> modifiedBooks = adapter.range();
++ //! [get-range]
++
++ //! [assign]
++ // reset to the original
++ adapter = books;
++ // or
++ adapter.assign(books);
++ //! [assign]
++ }
++
++ void dataAccess()
++ {
++ int row = 0;
++ int column = 0;
++ int path = 0;
++ int to = 0;
++ int branch = 0;
++
++ QRangeModelAdapter listAdapter(QList<int>{});
++ //! [list-data]
++ QVariant listItem = listAdapter.data(row);
++ //! [list-data]
++
++ QRangeModelAdapter tableAdapter(QList<QList<int>>{});
++ //! [table-data]
++ QVariant tableItem = tableAdapter.data(row, column);
++ //! [table-data]
++
++ QRangeModelAdapter treeAdapter(QList<TreeRow *>{});
++ //! [tree-data]
++ QVariant treeItem = treeAdapter.data({path, to, branch}, column);
++ //! [tree-data]
++
++ //! [multirole-data]
++ QRangeModelAdapter listOfBooks(QList<Book>{
++ // ~~~
++ });
++ QString bookTitle = listOfBooks.data(0, Book::TitleRole).toString();
++ Book multiRoleItem = listOfBooks.data(0).value<Book>();
++ //! [multirole-data]
++ }
++
++ void list_access()
++ {
++ QListView listView;
++ {
++ //! [list-access]
++ QRangeModelAdapter list(std::vector<int>{1, 2, 3, 4, 5});
++ listView.setModel(list.model());
++
++ int firstValue = list.at(0); // == 1
++ list.at(0) = -1;
++ list.at(1) = list.at(4);
++ //! [list-access]
++ }
++ {
++ //! [list-access-multirole]
++ QRangeModelAdapter books(QList<Book>{
++ // ~~~
++ });
++ Book firstBook = books.at(0);
++ Book newBook = {};
++ books.at(0) = newBook; // dataChanged() emitted
++ //! [list-access-multirole]
++
++ //! [list-access-multirole-member-access]
++ QString title = books.at(0)->title();
++ //! [list-access-multirole-member-access]
++
++ //! [list-access-multirole-write-back]
++ // books.at(0)->setRating(5); - not possible even though 'books' is not const
++ firstBook = books.at(0);
++ firstBook.setRating(5);
++ books.at(0) = firstBook; // dataChanged() emitted
++ //! [list-access-multirole-write-back]
++ }
++ }
++
++ void table_access()
++ {
++ QTableView tableView;
++ {
++ //! [table-item-access]
++ QRangeModelAdapter table(std::vector<std::vector<double>>{
++ {1.0, 2.0, 3.0, 4.0, 5.0},
++ {6.0, 7.0, 8.0, 9.0, 10.0},
++ });
++ tableView.setModel(table.model());
++
++ double value = table.at(0, 2); // value == 3.0
++ table.at(0, 2) = value * 2; // table[0, 2] == 6.0
++ //! [table-item-access]
++
++ //! [table-row-const-access]
++ const auto &constTable = table;
++ const std::vector<double> &topRow = constTable.at(0);
++ //! [table-row-const-access]
++
++ //! [table-row-access]
++ auto lastRow = table.at(table.rowCount() - 1);
++ lastRow = { 6.5, 7.5, 8.0, 9.0, 10 }; // emits dataChanged() for entire row
++ //! [table-row-access]
++ }
++
++ {
++ //! [table-mixed-type-access]
++ QRangeModelAdapter table(std::vector<std::tuple<int, QString>>{
++ // ~~~
++ });
++ int number = table.at(0, 0)->toInt();
++ QString text = table.at(0, 1)->toString();
++ //! [table-mixed-type-access]
++ }
++ }
++
++ void tree_access()
++ {
++ QTreeView treeView;
++
++ //! [tree-row-access]
++ QRangeModelAdapter tree = QRangeModelAdapter(Tree{
++ new TreeRow{"Germany", 357002, Tree{
++ new TreeRow("Bavaria", 70550)
++ },
++ },
++ new TreeRow{"France", 632702},
++ });
++ treeView.setModel(tree.model());
++
++ auto germanyData = tree.at(0);
++ auto bavariaData = tree.at({0, 0});
++ //! [tree-row-access]
++
++ //! [tree-item-access]
++ auto germanyName = tree.at(0, 0);
++ auto bavariaSize = tree.at({0, 0}, 1);
++ //! [tree-item-access]
++
++ //! [tree-row-write]
++ // deletes the old row - tree was moved in
++ tree.at({0, 0}) = new TreeRow{"Berlin", 892};
++ //! [tree-row-write]
++ }
++
++ void read_only()
++ {
++ #if 0
++ //! [read-only]
++ const QStringList strings = {"On", "Off"};
++ QRangeModelAdapter adapter(strings);
++ adapter.at(0) = "Undecided"; // compile error: return value of 'at' is const
++ adapter.insertRow(0); // compiler error: requirements not satisfied
++ //! [read-only]
++ #endif
++ }
++
++ void list_iterate()
++ {
++ QRangeModelAdapter books(QList<Book>{
++ // ~~~
++ });
++ QListView view;
++ view.setModel(books.model());
++
++ //! [ranged-for-const-list]
++ for (const auto &book : std::as_const(books)) {
++ qDebug() << "The book" << book.title()
++ << "written by" << book.author()
++ << "has" << book.rating() << "stars";
++ }
++ //! [ranged-for-const-list]
++
++ //! [ranged-for-mutable-list]
++ for (auto book : books) {
++ qDebug() << "The book" << book->title()
++ << "written by" << book->author()
++ << "has" << book->rating() << "stars";
++
++ Book copy = book;
++ copy.setRating(copy.rating() + 1);
++ book = copy;
++ }
++ //! [ranged-for-mutable-list]
++ }
++
++ void table_iterate()
++ {
++ //! [ranged-for-const-table]
++ QRangeModelAdapter table(std::vector<std::pair<int, QString>>{
++ // ~~~
++ });
++
++ for (const auto &row : std::as_const(table)) {
++ qDebug() << "Number is" << row->first << "and string is" << row->second;
++ }
++ //! [ranged-for-const-table]
++
++ //! [ranged-for-const-table-items]
++ for (const auto &row : std::as_const(table)) {
++ for (const auto &item : row) {
++ qDebug() << item; // item is a QVariant
++ }
++ }
++ //! [ranged-for-const-table-items]
++
++ //! [ranged-for-mutable-table]
++ for (auto row : table) {
++ qDebug() << "Number is" << row->first << "and string is" << row->second;
++ row = std::make_pair(42, u"forty-two"_s);
++ }
++ //! [ranged-for-mutable-table]
++
++ //! [ranged-for-mutable-table-items]
++ for (auto row : table) {
++ for (auto item : row) {
++ item = 42;
++ }
++ }
++ //! [ranged-for-mutable-table-items]
++ }
++
++ void tree_iterate()
++ {
++ QRangeModelAdapter tree = QRangeModelAdapter(Tree{
++ new TreeRow{"1", 1, Tree{
++ new TreeRow("1.1", 11)
++ },
++ },
++ new TreeRow{"2", 2},
++ });
++
++ static_assert(std::is_same_v<typename decltype(tree)::DataReference::value_type, QVariant>);
++
++ //! [ranged-for-tree]
++ for (auto row : tree) {
++ if (row.hasChildren()) {
++ for (auto child : row.children()) {
++ // ~~~
++ }
++ }
++ }
++ //! [ranged-for-tree]
++ }
++
++ #endif