CmWindowDockManager.cpp 11 KB

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