diff --git a/doc/src/images/fetchmore-example.png b/doc/src/images/fetchmore-example.png index e8c689ad5e6..7af30119c48 100644 Binary files a/doc/src/images/fetchmore-example.png and b/doc/src/images/fetchmore-example.png differ diff --git a/examples/widgets/doc/src/fetchmore.qdoc b/examples/widgets/doc/src/fetchmore.qdoc index a27efaf0716..940a2523197 100644 --- a/examples/widgets/doc/src/fetchmore.qdoc +++ b/examples/widgets/doc/src/fetchmore.qdoc @@ -34,15 +34,9 @@ \image fetchmore-example.png - - This example consists of a dialog where you can enter a directory - name in the \uicontrol Directory edit field. The application loads and - visualizes all files it finds as you are typing. It is not required - to press [Enter] to launch the search. - When you have large - or perhaps even infinite - data sets, you will need to add items to the model in batches, and preferably only - when the items are needed by the view (i.e., when they are visible + when the items are needed by the view (i.e., when they become visible in the view). In this example, we implement \c FileListModel - an item view @@ -50,6 +44,15 @@ Window, which sets up the GUI and feeds the model with directories. + The UI consists of a dialog with a list showing the contents + of the root directory. Directories can be navigated by double-clicking. + + At the bottom, there is a log window displaying messages when the view + asks the model for more data. + + To exercise it, navigate to a large directory (say \c /bin), and scroll + to the bottom. Log messages appear showing the data being retrieved. + Let's take a tour of \c {FileListModel}'s code. \section1 FileListModel Class Definition diff --git a/examples/widgets/itemviews/fetchmore/filelistmodel.cpp b/examples/widgets/itemviews/fetchmore/filelistmodel.cpp index 3ee80617c0f..5c93aba6f49 100644 --- a/examples/widgets/itemviews/fetchmore/filelistmodel.cpp +++ b/examples/widgets/itemviews/fetchmore/filelistmodel.cpp @@ -54,8 +54,10 @@ #include #include +static const int batchSize = 100; + FileListModel::FileListModel(QObject *parent) - : QAbstractListModel(parent), fileCount(0) + : QAbstractListModel(parent) {} //![4] @@ -67,24 +69,33 @@ int FileListModel::rowCount(const QModelIndex &parent) const QVariant FileListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) - return QVariant(); - - if (index.row() >= fileList.size() || index.row() < 0) - return QVariant(); - - if (role == Qt::DisplayRole) { - return fileList.at(index.row()); - } else if (role == Qt::BackgroundRole) { - int batch = (index.row() / 100) % 2; - if (batch == 0) - return qApp->palette().base(); - else - return qApp->palette().alternateBase(); + return {}; + + const int row = index.row(); + if (row >= fileList.size() || row < 0) + return {}; + + switch (role) { + case Qt::DisplayRole: + return fileList.at(row).fileName(); + case Qt::BackgroundRole: { + const int batch = row / batchSize; + const QPalette &palette = QGuiApplication::palette(); + return (batch % 2) != 0 ? palette.alternateBase() : palette.base(); + } + case Qt::DecorationRole: + return iconProvider.icon(fileList.at(row)); } - return QVariant(); + return {}; } + //![4] +QFileInfo FileListModel::fileInfoAt(const QModelIndex &index) const +{ + return fileList.at(index.row()); +} + //![1] bool FileListModel::canFetchMore(const QModelIndex &parent) const { @@ -99,19 +110,20 @@ void FileListModel::fetchMore(const QModelIndex &parent) { if (parent.isValid()) return; - int remainder = fileList.size() - fileCount; - int itemsToFetch = qMin(100, remainder); + const int start = fileCount; + const int remainder = int(fileList.size()) - start; + const int itemsToFetch = qMin(batchSize, remainder); if (itemsToFetch <= 0) return; - beginInsertRows(QModelIndex(), fileCount, fileCount + itemsToFetch - 1); + beginInsertRows(QModelIndex(), start, start + itemsToFetch - 1); fileCount += itemsToFetch; endInsertRows(); - emit numberPopulated(itemsToFetch); + emit numberPopulated(path, start, itemsToFetch, int(fileList.size())); } //![2] @@ -121,7 +133,8 @@ void FileListModel::setDirPath(const QString &path) QDir dir(path); beginResetModel(); - fileList = dir.entryList(); + this->path = path; + fileList = dir.entryInfoList(QDir::NoDot | QDir::AllEntries, QDir::Name); fileCount = 0; endResetModel(); } diff --git a/examples/widgets/itemviews/fetchmore/filelistmodel.h b/examples/widgets/itemviews/fetchmore/filelistmodel.h index 35cf6f7b461..1cd61f8c8ed 100644 --- a/examples/widgets/itemviews/fetchmore/filelistmodel.h +++ b/examples/widgets/itemviews/fetchmore/filelistmodel.h @@ -52,7 +52,8 @@ #define FILELISTMODEL_H #include -#include +#include +#include //![0] class FileListModel : public QAbstractListModel @@ -65,8 +66,10 @@ class FileListModel : public QAbstractListModel int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QFileInfo fileInfoAt(const QModelIndex &) const; + signals: - void numberPopulated(int number); + void numberPopulated(const QString &path, int start, int number, int total); public slots: void setDirPath(const QString &path); @@ -76,8 +79,10 @@ public slots: void fetchMore(const QModelIndex &parent) override; private: - QStringList fileList; - int fileCount; + QFileInfoList fileList; + QString path; + QFileIconProvider iconProvider; + int fileCount = 0; }; //![0] diff --git a/examples/widgets/itemviews/fetchmore/window.cpp b/examples/widgets/itemviews/fetchmore/window.cpp index fa23bddc736..64746c18f8b 100644 --- a/examples/widgets/itemviews/fetchmore/window.cpp +++ b/examples/widgets/itemviews/fetchmore/window.cpp @@ -56,37 +56,43 @@ Window::Window(QWidget *parent) : QWidget(parent) { - FileListModel *model = new FileListModel(this); - model->setDirPath(QLibraryInfo::path(QLibraryInfo::PrefixPath)); + model = new FileListModel(this); + model->setDirPath(QDir::rootPath()); - QLabel *label = new QLabel(tr("&Directory:")); - QLineEdit *lineEdit = new QLineEdit; - label->setBuddy(lineEdit); - - QListView *view = new QListView; + view = new QListView; view->setModel(model); - logViewer = new QTextBrowser(this); - logViewer->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); + logViewer = new QPlainTextEdit(this); + logViewer->setReadOnly(true); + logViewer->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, + QSizePolicy::Preferred)); - connect(lineEdit, &QLineEdit::textChanged, - model, &FileListModel::setDirPath); - connect(lineEdit, &QLineEdit::textChanged, - logViewer, &QTextEdit::clear); connect(model, &FileListModel::numberPopulated, this, &Window::updateLog); + connect(view, &QAbstractItemView::activated, + this, &Window::activated); - QGridLayout *layout = new QGridLayout; - layout->addWidget(label, 0, 0); - layout->addWidget(lineEdit, 0, 1); - layout->addWidget(view, 1, 0, 1, 2); - layout->addWidget(logViewer, 2, 0, 1, 2); + auto *layout = new QVBoxLayout(this); + layout->addWidget(view); + layout->addWidget(logViewer); - setLayout(layout); setWindowTitle(tr("Fetch More Example")); } -void Window::updateLog(int number) +void Window::updateLog(const QString &path, int start, int number, int total) +{ + const int last = start + number - 1; + const QString nativePath = QDir::toNativeSeparators(path); + const QString message = tr("%1..%2/%3 items from \"%4\" added.") + .arg(start).arg(last).arg(total).arg(nativePath); + logViewer->appendPlainText(message); +} + +void Window::activated(const QModelIndex &index) { - logViewer->append(tr("%1 items added.").arg(number)); + const QFileInfo fi = model->fileInfoAt(index); + if (fi.isDir()) { + logViewer->clear(); + model->setDirPath(fi.absoluteFilePath()); + } } diff --git a/examples/widgets/itemviews/fetchmore/window.h b/examples/widgets/itemviews/fetchmore/window.h index 61bcb94bde6..1f7c2a8728a 100644 --- a/examples/widgets/itemviews/fetchmore/window.h +++ b/examples/widgets/itemviews/fetchmore/window.h @@ -54,9 +54,13 @@ #include QT_BEGIN_NAMESPACE -class QTextBrowser; +class QModelIndex; +class QListView; +class QPlainTextEdit; QT_END_NAMESPACE +class FileListModel; + class Window : public QWidget { Q_OBJECT @@ -65,10 +69,13 @@ class Window : public QWidget Window(QWidget *parent = nullptr); public slots: - void updateLog(int number); + void updateLog(const QString &path, int start, int number, int total); + void activated(const QModelIndex &); private: - QTextBrowser *logViewer; + QPlainTextEdit *logViewer; + FileListModel *model; + QListView *view; }; #endif // WINDOW_H