CmQtEditorWindow.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. #include "CmQtEditorWindow.h"
  2. #include <QtWidgets/QTabWidget>
  3. #include <QtWidgets/QPushButton>
  4. #include <QtWidgets/QLayout>
  5. #include <QtWidgets/QLabel>
  6. #include <QtWidgets/QApplication>
  7. #include <QtWidgets/QStackedWidget>
  8. #include <QtGui/QMouseEvent>
  9. #include <QtGui/QPainter>
  10. #include <QtCore/QTimer>
  11. #include "CmDebug.h"
  12. #include "CmQtEditorWidget.h"
  13. #include "CmQtDynamicTabBar.h"
  14. #include "CmWindowDockManager.h"
  15. #include "CmEditorWindowManager.h"
  16. namespace CamelotEditor
  17. {
  18. QtEditorWindow::QtEditorWindow(QWidget* parent, INT32 id)
  19. :QWidget(parent), mResizeMode(RM_NONE), mMoveMode(false), mIsDocked(false), mId(id), mActiveWidgetIdx(0)
  20. {
  21. setupUi();
  22. }
  23. void QtEditorWindow::setupUi()
  24. {
  25. setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
  26. //setAttribute(Qt::WA_NoBackground, true);
  27. //setAutoFillBackground(false);
  28. //
  29. mTimer = new QTimer(this);
  30. /************************************************************************/
  31. /* TITLE BAR */
  32. /************************************************************************/
  33. mTitleBar = new QWidget(this);
  34. mTitleBar->setMouseTracking(true);
  35. mTabBar = new QtDynamicTabBar(this);
  36. mBtnClose = new QPushButton(this);
  37. QHBoxLayout* titleLayout = new QHBoxLayout(this);
  38. titleLayout->setMargin(0);
  39. titleLayout->addWidget(mTabBar, 1);
  40. titleLayout->addWidget(mBtnClose);
  41. mTitleBar->setLayout(titleLayout);
  42. /************************************************************************/
  43. /* CENTRAL LAYOUT */
  44. /************************************************************************/
  45. mCentralWidget = new QWidget(this);
  46. mStackedWidget = new QStackedWidget(this);
  47. QVBoxLayout* centralLayout = new QVBoxLayout(this);
  48. centralLayout->setMargin(0);
  49. centralLayout->addWidget(mCentralWidget);
  50. setLayout(centralLayout);
  51. QVBoxLayout* mainLayout = new QVBoxLayout(this);
  52. mainLayout->setMargin(0);
  53. mainLayout->setContentsMargins(2, 0, 2, 2);
  54. mainLayout->setSpacing(0);
  55. mainLayout->addWidget(mTitleBar);
  56. mainLayout->addWidget(mStackedWidget, 1);
  57. mCentralWidget->setLayout(mainLayout);
  58. setObjectNames();
  59. retranslateUi();
  60. setupSignals();
  61. }
  62. void QtEditorWindow::setupSignals()
  63. {
  64. connect(mBtnClose, SIGNAL(clicked()), this, SLOT(closeWidget()));
  65. connect(mTimer, SIGNAL(timeout()), this, SLOT(timerUpdate()));
  66. mTabBar->onTabSelected.connect(boost::bind(&QtEditorWindow::setActiveWidget, this, _1));
  67. }
  68. void QtEditorWindow::retranslateUi()
  69. {
  70. mBtnClose->setText(tr("Close"));
  71. }
  72. void QtEditorWindow::setObjectNames()
  73. {
  74. mTitleBar->setObjectName(QStringLiteral("TitleBar"));
  75. mTabBar->setObjectName(QStringLiteral("TabBar"));
  76. mBtnClose->setObjectName(QStringLiteral("BtnClose"));
  77. mCentralWidget->setObjectName(QStringLiteral("CentralWidget"));
  78. mStackedWidget->setObjectName(QStringLiteral("StackedWidget"));
  79. }
  80. QSizePolicy QtEditorWindow::sizePolicy() const
  81. {
  82. return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  83. }
  84. void QtEditorWindow::undock()
  85. {
  86. if(mIsDocked)
  87. {
  88. setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
  89. mIsDocked = false;
  90. }
  91. }
  92. void QtEditorWindow::dock()
  93. {
  94. if(!mIsDocked)
  95. {
  96. setWindowFlags(Qt::Widget);
  97. mIsDocked = true;
  98. }
  99. }
  100. WindowLayoutDesc QtEditorWindow::getLayoutDesc() const
  101. {
  102. WindowLayoutDesc desc;
  103. desc.width = geometry().width();
  104. desc.height = geometry().height();
  105. desc.left = geometry().x();
  106. desc.top = geometry().y();
  107. desc.screenIdx = -1;
  108. desc.id = getId();
  109. desc.maximized = false;
  110. desc.dockState = WDS_FLOATING;
  111. if(isDocked())
  112. {
  113. WindowDragDropLocation dockLocation = gWindowDockManager().getDockLocation(this);
  114. switch(dockLocation)
  115. {
  116. case CM_WINDROP_LEFT:
  117. desc.dockState = WDS_LEFT;
  118. break;
  119. case CM_WINDROP_RIGHT:
  120. desc.dockState = WDS_RIGHT;
  121. break;
  122. case CM_WINDROP_BOTTOM:
  123. desc.dockState = WDS_BOTTOM;
  124. break;
  125. case CM_WINDROP_TOP:
  126. desc.dockState = WDS_TOP;
  127. break;
  128. case CM_WINDROP_CENTER:
  129. desc.dockState = WDS_CENTER;
  130. break;
  131. default:
  132. assert(false);
  133. }
  134. desc.dockParentId = gWindowDockManager().getDockParentId(this);
  135. }
  136. else
  137. {
  138. desc.dockState = WDS_FLOATING;
  139. desc.dockParentId = -1;
  140. }
  141. desc.activeWidget = mActiveWidgetIdx;
  142. for(auto iter = mEditorWidgets.begin(); iter != mEditorWidgets.end(); ++iter)
  143. desc.childWidgetNames.push_back((*iter)->getName());
  144. return desc;
  145. }
  146. void QtEditorWindow::restoreFromLayoutDesc(const WindowLayoutDesc& desc)
  147. {
  148. setGeometry(desc.left, desc.top, desc.width, desc.height);
  149. mId = desc.id;
  150. for(auto iter2 = desc.childWidgetNames.begin(); iter2 != desc.childWidgetNames.end(); ++iter2)
  151. gEditorWindowManager().openWidget(*iter2, this);
  152. if(desc.dockState != WDS_FLOATING)
  153. {
  154. WindowDragDropLocation dockLocation = CM_WINDROP_NONE;
  155. switch(desc.dockState)
  156. {
  157. case WDS_LEFT:
  158. dockLocation = CM_WINDROP_LEFT;
  159. break;
  160. case WDS_RIGHT:
  161. dockLocation = CM_WINDROP_RIGHT;
  162. break;
  163. case WDS_BOTTOM:
  164. dockLocation = CM_WINDROP_BOTTOM;
  165. break;
  166. case WDS_TOP:
  167. dockLocation = CM_WINDROP_TOP;
  168. break;
  169. case WDS_CENTER:
  170. dockLocation = CM_WINDROP_CENTER;
  171. break;
  172. }
  173. QtEditorWindow* dockParent = nullptr;
  174. if(desc.dockParentId != -1)
  175. dockParent = gEditorWindowManager().getOpenWindow(desc.dockParentId);
  176. gWindowDockManager().dockWindow(this, dockParent, dockLocation);
  177. }
  178. setActiveWidget(desc.activeWidget);
  179. }
  180. void QtEditorWindow::addWidget(QtEditorWidget* widget)
  181. {
  182. assert(widget != nullptr);
  183. auto iterFind = std::find(mEditorWidgets.begin(), mEditorWidgets.end(), widget);
  184. if(iterFind != mEditorWidgets.end())
  185. CM_EXCEPT(InvalidParametersException, "Specified widget already exists in this window.");
  186. widget->setParentWindow(this);
  187. mEditorWidgets.push_back(widget);
  188. mTabBar->addTab(widget->getName());
  189. }
  190. void QtEditorWindow::insertWidget(UINT32 idx, QtEditorWidget* widget)
  191. {
  192. assert(widget != nullptr);
  193. auto iterFind = std::find(mEditorWidgets.begin(), mEditorWidgets.end(), widget);
  194. if(iterFind != mEditorWidgets.end())
  195. CM_EXCEPT(InvalidParametersException, "Specified widget already exists in this window.");
  196. if(idx > (UINT32)mEditorWidgets.size())
  197. idx = (UINT32)mEditorWidgets.size();
  198. widget->setParentWindow(this);
  199. mEditorWidgets.insert(mEditorWidgets.begin() + idx, widget);
  200. mTabBar->insertTab(idx, widget->getName());
  201. }
  202. void QtEditorWindow::removeWidget(UINT32 idx)
  203. {
  204. if(idx >= (UINT32)mEditorWidgets.size())
  205. CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) +". Valid range: 0 .. " + toString((UINT32)mEditorWidgets.size()));
  206. mEditorWidgets.erase(mEditorWidgets.begin() + idx);
  207. mTabBar->removeTab(idx);
  208. }
  209. QtEditorWidget* QtEditorWindow::getWidget(UINT32 idx) const
  210. {
  211. return mEditorWidgets.at(idx);
  212. }
  213. void QtEditorWindow::setActiveWidget(UINT32 idx)
  214. {
  215. if(idx >= (UINT32)mEditorWidgets.size())
  216. CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) +". Valid range: 0 .. " + toString((UINT32)mEditorWidgets.size()));
  217. mActiveWidgetIdx = idx;
  218. // TODO
  219. }
  220. QWidget* QtEditorWindow::getContentWidget() const
  221. {
  222. return mStackedWidget;
  223. }
  224. void QtEditorWindow::enterEvent(QEvent *e)
  225. {
  226. mTimer->start(100);
  227. }
  228. void QtEditorWindow::leaveEvent(QEvent *e)
  229. {
  230. mTimer->stop();
  231. }
  232. void QtEditorWindow::mousePressEvent(QMouseEvent* event)
  233. {
  234. if(event->buttons() & Qt::LeftButton)
  235. {
  236. if(isOverResizeArea(event->globalPos()))
  237. {
  238. //setUpdatesEnabled(false);
  239. mResizeMode = getResizeMode(event->globalPos());
  240. }
  241. else if(isOverDragArea(event->globalPos()))
  242. {
  243. mMoveMode = true;
  244. QPoint globalTopLeft = mapToGlobal(QPoint(0, 0));
  245. mDragOffset = QPoint(globalTopLeft.x() - event->globalPos().x(), globalTopLeft.y() - event->globalPos().y());
  246. }
  247. event->accept();
  248. }
  249. QWidget::mousePressEvent(event);
  250. }
  251. void QtEditorWindow::mouseReleaseEvent(QMouseEvent* event)
  252. {
  253. if(mMoveMode)
  254. {
  255. gWindowDockManager().windowReleased(this, event->globalPos());
  256. }
  257. mMoveMode = false;
  258. mResizeMode = RM_NONE;
  259. //setUpdatesEnabled(true);
  260. QWidget::mouseReleaseEvent(event);
  261. }
  262. void QtEditorWindow::mouseMoveEvent(QMouseEvent* event)
  263. {
  264. if(mMoveMode)
  265. {
  266. QPoint globalPos = mapToGlobal(event->pos());
  267. gWindowDockManager().windowDragged(this, globalPos);
  268. if(!mIsDocked)
  269. move(event->globalPos() + mDragOffset);
  270. event->accept();
  271. }
  272. else if(mResizeMode != RM_NONE)
  273. {
  274. if(!isDocked())
  275. {
  276. int left = geometry().left();
  277. int width = geometry().width();
  278. int top = geometry().top();
  279. int height = geometry().height();
  280. int leftOld = left;
  281. int topOld = top;
  282. if (mResizeMode & RM_LEFT)
  283. {
  284. width = width + (left - event->globalPos().x());
  285. left = event->globalPos().x();
  286. }
  287. if(mResizeMode & RM_RIGHT)
  288. {
  289. width = event->globalPos().x() - left;
  290. }
  291. if(mResizeMode & RM_TOP)
  292. {
  293. height = height + (top - event->globalPos().y());
  294. top = event->globalPos().y();
  295. }
  296. if(mResizeMode & RM_BOTTOM)
  297. {
  298. height = event->globalPos().y() - top;
  299. }
  300. // Check if we have reached width or height limit. We need to do some special
  301. // steps otherwise the window will move (instead of resize) after the limit is reached
  302. if(width < minimumWidth())
  303. {
  304. left = leftOld;
  305. width = minimumWidth();
  306. }
  307. if(height < minimumHeight())
  308. {
  309. top = topOld;
  310. height = minimumHeight();
  311. }
  312. setGeometry(left, top, width, height);
  313. update();
  314. }
  315. }
  316. QWidget::mouseMoveEvent(event);
  317. }
  318. void QtEditorWindow::timerUpdate()
  319. {
  320. if(!isDocked())
  321. {
  322. if(underMouse())
  323. {
  324. ResizeMode resizeMode = getResizeMode(QCursor::pos());
  325. switch(resizeMode)
  326. {
  327. case RM_LEFT:
  328. setCursor(Qt::SizeHorCursor);
  329. break;
  330. case RM_RIGHT:
  331. setCursor(Qt::SizeHorCursor);
  332. break;
  333. case RM_TOP:
  334. setCursor(Qt::SizeVerCursor);
  335. break;
  336. case RM_BOTTOM:
  337. setCursor(Qt::SizeVerCursor);
  338. break;
  339. case (ResizeMode)(RM_BOTTOM | RM_RIGHT):
  340. setCursor(Qt::SizeFDiagCursor);
  341. break;
  342. case (ResizeMode)(RM_TOP | RM_LEFT):
  343. setCursor(Qt::SizeFDiagCursor);
  344. break;
  345. case (ResizeMode)(RM_BOTTOM | RM_LEFT):
  346. setCursor(Qt::SizeBDiagCursor);
  347. break;
  348. case (ResizeMode)(RM_TOP | RM_RIGHT):
  349. setCursor(Qt::SizeBDiagCursor);
  350. break;
  351. case RM_NONE:
  352. setCursor(Qt::ArrowCursor);
  353. break;
  354. }
  355. }
  356. else
  357. {
  358. if(cursor().shape() != Qt::ArrowCursor)
  359. unsetCursor();
  360. }
  361. }
  362. }
  363. void QtEditorWindow::closeWindow()
  364. {
  365. gWindowDockManager().windowClosed(this); // TODO - Hook this up to use onClosed signal as well
  366. onClosed(this);
  367. close();
  368. }
  369. void QtEditorWindow::closeWidget()
  370. {
  371. QtEditorWidget* activeWidget = getWidget(mActiveWidgetIdx);
  372. removeWidget(mActiveWidgetIdx);
  373. activeWidget->closeWidget();
  374. if(getNumWidgets() == 0)
  375. closeWindow();
  376. }
  377. QtEditorWindow::ResizeMode QtEditorWindow::getResizeMode(QPoint mousePos)
  378. {
  379. QPoint localMousePos = mapFromGlobal(mousePos);
  380. QPoint topLeft = mapFromParent(geometry().topLeft());
  381. QPoint botRight = mapFromParent(geometry().bottomRight());
  382. int distFromRight = botRight.x() - localMousePos.x();
  383. int distFromLeft = localMousePos.x() - topLeft.x();
  384. int distFromBot = botRight.y() - localMousePos.y();
  385. int distFromTop = localMousePos.y() - topLeft.y();
  386. if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0 && distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
  387. return (ResizeMode)(RM_BOTTOM | RM_RIGHT);
  388. if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0 && distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
  389. return (ResizeMode)(RM_TOP | RM_LEFT);
  390. if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0 && distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
  391. return (ResizeMode)(RM_BOTTOM | RM_LEFT);
  392. if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0 && distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
  393. return (ResizeMode)(RM_TOP | RM_RIGHT);
  394. if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0)
  395. return RM_RIGHT;
  396. if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0)
  397. return RM_LEFT;
  398. if(distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
  399. return RM_BOTTOM;
  400. if(distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
  401. return RM_TOP;
  402. return RM_NONE;
  403. }
  404. bool QtEditorWindow::isOverResizeArea(QPoint mousePos)
  405. {
  406. QPoint localMousePos = mapFromGlobal(mousePos);
  407. int distFromRight = geometry().width() - localMousePos.x();
  408. int distFromLeft = localMousePos.x();
  409. int distFromBot = geometry().height() - localMousePos.y();
  410. int distFromTop = localMousePos.y();
  411. if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0)
  412. return true;
  413. if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0)
  414. return true;
  415. if(distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
  416. return true;
  417. if(distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
  418. return true;
  419. return false;
  420. }
  421. bool QtEditorWindow::isOverDragArea(QPoint mousePos)
  422. {
  423. if(getResizeMode(mousePos) != RM_NONE)
  424. return false;
  425. QPoint localMousePos = mapFromGlobal(mousePos);
  426. int distFromTop = localMousePos.y();
  427. if(distFromTop <= TITLE_BAR_HEIGHT)
  428. return true;
  429. return false;
  430. }
  431. }