BsGUIDropDownContent.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsGUIDropDownContent.h"
  4. #include "BsGUITexture.h"
  5. #include "BsGUIButton.h"
  6. #include "BsGUILabel.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_TOGGLE_STYLE_TYPE = "DropDownEntryToggleBtn";
  16. const String GUIDropDownContent::ENTRY_STYLE_TYPE = "DropDownEntryBtn";
  17. const String GUIDropDownContent::ENTRY_EXP_STYLE_TYPE = "DropDownEntryExpBtn";
  18. const String GUIDropDownContent::SEPARATOR_STYLE_TYPE = "DropDownSeparator";
  19. GUIDropDownContent::GUIDropDownContent(GUIDropDownMenu::DropDownSubMenu* parent, const GUIDropDownData& dropDownData,
  20. const String& style, const GUIDimensions& dimensions)
  21. :GUIElementContainer(dimensions, style), mDropDownData(dropDownData), mKeyboardFocus(true),
  22. mStates(dropDownData.states), mSelectedIdx(UINT_MAX), mRangeStart(0), mRangeEnd(0), mParent(parent),
  23. mIsToggle(parent->getType() == GUIDropDownType::MultiListBox)
  24. {
  25. }
  26. GUIDropDownContent::~GUIDropDownContent()
  27. {
  28. }
  29. GUIDropDownContent* GUIDropDownContent::create(GUIDropDownMenu::DropDownSubMenu* parent,
  30. const GUIDropDownData& dropDownData, const String& style)
  31. {
  32. const String* curStyle = &style;
  33. if (*curStyle == StringUtil::BLANK)
  34. curStyle = &GUIDropDownContent::getGUITypeName();
  35. return new (bs_alloc<GUIDropDownContent>()) GUIDropDownContent(parent, dropDownData, *curStyle, GUIDimensions::create());
  36. }
  37. GUIDropDownContent* GUIDropDownContent::create(GUIDropDownMenu::DropDownSubMenu* parent,
  38. const GUIDropDownData& dropDownData, const GUIOptions& options,
  39. const String& style)
  40. {
  41. const String* curStyle = &style;
  42. if (*curStyle == StringUtil::BLANK)
  43. curStyle = &GUIDropDownContent::getGUITypeName();
  44. return new (bs_alloc<GUIDropDownContent>()) GUIDropDownContent(parent, dropDownData, *curStyle, GUIDimensions::create(options));
  45. }
  46. void GUIDropDownContent::styleUpdated()
  47. {
  48. for (auto& visElem : mVisibleElements)
  49. {
  50. GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
  51. if (element.isSeparator())
  52. visElem.separator->setStyle(getSubStyleName(SEPARATOR_STYLE_TYPE));
  53. else if (element.isSubMenu())
  54. visElem.button->setStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE));
  55. else
  56. {
  57. if (mIsToggle)
  58. visElem.button->setStyle(getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE));
  59. else
  60. visElem.button->setStyle(getSubStyleName(ENTRY_STYLE_TYPE));
  61. }
  62. }
  63. }
  64. void GUIDropDownContent::setRange(UINT32 start, UINT32 end)
  65. {
  66. std::function<void(UINT32, UINT32)> onHover =
  67. [&](UINT32 idx, UINT32 visIdx)
  68. {
  69. setSelected(visIdx);
  70. mParent->elementSelected(idx);
  71. };
  72. std::function<void(UINT32, UINT32)> onClick =
  73. [&](UINT32 idx, UINT32 visIdx)
  74. {
  75. setSelected(visIdx);
  76. if (mIsToggle)
  77. mStates[idx] = !mStates[idx];
  78. mParent->elementActivated(idx, mVisibleElements[visIdx].button->_getLayoutData().area);
  79. };
  80. // Remove all elements
  81. while (_getNumChildren() > 0)
  82. {
  83. GUIElementBase* child = _getChild(_getNumChildren() - 1);
  84. assert(child->_getType() == GUIElementBase::Type::Element);
  85. GUIElement::destroy(static_cast<GUIElement*>(child));
  86. }
  87. mRangeStart = start;
  88. mRangeEnd = end;
  89. UINT32 range = end - start;
  90. if (mSelectedIdx != UINT_MAX && mSelectedIdx >= range)
  91. mSelectedIdx = UINT_MAX;
  92. mVisibleElements.clear();
  93. UINT32 curVisIdx = 0;
  94. for (UINT32 i = start; i < end; i++)
  95. {
  96. mVisibleElements.push_back(VisibleElement());
  97. VisibleElement& visElem = mVisibleElements.back();
  98. visElem.idx = i;
  99. GUIDropDownDataEntry& element = mDropDownData.entries[i];
  100. if (element.isSeparator())
  101. {
  102. visElem.separator = GUITexture::create(GUIImageScaleMode::StretchToFit, getSubStyleName(SEPARATOR_STYLE_TYPE));
  103. _registerChildElement(visElem.separator);
  104. }
  105. else if (element.isSubMenu())
  106. {
  107. visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_EXP_STYLE_TYPE));
  108. visElem.button->onHover.connect(std::bind(onClick, i, curVisIdx));
  109. _registerChildElement(visElem.button);
  110. }
  111. else
  112. {
  113. if (mIsToggle)
  114. {
  115. GUIToggle* toggle = GUIToggle::create(getElementLocalizedName(i), getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE));
  116. if (mStates[i])
  117. toggle->toggleOn();
  118. visElem.button = toggle;
  119. }
  120. else
  121. visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_STYLE_TYPE));
  122. visElem.button->onHover.connect(std::bind(onHover, i, curVisIdx));
  123. visElem.button->onClick.connect(std::bind(onClick, i, curVisIdx));
  124. _registerChildElement(visElem.button);
  125. const WString& shortcutTag = element.getShortcutTag();
  126. if (!shortcutTag.empty())
  127. {
  128. visElem.shortcutLabel = GUILabel::create(HString(shortcutTag), "RightAlignedLabel");
  129. _registerChildElement(visElem.shortcutLabel);
  130. }
  131. }
  132. curVisIdx++;
  133. }
  134. _markLayoutAsDirty();
  135. }
  136. UINT32 GUIDropDownContent::getElementHeight(UINT32 idx) const
  137. {
  138. if (_getParentWidget() == nullptr)
  139. return 14; // Arbitrary
  140. if (mDropDownData.entries[idx].isSeparator())
  141. return _getParentWidget()->getSkin().getStyle(getSubStyleName(SEPARATOR_STYLE_TYPE))->height;
  142. else if (mDropDownData.entries[idx].isSubMenu())
  143. return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE))->height;
  144. else
  145. {
  146. if (mIsToggle)
  147. return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE))->height;
  148. else
  149. return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_STYLE_TYPE))->height;
  150. }
  151. }
  152. HString GUIDropDownContent::getElementLocalizedName(UINT32 idx) const
  153. {
  154. const WString& label = mDropDownData.entries[idx].getLabel();
  155. auto findLocalizedName = mDropDownData.localizedNames.find(label);
  156. if (findLocalizedName != mDropDownData.localizedNames.end())
  157. return findLocalizedName->second;
  158. else
  159. return HString(label);
  160. }
  161. void GUIDropDownContent::setKeyboardFocus(bool focus)
  162. {
  163. mKeyboardFocus = focus;
  164. setFocus(focus);
  165. }
  166. bool GUIDropDownContent::_commandEvent(const GUICommandEvent& ev)
  167. {
  168. bool baseReturn = GUIElementContainer::_commandEvent(ev);
  169. if (!mKeyboardFocus)
  170. return baseReturn;
  171. UINT32 maxElemIdx = (UINT32)mDropDownData.entries.size();
  172. switch (ev.getType())
  173. {
  174. case GUICommandEventType::MoveDown:
  175. if (mSelectedIdx == UINT_MAX)
  176. selectNext(0);
  177. else
  178. selectNext(mVisibleElements[mSelectedIdx].idx + 1);
  179. return true;
  180. case GUICommandEventType::MoveUp:
  181. if (mSelectedIdx == UINT_MAX)
  182. selectNext(0);
  183. else
  184. selectPrevious(mVisibleElements[mSelectedIdx].idx - 1);
  185. return true;
  186. case GUICommandEventType::Escape:
  187. case GUICommandEventType::MoveLeft:
  188. mParent->close();
  189. return true;
  190. case GUICommandEventType::MoveRight:
  191. {
  192. if (mSelectedIdx == UINT_MAX)
  193. selectNext(0);
  194. else
  195. {
  196. GUIDropDownDataEntry& entry = mDropDownData.entries[mVisibleElements[mSelectedIdx].idx];
  197. if (entry.isSubMenu())
  198. mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getLayoutData().area);
  199. }
  200. }
  201. return true;
  202. case GUICommandEventType::Confirm:
  203. if (mSelectedIdx == UINT_MAX)
  204. selectNext(0);
  205. else
  206. {
  207. if (mIsToggle)
  208. mVisibleElements[mSelectedIdx].button->_setOn(!mVisibleElements[mSelectedIdx].button->_isOn());
  209. mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getLayoutData().area);
  210. }
  211. return true;
  212. }
  213. return baseReturn;
  214. }
  215. bool GUIDropDownContent::_mouseEvent(const GUIMouseEvent& ev)
  216. {
  217. if (ev.getType() == GUIMouseEventType::MouseWheelScroll)
  218. {
  219. if (ev.getWheelScrollAmount() < 0)
  220. mParent->scrollDown();
  221. else
  222. mParent->scrollUp();
  223. return true;
  224. }
  225. return false;
  226. }
  227. void GUIDropDownContent::setSelected(UINT32 idx)
  228. {
  229. if (mSelectedIdx != UINT_MAX)
  230. {
  231. if (mVisibleElements[mSelectedIdx].button->_isOn())
  232. mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::NormalOn);
  233. else
  234. mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::Normal);
  235. }
  236. mSelectedIdx = idx;
  237. if (mVisibleElements[mSelectedIdx].button->_isOn())
  238. mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::HoverOn);
  239. else
  240. mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::Hover);
  241. mParent->elementSelected(mVisibleElements[mSelectedIdx].idx);
  242. }
  243. void GUIDropDownContent::selectNext(UINT32 startIdx)
  244. {
  245. UINT32 numElements = (UINT32)mDropDownData.entries.size();
  246. bool gotNextIndex = false;
  247. UINT32 nextIdx = startIdx;
  248. for (UINT32 i = 0; i < numElements; i++)
  249. {
  250. if (nextIdx >= numElements)
  251. nextIdx = 0; // Wrap around
  252. GUIDropDownDataEntry& entry = mDropDownData.entries[nextIdx];
  253. if (!entry.isSeparator())
  254. {
  255. gotNextIndex = true;
  256. break;
  257. }
  258. nextIdx++;
  259. }
  260. if (gotNextIndex)
  261. {
  262. while (nextIdx < mRangeStart || nextIdx >= mRangeEnd)
  263. mParent->scrollDown();
  264. UINT32 visIdx = 0;
  265. for (auto& visElem : mVisibleElements)
  266. {
  267. if (visElem.idx == nextIdx)
  268. {
  269. setSelected(visIdx);
  270. break;
  271. }
  272. visIdx++;
  273. }
  274. }
  275. }
  276. void GUIDropDownContent::selectPrevious(UINT32 startIdx)
  277. {
  278. UINT32 numElements = (UINT32)mDropDownData.entries.size();
  279. bool gotNextIndex = false;
  280. INT32 prevIdx = (INT32)startIdx;
  281. for (UINT32 i = 0; i < numElements; i++)
  282. {
  283. if (prevIdx < 0)
  284. prevIdx = numElements - 1; // Wrap around
  285. GUIDropDownDataEntry& entry = mDropDownData.entries[prevIdx];
  286. if (!entry.isSeparator())
  287. {
  288. gotNextIndex = true;
  289. break;
  290. }
  291. prevIdx--;
  292. }
  293. if (gotNextIndex)
  294. {
  295. while (prevIdx < (INT32)mRangeStart || prevIdx >= (INT32)mRangeEnd)
  296. mParent->scrollUp();
  297. UINT32 visIdx = 0;
  298. for (auto& visElem : mVisibleElements)
  299. {
  300. if (visElem.idx == prevIdx)
  301. {
  302. setSelected(visIdx);
  303. break;
  304. }
  305. visIdx++;
  306. }
  307. }
  308. }
  309. Vector2I GUIDropDownContent::_getOptimalSize() const
  310. {
  311. Vector2I optimalSize;
  312. for (auto& visElem : mVisibleElements)
  313. {
  314. const GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
  315. optimalSize.y += (INT32)getElementHeight(visElem.idx);
  316. if (element.isSeparator())
  317. optimalSize.x = std::max(optimalSize.x, visElem.separator->_getOptimalSize().x);
  318. else
  319. optimalSize.x = std::max(optimalSize.x, visElem.button->_getOptimalSize().x);
  320. }
  321. return optimalSize;
  322. }
  323. void GUIDropDownContent::_updateLayoutInternal(const GUILayoutData& data)
  324. {
  325. GUILayoutData childData = data;
  326. INT32 yOffset = data.area.y;
  327. for (auto& visElem : mVisibleElements)
  328. {
  329. const GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
  330. GUIElement* guiMainElement = nullptr;
  331. if (element.isSeparator())
  332. guiMainElement = visElem.separator;
  333. else
  334. guiMainElement = visElem.button;
  335. childData.area.y = yOffset;
  336. childData.area.height = getElementHeight(visElem.idx);
  337. yOffset += childData.area.height;
  338. guiMainElement->_setLayoutData(childData);
  339. // Shortcut label
  340. GUILabel* shortcutLabel = visElem.shortcutLabel;
  341. if (shortcutLabel != nullptr)
  342. shortcutLabel->_setLayoutData(childData);
  343. }
  344. }
  345. const String& GUIDropDownContent::getGUITypeName()
  346. {
  347. static String typeName = "GUIDropDownContent";
  348. return typeName;
  349. }
  350. }