diff --git a/imagemodel.cpp b/imagemodel.cpp index 5c58083..3288ae0 100644 --- a/imagemodel.cpp +++ b/imagemodel.cpp @@ -4,6 +4,7 @@ #include #include #include +#include ImageModel::ImageModel(QObject *parent) : QAbstractListModel(parent) @@ -12,6 +13,9 @@ ImageModel::ImageModel(QObject *parent) m_placeholder = QPixmap(128, 128); m_placeholder.fill(Qt::lightGray); + + QFileIconProvider iconProvider; + m_folderIcon = iconProvider.icon(QFileIconProvider::Folder); } ImageModel::~ImageModel() @@ -29,8 +33,22 @@ void ImageModel::setDirectory(const QString &path) beginResetModel(); m_items.clear(); m_pendingRows.clear(); + m_currentDir = path; QDir dir(path); + + // Add subdirectories first + dir.setFilter(QDir::AllDirs | QDir::NoDot); + dir.setSorting(QDir::Name); + const auto dirs = dir.entryInfoList(); + QPixmap folderPixmap = m_folderIcon.pixmap(128, 128); + for (const QFileInfo &d : dirs) + { + QString name = d.fileName(); // preserves ".." for the parent entry + m_items.append({d.absoluteFilePath(), name, folderPixmap, true, true}); + } + + // Then add image files QStringList filters; const auto formats = QImageReader::supportedImageFormats(); @@ -39,12 +57,13 @@ void ImageModel::setDirectory(const QString &path) dir.setNameFilters(filters); dir.setFilter(QDir::Files); + dir.setSorting(QDir::Name); const auto files = dir.entryInfoList(); for (const QFileInfo &file : files) { - m_items.append({file.absoluteFilePath(), m_placeholder, false}); + m_items.append({file.absoluteFilePath(), file.fileName(), m_placeholder, false, false}); } endResetModel(); @@ -60,18 +79,18 @@ void ImageModel::requestThumbnails(int firstRow, int lastRow) for (int row = firstRow; row <= lastRow; ++row) { - if (!m_items[row].loaded) + if (!m_items[row].loaded && !m_items[row].isFolder) queueRow(row); } // Optional: preload nearby rows int buffer = 20; for (int row = firstRow - buffer; row < firstRow; ++row) - if (row >= 0 && !m_items[row].loaded) + if (row >= 0 && !m_items[row].loaded && !m_items[row].isFolder) queueRow(row); for (int row = lastRow + 1; row <= lastRow + buffer; ++row) - if (row < m_items.size() && !m_items[row].loaded) + if (row < m_items.size() && !m_items[row].loaded && !m_items[row].isFolder) queueRow(row); } @@ -102,6 +121,18 @@ QString ImageModel::filePath(const QModelIndex &index) const return m_items[index.row()].path; } +bool ImageModel::isFolder(const QModelIndex &index) const +{ + if (!index.isValid() || index.row() >= m_items.size()) + return false; + return m_items[index.row()].isFolder; +} + +QString ImageModel::currentDirectory() const +{ + return m_currentDir; +} + int ImageModel::rowCount(const QModelIndex &) const { return m_items.size(); @@ -118,7 +149,7 @@ QVariant ImageModel::data(const QModelIndex &index, int role) const return item.thumbnail; if (role == Qt::DisplayRole) - return QFileInfo(item.path).fileName(); + return item.displayName; return {}; } diff --git a/imagemodel.h b/imagemodel.h index c124ae6..7586159 100644 --- a/imagemodel.h +++ b/imagemodel.h @@ -6,6 +6,7 @@ #include #include #include +#include class ThumbnailWorker; @@ -19,6 +20,8 @@ public: void setDirectory(const QString &path); QString filePath(const QModelIndex &index) const; + bool isFolder(const QModelIndex &index) const; + QString currentDirectory() const; void requestThumbnails(int firstRow, int lastRow); @@ -31,8 +34,10 @@ private: struct Item { QString path; + QString displayName; QPixmap thumbnail; bool loaded = false; + bool isFolder = false; }; void queueRow(int row); @@ -43,4 +48,6 @@ private: QSet m_pendingRows; std::atomic_bool m_cancelFlag{false}; + QIcon m_folderIcon; + QString m_currentDir; }; diff --git a/mainwindow.cpp b/mainwindow.cpp index 0dc9252..aa686c6 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -125,7 +125,19 @@ void MainWindow::setupConnections() this, [this](const QModelIndex ¤t) { showPreview(current); }); connect(m_listView, &QListView::doubleClicked, this, - [this](const QModelIndex &index) { enterSingleImageMode(index); }); + [this](const QModelIndex &index) { + if (m_imageModel->isFolder(index)) { + QString path = m_imageModel->filePath(index); + m_imageModel->setDirectory(path); + // Update tree view selection to match + QModelIndex dirIdx = m_dirModel->index(path); + if (dirIdx.isValid()) + m_treeView->setCurrentIndex(dirIdx); + QTimer::singleShot(0, this, &MainWindow::loadVisibleThumbnails); + } else { + enterSingleImageMode(index); + } + }); connect(m_imageView, &ImageViewWidget::closeRequested, this, &MainWindow::leaveSingleImageMode); @@ -133,6 +145,8 @@ void MainWindow::setupConnections() connect(m_imageView, &ImageViewWidget::nextRequested, this, [this]() { QModelIndex cur = m_listView->currentIndex(); int next = cur.isValid() ? cur.row() + 1 : 0; + while (next < m_imageModel->rowCount() && m_imageModel->isFolder(m_imageModel->index(next))) + ++next; if (next < m_imageModel->rowCount()) { QModelIndex idx = m_imageModel->index(next); @@ -144,6 +158,8 @@ void MainWindow::setupConnections() connect(m_imageView, &ImageViewWidget::previousRequested, this, [this]() { QModelIndex cur = m_listView->currentIndex(); int prev = cur.isValid() ? cur.row() - 1 : 0; + while (prev >= 0 && m_imageModel->isFolder(m_imageModel->index(prev))) + --prev; if (prev >= 0) { QModelIndex idx = m_imageModel->index(prev); @@ -245,7 +261,16 @@ void MainWindow::keyPressEvent(QKeyEvent *event) QModelIndex current = m_listView->currentIndex(); if (current.isValid()) { - enterSingleImageMode(current); + if (m_imageModel->isFolder(current)) { + QString path = m_imageModel->filePath(current); + m_imageModel->setDirectory(path); + QModelIndex dirIdx = m_dirModel->index(path); + if (dirIdx.isValid()) + m_treeView->setCurrentIndex(dirIdx); + QTimer::singleShot(0, this, &MainWindow::loadVisibleThumbnails); + } else { + enterSingleImageMode(current); + } event->accept(); return; }