diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ea1e767 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.21) +project(ImageBrowser LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_AUTOMOC ON) + +find_package(Qt6 REQUIRED COMPONENTS Widgets) + +qt_add_executable(ImageBrowser + main.cpp + mainwindow.cpp + mainwindow.h + imagemodel.cpp + imagemodel.h +) + +target_link_libraries(ImageBrowser PRIVATE Qt6::Widgets) \ No newline at end of file diff --git a/imagemodel.cpp b/imagemodel.cpp new file mode 100644 index 0000000..f22f119 --- /dev/null +++ b/imagemodel.cpp @@ -0,0 +1,70 @@ +#include "imagemodel.h" + +#include +#include +#include + +ImageModel::ImageModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +void ImageModel::setDirectory(const QString &path) +{ + beginResetModel(); + m_items.clear(); + + QDir dir(path); + QStringList filters; + + const auto formats = QImageReader::supportedImageFormats(); + for (const QByteArray &fmt : formats) + filters << "*." + fmt; + + dir.setNameFilters(filters); + dir.setFilter(QDir::Files); + + for (const QFileInfo &file : dir.entryInfoList()) + { + QImageReader reader(file.absoluteFilePath()); + reader.setAutoTransform(true); + + QImage image = reader.read(); + QPixmap thumb = QPixmap::fromImage( + image.scaled(128, 128, Qt::KeepAspectRatio, + Qt::SmoothTransformation)); + + m_items.append({file.absoluteFilePath(), thumb}); + } + + endResetModel(); +} + +QString ImageModel::filePath(const QModelIndex &index) const +{ + if (!index.isValid()) + return {}; + + return m_items[index.row()].path; +} + +int ImageModel::rowCount(const QModelIndex &) const +{ + return m_items.size(); +} + +QVariant ImageModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + const Item &item = m_items[index.row()]; + + if (role == Qt::DecorationRole) + return item.thumbnail; + + if (role == Qt::DisplayRole) + return QFileInfo(item.path).fileName(); + + return {}; +} diff --git a/imagemodel.h b/imagemodel.h new file mode 100644 index 0000000..3915a94 --- /dev/null +++ b/imagemodel.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +class ImageModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit ImageModel(QObject *parent = nullptr); + + void setDirectory(const QString &path); + QString filePath(const QModelIndex &index) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + +private: + struct Item + { + QString path; + QPixmap thumbnail; + }; + + QVector m_items; +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..6ce4d29 --- /dev/null +++ b/main.cpp @@ -0,0 +1,13 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + MainWindow w; + w.resize(1200, 700); + w.show(); + + return app.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..a724b32 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,90 @@ +#include "mainwindow.h" +#include "imagemodel.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) +{ + setupUi(); + setupConnections(); +} + +void MainWindow::setupUi() +{ + // Folder model (left pane) + m_dirModel = new QFileSystemModel(this); + m_dirModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); + m_dirModel->setRootPath(QDir::homePath()); + + m_treeView = new QTreeView; + m_treeView->setModel(m_dirModel); + m_treeView->setRootIndex(m_dirModel->index(QDir::homePath())); + m_treeView->header()->hide(); + m_treeView->setColumnHidden(1, true); + m_treeView->setColumnHidden(2, true); + m_treeView->setColumnHidden(3, true); + + // Thumbnail model (center pane) + m_imageModel = new ImageModel(this); + + m_listView = new QListView; + m_listView->setModel(m_imageModel); + m_listView->setViewMode(QListView::IconMode); + m_listView->setIconSize(QSize(128, 128)); + m_listView->setResizeMode(QListView::Adjust); + m_listView->setSelectionMode(QAbstractItemView::SingleSelection); + m_listView->setSpacing(10); + + // Preview pane (right) + m_previewLabel = new QLabel; + m_previewLabel->setAlignment(Qt::AlignCenter); + m_previewLabel->setMinimumWidth(300); + + // Layout + QSplitter *rightSplitter = new QSplitter(Qt::Horizontal); + rightSplitter->addWidget(m_listView); + rightSplitter->addWidget(m_previewLabel); + rightSplitter->setStretchFactor(0, 3); + rightSplitter->setStretchFactor(1, 2); + + QSplitter *mainSplitter = new QSplitter(Qt::Horizontal); + mainSplitter->addWidget(m_treeView); + mainSplitter->addWidget(rightSplitter); + mainSplitter->setStretchFactor(1, 1); + + setCentralWidget(mainSplitter); +} + +void MainWindow::setupConnections() +{ + connect(m_treeView, &QTreeView::clicked, this, + [this](const QModelIndex &index) + { + QString path = m_dirModel->filePath(index); + m_imageModel->setDirectory(path); + }); + + connect(m_listView, &QListView::clicked, this, + [this](const QModelIndex &index) + { + QString path = m_imageModel->filePath(index); + + QImageReader reader(path); + reader.setAutoTransform(true); + QImage image = reader.read(); + + m_previewLabel->setPixmap( + QPixmap::fromImage(image).scaled( + m_previewLabel->size(), + Qt::KeepAspectRatio, + Qt::SmoothTransformation)); + }); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..43ecfe2 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +class QTreeView; +class QListView; +class QLabel; +class QFileSystemModel; +class ImageModel; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + +private: + void setupUi(); + void setupConnections(); + + QFileSystemModel *m_dirModel; + ImageModel *m_imageModel; + + QTreeView *m_treeView; + QListView *m_listView; + QLabel *m_previewLabel; +};