//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2018 Marko Pintera (marko.pintera@gmail.com). 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/BsGUICurves.h" #include "GUI/BsGUIButton.h" #include "GUI/BsGUIContextMenu.h" #include "GUI/BsGUISpace.h" using namespace std::placeholders; namespace bs { namespace impl { template bool isVertical() { return true; } template<> bool isVertical() { return false; } } template TGUIDistributionField::TGUIDistributionField(const PrivatelyConstruct& dummy, const GUIContent& labelContent, UINT32 labelWidth, const String& style, const GUIDimensions& dimensions, bool withLabel) : TGUIField(dummy, labelContent, labelWidth, style, dimensions, withLabel) { mContextMenu = bs_shared_ptr_new(); mContextMenu->addMenuItem("Constant", [this]() { mValue = TDistribution(mMinConstant); mPropertyType = PDT_Constant; rebuild(); }, 50); mContextMenu->addMenuItem("Range", [this]() { mValue = TDistribution(mMinConstant, mMaxConstant); mPropertyType = PDT_RandomRange; rebuild(); }, 40); mContextMenu->addMenuItem("Curve", [this]() { TAnimationCurve combinedCurve; AnimationUtility::combineCurve(mMinCurve, combinedCurve); mValue = TDistribution(combinedCurve); mPropertyType = PDT_Curve; rebuild(); }, 30); mContextMenu->addMenuItem("Curve range", [this]() { TAnimationCurve combinedCurveMin; AnimationUtility::combineCurve(mMinCurve, combinedCurveMin); TAnimationCurve combinedCurveMax; AnimationUtility::combineCurve(mMaxCurve, combinedCurveMax); mValue = TDistribution(combinedCurveMin, combinedCurveMax); mPropertyType = PDT_RandomCurveRange; rebuild(); }, 20); rebuild(); } template void TGUIDistributionField::setValue(const TDistribution& value) { mValue = value; switch (mPropertyType) { default: case PDT_Constant: mMinConstant = mValue.getMinConstant(); mMaxConstant = mMinConstant; for(UINT32 i = 0; i < NumComponents; i++) { mMinCurve[i] = TAnimationCurve({ { TCurveProperties::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f}, { TCurveProperties::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} }); mMaxCurve[i] = TAnimationCurve({ { TCurveProperties::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f}, { TCurveProperties::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({ { TCurveProperties::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f}, { TCurveProperties::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} }); mMaxCurve[i] = TAnimationCurve({ { TCurveProperties::getComponent(mMaxConstant, i), 0.0f, 0.0f, 0.0f}, { TCurveProperties::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::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f)); TCurveProperties::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f)); mCurveDisplay[i]->setCurves({ CurveDrawInfo(mMinCurve[i], Color::BansheeOrange) }); } break; case PDT_RandomCurveRange: AnimationUtility::splitCurve(mValue.getMinCurve(), mMinCurve); AnimationUtility::splitCurve(mValue.getMaxCurve(), mMaxCurve); for(UINT32 i = 0; i < NumComponents; i++) { TCurveProperties::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f)); TCurveProperties::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f)); mCurveDisplay[i]->setCurves( { CurveDrawInfo(mMinCurve[i], Color::BansheeOrange), CurveDrawInfo(mMaxCurve[i], Color::Red) }); } break; } } template bool TGUIDistributionField::hasInputFocus() const { if(mMinInput && mMinInput->hasInputFocus()) return true; if(mMaxInput && mMaxInput->hasInputFocus()) return true; return false; } template void TGUIDistributionField::setTint(const Color& color) { mDropDownButton->setTint(color); if (mLabel) mLabel->setTint(color); if(mMinInput) mMinInput->setTint(color); if(mMaxInput) mMaxInput->setTint(color); for(int i = 0; i < NumComponents; i++) { if(mCurveDisplay[i]) mCurveDisplay[i]->setTint(color); } } template Vector2I TGUIDistributionField::_getOptimalSize() const { Vector2I optimalsize = Vector2I::ZERO; if(impl::isVertical()) { 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; } 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; } } } else { if (mMinInput) { Vector2I elemOptimal = mMinInput->_calculateLayoutSizeRange().optimal; optimalsize.x += elemOptimal.x; optimalsize.y = std::max(optimalsize.y, elemOptimal.y); } if (mMaxInput) { Vector2I elemOptimal = mMaxInput->_calculateLayoutSizeRange().optimal; optimalsize.x += elemOptimal.x; optimalsize.y = std::max(optimalsize.y, elemOptimal.y); } for(UINT32 i = 0; i < NumComponents; i++) { if (mCurveDisplay[i]) { Vector2I elemOptimal = mCurveDisplay[i]->_calculateLayoutSizeRange().optimal; optimalsize.x += elemOptimal.x; optimalsize.y = std::max(optimalsize.y, elemOptimal.y); } } } Vector2I dropDownSize = mDropDownButton->_calculateLayoutSizeRange().optimal; optimalsize.x += dropDownSize.x; optimalsize.y = std::max(optimalsize.y, dropDownSize.y); if (mLabel) { Vector2I elemOptimal = mLabel->_calculateLayoutSizeRange().optimal; optimalsize.x += elemOptimal.x; optimalsize.y = std::max(optimalsize.y, elemOptimal.y); } return optimalsize; } template void TGUIDistributionField::styleUpdated() { mDropDownButton->setStyle(getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE)); if (mLabel) mLabel->setStyle(getSubStyleName(getLabelStyleType())); if (mMinInput) mMinInput->setStyle(getSubStyleName(FLOAT_FIELD_STYLE_TYPE)); if (mMaxInput) mMaxInput->setStyle(getSubStyleName(FLOAT_FIELD_STYLE_TYPE)); for(int i = 0; i < NumComponents; i++) { if (mCurveDisplay[i]) mCurveDisplay[i]->setStyle(getSubStyleName(CURVES_FIELD_STYLE_TYPE)); } } template void TGUIDistributionField::rebuild() { if(mLabel) mLayout->removeElement(mLabel); mLayout->clear(); mLayout->addElement(mLabel); GUILayout* valueLayout; if(impl::isVertical()) valueLayout = mLayout->addNewElement(); else valueLayout = mLayout; switch (mValue.getType()) { default: case PDT_Constant: mMinInput = GUIConstantType::create(GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE)); mMaxInput = nullptr; for(int i = 0; i < NumComponents; i++) mCurveDisplay[i] = nullptr; mMinInput->setValue(mMinConstant); mMinInput->onValueChanged.connect([this](T value) { mMinConstant = value; mValue = TDistribution(value); onConstantModified(); }); mMinInput->onConfirm.connect([this]() { onConstantConfirmed(); }); valueLayout->addElement(mMinInput); break; case PDT_RandomRange: mMinInput = GUIConstantType::create(HString("Min."), 40, GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE)); mMaxInput = GUIConstantType::create(HString("Max."), 40, GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE)); for(int i = 0; i < NumComponents; i++) mCurveDisplay[i] = nullptr; mMinInput->setValue(mMinConstant); mMinInput->onValueChanged.connect([this](T value) { mMinConstant = value; mValue = TDistribution(value, mMaxConstant); onConstantModified(); }); mMinInput->onConfirm.connect([this]() { onConstantConfirmed(); }); mMaxInput->setValue(mMaxConstant); mMaxInput->onValueChanged.connect([this](T value) { mMaxConstant = value; mValue = TDistribution(mMinConstant, value); onConstantModified(); }); mMaxInput->onConfirm.connect([this]() { onConstantConfirmed(); }); valueLayout->addElement(mMinInput); valueLayout->addElement(mMaxInput); break; case PDT_Curve: mMinInput = nullptr; mMaxInput = nullptr; for(int i = 0; i < NumComponents; i++) { mCurveDisplay[i] = GUICurves::create(CurveDrawOption::DrawMarkers, getSubStyleName(CURVES_FIELD_STYLE_TYPE)); mCurveDisplay[i]->setCurves({ CurveDrawInfo(mMinCurve[i], Color::BansheeOrange) }); mCurveDisplay[i]->setPadding(3); mCurveDisplay[i]->centerAndZoom(); mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked(i); }); valueLayout->addElement(mCurveDisplay[i]); } break; case PDT_RandomCurveRange: mMinInput = nullptr; mMaxInput = nullptr; for(int i = 0; i < NumComponents; i++) { mCurveDisplay[i] = GUICurves::create(CurveDrawOption::DrawMarkers | CurveDrawOption::DrawRange, getSubStyleName(CURVES_FIELD_STYLE_TYPE)); mCurveDisplay[i]->setCurves( { CurveDrawInfo(mMinCurve[i], Color::BansheeOrange), CurveDrawInfo(mMaxCurve[i], Color::Red) }); mCurveDisplay[i]->setPadding(3); mCurveDisplay[i]->centerAndZoom(); mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked(i); }); valueLayout->addElement(mCurveDisplay[i]); } break; } mDropDownButton = GUIButton::create(HString::dummy(), getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE)); mDropDownButton->onClick.connect([this]() { const Rect2I bounds = mDropDownButton->getBounds(mParentWidget->getPanel()); const Vector2I center(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2); mContextMenu->open(center, *mParentWidget); }); mLayout->addNewElement(10); mLayout->addElement(mDropDownButton); } template class BS_ED_EXPORT TGUIDistributionField; template class BS_ED_EXPORT TGUIDistributionField; template class BS_ED_EXPORT TGUIDistributionField; 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; } }