BsGUIResourceTreeView.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. #include "BsGUIResourceTreeView.h"
  2. #include "BsGUISkin.h"
  3. #include "BsProjectLibrary.h"
  4. #include "BsDragAndDropManager.h"
  5. #include "BsResources.h"
  6. #include "BsResourceManifest.h"
  7. #include "BsProjectLibrary.h"
  8. #include "BsFileSystem.h"
  9. #include "BsCGUIWidget.h"
  10. #include "BsViewport.h"
  11. #include "BsRenderWindow.h"
  12. #include "BsPlatform.h"
  13. #include "BsSelection.h"
  14. using namespace std::placeholders;
  15. namespace BansheeEngine
  16. {
  17. const MessageId GUIResourceTreeView::SELECTION_CHANGED_MSG = MessageId("ResourceTreeView_SelectionChanged");
  18. GUIResourceTreeView::InternalDraggedResources::InternalDraggedResources(UINT32 numObjects)
  19. :numObjects(numObjects)
  20. {
  21. resourcePaths = bs_newN<Path>(numObjects);
  22. }
  23. GUIResourceTreeView::InternalDraggedResources::~InternalDraggedResources()
  24. {
  25. bs_deleteN(resourcePaths, numObjects);
  26. resourcePaths = nullptr;
  27. }
  28. GUIResourceTreeView::GUIResourceTreeView(const String& backgroundStyle, const String& elementBtnStyle, const String& foldoutBtnStyle,
  29. const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, const String& editBoxStyle,
  30. const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions)
  31. :GUITreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle, highlightBackgroundStyle,
  32. selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, dimensions),
  33. mDraggedResources(nullptr), mCurrentWindow(nullptr), mDropTarget(nullptr), mDropTargetDragActive(false)
  34. {
  35. ResourceTreeViewLocator::_provide(this);
  36. gProjectLibrary().onEntryAdded.connect(std::bind(&GUIResourceTreeView::entryAdded, this, _1));
  37. gProjectLibrary().onEntryRemoved.connect(std::bind(&GUIResourceTreeView::entryRemoved, this, _1));
  38. const ProjectLibrary::LibraryEntry* rootEntry = gProjectLibrary().getRootEntry();
  39. mRootElement.mFullPath = rootEntry->path;
  40. mRootElement.mElementName = mRootElement.mFullPath.getWTail();
  41. expandElement(&mRootElement);
  42. updateFromProjectLibraryEntry(&mRootElement, rootEntry);
  43. }
  44. GUIResourceTreeView::~GUIResourceTreeView()
  45. {
  46. clearDropTarget();
  47. ResourceTreeViewLocator::_provide(nullptr);
  48. }
  49. GUIResourceTreeView* GUIResourceTreeView::create(const String& backgroundStyle, const String& elementBtnStyle,
  50. const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle,
  51. const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle)
  52. {
  53. return new (bs_alloc<GUIResourceTreeView>()) GUIResourceTreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle,
  54. highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create());
  55. }
  56. GUIResourceTreeView* GUIResourceTreeView::create(const GUIOptions& options, const String& backgroundStyle,
  57. const String& elementBtnStyle, const String& foldoutBtnStyle, const String& highlightBackgroundStyle,
  58. const String& selectionBackgroundStyle, const String& editBoxStyle, const String& dragHighlightStyle,
  59. const String& dragSepHighlightStyle)
  60. {
  61. return new (bs_alloc<GUIResourceTreeView>()) GUIResourceTreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle,
  62. highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create(options));
  63. }
  64. void GUIResourceTreeView::_updateLayoutInternal(const GUILayoutData& data)
  65. {
  66. GUITreeView::_updateLayoutInternal(data);
  67. if(mDropTarget != nullptr)
  68. {
  69. mDropTarget->setArea(data.area.x, data.area.y, data.area.width, data.area.height);
  70. }
  71. }
  72. void GUIResourceTreeView::updateTreeElementHierarchy()
  73. {
  74. // Do nothing, updates are handled via callbacks
  75. }
  76. void GUIResourceTreeView::renameTreeElement(GUITreeView::TreeElement* element, const WString& name)
  77. {
  78. ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(element);
  79. Path oldPath = resourceTreeElement->mFullPath;
  80. Path newPath = oldPath.getParent();
  81. newPath.append(name);
  82. gProjectLibrary().moveEntry(oldPath, findUniquePath(newPath));
  83. }
  84. void GUIResourceTreeView::deleteTreeElement(TreeElement* element)
  85. {
  86. ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(element);
  87. gProjectLibrary().deleteEntry(resourceTreeElement->mFullPath);
  88. }
  89. void GUIResourceTreeView::updateFromProjectLibraryEntry(ResourceTreeElement* treeElement, const ProjectLibrary::LibraryEntry* libraryEntry)
  90. {
  91. struct StackElem
  92. {
  93. StackElem(const ProjectLibrary::LibraryEntry* entry, ResourceTreeElement* treeElem)
  94. :entry(entry), treeElem(treeElem)
  95. { }
  96. const ProjectLibrary::LibraryEntry* entry;
  97. ResourceTreeElement* treeElem;
  98. };
  99. if(libraryEntry->type == ProjectLibrary::LibraryEntryType::Directory)
  100. {
  101. Stack<StackElem> todo;
  102. todo.push(StackElem(libraryEntry, treeElement));
  103. while(!todo.empty())
  104. {
  105. StackElem curElem = todo.top();
  106. todo.pop();
  107. const ProjectLibrary::DirectoryEntry* dirEntry = static_cast<const ProjectLibrary::DirectoryEntry*>(curElem.entry);
  108. for(auto& child : dirEntry->mChildren)
  109. {
  110. ResourceTreeElement* newChild = addTreeElement(curElem.treeElem, child->path);
  111. if(child->type == ProjectLibrary::LibraryEntryType::Directory)
  112. todo.push(StackElem(child, newChild));
  113. }
  114. sortTreeElement(curElem.treeElem);
  115. }
  116. }
  117. }
  118. GUIResourceTreeView::ResourceTreeElement* GUIResourceTreeView::addTreeElement(ResourceTreeElement* parent, const Path& fullPath)
  119. {
  120. ResourceTreeElement* newChild = bs_new<ResourceTreeElement>();
  121. newChild->mParent = parent;
  122. newChild->mName = fullPath.getTail();
  123. newChild->mFullPath = fullPath;
  124. newChild->mSortedIdx = (UINT32)parent->mChildren.size();
  125. newChild->mIsVisible = parent->mIsVisible && parent->mIsExpanded;
  126. newChild->mElementName = fullPath.getWTail();
  127. parent->mChildren.push_back(newChild);
  128. updateElementGUI(parent);
  129. updateElementGUI(newChild);
  130. return newChild;
  131. }
  132. void GUIResourceTreeView::deleteTreeElement(ResourceTreeElement* element)
  133. {
  134. closeTemporarilyExpandedElements(); // In case this element is one of them
  135. if (element->mIsHighlighted)
  136. clearPing();
  137. if(element->mIsSelected)
  138. unselectElement(element);
  139. if(element->mParent != nullptr)
  140. {
  141. auto iterFind = std::find(element->mParent->mChildren.begin(), element->mParent->mChildren.end(), element);
  142. if(iterFind != element->mParent->mChildren.end())
  143. element->mParent->mChildren.erase(iterFind);
  144. sortTreeElement(static_cast<ResourceTreeElement*>(element->mParent));
  145. updateElementGUI(element->mParent);
  146. }
  147. if(&mRootElement != element)
  148. bs_delete(element);
  149. }
  150. void GUIResourceTreeView::sortTreeElement(ResourceTreeElement* element)
  151. {
  152. auto cmp = [&] (const TreeElement* a, const TreeElement* b)
  153. {
  154. return a->mName.compare(b->mName) < 0;
  155. };
  156. std::sort(element->mChildren.begin(), element->mChildren.end(), cmp);
  157. UINT32 idx = 0;
  158. for(auto& child : element->mChildren)
  159. {
  160. child->mSortedIdx = idx;
  161. idx++;
  162. }
  163. }
  164. GUIResourceTreeView::ResourceTreeElement* GUIResourceTreeView::findTreeElement(const Path& fullPath)
  165. {
  166. if (!mRootElement.mFullPath.includes(fullPath))
  167. return nullptr;
  168. Path relPath = fullPath.getRelative(mRootElement.mFullPath);
  169. UINT32 numElems = relPath.getNumDirectories() + (relPath.isFile() ? 1 : 0);
  170. UINT32 idx = 0;
  171. ResourceTreeElement* current = &mRootElement;
  172. while (current != nullptr)
  173. {
  174. if (idx == numElems)
  175. return current;
  176. WString curElem;
  177. if (relPath.isFile() && idx == (numElems - 1))
  178. curElem = relPath.getWFilename();
  179. else
  180. curElem = relPath[idx];
  181. bool foundChild = false;
  182. for (auto& child : current->mChildren)
  183. {
  184. ResourceTreeElement* resourceChild = static_cast<ResourceTreeElement*>(child);
  185. if (Path::comparePathElem(curElem, resourceChild->mElementName))
  186. {
  187. idx++;
  188. current = resourceChild;
  189. foundChild = true;
  190. break;
  191. }
  192. }
  193. if (!foundChild)
  194. current = nullptr;
  195. }
  196. return nullptr;
  197. }
  198. void GUIResourceTreeView::entryAdded(const Path& path)
  199. {
  200. Path parentPath = path.getParent();
  201. ResourceTreeElement* parentElement = findTreeElement(parentPath);
  202. assert(parentElement != nullptr);
  203. ResourceTreeElement* newElement = addTreeElement(parentElement, path);
  204. sortTreeElement(parentElement);
  205. ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(path);
  206. assert(libEntry != nullptr);
  207. updateFromProjectLibraryEntry(newElement, libEntry);
  208. _markLayoutAsDirty();
  209. }
  210. void GUIResourceTreeView::entryRemoved(const Path& path)
  211. {
  212. ResourceTreeElement* treeElement = findTreeElement(path);
  213. if(treeElement != nullptr)
  214. deleteTreeElement(treeElement);
  215. }
  216. void GUIResourceTreeView::setDropTarget(RenderWindow* parentWindow, INT32 x, INT32 y, UINT32 width, UINT32 height)
  217. {
  218. if(mDropTarget != nullptr)
  219. {
  220. Platform::destroyDropTarget(*mDropTarget);
  221. mDropTargetEnterConn.disconnect();
  222. mDropTargetLeaveConn.disconnect();
  223. mDropTargetMoveConn.disconnect();
  224. mDropTargetDroppedConn.disconnect();
  225. }
  226. if(parentWindow != nullptr)
  227. {
  228. mCurrentWindow = parentWindow;
  229. mDropTarget = &Platform::createDropTarget(mCurrentWindow, mLayoutData.area.x, mLayoutData.area.y, mLayoutData.area.width, mLayoutData.area.height);
  230. mDropTargetEnterConn = mDropTarget->onEnter.connect(std::bind(&GUIResourceTreeView::dropTargetDragMove, this, _1, _2));
  231. mDropTargetMoveConn = mDropTarget->onDragOver.connect(std::bind(&GUIResourceTreeView::dropTargetDragMove, this, _1, _2));
  232. mDropTargetLeaveConn = mDropTarget->onLeave.connect(std::bind(&GUIResourceTreeView::dropTargetDragLeave, this));
  233. mDropTargetDroppedConn = mDropTarget->onDrop.connect(std::bind(&GUIResourceTreeView::dropTargetDragDropped, this, _1, _2));
  234. }
  235. else
  236. mDropTarget = nullptr;
  237. }
  238. void GUIResourceTreeView::clearDropTarget()
  239. {
  240. setDropTarget(nullptr, 0, 0, 0, 0);
  241. }
  242. void GUIResourceTreeView::dropTargetDragMove(INT32 x, INT32 y)
  243. {
  244. mDragPosition = Vector2I(x, y);
  245. mDragInProgress = true;
  246. mDropTargetDragActive = true;
  247. _markLayoutAsDirty();
  248. if(mBottomScrollBounds.contains(mDragPosition))
  249. {
  250. if(mScrollState != ScrollState::Down)
  251. mScrollState = ScrollState::TransitioningDown;
  252. }
  253. else if(mTopScrollBounds.contains(mDragPosition))
  254. {
  255. if(mScrollState != ScrollState::Up)
  256. mScrollState = ScrollState::TransitioningUp;
  257. }
  258. else
  259. mScrollState = ScrollState::None;
  260. }
  261. void GUIResourceTreeView::dropTargetDragLeave()
  262. {
  263. mDragInProgress = false;
  264. mDropTargetDragActive = false;
  265. _markLayoutAsDirty();
  266. }
  267. void GUIResourceTreeView::dropTargetDragDropped(INT32 x, INT32 y)
  268. {
  269. const GUITreeView::InteractableElement* element = findElementUnderCoord(Vector2I(x, y));
  270. TreeElement* treeElement = nullptr;
  271. if(element != nullptr)
  272. {
  273. if(element->isTreeElement())
  274. treeElement = element->getTreeElement();
  275. else
  276. treeElement = element->parent;
  277. }
  278. if(mDropTarget->getDropType() == OSDropType::FileList)
  279. {
  280. Vector<WString> fileList = mDropTarget->getFileList();
  281. mDraggedResources = bs_new<InternalDraggedResources>((UINT32)fileList.size());
  282. for(UINT32 i = 0; i < (UINT32)fileList.size(); i++)
  283. mDraggedResources->resourcePaths[i] = fileList[i];
  284. dragAndDropEnded(treeElement);
  285. bs_delete(mDraggedResources);
  286. mDraggedResources = nullptr;
  287. unselectAll();
  288. }
  289. mDragInProgress = false;
  290. mDropTargetDragActive = false;
  291. _markLayoutAsDirty();
  292. }
  293. Path GUIResourceTreeView::findUniquePath(const Path& path)
  294. {
  295. if(FileSystem::exists(path))
  296. {
  297. Path newPath = path;
  298. WString filename = path.getWFilename(false);
  299. UINT32 cnt = 1;
  300. do
  301. {
  302. newPath.setBasename(filename + toWString(cnt));
  303. cnt++;
  304. } while (FileSystem::exists(newPath));
  305. return newPath;
  306. }
  307. else
  308. return path;
  309. }
  310. bool GUIResourceTreeView::acceptDragAndDrop() const
  311. {
  312. return mDropTargetDragActive || DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::Resources;
  313. }
  314. void GUIResourceTreeView::dragAndDropStart()
  315. {
  316. assert(mDraggedResources == nullptr);
  317. DraggedResources* draggedResources = bs_new<DraggedResources>();
  318. InternalDraggedResources* internalDraggedResources = bs_new<InternalDraggedResources>((UINT32)mSelectedElements.size());
  319. UINT32 cnt = 0;
  320. for(auto& selectedElement : mSelectedElements)
  321. {
  322. ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(selectedElement.element);
  323. internalDraggedResources->resourcePaths[cnt] = resourceTreeElement->mFullPath;
  324. draggedResources->resourcePaths.push_back(internalDraggedResources->resourcePaths[cnt]);
  325. cnt++;
  326. }
  327. mDraggedResources = internalDraggedResources;
  328. DragAndDropManager::instance().startDrag((UINT32)DragAndDropType::Resources, (void*)draggedResources,
  329. std::bind(&GUIResourceTreeView::dragAndDropFinalize, this), true);
  330. }
  331. void GUIResourceTreeView::dragAndDropEnded(TreeElement* overTreeElement)
  332. {
  333. if(overTreeElement != nullptr && mDraggedResources != nullptr)
  334. {
  335. ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(overTreeElement);
  336. Path destDir = resourceTreeElement->mFullPath;
  337. if(FileSystem::isFile(destDir))
  338. destDir = destDir.getParent();
  339. for(UINT32 i = 0; i < mDraggedResources->numObjects; i++)
  340. {
  341. WString filename = mDraggedResources->resourcePaths[i].getWFilename();
  342. Path currentParent = mDraggedResources->resourcePaths[i].getParent();
  343. if(currentParent != destDir)
  344. {
  345. Path newPath = destDir;
  346. newPath.append(filename);
  347. gProjectLibrary().moveEntry(mDraggedResources->resourcePaths[i], findUniquePath(newPath));
  348. }
  349. }
  350. }
  351. }
  352. void GUIResourceTreeView::dragAndDropFinalize()
  353. {
  354. mDragInProgress = false;
  355. _markLayoutAsDirty();
  356. DraggedResources* draggedResources = reinterpret_cast<DraggedResources*>(DragAndDropManager::instance().getDragData());
  357. bs_delete(draggedResources);
  358. if(mDraggedResources != nullptr)
  359. {
  360. bs_delete(mDraggedResources);
  361. mDraggedResources = nullptr;
  362. }
  363. }
  364. void GUIResourceTreeView::_changeParentWidget(CGUIWidget* widget)
  365. {
  366. GUITreeView::_changeParentWidget(widget);
  367. if (widget != nullptr && widget->getTarget()->getTarget()->getProperties().isWindow())
  368. {
  369. RenderWindow* parentWindow = static_cast<RenderWindow*>(widget->getTarget()->getTarget().get());
  370. setDropTarget(parentWindow, mLayoutData.area.x, mLayoutData.area.y, mLayoutData.area.width, mLayoutData.area.height);
  371. }
  372. else
  373. clearDropTarget();
  374. }
  375. bool GUIResourceTreeView::_acceptDragAndDrop(const Vector2I position, UINT32 typeId) const
  376. {
  377. return typeId == (UINT32)DragAndDropType::Resources && !_isDisabled();
  378. }
  379. void GUIResourceTreeView::selectionChanged()
  380. {
  381. onSelectionChanged();
  382. sendMessage(SELECTION_CHANGED_MSG);
  383. }
  384. Vector<Path> GUIResourceTreeView::getSelection() const
  385. {
  386. Vector<Path> selectedPaths;
  387. for (auto& selectedElem : mSelectedElements)
  388. {
  389. ResourceTreeElement* resTreeElement = static_cast<ResourceTreeElement*>(selectedElem.element);
  390. selectedPaths.push_back(resTreeElement->mFullPath);
  391. }
  392. return selectedPaths;
  393. }
  394. void GUIResourceTreeView::setSelection(const Vector<Path>& paths)
  395. {
  396. unselectAll();
  397. ResourceTreeElement& root = mRootElement;
  398. Stack<ResourceTreeElement*> todo;
  399. todo.push(&mRootElement);
  400. while (!todo.empty())
  401. {
  402. ResourceTreeElement* currentElem = todo.top();
  403. todo.pop();
  404. auto iterFind = std::find(paths.begin(), paths.end(), currentElem->mFullPath);
  405. if (iterFind != paths.end())
  406. {
  407. expandToElement(currentElem);
  408. selectElement(currentElem);
  409. }
  410. for (auto& child : currentElem->mChildren)
  411. {
  412. ResourceTreeElement* sceneChild = static_cast<ResourceTreeElement*>(child);
  413. todo.push(sceneChild);
  414. }
  415. }
  416. }
  417. const String& GUIResourceTreeView::getGUITypeName()
  418. {
  419. static String typeName = "ResourceTreeView";
  420. return typeName;
  421. }
  422. }