You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
764 lines
25 KiB
C++
764 lines
25 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include "ComponentEntityEditorPlugin_precompiled.h"
|
|
|
|
#include "CryEdit.h"
|
|
#include "AssetCatalogModel.h"
|
|
#include "Objects/ComponentEntityObject.h"
|
|
|
|
#include <ISourceControl.h>
|
|
#include <IEditor.h>
|
|
#include <qevent.h>
|
|
#include <qmimedata.h>
|
|
|
|
#include <LmbrCentral/Rendering/LensFlareAsset.h>
|
|
#include <LmbrCentral/Rendering/MeshAsset.h>
|
|
#include <LmbrCentral/Rendering/MaterialAsset.h>
|
|
|
|
#include <AzCore/Memory/Memory.h>
|
|
#include <AzCore/RTTI/TypeInfo.h>
|
|
#include <AzCore/Component/ComponentApplicationBus.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
|
#include <AzCore/Slice/SliceAsset.h>
|
|
#include <AzCore/Asset/AssetManager.h>
|
|
#include <AzCore/Asset/AssetManagerBus.h>
|
|
#include <AzCore/Asset/AssetTypeInfoBus.h>
|
|
|
|
#include <AzFramework/API/ApplicationAPI.h>
|
|
|
|
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorAssetMimeDataContainer.h>
|
|
#include <AzToolsFramework/ToolsComponents/ComponentAssetMimeDataContainer.h>
|
|
#include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
|
|
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
|
#include <AzToolsFramework/Commands/EntityStateCommand.h>
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// AssetCatalogModelWorkerThread
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
AssetCatalogModelWorkerThread::AssetCatalogModelWorkerThread(AssetCatalogModel* catalog, QThread* returnThread)
|
|
: m_catalog(catalog)
|
|
, m_returnThread(returnThread)
|
|
{
|
|
connect(this, &QThread::started, this, &AssetCatalogModelWorkerThread::startJob);
|
|
connect(m_catalog, &AssetCatalogModel::LoadComplete, this, &AssetCatalogModelWorkerThread::ReturnToThread);
|
|
}
|
|
|
|
void AssetCatalogModelWorkerThread::ReturnToThread()
|
|
{
|
|
quit();
|
|
}
|
|
|
|
void AssetCatalogModelWorkerThread::startJob()
|
|
{
|
|
disconnect(this, &QThread::started, this, &AssetCatalogModelWorkerThread::startJob);
|
|
m_catalog->StartProcessingAssets();
|
|
QTimer::singleShot(0, m_catalog, &AssetCatalogModel::ProcessAssets);
|
|
}
|
|
|
|
void AssetCatalogModelWorkerThread::run()
|
|
{
|
|
exec();
|
|
|
|
disconnect(m_catalog, &AssetCatalogModel::LoadComplete, this, &AssetCatalogModelWorkerThread::ReturnToThread);
|
|
m_catalog->moveToThread(m_returnThread);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// AssetCatalogEntry
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
bool AssetCatalogEntry::operator<(const QStandardItem& other) const
|
|
{
|
|
// Set directories as always less than files.
|
|
bool leftIsDir = data(FolderRole).toBool();
|
|
bool rightIsDir = other.data(FolderRole).toBool();
|
|
|
|
if (leftIsDir != rightIsDir)
|
|
{
|
|
return leftIsDir;
|
|
}
|
|
|
|
QVariant leftName = data(Qt::DisplayRole);
|
|
QVariant rightName = other.data(Qt::DisplayRole);
|
|
|
|
return leftName.toString().compare(rightName.toString(), Qt::CaseInsensitive) < 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// AssetCatalogModel
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
AssetCatalogModel::AssetCatalogModel(QObject* parent)
|
|
: QStandardItemModel(parent)
|
|
, m_canProcessAssets(true)
|
|
{
|
|
AZStd::string allExtensions;
|
|
AZStd::vector<AZ::Data::AssetType> assetTypes;
|
|
// Discover all types that the Asset system recognizes.
|
|
// Create a one-to-many map that associates extensions with AssetTypes.
|
|
EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, GetHandledAssetTypes, assetTypes);
|
|
for (auto type : assetTypes)
|
|
{
|
|
AZStd::vector<AZStd::string> extensions;
|
|
allExtensions.clear();
|
|
EBUS_EVENT_ID(type, AZ::AssetTypeInfoBus, GetAssetTypeExtensions, extensions);
|
|
for (int i = 0; i < extensions.size(); i++)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
allExtensions += ";";
|
|
}
|
|
allExtensions += "."; // Adding dots to all extensions to be able to separate full extensions from substrings, i.e. "bin" and input"bin"dings.
|
|
allExtensions += extensions[i].c_str();
|
|
}
|
|
if (!allExtensions.empty())
|
|
{
|
|
auto existingEntry = m_extensionToAssetType.find(allExtensions);
|
|
if (existingEntry != m_extensionToAssetType.end())
|
|
{
|
|
existingEntry->second.push_back(type);
|
|
}
|
|
else
|
|
{
|
|
m_extensionToAssetType.insert(AZStd::make_pair(allExtensions, AZStd::vector<AZ::Uuid> {type}));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Special cases for SimpleAssets. If these get full-fledged AssetData types, these cases can be removed.
|
|
QString textureExtensions = LmbrCentral::TextureAsset::GetFileFilter();
|
|
m_extensionToAssetType.insert(AZStd::make_pair(textureExtensions.replace("*", "").replace(" ", "").toStdString().c_str(), AZStd::vector<AZ::Uuid> { AZ::AzTypeInfo<LmbrCentral::TextureAsset>::Uuid() }));
|
|
QString materialExtensions = LmbrCentral::MaterialAsset::GetFileFilter();
|
|
m_extensionToAssetType.insert(AZStd::make_pair(materialExtensions.replace("*", "").replace(" ", "").toStdString().c_str(), AZStd::vector<AZ::Uuid> { AZ::AzTypeInfo<LmbrCentral::MaterialAsset>::Uuid() }));
|
|
QString dccMaterialExtensions = LmbrCentral::DccMaterialAsset::GetFileFilter();
|
|
m_extensionToAssetType.insert(AZStd::make_pair(dccMaterialExtensions.replace("*", "").replace(" ", "").toStdString().c_str(), AZStd::vector<AZ::Uuid> { AZ::AzTypeInfo<LmbrCentral::DccMaterialAsset>::Uuid() }));
|
|
|
|
AZ::SerializeContext* serializeContext = nullptr;
|
|
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
|
|
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
|
|
|
|
serializeContext->EnumerateDerived<AZ::Component>([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
|
|
{
|
|
if (classData->m_editData)
|
|
{
|
|
AZ::Data::AssetType assetType;
|
|
const AZ::Edit::ElementData* element = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
|
|
if (element)
|
|
{
|
|
const AZ::Edit::Attribute* assetTypeAttribute = element->FindAttribute(AZ::Edit::Attributes::PrimaryAssetType);
|
|
if (assetTypeAttribute)
|
|
{
|
|
auto* assetTypeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Uuid>*>(assetTypeAttribute);
|
|
if (assetTypeData)
|
|
{
|
|
assetType = assetTypeData->Get(nullptr);
|
|
m_assetTypeToComponent[assetType] = classData->m_azRtti->GetTypeId();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assetType = AZ::Data::AssetType::CreateNull();
|
|
}
|
|
|
|
if (!assetType.IsNull())
|
|
{
|
|
const AZ::Edit::Attribute* iconAttribute = element->FindAttribute(AZ_CRC("Icon"));
|
|
if (iconAttribute)
|
|
{
|
|
auto* iconAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(iconAttribute);
|
|
if (iconAttributeData)
|
|
{
|
|
QIcon icon(iconAttributeData->Get(nullptr));
|
|
if (!icon.isNull())
|
|
{
|
|
m_assetTypeToIcon[assetType] = icon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
AssetCatalogModel::~AssetCatalogModel()
|
|
{
|
|
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
AZ::Data::AssetType AssetCatalogModel::GetAssetType(QString filename) const
|
|
{
|
|
AZ::Data::AssetType returnType = AZ::Uuid::CreateNull();
|
|
|
|
// Compare file extensions with the map created from the asset database.
|
|
int dotIndex = filename.lastIndexOf('.');
|
|
if (dotIndex >= 0)
|
|
{
|
|
QString extension = filename.mid(dotIndex);
|
|
for (auto pair : m_extensionToAssetType)
|
|
{
|
|
QString qExtensions = pair.first.c_str();
|
|
if (qExtensions.indexOf(extension) >= 0)
|
|
{
|
|
if (pair.second.size() > 1)
|
|
{
|
|
// There are multiple types with this extension. Check each handler to see if they can handle this data type.
|
|
AZStd::string azFilename = filename.toStdString().c_str();
|
|
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, MakePathAssetRootRelative, azFilename);
|
|
AZ::Data::AssetId assetId;
|
|
EBUS_EVENT_RESULT(assetId, AZ::Data::AssetCatalogRequestBus, GetAssetIdByPath, azFilename.c_str(), AZ::Data::s_invalidAssetType, false);
|
|
|
|
for (AZ::Uuid type : pair.second)
|
|
{
|
|
const AZ::Data::AssetHandler* handler = AZ::Data::AssetManager::Instance().GetHandler(type);
|
|
if (handler && handler->CanHandleAsset(assetId))
|
|
{
|
|
returnType = type;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
returnType = pair.second[0];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return returnType;
|
|
}
|
|
|
|
QStandardItem* AssetCatalogModel::GetPath(QString& path, bool createIfNeeded, QStandardItem* parent)
|
|
{
|
|
if (!parent)
|
|
{
|
|
parent = invisibleRootItem();
|
|
}
|
|
QString cleanPath = path.replace("\\", "/");
|
|
while (cleanPath.startsWith("/"))
|
|
{
|
|
cleanPath = cleanPath.mid(1);
|
|
}
|
|
while (cleanPath.endsWith("/"))
|
|
{
|
|
cleanPath.chop(1);
|
|
}
|
|
|
|
QString currentFolder;
|
|
QString restOfPath;
|
|
|
|
int slashIdx = cleanPath.indexOf('/', 1);
|
|
if (slashIdx < 0)
|
|
{
|
|
currentFolder = cleanPath;
|
|
restOfPath.clear();
|
|
}
|
|
else
|
|
{
|
|
currentFolder = cleanPath.left(slashIdx);
|
|
restOfPath = cleanPath.mid(slashIdx + 1);
|
|
}
|
|
|
|
if (currentFolder.isEmpty())
|
|
{
|
|
return parent;
|
|
}
|
|
|
|
for (int i = 0; i < parent->rowCount(); i++)
|
|
{
|
|
QString name = parent->child(i)->data(Qt::DisplayRole).toString();
|
|
bool isFolder = parent->child(i)->data(AssetCatalogEntry::FolderRole).toBool();
|
|
|
|
if (currentFolder == name && isFolder)
|
|
{
|
|
if (restOfPath.isEmpty())
|
|
{
|
|
return parent->child(i);
|
|
}
|
|
else
|
|
{
|
|
return GetPath(restOfPath, createIfNeeded, parent->child(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (createIfNeeded)
|
|
{
|
|
QString fullpath = parent->data(AssetCatalogEntry::FilePathRole).toString();
|
|
fullpath += currentFolder + "/";
|
|
AssetCatalogEntry* folder = new AssetCatalogEntry();
|
|
folder->setData(currentFolder, Qt::DisplayRole);
|
|
folder->setData(fullpath, AssetCatalogEntry::FilePathRole);
|
|
folder->setData(true, AssetCatalogEntry::FolderRole);
|
|
folder->setData(true, AssetCatalogEntry::VisibilityRole);
|
|
|
|
parent->appendRow(folder);
|
|
|
|
if (restOfPath.isEmpty())
|
|
{
|
|
return folder;
|
|
}
|
|
else
|
|
{
|
|
return GetPath(restOfPath, createIfNeeded, folder);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AssetCatalogEntry* AssetCatalogModel::FindAsset(QString assetPath)
|
|
{
|
|
QString path;
|
|
QString asset;
|
|
|
|
// Separate file name and folder name.
|
|
int slashIdx = assetPath.lastIndexOf('/');
|
|
if (slashIdx < 0)
|
|
{
|
|
asset = assetPath;
|
|
path.clear();
|
|
}
|
|
else
|
|
{
|
|
path = assetPath.left(slashIdx);
|
|
asset = assetPath.mid(slashIdx + 1);
|
|
}
|
|
|
|
QStandardItem* folder = GetPath(path, false);
|
|
|
|
if (folder)
|
|
{
|
|
for (int i = 0; i < folder->rowCount(); i++)
|
|
{
|
|
QString name = folder->child(i)->data(Qt::DisplayRole).toString();
|
|
if (name == asset)
|
|
{
|
|
AssetCatalogEntry* entry = static_cast<AssetCatalogEntry*>(folder->child(i));
|
|
return entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
AssetCatalogEntry* AssetCatalogModel::AddAsset(QString assetPath, AZ::Data::AssetId id)
|
|
{
|
|
QString path;
|
|
QString asset;
|
|
|
|
// Separate file name and folder name.
|
|
int slashIdx = assetPath.lastIndexOf('/');
|
|
if (slashIdx < 0)
|
|
{
|
|
asset = assetPath;
|
|
path.clear();
|
|
}
|
|
else
|
|
{
|
|
path = assetPath.left(slashIdx);
|
|
asset = assetPath.mid(slashIdx + 1);
|
|
}
|
|
|
|
QRegExp mipMapExtension("\\.dds\\.\\d+a?$"); // Files that end with ".dds.#", with an optional "a"
|
|
if (asset.contains(mipMapExtension))
|
|
{
|
|
// Mip map files should be ignored by the file browser.
|
|
// This is a temporary solution until texture streams are refactored.
|
|
return nullptr;
|
|
}
|
|
|
|
QStandardItem* folder = GetPath(path, true);
|
|
|
|
QString fullPath = folder->data(AssetCatalogEntry::FilePathRole).toString() + asset;
|
|
AZ::Data::AssetType assetType = GetAssetType(fullPath);
|
|
AZ::Uuid classId = AZ::Uuid::CreateNull();
|
|
auto it = m_assetTypeToComponent.find(assetType);
|
|
if (it != m_assetTypeToComponent.end())
|
|
{
|
|
classId = it->second;
|
|
}
|
|
|
|
AssetCatalogEntry* entry = new AssetCatalogEntry();
|
|
entry->setData(asset, Qt::DisplayRole);
|
|
entry->setData(fullPath, AssetCatalogEntry::FilePathRole);
|
|
entry->setData(false, AssetCatalogEntry::FolderRole);
|
|
entry->setData(true, AssetCatalogEntry::VisibilityRole);
|
|
|
|
entry->m_assetId = id;
|
|
entry->m_assetType = assetType;
|
|
entry->m_classId = classId;
|
|
|
|
if (!assetType.IsNull())
|
|
{
|
|
auto iconIt = m_assetTypeToIcon.find(assetType);
|
|
if (iconIt == m_assetTypeToIcon.end())
|
|
{
|
|
// The m_assetTypeToIcon map was seeded with icons for known asset types.
|
|
// If we come across an asset type that is not associated with a component,
|
|
// we'll get its icon from OS if we can. This will help users recognize files more easily.
|
|
QFileInfo fileInfo(m_rootPath + fullPath);
|
|
QIcon fileIcon = m_iconProvider.icon(fileInfo);
|
|
// Now, make a deep copy for OS-provided icons. On Windows 10, there seems to be an issue with
|
|
// icons' memory being reclaimed and crashing the Editor.
|
|
QSize size = fileIcon.actualSize(QSize(16, 16));
|
|
QIcon deepCopy = fileIcon.pixmap(size).copy(0, 0, size.width(), size.height());
|
|
|
|
if (!fileIcon.isNull())
|
|
{
|
|
m_assetTypeToIcon[assetType] = deepCopy;
|
|
}
|
|
}
|
|
}
|
|
|
|
folder->appendRow(entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
AssetCatalogEntry* AssetCatalogModel::RemoveAsset(QString assetPath)
|
|
{
|
|
AssetCatalogEntry* entry = FindAsset(assetPath);
|
|
if (entry)
|
|
{
|
|
QStandardItem* parent = entry->parent();
|
|
if (parent)
|
|
{
|
|
parent->removeRow(entry->row());
|
|
AssetCatalogEntry* folder = static_cast<AssetCatalogEntry*>(parent);
|
|
return folder;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void AssetCatalogModel::LoadDatabase()
|
|
{
|
|
clear();
|
|
|
|
AZStd::string assetRootFolder;
|
|
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
|
{
|
|
settingsRegistry->Get(assetRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
|
|
}
|
|
m_rootPath = assetRootFolder.c_str();
|
|
|
|
auto startCB = []() {};
|
|
|
|
auto enumerateCB = [this](const AZ::Data::AssetId id, const AZ::Data::AssetInfo& assetInfo)
|
|
{
|
|
DatabaseEntry* entry = new DatabaseEntry(id, assetInfo.m_relativePath.c_str());
|
|
m_fileCache.push_back(entry);
|
|
};
|
|
|
|
auto endCB = [this]()
|
|
{
|
|
m_fileCacheCurrentIndex = 0;
|
|
Q_EMIT UpdateProgress(0);
|
|
Q_EMIT SetTotalProgress(m_fileCache.size());
|
|
};
|
|
|
|
EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, EnumerateAssets, startCB, enumerateCB, endCB);
|
|
|
|
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
|
|
|
m_canProcessAssets = true;
|
|
}
|
|
|
|
void AssetCatalogModel::ProcessAssets()
|
|
{
|
|
if (m_fileCacheCurrentIndex >= m_fileCache.size())
|
|
{
|
|
sort(0);
|
|
m_fileCache.clear();
|
|
Q_EMIT LoadComplete();
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; m_canProcessAssets && i < ASSET_CATALOG_BATCH_SIZE && m_fileCacheCurrentIndex < m_fileCache.size(); i++, m_fileCacheCurrentIndex++)
|
|
{
|
|
AddAsset(m_fileCache[m_fileCacheCurrentIndex]->m_path, m_fileCache[m_fileCacheCurrentIndex]->m_id);
|
|
}
|
|
Q_EMIT UpdateProgress(m_fileCacheCurrentIndex);
|
|
|
|
if (m_canProcessAssets)
|
|
{
|
|
QTimer::singleShot(1, this, &AssetCatalogModel::ProcessAssets);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetCatalogModel::StartProcessingAssets()
|
|
{
|
|
m_canProcessAssets = true;
|
|
}
|
|
|
|
void AssetCatalogModel::StopProcessingAssets()
|
|
{
|
|
m_canProcessAssets = false;
|
|
}
|
|
|
|
|
|
void AssetCatalogModel::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
|
|
{
|
|
AZ::Data::AssetInfo assetInfo;
|
|
EBUS_EVENT_RESULT(assetInfo, AZ::Data::AssetCatalogRequestBus, GetAssetInfoById, assetId);
|
|
|
|
// note that this will get called twice, once with the real assetId and once with legacy assetId.
|
|
// we only want to add the real asset to the list, in which the assetId passed in is equal to the final assetId returned
|
|
// otherwise, you look up assetId (and its a legacy assetId) and the actual asset will be different.
|
|
if ((assetInfo.m_assetId.IsValid()) && (assetInfo.m_assetId == assetId))
|
|
{
|
|
AssetCatalogEntry* asset = AddAsset(assetInfo.m_relativePath.c_str(), assetInfo.m_assetId);
|
|
if (asset)
|
|
{
|
|
Q_EMIT itemChanged(asset);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetCatalogModel::OnCatalogAssetRemoved(const AZ::Data::AssetId& /*assetId*/, const AZ::Data::AssetInfo& assetInfo)
|
|
{
|
|
AssetCatalogEntry* asset = RemoveAsset(assetInfo.m_relativePath.c_str());
|
|
if (asset)
|
|
{
|
|
Q_EMIT itemChanged(asset);
|
|
}
|
|
}
|
|
|
|
QVariant AssetCatalogModel::data(const QModelIndex& index, int role) const
|
|
{
|
|
QStandardItem* item = itemFromIndex(index);
|
|
if (item && role == Qt::DecorationRole)
|
|
{
|
|
AssetCatalogEntry* entry = static_cast<AssetCatalogEntry*>(item);
|
|
auto it = m_assetTypeToIcon.find(entry->m_assetType);
|
|
if (it != m_assetTypeToIcon.end())
|
|
{
|
|
return it->second;
|
|
}
|
|
|
|
bool isFolder = item->data(AssetCatalogEntry::FolderRole).toBool();
|
|
return isFolder ? m_iconProvider.icon(QFileIconProvider::Folder) : m_iconProvider.icon(QFileIconProvider::File);
|
|
}
|
|
|
|
return QStandardItemModel::data(index, role);
|
|
}
|
|
|
|
QMimeData* AssetCatalogModel::mimeData(const QModelIndexList& indexes) const
|
|
{
|
|
AssetCatalogEntry* item = static_cast<AssetCatalogEntry*>(itemFromIndex(indexes[0]));
|
|
bool isFolder = item ? item->data(AssetCatalogEntry::FolderRole).toBool() : true;
|
|
|
|
if (isFolder)
|
|
{
|
|
return new QMimeData();
|
|
}
|
|
|
|
QString fullPath = item->data(AssetCatalogEntry::FilePathRole).toString();
|
|
QMimeData* mimeData = new QMimeData;
|
|
|
|
if (!item->m_assetType.IsNull() && item->m_assetId.IsValid())
|
|
{
|
|
// This mime data is used to drag into PropertyAssetCtrl fields.
|
|
AzToolsFramework::EditorAssetMimeDataContainer mimeDataContainer;
|
|
mimeDataContainer.AddEditorAsset(item->m_assetId, item->m_assetType);
|
|
mimeDataContainer.AddToMimeData(mimeData);
|
|
|
|
// This mime data is used for spawning of entities with components and the adding of components through assets.
|
|
AzToolsFramework::ComponentAssetMimeDataContainer componentContainer;
|
|
componentContainer.AddComponentAsset(item->m_classId, item->m_assetId);
|
|
componentContainer.AddToMimeData(mimeData);
|
|
}
|
|
// Also, add the filename, for untyped fields.
|
|
QList<QUrl> urls;
|
|
urls << QUrl::fromLocalFile(fullPath);
|
|
mimeData->setUrls(urls);
|
|
|
|
return mimeData;
|
|
}
|
|
|
|
QVariant AssetCatalogModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role == Qt::DisplayRole && section == 0 && orientation == Qt::Horizontal)
|
|
{
|
|
return tr("Assets");
|
|
}
|
|
|
|
return QAbstractItemModel::headerData(section, orientation, role);
|
|
}
|
|
|
|
void AssetCatalogModel::SearchCriteriaChanged(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator)
|
|
{
|
|
BuildFilter(criteriaList, filterOperator);
|
|
InvalidateFilter();
|
|
}
|
|
|
|
void AssetCatalogModel::BuildFilter(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator)
|
|
{
|
|
ClearFilterRegExp();
|
|
|
|
if (criteriaList.size() > 0)
|
|
{
|
|
QString filter, tag, text;
|
|
for (int i = 0; i < criteriaList.size(); i++)
|
|
{
|
|
AzToolsFramework::SearchCriteriaButton::SplitTagAndText(criteriaList[i], tag, text);
|
|
if (tag.isEmpty())
|
|
{
|
|
tag = "null";
|
|
}
|
|
|
|
filter = m_filtersRegExp[tag.toStdString().c_str()].pattern();
|
|
|
|
if (filterOperator == AzToolsFramework::FilterOperatorType::Or)
|
|
{
|
|
if (filter.isEmpty())
|
|
{
|
|
filter = text;
|
|
}
|
|
else
|
|
{
|
|
filter += "|" + text;
|
|
}
|
|
}
|
|
else if (filterOperator == AzToolsFramework::FilterOperatorType::And)
|
|
{
|
|
filter += "(?=.*" + text + ")"; // Using Lookaheads to produce an "and" effect.
|
|
}
|
|
|
|
SetFilterRegExp(tag.toStdString().c_str(), QRegExp(filter, Qt::CaseInsensitive));
|
|
}
|
|
}
|
|
}
|
|
|
|
void AssetCatalogModel::SetFilterRegExp(const AZStd::string& filterType, const QRegExp& regExp)
|
|
{
|
|
m_filtersRegExp[filterType] = regExp;
|
|
}
|
|
|
|
void AssetCatalogModel::ClearFilterRegExp(const AZStd::string& filterType)
|
|
{
|
|
if (filterType.empty())
|
|
{
|
|
for (auto& it : m_filtersRegExp)
|
|
{
|
|
it.second = QRegExp();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_filtersRegExp[filterType] = QRegExp();
|
|
}
|
|
}
|
|
|
|
void AssetCatalogModel::InvalidateFilter()
|
|
{
|
|
ApplyFilter(invisibleRootItem());
|
|
}
|
|
|
|
void AssetCatalogModel::ApplyFilter(QStandardItem* parent)
|
|
{
|
|
// Set the visibility as a breadth-first search of the tree.
|
|
// This will allow us to also set our parents visible if we are visible
|
|
// without a later search overriding us.
|
|
for (int i = 0; i < parent->rowCount(); i++)
|
|
{
|
|
QStandardItem* child = parent->child(i);
|
|
if (m_filtersRegExp["name"].isEmpty())
|
|
{
|
|
child->setData(true, AssetCatalogEntry::VisibilityRole);
|
|
}
|
|
else
|
|
{
|
|
QString assetname = child->data(Qt::DisplayRole).toString();
|
|
bool matchesFilter = assetname.contains(m_filtersRegExp["name"]);
|
|
child->setData(matchesFilter, AssetCatalogEntry::VisibilityRole);
|
|
|
|
if (matchesFilter)
|
|
{
|
|
// Set all parents to visible.
|
|
QStandardItem* visiblityParent = parent;
|
|
bool isVisible = visiblityParent->data(AssetCatalogEntry::VisibilityRole).toBool();
|
|
while (!isVisible) // Checking isVisible gives us a short circuit for already visible folders.
|
|
{
|
|
visiblityParent->setData(true, AssetCatalogEntry::VisibilityRole);
|
|
visiblityParent = visiblityParent->parent();
|
|
|
|
isVisible = visiblityParent ? visiblityParent->data(AssetCatalogEntry::VisibilityRole).toBool() : true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recurse through the children that are folders
|
|
for (int i = 0; i < parent->rowCount(); i++)
|
|
{
|
|
QStandardItem* child = parent->child(i);
|
|
bool isFolder = child->data(AssetCatalogEntry::FolderRole).toBool();
|
|
if (isFolder)
|
|
{
|
|
ApplyFilter(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
QString AssetCatalogModel::FileName(const QModelIndex& index) const
|
|
{
|
|
QStandardItem* item = itemFromIndex(index);
|
|
if (item)
|
|
{
|
|
return item->data(Qt::DisplayRole).toString();
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
QString AssetCatalogModel::FilePath(const QModelIndex& index) const
|
|
{
|
|
// filePath contains the name of the file.
|
|
QStandardItem* item = itemFromIndex(index);
|
|
if (item)
|
|
{
|
|
QString fullPath = RootPath();
|
|
fullPath += item->data(AssetCatalogEntry::FilePathRole).toString();
|
|
return fullPath;
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
AssetCatalogEntry* AssetCatalogModel::AssetData(const QModelIndex& index) const
|
|
{
|
|
return static_cast<AssetCatalogEntry*>(itemFromIndex(index));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// End of context menu handling
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <UI/moc_AssetCatalogModel.cpp>
|
|
|