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 sectionFontSize: label.font.pixelSize
property alias showTopSeparator: topSeparator.visible property alias showTopSeparator: topSeparator.visible
property alias showArrow: arrow.visible property alias showArrow: arrow.visible
property alias showLeftBorder: leftBorder.visible
property int leftPadding: 8 property int leftPadding: 8
property int rightPadding: 0 property int rightPadding: 0
property bool expanded: true 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 int levelShift: 10
property bool hideHeader: false property bool hideHeader: false
property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead
@@ -138,6 +140,14 @@ Item {
anchors.top: topSpacer.bottom anchors.top: topSpacer.bottom
} }
Rectangle {
id: leftBorder
visible: false
width: 1
height: parent.height - 15
color: header.color
}
Item { Item {
id: bottomSpacer id: bottomSpacer
height: addBottomPadding && row.height > 0 ? StudioTheme.Values.sectionHeadSpacerHeight : 0 height: addBottomPadding && row.height > 0 ? StudioTheme.Values.sectionHeadSpacerHeight : 0

View File

@@ -310,12 +310,10 @@ extend_qtc_plugin(QmlDesigner
assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h
assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h
assetimportupdatetreeview.cpp assetimportupdatetreeview.h assetimportupdatetreeview.cpp assetimportupdatetreeview.h
customfilesystemmodel.cpp customfilesystemmodel.h
itemlibrary.qrc itemlibrary.qrc
itemlibraryimageprovider.cpp itemlibraryimageprovider.h itemlibraryimageprovider.cpp itemlibraryimageprovider.h
itemlibraryitem.cpp itemlibraryitem.h itemlibraryitem.cpp itemlibraryitem.h
itemlibrarymodel.cpp itemlibrarymodel.h itemlibrarymodel.cpp itemlibrarymodel.h
itemlibraryresourceview.cpp itemlibraryresourceview.h
itemlibrarycategory.cpp itemlibrarycategory.h itemlibrarycategory.cpp itemlibrarycategory.h
itemlibraryitemsmodel.cpp itemlibraryitemsmodel.h itemlibraryitemsmodel.cpp itemlibraryitemsmodel.h
itemlibraryview.cpp itemlibraryview.h itemlibraryview.cpp itemlibraryview.h
@@ -327,6 +325,11 @@ extend_qtc_plugin(QmlDesigner
itemlibraryimport.cpp itemlibraryimport.h itemlibraryimport.cpp itemlibraryimport.h
itemlibrarycategoriesmodel.cpp itemlibrarycategoriesmodel.h itemlibrarycategoriesmodel.cpp itemlibrarycategoriesmodel.h
itemlibraryaddimportmodel.cpp itemlibraryaddimportmodel.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 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/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of Qt Creator. ** This file is part of Qt Creator.
@@ -25,32 +25,28 @@
#pragma once #pragma once
#include <previewtooltip/previewtooltipbackend.h> #include <QAbstractListModel>
#include "itemlibraryassetsdir.h"
#include <QListView>
QT_BEGIN_NAMESPACE
class QActionGroup;
QT_END_NAMESPACE
namespace QmlDesigner { namespace QmlDesigner {
class AsynchronousImageCache; class ItemLibraryAssetsDirsModel : public QAbstractListModel
{
class ItemLibraryResourceView : public QListView {
Q_OBJECT Q_OBJECT
public:
explicit ItemLibraryResourceView(AsynchronousImageCache &fontImageCache,
QWidget *parent = nullptr);
void startDrag(Qt::DropActions supportedActions) override; public:
bool viewportEvent(QEvent *event) override; 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: private:
void addSizeAction(QActionGroup *group, const QString &text, int size, int iconSize); QList<ItemLibraryAssetsDir *> m_dirs;
QHash<int, QByteArray> m_roleNames;
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;
}; };
} // namespace QmlDesigner } // 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/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of Qt Creator. ** This file is part of Qt Creator.
@@ -25,7 +25,7 @@
#pragma once #pragma once
#include <QAbstractTableModel> #include <QAbstractListModel>
#include <QDateTime> #include <QDateTime>
#include <QDir> #include <QDir>
#include <QHash> #include <QHash>
@@ -34,59 +34,54 @@
#include <QSet> #include <QSet>
#include <QTimer> #include <QTimer>
QT_BEGIN_NAMESPACE #include "itemlibraryassetsdir.h"
class QFileIconProvider;
class QFileSystemModel;
QT_END_NAMESPACE
namespace Utils { class FileSystemWatcher; } namespace Utils { class FileSystemWatcher; }
namespace QmlDesigner { namespace QmlDesigner {
class SynchronousImageCache; class SynchronousImageCache;
class ItemLibraryFileIconProvider;
class CustomFileSystemModel : public QAbstractListModel class ItemLibraryAssetsModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public:
CustomFileSystemModel(QmlDesigner::SynchronousImageCache &fontImageCache,
QObject *parent = nullptr);
void setFilter(QDir::Filters filters); public:
QString rootPath() const; ItemLibraryAssetsModel(QmlDesigner::SynchronousImageCache &fontImageCache,
QModelIndex setRootPath(const QString &newPath); Utils::FileSystemWatcher *fileSystemWatcher,
QObject *parent = nullptr);
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex & parent = QModelIndex()) 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; static const QStringList &supportedImageSuffixes();
QString fileName(const QModelIndex & index) const; static const QStringList &supportedFragmentShaderSuffixes();
QFileInfo fileInfo(const QModelIndex & index) const; 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> &supportedSuffixes() const;
const QSet<QString> &previewableSuffixes() const; const QSet<QString> &previewableSuffixes() const;
private: static void saveExpandedState(bool expanded, const QString &sectionName);
QModelIndex updatePath(const QString &newPath); static bool loadExpandedState(const QString &sectionName);
QModelIndex fileSystemModelIndex(const QModelIndex &index) const;
void appendIfNotFiltered(const QString &file);
QFileSystemModel *m_fileSystemModel; private:
QStringList m_files;
QString m_searchFilter;
Utils::FileSystemWatcher *m_fileSystemWatcher;
SynchronousImageCache &m_fontImageCache; SynchronousImageCache &m_fontImageCache;
ItemLibraryFileIconProvider *m_fileIconProvider = nullptr;
QHash<QString, QPair<QDateTime, QIcon>> m_iconCache; 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 "itemlibrarywidget.h"
#include "customfilesystemmodel.h" #include "itemlibraryassetsmodel.h"
#include "itemlibraryiconimageprovider.h" #include "itemlibraryiconimageprovider.h"
#include "itemlibraryimport.h" #include "itemlibraryimport.h"
@@ -37,6 +37,7 @@
#include <itemlibraryinfo.h> #include <itemlibraryinfo.h>
#include <itemlibrarymodel.h> #include <itemlibrarymodel.h>
#include <itemlibraryaddimportmodel.h> #include <itemlibraryaddimportmodel.h>
#include "itemlibraryassetsiconprovider.h"
#include <metainfo.h> #include <metainfo.h>
#include <model.h> #include <model.h>
#include <rewritingexception.h> #include <rewritingexception.h>
@@ -46,6 +47,7 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/flowlayout.h> #include <utils/flowlayout.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/filesystemwatcher.h>
#include <utils/stylehelper.h> #include <utils/stylehelper.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
@@ -125,13 +127,16 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
AsynchronousImageCache &asynchronousFontImageCache, AsynchronousImageCache &asynchronousFontImageCache,
SynchronousImageCache &synchronousFontImageCache) SynchronousImageCache &synchronousFontImageCache)
: m_itemIconSize(24, 24) : m_itemIconSize(24, 24)
, m_fontImageCache(synchronousFontImageCache)
, m_itemLibraryModel(new ItemLibraryModel(this)) , m_itemLibraryModel(new ItemLibraryModel(this))
, m_itemLibraryAddImportModel(new ItemLibraryAddImportModel(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_headerWidget(new QQuickWidget(this))
, m_addImportWidget(new QQuickWidget(this)) , m_addImportWidget(new QQuickWidget(this))
, m_itemViewQuickWidget(new QQuickWidget(this)) , m_itemViewQuickWidget(new QQuickWidget(this))
, m_resourcesView(new ItemLibraryResourceView(asynchronousFontImageCache, this)) , m_assetsWidget(new QQuickWidget(this))
, m_imageCache{imageCache} , m_imageCache{imageCache}
{ {
m_compressionTimer.setInterval(200); m_compressionTimer.setInterval(200);
@@ -180,12 +185,47 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
Theme::setupTheme(m_itemViewQuickWidget->engine()); Theme::setupTheme(m_itemViewQuickWidget->engine());
m_itemViewQuickWidget->installEventFilter(this); m_itemViewQuickWidget->installEventFilter(this);
// connect Resources view and its model m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache);
m_resourcesView->setModel(m_resourcesFileSystemModel.data()); // 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 = new QStackedWidget(this);
m_stackedWidget->addWidget(m_itemViewQuickWidget.data()); 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->addWidget(m_addImportWidget.data());
m_stackedWidget->setMinimumHeight(30); m_stackedWidget->setMinimumHeight(30);
m_stackedWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); m_stackedWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
@@ -201,34 +241,12 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
/* style sheets */ /* style sheets */
setStyleSheet(Theme::replaceCssColors( setStyleSheet(Theme::replaceCssColors(
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); 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); m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5), this);
connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource); connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource);
connect(&m_compressionTimer, &QTimer::timeout, this, &ItemLibraryWidget::updateModel); 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", m_itemViewQuickWidget->engine()->addImageProvider("itemlibrary_preview",
new ItemLibraryIconImageProvider{m_imageCache}); new ItemLibraryIconImageProvider{m_imageCache});
@@ -306,6 +324,11 @@ bool ItemLibraryWidget::isSearchActive() const
return !m_filterText.isEmpty(); return !m_filterText.isEmpty();
} }
void ItemLibraryWidget::handleFilesDrop(const QStringList &filesPaths)
{
addResources(filesPaths);
}
void ItemLibraryWidget::delayedUpdateModel() void ItemLibraryWidget::delayedUpdateModel()
{ {
static bool disableTimer = DesignerSettings::getValue(DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER).toBool(); 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); QTC_ASSERT(QFileInfo::exists(itemLibraryQmlPath), return);
m_itemViewQuickWidget->engine()->clearComponentCache(); m_itemViewQuickWidget->engine()->clearComponentCache();
m_itemViewQuickWidget->setSource(QUrl::fromLocalFile(itemLibraryQmlPath)); 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() void ItemLibraryWidget::updateModel()
@@ -395,9 +423,7 @@ void ItemLibraryWidget::updateSearch()
m_itemLibraryModel->setSearchText(m_filterText); m_itemLibraryModel->setSearchText(m_filterText);
m_itemViewQuickWidget->update(); m_itemViewQuickWidget->update();
} else if (m_stackedWidget->currentIndex() == 1) { // Assets tab selected } else if (m_stackedWidget->currentIndex() == 1) { // Assets tab selected
m_resourcesFileSystemModel->setSearchFilter(m_filterText); m_assetsModel->setSearchText(m_filterText);
m_resourcesFileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
m_resourcesView->scrollToTop();
} else if (m_stackedWidget->currentIndex() == 2) { // QML imports tab selected } else if (m_stackedWidget->currentIndex() == 2) { // QML imports tab selected
m_itemLibraryAddImportModel->setSearchText(m_filterText); m_itemLibraryAddImportModel->setSearchText(m_filterText);
} }
@@ -413,10 +439,7 @@ void ItemLibraryWidget::handlePriorityImportsChanged()
void ItemLibraryWidget::setResourcePath(const QString &resourcePath) void ItemLibraryWidget::setResourcePath(const QString &resourcePath)
{ {
if (m_resourcesView->model() == m_resourcesFileSystemModel.data()) { m_assetsModel->setRootPath(resourcePath);
m_resourcesFileSystemModel->setRootPath(resourcePath);
m_resourcesView->setRootIndex(m_resourcesFileSystemModel->indexForPath(resourcePath));
}
updateSearch(); updateSearch();
} }
@@ -429,6 +452,51 @@ void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry, const QPo
m_dragStartPoint = mousePos.toPoint(); 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) void ItemLibraryWidget::setFlowMode(bool b)
{ {
m_itemLibraryModel->setFlowMode(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 } // namespace QmlDesigner

View File

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