BsGUIDropDownBox.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #include "BsGUIDropDownBox.h"
  2. #include "BsGUIArea.h"
  3. #include "BsGUILayout.h"
  4. #include "BsGUITexture.h"
  5. #include "BsGUIButton.h"
  6. #include "BsEngineGUI.h"
  7. #include "BsGUIContent.h"
  8. #include "CmViewport.h"
  9. #include "BsGUIDropDownList.h"
  10. using namespace CamelotFramework;
  11. namespace BansheeEngine
  12. {
  13. const UINT32 GUIDropDownBox::DROP_DOWN_BOX_WIDTH = 150;
  14. GUIDropDownBox::GUIDropDownBox(const HSceneObject& parent)
  15. :GUIWidget(parent), mDropDownStartIdx(0)
  16. {
  17. }
  18. GUIDropDownBox::~GUIDropDownBox()
  19. {
  20. }
  21. void GUIDropDownBox::initialize(Viewport* target, RenderWindow* window, GUIDropDownList* parentList,
  22. const CM::Vector<WString>::type& elements, std::function<void(CM::UINT32)> selectedCallback)
  23. {
  24. GUIWidget::initialize(target, window);
  25. mDropDownElements = elements;
  26. mSelectedDropDownEntryCallback = selectedCallback;
  27. setDepth(0); // Needs to be in front of everything
  28. const GUIElementStyle* dropDownBoxStyle = EngineGUI::instance().getSkin().getStyle("DropDownBox");
  29. Rect dropDownListBounds = parentList->getBounds();
  30. Int2 position;
  31. // Determine x position and whether to align to left or right side of the drop down list
  32. UINT32 availableRightwardWidth = (UINT32)std::max(0, (target->getLeft() + target->getWidth()) - dropDownListBounds.x);
  33. UINT32 availableLeftwardWidth = (UINT32)std::max(0, (dropDownListBounds.x + dropDownListBounds.width) - target->getLeft());
  34. //// Prefer right if possible
  35. if(DROP_DOWN_BOX_WIDTH <= availableRightwardWidth)
  36. position.x = dropDownListBounds.x;
  37. else
  38. {
  39. if(availableRightwardWidth >= availableLeftwardWidth)
  40. position.x = dropDownListBounds.x;
  41. else
  42. position.x = dropDownListBounds.x - std::min(DROP_DOWN_BOX_WIDTH, availableLeftwardWidth);
  43. }
  44. // Determine maximum width
  45. UINT32 maxPossibleWidth = (UINT32)std::max(0, (target->getLeft() + target->getWidth()) - position.x);
  46. UINT32 width = std::min(DROP_DOWN_BOX_WIDTH, maxPossibleWidth);
  47. UINT32 contentWidth = (UINT32)std::max(0, (INT32)width - (INT32)dropDownBoxStyle->margins.left - (INT32)dropDownBoxStyle->margins.right);
  48. // Determine y position and whether to open upward or downward
  49. UINT32 scrollButtonUpHeight = EngineGUI::instance().getSkin().getStyle("DropDownScrollUpBtn")->height;
  50. UINT32 scrollButtonDownHeight = EngineGUI::instance().getSkin().getStyle("DropDownScrollDownBtn")->height;
  51. UINT32 helperElementHeight = scrollButtonUpHeight + scrollButtonDownHeight + dropDownBoxStyle->margins.top + dropDownBoxStyle->margins.bottom;
  52. UINT32 elementButtonHeight = EngineGUI::instance().getSkin().getStyle("DropDownEntryBtn")->height;
  53. UINT32 maxNeededHeight = elementButtonHeight * (UINT32)mDropDownElements.size() + helperElementHeight;
  54. UINT32 availableDownwardHeight = (UINT32)std::max(0, (target->getTop() + target->getHeight()) - (dropDownListBounds.y + dropDownListBounds.height));
  55. UINT32 availableUpwardHeight = (UINT32)std::max(0, dropDownListBounds.y - target->getTop());
  56. //// Prefer down if possible
  57. if(maxNeededHeight <= availableDownwardHeight)
  58. position.y = dropDownListBounds.y + dropDownListBounds.height;
  59. else
  60. {
  61. if(availableDownwardHeight >= availableUpwardHeight)
  62. position.y = dropDownListBounds.y + dropDownListBounds.height;
  63. else
  64. position.y = dropDownListBounds.y - std::min(maxNeededHeight, availableUpwardHeight);
  65. }
  66. // Determine height of the box, and how many visible elements we can fit in it
  67. UINT32 maxPossibleHeight = (UINT32)std::max(0, (target->getTop() + target->getHeight()) - position.y);
  68. UINT32 heightAvailableForContent = (UINT32)std::max(0, (INT32)maxPossibleHeight - (INT32)helperElementHeight);
  69. UINT32 numVisElements = 0;
  70. UINT32 contentAreaHeight = 0;
  71. for(UINT32 i = 0; i < (UINT32)mDropDownElements.size(); i++)
  72. {
  73. if(contentAreaHeight >= heightAvailableForContent)
  74. break;
  75. contentAreaHeight += elementButtonHeight;
  76. numVisElements++;
  77. }
  78. UINT32 totalHeight = contentAreaHeight + helperElementHeight;
  79. // Scroll up buttons
  80. GUIArea* scrollUpBtnArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left, position.y, contentWidth, scrollButtonUpHeight);
  81. scrollUpBtnArea->setDepth(100);
  82. const GUIElementStyle* scrollUpBtnArrow = EngineGUI::instance().getSkin().getStyle("DropDownScrollUpBtnArrow");
  83. GUIButton* scrollUpBtn = GUIButton::create(*this, GUIContent(L"", scrollUpBtnArrow->normal.texture), EngineGUI::instance().getSkin().getStyle("DropDownScrollUpBtn"));
  84. scrollUpBtnArea->getLayout().addElement(scrollUpBtn);
  85. // Entry buttons
  86. // TODO - It may happen there is not enough place for even a single element, in which case we just don't render any.
  87. // Some smart solution to deal with that case might be employed, but I think it's a fairly rare case and can be deal with in other ways.
  88. UINT32 contentAreaYOffset = scrollButtonUpHeight + dropDownBoxStyle->margins.top;
  89. GUIArea* dropDownEntriesArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left,
  90. position.y + contentAreaYOffset, contentWidth, contentAreaHeight);
  91. GUILayout* dropDownEntriesLayout = &dropDownEntriesArea->getLayout().addLayoutY();
  92. for(UINT32 i = 0; i < numVisElements; i++)
  93. {
  94. GUIButton* button = GUIButton::create(*this, mDropDownElements[i], EngineGUI::instance().getSkin().getStyle("DropDownEntryBtn"));
  95. button->onClick.connect(boost::bind(&GUIDropDownBox::entrySelected, this, i));
  96. dropDownEntriesLayout->addElement(button);
  97. mDropDownElementButtons.push_back(button);
  98. }
  99. // Scroll down buttons
  100. UINT32 scrollBtnDownOffset = position.y + contentAreaYOffset + contentAreaHeight;
  101. GUIArea* scrollDownBtnArea = GUIArea::create(*this, position.x + dropDownBoxStyle->margins.left,
  102. scrollBtnDownOffset, contentWidth, scrollButtonDownHeight);
  103. scrollDownBtnArea->setDepth(100);
  104. const GUIElementStyle* scrollDownBtnArrow = EngineGUI::instance().getSkin().getStyle("DropDownScrollDownBtnArrow");
  105. GUIButton* scrollDownBtn = GUIButton::create(*this, GUIContent(L"", scrollDownBtnArrow->normal.texture), EngineGUI::instance().getSkin().getStyle("DropDownScrollDownBtn"));
  106. scrollDownBtnArea->getLayout().addElement(scrollDownBtn);
  107. // Background frame
  108. GUIArea* dropDownBoxArea = GUIArea::create(*this, position.x, position.y, width, totalHeight);
  109. dropDownBoxArea->setDepth(102);
  110. dropDownBoxArea->getLayout().addElement(GUITexture::create(*this, GUIImageScaleMode::ScaleToFit,
  111. EngineGUI::instance().getSkin().getStyle("DropDownBox")));
  112. }
  113. void GUIDropDownBox::scrollDown()
  114. {
  115. INT32 maxNumElements = (INT32)mDropDownElements.size();
  116. INT32 numVisibleElements = (INT32)mDropDownElementButtons.size();
  117. INT32 newIdx = mDropDownStartIdx + numVisibleElements;
  118. INT32 clampedNewIdx = std::min(newIdx, maxNumElements - numVisibleElements);
  119. mDropDownStartIdx = (UINT32)std::min(newIdx, clampedNewIdx);
  120. UINT32 i = mDropDownStartIdx;
  121. for(auto& button : mDropDownElementButtons)
  122. {
  123. button->setContent(GUIContent(mDropDownElements[i]));
  124. i++;
  125. }
  126. }
  127. void GUIDropDownBox::scrollUp()
  128. {
  129. INT32 numVisibleElements = (INT32)mDropDownElementButtons.size();
  130. INT32 newIdx = mDropDownStartIdx - numVisibleElements;
  131. INT32 clampedNewIdx = std::max(newIdx, 0);
  132. mDropDownStartIdx = (UINT32)std::min(newIdx, clampedNewIdx);
  133. UINT32 i = mDropDownStartIdx;
  134. for(auto& button : mDropDownElementButtons)
  135. {
  136. button->setContent(GUIContent(mDropDownElements[i]));
  137. i++;
  138. }
  139. }
  140. void GUIDropDownBox::entrySelected(UINT32 idx)
  141. {
  142. if(!onEntrySelected.empty())
  143. onEntrySelected(mDropDownStartIdx + idx);
  144. }
  145. }