CmWindowDockManager.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. #include "CmWindowDockManager.h"
  2. #include "CmQtEditorWindow.h"
  3. #include "CmDebug.h"
  4. #include <QtWidgets/QWidget>
  5. #include <QtWidgets/QLayout>
  6. #include <QtWidgets/QSplitter>
  7. namespace CamelotEditor
  8. {
  9. WindowDockManager::WindowDockManager(QWidget* centralWidget, QtDockOverlayWidget* dockOverlayWidget)
  10. :mDockOverlayWidget(dockOverlayWidget), mCentralWidget(centralWidget), mCentralSplitter(nullptr), mLastDraggedWindow(nullptr)
  11. {
  12. QVBoxLayout* boxLayout = new QVBoxLayout();
  13. boxLayout->setMargin(0);
  14. boxLayout->setSpacing(0);
  15. mCentralWidget->setLayout(boxLayout);
  16. mCentralSplitter = new QSplitter(mCentralWidget);
  17. mCentralSplitter->setChildrenCollapsible(false);
  18. boxLayout->addWidget(mCentralSplitter);
  19. }
  20. void WindowDockManager::windowDragged(QtEditorWindow* window, const QPoint& mousePos)
  21. {
  22. assert(window != nullptr);
  23. if(mLastDraggedWindow != window)
  24. {
  25. mLastDragPosition = mousePos;
  26. mLastDraggedWindow = window;
  27. }
  28. if(!window->isDocked())
  29. {
  30. QWidget* dragOverWidget = getDockedWindowAtPosition(mousePos);
  31. if(dragOverWidget == nullptr && isPositionInDockArea(mousePos))
  32. dragOverWidget = mCentralWidget;
  33. if(dragOverWidget != nullptr)
  34. {
  35. WindowDragDropLocation dragLocation = getDropLocationAtPosition(dragOverWidget, mousePos);
  36. std::vector<QPolygon> dropLocations = getDropLocations(dragOverWidget);
  37. QPoint drawOffset = mCentralWidget->mapToGlobal(QPoint(0, 0)) - mDockOverlayWidget->mapToGlobal(QPoint(0, 0));
  38. mDockOverlayWidget->enableDropOverlay(dropLocations, drawOffset);
  39. mDockOverlayWidget->highlightDropLocation(dragLocation);
  40. }
  41. else
  42. {
  43. mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE);
  44. mDockOverlayWidget->disableDropOverlay();
  45. }
  46. }
  47. else
  48. {
  49. QPoint diff = mLastDragPosition - mousePos;
  50. if(diff.manhattanLength() > 4)
  51. {
  52. undockWindow(window);
  53. }
  54. }
  55. }
  56. void WindowDockManager::windowReleased(QtEditorWindow* window, const QPoint& mousePos)
  57. {
  58. mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE);
  59. mDockOverlayWidget->disableDropOverlay();
  60. if(!window->isDocked())
  61. {
  62. QtEditorWindow* windowUnderCursor = getDockedWindowAtPosition(mousePos);
  63. if(windowUnderCursor != nullptr)
  64. {
  65. WindowDragDropLocation dropLocation = getDropLocationAtPosition(windowUnderCursor, mousePos);
  66. dockWindow(window, windowUnderCursor, dropLocation);
  67. }
  68. else
  69. {
  70. if(isPositionInDockArea(mousePos))
  71. {
  72. WindowDragDropLocation dropLocation = getDropLocationAtPosition(mCentralWidget, mousePos);
  73. dockWindow(window, nullptr, dropLocation);
  74. }
  75. }
  76. }
  77. }
  78. void WindowDockManager::windowClosed(QtEditorWindow* window)
  79. {
  80. if(window->isDocked())
  81. undockWindow(window);
  82. }
  83. bool WindowDockManager::isDocked(const QtEditorWindow* window) const
  84. {
  85. auto findIter = mDockedWindows.find(const_cast<QtEditorWindow*>(window));
  86. return findIter != mDockedWindows.end();
  87. }
  88. WindowDragDropLocation WindowDockManager::getDockLocation(const QtEditorWindow* window) const
  89. {
  90. auto findIter = mDockedWindows.find(const_cast<QtEditorWindow*>(window));
  91. assert(findIter != mDockedWindows.end());
  92. return findIter->second.dockLocation;
  93. }
  94. INT32 WindowDockManager::getDockParentId(const QtEditorWindow* window) const
  95. {
  96. auto findIter = mDockedWindows.find(const_cast<QtEditorWindow*>(window));
  97. assert(findIter != mDockedWindows.end());
  98. return findIter->second.parentId;
  99. }
  100. QtEditorWindow* WindowDockManager::getDockedWindowAtPosition(const QPoint& globalPos)
  101. {
  102. QtEditorWindow* foundWindow = nullptr;
  103. for(auto iter = mDockedWindows.begin(); iter != mDockedWindows.end(); ++iter)
  104. {
  105. QtEditorWindow* curWindow = iter->first;
  106. QPoint globalWidgetPos = curWindow->mapToGlobal(QPoint(0, 0));
  107. QRect widgetRect(globalWidgetPos, curWindow->geometry().size());
  108. if(widgetRect.contains(globalPos))
  109. {
  110. foundWindow = curWindow;
  111. break;
  112. }
  113. }
  114. return foundWindow;
  115. }
  116. bool WindowDockManager::isPositionInDockArea(const QPoint& globalPos)
  117. {
  118. QPoint globalWidgetPos = mCentralWidget->mapToGlobal(QPoint(0, 0));
  119. QRect widgetRect(globalWidgetPos, mCentralWidget->geometry().size());
  120. return widgetRect.contains(globalPos);
  121. }
  122. WindowDragDropLocation WindowDockManager::getDropLocationAtPosition(const QWidget* widget, const QPoint& globalPos)
  123. {
  124. assert(widget != nullptr);
  125. QPoint localPos = mCentralWidget->mapFromGlobal(globalPos);
  126. std::vector<QPolygon> dragLocations = getDropLocations(widget);
  127. int idx = 0;
  128. for(auto iter = dragLocations.begin(); iter != dragLocations.end(); ++iter)
  129. {
  130. if(iter->containsPoint(localPos, Qt::OddEvenFill))
  131. return (WindowDragDropLocation)idx;
  132. ++idx;
  133. }
  134. return CM_WINDROP_NONE;
  135. }
  136. void WindowDockManager::dockWindow(QtEditorWindow* windowToDock, QtEditorWindow* dockAtWidget, WindowDragDropLocation dockAtPosition)
  137. {
  138. assert(windowToDock != nullptr);
  139. auto findIter = mDockedWindows.find(windowToDock);
  140. assert(findIter == mDockedWindows.end());
  141. if(dockAtPosition == CM_WINDROP_NONE)
  142. return;
  143. if(dockAtWidget == nullptr)
  144. {
  145. mCentralSplitter->addWidget(windowToDock);
  146. windowToDock->dock();
  147. }
  148. else
  149. {
  150. QSplitter* parentSplitter = dynamic_cast<QSplitter*>(dockAtWidget->parentWidget());
  151. if(parentSplitter == nullptr)
  152. {
  153. LOGWRN("Trying to dock a window to a widget that doesn't have a parent splitter.");
  154. return;
  155. }
  156. if(dockAtPosition == CM_WINDROP_CENTER)
  157. {
  158. CM_ASSERT(false); // TODO - Not implemented
  159. }
  160. else
  161. {
  162. if(parentSplitter->orientation() == Qt::Horizontal)
  163. {
  164. int idxDockAt = parentSplitter->indexOf(dockAtWidget);
  165. if(dockAtPosition == CM_WINDROP_LEFT)
  166. parentSplitter->insertWidget(idxDockAt, windowToDock);
  167. else if(dockAtPosition == CM_WINDROP_RIGHT)
  168. parentSplitter->insertWidget(idxDockAt + 1, windowToDock);
  169. else // Top or bottom
  170. {
  171. QSplitter* newSplitter = new QSplitter();
  172. newSplitter->setOrientation(Qt::Vertical);
  173. newSplitter->setChildrenCollapsible(false);
  174. if(dockAtPosition == CM_WINDROP_TOP)
  175. {
  176. newSplitter->addWidget(windowToDock);
  177. newSplitter->addWidget(dockAtWidget);
  178. }
  179. else
  180. {
  181. newSplitter->addWidget(dockAtWidget);
  182. newSplitter->addWidget(windowToDock);
  183. }
  184. parentSplitter->insertWidget(idxDockAt, newSplitter);
  185. }
  186. }
  187. else
  188. {
  189. int idxDockAt = parentSplitter->indexOf(dockAtWidget);
  190. if(dockAtPosition == CM_WINDROP_TOP)
  191. parentSplitter->insertWidget(idxDockAt, windowToDock);
  192. else if(dockAtPosition == CM_WINDROP_BOTTOM)
  193. parentSplitter->insertWidget(idxDockAt + 1, windowToDock);
  194. else // Left or right
  195. {
  196. QSplitter* newSplitter = new QSplitter();
  197. newSplitter->setOrientation(Qt::Horizontal);
  198. newSplitter->setChildrenCollapsible(false);
  199. if(dockAtPosition == CM_WINDROP_LEFT)
  200. {
  201. newSplitter->addWidget(windowToDock);
  202. newSplitter->addWidget(dockAtWidget);
  203. }
  204. else
  205. {
  206. newSplitter->addWidget(dockAtWidget);
  207. newSplitter->addWidget(windowToDock);
  208. }
  209. parentSplitter->insertWidget(idxDockAt, newSplitter);
  210. }
  211. }
  212. }
  213. windowToDock->dock();
  214. }
  215. DockedWindowInfo dockedInfo;
  216. dockedInfo.dockLocation = dockAtPosition;
  217. if(dockAtWidget == nullptr)
  218. dockedInfo.parentId = -1;
  219. else
  220. dockedInfo.parentId = dockAtWidget->getId();
  221. mDockedWindows[windowToDock] = dockedInfo;
  222. }
  223. void WindowDockManager::undockWindow(QtEditorWindow* windowToUndock)
  224. {
  225. CM_ASSERT(windowToUndock != nullptr);
  226. QSplitter* parentSplitter = dynamic_cast<QSplitter*>(windowToUndock->parentWidget());
  227. if(parentSplitter == nullptr)
  228. {
  229. LOGWRN("Trying to dock a window to a widget that doesn't have a parent splitter.");
  230. return;
  231. }
  232. windowToUndock->setParent(mCentralWidget);
  233. windowToUndock->undock();
  234. windowToUndock->show();
  235. // Check if there is just one widget in splitter, so there's no need for a splitter at all
  236. if(parentSplitter != mCentralSplitter && parentSplitter->count() == 1)
  237. {
  238. QSplitter* topParentSplitter = dynamic_cast<QSplitter*>(parentSplitter->parentWidget());
  239. if(topParentSplitter == nullptr)
  240. {
  241. CM_EXCEPT(InternalErrorException, "Splitter is not root splitter, but doesn't have a splitter parent.");
  242. }
  243. QWidget* remainingWidget = parentSplitter->widget(0);
  244. int splitterIdx = topParentSplitter->indexOf(parentSplitter);
  245. topParentSplitter->insertWidget(splitterIdx, remainingWidget);
  246. delete parentSplitter;
  247. }
  248. auto findIter = mDockedWindows.find(windowToUndock);
  249. CM_ASSERT(findIter != mDockedWindows.end());
  250. mDockedWindows.erase(findIter);
  251. }
  252. std::vector<QPolygon> WindowDockManager::getDropLocations(const QWidget* widget)
  253. {
  254. QPoint topLeft = widget->mapToGlobal(QPoint(0, 0));
  255. topLeft = mCentralWidget->mapFromGlobal(topLeft);
  256. QRect largeRect(topLeft.x() + 1, topLeft.y() + 1, widget->width() - 2, widget->height() - 2);
  257. int sizeOffset = widget->width() < widget->height() ? (widget->width() / 10) : (widget->height() / 10);
  258. QRect smallRect(topLeft.x() + sizeOffset, topLeft.y() + sizeOffset, widget->width() - 2 * sizeOffset, widget->height() - 2 * sizeOffset);
  259. QPolygon left(4);
  260. left[0] = largeRect.topLeft();
  261. left[1] = largeRect.bottomLeft();
  262. left[2] = smallRect.bottomLeft();
  263. left[3] = smallRect.topLeft();
  264. QPolygon right(4);
  265. right[0] = largeRect.topRight();
  266. right[1] = largeRect.bottomRight();
  267. right[2] = smallRect.bottomRight();
  268. right[3] = smallRect.topRight();
  269. QPolygon top(4);
  270. top[0] = largeRect.topLeft();
  271. top[1] = largeRect.topRight();
  272. top[2] = smallRect.topRight();
  273. top[3] = smallRect.topLeft();
  274. QPolygon bottom(4);
  275. bottom[0] = largeRect.bottomRight();
  276. bottom[1] = largeRect.bottomLeft();
  277. bottom[2] = smallRect.bottomLeft();
  278. bottom[3] = smallRect.bottomRight();
  279. QPolygon center(4);
  280. center[0] = smallRect.topLeft();
  281. center[1] = smallRect.topRight();
  282. center[2] = smallRect.bottomRight();
  283. center[3] = smallRect.bottomLeft();
  284. std::vector<QPolygon> dragLocations;
  285. dragLocations.push_back(left);
  286. dragLocations.push_back(right);
  287. dragLocations.push_back(top);
  288. dragLocations.push_back(bottom);
  289. dragLocations.push_back(center);
  290. return dragLocations;
  291. }
  292. WindowDockManager& gWindowDockManager()
  293. {
  294. return WindowDockManager::instance();
  295. }
  296. }