CmQtEditorWindow.cpp 15 KB

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