commit 1dcea855
Align the thumpnails
Changed files
| M | CMakeLists.txt before |
| M | src/imagemodel.cpp before |
| M | src/mainwindow.cpp before |
| A | src/thumbnaildelegate.cpp |
| A | src/thumbnaildelegate.h |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1026ab0..4cf8380 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,9 @@ qt_add_executable(SagoImageBrowser
src/exifreader.cpp
src/exifreader.h
src/pathcompletermodel.cpp
- src/pathcompletermodel.h)
+ src/pathcompletermodel.h
+ src/thumbnaildelegate.cpp
+ src/thumbnaildelegate.h)
target_include_directories(SagoImageBrowser PRIVATE ${EXIV2_INCLUDE_DIRS})
target_link_libraries(SagoImageBrowser PRIVATE Qt6::Widgets ${EXIV2_LIBRARIES})
\ No newline at end of file
diff --git a/src/imagemodel.cpp b/src/imagemodel.cpp
index 93cc8d4..27f2401 100644
--- a/src/imagemodel.cpp
+++ b/src/imagemodel.cpp
@@ -285,6 +285,9 @@ QVariant ImageModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole)
return item.displayName;
+ if (role == Qt::UserRole)
+ return item.isFolder;
+
return {};
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 97b6eb5..0fd7ed4 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -6,6 +6,7 @@
#include "exifreader.h"
#include "fsdirmodel.h"
#include "pathcompletermodel.h"
+#include "thumbnaildelegate.h"
#include <QFile>
#include <QTreeView>
@@ -104,6 +105,7 @@ void MainWindow::setupUi()
m_listView->setSpacing(4);
m_listView->setWordWrap(true);
m_listView->setTextElideMode(Qt::ElideRight);
+ m_listView->setItemDelegate(new ThumbnailDelegate(this));
// Preview pane (right)
m_previewLabel = new QLabel;
diff --git a/src/thumbnaildelegate.cpp b/src/thumbnaildelegate.cpp
new file mode 100644
index 0000000..6867107
--- /dev/null
+++ b/src/thumbnaildelegate.cpp
@@ -0,0 +1,90 @@
+#include "thumbnaildelegate.h"
+
+#include <QPainter>
+#include <QApplication>
+#include <QStyle>
+#include <QTextOption>
+#include <QFontMetrics>
+
+ThumbnailDelegate::ThumbnailDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+{
+}
+
+void ThumbnailDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ painter->save();
+
+ // Folder items use the default delegate rendering unchanged
+ if (index.data(Qt::UserRole).toBool()) {
+ painter->restore();
+ QStyledItemDelegate::paint(painter, option, index);
+ return;
+ }
+
+ // Draw selection / hover background
+ QStyle *style = option.widget ? option.widget->style() : QApplication::style();
+ QStyleOptionViewItem bgOption = option;
+ bgOption.showDecorationSelected = true;
+ style->drawPrimitive(QStyle::PE_PanelItemViewItem, &bgOption, painter, option.widget);
+
+ const QRect cellRect = option.rect;
+
+ // Fixed icon zone: top IconZoneHeight pixels of the cell
+ const QRect iconZone(cellRect.left(), cellRect.top(),
+ cellRect.width(), IconZoneHeight);
+
+ // Fixed text zone: everything below the icon zone
+ const QRect textZone(cellRect.left(), cellRect.top() + IconZoneHeight,
+ cellRect.width(), cellRect.height() - IconZoneHeight);
+
+ // --- Draw icon centered within the icon zone ---
+ const QVariant decorData = index.data(Qt::DecorationRole);
+ QPixmap pixmap;
+ if (decorData.userType() == QMetaType::QPixmap) {
+ pixmap = qvariant_cast<QPixmap>(decorData);
+ } else if (decorData.userType() == QMetaType::QIcon) {
+ pixmap = qvariant_cast<QIcon>(decorData).pixmap(option.decorationSize);
+ }
+ if (!pixmap.isNull()) {
+ // Center the pixmap in the icon zone
+ const int x = iconZone.left() + (iconZone.width() - pixmap.width()) / 2;
+ const int y = iconZone.top() + (iconZone.height() - pixmap.height()) / 2;
+ painter->drawPixmap(x, y, pixmap);
+ }
+
+ // --- Draw text top-aligned within the text zone ---
+ const QString text = index.data(Qt::DisplayRole).toString();
+ if (!text.isEmpty()) {
+ QColor textColor = option.palette.color(
+ (option.state & QStyle::State_Selected) ? QPalette::HighlightedText
+ : QPalette::Text);
+ painter->setPen(textColor);
+ painter->setFont(option.font);
+
+ QTextOption textOpt;
+ textOpt.setAlignment(Qt::AlignTop | Qt::AlignHCenter);
+ textOpt.setWrapMode(QTextOption::WordWrap);
+
+ // Elide text that would overflow the text zone
+ const QFontMetrics fm(option.font);
+ const QString elidedText = fm.elidedText(
+ text, option.textElideMode, textZone.width() * 2);
+
+ painter->drawText(QRectF(textZone), elidedText, textOpt);
+ }
+
+ painter->restore();
+}
+
+QSize ThumbnailDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ Q_UNUSED(option)
+ Q_UNUSED(index)
+ // Return the grid size set on the view (148×168)
+ return option.rect.size().isValid() ? option.rect.size()
+ : QStyledItemDelegate::sizeHint(option, index);
+}
diff --git a/src/thumbnaildelegate.h b/src/thumbnaildelegate.h
new file mode 100644
index 0000000..44aae83
--- /dev/null
+++ b/src/thumbnaildelegate.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <QStyledItemDelegate>
+
+class ThumbnailDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ static constexpr int IconZoneHeight = 128;
+
+ explicit ThumbnailDelegate(QObject *parent = nullptr);
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const override;
+};