Implement new Assets library

Build a new Qml based item library assets view.

Task-number: QDS-3590
Change-Id: Ib3a4bcac8f873469ec5f3429817c49f466ec1e2a
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Mahmoud Badri
2021-05-04 22:35:50 +03:00
parent 5525f2b112
commit 4786b03594
17 changed files with 1143 additions and 758 deletions

View File

@@ -0,0 +1,184 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuickDesignerTheme 1.0
import HelperWidgets 2.0
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
Item {
DropArea {
id: dropArea
property var files // list of supported dropped files
enabled: true
anchors.fill: parent
onEntered: {
files = []
for (var i = 0; i < drag.urls.length; ++i) {
var url = drag.urls[i].toString();
if (url.startsWith("file:///")) // remove file scheme (happens on Windows)
url = url.substr(8)
var ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase()
if (rootView.supportedSuffixes().includes('*.' + ext))
files.push(url)
}
if (files.length === 0)
drag.accepted = false;
}
onDropped: {
if (files.length > 0)
rootView.handleFilesDrop(files)
}
}
ScrollView { // TODO: experiment using ListView instead of ScrollView + Column
id: assetsView
anchors.fill: parent
Column {
spacing: 2
Repeater {
model: assetsModel // context property
delegate: dirSection
}
Component {
id: dirSection
Section {
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
caption: dirName
sectionHeight: 30
sectionFontSize: 15
contentLevel: dirDepth
levelShift: 20
leftPadding: 0
hideHeader: dirDepth === 0
showLeftBorder: true
expanded: dirExpanded
visible: dirVisible
expandOnClick: false
onToggleExpand: {
dirExpanded = !dirExpanded
}
Column {
spacing: 5
leftPadding: 15
Repeater {
model: dirsModel
delegate: dirSection
}
Repeater {
model: filesModel
delegate: fileSection
}
}
}
}
Component {
id: fileSection
Rectangle {
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
height: img.height
color: mouseArea.containsMouse ? "#444444" : "transparent"
Row {
spacing: 5
Image {
id: img
asynchronous: true
width: 48
height: 48
source: "image://qmldesigner_assets/" + filePath
}
Text {
text: fileName
color: StudioTheme.Values.themeTextColor
font.pixelSize: 14
anchors.verticalCenter: parent.verticalCenter
}
}
readonly property string suffix: fileName.substr(-4)
readonly property bool isFont: suffix === ".ttf" || suffix === ".otf"
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onExited: tooltipBackend.hideTooltip()
onCanceled: tooltipBackend.hideTooltip()
onPositionChanged: tooltipBackend.reposition()
onPressed: {
forceActiveFocus()
if (mouse.button === Qt.LeftButton)
rootView.startDragAsset(filePath)
else
print("TODO: impl context menu")
}
ToolTip {
visible: !isFont && mouseArea.containsMouse
text: filePath
delay: 1000
}
Timer {
interval: 1000
running: mouseArea.containsMouse
onTriggered: {
if (suffix === ".ttf" || suffix === ".otf") {
tooltipBackend.name = fileName
tooltipBackend.path = filePath
tooltipBackend.showTooltip()
}
}
}
}
}
}
}
}
}

View File

@@ -38,12 +38,14 @@ Item {
property alias sectionFontSize: label.font.pixelSize
property alias showTopSeparator: topSeparator.visible
property alias showArrow: arrow.visible
property alias showLeftBorder: leftBorder.visible
property int leftPadding: 8
property int rightPadding: 0
property bool expanded: true
property int level: 0
property int level: 0 // affects arrow and title
property int contentLevel: 0 // affects whole section
property int levelShift: 10
property bool hideHeader: false
property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead
@@ -138,6 +140,14 @@ Item {
anchors.top: topSpacer.bottom
}
Rectangle {
id: leftBorder
visible: false
width: 1
height: parent.height - 15
color: header.color
}
Item {
id: bottomSpacer
height: addBottomPadding && row.height > 0 ? StudioTheme.Values.sectionHeadSpacerHeight : 0
@@ -160,12 +170,12 @@ Item {
]
transitions: Transition {
id: trans
enabled: false
NumberAnimation {
properties: "implicitHeight,rotation";
duration: 120;
easing.type: Easing.OutCubic
}
id: trans
enabled: false
NumberAnimation {
properties: "implicitHeight,rotation";
duration: 120;
easing.type: Easing.OutCubic
}
}
}

View File

@@ -310,12 +310,10 @@ extend_qtc_plugin(QmlDesigner
assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h
assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h
assetimportupdatetreeview.cpp assetimportupdatetreeview.h
customfilesystemmodel.cpp customfilesystemmodel.h
itemlibrary.qrc
itemlibraryimageprovider.cpp itemlibraryimageprovider.h
itemlibraryitem.cpp itemlibraryitem.h
itemlibrarymodel.cpp itemlibrarymodel.h
itemlibraryresourceview.cpp itemlibraryresourceview.h
itemlibrarycategory.cpp itemlibrarycategory.h
itemlibraryitemsmodel.cpp itemlibraryitemsmodel.h
itemlibraryview.cpp itemlibraryview.h
@@ -327,6 +325,11 @@ extend_qtc_plugin(QmlDesigner
itemlibraryimport.cpp itemlibraryimport.h
itemlibrarycategoriesmodel.cpp itemlibrarycategoriesmodel.h
itemlibraryaddimportmodel.cpp itemlibraryaddimportmodel.h
itemlibraryassetsmodel.cpp itemlibraryassetsmodel.h
itemlibraryassetsiconprovider.cpp itemlibraryassetsiconprovider.h
itemlibraryassetsdir.cpp itemlibraryassetsdir.h
itemlibraryassetsdirsmodel.cpp itemlibraryassetsdirsmodel.h
itemlibraryassetsfilesmodel.cpp itemlibraryassetsfilesmodel.h
)
extend_qtc_plugin(QmlDesigner

View File

@@ -1,437 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 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 "customfilesystemmodel.h"
#include <synchronousimagecache.h>
#include <theme.h>
#include <hdrimage.h>
#include <utils/filesystemwatcher.h>
#include <QDir>
#include <QDirIterator>
#include <QFileIconProvider>
#include <QFileSystemModel>
#include <QFont>
#include <QImageReader>
#include <QPainter>
#include <QRawFont>
#include <qmath.h>
#include <condition_variable>
#include <mutex>
namespace QmlDesigner {
static const QStringList &supportedImageSuffixes()
{
static QStringList retList;
if (retList.isEmpty()) {
const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
for (const QByteArray &suffix : suffixes)
retList.append(QString::fromUtf8(suffix));
}
return retList;
}
static const QStringList &supportedFragmentShaderSuffixes()
{
static const QStringList retList {"frag", "glsl", "glslf", "fsh"};
return retList;
}
static const QStringList &supportedShaderSuffixes()
{
static const QStringList retList {"frag", "vert",
"glsl", "glslv", "glslf",
"vsh", "fsh"};
return retList;
}
static const QStringList &supportedFontSuffixes()
{
static const QStringList retList {"ttf", "otf"};
return retList;
}
static const QStringList &supportedAudioSuffixes()
{
static const QStringList retList {"wav"};
return retList;
}
static const QStringList &supportedTexture3DSuffixes()
{
// These are file types only supported by 3D textures
static QStringList retList {"hdr"};
return retList;
}
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 texturePixmap(const QString &fileName)
{
return HdrImage{fileName}.toPixmap();
}
QString fontFamily(const QFileInfo &info)
{
QRawFont font(info.absoluteFilePath(), 10);
if (font.isValid())
return font.familyName();
return {};
}
class ItemLibraryFileIconProvider : public QFileIconProvider
{
public:
ItemLibraryFileIconProvider(SynchronousImageCache &fontImageCache,
QHash<QString, QPair<QDateTime, QIcon>> &iconCache)
: QFileIconProvider()
, m_fontImageCache(fontImageCache)
, m_iconCache(iconCache)
{
}
QIcon icon( const QFileInfo & info ) const override
{
const QString filePath = info.absoluteFilePath();
QPair<QDateTime, QIcon> &cachedIcon = m_iconCache[filePath];
if (!cachedIcon.second.isNull() && cachedIcon.first == info.lastModified())
return cachedIcon.second;
QIcon icon;
const QString suffix = info.suffix().toLower();
// Provide icon depending on suffix
QPixmap origPixmap;
if (supportedFontSuffixes().contains(suffix))
return generateFontIcons(filePath);
else if (supportedImageSuffixes().contains(suffix))
origPixmap.load(filePath);
else if (supportedTexture3DSuffixes().contains(suffix))
origPixmap = texturePixmap(filePath);
for (auto iconSize : iconSizes) {
QPixmap pixmap = origPixmap;
if (pixmap.isNull()) {
if (supportedAudioSuffixes().contains(suffix))
pixmap = defaultPixmapForType("sound", iconSize);
else if (supportedShaderSuffixes().contains(suffix))
pixmap = defaultPixmapForType("shader", iconSize);
if (pixmap.isNull())
return QFileIconProvider::icon(info);
}
if ((pixmap.width() > iconSize.width()) || (pixmap.height() > iconSize.height()))
pixmap = pixmap.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
icon.addPixmap(pixmap);
}
cachedIcon.first = info.lastModified();
cachedIcon.second = icon;
return icon;
}
QIcon generateFontIcons(const QString &filePath) const
{
return m_fontImageCache.icon(
filePath,
{},
ImageCache::FontCollectorSizesAuxiliaryData{Utils::span{iconSizes},
Theme::getColor(Theme::DStextColor).name(),
"Abc"});
}
private:
// Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their
// x2 versions for HDPI sceens
std::vector<QSize> iconSizes = {{384, 384},
{192, 192}, // Large
{256, 256},
{128, 128}, // Drag
{96, 96}, // Medium
{48, 48}, // Small
{64, 64},
{32, 32}}; // List
SynchronousImageCache &m_fontImageCache;
QHash<QString, QPair<QDateTime, QIcon>> &m_iconCache;
};
CustomFileSystemModel::CustomFileSystemModel(SynchronousImageCache &fontImageCache, QObject *parent)
: QAbstractListModel(parent)
, m_fileSystemModel(new QFileSystemModel(this))
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
, m_fontImageCache(fontImageCache)
{
m_updatePathTimer.setInterval(200);
m_updatePathTimer.setSingleShot(true);
m_updatePathTimer.callOnTimeout([this]() {
updatePath(m_fileSystemModel->rootPath());
});
// If project directory contents change, or one of the asset files is modified, we must
// reconstruct the model to update the icons
connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this] {
m_updatePathTimer.start();
});
connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged, [this] {
m_updatePathTimer.start();
});
}
void CustomFileSystemModel::setFilter(QDir::Filters)
{
}
bool filterMetaIcons(const QString &fileName)
{
QFileInfo info(fileName);
if (info.dir().path().split("/").contains("designer")) {
QDir currentDir = info.dir();
int i = 0;
while (!currentDir.isRoot() && i < 3) {
if (currentDir.dirName() == "designer") {
if (!currentDir.entryList({"*.metainfo"}).isEmpty())
return false;
}
currentDir.cdUp();
++i;
}
if (info.dir().dirName() == "designer")
return false;
}
return true;
}
QModelIndex CustomFileSystemModel::setRootPath(const QString &newPath)
{
if (m_fileSystemModel->rootPath() == newPath)
return QAbstractListModel::index(0, 0);
return updatePath(newPath);
}
QVariant CustomFileSystemModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::ToolTipRole)
return fileInfo(index).filePath();
if (role == Qt::FontRole) {
QFont font = m_fileSystemModel->data(fileSystemModelIndex(index), role).value<QFont>();
font.setPixelSize(Theme::instance()->smallFontPixelSize());
return font;
}
return m_fileSystemModel->data(fileSystemModelIndex(index), role);
}
int CustomFileSystemModel::rowCount(const QModelIndex &) const
{
return m_files.count();
}
int CustomFileSystemModel::columnCount(const QModelIndex &) const
{
return 1;
}
QModelIndex CustomFileSystemModel::indexForPath(const QString &path, int /*column*/) const
{
return QAbstractListModel::index(m_files.indexOf(path), 0);
}
QIcon CustomFileSystemModel::fileIcon(const QModelIndex &index) const
{
return m_fileSystemModel->fileIcon(fileSystemModelIndex(index));
}
QString CustomFileSystemModel::fileName(const QModelIndex &index) const
{
return m_fileSystemModel->fileName(fileSystemModelIndex(index));
}
QFileInfo CustomFileSystemModel::fileInfo(const QModelIndex &index) const
{
return m_fileSystemModel->fileInfo(fileSystemModelIndex(index));
}
Qt::ItemFlags CustomFileSystemModel::flags(const QModelIndex &index) const
{
return m_fileSystemModel->flags (fileSystemModelIndex(index));
}
void CustomFileSystemModel::setSearchFilter(const QString &nameFilterList)
{
m_searchFilter = nameFilterList;
setRootPath(m_fileSystemModel->rootPath());
}
QPair<QString, QByteArray> CustomFileSystemModel::resourceTypeAndData(const QModelIndex &index) const
{
QFileInfo fi = fileInfo(index);
QString suffix = fi.suffix().toLower();
if (!suffix.isEmpty()) {
if (supportedImageSuffixes().contains(suffix)) {
// Data: Image format (suffix)
return {"application/vnd.bauhaus.libraryresource.image", suffix.toUtf8()};
} else if (supportedFontSuffixes().contains(suffix)) {
// Data: Font family name
return {"application/vnd.bauhaus.libraryresource.font", fontFamily(fi).toUtf8()};
} else if (supportedShaderSuffixes().contains(suffix)) {
// Data: shader type, frament (f) or vertex (v)
return {"application/vnd.bauhaus.libraryresource.shader",
supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"};
} else if (supportedAudioSuffixes().contains(suffix)) {
// No extra data for sounds
return {"application/vnd.bauhaus.libraryresource.sound", {}};
} else if (supportedTexture3DSuffixes().contains(suffix)) {
// Data: Image format (suffix)
return {"application/vnd.bauhaus.libraryresource.texture3d", suffix.toUtf8()};
}
}
return {};
}
const QSet<QString> &CustomFileSystemModel::supportedSuffixes() const
{
static QSet<QString> allSuffixes;
if (allSuffixes.isEmpty()) {
auto insertSuffixes = [](const QStringList &suffixes) {
for (const auto &suffix : suffixes)
allSuffixes.insert(suffix);
};
insertSuffixes(supportedImageSuffixes());
insertSuffixes(supportedShaderSuffixes());
insertSuffixes(supportedFontSuffixes());
insertSuffixes(supportedAudioSuffixes());
insertSuffixes(supportedTexture3DSuffixes());
}
return allSuffixes;
}
const QSet<QString> &CustomFileSystemModel::previewableSuffixes() const
{
static QSet<QString> 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))
m_files.append(file);
}
QModelIndex CustomFileSystemModel::updatePath(const QString &newPath)
{
beginResetModel();
// We must recreate icon provider to ensure modified icons are recreated
auto newProvider = new ItemLibraryFileIconProvider(m_fontImageCache, m_iconCache);
m_fileSystemModel->setIconProvider(newProvider);
delete m_fileIconProvider;
m_fileIconProvider = newProvider;
m_fileSystemModel->setRootPath(newPath);
m_fileSystemWatcher->removeDirectories(m_fileSystemWatcher->directories());
m_fileSystemWatcher->removeFiles(m_fileSystemWatcher->files());
m_fileSystemWatcher->addDirectory(newPath, Utils::FileSystemWatcher::WatchAllChanges);
QStringList nameFilterList;
const QString searchFilter = m_searchFilter;
if (searchFilter.contains(QLatin1Char('.'))) {
nameFilterList.append(QString(QStringLiteral("*%1*")).arg(searchFilter));
} else {
const QString filterTemplate("*%1*.%2");
auto appendFilters = [&](const QStringList &suffixes) {
for (const QString &ext : suffixes) {
nameFilterList.append(filterTemplate.arg(searchFilter, ext));
nameFilterList.append(filterTemplate.arg(searchFilter, ext.toUpper()));
}
};
appendFilters(supportedImageSuffixes());
appendFilters(supportedShaderSuffixes());
appendFilters(supportedFontSuffixes());
appendFilters(supportedAudioSuffixes());
appendFilters(supportedTexture3DSuffixes());
}
m_files.clear();
QDirIterator fileIterator(newPath, nameFilterList, QDir::Files, QDirIterator::Subdirectories);
while (fileIterator.hasNext())
appendIfNotFiltered(fileIterator.next());
QDirIterator dirIterator(newPath, {}, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot,
QDirIterator::Subdirectories);
while (dirIterator.hasNext()) {
const QString entry = dirIterator.next();
QFileInfo fi{entry};
if (fi.isDir())
m_fileSystemWatcher->addDirectory(entry, Utils::FileSystemWatcher::WatchAllChanges);
else if (supportedSuffixes().contains(fi.suffix()))
m_fileSystemWatcher->addFile(entry, Utils::FileSystemWatcher::WatchAllChanges);
}
endResetModel();
return QAbstractListModel::index(0, 0);
}
QModelIndex CustomFileSystemModel::fileSystemModelIndex(const QModelIndex &index) const
{
const int row = index.row();
return m_fileSystemModel->index(m_files.at(row));
}
} //QmlDesigner

View File

@@ -0,0 +1,89 @@
/****************************************************************************
**
** 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 "itemlibraryassetsdir.h"
#include "itemlibraryassetsdirsmodel.h"
#include "itemlibraryassetsfilesmodel.h"
namespace QmlDesigner {
ItemLibraryAssetsDir::ItemLibraryAssetsDir(const QString &path, int depth, bool expanded, QObject *parent)
: QObject(parent)
, m_dirPath(path)
, m_dirDepth(depth)
, m_dirExpanded(expanded)
{
}
QString ItemLibraryAssetsDir::dirName() const { return m_dirPath.split('/').last(); }
QString ItemLibraryAssetsDir::dirPath() const { return m_dirPath; }
int ItemLibraryAssetsDir::dirDepth() const { return m_dirDepth; }
bool ItemLibraryAssetsDir::dirExpanded() const { return m_dirExpanded; }
bool ItemLibraryAssetsDir::dirVisible() const { return m_dirVisible; }
void ItemLibraryAssetsDir::setDirExpanded(bool expand)
{
if (m_dirExpanded != expand) {
m_dirExpanded = expand;
emit dirExpandedChanged();
}
}
void ItemLibraryAssetsDir::setDirVisible(bool visible)
{
if (m_dirVisible != visible) {
m_dirVisible = visible;
emit dirVisibleChanged();
}
}
QObject *ItemLibraryAssetsDir::filesModel() const
{
return m_filesModel;
}
QObject *ItemLibraryAssetsDir::dirsModel() const
{
return m_dirsModel;
}
void ItemLibraryAssetsDir::addDir(ItemLibraryAssetsDir *assetsDir)
{
if (!m_dirsModel)
m_dirsModel = new ItemLibraryAssetsDirsModel(this);
m_dirsModel->addDir(assetsDir);
}
void ItemLibraryAssetsDir::addFile(const QString &filePath)
{
if (!m_filesModel)
m_filesModel = new ItemLibraryAssetsFilesModel(this);
m_filesModel->addFile(filePath);
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,83 @@
/****************************************************************************
**
** 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 <QObject>
namespace QmlDesigner {
class ItemLibraryAssetsDirsModel;
class ItemLibraryAssetsFilesModel;
class ItemLibraryAssetsDir : public QObject
{
Q_OBJECT
Q_PROPERTY(QString dirName READ dirName NOTIFY dirNameChanged)
Q_PROPERTY(QString dirPath READ dirPath NOTIFY dirPathChanged)
Q_PROPERTY(bool dirExpanded READ dirExpanded WRITE setDirExpanded NOTIFY dirExpandedChanged)
Q_PROPERTY(bool dirVisible READ dirVisible WRITE setDirVisible NOTIFY dirVisibleChanged)
Q_PROPERTY(int dirDepth READ dirDepth NOTIFY dirDepthChanged)
Q_PROPERTY(QObject *filesModel READ filesModel NOTIFY filesModelChanged)
Q_PROPERTY(QObject *dirsModel READ dirsModel NOTIFY dirsModelChanged)
public:
ItemLibraryAssetsDir(const QString &path, int depth, bool expanded = true, QObject *parent = nullptr);
QString dirName() const;
QString dirPath() const;
int dirDepth() const;
bool dirExpanded() const;
bool dirVisible() const;
void setDirExpanded(bool expand);
void setDirVisible(bool visible);
QObject *filesModel() const;
QObject *dirsModel() const;
void addDir(ItemLibraryAssetsDir *assetsDir);
void addFile(const QString &filePath);
signals:
void dirNameChanged();
void dirPathChanged();
void dirDepthChanged();
void dirExpandedChanged();
void dirVisibleChanged();
void filesModelChanged();
void dirsModelChanged();
private:
QString m_dirPath;
int m_dirDepth = 0;
bool m_dirExpanded = true;
bool m_dirVisible = true;
ItemLibraryAssetsDirsModel *m_dirsModel = nullptr;
ItemLibraryAssetsFilesModel *m_filesModel = nullptr;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,89 @@
/****************************************************************************
**
** 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 "itemlibraryassetsdirsmodel.h"
#include "itemlibraryassetsmodel.h"
#include <QMetaProperty>
namespace QmlDesigner {
ItemLibraryAssetsDirsModel::ItemLibraryAssetsDirsModel(QObject *parent)
: QAbstractListModel(parent)
{
// add roles
const QMetaObject meta = ItemLibraryAssetsDir::staticMetaObject;
for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i)
m_roleNames.insert(i, meta.property(i).name());
}
QVariant ItemLibraryAssetsDirsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row());
return {};
}
if (m_roleNames.contains(role))
return m_dirs[index.row()]->property(m_roleNames[role]);
qWarning() << Q_FUNC_INFO << "Invalid role requested: " << QString::number(role);
return {};
}
bool ItemLibraryAssetsDirsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// currently only dirExpanded property is updatable
if (index.isValid() && m_roleNames.contains(role)) {
QVariant currValue = m_dirs.at(index.row())->property(m_roleNames.value(role));
if (currValue != value) {
m_dirs.at(index.row())->setProperty(m_roleNames.value(role), value);
if (m_roleNames.value(role) == "dirExpanded")
ItemLibraryAssetsModel::saveExpandedState(value.toBool(), m_dirs.at(index.row())->dirPath());
emit dataChanged(index, index, {role});
return true;
}
}
return false;
}
int ItemLibraryAssetsDirsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_dirs.size();
}
QHash<int, QByteArray> ItemLibraryAssetsDirsModel::roleNames() const
{
return m_roleNames;
}
void ItemLibraryAssetsDirsModel::addDir(ItemLibraryAssetsDir *assetsDir)
{
m_dirs.append(assetsDir);
}
} // namespace QmlDesigner

View File

@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
@@ -25,32 +25,28 @@
#pragma once
#include <previewtooltip/previewtooltipbackend.h>
#include <QListView>
QT_BEGIN_NAMESPACE
class QActionGroup;
QT_END_NAMESPACE
#include <QAbstractListModel>
#include "itemlibraryassetsdir.h"
namespace QmlDesigner {
class AsynchronousImageCache;
class ItemLibraryResourceView : public QListView {
class ItemLibraryAssetsDirsModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ItemLibraryResourceView(AsynchronousImageCache &fontImageCache,
QWidget *parent = nullptr);
void startDrag(Qt::DropActions supportedActions) override;
bool viewportEvent(QEvent *event) override;
public:
ItemLibraryAssetsDirsModel(QObject *parent = nullptr);
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
void addDir(ItemLibraryAssetsDir *assetsDir);
private:
void addSizeAction(QActionGroup *group, const QString &text, int size, int iconSize);
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;
QList<ItemLibraryAssetsDir *> m_dirs;
QHash<int, QByteArray> m_roleNames;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** 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 "itemlibraryassetsfilesmodel.h"
namespace QmlDesigner {
ItemLibraryAssetsFilesModel::ItemLibraryAssetsFilesModel(QObject *parent)
: QAbstractListModel(parent)
{
// add roles
m_roleNames.insert(FileNameRole, "fileName");
m_roleNames.insert(FilePathRole, "filePath");
}
QVariant ItemLibraryAssetsFilesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row());
return {};
}
if (role == FileNameRole)
return m_files[index.row()].split('/').last();
if (role == FilePathRole)
return m_files[index.row()];
qWarning() << Q_FUNC_INFO << "Invalid role requested: " << QString::number(role);
return {};
}
int ItemLibraryAssetsFilesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_files.size();
}
QHash<int, QByteArray> ItemLibraryAssetsFilesModel::roleNames() const
{
return m_roleNames;
}
void ItemLibraryAssetsFilesModel::addFile(const QString &filePath)
{
m_files.append(filePath);
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,53 @@
/****************************************************************************
**
** 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 <QAbstractListModel>
namespace QmlDesigner {
class ItemLibraryAssetsFilesModel : public QAbstractListModel
{
Q_OBJECT
public:
ItemLibraryAssetsFilesModel(QObject *parent = nullptr);
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
void addFile(const QString &filePath);
private:
enum Roles {FileNameRole = Qt::UserRole + 1,
FilePathRole};
QStringList m_files;
QHash<int, QByteArray> m_roleNames;
};
} // QmlDesigner

View File

@@ -0,0 +1,79 @@
/****************************************************************************
**
** 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 "itemlibraryassetsiconprovider.h"
#include "itemlibraryassetsmodel.h"
#include <hdrimage.h>
#include <theme.h>
#include <utils/stylehelper.h>
namespace QmlDesigner {
ItemLibraryAssetsIconProvider::ItemLibraryAssetsIconProvider(SynchronousImageCache &fontImageCache)
: QQuickImageProvider(QQuickImageProvider::Pixmap)
, m_fontImageCache(fontImageCache)
{
}
QPixmap ItemLibraryAssetsIconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
{
QPixmap pixmap;
const QString suffix = "*." + id.split('.').last();
if (ItemLibraryAssetsModel::supportedFontSuffixes().contains(suffix))
pixmap = generateFontIcons(id);
else if (ItemLibraryAssetsModel::supportedImageSuffixes().contains(suffix))
pixmap = Utils::StyleHelper::dpiSpecificImageFile(id);
else if (ItemLibraryAssetsModel::supportedTexture3DSuffixes().contains(suffix))
pixmap = HdrImage{id}.toPixmap();
else if (ItemLibraryAssetsModel::supportedAudioSuffixes().contains(suffix))
pixmap = QPixmap(Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/asset_sound_48.png"));
if (size) {
size->setWidth(pixmap.width());
size->setHeight(pixmap.height());
}
if (pixmap.isNull()) {
pixmap = QPixmap(Utils::StyleHelper::dpiSpecificImageFile(
QStringLiteral(":/ItemLibrary/images/item-default-icon.png")));
}
if (requestedSize.isValid())
return pixmap.scaled(requestedSize);
return pixmap;
}
QPixmap ItemLibraryAssetsIconProvider::generateFontIcons(const QString &filePath) const
{
return m_fontImageCache.icon(filePath, {},
ImageCache::FontCollectorSizesAuxiliaryData{Utils::span{iconSizes},
Theme::getColor(Theme::DStextColor).name(),
"Abc"}).pixmap({48, 48});
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,58 @@
/****************************************************************************
**
** 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 <synchronousimagecache.h>
#include <QQuickImageProvider>
namespace QmlDesigner {
class ItemLibraryAssetsIconProvider : public QQuickImageProvider
{
public:
ItemLibraryAssetsIconProvider(SynchronousImageCache &fontImageCache);
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override;
private:
QPixmap generateFontIcons(const QString &filePath) const;
SynchronousImageCache &m_fontImageCache;
// Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their
// x2 versions for HDPI sceens
std::vector<QSize> iconSizes = {{384, 384},
{192, 192}, // Large
{256, 256},
{128, 128}, // Drag
{96, 96}, // Medium
{48, 48}, // Small
{64, 64},
{32, 32}}; // List
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,251 @@
/****************************************************************************
**
** 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 "itemlibraryassetsmodel.h"
#include "itemlibraryassetsdirsmodel.h"
#include "itemlibraryassetsfilesmodel.h"
#include <synchronousimagecache.h>
#include <theme.h>
#include <hdrimage.h>
#include <QDir>
#include <QDirIterator>
#include <QFont>
#include <QImageReader>
#include <QMetaProperty>
#include <QPainter>
#include <QRawFont>
#include "qmldesignerplugin.h"
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <utils/stylehelper.h>
#include <utils/filesystemwatcher.h>
namespace QmlDesigner {
void ItemLibraryAssetsModel::saveExpandedState(bool expanded, const QString &sectionName)
{
m_expandedStateHash.insert(sectionName, expanded);
}
bool ItemLibraryAssetsModel::loadExpandedState(const QString &sectionName)
{
return m_expandedStateHash.value(sectionName, true);
}
const QStringList &ItemLibraryAssetsModel::supportedImageSuffixes()
{
static QStringList retList;
if (retList.isEmpty()) {
const QList<QByteArray> suffixes = QImageReader::supportedImageFormats();
for (const QByteArray &suffix : suffixes)
retList.append("*." + QString::fromUtf8(suffix));
}
return retList;
}
const QStringList &ItemLibraryAssetsModel::supportedFragmentShaderSuffixes()
{
static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"};
return retList;
}
const QStringList &ItemLibraryAssetsModel::supportedShaderSuffixes()
{
static const QStringList retList {"*.frag", "*.vert",
"*.glsl", "*.glslv", "*.glslf",
"*.vsh", "*.fsh"};
return retList;
}
const QStringList &ItemLibraryAssetsModel::supportedFontSuffixes()
{
static const QStringList retList {"*.ttf", "*.otf"};
return retList;
}
const QStringList &ItemLibraryAssetsModel::supportedAudioSuffixes()
{
static const QStringList retList {"*.wav"};
return retList;
}
const QStringList &ItemLibraryAssetsModel::supportedTexture3DSuffixes()
{
// These are file types only supported by 3D textures
static QStringList retList {"*.hdr"};
return retList;
}
ItemLibraryAssetsModel::ItemLibraryAssetsModel(SynchronousImageCache &fontImageCache,
Utils::FileSystemWatcher *fileSystemWatcher,
QObject *parent)
: QAbstractListModel(parent)
, m_fontImageCache(fontImageCache)
, m_fileSystemWatcher(fileSystemWatcher)
{
// add role names
int role = 0;
const QMetaObject meta = ItemLibraryAssetsDir::staticMetaObject;
for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i)
m_roleNames.insert(role++, meta.property(i).name());
}
QVariant ItemLibraryAssetsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row());
return {};
}
if (m_assetsDir && m_roleNames.contains(role)) {
return m_assetsDir->property(m_roleNames.value(role));
}
qWarning() << Q_FUNC_INFO << "Invalid role requested: " << QString::number(role);
return {};
}
int ItemLibraryAssetsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QHash<int, QByteArray> ItemLibraryAssetsModel::roleNames() const
{
return m_roleNames;
}
// called when a directory is changed to refresh the model for this directory
void ItemLibraryAssetsModel::refresh()
{
setRootPath(m_assetsDir->dirPath());
}
void ItemLibraryAssetsModel::setRootPath(const QString &path)
{
static const QStringList supportedTopLevelDirs {"images", "sounds", "fonts", "assets"};
m_fileSystemWatcher->removeDirectories(m_fileSystemWatcher->directories());
m_fileSystemWatcher->removeFiles(m_fileSystemWatcher->files());
DesignDocument *currDesignDoc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (!currDesignDoc) // happens sometimes on QDS shutdown
return;
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(currDesignDoc->fileName());
QString projectName = project ? project->displayName() : "";
std::function<bool(ItemLibraryAssetsDir *, int)> parseDirRecursive;
parseDirRecursive = [this, &parseDirRecursive, &projectName](ItemLibraryAssetsDir *currAssetsDir, int currDepth) {
m_fileSystemWatcher->addDirectory(currAssetsDir->dirPath(), Utils::FileSystemWatcher::WatchAllChanges);
QDir dir(currAssetsDir->dirPath());
dir.setNameFilters(supportedSuffixes().values());
dir.setFilter(QDir::Files);
QDirIterator itFiles(dir);
bool isEmpty = true;
while (itFiles.hasNext()) {
QString filePath = itFiles.next();
QString fileName = filePath.split('/').last();
if (m_searchText.isEmpty() || fileName.contains(m_searchText, Qt::CaseInsensitive)) {
currAssetsDir->addFile(filePath);
m_fileSystemWatcher->addFile(filePath, Utils::FileSystemWatcher::WatchAllChanges);
isEmpty = false;
}
}
dir.setNameFilters({});
dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
QDirIterator itDirs(dir);
while (itDirs.hasNext()) {
QDir subDir = itDirs.next();
if (subDir.isEmpty() || projectName == subDir.dirName()
|| (currDepth == 1 && !supportedTopLevelDirs.contains(subDir.dirName()))) {
continue;
}
ItemLibraryAssetsDir *assetsDir = new ItemLibraryAssetsDir(subDir.path(), currDepth, loadExpandedState(subDir.path()), currAssetsDir);
currAssetsDir->addDir(assetsDir);
isEmpty &= parseDirRecursive(assetsDir, currDepth + 1);
}
if (isEmpty)
currAssetsDir->setDirVisible(false);
return isEmpty;
};
if (m_assetsDir)
delete m_assetsDir;
beginResetModel();
m_assetsDir = new ItemLibraryAssetsDir(path, 0, true, this);
parseDirRecursive(m_assetsDir, 1);
endResetModel();
}
void ItemLibraryAssetsModel::setSearchText(const QString &searchText)
{
if (m_searchText != searchText) {
m_searchText = searchText;
refresh();
}
}
const QSet<QString> &ItemLibraryAssetsModel::supportedSuffixes() const
{
static QSet<QString> allSuffixes;
if (allSuffixes.isEmpty()) {
auto insertSuffixes = [](const QStringList &suffixes) {
for (const auto &suffix : suffixes)
allSuffixes.insert(suffix);
};
insertSuffixes(supportedImageSuffixes());
insertSuffixes(supportedShaderSuffixes());
insertSuffixes(supportedFontSuffixes());
insertSuffixes(supportedAudioSuffixes());
insertSuffixes(supportedTexture3DSuffixes());
}
return allSuffixes;
}
const QSet<QString> &ItemLibraryAssetsModel::previewableSuffixes() const
{
static QSet<QString> previewableSuffixes;
if (previewableSuffixes.isEmpty()) {
auto insertSuffixes = [](const QStringList &suffixes) {
for (const auto &suffix : suffixes)
previewableSuffixes.insert(suffix);
};
insertSuffixes(supportedFontSuffixes());
}
return previewableSuffixes;
}
} // namespace QmlDesigner

View File

@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
@@ -25,7 +25,7 @@
#pragma once
#include <QAbstractTableModel>
#include <QAbstractListModel>
#include <QDateTime>
#include <QDir>
#include <QHash>
@@ -34,59 +34,54 @@
#include <QSet>
#include <QTimer>
QT_BEGIN_NAMESPACE
class QFileIconProvider;
class QFileSystemModel;
QT_END_NAMESPACE
#include "itemlibraryassetsdir.h"
namespace Utils { class FileSystemWatcher; }
namespace QmlDesigner {
class SynchronousImageCache;
class ItemLibraryFileIconProvider;
class CustomFileSystemModel : public QAbstractListModel
class ItemLibraryAssetsModel : public QAbstractListModel
{
Q_OBJECT
public:
CustomFileSystemModel(QmlDesigner::SynchronousImageCache &fontImageCache,
QObject *parent = nullptr);
void setFilter(QDir::Filters filters);
QString rootPath() const;
QModelIndex setRootPath(const QString &newPath);
public:
ItemLibraryAssetsModel(QmlDesigner::SynchronousImageCache &fontImageCache,
Utils::FileSystemWatcher *fileSystemWatcher,
QObject *parent = nullptr);
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
int columnCount(const QModelIndex & parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
QModelIndex indexForPath(const QString & path, int column = 0) const;
void refresh();
void setRootPath(const QString &path);
void setSearchText(const QString &searchText);
QIcon fileIcon(const QModelIndex & index) const;
QString fileName(const QModelIndex & index) const;
QFileInfo fileInfo(const QModelIndex & index) const;
static const QStringList &supportedImageSuffixes();
static const QStringList &supportedFragmentShaderSuffixes();
static const QStringList &supportedShaderSuffixes();
static const QStringList &supportedFontSuffixes();
static const QStringList &supportedAudioSuffixes();
static const QStringList &supportedTexture3DSuffixes();
Qt::ItemFlags flags(const QModelIndex &index) const override;
void setSearchFilter(const QString &nameFilterList);
QPair<QString, QByteArray> resourceTypeAndData(const QModelIndex &index) const;
const QSet<QString> &supportedSuffixes() const;
const QSet<QString> &previewableSuffixes() const;
private:
QModelIndex updatePath(const QString &newPath);
QModelIndex fileSystemModelIndex(const QModelIndex &index) const;
void appendIfNotFiltered(const QString &file);
static void saveExpandedState(bool expanded, const QString &sectionName);
static bool loadExpandedState(const QString &sectionName);
QFileSystemModel *m_fileSystemModel;
QStringList m_files;
QString m_searchFilter;
Utils::FileSystemWatcher *m_fileSystemWatcher;
private:
SynchronousImageCache &m_fontImageCache;
ItemLibraryFileIconProvider *m_fileIconProvider = nullptr;
QHash<QString, QPair<QDateTime, QIcon>> m_iconCache;
QTimer m_updatePathTimer;
QString m_searchText;
Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr;
ItemLibraryAssetsDir *m_assetsDir = nullptr;
QHash<int, QByteArray> m_roleNames;
inline static QHash<QString, bool> m_expandedStateHash;
};
} //QmlDesigner
} // namespace QmlDesigner

View File

@@ -1,206 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 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 "itemlibraryresourceview.h"
#include "customfilesystemmodel.h"
#include <theme.h>
#include <asynchronousimagecache.h>
#include <QAction>
#include <QActionGroup>
#include <QDebug>
#include <QDrag>
#include <QFileSystemModel>
#include <QMimeData>
#include <QPainter>
#include <QPixmap>
#include <QProxyStyle>
#include <QScrollBar>
#include <QtGui/qevent.h>
#include <functional>
enum { debug = 0 };
namespace QmlDesigner {
void ItemLibraryResourceView::addSizeAction(QActionGroup *group, const QString &text, int gridSize, int iconSize)
{
auto action = new QAction(text, group);
group->addAction(action);
action->setCheckable(true);
QAction::connect(action, &QAction::triggered, this, [this, gridSize, iconSize]() {
setViewMode(QListView::IconMode);
setGridSize(QSize(gridSize, gridSize));
setIconSize(QSize(iconSize, iconSize));
verticalScrollBar()->setSingleStep(-1); //step auto-adjustment
setDragEnabled(true);
setWrapping(true);
});
}
ItemLibraryResourceView::ItemLibraryResourceView(AsynchronousImageCache &fontImageCache,
QWidget *parent)
: QListView(parent)
{
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setAttribute(Qt::WA_MacShowFocusRect, false);
setGridSize(QSize(128, 128));
setIconSize(QSize(96, 96));
setSpacing(4);
setViewMode(QListView::IconMode);
setMovement(QListView::Snap);
setResizeMode(QListView::Adjust);
setSelectionRectVisible(false);
setWrapping(true);
setWordWrap(true);
setDragDropMode(QAbstractItemView::DragOnly);
setContextMenuPolicy(Qt::ActionsContextMenu);
auto actionGroup = new QActionGroup(this);
actionGroup->setExclusive(true);
addSizeAction(actionGroup, tr("Large Icons"), 256, 192);
addSizeAction(actionGroup, tr("Medium Icons"), 128, 96);
addSizeAction(actionGroup, tr("Small Icons"), 96, 48);
QAction *action = new QAction(tr("List"), actionGroup);
actionGroup->addAction(action);
action->setCheckable(true);
QAction::connect(action, &QAction::triggered, this, [this](){
setViewMode(QListView::ListMode);
setGridSize(QSize());
setIconSize(QSize(32, 32));
setDragEnabled(true);
setWrapping(false);
});
QAction *defaultAction = actionGroup->actions().at(1);
defaultAction->toggle();
addActions(actionGroup->actions());
viewport()->setAttribute(Qt::WA_Hover);
m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(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->setAuxiliaryData(
ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300},
Theme::getColor(Theme::DStextColor).name(),
QStringLiteral("The quick brown fox jumps\n"
"over the lazy dog\n"
"1234567890")});
}
void ItemLibraryResourceView::startDrag(Qt::DropActions /* supportedActions */)
{
if (debug)
qDebug() << Q_FUNC_INFO;
const auto indexes = selectedIndexes();
if (indexes.isEmpty())
return;
const QModelIndex &index = indexes.constFirst();
if (!index.isValid())
return;
auto fileSystemModel = qobject_cast<CustomFileSystemModel*>(model());
Q_ASSERT(fileSystemModel);
QPair<QString, QByteArray> typeAndData = fileSystemModel->resourceTypeAndData(index);
if (typeAndData.first.isEmpty())
return;
QFileInfo fileInfo = fileSystemModel->fileInfo(index);
auto drag = new QDrag(this);
drag->setPixmap(fileSystemModel->fileIcon(index).pixmap(128, 128));
QMimeData *mimeData = new QMimeData;
mimeData->setData(QLatin1String("application/vnd.bauhaus.libraryresource"),
fileInfo.absoluteFilePath().toUtf8());
mimeData->setData(typeAndData.first, typeAndData.second);
drag->setMimeData(mimeData);
drag->exec();
}
bool ItemLibraryResourceView::viewportEvent(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
auto fileSystemModel = qobject_cast<CustomFileSystemModel *>(model());
Q_ASSERT(fileSystemModel);
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(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<CustomFileSystemModel *>(model());
Q_ASSERT(fileSystemModel);
auto *he = static_cast<QHoverEvent *>(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

View File

@@ -25,7 +25,7 @@
#include "itemlibrarywidget.h"
#include "customfilesystemmodel.h"
#include "itemlibraryassetsmodel.h"
#include "itemlibraryiconimageprovider.h"
#include "itemlibraryimport.h"
@@ -37,6 +37,7 @@
#include <itemlibraryinfo.h>
#include <itemlibrarymodel.h>
#include <itemlibraryaddimportmodel.h>
#include "itemlibraryassetsiconprovider.h"
#include <metainfo.h>
#include <model.h>
#include <rewritingexception.h>
@@ -46,6 +47,7 @@
#include <utils/algorithm.h>
#include <utils/flowlayout.h>
#include <utils/fileutils.h>
#include <utils/filesystemwatcher.h>
#include <utils/stylehelper.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -125,13 +127,16 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
AsynchronousImageCache &asynchronousFontImageCache,
SynchronousImageCache &synchronousFontImageCache)
: m_itemIconSize(24, 24)
, m_fontImageCache(synchronousFontImageCache)
, m_itemLibraryModel(new ItemLibraryModel(this))
, m_itemLibraryAddImportModel(new ItemLibraryAddImportModel(this))
, m_resourcesFileSystemModel{new CustomFileSystemModel(synchronousFontImageCache, this)}
, m_assetsIconProvider(new ItemLibraryAssetsIconProvider(synchronousFontImageCache))
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
, m_assetsModel(new ItemLibraryAssetsModel(synchronousFontImageCache, m_fileSystemWatcher, this))
, m_headerWidget(new QQuickWidget(this))
, m_addImportWidget(new QQuickWidget(this))
, m_itemViewQuickWidget(new QQuickWidget(this))
, m_resourcesView(new ItemLibraryResourceView(asynchronousFontImageCache, this))
, m_assetsWidget(new QQuickWidget(this))
, m_imageCache{imageCache}
{
m_compressionTimer.setInterval(200);
@@ -180,12 +185,47 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
Theme::setupTheme(m_itemViewQuickWidget->engine());
m_itemViewQuickWidget->installEventFilter(this);
// connect Resources view and its model
m_resourcesView->setModel(m_resourcesFileSystemModel.data());
m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache);
// 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.
m_fontPreviewTooltipBackend->setAuxiliaryData(
ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300},
Theme::getColor(Theme::DStextColor).name(),
QStringLiteral("The quick brown fox jumps\n"
"over the lazy dog\n"
"1234567890")});
// create assets widget
m_assetsWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
Theme::setupTheme(m_assetsWidget->engine());
m_assetsWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_assetsWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
m_assetsWidget->engine()->addImageProvider("qmldesigner_assets", m_assetsIconProvider);
m_assetsWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{
{{"assetsModel"}, QVariant::fromValue(m_assetsModel.data())},
{{"rootView"}, QVariant::fromValue(this)},
{{"tooltipBackend"}, QVariant::fromValue(m_fontPreviewTooltipBackend.get())}
});
// If project directory contents change, or one of the asset files is modified, we must
// reconstruct the model to update the icons
connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this](const QString & changedDirPath) {
Q_UNUSED(changedDirPath)
// TODO: find a clever way to only refresh the changed directory part of the model
m_assetsModel->refresh();
// reload assets qml so that an overridden file's image shows the new image
QTimer::singleShot(100, [this] {
const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml";
m_assetsWidget->engine()->clearComponentCache();
m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath));
});
});
m_stackedWidget = new QStackedWidget(this);
m_stackedWidget->addWidget(m_itemViewQuickWidget.data());
m_stackedWidget->addWidget(m_resourcesView.data());
m_stackedWidget->addWidget(m_assetsWidget.data());
m_stackedWidget->addWidget(m_addImportWidget.data());
m_stackedWidget->setMinimumHeight(30);
m_stackedWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
@@ -201,34 +241,12 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
/* style sheets */
setStyleSheet(Theme::replaceCssColors(
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
m_resourcesView->setStyleSheet(Theme::replaceCssColors(
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))));
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5), this);
connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource);
connect(&m_compressionTimer, &QTimer::timeout, this, &ItemLibraryWidget::updateModel);
const auto dropSupport = new Utils::DropSupport(
m_resourcesView.data(), [this](QDropEvent *event, Utils::DropSupport *) {
// Accept supported file types
if (event->type() == QDropEvent::DragEnter && !Utils::DropSupport::isFileDrop(event))
return false; // do not accept drops without files
bool accept = false;
const QSet<QString> &suffixes = m_resourcesFileSystemModel->supportedSuffixes();
const QList<QUrl> urls = event->mimeData()->urls();
for (const QUrl &url : urls) {
QFileInfo fi(url.toLocalFile());
if (suffixes.contains(fi.suffix().toLower())) {
accept = true;
break;
}
}
return accept;
});
connect(dropSupport, &Utils::DropSupport::filesDropped,
this, &ItemLibraryWidget::importDroppedFiles);
m_itemViewQuickWidget->engine()->addImageProvider("itemlibrary_preview",
new ItemLibraryIconImageProvider{m_imageCache});
@@ -306,6 +324,11 @@ bool ItemLibraryWidget::isSearchActive() const
return !m_filterText.isEmpty();
}
void ItemLibraryWidget::handleFilesDrop(const QStringList &filesPaths)
{
addResources(filesPaths);
}
void ItemLibraryWidget::delayedUpdateModel()
{
static bool disableTimer = DesignerSettings::getValue(DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER).toBool();
@@ -356,6 +379,11 @@ void ItemLibraryWidget::reloadQmlSource()
QTC_ASSERT(QFileInfo::exists(itemLibraryQmlPath), return);
m_itemViewQuickWidget->engine()->clearComponentCache();
m_itemViewQuickWidget->setSource(QUrl::fromLocalFile(itemLibraryQmlPath));
const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml";
QTC_ASSERT(QFileInfo::exists(assetsQmlPath), return);
m_assetsWidget->engine()->clearComponentCache();
m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath));
}
void ItemLibraryWidget::updateModel()
@@ -395,9 +423,7 @@ void ItemLibraryWidget::updateSearch()
m_itemLibraryModel->setSearchText(m_filterText);
m_itemViewQuickWidget->update();
} else if (m_stackedWidget->currentIndex() == 1) { // Assets tab selected
m_resourcesFileSystemModel->setSearchFilter(m_filterText);
m_resourcesFileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
m_resourcesView->scrollToTop();
m_assetsModel->setSearchText(m_filterText);
} else if (m_stackedWidget->currentIndex() == 2) { // QML imports tab selected
m_itemLibraryAddImportModel->setSearchText(m_filterText);
}
@@ -413,10 +439,7 @@ void ItemLibraryWidget::handlePriorityImportsChanged()
void ItemLibraryWidget::setResourcePath(const QString &resourcePath)
{
if (m_resourcesView->model() == m_resourcesFileSystemModel.data()) {
m_resourcesFileSystemModel->setRootPath(resourcePath);
m_resourcesView->setRootIndex(m_resourcesFileSystemModel->indexForPath(resourcePath));
}
m_assetsModel->setRootPath(resourcePath);
updateSearch();
}
@@ -429,6 +452,51 @@ void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry, const QPo
m_dragStartPoint = mousePos.toPoint();
}
void ItemLibraryWidget::startDragAsset(const QString &assetPath)
{
QFileInfo fileInfo(assetPath);
QPair<QString, QByteArray> typeAndData = getAssetTypeAndData(fileInfo);
if (typeAndData.first.isEmpty())
return;
auto drag = new QDrag(this);
drag->setPixmap(m_assetsIconProvider->requestPixmap(assetPath, nullptr, {128, 128}));
QMimeData *mimeData = new QMimeData;
mimeData->setData(QLatin1String("application/vnd.bauhaus.libraryresource"),
fileInfo.absoluteFilePath().toUtf8());
mimeData->setData(typeAndData.first, typeAndData.second);
drag->setMimeData(mimeData);
drag->exec();
}
QPair<QString, QByteArray> ItemLibraryWidget::getAssetTypeAndData(const QFileInfo &fi) const
{
QString suffix = "*." + fi.suffix().toLower();
if (!suffix.isEmpty()) {
if (ItemLibraryAssetsModel::supportedImageSuffixes().contains(suffix)) {
// Data: Image format (suffix)
return {"application/vnd.bauhaus.libraryresource.image", suffix.toUtf8()};
} else if (ItemLibraryAssetsModel::supportedFontSuffixes().contains(suffix)) {
// Data: Font family name
QRawFont font(fi.absoluteFilePath(), 10);
QString fontFamily = font.isValid() ? font.familyName() : "";
return {"application/vnd.bauhaus.libraryresource.font", fontFamily.toUtf8()};
} else if (ItemLibraryAssetsModel::supportedShaderSuffixes().contains(suffix)) {
// Data: shader type, frament (f) or vertex (v)
return {"application/vnd.bauhaus.libraryresource.shader",
ItemLibraryAssetsModel::supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"};
} else if (ItemLibraryAssetsModel::supportedAudioSuffixes().contains(suffix)) {
// No extra data for sounds
return {"application/vnd.bauhaus.libraryresource.sound", {}};
} else if (ItemLibraryAssetsModel::supportedTexture3DSuffixes().contains(suffix)) {
// Data: Image format (suffix)
return {"application/vnd.bauhaus.libraryresource.texture3d", suffix.toUtf8()};
}
}
return {};
}
void ItemLibraryWidget::setFlowMode(bool b)
{
m_itemLibraryModel->setFlowMode(b);
@@ -533,15 +601,4 @@ void ItemLibraryWidget::addResources(const QStringList &files)
}
}
void ItemLibraryWidget::importDroppedFiles(const QList<Utils::DropSupport::FileSpec> &files)
{
QStringList fileNames;
for (const auto &file : files) {
QFileInfo fi(file.filePath);
if (m_resourcesFileSystemModel->supportedSuffixes().contains(fi.suffix().toLower()))
fileNames.append(fi.absoluteFilePath());
}
if (!fileNames.isEmpty())
addResources(fileNames);
}
} // namespace QmlDesigner

View File

@@ -26,12 +26,12 @@
#pragma once
#include "itemlibraryinfo.h"
#include "itemlibraryresourceview.h"
#include "import.h"
#include <utils/fancylineedit.h>
#include <utils/dropsupport.h>
#include <previewtooltip/previewtooltipbackend.h>
#include "itemlibraryassetsmodel.h"
#include <QFrame>
#include <QToolButton>
@@ -48,6 +48,8 @@ class QStackedWidget;
class QShortcut;
QT_END_NAMESPACE
namespace Utils { class FileSystemWatcher; }
namespace QmlDesigner {
class MetaInfo;
@@ -55,8 +57,9 @@ class ItemLibraryEntry;
class Model;
class CustomFileSystemModel;
class ItemLibraryModel;
class ItemLibraryAssetsIconProvider;
class ItemLibraryAssetsModel;
class ItemLibraryAddImportModel;
class ItemLibraryResourceView;
class SynchronousImageCache;
@@ -87,8 +90,10 @@ public:
void setResourcePath(const QString &resourcePath);
void setModel(Model *model);
void setFlowMode(bool b);
QPair<QString, QByteArray> getAssetTypeAndData(const QFileInfo &fi) const;
Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos);
Q_INVOKABLE void startDragAsset(const QString &assetPath);
Q_INVOKABLE void removeImport(const QString &importUrl);
Q_INVOKABLE void addImportForItem(const QString &importUrl);
Q_INVOKABLE void handleTabChanged(int index);
@@ -97,6 +102,8 @@ public:
Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText);
Q_INVOKABLE void handleAddImport(int index);
Q_INVOKABLE bool isSearchActive() const;
Q_INVOKABLE void handleFilesDrop(const QStringList &filesPaths);
Q_INVOKABLE QSet<QString> supportedSuffixes() const { return m_assetsModel->supportedSuffixes(); };
signals:
void itemActivated(const QString& itemName);
@@ -108,26 +115,29 @@ private:
void reloadQmlSource();
void addResources(const QStringList &files);
void importDroppedFiles(const QList<Utils::DropSupport::FileSpec> &files);
void updateSearch();
void handlePriorityImportsChanged();
QTimer m_compressionTimer;
QSize m_itemIconSize;
SynchronousImageCache &m_fontImageCache;
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
QPointer<ItemLibraryModel> m_itemLibraryModel;
QPointer<ItemLibraryAddImportModel> m_itemLibraryAddImportModel;
QPointer<CustomFileSystemModel> m_resourcesFileSystemModel;
ItemLibraryAssetsIconProvider *m_assetsIconProvider = nullptr;
Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr;
QPointer<ItemLibraryAssetsModel> m_assetsModel;
QPointer<QStackedWidget> m_stackedWidget;
QScopedPointer<QQuickWidget> m_headerWidget;
QScopedPointer<QQuickWidget> m_addImportWidget;
QScopedPointer<QQuickWidget> m_itemViewQuickWidget;
QScopedPointer<ItemLibraryResourceView> m_resourcesView;
QScopedPointer<QQuickWidget> m_assetsWidget;
std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut;
AsynchronousImageCache &m_imageCache;