| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #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;
|
| | }
|
| |
|