BsGUITabbedTitleBar.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #include "BsGUITabbedTitleBar.h"
  2. #include "BsGUIArea.h"
  3. #include "BsGUILayout.h"
  4. #include "BsGUITexture.h"
  5. #include "BsGUIButton.h"
  6. #include "BsGUITabButton.h"
  7. #include "BsGUISpace.h"
  8. #include "BsGUIWindowDropArea.h"
  9. #include "BsEngineGUI.h"
  10. #include "BsGUIWidget.h"
  11. #include "CmMath.h"
  12. #include "CmPlatform.h"
  13. using namespace CamelotFramework;
  14. using namespace BansheeEngine;
  15. namespace BansheeEditor
  16. {
  17. const UINT32 GUITabbedTitleBar::TAB_SPACING = 20;
  18. const UINT32 GUITabbedTitleBar::OPTION_BTN_SPACING = 3;
  19. GUITabbedTitleBar::GUITabbedTitleBar(BS::GUIWidget* parent, CM::RenderWindow* parentWindow)
  20. :mParentWindow(parentWindow), mMinBtn(nullptr), mCloseBtn(nullptr), mCloseBtnStyle(nullptr),
  21. mMinimizeBtnStyle(nullptr), mParentWidget(parent), mBackgroundArea(nullptr), mUniqueTabIdx(0), mActiveTabIdx(0),
  22. mDragInProgress(false), mDraggedBtn(nullptr), mDragBtnOffset(0), mInitialDragOffset(0)
  23. {
  24. mBackgroundArea = GUIArea::create(*parent, 0, 0, 1, 13, 9900);
  25. GUITexture* titleBarBg = GUITexture::create(*parent, parent->getSkin().getStyle("TitleBarBackground"));
  26. mBackgroundArea->getLayout().addSpace(1);
  27. mBackgroundArea->getLayout().addElement(titleBarBg);
  28. mBackgroundArea->getLayout().addSpace(1);
  29. mMinimizeBtnStyle = parent->getSkin().getStyle("WinMinimizeBtn");
  30. mCloseBtnStyle = parent->getSkin().getStyle("WinCloseBtn");
  31. mMinBtn = GUIButton::create(*parent, HString(L""), mMinimizeBtnStyle);
  32. mCloseBtn = GUIButton::create(*parent, HString(L""), mCloseBtnStyle);
  33. mCloseBtn->onClick.connect(boost::bind(&GUITabbedTitleBar::tabClosed, this));
  34. mTabToggleGroup = GUIToggle::createToggleGroup();
  35. }
  36. GUITabbedTitleBar::~GUITabbedTitleBar()
  37. {
  38. GUIArea::destroy(mBackgroundArea);
  39. GUIElement::destroy(mMinBtn);
  40. GUIElement::destroy(mCloseBtn);
  41. for(auto& tabButton : mTabButtons)
  42. {
  43. GUIElement::destroy(tabButton);
  44. }
  45. }
  46. void GUITabbedTitleBar::addTab(const CM::HString& name)
  47. {
  48. insertTab((UINT32)mTabButtons.size(), name);
  49. }
  50. void GUITabbedTitleBar::insertTab(UINT32 idx, const CM::HString& name)
  51. {
  52. GUITabButton* newTabToggle = GUITabButton::create(*mParentWidget, mTabToggleGroup, mUniqueTabIdx, name, EngineGUI::instance().getSkin().getStyle("TabbedBarBtn"));
  53. idx = Math::clamp(idx, 0U, (UINT32)mTabButtons.size());
  54. newTabToggle->onToggled.connect(boost::bind(&GUITabbedTitleBar::tabToggled, this, mUniqueTabIdx));
  55. newTabToggle->onDragged.connect(boost::bind(&GUITabbedTitleBar::tabDragged, this, _1, _2));
  56. newTabToggle->onDragEnd.connect(boost::bind(&GUITabbedTitleBar::tabDragEnd, this, _1, _2));
  57. mTabButtons.insert(mTabButtons.begin() + idx, newTabToggle);
  58. mUniqueTabIdx++;
  59. }
  60. void GUITabbedTitleBar::removeTab(UINT32 idx)
  61. {
  62. if(mTabButtons.size() == 0)
  63. return;
  64. idx = Math::clamp(idx, 0U, (UINT32)mTabButtons.size() - 1);
  65. GUIElement::destroy(mTabButtons[idx]);
  66. mTabButtons.erase(mTabButtons.begin() + idx);
  67. }
  68. void GUITabbedTitleBar::updateLayout(INT32 x, INT32 y, UINT32 width, UINT32 height,
  69. RectI clipRect, UINT8 widgetDepth)
  70. {
  71. CM::Vector<CM::RectI>::type nonClientAreas;
  72. UINT32 curX = x + 1;
  73. UINT32 curY = y;
  74. UINT32 tabBtnHeight = 0;
  75. for(UINT32 i = 0; i < (UINT32)mTabButtons.size(); i++)
  76. {
  77. GUITabButton* btn = mTabButtons[i];
  78. Vector2I optimalSize = btn->_getOptimalSize();
  79. tabBtnHeight = optimalSize.y;
  80. nonClientAreas.push_back(RectI(curX, curY, TAB_SPACING, tabBtnHeight));
  81. curX += TAB_SPACING;
  82. Vector2I offset(curX, curY);
  83. if(mDragInProgress && btn == mDraggedBtn)
  84. offset.x = mDragBtnOffset;
  85. btn->_setOffset(offset);
  86. btn->_setWidth(optimalSize.x);
  87. btn->_setHeight(optimalSize.y);
  88. btn->_setAreaDepth(9899);
  89. btn->_setWidgetDepth(widgetDepth);
  90. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  91. btn->_setClipRect(elemClipRect);
  92. curX += optimalSize.x;
  93. }
  94. Vector2I minBtnOptimalSize = mMinBtn->_getOptimalSize();
  95. Vector2I closeBtnOptimalSize = mCloseBtn->_getOptimalSize();
  96. UINT32 endButtonWidth = minBtnOptimalSize.x + closeBtnOptimalSize.x + OPTION_BTN_SPACING;
  97. UINT32 remainingWidth = (UINT32)std::max(0, (INT32)(width - curX - endButtonWidth - 1));
  98. nonClientAreas.push_back(RectI(curX, curY, remainingWidth, tabBtnHeight));
  99. INT32 optionBtnXPos = x + width - endButtonWidth - 1;
  100. {
  101. INT32 optionBtnYPos = curY + Math::floorToInt((tabBtnHeight - minBtnOptimalSize.y) * 0.5f);
  102. Vector2I offset(optionBtnXPos, optionBtnYPos);
  103. mMinBtn->_setOffset(offset);
  104. mMinBtn->_setWidth(minBtnOptimalSize.x);
  105. mMinBtn->_setHeight(minBtnOptimalSize.y);
  106. mMinBtn->_setAreaDepth(9899);
  107. mMinBtn->_setWidgetDepth(widgetDepth);
  108. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  109. mMinBtn->_setClipRect(elemClipRect);
  110. }
  111. optionBtnXPos += minBtnOptimalSize.x + OPTION_BTN_SPACING;
  112. {
  113. INT32 optionBtnYPos = curY + Math::floorToInt((tabBtnHeight - closeBtnOptimalSize.y) * 0.5f);
  114. Vector2I offset(optionBtnXPos, optionBtnYPos);
  115. mCloseBtn->_setOffset(offset);
  116. mCloseBtn->_setWidth(closeBtnOptimalSize.x);
  117. mCloseBtn->_setHeight(closeBtnOptimalSize.y);
  118. mCloseBtn->_setAreaDepth(9899);
  119. mCloseBtn->_setWidgetDepth(widgetDepth);
  120. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  121. mCloseBtn->_setClipRect(elemClipRect);
  122. }
  123. Platform::setCaptionNonClientAreas(*mParentWindow, nonClientAreas);
  124. }
  125. void GUITabbedTitleBar::setPosition(INT32 x, INT32 y)
  126. {
  127. mArea.x = x;
  128. mArea.y = y;
  129. mBackgroundArea->setPosition(x, y);
  130. updateLayout(mArea.x, mArea.y, mArea.width, mArea.height, RectI(mArea.x, mArea.y, mArea.width, mArea.height), mParentWidget->getDepth());
  131. }
  132. void GUITabbedTitleBar::setSize(UINT32 width, UINT32 height)
  133. {
  134. mArea.width = width;
  135. mArea.height = height;
  136. mBackgroundArea->setSize(width, height);
  137. updateLayout(mArea.x, mArea.y, mArea.width, mArea.height, RectI(mArea.x, mArea.y, mArea.width, mArea.height), mParentWidget->getDepth());
  138. }
  139. void GUITabbedTitleBar::tabToggled(CM::UINT32 tabIdx)
  140. {
  141. INT32 idx = uniqueIdxToSeqIdx(tabIdx);
  142. if(idx != -1)
  143. {
  144. if(!onTabActivated.empty())
  145. onTabActivated(idx);
  146. }
  147. mActiveTabIdx = tabIdx;
  148. }
  149. void GUITabbedTitleBar::tabClosed()
  150. {
  151. INT32 idx = uniqueIdxToSeqIdx(mActiveTabIdx);
  152. if(idx != -1)
  153. {
  154. removeTab(idx);
  155. if(mTabButtons.size() > 0)
  156. mActiveTabIdx = mTabButtons[0]->getIndex();
  157. if(!onTabClosed.empty())
  158. onTabClosed(idx);
  159. }
  160. }
  161. void GUITabbedTitleBar::tabDragged(CM::UINT32 tabIdx, const Vector2I& dragPos)
  162. {
  163. INT32 idx = uniqueIdxToSeqIdx(tabIdx);
  164. if(idx != -1)
  165. {
  166. if(mArea.contains(dragPos))
  167. {
  168. mDraggedBtn = mTabButtons[idx];
  169. if(!mDragInProgress)
  170. {
  171. Vector2I offset = mDraggedBtn->_getOffset();
  172. mInitialDragOffset = (dragPos.x - offset.x);
  173. mDragInProgress = true;
  174. }
  175. mDragBtnOffset = dragPos.x - mArea.x - mInitialDragOffset;
  176. Vector2I offset = mDraggedBtn->_getOffset();
  177. INT32 diff = mDragBtnOffset - offset.x;
  178. offset.x += diff;
  179. mDraggedBtn->_setOffset(offset);
  180. RectI clipRect = mDraggedBtn->_getClipRect();
  181. clipRect.x -= diff;
  182. mDraggedBtn->_setClipRect(clipRect);
  183. mDragInProgress = true;
  184. for(UINT32 i = 0; i < idx; i++)
  185. {
  186. UINT32 width = mTabButtons[i]->_getWidth();
  187. INT32 centerX = mTabButtons[i]->_getOffset().x + width / 2;
  188. if(dragPos.x < centerX)
  189. {
  190. GUITabButton* temp = mTabButtons[i];
  191. mTabButtons[i] = mTabButtons[idx];
  192. mTabButtons[idx] = temp;
  193. break;
  194. }
  195. }
  196. for(UINT32 i = idx + 1; i < (UINT32)mTabButtons.size(); i++)
  197. {
  198. UINT32 width = mTabButtons[i]->_getWidth();
  199. INT32 centerX = mTabButtons[i]->_getOffset().x + width / 2;
  200. if(dragPos.x > centerX)
  201. {
  202. GUITabButton* temp = mTabButtons[i];
  203. mTabButtons[i] = mTabButtons[idx];
  204. mTabButtons[idx] = temp;
  205. break;
  206. }
  207. }
  208. }
  209. else
  210. {
  211. mDragInProgress = false;
  212. mDraggedBtn = nullptr;
  213. if(!onTabDraggedOff.empty())
  214. onTabDraggedOff(idx);
  215. }
  216. }
  217. }
  218. void GUITabbedTitleBar::tabDragEnd(CM::UINT32 tabIdx, const Vector2I& dragPos)
  219. {
  220. mDragInProgress = false;
  221. mDraggedBtn = nullptr;
  222. updateLayout(mArea.x, mArea.y, mArea.width, mArea.height, RectI(mArea.x, mArea.y, mArea.width, mArea.height), mParentWidget->getDepth());
  223. }
  224. void GUITabbedTitleBar::tabDraggedOn(CM::UINT32 tabIdx)
  225. {
  226. INT32 idx = uniqueIdxToSeqIdx(tabIdx);
  227. if(idx != -1)
  228. {
  229. if(!onTabDraggedOn.empty())
  230. onTabDraggedOn(idx + 1);
  231. }
  232. }
  233. CM::INT32 GUITabbedTitleBar::uniqueIdxToSeqIdx(CM::UINT32 uniqueIdx) const
  234. {
  235. UINT32 idx = 0;
  236. for(auto& tab : mTabButtons)
  237. {
  238. if(tab->getIndex() == uniqueIdx)
  239. return idx;
  240. idx++;
  241. }
  242. return -1;
  243. }
  244. }