2
0

BsGUIFloatDistributionField.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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. if(mValue == value)
  69. return;
  70. mValue = value;
  71. switch (mPropertyType)
  72. {
  73. default:
  74. case PDT_Constant:
  75. mMinConstant = mValue.getMinConstant();
  76. mMaxConstant = mMinConstant;
  77. for(UINT32 i = 0; i < NumComponents; i++)
  78. {
  79. mMinCurve[i] = TAnimationCurve<float>({
  80. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
  81. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
  82. mMaxCurve[i] = TAnimationCurve<float>({
  83. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
  84. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
  85. }
  86. mMinInput->setValue(mMinConstant);
  87. break;
  88. case PDT_RandomRange:
  89. mMinConstant = mValue.getMinConstant();
  90. mMaxConstant = mValue.getMaxConstant();
  91. for(UINT32 i = 0; i < NumComponents; i++)
  92. {
  93. mMinCurve[i] = TAnimationCurve<float>({
  94. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 0.0f},
  95. { TCurveProperties<T>::getComponent(mMinConstant, i), 0.0f, 0.0f, 1.0f} });
  96. mMaxCurve[i] = TAnimationCurve<float>({
  97. { TCurveProperties<T>::getComponent(mMaxConstant, i), 0.0f, 0.0f, 0.0f},
  98. { TCurveProperties<T>::getComponent(mMaxConstant, i), 0.0f, 0.0f, 1.0f} });
  99. }
  100. mMinInput->setValue(mMinConstant);
  101. mMaxInput->setValue(mMaxConstant);
  102. break;
  103. case PDT_Curve:
  104. AnimationUtility::splitCurve(mValue.getMinCurve(), mMinCurve);
  105. AnimationUtility::splitCurve(mValue.getMinCurve(), mMaxCurve);
  106. for(UINT32 i = 0; i < NumComponents; i++)
  107. {
  108. TCurveProperties<T>::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f));
  109. TCurveProperties<T>::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f));
  110. mCurveDisplay[i]->setCurve(mMinCurve[i]);
  111. }
  112. break;
  113. case PDT_RandomCurveRange:
  114. AnimationUtility::splitCurve(mValue.getMinCurve(), mMinCurve);
  115. AnimationUtility::splitCurve(mValue.getMaxCurve(), mMaxCurve);
  116. for(UINT32 i = 0; i < NumComponents; i++)
  117. {
  118. TCurveProperties<T>::setComponent(mMinConstant, i, mMinCurve[i].evaluate(0.0f));
  119. TCurveProperties<T>::setComponent(mMaxConstant, i, mMaxCurve[i].evaluate(0.0f));
  120. mCurveDisplay[i]->setCurveRange(mMinCurve[i], mMaxCurve[i]);
  121. }
  122. break;
  123. }
  124. }
  125. template<class T, class SELF>
  126. bool TGUIDistributionField<T, SELF>::hasInputFocus() const
  127. {
  128. if(mMinInput && mMinInput->hasInputFocus())
  129. return true;
  130. if(mMaxInput && mMaxInput->hasInputFocus())
  131. return true;
  132. return false;
  133. }
  134. template<class T, class SELF>
  135. void TGUIDistributionField<T, SELF>::setTint(const Color& color)
  136. {
  137. mDropDownButton->setTint(color);
  138. if (mLabel)
  139. mLabel->setTint(color);
  140. if(mMinInput)
  141. mMinInput->setTint(color);
  142. if(mMaxInput)
  143. mMaxInput->setTint(color);
  144. for(auto& entry : mLabels)
  145. {
  146. if(entry)
  147. entry->setTint(color);
  148. }
  149. for(int i = 0; i < NumComponents; i++)
  150. {
  151. if(mCurveDisplay[i])
  152. mCurveDisplay[i]->setTint(color);
  153. }
  154. }
  155. template<class T, class SELF>
  156. Vector2I TGUIDistributionField<T, SELF>::_getOptimalSize() const
  157. {
  158. Vector2I optimalsize = Vector2I::ZERO;
  159. if (mMinInput)
  160. {
  161. Vector2I elemOptimal = mMinInput->_calculateLayoutSizeRange().optimal;
  162. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  163. optimalsize.y += elemOptimal.y;
  164. }
  165. if (mMaxInput)
  166. {
  167. Vector2I elemOptimal = mMaxInput->_calculateLayoutSizeRange().optimal;
  168. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  169. optimalsize.y += elemOptimal.y;
  170. optimalsize.y += ELEM_SPACING;
  171. }
  172. for (auto& entry : mLabels)
  173. {
  174. if (!entry)
  175. continue;
  176. Vector2I elemOptimal = entry->_calculateLayoutSizeRange().optimal;
  177. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  178. optimalsize.y += elemOptimal.y;
  179. }
  180. for (UINT32 i = 0; i < NumComponents; i++)
  181. {
  182. if (mCurveDisplay[i])
  183. {
  184. Vector2I elemOptimal = mCurveDisplay[i]->_calculateLayoutSizeRange().optimal;
  185. optimalsize.x = std::max(optimalsize.x, elemOptimal.x);
  186. optimalsize.y += elemOptimal.y;
  187. if (i != 0)
  188. optimalsize.y += ELEM_SPACING;
  189. }
  190. }
  191. Vector2I dropDownSize = mDropDownButton->_calculateLayoutSizeRange().optimal;
  192. optimalsize.x += dropDownSize.x;
  193. optimalsize.y = std::max(optimalsize.y, dropDownSize.y);
  194. if (mLabel)
  195. {
  196. Vector2I elemOptimal = mLabel->_calculateLayoutSizeRange().optimal;
  197. optimalsize.x += elemOptimal.x;
  198. optimalsize.y = std::max(optimalsize.y, elemOptimal.y);
  199. }
  200. return optimalsize;
  201. }
  202. template<class T, class SELF>
  203. void TGUIDistributionField<T, SELF>::styleUpdated()
  204. {
  205. mDropDownButton->setStyle(getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE));
  206. if (mLabel)
  207. mLabel->setStyle(getSubStyleName(getLabelStyleType()));
  208. if (mMinInput)
  209. mMinInput->setStyle(getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  210. if (mMaxInput)
  211. mMaxInput->setStyle(getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  212. for (int i = 0; i < NumComponents; i++)
  213. {
  214. if (mCurveDisplay[i])
  215. mCurveDisplay[i]->setStyle(getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  216. }
  217. }
  218. template<class T, class SELF>
  219. void TGUIDistributionField<T, SELF>::rebuild()
  220. {
  221. constexpr const char* COMP_NAMES[] = { "X", "Y", "Z", "W" };
  222. constexpr UINT32 ELEMENT_LABEL_WIDTH = 15;
  223. if(mLabel)
  224. mLayout->removeElement(mLabel);
  225. mLayout->clear();
  226. mLayout->addElement(mLabel);
  227. GUILayout* valueLayout = mLayout->addNewElement<GUILayoutY>();
  228. switch (mValue.getType())
  229. {
  230. default:
  231. case PDT_Constant:
  232. mMinInput = GUIConstantType::create(GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  233. mMaxInput = nullptr;
  234. mLabels = { nullptr, nullptr };
  235. for(int i = 0; i < NumComponents; i++)
  236. mCurveDisplay[i] = nullptr;
  237. mMinInput->setValue(mMinConstant);
  238. mMinInput->onValueChanged.connect([this](T value)
  239. {
  240. mMinConstant = value;
  241. mValue = TDistribution<T>(value);
  242. onConstantModified();
  243. });
  244. mMinInput->onConfirm.connect([this]() { onConstantConfirmed(); });
  245. valueLayout->addElement(mMinInput);
  246. break;
  247. case PDT_RandomRange:
  248. mMinInput = GUIConstantType::create(GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  249. mMaxInput = GUIConstantType::create(GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
  250. for(int i = 0; i < NumComponents; i++)
  251. mCurveDisplay[i] = nullptr;
  252. mMinInput->setValue(mMinConstant);
  253. mMinInput->onValueChanged.connect([this](T value)
  254. {
  255. mMinConstant = value;
  256. mValue = TDistribution<T>(value, mMaxConstant);
  257. onConstantModified();
  258. });
  259. mMinInput->onConfirm.connect([this]() { onConstantConfirmed(); });
  260. mMaxInput->setValue(mMaxConstant);
  261. mMaxInput->onValueChanged.connect([this](T value)
  262. {
  263. mMaxConstant = value;
  264. mValue = TDistribution<T>(mMinConstant, value);
  265. onConstantModified();
  266. });
  267. mMaxInput->onConfirm.connect([this]() { onConstantConfirmed(); });
  268. mLabels[0] = valueLayout->addNewElement<GUILabel>(HString("Min."), "HeaderLight");
  269. valueLayout->addElement(mMinInput);
  270. valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
  271. mLabels[1] = valueLayout->addNewElement<GUILabel>(HString("Max."), "HeaderLight");
  272. valueLayout->addElement(mMaxInput);
  273. break;
  274. case PDT_Curve:
  275. mMinInput = nullptr;
  276. mMaxInput = nullptr;
  277. mLabels = { nullptr, nullptr };
  278. for(int i = 0; i < NumComponents; i++)
  279. {
  280. if(NumComponents > 1)
  281. {
  282. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers, HString(COMP_NAMES[i]),
  283. ELEMENT_LABEL_WIDTH, getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  284. }
  285. else
  286. {
  287. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers,
  288. getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  289. }
  290. mCurveDisplay[i]->setCurve(mMinCurve[i]);
  291. mCurveDisplay[i]->setPadding(3);
  292. mCurveDisplay[i]->centerAndZoom();
  293. mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked(i); });
  294. if(i != 0)
  295. valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
  296. valueLayout->addElement(mCurveDisplay[i]);
  297. }
  298. break;
  299. case PDT_RandomCurveRange:
  300. mMinInput = nullptr;
  301. mMaxInput = nullptr;
  302. mLabels = { nullptr, nullptr };
  303. for(int i = 0; i < NumComponents; i++)
  304. {
  305. if(NumComponents > 1)
  306. {
  307. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers, HString(COMP_NAMES[i]),
  308. ELEMENT_LABEL_WIDTH, getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  309. }
  310. else
  311. {
  312. mCurveDisplay[i] = GUICurvesField::create(CurveDrawOption::DrawMarkers,
  313. getSubStyleName(CURVES_FIELD_STYLE_TYPES[i]));
  314. }
  315. mCurveDisplay[i]->setCurveRange(mMinCurve[i], mMaxCurve[i]);
  316. mCurveDisplay[i]->setPadding(3);
  317. mCurveDisplay[i]->centerAndZoom();
  318. mCurveDisplay[i]->onClicked.connect([this,i]() { onClicked(i); });
  319. if(i != 0)
  320. valueLayout->addNewElement<GUIFixedSpace>(ELEM_SPACING);
  321. valueLayout->addElement(mCurveDisplay[i]);
  322. }
  323. break;
  324. }
  325. mDropDownButton = GUIButton::create(HString::dummy(), getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE));
  326. mDropDownButton->onClick.connect([this]()
  327. {
  328. const Rect2I bounds = mDropDownButton->getBounds(mParentWidget->getPanel());
  329. const Vector2I center(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
  330. mContextMenu->open(center, *mParentWidget);
  331. });
  332. mLayout->addNewElement<GUIFixedSpace>(10);
  333. mLayout->addElement(mDropDownButton);
  334. }
  335. template class BS_ED_EXPORT TGUIDistributionField<float, GUIFloatDistributionField>;
  336. template class BS_ED_EXPORT TGUIDistributionField<Vector2, GUIVector2DistributionField>;
  337. template class BS_ED_EXPORT TGUIDistributionField<Vector3, GUIVector3DistributionField>;
  338. const String& GUIFloatDistributionField::getGUITypeName()
  339. {
  340. static String typeName = "GUIFloatDistributionField";
  341. return typeName;
  342. }
  343. const String& GUIVector2DistributionField::getGUITypeName()
  344. {
  345. static String typeName = "GUIVector2DistributionField";
  346. return typeName;
  347. }
  348. const String& GUIVector3DistributionField::getGUITypeName()
  349. {
  350. static String typeName = "GUIVector3DistributionField";
  351. return typeName;
  352. }
  353. }