BsGUIDropDownContent.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. #include "BsGUIDropDownContent.h"
  2. #include "BsGUILayout.h"
  3. #include "BsGUITexture.h"
  4. #include "BsGUIButton.h"
  5. #include "BsGUILabel.h"
  6. #include "BsGUISpace.h"
  7. #include "BsGUIWidget.h"
  8. #include "BsGUIToggle.h"
  9. #include "BsGUISkin.h"
  10. #include "BsGUIMouseEvent.h"
  11. #include "BsGUICommandEvent.h"
  12. using namespace std::placeholders;
  13. namespace BansheeEngine
  14. {
  15. const String GUIDropDownContent::ENTRY_STYLE_TYPE = "DropDownEntryBtn";
  16. const String GUIDropDownContent::ENTRY_EXP_STYLE_TYPE = "DropDownEntryExpBtn";
  17. const String GUIDropDownContent::SEPARATOR_STYLE_TYPE = "DropDownSeparator";
  18. GUIDropDownContent::GUIDropDownContent(GUIDropDownBox::DropDownSubMenu* parent, const GUIDropDownData& dropDownData,
  19. const String& style, const GUIDimensions& dimensions)
  20. :GUIElementContainer(dimensions, style), mDropDownData(dropDownData),
  21. mSelectedIdx(UINT_MAX), mRangeStart(0), mRangeEnd(0), mParent(parent)
  22. {
  23. }
  24. GUIDropDownContent::~GUIDropDownContent()
  25. {
  26. }
  27. GUIDropDownContent* GUIDropDownContent::create(GUIDropDownBox::DropDownSubMenu* parent,
  28. const GUIDropDownData& dropDownData, const String& style)
  29. {
  30. const String* curStyle = &style;
  31. if (*curStyle == StringUtil::BLANK)
  32. curStyle = &GUIDropDownContent::getGUITypeName();
  33. return new (bs_alloc<GUIDropDownContent>()) GUIDropDownContent(parent, dropDownData, *curStyle, GUIDimensions::create());
  34. }
  35. GUIDropDownContent* GUIDropDownContent::create(GUIDropDownBox::DropDownSubMenu* parent,
  36. const GUIDropDownData& dropDownData, const GUIOptions& options,
  37. const String& style)
  38. {
  39. const String* curStyle = &style;
  40. if (*curStyle == StringUtil::BLANK)
  41. curStyle = &GUIDropDownContent::getGUITypeName();
  42. return new (bs_alloc<GUIDropDownContent>()) GUIDropDownContent(parent, dropDownData, *curStyle, GUIDimensions::create(options));
  43. }
  44. void GUIDropDownContent::styleUpdated()
  45. {
  46. for (auto& visElem : mVisibleElements)
  47. {
  48. GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
  49. if (element.isSeparator())
  50. visElem.separator->setStyle(getSubStyleName(SEPARATOR_STYLE_TYPE));
  51. else if (element.isSubMenu())
  52. visElem.button->setStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE));
  53. else
  54. visElem.button->setStyle(getSubStyleName(ENTRY_STYLE_TYPE));
  55. }
  56. }
  57. void GUIDropDownContent::setRange(UINT32 start, UINT32 end)
  58. {
  59. std::function<void(UINT32, UINT32)> onHover =
  60. [&](UINT32 idx, UINT32 visIdx)
  61. {
  62. setSelected(visIdx);
  63. mParent->elementSelected(idx);
  64. };
  65. std::function<void(UINT32, UINT32)> onClick =
  66. [&](UINT32 idx, UINT32 visIdx)
  67. {
  68. setSelected(visIdx);
  69. mParent->elementActivated(idx, mVisibleElements[visIdx].button->_getCachedBounds());
  70. };
  71. // Remove all elements
  72. while (_getNumChildren() > 0)
  73. {
  74. GUIElementBase* child = _getChild(_getNumChildren() - 1);
  75. assert(child->_getType() == GUIElementBase::Type::Element);
  76. GUIElement::destroy(static_cast<GUIElement*>(child));
  77. }
  78. mRangeStart = start;
  79. mRangeEnd = end;
  80. mVisibleElements.clear();
  81. UINT32 curVisIdx = 0;
  82. for (UINT32 i = start; i < end; i++)
  83. {
  84. mVisibleElements.push_back(VisibleElement());
  85. VisibleElement& visElem = mVisibleElements.back();
  86. visElem.idx = i;
  87. GUIDropDownDataEntry& element = mDropDownData.entries[i];
  88. if (element.isSeparator())
  89. {
  90. visElem.separator = GUITexture::create(GUIImageScaleMode::StretchToFit, getSubStyleName(SEPARATOR_STYLE_TYPE));
  91. _registerChildElement(visElem.separator);
  92. }
  93. else if (element.isSubMenu())
  94. {
  95. visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_EXP_STYLE_TYPE));
  96. visElem.button->onHover.connect(std::bind(onClick, i, curVisIdx));
  97. _registerChildElement(visElem.button);
  98. }
  99. else
  100. {
  101. visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_STYLE_TYPE));
  102. visElem.button->onHover.connect(std::bind(onHover, i, curVisIdx));
  103. visElem.button->onClick.connect(std::bind(onClick, i, curVisIdx));
  104. _registerChildElement(visElem.button);
  105. const WString& shortcutTag = element.getShortcutTag();
  106. if (!shortcutTag.empty())
  107. {
  108. visElem.shortcutLabel = GUILabel::create(HString(shortcutTag), "RightAlignedLabel");
  109. _registerChildElement(visElem.shortcutLabel);
  110. }
  111. }
  112. curVisIdx++;
  113. }
  114. markContentAsDirty();
  115. }
  116. UINT32 GUIDropDownContent::getElementHeight(UINT32 idx) const
  117. {
  118. if (_getParentWidget() == nullptr)
  119. return 14; // Arbitrary
  120. if (mDropDownData.entries[idx].isSeparator())
  121. return _getParentWidget()->getSkin().getStyle(getSubStyleName(SEPARATOR_STYLE_TYPE))->height;
  122. else if (mDropDownData.entries[idx].isSubMenu())
  123. return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE))->height;
  124. else
  125. return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_STYLE_TYPE))->height;
  126. }
  127. HString GUIDropDownContent::getElementLocalizedName(UINT32 idx) const
  128. {
  129. const WString& label = mDropDownData.entries[idx].getLabel();
  130. auto findLocalizedName = mDropDownData.localizedNames.find(label);
  131. if (findLocalizedName != mDropDownData.localizedNames.end())
  132. return findLocalizedName->second;
  133. else
  134. return HString(label);
  135. }
  136. void GUIDropDownContent::setKeyboardFocus(bool focus)
  137. {
  138. mKeyboardFocus = focus;
  139. setFocus(focus);
  140. }
  141. bool GUIDropDownContent::_commandEvent(const GUICommandEvent& ev)
  142. {
  143. if (!mKeyboardFocus)
  144. return false;
  145. UINT32 maxElemIdx = (UINT32)mDropDownData.entries.size();
  146. switch (ev.getType())
  147. {
  148. case GUICommandEventType::MoveDown:
  149. if (mSelectedIdx == UINT_MAX)
  150. selectNext(0);
  151. else
  152. selectNext(mSelectedIdx + 1);
  153. return true;
  154. case GUICommandEventType::MoveUp:
  155. if (mSelectedIdx == UINT_MAX)
  156. selectNext(0);
  157. else
  158. selectPrevious(mSelectedIdx - 1);
  159. return true;
  160. case GUICommandEventType::Escape:
  161. case GUICommandEventType::MoveLeft:
  162. mParent->close();
  163. return true;
  164. case GUICommandEventType::MoveRight:
  165. {
  166. if (mSelectedIdx == UINT_MAX)
  167. selectNext(0);
  168. else
  169. {
  170. GUIDropDownDataEntry& entry = mDropDownData.entries[mVisibleElements[mSelectedIdx].idx];
  171. if (entry.isSubMenu())
  172. mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getCachedBounds());
  173. }
  174. }
  175. return true;
  176. case GUICommandEventType::Return:
  177. if (mSelectedIdx == UINT_MAX)
  178. selectNext(0);
  179. else
  180. mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getCachedBounds());
  181. return true;
  182. }
  183. return false;
  184. }
  185. void GUIDropDownContent::setSelected(UINT32 idx)
  186. {
  187. if (mSelectedIdx != UINT_MAX)
  188. mVisibleElements[mSelectedIdx].button->_setOn(false);
  189. mSelectedIdx = idx;
  190. mVisibleElements[mSelectedIdx].button->_setOn(true);
  191. mParent->elementSelected(mVisibleElements[mSelectedIdx].idx);
  192. }
  193. void GUIDropDownContent::selectNext(UINT32 startIdx)
  194. {
  195. UINT32 numElements = (UINT32)mDropDownData.entries.size();
  196. bool gotNextIndex = false;
  197. UINT32 nextIdx = startIdx;
  198. for (UINT32 i = 0; i < numElements; i++)
  199. {
  200. if (nextIdx >= numElements)
  201. nextIdx = 0; // Wrap around
  202. GUIDropDownDataEntry& entry = mDropDownData.entries[nextIdx];
  203. if (!entry.isSeparator())
  204. {
  205. gotNextIndex = true;
  206. break;
  207. }
  208. nextIdx++;
  209. }
  210. if (gotNextIndex)
  211. {
  212. while (nextIdx < mRangeStart || nextIdx >= mRangeEnd)
  213. mParent->scrollDown();
  214. UINT32 visIdx = 0;
  215. for (auto& visElem : mVisibleElements)
  216. {
  217. if (visElem.idx == nextIdx)
  218. {
  219. setSelected(visIdx);
  220. break;
  221. }
  222. visIdx++;
  223. }
  224. }
  225. }
  226. void GUIDropDownContent::selectPrevious(UINT32 startIdx)
  227. {
  228. UINT32 numElements = (UINT32)mDropDownData.entries.size();
  229. bool gotNextIndex = false;
  230. INT32 prevIdx = (INT32)startIdx;
  231. for (UINT32 i = 0; i < numElements; i++)
  232. {
  233. if (prevIdx < 0)
  234. prevIdx = numElements - 1; // Wrap around
  235. GUIDropDownDataEntry& entry = mDropDownData.entries[prevIdx];
  236. if (!entry.isSeparator())
  237. {
  238. gotNextIndex = true;
  239. break;
  240. }
  241. prevIdx--;
  242. }
  243. if (gotNextIndex)
  244. {
  245. while (prevIdx < (INT32)mRangeStart || prevIdx >= (INT32)mRangeEnd)
  246. mParent->scrollUp();
  247. UINT32 visIdx = 0;
  248. for (auto& visElem : mVisibleElements)
  249. {
  250. if (visElem.idx == prevIdx)
  251. {
  252. setSelected(visIdx);
  253. break;
  254. }
  255. visIdx++;
  256. }
  257. }
  258. }
  259. Vector2I GUIDropDownContent::_getOptimalSize() const
  260. {
  261. Vector2I optimalSize;
  262. for (auto& visElem : mVisibleElements)
  263. {
  264. const GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
  265. optimalSize.y += (INT32)getElementHeight(visElem.idx);
  266. if (element.isSeparator())
  267. optimalSize.x = std::max(optimalSize.x, visElem.separator->_getOptimalSize().x);
  268. else
  269. optimalSize.x = std::max(optimalSize.x, visElem.button->_getOptimalSize().x);
  270. }
  271. return optimalSize;
  272. }
  273. void GUIDropDownContent::updateClippedBounds()
  274. {
  275. Vector2I offset = _getOffset();
  276. mClippedBounds = Rect2I(offset.x, offset.y, _getWidth(), _getHeight());
  277. Rect2I localClipRect(mClipRect.x + mOffset.x, mClipRect.y + mOffset.y, mClipRect.width, mClipRect.height);
  278. mClippedBounds.clip(localClipRect);
  279. }
  280. void GUIDropDownContent::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
  281. Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
  282. {
  283. INT32 yOffset = y;
  284. for (auto& visElem : mVisibleElements)
  285. {
  286. const GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
  287. GUIElement* guiMainElement = nullptr;
  288. if (element.isSeparator())
  289. guiMainElement = visElem.separator;
  290. else
  291. guiMainElement = visElem.button;
  292. UINT32 elemHeight = getElementHeight(visElem.idx);
  293. Vector2I offset(x, yOffset);
  294. yOffset += elemHeight;
  295. guiMainElement->_setPosition(offset);
  296. guiMainElement->_setWidth(width);
  297. guiMainElement->_setHeight(elemHeight);
  298. guiMainElement->_setAreaDepth(panelDepth);
  299. guiMainElement->_setWidgetDepth(widgetDepth);
  300. Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  301. guiMainElement->_setClipRect(elemClipRect);
  302. // Shortcut label
  303. GUILabel* shortcutLabel = visElem.shortcutLabel;
  304. if (shortcutLabel != nullptr)
  305. {
  306. shortcutLabel->_setPosition(offset);
  307. shortcutLabel->_setWidth(width);
  308. shortcutLabel->_setHeight(elemHeight);
  309. shortcutLabel->_setAreaDepth(panelDepth);
  310. shortcutLabel->_setWidgetDepth(widgetDepth);
  311. shortcutLabel->_setClipRect(elemClipRect);
  312. }
  313. }
  314. }
  315. const String& GUIDropDownContent::getGUITypeName()
  316. {
  317. static String typeName = "GUIDropDownContent";
  318. return typeName;
  319. }
  320. }