CmWindowDockManager.cpp 9.7 KB

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