| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "BsGUIDropDownContent.h"
- #include "BsGUITexture.h"
- #include "BsGUIButton.h"
- #include "BsGUILabel.h"
- #include "BsGUIWidget.h"
- #include "BsGUIToggle.h"
- #include "BsGUISkin.h"
- #include "BsGUIMouseEvent.h"
- #include "BsGUICommandEvent.h"
- using namespace std::placeholders;
- namespace BansheeEngine
- {
- const String GUIDropDownContent::ENTRY_TOGGLE_STYLE_TYPE = "DropDownEntryToggleBtn";
- const String GUIDropDownContent::ENTRY_STYLE_TYPE = "DropDownEntryBtn";
- const String GUIDropDownContent::ENTRY_EXP_STYLE_TYPE = "DropDownEntryExpBtn";
- const String GUIDropDownContent::SEPARATOR_STYLE_TYPE = "DropDownSeparator";
- GUIDropDownContent::GUIDropDownContent(GUIDropDownMenu::DropDownSubMenu* parent, const GUIDropDownData& dropDownData,
- const String& style, const GUIDimensions& dimensions)
- :GUIElementContainer(dimensions, style), mDropDownData(dropDownData), mKeyboardFocus(true),
- mStates(dropDownData.states), mSelectedIdx(UINT_MAX), mRangeStart(0), mRangeEnd(0), mParent(parent),
- mIsToggle(parent->getType() == GUIDropDownType::MultiListBox)
- {
- }
- GUIDropDownContent::~GUIDropDownContent()
- {
- }
- GUIDropDownContent* GUIDropDownContent::create(GUIDropDownMenu::DropDownSubMenu* parent,
- const GUIDropDownData& dropDownData, const String& style)
- {
- const String* curStyle = &style;
- if (*curStyle == StringUtil::BLANK)
- curStyle = &GUIDropDownContent::getGUITypeName();
- return new (bs_alloc<GUIDropDownContent>()) GUIDropDownContent(parent, dropDownData, *curStyle, GUIDimensions::create());
- }
- GUIDropDownContent* GUIDropDownContent::create(GUIDropDownMenu::DropDownSubMenu* parent,
- const GUIDropDownData& dropDownData, const GUIOptions& options,
- const String& style)
- {
- const String* curStyle = &style;
- if (*curStyle == StringUtil::BLANK)
- curStyle = &GUIDropDownContent::getGUITypeName();
- return new (bs_alloc<GUIDropDownContent>()) GUIDropDownContent(parent, dropDownData, *curStyle, GUIDimensions::create(options));
- }
- void GUIDropDownContent::styleUpdated()
- {
- for (auto& visElem : mVisibleElements)
- {
- GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
- if (element.isSeparator())
- visElem.separator->setStyle(getSubStyleName(SEPARATOR_STYLE_TYPE));
- else if (element.isSubMenu())
- visElem.button->setStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE));
- else
- {
- if (mIsToggle)
- visElem.button->setStyle(getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE));
- else
- visElem.button->setStyle(getSubStyleName(ENTRY_STYLE_TYPE));
- }
- }
- }
- void GUIDropDownContent::setRange(UINT32 start, UINT32 end)
- {
- std::function<void(UINT32, UINT32)> onHover =
- [&](UINT32 idx, UINT32 visIdx)
- {
- setSelected(visIdx);
- mParent->elementSelected(idx);
- };
- std::function<void(UINT32, UINT32)> onClick =
- [&](UINT32 idx, UINT32 visIdx)
- {
- setSelected(visIdx);
- if (mIsToggle)
- mStates[idx] = !mStates[idx];
- mParent->elementActivated(idx, mVisibleElements[visIdx].button->_getLayoutData().area);
- };
- // Remove all elements
- while (_getNumChildren() > 0)
- {
- GUIElementBase* child = _getChild(_getNumChildren() - 1);
- assert(child->_getType() == GUIElementBase::Type::Element);
- GUIElement::destroy(static_cast<GUIElement*>(child));
- }
- mRangeStart = start;
- mRangeEnd = end;
-
- UINT32 range = end - start;
- if (mSelectedIdx != UINT_MAX && mSelectedIdx >= range)
- mSelectedIdx = UINT_MAX;
- mVisibleElements.clear();
- UINT32 curVisIdx = 0;
- for (UINT32 i = start; i < end; i++)
- {
- mVisibleElements.push_back(VisibleElement());
- VisibleElement& visElem = mVisibleElements.back();
- visElem.idx = i;
- GUIDropDownDataEntry& element = mDropDownData.entries[i];
- if (element.isSeparator())
- {
- visElem.separator = GUITexture::create(GUIImageScaleMode::StretchToFit, getSubStyleName(SEPARATOR_STYLE_TYPE));
- _registerChildElement(visElem.separator);
- }
- else if (element.isSubMenu())
- {
- visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_EXP_STYLE_TYPE));
- visElem.button->onHover.connect(std::bind(onClick, i, curVisIdx));
- _registerChildElement(visElem.button);
- }
- else
- {
- if (mIsToggle)
- {
- GUIToggle* toggle = GUIToggle::create(getElementLocalizedName(i), getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE));
- if (mStates[i])
- toggle->toggleOn();
- visElem.button = toggle;
- }
- else
- visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_STYLE_TYPE));
- visElem.button->onHover.connect(std::bind(onHover, i, curVisIdx));
- visElem.button->onClick.connect(std::bind(onClick, i, curVisIdx));
- _registerChildElement(visElem.button);
- const WString& shortcutTag = element.getShortcutTag();
- if (!shortcutTag.empty())
- {
- visElem.shortcutLabel = GUILabel::create(HString(shortcutTag), "RightAlignedLabel");
- _registerChildElement(visElem.shortcutLabel);
- }
- }
- curVisIdx++;
- }
- _markLayoutAsDirty();
- }
- UINT32 GUIDropDownContent::getElementHeight(UINT32 idx) const
- {
- if (_getParentWidget() == nullptr)
- return 14; // Arbitrary
- if (mDropDownData.entries[idx].isSeparator())
- return _getParentWidget()->getSkin().getStyle(getSubStyleName(SEPARATOR_STYLE_TYPE))->height;
- else if (mDropDownData.entries[idx].isSubMenu())
- return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE))->height;
- else
- {
- if (mIsToggle)
- return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE))->height;
- else
- return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_STYLE_TYPE))->height;
- }
- }
- HString GUIDropDownContent::getElementLocalizedName(UINT32 idx) const
- {
- const WString& label = mDropDownData.entries[idx].getLabel();
- auto findLocalizedName = mDropDownData.localizedNames.find(label);
- if (findLocalizedName != mDropDownData.localizedNames.end())
- return findLocalizedName->second;
- else
- return HString(label);
- }
- void GUIDropDownContent::setKeyboardFocus(bool focus)
- {
- mKeyboardFocus = focus;
- setFocus(focus);
- }
- bool GUIDropDownContent::_commandEvent(const GUICommandEvent& ev)
- {
- bool baseReturn = GUIElementContainer::_commandEvent(ev);
- if (!mKeyboardFocus)
- return baseReturn;
- UINT32 maxElemIdx = (UINT32)mDropDownData.entries.size();
- switch (ev.getType())
- {
- case GUICommandEventType::MoveDown:
- if (mSelectedIdx == UINT_MAX)
- selectNext(0);
- else
- selectNext(mVisibleElements[mSelectedIdx].idx + 1);
- return true;
- case GUICommandEventType::MoveUp:
- if (mSelectedIdx == UINT_MAX)
- selectNext(0);
- else
- selectPrevious(mVisibleElements[mSelectedIdx].idx - 1);
- return true;
- case GUICommandEventType::Escape:
- case GUICommandEventType::MoveLeft:
- mParent->close();
- return true;
- case GUICommandEventType::MoveRight:
- {
- if (mSelectedIdx == UINT_MAX)
- selectNext(0);
- else
- {
- GUIDropDownDataEntry& entry = mDropDownData.entries[mVisibleElements[mSelectedIdx].idx];
- if (entry.isSubMenu())
- mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getLayoutData().area);
- }
- }
- return true;
- case GUICommandEventType::Confirm:
- if (mSelectedIdx == UINT_MAX)
- selectNext(0);
- else
- {
- if (mIsToggle)
- mVisibleElements[mSelectedIdx].button->_setOn(!mVisibleElements[mSelectedIdx].button->_isOn());
- mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getLayoutData().area);
- }
- return true;
- }
- return baseReturn;
- }
- bool GUIDropDownContent::_mouseEvent(const GUIMouseEvent& ev)
- {
- if (ev.getType() == GUIMouseEventType::MouseWheelScroll)
- {
- if (ev.getWheelScrollAmount() < 0)
- mParent->scrollDown();
- else
- mParent->scrollUp();
- return true;
- }
- return false;
- }
- void GUIDropDownContent::setSelected(UINT32 idx)
- {
- if (mSelectedIdx != UINT_MAX)
- {
- if (mVisibleElements[mSelectedIdx].button->_isOn())
- mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::NormalOn);
- else
- mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::Normal);
- }
- mSelectedIdx = idx;
- if (mVisibleElements[mSelectedIdx].button->_isOn())
- mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::HoverOn);
- else
- mVisibleElements[mSelectedIdx].button->_setState(GUIElementState::Hover);
- mParent->elementSelected(mVisibleElements[mSelectedIdx].idx);
- }
- void GUIDropDownContent::selectNext(UINT32 startIdx)
- {
- UINT32 numElements = (UINT32)mDropDownData.entries.size();
- bool gotNextIndex = false;
- UINT32 nextIdx = startIdx;
- for (UINT32 i = 0; i < numElements; i++)
- {
- if (nextIdx >= numElements)
- nextIdx = 0; // Wrap around
- GUIDropDownDataEntry& entry = mDropDownData.entries[nextIdx];
- if (!entry.isSeparator())
- {
- gotNextIndex = true;
- break;
- }
- nextIdx++;
- }
- if (gotNextIndex)
- {
- while (nextIdx < mRangeStart || nextIdx >= mRangeEnd)
- mParent->scrollDown();
- UINT32 visIdx = 0;
- for (auto& visElem : mVisibleElements)
- {
- if (visElem.idx == nextIdx)
- {
- setSelected(visIdx);
- break;
- }
- visIdx++;
- }
- }
- }
- void GUIDropDownContent::selectPrevious(UINT32 startIdx)
- {
- UINT32 numElements = (UINT32)mDropDownData.entries.size();
- bool gotNextIndex = false;
- INT32 prevIdx = (INT32)startIdx;
- for (UINT32 i = 0; i < numElements; i++)
- {
- if (prevIdx < 0)
- prevIdx = numElements - 1; // Wrap around
- GUIDropDownDataEntry& entry = mDropDownData.entries[prevIdx];
- if (!entry.isSeparator())
- {
- gotNextIndex = true;
- break;
- }
- prevIdx--;
- }
- if (gotNextIndex)
- {
- while (prevIdx < (INT32)mRangeStart || prevIdx >= (INT32)mRangeEnd)
- mParent->scrollUp();
- UINT32 visIdx = 0;
- for (auto& visElem : mVisibleElements)
- {
- if (visElem.idx == prevIdx)
- {
- setSelected(visIdx);
- break;
- }
- visIdx++;
- }
- }
- }
- Vector2I GUIDropDownContent::_getOptimalSize() const
- {
- Vector2I optimalSize;
- for (auto& visElem : mVisibleElements)
- {
- const GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
- optimalSize.y += (INT32)getElementHeight(visElem.idx);
- if (element.isSeparator())
- optimalSize.x = std::max(optimalSize.x, visElem.separator->_getOptimalSize().x);
- else
- optimalSize.x = std::max(optimalSize.x, visElem.button->_getOptimalSize().x);
- }
- return optimalSize;
- }
- void GUIDropDownContent::_updateLayoutInternal(const GUILayoutData& data)
- {
- GUILayoutData childData = data;
- INT32 yOffset = data.area.y;
- for (auto& visElem : mVisibleElements)
- {
- const GUIDropDownDataEntry& element = mDropDownData.entries[visElem.idx];
- GUIElement* guiMainElement = nullptr;
- if (element.isSeparator())
- guiMainElement = visElem.separator;
- else
- guiMainElement = visElem.button;
- childData.area.y = yOffset;
- childData.area.height = getElementHeight(visElem.idx);
- yOffset += childData.area.height;
- guiMainElement->_setLayoutData(childData);
- // Shortcut label
- GUILabel* shortcutLabel = visElem.shortcutLabel;
- if (shortcutLabel != nullptr)
- shortcutLabel->_setLayoutData(childData);
- }
- }
- const String& GUIDropDownContent::getGUITypeName()
- {
- static String typeName = "GUIDropDownContent";
- return typeName;
- }
- }
|