QmlDesigner: Add navigator preview tooltip for images and textures

Show custom tooltip for image and texture items that shows the source
image.

Task-number: QDS-2750
Change-Id: If8c491f513d9fcada74300206d6cbc3af80c7663
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2020-09-08 11:41:45 +03:00
parent 0b6be2f93d
commit 8dbe3fe61f
10 changed files with 444 additions and 11 deletions

View File

@@ -336,6 +336,8 @@ extend_qtc_plugin(QmlDesigner
navigatorwidget.cpp navigatorwidget.h navigatorwidget.cpp navigatorwidget.h
choosetexturepropertydialog.cpp choosetexturepropertydialog.h choosetexturepropertydialog.cpp choosetexturepropertydialog.h
choosetexturepropertydialog.ui choosetexturepropertydialog.ui
previewtooltip.cpp previewtooltip.h
previewtooltip.ui
) )
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner

View File

@@ -6,7 +6,8 @@ SOURCES += navigatorview.cpp \
nameitemdelegate.cpp \ nameitemdelegate.cpp \
iconcheckboxitemdelegate.cpp \ iconcheckboxitemdelegate.cpp \
navigatortreeview.cpp \ navigatortreeview.cpp \
choosetexturepropertydialog.cpp choosetexturepropertydialog.cpp \
previewtooltip.cpp
HEADERS += navigatorview.h \ HEADERS += navigatorview.h \
navigatortreemodel.h \ navigatortreemodel.h \
@@ -15,8 +16,10 @@ HEADERS += navigatorview.h \
iconcheckboxitemdelegate.h \ iconcheckboxitemdelegate.h \
navigatortreeview.h \ navigatortreeview.h \
navigatormodelinterface.h \ navigatormodelinterface.h \
choosetexturepropertydialog.h choosetexturepropertydialog.h \
previewtooltip.h
RESOURCES += navigator.qrc RESOURCES += navigator.qrc
FORMS += choosetexturepropertydialog.ui FORMS += choosetexturepropertydialog.ui \
previewtooltip.ui

View File

@@ -53,6 +53,7 @@
#include <QPointF> #include <QPointF>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QDateTime>
#include <coreplugin/messagebox.h> #include <coreplugin/messagebox.h>
@@ -200,6 +201,13 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
if (role == ItemIsVisibleRole) //independent of column if (role == ItemIsVisibleRole) //independent of column
return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked;
auto hasImageToolTip = [modelNode]() -> bool {
if (modelNode.isValid() && modelNode.metaInfo().isValid())
return modelNode.type() == "QtQuick.Image" || modelNode.type() == "QtQuick3D.Texture";
else
return false;
};
if (index.column() == 0) { if (index.column() == 0) {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
return modelNode.displayName(); return modelNode.displayName();
@@ -212,17 +220,81 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
} else if (role == Qt::ToolTipRole) { } else if (role == Qt::ToolTipRole) {
if (currentQmlObjectNode.hasError()) { if (currentQmlObjectNode.hasError()) {
QString errorString = currentQmlObjectNode.error(); QString errorString = currentQmlObjectNode.error();
if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool() && if (DesignerSettings::getValue(DesignerSettingsKey::STANDALONE_MODE).toBool()
currentQmlObjectNode.isRootNode()) && currentQmlObjectNode.isRootNode()) {
errorString.append(QString("\n%1").arg(tr("Changing the setting \"%1\" might solve the issue.").arg( errorString.append(QString("\n%1").arg(tr("Changing the setting \"%1\" might solve the issue.").arg(
tr("Use QML emulation layer that is built with the selected Qt")))); tr("Use QML emulation layer that is built with the selected Qt"))));
}
return errorString; return errorString;
} }
if (modelNode.metaInfo().isValid())
return modelNode.type(); if (modelNode.metaInfo().isValid()) {
if (hasImageToolTip())
return {}; // Images have special tooltip popup, so suppress regular one
else else
return modelNode.type();
} else {
return msgUnknownItem(QString::fromUtf8(modelNode.type())); return msgUnknownItem(QString::fromUtf8(modelNode.type()));
}
} else if (role == ToolTipImageRole) {
if (currentQmlObjectNode.hasError()) // Error already shown on regular tooltip
return {};
if (hasImageToolTip()) {
VariantProperty prop = modelNode.variantProperty("source");
QString imageSource = prop.value().toString();
QFileInfo fi(imageSource);
if (fi.isRelative())
imageSource = QmlDesignerPlugin::instance()->documentManager().currentFilePath().toFileInfo().dir().absoluteFilePath(imageSource);
fi = QFileInfo(imageSource);
QDateTime modified = fi.lastModified();
struct ImageData {
QDateTime time;
QImage image;
QString type;
QString id;
QString info;
};
static QHash<QString, ImageData> toolTipImageMap;
ImageData imageData;
bool reload = true;
if (toolTipImageMap.contains(imageSource)) {
imageData = toolTipImageMap[imageSource];
if (modified == imageData.time)
reload = false;
}
if (reload) {
QImage originalImage;
originalImage.load(imageSource);
if (!originalImage.isNull()) {
imageData.image = originalImage.scaled(150, 150, Qt::KeepAspectRatio);
double imgSize = double(fi.size());
imageData.type = QStringLiteral("%1 (%2)").arg(QString::fromLatin1(modelNode.type())).arg(fi.suffix());
imageData.id = modelNode.id();
static QStringList units({tr("B"), tr("KB"), tr("MB"), tr("GB")});
int unitIndex = 0;
while (imgSize > 1024. && unitIndex < units.size() - 1) {
++unitIndex;
imgSize /= 1024.;
}
imageData.info = QStringLiteral("%1 x %2 (%3%4)").arg(originalImage.width()).arg(originalImage.height())
.arg(QString::number(imgSize, 'g', 3)).arg(units[unitIndex]);
toolTipImageMap.insert(imageSource, imageData);
}
}
if (!imageData.image.isNull()) {
QVariantMap map;
map.insert("type", imageData.type);
map.insert("image", QVariant::fromValue<QImage>(imageData.image));
map.insert("id", imageData.id);
map.insert("info", imageData.info);
return map;
}
}
} else if (role == ModelNodeRole) { } else if (role == ModelNodeRole) {
return QVariant::fromValue<ModelNode>(modelNode); return QVariant::fromValue<ModelNode>(modelNode);
} }

View File

@@ -30,6 +30,7 @@
#include "navigatorview.h" #include "navigatorview.h"
#include "navigatortreemodel.h" #include "navigatortreemodel.h"
#include "qproxystyle.h" #include "qproxystyle.h"
#include "previewtooltip.h"
#include <metainfo.h> #include <metainfo.h>
@@ -42,7 +43,9 @@
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QStyleFactory> #include <QStyleFactory>
#include <QEvent>
#include <QImage>
#include <QApplication>
namespace QmlDesigner { namespace QmlDesigner {
@@ -174,5 +177,36 @@ void NavigatorTreeView::drawSelectionBackground(QPainter *painter, const QStyleO
painter->restore(); painter->restore();
} }
bool NavigatorTreeView::viewportEvent(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
auto navModel = qobject_cast<NavigatorTreeModel *>(model());
if (navModel) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
QModelIndex index = indexAt(helpEvent->pos());
QVariantMap imgMap = navModel->data(index, ToolTipImageRole).toMap();
if (!imgMap.isEmpty()) {
if (!m_previewToolTip)
m_previewToolTip = new PreviewToolTip(QApplication::activeWindow());
m_previewToolTip->setId(imgMap["id"].toString());
m_previewToolTip->setType(imgMap["type"].toString());
m_previewToolTip->setInfo(imgMap["info"].toString());
m_previewToolTip->setImage(imgMap["image"].value<QImage>());
m_previewToolTip->move(helpEvent->pos());
if (!m_previewToolTip->isVisible())
m_previewToolTip->show();
} else if (m_previewToolTip) {
m_previewToolTip->hide();
}
}
} else if (event->type() == QEvent::Leave) {
if (m_previewToolTip)
m_previewToolTip->hide();
}
return QTreeView::viewportEvent(event);
}
} }

View File

@@ -29,6 +29,8 @@
namespace QmlDesigner { namespace QmlDesigner {
class PreviewToolTip;
class NavigatorTreeView : public QTreeView class NavigatorTreeView : public QTreeView
{ {
Q_OBJECT Q_OBJECT
@@ -36,5 +38,9 @@ class NavigatorTreeView : public QTreeView
public: public:
NavigatorTreeView(QWidget *parent = nullptr); NavigatorTreeView(QWidget *parent = nullptr);
static void drawSelectionBackground(QPainter *painter, const QStyleOption &option); static void drawSelectionBackground(QPainter *painter, const QStyleOption &option);
bool viewportEvent(QEvent *event) override;
private:
PreviewToolTip *m_previewToolTip = nullptr;
}; };
} }

View File

@@ -48,7 +48,8 @@ class NavigatorTreeModel;
enum NavigatorRoles { enum NavigatorRoles {
ItemIsVisibleRole = Qt::UserRole, ItemIsVisibleRole = Qt::UserRole,
RowIsPropertyRole = Qt::UserRole + 1, RowIsPropertyRole = Qt::UserRole + 1,
ModelNodeRole = Qt::UserRole + 2 ModelNodeRole = Qt::UserRole + 2,
ToolTipImageRole = Qt::UserRole + 3
}; };
class NavigatorView : public AbstractView class NavigatorView : public AbstractView

View File

@@ -0,0 +1,70 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "previewtooltip.h"
#include "ui_previewtooltip.h"
#include <utils/theme/theme.h>
#include <QtGui/qpixmap.h>
namespace QmlDesigner {
PreviewToolTip::PreviewToolTip(QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::PreviewToolTip)
{
setAttribute(Qt::WA_TransparentForMouseEvents);
setWindowFlags(Qt::Widget);
m_ui->setupUi(this);
setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name()));
}
PreviewToolTip::~PreviewToolTip()
{
delete m_ui;
}
void PreviewToolTip::setId(const QString &id)
{
m_ui->idLabel->setText(id);
}
void PreviewToolTip::setType(const QString &type)
{
m_ui->typeLabel->setText(type);
}
void PreviewToolTip::setInfo(const QString &info)
{
m_ui->infoLabel->setText(info);
}
void PreviewToolTip::setImage(const QImage &image)
{
m_ui->imageLabel->setPixmap(QPixmap::fromImage(image));
}
}

View File

@@ -0,0 +1,52 @@
/****************************************************************************
**
** Copyright (C) 2020 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 <QtWidgets/qwidget.h>
#include <QtGui/qimage.h>
namespace QmlDesigner {
namespace Ui {
class PreviewToolTip;
}
class PreviewToolTip : public QWidget
{
Q_OBJECT
public:
explicit PreviewToolTip(QWidget *parent = nullptr);
~PreviewToolTip();
void setId(const QString &id);
void setType(const QString &type);
void setInfo(const QString &info);
void setImage(const QImage &image);
private:
Ui::PreviewToolTip *m_ui;
};
}

View File

@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmlDesigner::PreviewToolTip</class>
<widget class="QWidget" name="QmlDesigner::PreviewToolTip">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>160</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>150</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1000</width>
<height>1000</height>
</size>
</property>
<property name="windowTitle">
<string/>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="sizeGripEnabled" stdset="0">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="imageLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>140</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="text">
<string notr="true">&lt;image&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="idLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">&lt;id label&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="typeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">&lt;type label&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>3</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">&lt;info label&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -613,6 +613,9 @@ Project {
"navigator/choosetexturepropertydialog.cpp", "navigator/choosetexturepropertydialog.cpp",
"navigator/choosetexturepropertydialog.h", "navigator/choosetexturepropertydialog.h",
"navigator/choosetexturepropertydialog.ui", "navigator/choosetexturepropertydialog.ui",
"navigator/previewtooltip.cpp",
"navigator/previewtooltip.h",
"navigator/previewtooltip.ui",
"propertyeditor/aligndistribute.cpp", "propertyeditor/aligndistribute.cpp",
"propertyeditor/aligndistribute.h", "propertyeditor/aligndistribute.h",
"propertyeditor/designerpropertymap.cpp", "propertyeditor/designerpropertymap.cpp",