BsGUIFloatDistributionField.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2018 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "GUI/BsGUIFloatDistributionField.h"
  4. #include "GUI/BsGUILayout.h"
  5. #include "GUI/BsGUILayoutY.h"
  6. #include "GUI/BsGUILabel.h"
  7. #include "GUI/BsGUIColor.h"
  8. #include "GUI/BsGUIFloatField.h"
  9. #include "GUI/BsGUIVector2Field.h"
  10. #include "GUI/BsGUIVector3Field.h"
  11. #include "GUI/BsGUICurvesField.h"
  12. #include "GUI/BsGUIButton.h"
  13. #include "GUI/BsGUIContextMenu.h"
  14. #include "GUI/BsGUISpace.h"
  15. using namespace std::placeholders;
  16. namespace bs
  17. {
  18. /** Style type name for the internal float fields. */
  19. static constexpr const char* FLOAT_FIELD_STYLE_TYPE = "FloatField";
  20. /** Style type name for the internal curve display fields. */
  21. static constexpr const char* CURVES_FIELD_STYLE_TYPES[4] =
  22. { "CurvesFieldX", "CurvesFieldY", "CurvesFieldZ", "CurvesFieldW" };
  23. /** Style type name for the internal drop down button. */
  24. static constexpr const char* DROP_DOWN_FIELD_STYLE_TYPE = "DropDownButton";
  25. /** Spacing between two internal elements, in pixels. */
  26. static constexpr UINT32 ELEM_SPACING = 5;
  27. template<class T, class SELF>
  28. TGUIDistributionField<T, SELF>::TGUIDistributionField(const PrivatelyConstruct& dummy, const GUIContent& labelContent,
  29. UINT32 labelWidth, const String& style, const GUIDimensions& dimensions, bool withLabel)
  30. : TGUIField(dummy, labelContent, labelWidth, style, dimensions, withLabel)
  31. {
  32. mContextMenu = bs_shared_ptr_new<GUIContextMenu>();
  33. mContextMenu->addMenuItem("Constant", [this]()
  34. {
  35. mValue = TDistribution<T>(mMinConstant);
  36. mPropertyType = PDT_Constant;
  37. rebuild();
  38. }, 50);
  39. mContextMenu->addMenuItem("Range", [this]()
  40. {
  41. mValue = TDistribution<T>(mMinConstant, mMaxConstant);
  42. mPropertyType = PDT_RandomRange;
  43. rebuild();
  44. }, 40);
  45. mContextMenu->addMenuItem("Curve", [this]()
  46. {
  47. TAnimationCurve<T> combinedCurve;
  48. AnimationUtility::combineCurve<T>(mMinCurve, combinedCurve);
  49. mValue = TDistribution<T>(combinedCurve);
  50. mPropertyType = PDT_Curve;
  51. rebuild();
  52. }, 30);
  53. mContextMenu->addMenuItem("Curve range", [this]()
  54. {
  55. TAnimationCurve<T> combinedCurveMin;
  56. AnimationUtility::combineCurve<T>(mMinCurve, combinedCurveMin);
  57. TAnimationCurve<T> combinedCurveMax;
  58. AnimationUtility::combineCurve<T>(mMaxCurve, combinedCurveMax);
  59. mValue = TDistribution<T>(combinedCurveMin, combinedCurveMax);
  60. mPropertyType = PDT_RandomCurveRange;
  61. rebuild();
  62. }, 20);
  63. rebuild();
  64. }
  65. template<class T, class SELF>
  66. void TGUIDistributionField<T, SELF>::setValue(const TDistribution<T>& value)
  67. {
  68. mValue = value;
  69. switch (mPropertyType)
  70. {
  71. default:
  72. case PDT_Constant:
  73. mMinConstant = mValue.getMinConstant();
  74. mMaxConstant = mMinConstant;
  75. for(UINT32 i = 0; i < NumComponents; i++)
  76. {
  77. mMinCurve[i] = TAnimationCurve<float>({
  78. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
  79. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
  80. mMaxCurve[i] = TAnimationCurve<float>({
  81. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
  82. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
  83. }
  84. mMinInput->setValue(mMinConstant);
  85. break;
  86. case PDT_RandomRange:
  87. mMinConstant = mValue.getMinConstant();
  88. mMaxConstant = mValue.getMaxConstant();
  89. for(UINT32 i = 0; i < NumComponents; i++)
  90. {
  91. mMinCurve[i] = TAnimationCurve<float>({
  92. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
  93. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
  94. mMaxCurve[i] = TAnimationCurve<float>({
  95. { TCurveProperties<T>::getComponent(mMaxConstant, i), 0.0f, 0.0f, 0.0f},
  96. { TCurveProperties<T>::getComponent(mMaxConstant, i), 0.0f, 0.0f, 1.0f} });
  97. }
  98. mMinInput->setValue(mMinConstant);
  99. mMaxInput->setValue(mMaxConstant);
  100. break;
  101. case PDT_Curve:
  102. AnimationUtility::splitCurve(mValue.getMinCurve(), mMinCurve);
  103. AnimationUtility::splitCurve(mValue.getMinCurve(), mMaxCurve);
  104. for(UINT32 i = 0; i < NumComponents; i++)
  105. {
  106. TCurveProperties<T>::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f));
  107. TCurveProperties<T>::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f));
  108. mCurveDisplay[i]->setCurve(mMinCurve[i]);
  109. }
  110. break;
  111. case PDT_RandomCurveRange:
  112. AnimationUtility::splitCurve(mValue.getMinCurve(), mMinCurve);
  113. AnimationUtility::splitCurve(mValue.getMaxCurve(), mMaxCurve);
  114. for(UINT32 i = 0; i < NumComponents; i++)
  115. {
  116. TCurveProperties<T>::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f));
  117. TCurveProperties<T>::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f));
  118. mCurveDisplay[i]->setCurveRange(mMinCurve[i], mMaxCurve[i]);
  119. }
  120. break;
  121. }
  122. }
  123. template<class T, class SELF>
  124. bool TGUIDistributionField<T, SELF>::hasInputFocus() const
  125. {
  126. if(mMinInput && mMinInput->hasInputFocus())
  127. return true;
  128. if(mMaxInput && mMaxInput->hasInputFocus())
  129. return true;
  130. return false;
  131. }
  132. template<class T, class SELF>
  133. void TGUIDistributionField<T, SELF>::setTint(const Color& color)
  134. {
  135. mDropDownButton->setTint(color);
  136. if (mLabel)
  137. mLabel->setTint(color);
  138. if(mMinInput)
  139. mMinInput->setTint(color);
  140. if(mMaxInput)
  141. mMaxInput->setTint(color);
  142. for(auto& entry : mLabels)
  143. {
  144. if(entry)
  145. entry->setTint(color);
  146. }
  147. for(int i = 0; i < NumComponents; i++)
  148. {
  149. if(mCurveDisplay[i])
  150. mCurveDisplay[i]->setTint(color);
  151. }
  152. }
  153. template<class T, class SELF>
  154. Vector2I TGUIDistributionField<T, SELF>::_getOptimalSize() const
  155. {
  156. Vector2I optimalsize = Vector2I::ZERO;
  157. if (mMinInput)
  158. {
  159. Vector2I elemOptimal = mMinInput->_calculateLayoutSizeRange().optimal;
  160. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  161. optimalsize.y += elemOptimal.y;
  162. }
  163. if (mMaxInput)
  164. {
  165. Vector2I elemOptimal = mMaxInput->_calculateLayoutSizeRange().optimal;
  166. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  167. optimalsize.y += elemOptimal.y;
  168. optimalsize.y += ELEM_SPACING;
  169. }
  170. for (auto& entry : mLabels)
  171. {
  172. if (!entry)
  173. continue;
  174. Vector2I elemOptimal = entry->_calculateLayoutSizeRange().optimal;
  175. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  176. optimalsize.y += elemOptimal.y;
  177. }
  178. for (UINT32 i = 0; i < NumComponents; i++)
  179. {
  180. if (mCurveDisplay[i])
  181. {
  182. Vector2I elemOptimal = mCurveDisplay[i]->_calculateLayoutSizeRange().optimal;
  183. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  184. optimalsize.y += elemOptimal.y;
  185. if (i != 0)
  186. optimalsize.y += ELEM_SPACING;
  187. }
  188. }
  189. Vector2I dropDownSize = mDropDownButton->_calculateLayoutSizeRange().optimal;
  190. optimalsize.x += dropDownSize.x;
  191. optimalsize.y = std::max(optimalsize.y, dropDownSize.y);
  192. if (mLabel)
  193. {
  194. Vector2I elemOptimal = mLabel->_calculateLayoutSizeRange().optimal;
  195. optimalsize.x += elemOptimal.x;
  196. optimalsize.y = std::max(optimalsize.y, elemOptimal.y);
  197. }
  198. return optimalsize;
  199. }
  200. template<class T, class SELF>
  201. void TGUIDistributionField<T, SELF>::styleUpdated()
  202. {
  203. mDropDownButton->setStyle(getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE));
  204. if (mLabel)
  205. mLabel->setStyle(getSubStyleName(getLabelStyleType()));
  206. if (mMinInput)
  207. mMinInput->setStyle(getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  208. if (mMaxInput)
  209. mMaxInput->setStyle(getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  210. for (int i = 0; i < NumComponents; i++)
  211. {
  212. if (mCurveDisplay[i])
  213. mCurveDisplay[i]->setStyle(getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  214. }
  215. }
  216. template<class T, class SELF>
  217. void TGUIDistributionField<T, SELF>::rebuild()
  218. {
  219. constexpr const char* COMP_NAMES[] = { "X", "Y", "Z", "W" };
  220. constexpr UINT32 ELEMENT_LABEL_WIDTH = 15;
  221. if(mLabel)
  222. mLayout->removeElement(mLabel);
  223. mLayout->clear();
  224. mLayout->addElement(mLabel);
  225. GUILayout* valueLayout = mLayout->addNewElement<GUILayoutY>();
  226. switch (mValue.getType())
  227. {
  228. default:
  229. case PDT_Constant:
  230. mMinInput = GUIConstantType::create(GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  231. mMaxInput = nullptr;
  232. mLabels = { nullptr, nullptr };
  233. for(int i = 0; i < NumComponents; i++)
  234. mCurveDisplay[i] = nullptr;
  235. mMinInput->setValue(mMinConstant);
  236. mMinInput->onValueChanged.connect([this](T value)
  237. {
  238. mMinConstant = value;
  239. mValue = TDistribution<T>(value);
  240. onConstantModified();
  241. });
  242. mMinInput->onConfirm.connect([this]() { onConstantConfirmed(); });
  243. valueLayout->addElement(mMinInput);
  244. break;
  245. case PDT_RandomRange:
  246. mMinInput = GUIConstantType::create(GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  247. mMaxInput = GUIConstantType::create(GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  248. for(int i = 0; i < NumComponents; i++)
  249. mCurveDisplay[i] = nullptr;
  250. mMinInput->setValue(mMinConstant);
  251. mMinInput->onValueChanged.connect([this](T value)
  252. {
  253. mMinConstant = value;
  254. mValue = TDistribution<T>(value, mMaxConstant);
  255. onConstantModified();
  256. });
  257. mMinInput->onConfirm.connect([this]() { onConstantConfirmed(); });
  258. mMaxInput->setValue(mMaxConstant);
  259. mMaxInput->onValueChanged.connect([this](T value)
  260. {
  261. mMaxConstant = value;
  262. mValue = TDistribution<T>(mMinConstant, value);
  263. onConstantModified();
  264. });
  265. mMaxInput->onConfirm.connect([this]() { onConstantConfirmed(); });
  266. mLabels[0] = valueLayout->addNewElement<GUILabel>(HString("Min."), "HeaderLight");
  267. valueLayout->addElement(mMinInput);
  268. valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
  269. mLabels[1] = valueLayout->addNewElement<GUILabel>(HString("Max."), "HeaderLight");
  270. valueLayout->addElement(mMaxInput);
  271. break;
  272. case PDT_Curve:
  273. mMinInput = nullptr;
  274. mMaxInput = nullptr;
  275. mLabels = { nullptr, nullptr };
  276. for(int i = 0; i < NumComponents; i++)
  277. {
  278. if(NumComponents > 1)
  279. {
  280. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers, HString(COMP_NAMES[i]),
  281. ELEMENT_LABEL_WIDTH, getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  282. }
  283. else
  284. {
  285. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers,
  286. getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  287. }
  288. mCurveDisplay[i]->setCurve(mMinCurve[i]);
  289. mCurveDisplay[i]->setPadding(3);
  290. mCurveDisplay[i]->centerAndZoom();
  291. mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked(i); });
  292. if(i != 0)
  293. valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
  294. valueLayout->addElement(mCurveDisplay[i]);
  295. }
  296. break;
  297. case PDT_RandomCurveRange:
  298. mMinInput = nullptr;
  299. mMaxInput = nullptr;
  300. mLabels = { nullptr, nullptr };
  301. for(int i = 0; i < NumComponents; i++)
  302. {
  303. if(NumComponents > 1)
  304. {
  305. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers, HString(COMP_NAMES[i]),
  306. ELEMENT_LABEL_WIDTH, getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  307. }
  308. else
  309. {
  310. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers,
  311. getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  312. }
  313. mCurveDisplay[i]->setCurveRange(mMinCurve[i], mMaxCurve[i]);
  314. mCurveDisplay[i]->setPadding(3);
  315. mCurveDisplay[i]->centerAndZoom();
  316. mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked(i); });
  317. if(i != 0)
  318. valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
  319. valueLayout->addElement(mCurveDisplay[i]);
  320. }
  321. break;
  322. }
  323. mDropDownButton = GUIButton::create(HString::dummy(), getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE));
  324. mDropDownButton->onClick.connect([this]()
  325. {
  326. const Rect2I bounds = mDropDownButton->getBounds(mParentWidget->getPanel());
  327. const Vector2I center(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
  328. mContextMenu->open(center, *mParentWidget);
  329. });
  330. mLayout->addNewElement<GUIFixedSpace>(10);
  331. mLayout->addElement(mDropDownButton);
  332. }
  333. template class BS_ED_EXPORT TGUIDistributionField<float, GUIFloatDistributionField>;
  334. template class BS_ED_EXPORT TGUIDistributionField<Vector2, GUIVector2DistributionField>;
  335. template class BS_ED_EXPORT TGUIDistributionField<Vector3, GUIVector3DistributionField>;
  336. const String& GUIFloatDistributionField::getGUITypeName()
  337. {
  338. static String typeName = "GUIFloatDistributionField";
  339. return typeName;
  340. }
  341. const String& GUIVector2DistributionField::getGUITypeName()
  342. {
  343. static String typeName = "GUIVector2DistributionField";
  344. return typeName;
  345. }
  346. const String& GUIVector3DistributionField::getGUITypeName()
  347. {
  348. static String typeName = "GUIVector3DistributionField";
  349. return typeName;
  350. }
  351. }