diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml new file mode 100644 index 00000000000..58f42cdb5d1 --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml @@ -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() + } + } + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index 09697c23ecf..458c5d3150c 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -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 } + } } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5d4f8e84da0..23fc187d995 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -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 diff --git a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp deleted file mode 100644 index 9873963411c..00000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp +++ /dev/null @@ -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 -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace QmlDesigner { - -static const QStringList &supportedImageSuffixes() -{ - static QStringList retList; - if (retList.isEmpty()) { - const QList 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> &iconCache) - : QFileIconProvider() - , m_fontImageCache(fontImageCache) - , m_iconCache(iconCache) - { - } - - QIcon icon( const QFileInfo & info ) const override - { - const QString filePath = info.absoluteFilePath(); - QPair &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 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> &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(); - 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 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 &CustomFileSystemModel::supportedSuffixes() const -{ - static QSet 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 &CustomFileSystemModel::previewableSuffixes() const -{ - static QSet previewableSuffixes; - if (previewableSuffixes.isEmpty()) { - auto insertSuffixes = [](const QStringList &suffixes) { - for (const auto &suffix : suffixes) - previewableSuffixes.insert(suffix); - }; - insertSuffixes(supportedFontSuffixes()); - } - return previewableSuffixes; - -} - -void CustomFileSystemModel::appendIfNotFiltered(const QString &file) -{ - if (filterMetaIcons(file)) - 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 diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp new file mode 100644 index 00000000000..755752f4fc9 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp @@ -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 diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h new file mode 100644 index 00000000000..4ef67ce63ea --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h @@ -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 + +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 diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp new file mode 100644 index 00000000000..ae193a86a3c --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp @@ -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 + +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 ItemLibraryAssetsDirsModel::roleNames() const +{ + return m_roleNames; +} + +void ItemLibraryAssetsDirsModel::addDir(ItemLibraryAssetsDir *assetsDir) +{ + m_dirs.append(assetsDir); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.h similarity index 65% rename from src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h rename to src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.h index fa22973c0c5..06bc5a5839a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.h @@ -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 - -#include - -QT_BEGIN_NAMESPACE -class QActionGroup; -QT_END_NAMESPACE +#include +#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 roleNames() const override; + + void addDir(ItemLibraryAssetsDir *assetsDir); private: - void addSizeAction(QActionGroup *group, const QString &text, int size, int iconSize); - - std::unique_ptr m_fontPreviewTooltipBackend; + QList m_dirs; + QHash m_roleNames; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp new file mode 100644 index 00000000000..a2142b683eb --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp @@ -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 ItemLibraryAssetsFilesModel::roleNames() const +{ + return m_roleNames; +} + +void ItemLibraryAssetsFilesModel::addFile(const QString &filePath) +{ + m_files.append(filePath); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h new file mode 100644 index 00000000000..25577dce515 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h @@ -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 + +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 roleNames() const override; + + void addFile(const QString &filePath); + +private: + enum Roles {FileNameRole = Qt::UserRole + 1, + FilePathRole}; + + QStringList m_files; + QHash m_roleNames; +}; + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp new file mode 100644 index 00000000000..7382a175fc0 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp @@ -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 +#include +#include + +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 + diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h new file mode 100644 index 00000000000..3c5bc4081e3 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h @@ -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 + +#include + +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 iconSizes = {{384, 384}, + {192, 192}, // Large + {256, 256}, + {128, 128}, // Drag + {96, 96}, // Medium + {48, 48}, // Small + {64, 64}, + {32, 32}}; // List +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp new file mode 100644 index 00000000000..c7dbf758e5a --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "qmldesignerplugin.h" +#include +#include +#include +#include + +namespace QmlDesigner { + +void ItemLibraryAssetsModel::saveExpandedState(bool expanded, const QString §ionName) +{ + m_expandedStateHash.insert(sectionName, expanded); +} + +bool ItemLibraryAssetsModel::loadExpandedState(const QString §ionName) +{ + return m_expandedStateHash.value(sectionName, true); +} + +const QStringList &ItemLibraryAssetsModel::supportedImageSuffixes() +{ + static QStringList retList; + if (retList.isEmpty()) { + const QList 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 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 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 &ItemLibraryAssetsModel::supportedSuffixes() const +{ + static QSet 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 &ItemLibraryAssetsModel::previewableSuffixes() const +{ + static QSet previewableSuffixes; + if (previewableSuffixes.isEmpty()) { + auto insertSuffixes = [](const QStringList &suffixes) { + for (const auto &suffix : suffixes) + previewableSuffixes.insert(suffix); + }; + insertSuffixes(supportedFontSuffixes()); + } + return previewableSuffixes; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.h similarity index 55% rename from src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h rename to src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.h index eabd6c1e23b..1a6535bdfe5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.h @@ -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 +#include #include #include #include @@ -34,59 +34,54 @@ #include #include -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 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 resourceTypeAndData(const QModelIndex &index) const; const QSet &supportedSuffixes() const; const QSet &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 §ionName); + static bool loadExpandedState(const QString §ionName); - QFileSystemModel *m_fileSystemModel; - QStringList m_files; - QString m_searchFilter; - Utils::FileSystemWatcher *m_fileSystemWatcher; +private: SynchronousImageCache &m_fontImageCache; - ItemLibraryFileIconProvider *m_fileIconProvider = nullptr; QHash> m_iconCache; - QTimer m_updatePathTimer; + + QString m_searchText; + Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr; + ItemLibraryAssetsDir *m_assetsDir = nullptr; + + QHash m_roleNames; + inline static QHash m_expandedStateHash; }; -} //QmlDesigner +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp deleted file mode 100644 index 110c14373aa..00000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp +++ /dev/null @@ -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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -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(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(model()); - Q_ASSERT(fileSystemModel); - QPair 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(model()); - Q_ASSERT(fileSystemModel); - QHelpEvent *helpEvent = static_cast(event); - QModelIndex index = indexAt(helpEvent->pos()); - if (index.isValid()) { - QFileInfo fi = fileSystemModel->fileInfo(index); - if (fileSystemModel->previewableSuffixes().contains(fi.suffix())) { - QString filePath = fi.absoluteFilePath(); - if (!filePath.isEmpty()) { - if (!m_fontPreviewTooltipBackend->isVisible() - || m_fontPreviewTooltipBackend->path() != filePath) { - m_fontPreviewTooltipBackend->setPath(filePath); - m_fontPreviewTooltipBackend->setName(fi.fileName()); - m_fontPreviewTooltipBackend->showTooltip(); - } else { - m_fontPreviewTooltipBackend->reposition(); - } - return true; - } - } - } - m_fontPreviewTooltipBackend->hideTooltip(); - } else if (event->type() == QEvent::Leave) { - m_fontPreviewTooltipBackend->hideTooltip(); - } else if (event->type() == QEvent::HoverMove) { - if (m_fontPreviewTooltipBackend->isVisible()) { - auto fileSystemModel = qobject_cast(model()); - Q_ASSERT(fileSystemModel); - auto *he = static_cast(event); - QModelIndex index = indexAt(he->pos()); - if (index.isValid()) { - QFileInfo fi = fileSystemModel->fileInfo(index); - if (fi.absoluteFilePath() != m_fontPreviewTooltipBackend->path()) - m_fontPreviewTooltipBackend->hideTooltip(); - else - m_fontPreviewTooltipBackend->reposition(); - } - } - } - - return QListView::viewportEvent(event); -} - -} // namespace QmlDesigner - diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 10e8cde8c3c..f220822bc13 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -25,7 +25,7 @@ #include "itemlibrarywidget.h" -#include "customfilesystemmodel.h" +#include "itemlibraryassetsmodel.h" #include "itemlibraryiconimageprovider.h" #include "itemlibraryimport.h" @@ -37,6 +37,7 @@ #include #include #include +#include "itemlibraryassetsiconprovider.h" #include #include #include @@ -46,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -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(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{ + {{"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 &suffixes = m_resourcesFileSystemModel->supportedSuffixes(); - const QList 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 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 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 &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 diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 72bd9bdfe92..122e67eacd3 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -26,12 +26,12 @@ #pragma once #include "itemlibraryinfo.h" -#include "itemlibraryresourceview.h" #include "import.h" #include #include #include +#include "itemlibraryassetsmodel.h" #include #include @@ -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 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 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 &files); void updateSearch(); void handlePriorityImportsChanged(); QTimer m_compressionTimer; QSize m_itemIconSize; + SynchronousImageCache &m_fontImageCache; QPointer m_itemLibraryInfo; QPointer m_itemLibraryModel; QPointer m_itemLibraryAddImportModel; - QPointer m_resourcesFileSystemModel; + ItemLibraryAssetsIconProvider *m_assetsIconProvider = nullptr; + Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr; + QPointer m_assetsModel; QPointer m_stackedWidget; QScopedPointer m_headerWidget; QScopedPointer m_addImportWidget; QScopedPointer m_itemViewQuickWidget; - QScopedPointer m_resourcesView; + QScopedPointer m_assetsWidget; std::unique_ptr m_previewTooltipBackend; + std::unique_ptr m_fontPreviewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut; AsynchronousImageCache &m_imageCache;