| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2018 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "GUI/BsGUIFloatDistributionField.h"
- #include "GUI/BsGUILayout.h"
- #include "GUI/BsGUILayoutY.h"
- #include "GUI/BsGUILabel.h"
- #include "GUI/BsGUIColor.h"
- #include "GUI/BsGUIFloatField.h"
- #include "GUI/BsGUIVector2Field.h"
- #include "GUI/BsGUIVector3Field.h"
- #include "GUI/BsGUICurvesField.h"
- #include "GUI/BsGUIButton.h"
- #include "GUI/BsGUIContextMenu.h"
- #include "GUI/BsGUISpace.h"
- using namespace std::placeholders;
- namespace bs
- {
- /** Style type name for the internal float fields. */
- static constexpr const char* FLOAT_FIELD_STYLE_TYPE = "FloatField";
- /** Style type name for the internal curve display fields. */
- static constexpr const char* CURVES_FIELD_STYLE_TYPES[4] =
- { "CurvesFieldX", "CurvesFieldY", "CurvesFieldZ", "CurvesFieldW" };
- /** Style type name for the internal drop down button. */
- static constexpr const char* DROP_DOWN_FIELD_STYLE_TYPE = "DropDownButton";
- /** Spacing between two internal elements, in pixels. */
- static constexpr UINT32 ELEM_SPACING = 5;
- namespace impl
- {
- template<class T, class F>
- void addOnConfirmCallback(T* field, F func)
- {
- field->onConfirm.connect([func]() { func(VectorComponent::X); });
- }
- template<class F>
- void addOnConfirmCallback(GUIVector2Field* field, F func)
- {
- field->onConfirm.connect([func](VectorComponent x) { func(x); });
- }
- template<class F>
- void addOnConfirmCallback(GUIVector3Field* field, F func)
- {
- field->onConfirm.connect([func](VectorComponent x) { func(x); });
- }
- template<class T, class F>
- void addOnValueChangedCallback(T* field, F func)
- {
- field->onValueChanged.connect([func](float val) { func(val, VectorComponent::X); });
- }
- template<class F>
- void addOnValueChangedCallback(GUIVector2Field* field, F func)
- {
- field->onComponentChanged.connect([func](float val, VectorComponent x) { func(val, x); });
- }
- template<class F>
- void addOnValueChangedCallback(GUIVector3Field* field, F func)
- {
- field->onComponentChanged.connect([func](float val, VectorComponent x) { func(val, x); });
- }
- template<class T, class F>
- void addOnInputChangedCallback(T* field, F func)
- {
- field->onFocusChanged.connect([func](bool val) { func(val, VectorComponent::X); });
- }
- template<class F>
- void addOnInputChangedCallback(GUIVector2Field* field, F func)
- {
- field->onComponentFocusChanged.connect([func](bool val, VectorComponent x) { func(val, x); });
- }
- template<class F>
- void addOnInputChangedCallback(GUIVector3Field* field, F func)
- {
- field->onComponentFocusChanged.connect([func](bool val, VectorComponent x) { func(val, x); });
- }
- template<class T>
- void setFocus(T* field, VectorComponent component, bool focus)
- {
- field->setFocus(focus);
- }
- template<>
- void setFocus(GUIVector2Field* field, VectorComponent component, bool focus)
- {
- field->setInputFocus(component, focus);
- }
- template<>
- void setFocus(GUIVector3Field* field, VectorComponent component, bool focus)
- {
- field->setInputFocus(component, focus);
- }
- }
- template<class T, class SELF>
- TGUIDistributionField<T, SELF>::TGUIDistributionField(const PrivatelyConstruct& dummy, const GUIContent& labelContent,
- UINT32 labelWidth, const String& style, const GUIDimensions& dimensions, bool withLabel)
- : TGUIField<SELF>(dummy, labelContent, labelWidth, style, dimensions, withLabel)
- {
- mContextMenu = bs_shared_ptr_new<GUIContextMenu>();
- mContextMenu->addMenuItem("Constant", [this]()
- {
- mValue = TDistribution<T>(mMinConstant);
- mPropertyType = PDT_Constant;
- rebuild();
- }, 50);
- mContextMenu->addMenuItem("Range", [this]()
- {
- mValue = TDistribution<T>(mMinConstant, mMaxConstant);
- mPropertyType = PDT_RandomRange;
- rebuild();
- }, 40);
- mContextMenu->addMenuItem("Curve", [this]()
- {
- TAnimationCurve<T> combinedCurve;
- AnimationUtility::combineCurve<T>(mMinCurve, combinedCurve);
- mValue = TDistribution<T>(combinedCurve);
- mPropertyType = PDT_Curve;
- rebuild();
- }, 30);
- mContextMenu->addMenuItem("Curve range", [this]()
- {
- TAnimationCurve<T> combinedCurveMin;
- AnimationUtility::combineCurve<T>(mMinCurve, combinedCurveMin);
- TAnimationCurve<T> combinedCurveMax;
- AnimationUtility::combineCurve<T>(mMaxCurve, combinedCurveMax);
- mValue = TDistribution<T>(combinedCurveMin, combinedCurveMax);
- mPropertyType = PDT_RandomCurveRange;
- rebuild();
- }, 20);
- rebuild();
- }
- template<class T, class SELF>
- void TGUIDistributionField<T, SELF>::setValue(const TDistribution<T>& value)
- {
- if(mValue == value)
- return;
- mValue = value;
- switch (mPropertyType)
- {
- default:
- case PDT_Constant:
- mMinConstant = mValue.getMinConstant();
- mMaxConstant = mMinConstant;
- for(UINT32 i = 0; i < NumComponents; i++)
- {
- mMinCurve[i] = TAnimationCurve<float>({
- { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
- { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
- mMaxCurve[i] = TAnimationCurve<float>({
- { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
- { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
- }
- mMinInput->setValue(mMinConstant);
- break;
- case PDT_RandomRange:
- mMinConstant = mValue.getMinConstant();
- mMaxConstant = mValue.getMaxConstant();
- for(UINT32 i = 0; i < NumComponents; i++)
- {
- mMinCurve[i] = TAnimationCurve<float>({
- { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
- { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
- mMaxCurve[i] = TAnimationCurve<float>({
- { TCurveProperties<T>::getComponent(mMaxConstant, i), 0.0f, 0.0f, 0.0f},
- { TCurveProperties<T>::getComponent(mMaxConstant, i), 0.0f, 0.0f, 1.0f} });
- }
- mMinInput->setValue(mMinConstant);
- mMaxInput->setValue(mMaxConstant);
- break;
- case PDT_Curve:
- AnimationUtility::splitCurve(mValue.getMinCurve(), mMinCurve);
- AnimationUtility::splitCurve(mValue.getMinCurve(), mMaxCurve);
- for(UINT32 i = 0; i < NumComponents; i++)
- {
- TCurveProperties<T>::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f));
- TCurveProperties<T>::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f));
- mCurveDisplay[i]->setCurve(mMinCurve[i]);
- }
- break;
- case PDT_RandomCurveRange:
- AnimationUtility::splitCurve(mValue.getMinCurve(), mMinCurve);
- AnimationUtility::splitCurve(mValue.getMaxCurve(), mMaxCurve);
- for(UINT32 i = 0; i < NumComponents; i++)
- {
- TCurveProperties<T>::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f));
- TCurveProperties<T>::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f));
- mCurveDisplay[i]->setCurveRange(mMinCurve[i], mMaxCurve[i]);
- }
- break;
- }
- }
- template<class T, class SELF>
- bool TGUIDistributionField<T, SELF>::hasInputFocus() const
- {
- if(mMinInput && mMinInput->hasInputFocus())
- return true;
- if(mMaxInput && mMaxInput->hasInputFocus())
- return true;
- return false;
- }
- template <class T, class SELF>
- void TGUIDistributionField<T, SELF>::setInputFocus(RangeComponent rangeComponent, VectorComponent vectorComponent, bool focus)
- {
- switch(mPropertyType)
- {
- case PDT_Constant:
- case PDT_RandomRange:
- if(rangeComponent == RangeComponent::Min)
- impl::setFocus(mMinInput, vectorComponent, focus);
- else
- impl::setFocus(mMaxInput, vectorComponent, focus);
- break;
- case PDT_Curve:
- case PDT_RandomCurveRange:
- {
- for (UINT32 i = 0; i < NumComponents; i++)
- {
- if ((VectorComponent)i == vectorComponent && mCurveDisplay[i])
- mCurveDisplay[i]->setFocus(focus);
- }
- }
- break;
- }
-
- }
- template<class T, class SELF>
- void TGUIDistributionField<T, SELF>::setTint(const Color& color)
- {
- mDropDownButton->setTint(color);
- if (this->mLabel)
- this->mLabel->setTint(color);
- if(mMinInput)
- mMinInput->setTint(color);
- if(mMaxInput)
- mMaxInput->setTint(color);
- for(auto& entry : mLabels)
- {
- if(entry)
- entry->setTint(color);
- }
- for(int i = 0; i < NumComponents; i++)
- {
- if(mCurveDisplay[i])
- mCurveDisplay[i]->setTint(color);
- }
- }
- template<class T, class SELF>
- Vector2I TGUIDistributionField<T, SELF>::_getOptimalSize() const
- {
- Vector2I optimalsize = Vector2I::ZERO;
- if (mMinInput)
- {
- Vector2I elemOptimal = mMinInput->_calculateLayoutSizeRange().optimal;
- optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
- optimalsize.y += elemOptimal.y;
- }
- if (mMaxInput)
- {
- Vector2I elemOptimal = mMaxInput->_calculateLayoutSizeRange().optimal;
- optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
- optimalsize.y += elemOptimal.y;
- optimalsize.y += ELEM_SPACING;
- }
- for (auto& entry : mLabels)
- {
- if (!entry)
- continue;
- Vector2I elemOptimal = entry->_calculateLayoutSizeRange().optimal;
- optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
- optimalsize.y += elemOptimal.y;
- }
- for (UINT32 i = 0; i < NumComponents; i++)
- {
- if (mCurveDisplay[i])
- {
- Vector2I elemOptimal = mCurveDisplay[i]->_calculateLayoutSizeRange().optimal;
- optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
- optimalsize.y += elemOptimal.y;
- if (i != 0)
- optimalsize.y += ELEM_SPACING;
- }
- }
-
- Vector2I dropDownSize = mDropDownButton->_calculateLayoutSizeRange().optimal;
- optimalsize.x += dropDownSize.x;
- optimalsize.y = std::max(optimalsize.y, dropDownSize.y);
- if (this->mLabel)
- {
- Vector2I elemOptimal = this->mLabel->_calculateLayoutSizeRange().optimal;
- optimalsize.x += elemOptimal.x;
- optimalsize.y = std::max(optimalsize.y, elemOptimal.y);
- }
- return optimalsize;
- }
- template<class T, class SELF>
- void TGUIDistributionField<T, SELF>::styleUpdated()
- {
- mDropDownButton->setStyle(this->getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE));
- if (this->mLabel)
- this->mLabel->setStyle(this->getSubStyleName(this->getLabelStyleType()));
- if (mMinInput)
- mMinInput->setStyle(this->getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
- if (mMaxInput)
- mMaxInput->setStyle(this->getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
- for (int i = 0; i < NumComponents; i++)
- {
- if (mCurveDisplay[i])
- mCurveDisplay[i]->setStyle(this->getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
- }
- }
- template<class T, class SELF>
- void TGUIDistributionField<T, SELF>::rebuild()
- {
- constexpr const char* COMP_NAMES[] = { "X", "Y", "Z", "W" };
- constexpr UINT32 ELEMENT_LABEL_WIDTH = 15;
- if(this->mLabel)
- this->mLayout->removeElement(this->mLabel);
- this->mLayout->clear();
- this->mLayout->addElement(this->mLabel);
- GUILayout* valueLayout = this->mLayout->template addNewElement<GUILayoutY>();
- switch (mValue.getType())
- {
- default:
- case PDT_Constant:
- mMinInput = GUIConstantType::create(GUIOptions(), this->getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
- mMaxInput = nullptr;
- mLabels = { nullptr, nullptr };
- for(int i = 0; i < NumComponents; i++)
- mCurveDisplay[i] = nullptr;
- mMinInput->setValue(mMinConstant);
- impl::addOnValueChangedCallback(mMinInput, [this](float value, VectorComponent component)
- {
- mMinConstant = mMinInput->getValue();
- mValue = TDistribution<T>(mMinConstant);
- onConstantModified(RangeComponent::Min, component);
- });
- impl::addOnConfirmCallback(mMinInput, [this](VectorComponent component)
- {
- onConstantConfirmed(RangeComponent::Min, component);
- });
- impl::addOnInputChangedCallback(mMinInput, [this](bool focus, VectorComponent component)
- {
- onConstantFocusChanged(focus, RangeComponent::Min, component);
- });
- valueLayout->addElement(mMinInput);
- break;
- case PDT_RandomRange:
- mMinInput = GUIConstantType::create(GUIOptions(), this->getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
- mMaxInput = GUIConstantType::create(GUIOptions(), this->getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
- for(int i = 0; i < NumComponents; i++)
- mCurveDisplay[i] = nullptr;
- mMinInput->setValue(mMinConstant);
- impl::addOnValueChangedCallback(mMinInput, [this](float value, VectorComponent component)
- {
- mMinConstant = mMinInput->getValue();
- mValue = TDistribution<T>(mMinConstant, mMaxConstant);
- onConstantModified(RangeComponent::Min, component);
- });
- impl::addOnConfirmCallback(mMinInput, [this](VectorComponent component)
- {
- onConstantConfirmed(RangeComponent::Min, component);
- });
- impl::addOnInputChangedCallback(mMinInput, [this](bool focus, VectorComponent component)
- {
- onConstantFocusChanged(focus, RangeComponent::Min, component);
- });
- mMaxInput->setValue(mMaxConstant);
- impl::addOnValueChangedCallback(mMaxInput, [this](float value, VectorComponent component)
- {
- mMaxConstant = mMaxInput->getValue();
- mValue = TDistribution<T>(mMinConstant, mMaxConstant);
- onConstantModified(RangeComponent::Max, component);
- });
- impl::addOnConfirmCallback(mMaxInput, [this](VectorComponent component)
- {
- onConstantConfirmed(RangeComponent::Max, component);
- });
- impl::addOnInputChangedCallback(mMaxInput, [this](bool focus, VectorComponent component)
- {
- onConstantFocusChanged(focus, RangeComponent::Max, component);
- });
- mLabels[0] = valueLayout->addNewElement<GUILabel>(HString("Min."), "HeaderLight");
- valueLayout->addElement(mMinInput);
- valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
- mLabels[1] = valueLayout->addNewElement<GUILabel>(HString("Max."), "HeaderLight");
- valueLayout->addElement(mMaxInput);
- break;
- case PDT_Curve:
- mMinInput = nullptr;
- mMaxInput = nullptr;
- mLabels = { nullptr, nullptr };
- for(int i = 0; i < NumComponents; i++)
- {
- if(NumComponents > 1)
- {
- mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers, HString(COMP_NAMES[i]),
- ELEMENT_LABEL_WIDTH, this->getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
- }
- else
- {
- mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers,
- this->getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
- }
- mCurveDisplay[i]->setCurve(mMinCurve[i]);
- mCurveDisplay[i]->setPadding(3);
- mCurveDisplay[i]->centerAndZoom();
- mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked((VectorComponent)i); });
- if(i != 0)
- valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
- valueLayout->addElement(mCurveDisplay[i]);
- }
- break;
- case PDT_RandomCurveRange:
- mMinInput = nullptr;
- mMaxInput = nullptr;
- mLabels = { nullptr, nullptr };
- for(int i = 0; i < NumComponents; i++)
- {
- if(NumComponents > 1)
- {
- mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers, HString(COMP_NAMES[i]),
- ELEMENT_LABEL_WIDTH, this->getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
- }
- else
- {
- mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers,
- this->getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
- }
- mCurveDisplay[i]->setCurveRange(mMinCurve[i], mMaxCurve[i]);
- mCurveDisplay[i]->setPadding(3);
- mCurveDisplay[i]->centerAndZoom();
- mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked((VectorComponent)i); });
- if(i != 0)
- valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
- valueLayout->addElement(mCurveDisplay[i]);
- }
- break;
- }
- mDropDownButton = GUIButton::create(HString::dummy(), this->getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE));
- mDropDownButton->onClick.connect([this]()
- {
- const Rect2I bounds = mDropDownButton->getBounds(this->mParentWidget->getPanel());
- const Vector2I center(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
- mContextMenu->open(center, *(this->mParentWidget));
- });
- this->mLayout->template addNewElement<GUIFixedSpace>(10);
- this->mLayout->addElement(mDropDownButton);
- }
- template class BS_ED_EXPORT TGUIDistributionField<float, GUIFloatDistributionField>;
- template class BS_ED_EXPORT TGUIDistributionField<Vector2, GUIVector2DistributionField>;
- template class BS_ED_EXPORT TGUIDistributionField<Vector3, GUIVector3DistributionField>;
- const String& GUIFloatDistributionField::getGUITypeName()
- {
- static String typeName = "GUIFloatDistributionField";
- return typeName;
- }
- const String& GUIVector2DistributionField::getGUITypeName()
- {
- static String typeName = "GUIVector2DistributionField";
- return typeName;
- }
- const String& GUIVector3DistributionField::getGUITypeName()
- {
- static String typeName = "GUIVector3DistributionField";
- return typeName;
- }
- }
|