diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp index 4581bc6ce87..c0d763c906b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp @@ -44,10 +44,10 @@ QImage renderImage(ServerNodeInstance rootNodeInstance) QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize(); if (previewImageSize.isEmpty()) - previewImageSize = {640, 480}; + previewImageSize = {300, 300}; - if (previewImageSize.width() > 800 || previewImageSize.height() > 800) - previewImageSize.scale({800, 800}, Qt::KeepAspectRatio); + if (previewImageSize.width() > 300 || previewImageSize.height() > 300) + previewImageSize.scale({300, 300}, Qt::KeepAspectRatio); QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize); diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml index 51194cc3cb6..5921bf50520 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml @@ -33,6 +33,7 @@ MouseArea { onExited: tooltipBackend.hideTooltip() onCanceled: tooltipBackend.hideTooltip() onClicked: forceActiveFocus() + onPositionChanged: tooltipBackend.reposition() hoverEnabled: true @@ -40,8 +41,8 @@ MouseArea { interval: 1000 running: mouseArea.containsMouse onTriggered: { - tooltipBackend.componentName = itemName - tooltipBackend.componentPath = componentPath + tooltipBackend.name = itemName + tooltipBackend.path = componentPath tooltipBackend.showTooltip() } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 4448cd9f90e..a47fff62d6c 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -599,6 +599,8 @@ extend_qtc_plugin(QmlDesigner imagecache/imagecachecollector.h imagecache/imagecachecollector.cpp + imagecache/imagecachefontcollector.h + imagecache/imagecachefontcollector.cpp imagecache/imagecache.cpp imagecache/imagecachecollectorinterface.h imagecache/imagecacheconnectionmanager.cpp diff --git a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp index b16902ecd4e..29a47a313cd 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp @@ -26,6 +26,7 @@ #include "customfilesystemmodel.h" #include +#include #include @@ -37,8 +38,10 @@ #include #include #include -#include +#include #include +#include +#include namespace QmlDesigner { @@ -84,68 +87,6 @@ static QPixmap defaultPixmapForType(const QString &type, const QSize &size) return QPixmap(QStringLiteral(":/ItemLibrary/images/asset_%1_%2.png").arg(type).arg(size.width())); } -static QPixmap generateFontImage(const QFileInfo &info, const QSize &size) -{ - static QHash fontImageCache; - const QString file = info.absoluteFilePath(); - const QString key = QStringLiteral("%1@%2@%3").arg(file).arg(size.width()).arg(size.height()); - if (!fontImageCache.contains(key)) { - QPixmap pixmap(size); - pixmap.fill(Qt::transparent); - qreal pixelSize = size.width() / 2.; - bool done = false; - while (!done) { - QRawFont font(file, pixelSize); - if (!font.isValid()) - break; - QGlyphRun gr; - gr.setRawFont(font); - QVector indices = font.glyphIndexesForString("Abc"); - if (indices.isEmpty()) - break; - const QVector advances = font.advancesForGlyphIndexes(indices); - QVector positions; - QPointF totalAdvance; - for (const QPointF &advance : advances) { - positions.append(totalAdvance); - totalAdvance += advance; - } - - gr.setGlyphIndexes(indices); - gr.setPositions(positions); - QRectF bounds = gr.boundingRect(); - if (bounds.width() <= 0 || bounds.height() <= 0) - break; - - bounds.moveCenter({size.width() / 2., size.height() / 2.}); - - // Bounding rectangle doesn't necessarily contain the entirety of glyphs for some - // reason, so also check totalAdvance for overflow. - qreal limitX = qMax(bounds.width(), totalAdvance.x()); - if (size.width() < limitX) { - pixelSize = qreal(qFloor(pixelSize * size.width() / limitX)); - continue; - } - qreal limitY = qMax(bounds.height(), totalAdvance.y()); - if (size.height() < limitY) { - pixelSize = qreal(qFloor(pixelSize * size.height() / limitY)); - continue; - } - - QPainter painter(&pixmap); - painter.setPen(Theme::getColor(Theme::DStextColor)); - painter.drawGlyphRun(bounds.bottomLeft(), gr); - done = true; - } - - if (!done) - pixmap = defaultPixmapForType("font", size); - - fontImageCache[key] = pixmap; - } - return fontImageCache[key]; -} - QString fontFamily(const QFileInfo &info) { QRawFont font(info.absoluteFilePath(), 10); @@ -157,21 +98,27 @@ QString fontFamily(const QFileInfo &info) class ItemLibraryFileIconProvider : public QFileIconProvider { public: - ItemLibraryFileIconProvider() = default; + ItemLibraryFileIconProvider(ImageCache &fontImageCache) + : QFileIconProvider() + , m_fontImageCache(fontImageCache) + { + } QIcon icon( const QFileInfo & info ) const override { QIcon icon; const QString suffix = info.suffix(); + const QString filePath = info.absoluteFilePath(); + + if (supportedFontSuffixes().contains(suffix)) + return generateFontIcons(filePath); for (auto iconSize : iconSizes) { // Provide icon depending on suffix QPixmap pixmap; if (supportedImageSuffixes().contains(suffix)) - pixmap.load(info.absoluteFilePath()); - else if (supportedFontSuffixes().contains(suffix)) - pixmap = generateFontImage(info, iconSize); + pixmap.load(filePath); else if (supportedAudioSuffixes().contains(suffix)) pixmap = defaultPixmapForType("sound", iconSize); else if (supportedShaderSuffixes().contains(suffix)) @@ -189,6 +136,61 @@ public: return icon; } + QIcon generateFontIcons(const QString &filePath) const + { + QIcon icon; + QString colorName = Theme::getColor(Theme::DStextColor).name(); + std::condition_variable condition; + int count = iconSizes.size(); + std::mutex mutex; + QList> images; + + for (auto iconSize : iconSizes) { + m_fontImageCache.requestImage( + filePath, + [&images, &condition, &count, &mutex, iconSize](const QImage &image) { + int currentCount; + { + std::unique_lock lock{mutex}; + currentCount = --count; + images.append({iconSize, image}); + } + if (currentCount <= 0) + condition.notify_all(); + }, + [&images, &condition, &count, &mutex, iconSize] { + int currentCount; + { + std::unique_lock lock{mutex}; + currentCount = --count; + images.append({iconSize, {}}); + } + if (currentCount <= 0) + condition.notify_all(); + }, + QStringLiteral("%1@%2@Abc").arg(QString::number(iconSize.width()), + colorName) + ); + } + + { + // Block main thread until icons are generated, as it has to be done synchronously + std::unique_lock lock{mutex}; + if (count > 0) + condition.wait(lock, [&]{ return count <= 0; }); + } + + for (const auto &pair : qAsConst(images)) { + QImage image = pair.second; + if (image.isNull()) + icon.addPixmap(defaultPixmapForType("font", pair.first)); + else + icon.addPixmap(QPixmap::fromImage(image)); + } + + return icon; + } + // Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their // x2 versions for HDPI sceens QList iconSizes = {{384, 384}, {192, 192}, // Large @@ -197,13 +199,15 @@ public: {48, 48}, // Small {64, 64}, {32, 32}}; // List + ImageCache &m_fontImageCache; }; -CustomFileSystemModel::CustomFileSystemModel(QObject *parent) : QAbstractListModel(parent) - , m_fileSystemModel(new QFileSystemModel(this)) - , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) +CustomFileSystemModel::CustomFileSystemModel(ImageCache &fontImageCache, QObject *parent) + : QAbstractListModel(parent) + , m_fileSystemModel(new QFileSystemModel(this)) + , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) { - m_fileSystemModel->setIconProvider(new ItemLibraryFileIconProvider()); + m_fileSystemModel->setIconProvider(new ItemLibraryFileIconProvider(fontImageCache)); connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this] { updatePath(m_fileSystemModel->rootPath()); @@ -345,6 +349,20 @@ const QSet &CustomFileSystemModel::supportedSuffixes() const return allSuffixes; } +const QSet &CustomFileSystemModel::previewableSuffixes() const +{ + static QSet previewableSuffixes; + if (previewableSuffixes.isEmpty()) { + auto insertSuffixes = [](const QStringList &suffixes) { + for (const auto &suffix : suffixes) + previewableSuffixes.insert(suffix); + }; + insertSuffixes(supportedFontSuffixes()); + } + return previewableSuffixes; + +} + void CustomFileSystemModel::appendIfNotFiltered(const QString &file) { if (filterMetaIcons(file)) diff --git a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h b/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h index 8825304be1c..1e079db8eef 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h @@ -39,11 +39,13 @@ namespace Utils { class FileSystemWatcher; } namespace QmlDesigner { +class ImageCache; + class CustomFileSystemModel : public QAbstractListModel { Q_OBJECT public: - CustomFileSystemModel(QObject *parent = nullptr); + CustomFileSystemModel(ImageCache &fontImageCache, QObject *parent = nullptr); void setFilter(QDir::Filters filters); QString rootPath() const; @@ -64,6 +66,7 @@ public: QPair resourceTypeAndData(const QModelIndex &index) const; const QSet &supportedSuffixes() const; + const QSet &previewableSuffixes() const; private: QModelIndex updatePath(const QString &newPath); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp index 60b4eac3aed..cbe21c190eb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp @@ -27,6 +27,10 @@ #include "customfilesystemmodel.h" +#include +#include +#include + #include #include #include @@ -35,6 +39,7 @@ #include #include #include +#include #include @@ -58,7 +63,7 @@ void ItemLibraryResourceView::addSizeAction(QActionGroup *group, const QString & }); } -ItemLibraryResourceView::ItemLibraryResourceView(QWidget *parent) : +ItemLibraryResourceView::ItemLibraryResourceView(ImageCache &fontImageCache, QWidget *parent) : QListView(parent) { setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -102,6 +107,20 @@ ItemLibraryResourceView::ItemLibraryResourceView(QWidget *parent) : defaultAction->toggle(); addActions(actionGroup->actions()); + + viewport()->setAttribute(Qt::WA_Hover); + m_fontPreviewTooltipBackend = std::make_unique(fontImageCache); + // Note: Though the text specified here appears in UI, it shouldn't be translated, as it's + // a commonly used sentence to preview the font glyphs in latin fonts. + // For fonts that do not have latin glyphs, the font family name will have to + // suffice for preview. Font family name is inserted into %1 at render time. + m_fontPreviewTooltipBackend->setState(QStringLiteral("%1@%2@%3") + .arg(QString::number(300), + Theme::getColor(Theme::DStextColor).name(), + QStringLiteral("%1\n\n" + "The quick brown fox jumps\n" + "over the lazy dog\n" + "1234567890"))); } void ItemLibraryResourceView::startDrag(Qt::DropActions /* supportedActions */) @@ -136,5 +155,51 @@ void ItemLibraryResourceView::startDrag(Qt::DropActions /* supportedActions */) drag->exec(); } +bool ItemLibraryResourceView::viewportEvent(QEvent *event) +{ + if (event->type() == QEvent::ToolTip) { + auto fileSystemModel = qobject_cast(model()); + Q_ASSERT(fileSystemModel); + QHelpEvent *helpEvent = static_cast(event); + QModelIndex index = indexAt(helpEvent->pos()); + if (index.isValid()) { + QFileInfo fi = fileSystemModel->fileInfo(index); + if (fileSystemModel->previewableSuffixes().contains(fi.suffix())) { + QString filePath = fi.absoluteFilePath(); + if (!filePath.isEmpty()) { + if (!m_fontPreviewTooltipBackend->isVisible() + || m_fontPreviewTooltipBackend->path() != filePath) { + m_fontPreviewTooltipBackend->setPath(filePath); + m_fontPreviewTooltipBackend->setName(fi.fileName()); + m_fontPreviewTooltipBackend->showTooltip(); + } else { + m_fontPreviewTooltipBackend->reposition(); + } + return true; + } + } + } + m_fontPreviewTooltipBackend->hideTooltip(); + } else if (event->type() == QEvent::Leave) { + m_fontPreviewTooltipBackend->hideTooltip(); + } else if (event->type() == QEvent::HoverMove) { + if (m_fontPreviewTooltipBackend->isVisible()) { + auto fileSystemModel = qobject_cast(model()); + Q_ASSERT(fileSystemModel); + auto *he = static_cast(event); + QModelIndex index = indexAt(he->pos()); + if (index.isValid()) { + QFileInfo fi = fileSystemModel->fileInfo(index); + if (fi.absoluteFilePath() != m_fontPreviewTooltipBackend->path()) + m_fontPreviewTooltipBackend->hideTooltip(); + else + m_fontPreviewTooltipBackend->reposition(); + } + } + } + + return QListView::viewportEvent(event); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h index a29edac8670..590d4ad68ef 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h @@ -33,15 +33,22 @@ QT_END_NAMESPACE namespace QmlDesigner { +class PreviewTooltipBackend; +class ImageCache; + class ItemLibraryResourceView : public QListView { Q_OBJECT public: - explicit ItemLibraryResourceView(QWidget *parent = nullptr); + explicit ItemLibraryResourceView(ImageCache &fontImageCache, QWidget *parent = nullptr); void startDrag(Qt::DropActions supportedActions) override; + bool viewportEvent(QEvent *event) override; + private: void addSizeAction(QActionGroup *group, const QString &text, int size, int iconSize); + + std::unique_ptr m_fontPreviewTooltipBackend; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 5360417432a..7004f3f3cd8 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -55,9 +56,12 @@ public: ImageCacheStorage storage{database}; ImageCacheConnectionManager connectionManager; ImageCacheCollector collector{connectionManager}; + ImageCacheFontCollector fontCollector; ImageCacheGenerator generator{collector, storage}; + ImageCacheGenerator fontGenerator{fontCollector, storage}; TimeStampProvider timeStampProvider; ImageCache cache{storage, generator, timeStampProvider}; + ImageCache fontImageCache{storage, fontGenerator, timeStampProvider}; }; ItemLibraryView::ItemLibraryView(QObject* parent) @@ -78,7 +82,7 @@ bool ItemLibraryView::hasWidget() const WidgetInfo ItemLibraryView::widgetInfo() { if (m_widget.isNull()) { - m_widget = new ItemLibraryWidget{m_imageCacheData->cache}; + m_widget = new ItemLibraryWidget{m_imageCacheData->cache, m_imageCacheData->fontImageCache}; m_widget->setImportsWidget(m_importManagerView->widgetInfo().widget); } @@ -155,7 +159,7 @@ void ItemLibraryView::importsChanged(const QList &addedImports, const QL void ItemLibraryView::setResourcePath(const QString &resourcePath) { if (m_widget.isNull()) - m_widget = new ItemLibraryWidget{m_imageCacheData->cache}; + m_widget = new ItemLibraryWidget{m_imageCacheData->cache, m_imageCacheData->fontImageCache}; m_widget->setResourcePath(resourcePath); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index a0e2214c730..96aa07bf9eb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -83,10 +83,10 @@ static QString propertyEditorResourcesPath() { return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources"); } -ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache) +ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontImageCache) : m_itemIconSize(24, 24) , m_itemViewQuickWidget(new QQuickWidget(this)) - , m_resourcesView(new ItemLibraryResourceView(this)) + , m_resourcesView(new ItemLibraryResourceView(fontImageCache, this)) , m_importTagsWidget(new QWidget(this)) , m_addResourcesWidget(new QWidget(this)) , m_imageCache{imageCache} @@ -115,12 +115,11 @@ ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache) m_previewTooltipBackend = std::make_unique(m_imageCache); m_itemViewQuickWidget->rootContext()->setContextProperty("tooltipBackend", m_previewTooltipBackend.get()); - m_itemViewQuickWidget->setClearColor( Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); /* create Resources view and its model */ - m_resourcesFileSystemModel = new CustomFileSystemModel(this); + m_resourcesFileSystemModel = new CustomFileSystemModel(fontImageCache, this); m_resourcesView->setModel(m_resourcesFileSystemModel.data()); /* create image provider for loading item icons */ diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index b08ccec88d9..c4ca4c6e702 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -69,7 +69,7 @@ class ItemLibraryWidget : public QFrame }; public: - ItemLibraryWidget(ImageCache &imageCache); + ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontImageCache); ~ItemLibraryWidget(); void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo); diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp index d3c972c2176..048e33f9aa5 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp @@ -36,28 +36,36 @@ PreviewImageTooltip::PreviewImageTooltip(QWidget *parent) : QWidget(parent) , m_ui(std::make_unique()) { - // setAttribute(Qt::WA_TransparentForMouseEvents); - setWindowFlags(Qt::ToolTip); + setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowTransparentForInput + | Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus); m_ui->setupUi(this); + m_ui->nameLabel->setElideMode(Qt::ElideLeft); + m_ui->pathLabel->setElideMode(Qt::ElideLeft); + m_ui->infoLabel->setElideMode(Qt::ElideLeft); setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name())); } PreviewImageTooltip::~PreviewImageTooltip() = default; -void PreviewImageTooltip::setComponentPath(const QString &path) +void PreviewImageTooltip::setName(const QString &name) { - m_ui->componentPathLabel->setText(path); + m_ui->nameLabel->setText(name); } -void PreviewImageTooltip::setComponentName(const QString &name) +void PreviewImageTooltip::setPath(const QString &path) { - m_ui->componentNameLabel->setText(name); + m_ui->pathLabel->setText(path); +} + +void PreviewImageTooltip::setInfo(const QString &info) +{ + m_ui->infoLabel->setText(info); } void PreviewImageTooltip::setImage(const QImage &image) { - resize(image.width() + 20 + m_ui->componentNameLabel->width(), - std::max(image.height() + 20, height())); - m_ui->imageLabel->setPixmap(QPixmap::fromImage({image})); + m_ui->imageLabel->setPixmap(QPixmap::fromImage({image}).scaled(m_ui->imageLabel->width(), + m_ui->imageLabel->height(), + Qt::KeepAspectRatio)); } } diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h index e05b8a07277..09ca27fa2e3 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h @@ -43,8 +43,9 @@ public: explicit PreviewImageTooltip(QWidget *parent = {}); ~PreviewImageTooltip(); - void setComponentPath(const QString &path); - void setComponentName(const QString &name); + void setName(const QString &name); + void setPath(const QString &path); + void setInfo(const QString &info); void setImage(const QImage &pixmap); private: diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui index 16f34fae07d..fec0ba8f384 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui @@ -6,20 +6,20 @@ 0 0 - 400 - 300 + 651 + 318 - + 0 0 - 200 - 200 + 300 + 140 @@ -64,37 +64,36 @@ 1 - - - + + + 6 + + + 6 + + + 6 + + + 6 + + + 6 + + + - + 0 - 1 + 0 - 0 - 0 + 300 + 300 - - - - - Qt::AlignCenter - - - true - - - Qt::NoTextInteraction - - - - - QFrame::Box @@ -102,43 +101,89 @@ QFrame::Plain - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignCenter - - - - - 0 - 1 - - - - - 0 - 0 - - - - - 12 - 75 - true - - - - - - - Qt::AlignCenter - - - true - - - Qt::NoTextInteraction - + + + + + + + + 0 + 1 + + + + + 0 + 0 + + + + <name label> + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 0 + 1 + + + + + 0 + 0 + + + + <path label> + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 0 + 1 + + + + + 0 + 0 + + + + <info label> + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp index 4fac1432e58..8073ebeebf3 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp @@ -32,6 +32,7 @@ #include #include +#include namespace QmlDesigner { @@ -46,28 +47,25 @@ PreviewTooltipBackend::~PreviewTooltipBackend() void PreviewTooltipBackend::showTooltip() { - if (m_componentPath.isEmpty()) - return; - m_tooltip = std::make_unique(); - m_tooltip->setComponentName(m_componentName); - m_tooltip->setComponentPath(m_componentPath); + m_tooltip->setName(m_name); + m_tooltip->setPath(m_path); + m_tooltip->setInfo(m_info); m_cache.requestImage( - m_componentPath, + m_path, [tooltip = QPointer(m_tooltip.get())](const QImage &image) { QMetaObject::invokeMethod(tooltip, [tooltip, image] { if (tooltip) tooltip->setImage(image); }); }, - [] {}); + [] {}, + m_state + ); - auto mousePosition = QCursor::pos(); - - mousePosition += {20, 20}; - m_tooltip->move(mousePosition); + reposition(); m_tooltip->show(); } @@ -79,30 +77,94 @@ void PreviewTooltipBackend::hideTooltip() m_tooltip.reset(); } -QString QmlDesigner::PreviewTooltipBackend::componentPath() const +bool PreviewTooltipBackend::isVisible() const { - return m_componentPath; + if (m_tooltip) + return m_tooltip->isVisible(); + return false; } -void QmlDesigner::PreviewTooltipBackend::setComponentPath(const QString &path) +void PreviewTooltipBackend::reposition() { - m_componentPath = path; + if (m_tooltip) { + // By default tooltip is placed right and below the cursor, but if the screen + // doesn't have sufficient space in that direction, position is adjusted. + // It is assumed that some diagonal direction will have enough space. + const QPoint mousePos = QCursor::pos(); + QScreen *screen = qApp->screenAt(mousePos); + QRect tipRect = m_tooltip->geometry(); + QPoint offset(10, 5); + QPoint pos = mousePos + offset; + if (screen) { + QRect rect = screen->geometry(); + tipRect.moveTo(pos); + if (!rect.contains(tipRect)) { + pos = mousePos + QPoint(-offset.x() - m_tooltip->size().width(), + offset.y()); + tipRect.moveTo(pos); + if (!rect.contains(tipRect)) { + pos = mousePos + QPoint(offset.x(), -offset.y() - m_tooltip->size().height()); + tipRect.moveTo(pos); + if (!rect.contains(tipRect)) { + pos = mousePos + QPoint(-offset.x() - m_tooltip->size().width(), + -offset.y() - m_tooltip->size().height()); + tipRect.moveTo(pos); + } + } + } + } - if (m_componentPath != path) - emit componentPathChanged(); + m_tooltip->move(pos); + } } -QString QmlDesigner::PreviewTooltipBackend::componentName() const +QString PreviewTooltipBackend::name() const { - return m_componentName; + return m_name; } -void QmlDesigner::PreviewTooltipBackend::setComponentName(const QString &name) +void PreviewTooltipBackend::setName(const QString &name) { - m_componentName = name; + m_name = name; + if (m_name != name) + emit nameChanged(); +} - if (m_componentName != name) - emit componentNameChanged(); +QString PreviewTooltipBackend::path() const +{ + return m_path; +} + +void PreviewTooltipBackend::setPath(const QString &path) +{ + m_path = path; + if (m_path != path) + emit pathChanged(); +} + +QString PreviewTooltipBackend::info() const +{ + return m_info; +} + +void PreviewTooltipBackend::setInfo(const QString &info) +{ + m_info = info; + if (m_info != info) + emit infoChanged(); +} + +QString PreviewTooltipBackend::state() const +{ + return m_state; +} + +// Sets the imageCache state hint. Valid content depends on image cache collector used. +void PreviewTooltipBackend::setState(const QString &state) +{ + m_state = state; + if (m_state != state) + emit stateChanged(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h index b5f777662b9..a8f1f091bc2 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h @@ -39,8 +39,10 @@ class PreviewTooltipBackend : public QObject { Q_OBJECT - Q_PROPERTY(QString componentPath READ componentPath WRITE setComponentPath NOTIFY componentPathChanged) - Q_PROPERTY(QString componentName READ componentName WRITE setComponentName NOTIFY componentNameChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged) + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) public: PreviewTooltipBackend(ImageCache &cache); @@ -48,20 +50,30 @@ public: Q_INVOKABLE void showTooltip(); Q_INVOKABLE void hideTooltip(); + Q_INVOKABLE void reposition(); - QString componentPath() const; - void setComponentPath(const QString &path); + QString name() const; + void setName(const QString &name); + QString path() const; + void setPath(const QString &path); + QString info() const; + void setInfo(const QString &info); + QString state() const; + void setState(const QString &state); - QString componentName() const; - void setComponentName(const QString &path); + bool isVisible() const; signals: - void componentPathChanged(); - void componentNameChanged(); + void nameChanged(); + void pathChanged(); + void infoChanged(); + void stateChanged(); private: - QString m_componentPath; - QString m_componentName; + QString m_name; + QString m_path; + QString m_info; + QString m_state; std::unique_ptr m_tooltip; ImageCache &m_cache; }; diff --git a/src/plugins/qmldesigner/designercore/designercore-lib.pri b/src/plugins/qmldesigner/designercore/designercore-lib.pri index 1fe37967182..28351c511c2 100644 --- a/src/plugins/qmldesigner/designercore/designercore-lib.pri +++ b/src/plugins/qmldesigner/designercore/designercore-lib.pri @@ -15,6 +15,7 @@ include (../../../../share/qtcreator/qml/qmlpuppet/types/types.pri) SOURCES += $$PWD/model/abstractview.cpp \ $$PWD/imagecache/imagecachecollector.cpp \ + $$PWD/imagecache/imagecachefontcollector.cpp \ $$PWD/model/rewriterview.cpp \ $$PWD/model/documentmessage.cpp \ $$PWD/metainfo/metainfo.cpp \ @@ -95,6 +96,7 @@ SOURCES += $$PWD/model/abstractview.cpp \ HEADERS += $$PWD/include/qmldesignercorelib_global.h \ $$PWD/imagecache/imagecachecollector.h \ + $$PWD/imagecache/imagecachefontcollector.h \ $$PWD/include/abstractview.h \ $$PWD/include/nodeinstanceview.h \ $$PWD/include/rewriterview.h \ diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp new file mode 100644 index 00000000000..571370b36f4 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "imagecachefontcollector.h" + +#include + +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +ImageCacheFontCollector::ImageCacheFontCollector() = default; + +ImageCacheFontCollector::~ImageCacheFontCollector() = default; + +QByteArray fileToByteArray(QString const &filename) +{ + QFile file(filename); + QFileInfo fileInfo(file); + + if (fileInfo.exists() && file.open(QFile::ReadOnly)) + return file.readAll(); + + return {}; +} + +void ImageCacheFontCollector::start(Utils::SmallStringView name, + Utils::SmallStringView state, + CaptureCallback captureCallback, + AbortCallback abortCallback) +{ + // State contains size, text color, and sample text + QStringList hints = QString(state).split('@'); + int dim(300); + if (hints.size() >= 1) { + bool ok = false; + int newDim = QString(hints[0]).toInt(&ok); + if (ok) + dim = newDim; + } + QColor textColor(Theme::getColor(Theme::DStextColor)); + if (hints.size() >= 2) + textColor.setNamedColor(hints[1]); + QString text = hints.size() >= 3 ? hints[2] : "Abc"; + QSize size(dim, dim); + QRect rect({0, 0}, size); + + QByteArray fontData(fileToByteArray(QString(name))); + if (!fontData.isEmpty()) { + int fontId = QFontDatabase::addApplicationFontFromData(fontData); + if (fontId != -1) { + QRawFont rawFont(fontData, 10.); // Pixel size is irrelevant, we only need style/weight + const QStringList families = QFontDatabase::applicationFontFamilies(fontId); + if (!families.isEmpty()) { + QString fontFamily = families.first(); + if (text.contains("%1")) + text = text.arg(fontFamily); + QFont font(fontFamily); + font.setStyle(rawFont.style()); + font.setStyleName(rawFont.styleName()); + font.setWeight(rawFont.weight()); + QImage image(size, QImage::Format_ARGB32); + image.fill(Qt::transparent); + int pixelSize(200); + int flags = Qt::AlignCenter; + while (pixelSize >= 2) { + font.setPixelSize(pixelSize); + QFontMetrics fm(font, &image); + QRect bounds = fm.boundingRect(rect, flags, text); + if (bounds.width() < rect.width() && bounds.height() < rect.height()) { + break; + } else { + int newPixelSize = pixelSize - 1; + if (bounds.width() >= rect.width()) + newPixelSize = int(qreal(pixelSize) * qreal(rect.width()) / qreal(bounds.width())); + else if (bounds.height() >= rect.height()) + newPixelSize = int(qreal(pixelSize) * qreal(rect.height()) / qreal(bounds.height())); + if (newPixelSize < pixelSize) + pixelSize = newPixelSize; + else + --pixelSize; + } + } + + QPainter painter(&image); + painter.setPen(textColor); + painter.setFont(font); + painter.drawText(rect, flags, text); + + captureCallback(std::move(image)); + return; + } + QFontDatabase::removeApplicationFont(fontId); + } + } + abortCallback(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h new file mode 100644 index 00000000000..61704ffe286 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "imagecachecollectorinterface.h" + +namespace QmlDesigner { + +class ImageCacheFontCollector final : public ImageCacheCollectorInterface +{ +public: + ImageCacheFontCollector(); + + ~ImageCacheFontCollector(); + + void start(Utils::SmallStringView filePath, + Utils::SmallStringView state, + CaptureCallback captureCallback, + AbortCallback abortCallback) override; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 782cd21c803..8fdc49ae0f6 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -418,6 +418,8 @@ Project { "include/imagecacheinterface.h", "imagecache/imagecachecollector.cpp", "imagecache/imagecachecollector.h", + "imagecache/imagecachefontcollector.cpp", + "imagecache/imagecachefontcollector.h", "imagecache/imagecache.cpp", "imagecache/imagecachecollectorinterface.h", "imagecache/imagecacheconnectionmanager.cpp",