AssetCatalogModel.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <EditorDefs.h>
  9. #include "CryEdit.h"
  10. #include "AssetCatalogModel.h"
  11. #include <IEditor.h>
  12. #include <qevent.h>
  13. #include <qmimedata.h>
  14. #include <AzCore/Memory/Memory.h>
  15. #include <AzCore/RTTI/TypeInfo.h>
  16. #include <AzCore/Component/ComponentApplicationBus.h>
  17. #include <AzCore/Serialization/SerializeContext.h>
  18. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  19. #include <AzCore/Asset/AssetManager.h>
  20. #include <AzCore/Asset/AssetManagerBus.h>
  21. #include <AzCore/Asset/AssetTypeInfoBus.h>
  22. #include <AzFramework/API/ApplicationAPI.h>
  23. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  24. #include <AzToolsFramework/Entity/EditorEntityContextBus.h>
  25. #include <AzToolsFramework/ToolsComponents/EditorAssetMimeDataContainer.h>
  26. #include <AzToolsFramework/ToolsComponents/ComponentAssetMimeDataContainer.h>
  27. #include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
  28. #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
  29. #include <QTimer>
  30. ///////////////////////////////////////////////////////////////////////////////
  31. // AssetCatalogModelWorkerThread
  32. ///////////////////////////////////////////////////////////////////////////////
  33. AssetCatalogModelWorkerThread::AssetCatalogModelWorkerThread(AssetCatalogModel* catalog, QThread* returnThread)
  34. : m_catalog(catalog)
  35. , m_returnThread(returnThread)
  36. {
  37. connect(this, &QThread::started, this, &AssetCatalogModelWorkerThread::startJob);
  38. connect(m_catalog, &AssetCatalogModel::LoadComplete, this, &AssetCatalogModelWorkerThread::ReturnToThread);
  39. }
  40. void AssetCatalogModelWorkerThread::ReturnToThread()
  41. {
  42. quit();
  43. }
  44. void AssetCatalogModelWorkerThread::startJob()
  45. {
  46. disconnect(this, &QThread::started, this, &AssetCatalogModelWorkerThread::startJob);
  47. m_catalog->StartProcessingAssets();
  48. QTimer::singleShot(0, m_catalog, &AssetCatalogModel::ProcessAssets);
  49. }
  50. void AssetCatalogModelWorkerThread::run()
  51. {
  52. exec();
  53. disconnect(m_catalog, &AssetCatalogModel::LoadComplete, this, &AssetCatalogModelWorkerThread::ReturnToThread);
  54. m_catalog->moveToThread(m_returnThread);
  55. }
  56. ///////////////////////////////////////////////////////////////////////////////
  57. // AssetCatalogEntry
  58. ///////////////////////////////////////////////////////////////////////////////
  59. bool AssetCatalogEntry::operator<(const QStandardItem& other) const
  60. {
  61. // Set directories as always less than files.
  62. bool leftIsDir = data(FolderRole).toBool();
  63. bool rightIsDir = other.data(FolderRole).toBool();
  64. if (leftIsDir != rightIsDir)
  65. {
  66. return leftIsDir;
  67. }
  68. QVariant leftName = data(Qt::DisplayRole);
  69. QVariant rightName = other.data(Qt::DisplayRole);
  70. return leftName.toString().compare(rightName.toString(), Qt::CaseInsensitive) < 0;
  71. }
  72. ///////////////////////////////////////////////////////////////////////////////
  73. // AssetCatalogModel
  74. ///////////////////////////////////////////////////////////////////////////////
  75. AssetCatalogModel::AssetCatalogModel(QObject* parent)
  76. : QStandardItemModel(parent)
  77. , m_canProcessAssets(true)
  78. {
  79. AZStd::string allExtensions;
  80. AZStd::vector<AZ::Data::AssetType> assetTypes;
  81. // Discover all types that the Asset system recognizes.
  82. // Create a one-to-many map that associates extensions with AssetTypes.
  83. AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::GetHandledAssetTypes, assetTypes);
  84. for (auto type : assetTypes)
  85. {
  86. AZStd::vector<AZStd::string> extensions;
  87. allExtensions.clear();
  88. AZ::AssetTypeInfoBus::Event(type, &AZ::AssetTypeInfoBus::Events::GetAssetTypeExtensions, extensions);
  89. for (int i = 0; i < extensions.size(); i++)
  90. {
  91. if (i > 0)
  92. {
  93. allExtensions += ";";
  94. }
  95. allExtensions += "."; // Adding dots to all extensions to be able to separate full extensions from substrings, i.e. "bin" and input"bin"dings.
  96. allExtensions += extensions[i].c_str();
  97. }
  98. if (!allExtensions.empty())
  99. {
  100. auto existingEntry = m_extensionToAssetType.find(allExtensions);
  101. if (existingEntry != m_extensionToAssetType.end())
  102. {
  103. existingEntry->second.push_back(type);
  104. }
  105. else
  106. {
  107. m_extensionToAssetType.insert(AZStd::make_pair(allExtensions, AZStd::vector<AZ::Uuid> {type}));
  108. }
  109. }
  110. }
  111. AZ::SerializeContext* serializeContext = nullptr;
  112. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  113. AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
  114. serializeContext->EnumerateDerived<AZ::Component>([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
  115. {
  116. if (classData->m_editData)
  117. {
  118. AZ::Data::AssetType assetType;
  119. const AZ::Edit::ElementData* element = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
  120. if (element)
  121. {
  122. const AZ::Edit::Attribute* assetTypeAttribute = element->FindAttribute(AZ::Edit::Attributes::PrimaryAssetType);
  123. if (assetTypeAttribute)
  124. {
  125. auto* assetTypeData = azdynamic_cast<const AZ::Edit::AttributeData<AZ::Uuid>*>(assetTypeAttribute);
  126. if (assetTypeData)
  127. {
  128. assetType = assetTypeData->Get(nullptr);
  129. m_assetTypeToComponent[assetType] = classData->m_azRtti->GetTypeId();
  130. }
  131. }
  132. else
  133. {
  134. assetType = AZ::Data::AssetType::CreateNull();
  135. }
  136. if (!assetType.IsNull())
  137. {
  138. const AZ::Edit::Attribute* iconAttribute = element->FindAttribute(AZ_CRC_CE("Icon"));
  139. if (iconAttribute)
  140. {
  141. auto* iconAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(iconAttribute);
  142. if (iconAttributeData)
  143. {
  144. QIcon icon(iconAttributeData->Get(nullptr));
  145. if (!icon.isNull())
  146. {
  147. m_assetTypeToIcon[assetType] = icon;
  148. }
  149. }
  150. }
  151. }
  152. }
  153. }
  154. return true;
  155. });
  156. }
  157. AssetCatalogModel::~AssetCatalogModel()
  158. {
  159. AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
  160. }
  161. AZ::Data::AssetType AssetCatalogModel::GetAssetType(const QString &filename) const
  162. {
  163. // Compare file extensions with the map created from the asset database.
  164. int dotIndex = filename.lastIndexOf('.');
  165. if (dotIndex < 0)
  166. {
  167. return AZ::Uuid::CreateNull();
  168. }
  169. QStringRef extension = filename.midRef(dotIndex);
  170. for (const auto& pair : m_extensionToAssetType)
  171. {
  172. QString qExtensions = pair.first.c_str();
  173. if (qExtensions.indexOf(extension) < 0 || pair.second.empty())
  174. {
  175. continue;
  176. }
  177. if (pair.second.size() == 1)
  178. {
  179. return pair.second[0];
  180. }
  181. // There are multiple types with this extension. Search for a handler that can handle this data type.
  182. AZStd::string azFilename = filename.toStdString().c_str();
  183. AzFramework::ApplicationRequests::Bus::Broadcast(
  184. &AzFramework::ApplicationRequests::Bus::Events::MakePathAssetRootRelative, azFilename);
  185. AZ::Data::AssetId assetId;
  186. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  187. assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, azFilename.c_str(), AZ::Data::s_invalidAssetType, false);
  188. for (const AZ::Uuid& type : pair.second)
  189. {
  190. const AZ::Data::AssetHandler* handler = AZ::Data::AssetManager::Instance().GetHandler(type);
  191. if (handler && handler->CanHandleAsset(assetId))
  192. {
  193. return type;
  194. }
  195. }
  196. }
  197. return AZ::Uuid::CreateNull();
  198. }
  199. QStandardItem* AssetCatalogModel::GetPath(QString& path, bool createIfNeeded, QStandardItem* parent)
  200. {
  201. if (!parent)
  202. {
  203. parent = invisibleRootItem();
  204. }
  205. QString cleanPath = path.replace("\\", "/");
  206. while (cleanPath.startsWith("/"))
  207. {
  208. cleanPath = cleanPath.mid(1);
  209. }
  210. while (cleanPath.endsWith("/"))
  211. {
  212. cleanPath.chop(1);
  213. }
  214. QString currentFolder;
  215. QString restOfPath;
  216. int slashIdx = cleanPath.indexOf('/', 1);
  217. if (slashIdx < 0)
  218. {
  219. currentFolder = cleanPath;
  220. restOfPath.clear();
  221. }
  222. else
  223. {
  224. currentFolder = cleanPath.left(slashIdx);
  225. restOfPath = cleanPath.mid(slashIdx + 1);
  226. }
  227. if (currentFolder.isEmpty())
  228. {
  229. return parent;
  230. }
  231. for (int i = 0; i < parent->rowCount(); i++)
  232. {
  233. QString name = parent->child(i)->data(Qt::DisplayRole).toString();
  234. bool isFolder = parent->child(i)->data(AssetCatalogEntry::FolderRole).toBool();
  235. if (currentFolder == name && isFolder)
  236. {
  237. if (restOfPath.isEmpty())
  238. {
  239. return parent->child(i);
  240. }
  241. else
  242. {
  243. return GetPath(restOfPath, createIfNeeded, parent->child(i));
  244. }
  245. }
  246. }
  247. if (createIfNeeded)
  248. {
  249. QString fullpath = parent->data(AssetCatalogEntry::FilePathRole).toString();
  250. fullpath += currentFolder + "/";
  251. AssetCatalogEntry* folder = new AssetCatalogEntry();
  252. folder->setData(currentFolder, Qt::DisplayRole);
  253. folder->setData(fullpath, AssetCatalogEntry::FilePathRole);
  254. folder->setData(true, AssetCatalogEntry::FolderRole);
  255. folder->setData(true, AssetCatalogEntry::VisibilityRole);
  256. parent->appendRow(folder);
  257. if (restOfPath.isEmpty())
  258. {
  259. return folder;
  260. }
  261. else
  262. {
  263. return GetPath(restOfPath, createIfNeeded, folder);
  264. }
  265. }
  266. else
  267. {
  268. return nullptr;
  269. }
  270. }
  271. AssetCatalogEntry* AssetCatalogModel::FindAsset(QString assetPath)
  272. {
  273. QString path;
  274. QString asset;
  275. // Separate file name and folder name.
  276. int slashIdx = assetPath.lastIndexOf('/');
  277. if (slashIdx < 0)
  278. {
  279. asset = assetPath;
  280. path.clear();
  281. }
  282. else
  283. {
  284. path = assetPath.left(slashIdx);
  285. asset = assetPath.mid(slashIdx + 1);
  286. }
  287. QStandardItem* folder = GetPath(path, false);
  288. if (folder)
  289. {
  290. for (int i = 0; i < folder->rowCount(); i++)
  291. {
  292. QString name = folder->child(i)->data(Qt::DisplayRole).toString();
  293. if (name == asset)
  294. {
  295. AssetCatalogEntry* entry = static_cast<AssetCatalogEntry*>(folder->child(i));
  296. return entry;
  297. }
  298. }
  299. }
  300. return nullptr;
  301. }
  302. AssetCatalogEntry* AssetCatalogModel::AddAsset(QString assetPath, AZ::Data::AssetId id)
  303. {
  304. QString path;
  305. QString asset;
  306. // Separate file name and folder name.
  307. int slashIdx = assetPath.lastIndexOf('/');
  308. if (slashIdx < 0)
  309. {
  310. asset = assetPath;
  311. path.clear();
  312. }
  313. else
  314. {
  315. path = assetPath.left(slashIdx);
  316. asset = assetPath.mid(slashIdx + 1);
  317. }
  318. QRegExp mipMapExtension("\\.dds\\.\\d+a?$"); // Files that end with ".dds.#", with an optional "a"
  319. if (asset.contains(mipMapExtension))
  320. {
  321. // Mip map files should be ignored by the file browser.
  322. // This is a temporary solution until texture streams are refactored.
  323. return nullptr;
  324. }
  325. QStandardItem* folder = GetPath(path, true);
  326. QString fullPath = folder->data(AssetCatalogEntry::FilePathRole).toString() + asset;
  327. AZ::Data::AssetType assetType = GetAssetType(fullPath);
  328. AZ::Uuid classId = AZ::Uuid::CreateNull();
  329. auto it = m_assetTypeToComponent.find(assetType);
  330. if (it != m_assetTypeToComponent.end())
  331. {
  332. classId = it->second;
  333. }
  334. AssetCatalogEntry* entry = new AssetCatalogEntry();
  335. entry->setData(asset, Qt::DisplayRole);
  336. entry->setData(fullPath, AssetCatalogEntry::FilePathRole);
  337. entry->setData(false, AssetCatalogEntry::FolderRole);
  338. entry->setData(true, AssetCatalogEntry::VisibilityRole);
  339. entry->m_assetId = id;
  340. entry->m_assetType = assetType;
  341. entry->m_classId = classId;
  342. if (!assetType.IsNull())
  343. {
  344. auto iconIt = m_assetTypeToIcon.find(assetType);
  345. if (iconIt == m_assetTypeToIcon.end())
  346. {
  347. // The m_assetTypeToIcon map was seeded with icons for known asset types.
  348. // If we come across an asset type that is not associated with a component,
  349. // we'll get its icon from OS if we can. This will help users recognize files more easily.
  350. QFileInfo fileInfo(m_rootPath + fullPath);
  351. QIcon fileIcon = m_iconProvider.icon(fileInfo);
  352. // Now, make a deep copy for OS-provided icons. On Windows 10, there seems to be an issue with
  353. // icons' memory being reclaimed and crashing the Editor.
  354. QSize size = fileIcon.actualSize(QSize(16, 16));
  355. QIcon deepCopy = fileIcon.pixmap(size).copy(0, 0, size.width(), size.height());
  356. if (!fileIcon.isNull())
  357. {
  358. m_assetTypeToIcon[assetType] = deepCopy;
  359. }
  360. }
  361. }
  362. folder->appendRow(entry);
  363. return entry;
  364. }
  365. AssetCatalogEntry* AssetCatalogModel::RemoveAsset(QString assetPath)
  366. {
  367. AssetCatalogEntry* entry = FindAsset(assetPath);
  368. if (entry)
  369. {
  370. QStandardItem* parent = entry->parent();
  371. if (parent)
  372. {
  373. parent->removeRow(entry->row());
  374. AssetCatalogEntry* folder = static_cast<AssetCatalogEntry*>(parent);
  375. return folder;
  376. }
  377. }
  378. return nullptr;
  379. }
  380. void AssetCatalogModel::LoadDatabase()
  381. {
  382. clear();
  383. AZStd::string assetRootFolder;
  384. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  385. {
  386. settingsRegistry->Get(assetRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
  387. }
  388. m_rootPath = assetRootFolder.c_str();
  389. auto startCB = []() {};
  390. auto enumerateCB = [this](const AZ::Data::AssetId id, const AZ::Data::AssetInfo& assetInfo)
  391. {
  392. DatabaseEntry* entry = new DatabaseEntry(id, assetInfo.m_relativePath.c_str());
  393. m_fileCache.push_back(entry);
  394. };
  395. auto endCB = [this]()
  396. {
  397. m_fileCacheCurrentIndex = 0;
  398. Q_EMIT UpdateProgress(0);
  399. Q_EMIT SetTotalProgress(static_cast<int>(m_fileCache.size()));
  400. };
  401. AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, startCB, enumerateCB, endCB);
  402. AzFramework::AssetCatalogEventBus::Handler::BusConnect();
  403. m_canProcessAssets = true;
  404. }
  405. void AssetCatalogModel::ProcessAssets()
  406. {
  407. if (m_fileCacheCurrentIndex >= m_fileCache.size())
  408. {
  409. sort(0);
  410. m_fileCache.clear();
  411. Q_EMIT LoadComplete();
  412. }
  413. else
  414. {
  415. for (int i = 0; m_canProcessAssets && i < ASSET_CATALOG_BATCH_SIZE && m_fileCacheCurrentIndex < m_fileCache.size(); i++, m_fileCacheCurrentIndex++)
  416. {
  417. AddAsset(m_fileCache[m_fileCacheCurrentIndex]->m_path, m_fileCache[m_fileCacheCurrentIndex]->m_id);
  418. }
  419. Q_EMIT UpdateProgress(m_fileCacheCurrentIndex);
  420. if (m_canProcessAssets)
  421. {
  422. QTimer::singleShot(1, this, &AssetCatalogModel::ProcessAssets);
  423. }
  424. }
  425. }
  426. void AssetCatalogModel::StartProcessingAssets()
  427. {
  428. m_canProcessAssets = true;
  429. }
  430. void AssetCatalogModel::StopProcessingAssets()
  431. {
  432. m_canProcessAssets = false;
  433. }
  434. void AssetCatalogModel::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
  435. {
  436. AZ::Data::AssetInfo assetInfo;
  437. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, assetId);
  438. // note that this will get called twice, once with the real assetId and once with legacy assetId.
  439. // we only want to add the real asset to the list, in which the assetId passed in is equal to the final assetId returned
  440. // otherwise, you look up assetId (and its a legacy assetId) and the actual asset will be different.
  441. if ((assetInfo.m_assetId.IsValid()) && (assetInfo.m_assetId == assetId))
  442. {
  443. AssetCatalogEntry* asset = AddAsset(assetInfo.m_relativePath.c_str(), assetInfo.m_assetId);
  444. if (asset)
  445. {
  446. Q_EMIT itemChanged(asset);
  447. }
  448. }
  449. }
  450. void AssetCatalogModel::OnCatalogAssetRemoved(const AZ::Data::AssetId& /*assetId*/, const AZ::Data::AssetInfo& assetInfo)
  451. {
  452. AssetCatalogEntry* asset = RemoveAsset(assetInfo.m_relativePath.c_str());
  453. if (asset)
  454. {
  455. Q_EMIT itemChanged(asset);
  456. }
  457. }
  458. QVariant AssetCatalogModel::data(const QModelIndex& index, int role) const
  459. {
  460. QStandardItem* item = itemFromIndex(index);
  461. if (item && role == Qt::DecorationRole)
  462. {
  463. AssetCatalogEntry* entry = static_cast<AssetCatalogEntry*>(item);
  464. auto it = m_assetTypeToIcon.find(entry->m_assetType);
  465. if (it != m_assetTypeToIcon.end())
  466. {
  467. return it->second;
  468. }
  469. bool isFolder = item->data(AssetCatalogEntry::FolderRole).toBool();
  470. return isFolder ? m_iconProvider.icon(QFileIconProvider::Folder) : m_iconProvider.icon(QFileIconProvider::File);
  471. }
  472. return QStandardItemModel::data(index, role);
  473. }
  474. QMimeData* AssetCatalogModel::mimeData(const QModelIndexList& indexes) const
  475. {
  476. AssetCatalogEntry* item = static_cast<AssetCatalogEntry*>(itemFromIndex(indexes[0]));
  477. bool isFolder = item ? item->data(AssetCatalogEntry::FolderRole).toBool() : true;
  478. if (isFolder)
  479. {
  480. return new QMimeData();
  481. }
  482. QString fullPath = item->data(AssetCatalogEntry::FilePathRole).toString();
  483. QMimeData* mimeData = new QMimeData;
  484. if (!item->m_assetType.IsNull() && item->m_assetId.IsValid())
  485. {
  486. // This mime data is used to drag into PropertyAssetCtrl fields.
  487. AzToolsFramework::EditorAssetMimeDataContainer mimeDataContainer;
  488. mimeDataContainer.AddEditorAsset(item->m_assetId, item->m_assetType);
  489. mimeDataContainer.AddToMimeData(mimeData);
  490. // This mime data is used for spawning of entities with components and the adding of components through assets.
  491. AzToolsFramework::ComponentAssetMimeDataContainer componentContainer;
  492. componentContainer.AddComponentAsset(item->m_classId, item->m_assetId);
  493. componentContainer.AddToMimeData(mimeData);
  494. }
  495. // Also, add the filename, for untyped fields.
  496. QList<QUrl> urls;
  497. urls << QUrl::fromLocalFile(fullPath);
  498. mimeData->setUrls(urls);
  499. return mimeData;
  500. }
  501. QVariant AssetCatalogModel::headerData(int section, Qt::Orientation orientation, int role) const
  502. {
  503. if (role == Qt::DisplayRole && section == 0 && orientation == Qt::Horizontal)
  504. {
  505. return tr("Assets");
  506. }
  507. return QAbstractItemModel::headerData(section, orientation, role);
  508. }
  509. void AssetCatalogModel::SearchCriteriaChanged(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator)
  510. {
  511. BuildFilter(criteriaList, filterOperator);
  512. InvalidateFilter();
  513. }
  514. void AssetCatalogModel::BuildFilter(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator)
  515. {
  516. ClearFilterRegExp();
  517. if (criteriaList.size() > 0)
  518. {
  519. QString filter, tag, text;
  520. for (int i = 0; i < criteriaList.size(); i++)
  521. {
  522. AzToolsFramework::SearchCriteriaButton::SplitTagAndText(criteriaList[i], tag, text);
  523. if (tag.isEmpty())
  524. {
  525. tag = "null";
  526. }
  527. filter = m_filtersRegExp[tag.toStdString().c_str()].pattern();
  528. if (filterOperator == AzToolsFramework::FilterOperatorType::Or)
  529. {
  530. if (filter.isEmpty())
  531. {
  532. filter = text;
  533. }
  534. else
  535. {
  536. filter += "|" + text;
  537. }
  538. }
  539. else if (filterOperator == AzToolsFramework::FilterOperatorType::And)
  540. {
  541. filter += "(?=.*" + text + ")"; // Using Lookaheads to produce an "and" effect.
  542. }
  543. SetFilterRegExp(tag.toStdString().c_str(), QRegExp(filter, Qt::CaseInsensitive));
  544. }
  545. }
  546. }
  547. void AssetCatalogModel::SetFilterRegExp(const AZStd::string& filterType, const QRegExp& regExp)
  548. {
  549. m_filtersRegExp[filterType] = regExp;
  550. }
  551. void AssetCatalogModel::ClearFilterRegExp(const AZStd::string& filterType)
  552. {
  553. if (filterType.empty())
  554. {
  555. for (auto& it : m_filtersRegExp)
  556. {
  557. it.second = QRegExp();
  558. }
  559. }
  560. else
  561. {
  562. m_filtersRegExp[filterType] = QRegExp();
  563. }
  564. }
  565. void AssetCatalogModel::InvalidateFilter()
  566. {
  567. ApplyFilter(invisibleRootItem());
  568. }
  569. void AssetCatalogModel::ApplyFilter(QStandardItem* parent)
  570. {
  571. // Set the visibility as a breadth-first search of the tree.
  572. // This will allow us to also set our parents visible if we are visible
  573. // without a later search overriding us.
  574. for (int i = 0; i < parent->rowCount(); i++)
  575. {
  576. QStandardItem* child = parent->child(i);
  577. if (m_filtersRegExp["name"].isEmpty())
  578. {
  579. child->setData(true, AssetCatalogEntry::VisibilityRole);
  580. }
  581. else
  582. {
  583. QString assetname = child->data(Qt::DisplayRole).toString();
  584. bool matchesFilter = assetname.contains(m_filtersRegExp["name"]);
  585. child->setData(matchesFilter, AssetCatalogEntry::VisibilityRole);
  586. if (matchesFilter)
  587. {
  588. // Set all parents to visible.
  589. QStandardItem* visiblityParent = parent;
  590. bool isVisible = visiblityParent->data(AssetCatalogEntry::VisibilityRole).toBool();
  591. while (!isVisible) // Checking isVisible gives us a short circuit for already visible folders.
  592. {
  593. visiblityParent->setData(true, AssetCatalogEntry::VisibilityRole);
  594. visiblityParent = visiblityParent->parent();
  595. isVisible = visiblityParent ? visiblityParent->data(AssetCatalogEntry::VisibilityRole).toBool() : true;
  596. }
  597. }
  598. }
  599. }
  600. // Recurse through the children that are folders
  601. for (int i = 0; i < parent->rowCount(); i++)
  602. {
  603. QStandardItem* child = parent->child(i);
  604. bool isFolder = child->data(AssetCatalogEntry::FolderRole).toBool();
  605. if (isFolder)
  606. {
  607. ApplyFilter(child);
  608. }
  609. }
  610. }
  611. QString AssetCatalogModel::FileName(const QModelIndex& index) const
  612. {
  613. QStandardItem* item = itemFromIndex(index);
  614. if (item)
  615. {
  616. return item->data(Qt::DisplayRole).toString();
  617. }
  618. return QString();
  619. }
  620. QString AssetCatalogModel::FilePath(const QModelIndex& index) const
  621. {
  622. // filePath contains the name of the file.
  623. QStandardItem* item = itemFromIndex(index);
  624. if (item)
  625. {
  626. QString fullPath = RootPath();
  627. fullPath += item->data(AssetCatalogEntry::FilePathRole).toString();
  628. return fullPath;
  629. }
  630. return QString();
  631. }
  632. AssetCatalogEntry* AssetCatalogModel::AssetData(const QModelIndex& index) const
  633. {
  634. return static_cast<AssetCatalogEntry*>(itemFromIndex(index));
  635. }
  636. ///////////////////////////////////////////////////////////////////////////////
  637. // End of context menu handling
  638. ///////////////////////////////////////////////////////////////////////////////
  639. #include <UI/moc_AssetCatalogModel.cpp>