|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| #include <QFile>
|
| #include <QFileIconProvider>
|
| #include <QImageReader>
|
| #include <QPainter>
|
| #include <QStyleOptionViewItem>
|
| #include <QLabel>
|
| #include <QModelIndex>
|
| #include <QVBoxLayout>
|
| #include <QApplication>
|
| #include <QPushButton>
|
| #include <QString>
|
| #include <QAbstractItemView>
|
|
|
| #include "FileCardDelegate.h"
|
| #include "../App/DisplayedFilesModel.h"
|
| #include "App/Application.h"
|
| #include <Base/Color.h>
|
| #include <Base/Console.h>
|
| #include <Gui/Application.h>
|
| #include <Gui/MainWindow.h>
|
|
|
| using namespace Start;
|
|
|
| QCache<QString, QPixmap> FileCardDelegate::_thumbnailCache;
|
|
|
| FileCardDelegate::FileCardDelegate(QObject* parent)
|
| : QStyledItemDelegate(parent)
|
| {
|
| _parameterGroup = App::GetApplication().GetParameterGroupByPath(
|
| "User parameter:BaseApp/Preferences/Mod/Start"
|
| );
|
| setObjectName(QStringLiteral("thumbnailWidget"));
|
|
|
|
|
| if (_thumbnailCache.maxCost() == 0) {
|
| int thumbnailSize = static_cast<int>(_parameterGroup->GetInt("FileThumbnailIconsSize", 128));
|
| int thumbnailMemory = thumbnailSize * thumbnailSize * 4;
|
| int maxCacheItems = (CACHE_SIZE_MB * 1024 * 1024) / thumbnailMemory;
|
| _thumbnailCache.setMaxCost(maxCacheItems);
|
| Base::Console().log(
|
| "FileCardDelegate: Initialized thumbnail cache for %d items (%d MB)\n",
|
| maxCacheItems,
|
| CACHE_SIZE_MB
|
| );
|
| }
|
| }
|
|
|
| void FileCardDelegate::paint(
|
| QPainter* painter,
|
| const QStyleOptionViewItem& option,
|
| const QModelIndex& index
|
| ) const
|
| {
|
| painter->save();
|
|
|
| QStyleOptionButton buttonOption;
|
| buttonOption.initFrom(option.widget);
|
| buttonOption.rect = option.rect;
|
| buttonOption.state = QStyle::State_Enabled;
|
|
|
| if ((option.state & QStyle::State_MouseOver) != 0) {
|
| buttonOption.state |= QStyle::State_MouseOver;
|
| }
|
| if ((option.state & QStyle::State_Selected) != 0) {
|
| buttonOption.state |= QStyle::State_On;
|
| }
|
| if ((option.state & QStyle::State_Sunken) != 0) {
|
| buttonOption.state |= QStyle::State_Sunken;
|
| }
|
| qApp->style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter, &styleButton);
|
|
|
|
|
| auto thumbnailSize = static_cast<int>(_parameterGroup->GetInt("FileThumbnailIconsSize", 128));
|
| auto baseName = index.data(static_cast<int>(DisplayedFilesModelRoles::baseName)).toString();
|
| auto elidedName = painter->fontMetrics().elidedText(baseName, Qt::ElideRight, thumbnailSize);
|
| auto size = index.data(static_cast<int>(DisplayedFilesModelRoles::size)).toString();
|
| auto image = index.data(static_cast<int>(DisplayedFilesModelRoles::image)).toByteArray();
|
| auto path = index.data(static_cast<int>(DisplayedFilesModelRoles::path)).toString();
|
|
|
| QPixmap pixmap;
|
| if (!image.isEmpty()) {
|
| pixmap.loadFromData(image);
|
| }
|
| else {
|
| pixmap = generateThumbnail(path);
|
| }
|
|
|
| QPixmap scaledPixmap = pixmap.scaled(
|
| QSize(thumbnailSize, thumbnailSize),
|
| Qt::KeepAspectRatio,
|
| Qt::SmoothTransformation
|
| );
|
|
|
|
|
| QRect thumbnailRect(option.rect.x() + margin, option.rect.y() + margin, thumbnailSize, thumbnailSize);
|
| QRect textRect(
|
| option.rect.x() + margin,
|
| thumbnailRect.bottom() + margin,
|
| thumbnailSize,
|
| painter->fontMetrics().lineSpacing()
|
| );
|
|
|
| QRect sizeRect(
|
| option.rect.x() + margin,
|
| textRect.bottom() + textspacing,
|
| thumbnailSize,
|
| painter->fontMetrics().lineSpacing() + margin
|
| );
|
|
|
|
|
| QRect pixmapRect(thumbnailRect.topLeft(), scaledPixmap.size());
|
| pixmapRect.moveCenter(thumbnailRect.center());
|
| painter->drawPixmap(pixmapRect.topLeft(), scaledPixmap);
|
| painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, elidedName);
|
| painter->drawText(sizeRect, Qt::AlignLeft | Qt::AlignTop, size);
|
| painter->restore();
|
| }
|
|
|
|
|
| QSize FileCardDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
|
| {
|
| Q_UNUSED(option);
|
| Q_UNUSED(index);
|
|
|
| auto thumbnailSize = _parameterGroup->GetInt("FileThumbnailIconsSize", 128);
|
|
|
| QFontMetrics qfm(QGuiApplication::font());
|
| int textHeight = textspacing + qfm.lineSpacing() * 2;
|
| int cardWidth = static_cast<int>(thumbnailSize) + 2 * margin;
|
| int cardHeight = static_cast<int>(thumbnailSize) + textHeight + 3 * margin;
|
|
|
| return {cardWidth, cardHeight};
|
| }
|
|
|
| QPixmap FileCardDelegate::generateThumbnail(const QString& path) const
|
| {
|
| auto thumbnailSize = static_cast<int>(_parameterGroup->GetInt("FileThumbnailIconsSize", 128));
|
|
|
|
|
| QString cacheKey = getCacheKey(path, thumbnailSize);
|
| if (!cacheKey.isEmpty()) {
|
| if (QPixmap* cachedThumbnail = _thumbnailCache.object(cacheKey)) {
|
| return *cachedThumbnail;
|
| }
|
| }
|
|
|
|
|
| return loadAndCacheThumbnail(path, thumbnailSize);
|
| }
|
|
|
| QString FileCardDelegate::getCacheKey(const QString& path, int thumbnailSize) const
|
| {
|
| QFileInfo fileInfo(path);
|
| if (!fileInfo.exists()) {
|
| return {};
|
| }
|
|
|
|
|
| QString modTime = QString::number(fileInfo.lastModified().toSecsSinceEpoch());
|
| return QStringLiteral("%1:%2:%3").arg(path, modTime, QString::number(thumbnailSize));
|
| }
|
|
|
| QPixmap FileCardDelegate::loadAndCacheThumbnail(const QString& path, int thumbnailSize) const
|
| {
|
| QPixmap thumbnail;
|
|
|
| if (path.endsWith(QLatin1String(".fcstd"), Qt::CaseSensitivity::CaseInsensitive)) {
|
|
|
|
|
| QImageReader reader(QLatin1String(":/icons/freecad-doc.svg"));
|
| reader.setScaledSize(QSize(thumbnailSize, thumbnailSize));
|
| thumbnail = QPixmap::fromImage(reader.read());
|
| }
|
| else if (path.endsWith(QLatin1String(".fcmacro"), Qt::CaseSensitivity::CaseInsensitive)) {
|
| QImageReader reader(QLatin1String(":/icons/MacroEditor.svg"));
|
| reader.setScaledSize(QSize(thumbnailSize, thumbnailSize));
|
| thumbnail = QPixmap::fromImage(reader.read());
|
| }
|
| else if (!QImageReader::imageFormat(path).isEmpty()) {
|
|
|
| QImageReader reader(path);
|
|
|
|
|
| QSize originalSize = reader.size();
|
| if (originalSize.isValid()) {
|
| QSize scaledSize = originalSize.scaled(thumbnailSize, thumbnailSize, Qt::KeepAspectRatio);
|
| reader.setScaledSize(scaledSize);
|
| }
|
|
|
| auto image = reader.read();
|
| if (!image.isNull()) {
|
| thumbnail = QPixmap::fromImage(image);
|
| }
|
| else {
|
| Base::Console().log(
|
| "FileCardDelegate: Failed to load image %s: %s\n",
|
| path.toStdString().c_str(),
|
| reader.errorString().toStdString().c_str()
|
| );
|
| }
|
| }
|
|
|
|
|
| if (thumbnail.isNull()) {
|
| QIcon icon = QFileIconProvider().icon(QFileInfo(path));
|
| if (!icon.isNull()) {
|
| thumbnail = icon.pixmap(thumbnailSize);
|
| }
|
| else {
|
| thumbnail = QPixmap(thumbnailSize, thumbnailSize);
|
| thumbnail.fill();
|
| }
|
| }
|
|
|
|
|
| if (!thumbnail.isNull()) {
|
| QString cacheKey = getCacheKey(path, thumbnailSize);
|
| if (!cacheKey.isEmpty()) {
|
| _thumbnailCache.insert(cacheKey, new QPixmap(thumbnail), 1);
|
| }
|
| }
|
|
|
| return thumbnail;
|
| }
|
|
|