3
0

AtomToolsAssetBrowserInteractions.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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 <Atom/RPI.Edit/Common/AssetUtils.h>
  9. #include <AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h>
  10. #include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
  11. #include <AtomToolsFramework/Util/Util.h>
  12. #include <AzCore/Utils/Utils.h>
  13. #include <AzCore/std/string/wildcard.h>
  14. #include <AzQtComponents/Utilities/DesktopUtilities.h>
  15. #include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
  16. #include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
  17. #include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
  18. #include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
  19. #include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
  20. #include <QApplication>
  21. #include <QClipboard>
  22. #include <QDesktopServices>
  23. #include <QDir>
  24. #include <QFileDialog>
  25. #include <QFileInfo>
  26. #include <QInputDialog>
  27. #include <QMenu>
  28. #include <QMessageBox>
  29. namespace AtomToolsFramework
  30. {
  31. AtomToolsAssetBrowserInteractions::AtomToolsAssetBrowserInteractions()
  32. {
  33. AssetBrowserInteractionNotificationBus::Handler::BusConnect();
  34. }
  35. AtomToolsAssetBrowserInteractions::~AtomToolsAssetBrowserInteractions()
  36. {
  37. AssetBrowserInteractionNotificationBus::Handler::BusDisconnect();
  38. }
  39. void AtomToolsAssetBrowserInteractions::RegisterContextMenuActions(
  40. const FilterCallback& filterCallback, const ActionCallback& actionCallback)
  41. {
  42. m_contextMenuCallbacks.emplace_back(filterCallback, actionCallback);
  43. }
  44. void AtomToolsAssetBrowserInteractions::AddContextMenuActions(
  45. QWidget* caller, QMenu* menu, const AssetBrowserEntryVector& entries)
  46. {
  47. const AssetBrowserEntry* entry = entries.empty() ? nullptr : entries.front();
  48. if (!entry)
  49. {
  50. return;
  51. }
  52. m_caller = caller;
  53. QObject::connect(m_caller, &QObject::destroyed, [this]() { m_caller = nullptr; });
  54. // Add all of the custom context menu entries first
  55. for (const auto& contextMenuCallbackPair : m_contextMenuCallbacks)
  56. {
  57. if (contextMenuCallbackPair.first(entries))
  58. {
  59. contextMenuCallbackPair.second(caller, menu, entries);
  60. }
  61. }
  62. if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
  63. {
  64. AddContextMenuActionsForSourceEntries(caller, menu, entry);
  65. }
  66. else if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
  67. {
  68. AddContextMenuActionsForFolderEntries(caller, menu, entry);
  69. }
  70. AddContextMenuActionsForAllEntries(caller, menu, entry);
  71. AddContextMenuActionsForSourceControl(caller, menu, entry);
  72. }
  73. void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForSourceEntries(
  74. [[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
  75. {
  76. menu->addAction("Duplicate", [entry]()
  77. {
  78. const auto& duplicateFilePath = GetUniqueFilePath(entry->GetFullPath());
  79. if (QFile::copy(entry->GetFullPath().c_str(), duplicateFilePath.c_str()))
  80. {
  81. QFile::setPermissions(duplicateFilePath.c_str(), QFile::ReadOther | QFile::WriteOther);
  82. // Auto add file to source control
  83. AzToolsFramework::SourceControlCommandBus::Broadcast(&AzToolsFramework::SourceControlCommandBus::Events::RequestEdit,
  84. duplicateFilePath.c_str(), true, [](bool, const AzToolsFramework::SourceControlFileInfo&) {});
  85. }
  86. });
  87. QMenu* scriptsMenu = menu->addMenu(QObject::tr("Python Scripts"));
  88. const AZStd::vector<AZStd::string> arguments{ entry->GetFullPath() };
  89. AddRegisteredScriptToMenu(scriptsMenu, "/O3DE/AtomToolsFramework/AssetBrowser/ContextMenuScripts", arguments);
  90. }
  91. void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForFolderEntries(
  92. QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
  93. {
  94. menu->addAction(QObject::tr("Create new sub folder..."), [caller, entry]()
  95. {
  96. bool ok = false;
  97. QString newFolderName = QInputDialog::getText(caller, "Enter new folder name", "name:", QLineEdit::Normal, "NewFolder", &ok);
  98. if (ok)
  99. {
  100. if (newFolderName.isEmpty())
  101. {
  102. QMessageBox msgBox(QMessageBox::Icon::Critical, "Error", "Folder name can't be empty", QMessageBox::Ok, caller);
  103. msgBox.exec();
  104. }
  105. else
  106. {
  107. AZStd::string newFolderPath;
  108. AzFramework::StringFunc::Path::Join(entry->GetFullPath().c_str(), newFolderName.toUtf8().constData(), newFolderPath);
  109. QDir dir(newFolderPath.c_str());
  110. if (dir.exists())
  111. {
  112. QMessageBox::critical(caller, "Error", "Folder with this name already exists");
  113. return;
  114. }
  115. auto result = dir.mkdir(newFolderPath.c_str());
  116. if (!result)
  117. {
  118. AZ_Error("MaterialBrowser", false, "Failed to make new folder");
  119. return;
  120. }
  121. }
  122. }
  123. });
  124. }
  125. void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForAllEntries(
  126. [[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
  127. {
  128. menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
  129. {
  130. AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
  131. });
  132. menu->addSeparator();
  133. menu->addAction(QObject::tr("Copy Name To Clipboard"), [=]()
  134. {
  135. QApplication::clipboard()->setText(entry->GetName().c_str());
  136. });
  137. menu->addAction(QObject::tr("Copy Path To Clipboard"), [=]()
  138. {
  139. QApplication::clipboard()->setText(entry->GetFullPath().c_str());
  140. });
  141. }
  142. void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForSourceControl(
  143. [[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
  144. {
  145. using namespace AzToolsFramework;
  146. bool isActive = false;
  147. SourceControlConnectionRequestBus::BroadcastResult(isActive, &SourceControlConnectionRequests::IsActive);
  148. AZStd::string path = entry->GetFullPath();
  149. if (isActive && ValidateDocumentPath(path))
  150. {
  151. menu->addSeparator();
  152. QMenu* sourceControlMenu = menu->addMenu("Source Control");
  153. // Update the enabled state of source control menu actions only if menu is shown
  154. QMenu::connect(sourceControlMenu, &QMenu::aboutToShow, [this, path]()
  155. {
  156. SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::GetFileInfo, path.c_str(),
  157. [this](bool success, const SourceControlFileInfo& info) { UpdateContextMenuActionsForSourceControl(success, info); });
  158. });
  159. // add get latest action
  160. m_getLatestAction = sourceControlMenu->addAction("Get Latest", [path]()
  161. {
  162. SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestLatest, path.c_str(),
  163. [](bool, const SourceControlFileInfo&) {});
  164. });
  165. QObject::connect(m_getLatestAction, &QObject::destroyed, [this]()
  166. {
  167. m_getLatestAction = nullptr;
  168. });
  169. m_getLatestAction->setEnabled(false);
  170. // add add action
  171. m_addAction = sourceControlMenu->addAction("Add", [path]()
  172. {
  173. SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestEdit, path.c_str(), true,
  174. [path](bool, const SourceControlFileInfo&)
  175. {
  176. SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
  177. });
  178. });
  179. QObject::connect(m_addAction, &QObject::destroyed, [this]()
  180. {
  181. m_addAction = nullptr;
  182. });
  183. m_addAction->setEnabled(false);
  184. // add checkout action
  185. m_checkOutAction = sourceControlMenu->addAction("Check Out", [path]()
  186. {
  187. SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestEdit, path.c_str(), true,
  188. [path](bool, const SourceControlFileInfo&)
  189. {
  190. SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
  191. });
  192. });
  193. QObject::connect(m_checkOutAction, &QObject::destroyed, [this]()
  194. {
  195. m_checkOutAction = nullptr;
  196. });
  197. m_checkOutAction->setEnabled(false);
  198. // add undo checkout action
  199. m_undoCheckOutAction = sourceControlMenu->addAction("Undo Check Out", [path]()
  200. {
  201. SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestRevert, path.c_str(),
  202. [path](bool, const SourceControlFileInfo&)
  203. {
  204. SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
  205. });
  206. });
  207. QObject::connect(m_undoCheckOutAction, &QObject::destroyed, [this]()
  208. {
  209. m_undoCheckOutAction = nullptr;
  210. });
  211. m_undoCheckOutAction->setEnabled(false);
  212. }
  213. }
  214. void AtomToolsAssetBrowserInteractions::UpdateContextMenuActionsForSourceControl(bool success, AzToolsFramework::SourceControlFileInfo info)
  215. {
  216. if (!success && m_caller)
  217. {
  218. QMessageBox::critical(m_caller, "Error", "Source control operation failed.");
  219. }
  220. if (m_getLatestAction)
  221. {
  222. m_getLatestAction->setEnabled(info.IsManaged() && info.HasFlag(AzToolsFramework::SCF_OutOfDate));
  223. }
  224. if (m_addAction)
  225. {
  226. m_addAction->setEnabled(!info.IsManaged());
  227. }
  228. if (m_checkOutAction)
  229. {
  230. m_checkOutAction->setEnabled(info.IsManaged() && info.IsReadOnly() && !info.IsLockedByOther());
  231. }
  232. if (m_undoCheckOutAction)
  233. {
  234. m_undoCheckOutAction->setEnabled(info.IsManaged() && !info.IsReadOnly());
  235. }
  236. }
  237. } // namespace AtomToolsFramework