AzAssetBrowserWindow.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  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 "AzAssetBrowserWindow.h"
  10. #include "AzAssetBrowserMultiWindow.h"
  11. #include <AssetBrowser/Views/AssetBrowserTreeView.h>
  12. // AzToolsFramework
  13. #include <AzCore/Console/IConsole.h>
  14. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  15. #include <AzToolsFramework/API/ViewPaneOptions.h>
  16. #include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
  17. #include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
  18. #include <AzToolsFramework/AssetBrowser/AssetBrowserListModel.h>
  19. #include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
  20. #include <AzToolsFramework/AssetBrowser/AssetBrowserThumbnailViewProxyModel.h>
  21. #include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntryUtils.h>
  22. #include <AzToolsFramework/AssetBrowser/AssetBrowserEntityInspectorWidget.h>
  23. // AzQtComponents
  24. #include <AzQtComponents/Utilities/QtWindowUtilities.h>
  25. #include <AzQtComponents/Components/Widgets/AssetFolderThumbnailView.h>
  26. #include <AzQtComponents/Components/Widgets/AssetFolderTableView.h>
  27. // Editor
  28. #include "AzAssetBrowser/AzAssetBrowserRequestHandler.h"
  29. #include "LyViewPaneNames.h"
  30. AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
  31. #include <AzAssetBrowser/ui_AzAssetBrowserWindow.h>
  32. AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
  33. AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserListView);
  34. AZ_CVAR(bool, ed_useWIPAssetBrowserDesign, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Use the in-progress new Asset Browser design");
  35. //! When the Asset Browser window is resized to be less than this many pixels in width
  36. //! the layout changes to accomodate its narrow state better. See AzAssetBrowserWindow::SetNarrowMode
  37. static constexpr int s_narrowModeThreshold = 700;
  38. static constexpr int MinimumWidth = 328;
  39. using namespace AzToolsFramework::AssetBrowser;
  40. namespace
  41. {
  42. inline QString FromStdString(AZStd::string_view string)
  43. {
  44. return QString::fromUtf8(string.data(), static_cast<int>(string.size()));
  45. }
  46. }
  47. namespace AzToolsFramework
  48. {
  49. namespace AssetBrowser
  50. {
  51. static constexpr const char* MenuIcon = ":/Menu/menu.svg";
  52. } // namespace AssetBrowser
  53. } // namespace AzToolsFramework
  54. class ListenerForShowAssetEditorEvent
  55. : public QObject
  56. , private AzToolsFramework::EditorEvents::Bus::Handler
  57. {
  58. public:
  59. ListenerForShowAssetEditorEvent(QObject* parent = nullptr)
  60. : QObject(parent)
  61. {
  62. AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
  63. }
  64. ~ListenerForShowAssetEditorEvent() override
  65. {
  66. AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
  67. }
  68. void SelectAsset(const QString& assetPath) override
  69. {
  70. AzToolsFramework::OpenViewPane(LyViewPane::AssetBrowser);
  71. AzAssetBrowserWindow* assetBrowser = AzToolsFramework::GetViewPaneWidget<AzAssetBrowserWindow>(LyViewPane::AssetBrowser);
  72. if (assetBrowser)
  73. {
  74. AzQtComponents::bringWindowToTop(assetBrowser);
  75. assetBrowser->SelectAsset(assetPath);
  76. }
  77. }
  78. };
  79. AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
  80. : QWidget(parent)
  81. , m_ui(new Ui::AzAssetBrowserWindowClass())
  82. , m_filterModel(new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel(parent))
  83. , m_listModel(new AzToolsFramework::AssetBrowser::AssetBrowserListModel(parent))
  84. {
  85. m_ui->setupUi(this);
  86. m_ui->m_searchWidget->Setup(true, true);
  87. CreateToolsMenu();
  88. namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
  89. AzAssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(
  90. m_assetBrowserModel, &AzAssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel);
  91. AZ_Assert(m_assetBrowserModel, "Failed to get filebrowser model");
  92. m_filterModel->setSourceModel(m_assetBrowserModel);
  93. m_filterModel->SetFilter(m_ui->m_searchWidget->GetFilter());
  94. // Turn off DynamicSort as sorting is now manual.
  95. m_filterModel->setDynamicSortFilter(false);
  96. m_ui->m_assetBrowserListViewWidget->setVisible(false);
  97. m_ui->m_toolsMenuButton->setVisible(false);
  98. m_ui->m_searchWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
  99. m_assetBrowserModel->SetFilterModel(m_filterModel.data());
  100. m_assetBrowserModel->EnableTickBus();
  101. this->setMinimumWidth(MinimumWidth);
  102. if (ed_useNewAssetBrowserListView)
  103. {
  104. m_ui->m_toolsMenuButton->setVisible(true);
  105. m_ui->m_toolsMenuButton->setEnabled(true);
  106. m_ui->m_toolsMenuButton->setAutoRaise(true);
  107. m_ui->m_toolsMenuButton->setIcon(QIcon(AzAssetBrowser::MenuIcon));
  108. m_listModel->setFilterRole(Qt::DisplayRole);
  109. m_listModel->setSourceModel(m_filterModel.data());
  110. m_listModel->setDynamicSortFilter(true);
  111. m_ui->m_assetBrowserListViewWidget->setModel(m_listModel.data());
  112. m_createMenu = new QMenu("Create New Asset Menu", this);
  113. m_ui->m_createButton->setMenu(m_createMenu);
  114. m_ui->m_createButton->setEnabled(true);
  115. m_ui->m_createButton->setAutoRaise(true);
  116. m_ui->m_createButton->setPopupMode(QToolButton::InstantPopup);
  117. connect(m_createMenu, &QMenu::aboutToShow, this, &AzAssetBrowserWindow::AddCreateMenu);
  118. connect(m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged,
  119. this, &AzAssetBrowserWindow::UpdateWidgetAfterFilter);
  120. connect(m_ui->m_assetBrowserListViewWidget->selectionModel(), &QItemSelectionModel::currentChanged,
  121. this, &AzAssetBrowserWindow::CurrentIndexChangedSlot);
  122. connect(
  123. m_ui->m_assetBrowserListViewWidget->selectionModel(),
  124. &QItemSelectionModel::selectionChanged,
  125. this,
  126. [this](const QItemSelection& selected, const QItemSelection& deselected)
  127. {
  128. Q_UNUSED(deselected);
  129. if (selected.indexes().size() > 0)
  130. {
  131. CurrentIndexChangedSlot(selected.indexes()[0]);
  132. }
  133. });
  134. connect(m_ui->m_assetBrowserListViewWidget, &QAbstractItemView::doubleClicked,
  135. this, &AzAssetBrowserWindow::DoubleClickedItem);
  136. connect(m_ui->m_assetBrowserListViewWidget, &AzAssetBrowser::AssetBrowserListView::ClearStringFilter,
  137. m_ui->m_searchWidget, &AzAssetBrowser::SearchWidget::ClearStringFilter);
  138. connect(m_ui->m_assetBrowserListViewWidget, &AzAssetBrowser::AssetBrowserListView::ClearTypeFilter,
  139. m_ui->m_searchWidget, &AzAssetBrowser::SearchWidget::ClearTypeFilter);
  140. m_ui->m_assetBrowserListViewWidget->SetIsAssetBrowserMainView();
  141. connect(
  142. m_ui->m_thumbnailView,
  143. &AssetBrowserThumbnailView::entryDoubleClicked,
  144. this,
  145. [this](const AssetBrowserEntry* entry)
  146. {
  147. OnDoubleClick(entry);
  148. });
  149. connect(
  150. m_ui->m_thumbnailView,
  151. &AssetBrowserThumbnailView::showInFolderTriggered,
  152. this,
  153. [this](const AssetBrowserEntry* entry)
  154. {
  155. if (entry && entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product)
  156. {
  157. entry = entry->GetParent();
  158. }
  159. if (!entry || !entry->GetParent())
  160. {
  161. return;
  162. }
  163. m_ui->m_searchWidget->ClearStringFilter();
  164. QModelIndex indexForEntry;
  165. m_assetBrowserModel->GetEntryIndex(const_cast<AssetBrowserEntry*>(entry->GetParent()), indexForEntry);
  166. if (!indexForEntry.isValid())
  167. {
  168. return;
  169. }
  170. auto targetIndex = m_filterModel.data()->mapFromSource(indexForEntry);
  171. m_ui->m_assetBrowserTreeViewWidget->SetShowIndexAfterUpdate(targetIndex);
  172. });
  173. }
  174. connect(m_ui->m_tableView, &AssetBrowserTableView::entryDoubleClicked,
  175. this,
  176. [this](const AssetBrowserEntry* entry)
  177. {
  178. OnDoubleClick(entry);
  179. });
  180. if (!ed_useWIPAssetBrowserDesign)
  181. {
  182. m_ui->m_breadcrumbsWrapper->hide();
  183. m_ui->m_middleStackWidget->hide();
  184. m_ui->m_treeViewButton->hide();
  185. m_ui->m_thumbnailViewButton->hide();
  186. m_ui->m_tableViewButton->hide();
  187. m_ui->m_createButton->hide();
  188. m_ui->m_searchWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  189. }
  190. m_ui->horizontalLayout->setAlignment(m_ui->m_toolsMenuButton, Qt::AlignTop);
  191. m_ui->horizontalLayout->setAlignment(m_ui->m_treeViewButton, Qt::AlignTop);
  192. m_ui->horizontalLayout->setAlignment(m_ui->m_tableViewButton, Qt::AlignTop);
  193. m_ui->horizontalLayout->setAlignment(m_ui->m_thumbnailViewButton, Qt::AlignTop);
  194. m_ui->horizontalLayout->setAlignment(m_ui->m_breadcrumbsWrapper, Qt::AlignTop);
  195. m_ui->horizontalLayout->setAlignment(m_ui->m_createButton, Qt::AlignTop);
  196. m_ui->m_breadcrumbsLayout->insertWidget(0, m_ui->m_pathBreadCrumbs->createSeparator());
  197. m_ui->m_breadcrumbsLayout->insertWidget(0, m_ui->m_pathBreadCrumbs->createBackForwardToolBar());
  198. connect(m_ui->m_pathBreadCrumbs, &AzQtComponents::BreadCrumbs::linkClicked, this, [this](const QString& path) {
  199. m_ui->m_assetBrowserTreeViewWidget->SelectFolder(path.toUtf8().constData());
  200. });
  201. connect(m_ui->m_pathBreadCrumbs, &AzQtComponents::BreadCrumbs::pathChanged, this, &AzAssetBrowserWindow::BreadcrumbsPathChangedSlot);
  202. connect(m_ui->m_pathBreadCrumbs, &AzQtComponents::BreadCrumbs::pathEdited, this, [this](const QString& path) {
  203. const auto* entry = m_ui->m_assetBrowserTreeViewWidget->GetEntryByPath(path);
  204. const auto* folderEntry = AzToolsFramework::AssetBrowser::Utils::FolderForEntry(entry);
  205. if (folderEntry)
  206. {
  207. // No need to select the folder ourselves, callback from Breadcrumbs will take care of that
  208. m_ui->m_pathBreadCrumbs->pushFullPath(FromStdString(folderEntry->GetFullPath()), FromStdString(folderEntry->GetVisiblePath()));
  209. }
  210. });
  211. connect(m_ui->m_thumbnailViewButton, &QAbstractButton::clicked, this, [this] { SetCurrentMode(AssetBrowserMode::ThumbnailView); });
  212. connect(m_ui->m_tableViewButton, &QAbstractButton::clicked, this, [this] { SetCurrentMode(AssetBrowserMode::TableView); });
  213. connect(m_ui->m_treeViewButton, &QAbstractButton::clicked, this, [this] { SetCurrentMode(AssetBrowserMode::ListView); });
  214. m_ui->m_assetBrowserTreeViewWidget->setModel(m_filterModel.data());
  215. m_ui->m_thumbnailView->SetAssetTreeView(m_ui->m_assetBrowserTreeViewWidget);
  216. m_ui->m_tableView->SetAssetTreeView(m_ui->m_assetBrowserTreeViewWidget);
  217. connect(m_ui->m_searchWidget->GetFilter().data(), &AzAssetBrowser::AssetBrowserEntryFilter::updatedSignal,
  218. m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterUpdatedSlot);
  219. connect(m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
  220. [this]()
  221. {
  222. const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
  223. const bool selectFirstFilteredIndex = false;
  224. m_ui->m_assetBrowserTreeViewWidget->UpdateAfterFilter(hasFilter, selectFirstFilteredIndex);
  225. });
  226. connect(m_ui->m_assetBrowserTreeViewWidget->selectionModel(), &QItemSelectionModel::currentChanged,
  227. this, &AzAssetBrowserWindow::CurrentIndexChangedSlot);
  228. connect(
  229. m_ui->m_assetBrowserTreeViewWidget->selectionModel(),
  230. &QItemSelectionModel::selectionChanged,
  231. this,
  232. [this](const QItemSelection& selected, const QItemSelection& deselected)
  233. {
  234. Q_UNUSED(deselected);
  235. if (selected.indexes().size() > 0)
  236. {
  237. CurrentIndexChangedSlot(selected.indexes()[0]);
  238. m_ui->m_createButton->setEnabled(true);
  239. }
  240. else
  241. {
  242. m_ui->m_createButton->setDisabled(true);
  243. }
  244. });
  245. connect(
  246. m_ui->m_assetBrowserTreeViewWidget,
  247. &QAbstractItemView::clicked,
  248. this,
  249. [this](const QModelIndex& idx)
  250. {
  251. using namespace AzToolsFramework::AssetBrowser;
  252. auto* entry = idx.data(AssetBrowserModel::Roles::EntryRole).value<const AssetBrowserEntry*>();
  253. if (entry->GetEntryType() != AssetBrowserEntry::AssetEntryType::Folder)
  254. {
  255. AssetBrowserPreviewRequestBus::Broadcast(&AssetBrowserPreviewRequest::PreviewAsset, entry);
  256. }
  257. m_ui->m_searchWidget->ClearStringFilter();
  258. });
  259. connect(m_ui->m_assetBrowserTreeViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
  260. connect(m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearStringFilter,
  261. m_ui->m_searchWidget, &AzAssetBrowser::SearchWidget::ClearStringFilter);
  262. connect(m_ui->m_assetBrowserTreeViewWidget, &AzAssetBrowser::AssetBrowserTreeView::ClearTypeFilter,
  263. m_ui->m_searchWidget, &AzAssetBrowser::SearchWidget::ClearTypeFilter);
  264. connect(
  265. m_assetBrowserModel,
  266. &AzAssetBrowser::AssetBrowserModel::RequestOpenItemForEditing,
  267. this,
  268. [this](const QModelIndex& index)
  269. {
  270. // If multiple AssetBrowsers are open, only the focused browser should perform the rename.
  271. QWidget* focusWidget = QApplication::focusWidget();
  272. if (!isAncestorOf(focusWidget))
  273. {
  274. return;
  275. }
  276. if (m_ui->m_thumbnailView->GetThumbnailActiveView())
  277. {
  278. m_ui->m_thumbnailView->OpenItemForEditing(index);
  279. }
  280. else if (m_ui->m_tableView->GetTableViewActive())
  281. {
  282. m_ui->m_tableView->OpenItemForEditing(index);
  283. }
  284. m_ui->m_assetBrowserTreeViewWidget->OpenItemForEditing(index);
  285. });
  286. connect(this, &AzAssetBrowserWindow::SizeChangedSignal,
  287. m_ui->m_assetBrowserListViewWidget, &AzAssetBrowser::AssetBrowserListView::UpdateSizeSlot);
  288. m_ui->m_assetBrowserTreeViewWidget->SetIsAssetBrowserMainView();
  289. m_ui->m_thumbnailView->SetIsAssetBrowserMainView();
  290. m_ui->m_tableView->SetIsAssetBrowserMainView();
  291. }
  292. AzAssetBrowserWindow::~AzAssetBrowserWindow()
  293. {
  294. m_assetBrowserModel->DisableTickBus();
  295. m_ui->m_assetBrowserTreeViewWidget->SaveState();
  296. }
  297. void AzAssetBrowserWindow::AddCreateMenu()
  298. {
  299. using namespace AzToolsFramework::AssetBrowser;
  300. m_createMenu->clear();
  301. const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible() ? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()
  302. : m_ui->m_assetBrowserListViewWidget->GetSelectedAssets();
  303. const AssetBrowserEntry* entry = selectedAssets.empty() ? nullptr : selectedAssets.front();
  304. if (!entry || selectedAssets.size() != 1)
  305. {
  306. return;
  307. }
  308. if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Product)
  309. {
  310. entry = entry->GetParent();
  311. if (!entry)
  312. {
  313. return;
  314. }
  315. }
  316. AZStd::string fullFilePath = entry->GetFullPath();
  317. AZStd::string folderPath;
  318. if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
  319. {
  320. folderPath = fullFilePath;
  321. }
  322. else
  323. {
  324. AzFramework::StringFunc::Path::GetFolderPath(fullFilePath.c_str(), folderPath);
  325. }
  326. AZ::Uuid sourceID = AZ::Uuid::CreateNull();
  327. SourceFileCreatorList creators;
  328. AssetBrowserInteractionNotificationBus::Broadcast(
  329. &AssetBrowserInteractionNotificationBus::Events::AddSourceFileCreators, folderPath.c_str(), sourceID, creators);
  330. if (!creators.empty())
  331. {
  332. for (const SourceFileCreatorDetails& creatorDetails : creators)
  333. {
  334. if (creatorDetails.m_creator)
  335. {
  336. m_createMenu->addAction(
  337. creatorDetails.m_iconToUse,
  338. tr("New ") + tr(creatorDetails.m_displayText.c_str()),
  339. [sourceID, fullFilePath, creatorDetails]()
  340. {
  341. creatorDetails.m_creator(fullFilePath.c_str(), sourceID);
  342. });
  343. }
  344. }
  345. }
  346. }
  347. void AzAssetBrowserWindow::RegisterViewClass()
  348. {
  349. AzToolsFramework::ViewPaneOptions options;
  350. options.preferedDockingArea = Qt::BottomDockWidgetArea;
  351. AzToolsFramework::RegisterViewPane<AzAssetBrowserWindow>(LyViewPane::AssetBrowser, LyViewPane::CategoryTools, options);
  352. options.showInMenu = false;
  353. const QString name = QString("%1 (2)").arg(LyViewPane::AssetBrowser);
  354. AzToolsFramework::RegisterViewPane<AzAssetBrowserWindow>(qPrintable(name), LyViewPane::CategoryTools, options);
  355. }
  356. QObject* AzAssetBrowserWindow::createListenerForShowAssetEditorEvent(QObject* parent)
  357. {
  358. auto* listener = new ListenerForShowAssetEditorEvent(parent);
  359. // the listener is attached to the parent and will get cleaned up then
  360. return listener;
  361. }
  362. bool AzAssetBrowserWindow::ViewWidgetBelongsTo(QWidget* viewWidget)
  363. {
  364. return m_ui->m_assetBrowserTreeViewWidget == viewWidget ||
  365. m_ui->m_assetBrowserListViewWidget == viewWidget ||
  366. m_ui->m_thumbnailView == viewWidget ||
  367. m_ui->m_tableView == viewWidget;
  368. }
  369. void AzAssetBrowserWindow::resizeEvent(QResizeEvent* resizeEvent)
  370. {
  371. // leftLayout is the parent of the listView
  372. // rightLayout is the parent of the preview window.
  373. // Workaround: When docking windows this event keeps holding the old size of the widgets instead of the new one
  374. // but the resizeEvent holds the new size of the whole widget
  375. // So we have to save the proportions somehow
  376. const QWidget* leftLayout = m_ui->m_leftLayout;
  377. const float oldLeftLayoutWidth = aznumeric_cast<float>(leftLayout->geometry().width());
  378. const float oldWidth = aznumeric_cast<float>(leftLayout->geometry().width());
  379. const float newWidth = oldLeftLayoutWidth * aznumeric_cast<float>(resizeEvent->size().width()) / oldWidth;
  380. const bool isNarrow = resizeEvent->size().width() < s_narrowModeThreshold;
  381. SetNarrowMode(isNarrow);
  382. emit SizeChangedSignal(aznumeric_cast<int>(newWidth));
  383. QWidget::resizeEvent(resizeEvent);
  384. }
  385. void AzAssetBrowserWindow::CreateToolsMenu()
  386. {
  387. if (m_toolsMenu != nullptr)
  388. {
  389. return;
  390. }
  391. m_toolsMenu = new QMenu("Asset Browser Mode Selection", this);
  392. m_ui->m_toolsMenuButton->setMenu(m_toolsMenu);
  393. m_ui->m_toolsMenuButton->setPopupMode(QToolButton::InstantPopup);
  394. if (ed_useWIPAssetBrowserDesign)
  395. {
  396. auto* openNewAction = new QAction(tr("Open Another Asset Browser"), this);
  397. connect(openNewAction, &QAction::triggered, this, [] { AzAssetBrowserMultiWindow::OpenNewAssetBrowserWindow(); });
  398. m_toolsMenu->addAction(openNewAction);
  399. m_toolsMenu->addSeparator();
  400. auto* collapseAllAction = new QAction(tr("Collapse All"), this);
  401. connect(collapseAllAction, &QAction::triggered, this, [this] { m_ui->m_assetBrowserTreeViewWidget->collapseAll(); });
  402. m_toolsMenu->addAction(collapseAllAction);
  403. m_toolsMenu->addSeparator();
  404. auto* projectSourceAssets = new QAction(tr("Hide Engine Folders"), this);
  405. projectSourceAssets->setCheckable(true);
  406. projectSourceAssets->setChecked(true);
  407. connect(projectSourceAssets, &QAction::triggered, this,
  408. [this, projectSourceAssets]
  409. {
  410. m_ui->m_searchWidget->ToggleEngineFilter(projectSourceAssets->isChecked());
  411. });
  412. m_toolsMenu->addAction(projectSourceAssets);
  413. m_ui->m_searchWidget->ToggleEngineFilter(projectSourceAssets->isChecked());
  414. auto* unusableProductAssets = new QAction(tr("Hide Unusable Product Assets"), this);
  415. unusableProductAssets->setCheckable(true);
  416. unusableProductAssets->setChecked(true);
  417. connect(
  418. unusableProductAssets,
  419. &QAction::triggered,
  420. this,
  421. [this, unusableProductAssets]
  422. {
  423. m_ui->m_searchWidget->ToggleUnusableProductsFilter(unusableProductAssets->isChecked());
  424. });
  425. m_toolsMenu->addAction(unusableProductAssets);
  426. m_ui->m_searchWidget->ToggleUnusableProductsFilter(unusableProductAssets->isChecked());
  427. m_ui->m_searchWidget->AddFolderFilter();
  428. m_assetBrowserDisplayState = AzToolsFramework::AssetBrowser::AssetBrowserDisplayState::TreeViewMode;
  429. m_ui->m_assetBrowserListViewWidget->setVisible(false);
  430. m_ui->m_assetBrowserTreeViewWidget->setVisible(true);
  431. m_ui->m_thumbnailView->SetThumbnailActiveView(true);
  432. m_ui->m_tableView->SetTableViewActive(false);
  433. }
  434. else
  435. {
  436. m_listViewMode = new QAction(tr("List View"), this);
  437. m_listViewMode->setCheckable(true);
  438. connect(m_listViewMode, &QAction::triggered, this, &AzAssetBrowserWindow::SetListViewMode);
  439. m_toolsMenu->addAction(m_listViewMode);
  440. m_treeViewMode = new QAction(tr("Tree View"), this);
  441. m_treeViewMode->setCheckable(true);
  442. connect(m_treeViewMode, &QAction::triggered, this, &AzAssetBrowserWindow::SetTreeViewMode);
  443. m_toolsMenu->addAction(m_treeViewMode);
  444. connect(m_toolsMenu, &QMenu::aboutToShow, this, &AzAssetBrowserWindow::UpdateDisplayInfo);
  445. UpdateDisplayInfo();
  446. }
  447. }
  448. void AzAssetBrowserWindow::UpdateDisplayInfo()
  449. {
  450. namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
  451. if (m_toolsMenu == nullptr)
  452. {
  453. return;
  454. }
  455. m_treeViewMode->setChecked(false);
  456. m_listViewMode->setChecked(false);
  457. switch (m_assetBrowserDisplayState)
  458. {
  459. case AzAssetBrowser::AssetBrowserDisplayState::TreeViewMode:
  460. {
  461. m_treeViewMode->setChecked(true);
  462. break;
  463. }
  464. case AzAssetBrowser::AssetBrowserDisplayState::ListViewMode:
  465. {
  466. m_listViewMode->setChecked(true);
  467. break;
  468. }
  469. }
  470. }
  471. void AzAssetBrowserWindow::SetNarrowMode(bool narrow)
  472. {
  473. if (m_inNarrowMode == narrow)
  474. {
  475. return;
  476. }
  477. // In narrow mode, breadcrumbs are below the search bar and view switching buttons
  478. m_inNarrowMode = narrow;
  479. if (narrow)
  480. {
  481. m_ui->scrollAreaVerticalLayout->insertWidget(1, m_ui->m_breadcrumbsWrapper);
  482. m_ui->m_searchWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  483. m_ui->m_breadcrumbsWrapper->setContentsMargins(0, 0, 0, 5);
  484. }
  485. else
  486. {
  487. m_ui->horizontalLayout->insertWidget(4, m_ui->m_breadcrumbsWrapper);
  488. m_ui->m_breadcrumbsWrapper->setContentsMargins(0, 0, 0, 0);
  489. m_ui->horizontalLayout->setAlignment(m_ui->m_breadcrumbsWrapper, Qt::AlignTop);
  490. // Once we fully move to new design this cvar will be gone and the condition can be deleted
  491. if (ed_useWIPAssetBrowserDesign)
  492. {
  493. m_ui->m_searchWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
  494. }
  495. }
  496. }
  497. void AzAssetBrowserWindow::SetTreeViewMode()
  498. {
  499. namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
  500. m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::TreeViewMode;
  501. if (m_ui->m_assetBrowserListViewWidget->isVisible())
  502. {
  503. m_ui->m_assetBrowserListViewWidget->setVisible(false);
  504. m_ui->m_assetBrowserTreeViewWidget->setVisible(true);
  505. }
  506. }
  507. void AzAssetBrowserWindow::SetListViewMode()
  508. {
  509. namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
  510. m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::ListViewMode;
  511. UpdateWidgetAfterFilter();
  512. }
  513. void AzAssetBrowserWindow::UpdateWidgetAfterFilter()
  514. {
  515. namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
  516. const bool hasFilter = !m_ui->m_searchWidget->GetFilterString().isEmpty();
  517. if (m_assetBrowserDisplayState == AzAssetBrowser::AssetBrowserDisplayState::ListViewMode)
  518. {
  519. m_ui->m_assetBrowserListViewWidget->setVisible(hasFilter);
  520. m_ui->m_assetBrowserTreeViewWidget->setVisible(!hasFilter);
  521. }
  522. if (hasFilter)
  523. {
  524. m_ui->m_assetBrowserTreeViewWidget->selectionModel()->select(
  525. m_ui->m_assetBrowserTreeViewWidget->model()->index(0, 0, {}), QItemSelectionModel::ClearAndSelect);
  526. }
  527. if (ed_useNewAssetBrowserListView)
  528. {
  529. auto thumbnailWidget = m_ui->m_thumbnailView->GetThumbnailViewWidget();
  530. auto tableWidget = m_ui->m_tableView->GetTableViewWidget();
  531. if (hasFilter)
  532. {
  533. if (thumbnailWidget)
  534. {
  535. thumbnailWidget->setRootIndex(thumbnailWidget->model()->index(0, 0, {}));
  536. m_ui->m_thumbnailView->SetSearchString(m_ui->m_searchWidget->GetFilterString());
  537. }
  538. if (tableWidget)
  539. {
  540. tableWidget->setRootIndex(tableWidget->model()->index(0, 0, {}));
  541. m_ui->m_tableView->SetSearchString(m_ui->m_searchWidget->GetFilterString());
  542. }
  543. m_ui->m_assetBrowserTreeViewWidget->SetSearchString(m_ui->m_searchWidget->GetFilterString());
  544. }
  545. else
  546. {
  547. if (thumbnailWidget)
  548. {
  549. m_ui->m_thumbnailView->SetSearchString("");
  550. }
  551. if (tableWidget)
  552. {
  553. m_ui->m_tableView->SetSearchString("");
  554. }
  555. m_ui->m_assetBrowserTreeViewWidget->SetSearchString("");
  556. }
  557. }
  558. }
  559. void AzAssetBrowserWindow::UpdateBreadcrumbs(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* selectedEntry) const
  560. {
  561. using namespace AzToolsFramework::AssetBrowser;
  562. QString entryPath;
  563. QString fullPath;
  564. if (selectedEntry)
  565. {
  566. const AssetBrowserEntry* folderEntry = Utils::FolderForEntry(selectedEntry);
  567. if (folderEntry)
  568. {
  569. entryPath = FromStdString(folderEntry->GetVisiblePath());
  570. fullPath = FromStdString(folderEntry->GetFullPath());
  571. }
  572. }
  573. m_ui->m_pathBreadCrumbs->pushFullPath(fullPath, entryPath);
  574. }
  575. void AzAssetBrowserWindow::SetTwoColumnMode(QWidget* viewToShow)
  576. {
  577. auto* thumbnailView = qobject_cast<AssetBrowserThumbnailView*>(viewToShow);
  578. if (thumbnailView && m_ui->m_thumbnailView->GetThumbnailActiveView())
  579. {
  580. return;
  581. }
  582. auto* tableView = qobject_cast<AssetBrowserTableView*>(viewToShow);
  583. if (tableView && m_ui->m_tableView->GetTableViewActive())
  584. {
  585. return;
  586. }
  587. m_ui->m_middleStackWidget->show();
  588. m_ui->m_middleStackWidget->setCurrentWidget(viewToShow);
  589. m_ui->m_assetBrowserTreeViewWidget->SetApplySnapshot(false);
  590. m_ui->m_searchWidget->AddFolderFilter();
  591. if (thumbnailView)
  592. {
  593. m_ui->m_thumbnailView->SetThumbnailActiveView(true);
  594. m_ui->m_tableView->SetTableViewActive(false);
  595. }
  596. else if (tableView)
  597. {
  598. m_ui->m_thumbnailView->SetThumbnailActiveView(false);
  599. m_ui->m_tableView->SetTableViewActive(true);
  600. }
  601. }
  602. void AzAssetBrowserWindow::SetOneColumnMode()
  603. {
  604. if (m_ui->m_thumbnailView->GetThumbnailActiveView() || m_ui->m_tableView->GetTableViewActive())
  605. {
  606. m_ui->m_middleStackWidget->hide();
  607. m_ui->m_assetBrowserTreeViewWidget->SetApplySnapshot(false);
  608. m_ui->m_searchWidget->RemoveFolderFilter();
  609. if (!m_ui->m_assetBrowserTreeViewWidget->selectionModel()->selectedRows().isEmpty())
  610. {
  611. m_ui->m_assetBrowserTreeViewWidget->expand(m_ui->m_assetBrowserTreeViewWidget->selectionModel()->selectedRows()[0]);
  612. }
  613. m_ui->m_thumbnailView->SetThumbnailActiveView(false);
  614. m_ui->m_tableView->SetTableViewActive(false);
  615. }
  616. }
  617. void AzAssetBrowserWindow::OnDoubleClick(const AssetBrowserEntry* entry)
  618. {
  619. if (!m_ui->m_assetBrowserTreeViewWidget || !entry || !m_assetBrowserModel || !m_filterModel.data())
  620. {
  621. return;
  622. }
  623. QModelIndex indexForEntry;
  624. m_assetBrowserModel->GetEntryIndex(const_cast<AssetBrowserEntry*>(entry), indexForEntry);
  625. if (!indexForEntry.isValid())
  626. {
  627. return;
  628. }
  629. auto entryType = entry->GetEntryType();
  630. if (entryType == AssetBrowserEntry::AssetEntryType::Folder)
  631. {
  632. m_ui->m_searchWidget->ClearStringFilter();
  633. auto selectionModel = m_ui->m_assetBrowserTreeViewWidget->selectionModel();
  634. auto targetIndex = m_filterModel.data()->mapFromSource(indexForEntry);
  635. selectionModel->select(targetIndex, QItemSelectionModel::ClearAndSelect);
  636. auto targetIndexAncestor = targetIndex.parent();
  637. while (targetIndexAncestor.isValid())
  638. {
  639. m_ui->m_assetBrowserTreeViewWidget->expand(targetIndexAncestor);
  640. targetIndexAncestor = targetIndexAncestor.parent();
  641. }
  642. if (m_ui->m_thumbnailView->GetThumbnailActiveView())
  643. {
  644. m_ui->m_thumbnailView->GetThumbnailViewWidget()->selectionModel()->clearSelection();
  645. }
  646. else if (m_ui->m_tableView->GetTableViewActive())
  647. {
  648. m_ui->m_tableView->GetTableViewWidget()->selectionModel()->clearSelection();
  649. }
  650. m_ui->m_assetBrowserTreeViewWidget->scrollTo(targetIndex, QAbstractItemView::ScrollHint::PositionAtCenter);
  651. }
  652. else if (entryType == AssetBrowserEntry::AssetEntryType::Product || entryType == AssetBrowserEntry::AssetEntryType::Source)
  653. {
  654. AZ::Data::AssetId assetIdToOpen;
  655. AZStd::string fullFilePath;
  656. if (const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry =
  657. azrtti_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry))
  658. {
  659. assetIdToOpen = productEntry->GetAssetId();
  660. fullFilePath = entry->GetFullPath();
  661. }
  662. else if (const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* sourceEntry =
  663. azrtti_cast<const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry*>(entry))
  664. {
  665. // manufacture an empty AssetID with the source's UUID
  666. assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0);
  667. fullFilePath = entry->GetFullPath();
  668. }
  669. bool handledBySomeone = false;
  670. if (assetIdToOpen.IsValid())
  671. {
  672. AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast(
  673. &AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor,
  674. assetIdToOpen,
  675. handledBySomeone);
  676. }
  677. if (!handledBySomeone && !fullFilePath.empty())
  678. {
  679. AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath);
  680. }
  681. }
  682. }
  683. static void ExpandTreeToIndex(QTreeView* treeView, const QModelIndex& index)
  684. {
  685. treeView->collapseAll();
  686. // Note that we deliberately don't expand the index passed in
  687. // collapseAll above will close all but the top level nodes.
  688. // treeView->expand(index) marks a node as expanded, but if it's parent isn't expanded,
  689. // there won't be any paint updates because it doesn't expand parent nodes.
  690. // So, to minimize paint updates, we expand everything in reverse order (leaf up to root), so that
  691. // painting will only actually occur once the top level parent is expanded.
  692. QModelIndex parentIndex = index.parent();
  693. while (parentIndex.isValid())
  694. {
  695. treeView->expand(parentIndex);
  696. parentIndex = parentIndex.parent();
  697. }
  698. }
  699. void AzAssetBrowserWindow::SelectAsset(const QString& assetPath)
  700. {
  701. if (ed_useWIPAssetBrowserDesign)
  702. {
  703. QTimer::singleShot(
  704. 0,
  705. this,
  706. [this, assetPath]
  707. {
  708. m_ui->m_searchWidget->ClearTextFilter();
  709. m_ui->m_searchWidget->ClearTypeFilter();
  710. m_ui->m_assetBrowserTreeViewWidget->SelectFileAtPathAfterUpdate(assetPath.toUtf8().data());
  711. });
  712. }
  713. else
  714. {
  715. QModelIndex index = m_assetBrowserModel->findIndex(assetPath);
  716. if (index.isValid())
  717. {
  718. m_ui->m_searchWidget->ClearTextFilter();
  719. m_ui->m_searchWidget->ClearTypeFilter();
  720. // Queue the expand and select stuff, so that it doesn't get processed the same
  721. // update as the search widget clearing - something with the search widget clearing
  722. // interferes with the update from the select and expand, and if you don't
  723. // queue it, the tree doesn't expand reliably.
  724. QTimer::singleShot(
  725. 0,
  726. this,
  727. [this, filteredIndex = index]
  728. {
  729. // the treeview has a filter model so we have to backwards go from that
  730. QModelIndex modelIndex = m_filterModel->mapFromSource(filteredIndex);
  731. QTreeView* treeView = m_ui->m_assetBrowserTreeViewWidget;
  732. ExpandTreeToIndex(treeView, modelIndex);
  733. treeView->scrollTo(modelIndex);
  734. treeView->setCurrentIndex(modelIndex);
  735. treeView->selectionModel()->select(modelIndex, QItemSelectionModel::ClearAndSelect);
  736. });
  737. }
  738. }
  739. }
  740. void AzAssetBrowserWindow::CurrentIndexChangedSlot(const QModelIndex& idx) const
  741. {
  742. using namespace AzToolsFramework::AssetBrowser;
  743. auto* entry = idx.data(AssetBrowserModel::Roles::EntryRole).value<const AssetBrowserEntry*>();
  744. UpdateBreadcrumbs(entry);
  745. }
  746. // while its tempting to use Activated here, we don't actually want it to count as activation
  747. // just because on some OS clicking once is activation.
  748. void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element)
  749. {
  750. namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
  751. const auto& selectedAssets = m_ui->m_assetBrowserTreeViewWidget->isVisible() ? m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets()
  752. : m_ui->m_assetBrowserListViewWidget->GetSelectedAssets();
  753. for (const AzAssetBrowser::AssetBrowserEntry* entry : selectedAssets)
  754. {
  755. AZ::Data::AssetId assetIdToOpen;
  756. AZStd::string fullFilePath;
  757. if (const AzAssetBrowser::ProductAssetBrowserEntry* productEntry =
  758. azrtti_cast<const AzAssetBrowser::ProductAssetBrowserEntry*>(entry))
  759. {
  760. assetIdToOpen = productEntry->GetAssetId();
  761. fullFilePath = entry->GetFullPath();
  762. }
  763. else if (
  764. const AzAssetBrowser::SourceAssetBrowserEntry* sourceEntry = azrtti_cast<const AzAssetBrowser::SourceAssetBrowserEntry*>(entry))
  765. {
  766. // manufacture an empty AssetID with the source's UUID
  767. assetIdToOpen = AZ::Data::AssetId(sourceEntry->GetSourceUuid(), 0);
  768. fullFilePath = entry->GetFullPath();
  769. }
  770. bool handledBySomeone = false;
  771. if (assetIdToOpen.IsValid())
  772. {
  773. AzAssetBrowser::AssetBrowserInteractionNotificationBus::Broadcast(
  774. &AzAssetBrowser::AssetBrowserInteractionNotifications::OpenAssetInAssociatedEditor, assetIdToOpen, handledBySomeone);
  775. }
  776. if (!handledBySomeone && !fullFilePath.empty())
  777. {
  778. AzAssetBrowserRequestHandler::OpenWithOS(fullFilePath);
  779. }
  780. }
  781. }
  782. //! This slot ignores path change coming from breadcrumbs if we already have a file selected directly in this folder. This may happen
  783. //! in the one column mode where tree view shows files too.
  784. void AzAssetBrowserWindow::BreadcrumbsPathChangedSlot(const QString& path) const
  785. {
  786. using namespace AzToolsFramework::AssetBrowser;
  787. const auto* currentEntry =
  788. m_ui->m_assetBrowserTreeViewWidget->currentIndex().data(AssetBrowserModel::Roles::EntryRole).value<const AssetBrowserEntry*>();
  789. const AssetBrowserEntry* folderForCurrent = Utils::FolderForEntry(currentEntry);
  790. const QString currentFolderPath =
  791. folderForCurrent ? QString::fromUtf8(folderForCurrent->GetVisiblePath().c_str()).replace('\\', '/') : QString();
  792. if (path != currentFolderPath)
  793. {
  794. m_ui->m_assetBrowserTreeViewWidget->SelectFolder(path.toUtf8().constData());
  795. }
  796. }
  797. AssetBrowserMode AzAssetBrowserWindow::GetCurrentMode() const
  798. {
  799. return m_currentMode;
  800. }
  801. void AzAssetBrowserWindow::SetCurrentMode(const AssetBrowserMode mode)
  802. {
  803. if (ed_useWIPAssetBrowserDesign)
  804. {
  805. switch (mode)
  806. {
  807. case AssetBrowserMode::TableView:
  808. SetTwoColumnMode(m_ui->m_tableView);
  809. break;
  810. case AssetBrowserMode::ListView:
  811. SetOneColumnMode();
  812. break;
  813. default:
  814. SetTwoColumnMode(m_ui->m_thumbnailView);
  815. break;
  816. }
  817. }
  818. m_currentMode = mode;
  819. }
  820. #include <AzAssetBrowser/moc_AzAssetBrowserWindow.cpp>