Browse Source

Feature: Added a float distribution GUI field

BearishSun 7 years ago
parent
commit
3e9c42e57c

+ 47 - 0
Data/Raw/GUISkin.json

@@ -3087,5 +3087,52 @@
         "normal": {
             "texture": "ContainerBg.png"
         }
+    },
+    {
+        "fixedHeight": true,
+        "height": 15,
+        "margins": {
+            "bottom": 2,
+            "left": 2,
+            "right": 2,
+            "top": 2
+        },
+        "minWidth": 10,
+        "name": "ColorGradient"
+    },
+    {
+        "name": "GUICurves",
+        "minHeight": 20,
+        "margins": {
+            "bottom": 2,
+            "left": 2,
+            "right": 2,
+            "top": 2
+        },
+        "minWidth": 30
+    },
+    {
+        "name": "GUIFloatDistributionField",
+        "minHeight": 20,
+		"maxHeight": 50,
+        "minWidth": 30,
+        "subStyles": [
+            {
+                "name": "FloatField",
+                "style": "GUIFloatField"
+            },
+            {
+                "name": "CurvesField",
+                "style": "GUICurves"
+            },
+            {
+                "name": "DropDownButton",
+                "style": "Button"
+            },
+            {
+                "name": "EditorFieldLabel",
+                "style": "EditorFieldLabel"
+            }
+        ]
     }
 ]

+ 2 - 0
Source/EditorCore/CMakeSources.cmake

@@ -64,6 +64,7 @@ set(BS_BANSHEEEDITOR_SRC_GUI
 	"GUI/BsGUIWindowFrameWidget.cpp"
 	"GUI/BsGUICurves.cpp"
 	"GUI/BsGUICurvesField.cpp"
+	"GUI/BsGUIFloatDistributionField.cpp"
 )
 
 set(BS_BANSHEEEDITOR_INC_LIBRARY
@@ -102,6 +103,7 @@ set(BS_BANSHEEEDITOR_INC_GUI
 	"GUI/BsGUIWindowFrameWidget.h"
 	"GUI/BsGUICurves.h"
 	"GUI/BsGUICurvesField.h"
+	"GUI/BsGUIFloatDistributionField.h"
 )
 
 set(BS_BANSHEEEDITOR_INC_UNDOREDO

+ 23 - 19
Source/EditorCore/GUI/BsGUICurves.cpp

@@ -11,8 +11,6 @@
 
 namespace bs
 {
-	static constexpr INT32 TIMELINE_PADDING_PX = 30;
-
 	GUITimeline::GUITimeline(const String& styleName, const GUIDimensions& dimensions)
 		:GUIElementContainer(dimensions, styleName)
 	{
@@ -56,19 +54,25 @@ namespace bs
 		_markContentAsDirty();
 	}
 
+	void GUITimeline::setPadding(UINT32 padding)
+	{
+		mPadding = padding;
+		_markContentAsDirty();
+	}
+
 	UINT32 GUITimeline::getFrame(const Vector2I& pixelCoords) const
 	{
 		const Rect2I& bounds = mLayoutData.area;
 
-		if (pixelCoords.x < (TIMELINE_PADDING_PX) || 
-			pixelCoords.x >= ((INT32)bounds.width - TIMELINE_PADDING_PX) ||
+		if (pixelCoords.x < (INT32)mPadding || 
+			pixelCoords.x >= ((INT32)bounds.width - (INT32)mPadding) ||
 			pixelCoords.y < 0 || 
 			pixelCoords.y >= (INT32)bounds.height)
 		{
 			return -1;
 		}
 
-		const Vector2I relativeCoords = pixelCoords - Vector2I(bounds.x + TIMELINE_PADDING_PX, bounds.y);
+		const Vector2I relativeCoords = pixelCoords - Vector2I(bounds.x + mPadding, bounds.y);
 
 		const float lengthPerPixel = getRange() / getDrawableWidth();
 		const float time = mOffset + relativeCoords.x * lengthPerPixel;
@@ -79,7 +83,7 @@ namespace bs
 	float GUITimeline::getTime(INT32 pixel) const
 	{
 		const Rect2I& bounds = mLayoutData.area;
-		const INT32 relativeCoords = pixel - (bounds.x + TIMELINE_PADDING_PX);
+		const INT32 relativeCoords = pixel - (bounds.x + mPadding);
 
 		const float lengthPerPixel = getRange() / getDrawableWidth();
 		return mOffset + relativeCoords * lengthPerPixel;
@@ -87,7 +91,7 @@ namespace bs
 
 	INT32 GUITimeline::getOffset(float time) const
 	{
-		return (INT32)(((time - mOffset) / getRange()) * getDrawableWidth()) + TIMELINE_PADDING_PX;
+		return (INT32)(((time - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
 	}
 
 	float GUITimeline::getTimeForFrame(INT32 index) const
@@ -97,7 +101,7 @@ namespace bs
 
 	UINT32 GUITimeline::getDrawableWidth() const
 	{
-		return std::max(0, (INT32)mLayoutData.area.width - TIMELINE_PADDING_PX * 2);
+		return std::max(0, (INT32)mLayoutData.area.width - (INT32)mPadding * 2);
 	}
 
 	float GUITimeline::getRangeWithPadding() const
@@ -105,14 +109,14 @@ namespace bs
 		const float spf = 1.0f / (float)mFPS;
 
 		const float lengthPerPixel = mRange / getDrawableWidth();
-		const float range = mRange + lengthPerPixel * TIMELINE_PADDING_PX;
+		const float range = mRange + lengthPerPixel * mPadding;
 
 		return std::max(1.0f, range / spf) * spf;
 	}
 
 	void GUITimeline::drawFrameMarker(float t)
 	{
-		const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + TIMELINE_PADDING_PX;
+		const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
 
 		const Vector2I start(xPos, 0);
 		const Vector2I end(xPos, mLayoutData.area.height);
@@ -211,8 +215,8 @@ namespace bs
 
 		float xRange = xMax - xMin;
 
-		float yRange = (yMax - yMin) * 0.5f;
-		float yOffset = yMin + yRange;
+		float yRange = (yMax - yMin);
+		float yOffset = yMin + yRange * 0.5f;
 
 		// Add padding to y range
 		yRange *= 1.05f;
@@ -362,7 +366,7 @@ namespace bs
 		if (padding)
 			outsideHorizontal = pixelCoords.x < 0 || pixelCoords.x >= (INT32)bounds.width;
 		else
-			outsideHorizontal = pixelCoords.x < TIMELINE_PADDING_PX || pixelCoords.x >= ((INT32)bounds.width - TIMELINE_PADDING_PX);
+			outsideHorizontal = pixelCoords.x < (INT32)mPadding || pixelCoords.x >= ((INT32)bounds.width - (INT32)mPadding);
 
 		// Check if outside of curve drawing bounds
 		if (outsideHorizontal || pixelCoords.y < 0 || pixelCoords.y >= (INT32)bounds.height)
@@ -372,7 +376,7 @@ namespace bs
 		}
 
 		// Find time and value of the place under the coordinates
-		const Vector2I relativeCoords = pixelCoords - Vector2I(TIMELINE_PADDING_PX, 0);
+		const Vector2I relativeCoords = pixelCoords - Vector2I(mPadding, 0);
 
 		const float lengthPerPixel = getRange() / getDrawableWidth();
 		const float heightPerPixel = mYRange / mLayoutData.area.height;
@@ -396,7 +400,7 @@ namespace bs
 		const Vector2 relativeCurveCoords = curveCoords - Vector2(mOffset, mYOffset);
 
 		Vector2I pixelCoords = Vector2I();
-		pixelCoords.x = (int)((relativeCurveCoords.x / getRange()) * getDrawableWidth()) + TIMELINE_PADDING_PX;
+		pixelCoords.x = (int)((relativeCurveCoords.x / getRange()) * getDrawableWidth()) + mPadding;
 		pixelCoords.y = heightOffset - (int)((relativeCurveCoords.y / mYRange) * mLayoutData.area.height);
 
 		return pixelCoords;
@@ -448,7 +452,7 @@ namespace bs
 
 	void GUICurves::drawFrameMarker(float t, Color color, bool onTop)
 	{
-		const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + TIMELINE_PADDING_PX;
+		const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
 
 		const Vector2I start = Vector2I(xPos, 0);
 		const Vector2I end = Vector2I(xPos, mLayoutData.area.height);
@@ -533,7 +537,7 @@ namespace bs
 			const float curveValue = curve.evaluate(curveStart, false);
 
 			const Vector2I end = curveToPixelSpace(Vector2(curveStart, curveValue));
-			const Vector2I start = Vector2I(-TIMELINE_PADDING_PX, end.y);
+			const Vector2I start = Vector2I(-(INT32)mPadding, end.y);
 
 			if (start.x < end.x)
 				mCanvas->drawLine(start, end, COLOR_MID_GRAY);
@@ -637,7 +641,7 @@ namespace bs
 
 		float time = mOffset;
 		const float lengthPerPixel = mRange / getDrawableWidth();
-		time -= lengthPerPixel * TIMELINE_PADDING_PX;
+		time -= lengthPerPixel * mPadding;
 
 		INT32 keyframeIndices[] = { 0, 0 };
 
@@ -874,7 +878,7 @@ namespace bs
 		bool drawMarkers = mDrawOptions.isSet(CurveDrawOption::DrawMarkers);
 		if (drawMarkers)
 		{
-			mTickHandler.setRange(mOffset, mOffset + getRangeWithPadding(), getDrawableWidth() + TIMELINE_PADDING_PX);
+			mTickHandler.setRange(mOffset, mOffset + getRangeWithPadding(), getDrawableWidth() + mPadding);
 
 			// Draw vertical frame markers
 			const INT32 numTickLevels = (INT32)mTickHandler.getNumLevels();

+ 9 - 0
Source/EditorCore/GUI/BsGUICurves.h

@@ -118,6 +118,14 @@ namespace bs
 		BS_SCRIPT_EXPORT()
 		float getTimeForFrame(INT32 index) const;
 
+		/** Sets the size of padding to apply to the left and right sides of the curve drawing, in pixels. */
+		BS_SCRIPT_EXPORT(pr:setter,n:Padding)
+		void setPadding(UINT32 padding);
+
+		/** @copydoc setPadding */
+		BS_SCRIPT_EXPORT(pr:getter,n:Padding)
+		UINT32 getPadding() const { return mPadding; };
+
 		/**
 		 * @name Internal
 		 * @{
@@ -155,6 +163,7 @@ namespace bs
 		float mOffset = 0.0f;
 		UINT32 mFPS = 1;
 		UINT32 mMarkedFrame = (UINT32)-1;
+		UINT32 mPadding = 30;
 	};
 
 	/** Determines how should ticks reported by <see cref="GUIGraphTicks"/> be distributed. */

+ 1 - 1
Source/EditorCore/GUI/BsGUIFieldBase.h

@@ -41,7 +41,7 @@ namespace bs
 
 		/** @} */
 	protected:
-		virtual ~GUIFieldBase() { }
+		virtual ~GUIFieldBase() = default;
 
 		/** @copydoc GUIElementContainer::styleUpdated */
 		void styleUpdated() override;

+ 268 - 0
Source/EditorCore/GUI/BsGUIFloatDistributionField.cpp

@@ -0,0 +1,268 @@
+//********************************** 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/BsGUILabel.h"
+#include "GUI/BsGUIColor.h"
+#include "GUI/BsGUIFloatField.h"
+#include "GUI/BsGUICurves.h"
+#include "GUI/BsGUIButton.h"
+#include "GUI/BsGUIContextMenu.h"
+#include "GUI/BsGUISpace.h"
+
+using namespace std::placeholders;
+
+namespace bs
+{
+	GUIFloatDistributionField::GUIFloatDistributionField(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<GUIContextMenu>();
+
+		mContextMenu->addMenuItem("Constant", [this]()
+		{
+			mValue = FloatDistribution(mMinConstant);
+			rebuild();
+		}, 50);
+
+		mContextMenu->addMenuItem("Range", [this]()
+		{
+			mValue = FloatDistribution(mMinConstant, mMaxConstant);
+			rebuild();
+		}, 40);
+
+		mContextMenu->addMenuItem("Curve", [this]()
+		{
+			mValue = FloatDistribution(mCurves[0]);
+			rebuild();
+		}, 30);
+
+		mContextMenu->addMenuItem("Curve range", [this]()
+		{
+			mValue = FloatDistribution(mCurves[0], mCurves[1]);
+			rebuild();
+		}, 20);
+
+		rebuild();
+	}
+
+	void GUIFloatDistributionField::setValue(const FloatDistribution& value)
+	{
+		mValue = value;
+
+		switch (mValue.getType())
+		{
+		default:
+		case PDT_Constant:
+			mMinConstant = mValue.getMinConstant();
+			mMaxConstant = mMinConstant;
+			mCurves[0] = TAnimationCurve<float>({
+				{ mMinConstant, 0.0f, 0.0f, 0.0f}, 
+				{ mMinConstant, 0.0f, 0.0f, 1.0f}});
+			mCurves[1] = TAnimationCurve<float>({
+				{ mMinConstant, 0.0f, 0.0f, 0.0f}, 
+				{ mMinConstant, 0.0f, 0.0f, 1.0f}});
+			break;
+		case PDT_RandomRange: 
+			mMinConstant = mValue.getMinConstant();
+			mMaxConstant = mValue.getMaxConstant();
+			mCurves[0] = TAnimationCurve<float>({
+				{ mMinConstant, 0.0f, 0.0f, 0.0f}, 
+				{ mMinConstant, 0.0f, 0.0f, 1.0f}});
+			mCurves[1] = TAnimationCurve<float>({
+				{ mMaxConstant, 0.0f, 0.0f, 0.0f}, 
+				{ mMaxConstant, 0.0f, 0.0f, 1.0f}});
+			break;
+		case PDT_Curve: 
+			mCurves[0] = mValue.getMinCurve();
+			mCurves[1] = mValue.getMinCurve();
+			mMinConstant = mCurves[0].evaluate(0.0f);
+			mMaxConstant = mCurves[1].evaluate(0.0f);
+			break;
+		case PDT_RandomCurveRange: 
+			mCurves[0] = mValue.getMinCurve();
+			mCurves[1] = mValue.getMaxCurve();
+			mMinConstant = mCurves[0].evaluate(0.0f);
+			mMaxConstant = mCurves[1].evaluate(0.0f);
+			break;
+		}
+
+		rebuild();
+	}
+
+	void GUIFloatDistributionField::setTint(const Color& color)
+	{
+		mDropDownButton->setTint(color);
+
+		if (mLabel)
+			mLabel->setTint(color);
+
+		if(mMinInput)
+			mMinInput->setTint(color);
+
+		if(mMaxInput)
+			mMaxInput->setTint(color);
+
+		if(mCurveDisplay)
+			mCurveDisplay->setTint(color);
+	}
+
+	Vector2I GUIFloatDistributionField::_getOptimalSize() const
+	{
+		Vector2I optimalsize = mDropDownButton->_getOptimalSize();
+
+		if(mLabel)
+		{
+			optimalsize.x += mLabel->_getOptimalSize().x;
+			optimalsize.y = std::max(optimalsize.y, mLabel->_getOptimalSize().y);
+		}
+
+		if(mMinInput)
+		{
+			optimalsize.x += mMinInput->_getOptimalSize().x;
+			optimalsize.y = std::max(optimalsize.y, mMinInput->_getOptimalSize().y);
+		}
+
+		if(mMaxInput)
+		{
+			optimalsize.x += mMaxInput->_getOptimalSize().x;
+			optimalsize.y = std::max(optimalsize.y, mMaxInput->_getOptimalSize().y);
+		}
+
+		if(mCurveDisplay)
+		{
+			optimalsize.x += mCurveDisplay->_getOptimalSize().x;
+			optimalsize.y = std::max(optimalsize.y, 50);
+		}
+
+		return optimalsize;
+	}
+
+	void GUIFloatDistributionField::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));
+
+		if (mCurveDisplay)
+			mCurveDisplay->setStyle(getSubStyleName(CURVES_FIELD_STYLE_TYPE));
+	}
+
+	void GUIFloatDistributionField::rebuild()
+	{
+		if(mLabel)
+			mLayout->removeElement(mLabel);
+
+		mLayout->clear();
+		mLayout->addElement(mLabel);
+
+		switch (mValue.getType())
+		{
+		default:
+		case PDT_Constant:
+			mMinInput = GUIFloatField::create(HString("Constant"), 50, GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
+			mMaxInput = nullptr;
+			mCurveDisplay = nullptr;
+
+			mMinInput->setValue(mMinConstant);
+			mMinInput->onValueChanged.connect([this](float value)
+			{
+				mMinConstant = value;
+				mValue = FloatDistribution(value);
+
+				onConstantModified();
+			});
+
+			mLayout->addElement(mMinInput);
+			break;
+		case PDT_RandomRange: 
+			mMinInput = GUIFloatField::create(HString("Min."), 40, GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
+			mMaxInput = GUIFloatField::create(HString("Max."), 40, GUIOptions(), getSubStyleName(FLOAT_FIELD_STYLE_TYPE));
+			mCurveDisplay = nullptr;
+
+			mMinInput->setValue(mMinConstant);
+			mMinInput->onValueChanged.connect([this](float value)
+			{
+				mMinConstant = value;
+				mValue = FloatDistribution(value, mMaxConstant);
+
+				onConstantModified();
+			});
+
+			mMaxInput->setValue(mMaxConstant);
+			mMaxInput->onValueChanged.connect([this](float value)
+			{
+				mMaxConstant = value;
+				mValue = FloatDistribution(mMinConstant, value);
+
+				onConstantModified();
+			});
+
+			mLayout->addElement(mMinInput);
+			mLayout->addElement(mMaxInput);
+			break;
+		case PDT_Curve: 
+			mMinInput = nullptr;
+			mMaxInput = nullptr;
+			mCurveDisplay = GUICurves::create(CurveDrawOption::DrawMarkers, 
+				getSubStyleName(CURVES_FIELD_STYLE_TYPE));
+
+			mCurveDisplay->setCurves(
+				{
+					CurveDrawInfo(mValue.getMinCurve(), Color::BansheeOrange)
+				});
+
+			mCurveDisplay->setPadding(3);
+			mCurveDisplay->centerAndZoom();
+			mCurveDisplay->onClicked.connect([this](){ onClicked(); });
+
+			mLayout->addElement(mCurveDisplay);
+			break;
+		case PDT_RandomCurveRange: 
+			mMinInput = nullptr;
+			mMaxInput = nullptr;
+			mCurveDisplay = GUICurves::create(CurveDrawOption::DrawMarkers | CurveDrawOption::DrawRange, 
+				getSubStyleName(CURVES_FIELD_STYLE_TYPE));
+
+			mCurveDisplay->setCurves(
+				{
+					CurveDrawInfo(mValue.getMinCurve(), Color::BansheeOrange),
+					CurveDrawInfo(mValue.getMaxCurve(), Color::Red)
+				});
+
+			mCurveDisplay->setPadding(3);
+			mCurveDisplay->centerAndZoom();
+			mCurveDisplay->onClicked.connect([this](){ onClicked(); });
+
+			mLayout->addElement(mCurveDisplay);
+			break;
+		}
+
+		mDropDownButton = GUIButton::create(HString::dummy(), GUIOptions(GUIOption::fixedWidth(50)), 
+			getSubStyleName(DROP_DOWN_FIELD_STYLE_TYPE));
+		mDropDownButton->onClick.connect([this]()
+		{
+			const Rect2I bounds = getBounds();
+			const Vector2I center(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
+
+			mContextMenu->open(center, *mParentWidget);
+		});
+
+		mLayout->addNewElement<GUIFixedSpace>(10);
+		mLayout->addElement(mDropDownButton);
+	}
+
+	const String& GUIFloatDistributionField::getGUITypeName()
+	{
+		static String typeName = "GUIFloatDistributionField";
+		return typeName;
+	}
+}

+ 94 - 0
Source/EditorCore/GUI/BsGUIFloatDistributionField.h

@@ -0,0 +1,94 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2018 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "GUI/BsGUIFieldBase.h"
+#include "Particles/BsParticleDistribution.h"
+
+namespace bs
+{
+	class GUICurves;
+
+	/** @addtogroup GUI-Editor
+	 *  @{
+	 */
+
+	/**
+	 * A composite GUI object representing an editor field. Editor fields are a combination of a label and an input field.
+	 * Label is optional. This specific implementation displays an input field for a floating point distribution.
+	 */
+	class BS_ED_EXPORT BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) 
+	GUIFloatDistributionField final : public TGUIField<GUIFloatDistributionField>
+	{
+	public:
+		/** 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 field. */
+		static constexpr const char* CURVES_FIELD_STYLE_TYPE = "CurvesField";
+
+		/** Style type name for the internal drop down button. */
+		static constexpr const char* DROP_DOWN_FIELD_STYLE_TYPE = "DropDownButton";
+
+		/** Returns type name of the GUI element used for finding GUI element styles. */
+		static const String& getGUITypeName();
+
+		GUIFloatDistributionField(const PrivatelyConstruct& dummy, const GUIContent& labelContent, UINT32 labelWidth,
+			const String& style, const GUIDimensions& dimensions, bool withLabel);
+
+		/**	Returns the value of the field. */
+		BS_SCRIPT_EXPORT(pr:getter,n:Value)
+		FloatDistribution getValue() const { return mValue; }
+
+		/**	Changes the value of the field. */
+		BS_SCRIPT_EXPORT(pr:setter,n:Value)
+		void setValue(const FloatDistribution& value);
+
+		/** @copydoc GUIElement::setTint */
+		void setTint(const Color& color) override;
+
+		/** 
+		 * Triggered when the user clicks on the curve display. Only relevant if the distribution is a curve distribution. 
+		 */
+		BS_SCRIPT_EXPORT(in:true)
+		Event<void()> onClicked; /**< Triggered when the user clicks on the GUI element. */
+
+		/** 
+		 * Triggered when the user modifies either of the non-curve (constant) values of the distribution. Only relevant
+		 * if the distribution is not a curve distribution.
+		 */
+		BS_SCRIPT_EXPORT(in:true)
+		Event<void()> onConstantModified; /**< Triggered when the user clicks on the GUI element. */
+
+		/** @name Internal 
+		 *  @{
+		 */
+
+		/** @copydoc GUIElement::_getOptimalSize */
+		Vector2I _getOptimalSize() const override;
+
+		/** @} */
+
+	protected:
+		/** @copydoc GUIElement::styleUpdated */
+		void styleUpdated() override;
+
+		/** Rebuilds the internal GUI components for the current property type. */
+		void rebuild();
+
+		UINT32 mLabelWidth = 100;
+		FloatDistribution mValue = 0.0f;
+		GUIButton* mDropDownButton = nullptr;
+		GUIFloatField* mMinInput = nullptr;
+		GUIFloatField* mMaxInput = nullptr;
+		GUICurves* mCurveDisplay = nullptr;
+
+		float mMinConstant = 0.0f;
+		float mMaxConstant = 0.0f;
+		TAnimationCurve<float> mCurves[2];
+		SPtr<GUIContextMenu> mContextMenu;
+	};
+
+	/** @} */
+}

+ 34 - 0
Source/Scripting/MBansheeEditor/GUI/GUIFloatDistributionField.cs

@@ -0,0 +1,34 @@
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    partial class GUIFloatDistributionField
+    {
+        /// <summary>
+        /// Callback triggered when the user clicks on the curve display in the GUI element.
+        /// </summary>
+        partial void OnClicked()
+        {
+            FloatDistribution distribution = Value;
+
+            if (distribution.DistributionType == PropertyDistributionType.Curve)
+            {
+                CurveEditorWindow.Show(distribution.GetMinCurve(), (success, curve) =>
+                {
+                    if(success)
+                        Value = new FloatDistribution(curve);
+                });
+            }
+            else if (distribution.DistributionType == PropertyDistributionType.RandomCurveRange)
+            {
+                CurveEditorWindow.Show(distribution.GetMinCurve(), distribution.GetMaxCurve(), 
+                    (success, minCurve, maxCurve) =>
+                {
+                    if(success)
+                        Value = new FloatDistribution(minCurve, maxCurve);
+                });
+            }
+        }
+    }
+}

+ 115 - 0
Source/Scripting/MBansheeEditor/Generated/GUIFloatDistributionField.generated.cs

@@ -0,0 +1,115 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+	/** @addtogroup GUIEditor
+	 *  @{
+	 */
+
+	/// <summary>
+	/// A composite GUI object representing an editor field. Editor fields are a combination of a label and an input field. 
+	/// Label is optional. This specific implementation displays an input field for a floating point distribution.
+	/// </summary>
+	public partial class GUIFloatDistributionField : GUIElement
+	{
+		private GUIFloatDistributionField(bool __dummy0) { }
+		protected GUIFloatDistributionField() { }
+
+		/// <summary>Creates a new GUI editor field with a label.</summary>
+		/// <param name="labelContent">Content to display in the editor field label.</param>
+		/// <param name="labelWidth">Width of the label in pixels.</param>
+		/// <param name="style">
+		/// Optional style to use for the element. Style will be retrieved from GUISkin of the GUIWidget the element is used on. 
+		/// If not specified default style is used.
+		/// </param>
+		public GUIFloatDistributionField(GUIContent labelContent, uint labelWidth, string style = "")
+		{
+			Internal_create(this, ref labelContent, labelWidth, style);
+		}
+
+		/// <summary>Creates a new GUI editor field with a label.</summary>
+		/// <param name="labelContent">Content to display in the editor field label.</param>
+		/// <param name="style">
+		/// Optional style to use for the element. Style will be retrieved from GUISkin of the GUIWidget the element is used on. 
+		/// If not specified default style is used.
+		/// </param>
+		public GUIFloatDistributionField(GUIContent labelContent, string style = "")
+		{
+			Internal_create0(this, ref labelContent, style);
+		}
+
+		/// <summary>Creates a new GUI editor field with a label.</summary>
+		/// <param name="labelText">String to display in the editor field label.</param>
+		/// <param name="labelWidth">Width of the label in pixels.</param>
+		/// <param name="style">
+		/// Optional style to use for the element. Style will be retrieved from GUISkin of the GUIWidget the element is used on. 
+		/// If not specified default style is used.
+		/// </param>
+		public GUIFloatDistributionField(LocString labelText, uint labelWidth, string style = "")
+		{
+			Internal_create1(this, labelText, labelWidth, style);
+		}
+
+		/// <summary>Creates a new GUI editor field with a label.</summary>
+		/// <param name="labelText">String to display in the editor field label.</param>
+		/// <param name="style">
+		/// Optional style to use for the element. Style will be retrieved from GUISkin of the GUIWidget the element is used on. 
+		/// If not specified default style is used.
+		/// </param>
+		public GUIFloatDistributionField(LocString labelText, string style = "")
+		{
+			Internal_create2(this, labelText, style);
+		}
+
+		/// <summary>Creates a new GUI editor field without a label.</summary>
+		/// <param name="style">
+		/// Optional style to use for the element. Style will be retrieved from GUISkin of the GUIWidget the element is used on. 
+		/// If not specified default style is used.
+		/// </param>
+		public GUIFloatDistributionField(string style = "")
+		{
+			Internal_create3(this, style);
+		}
+
+		/// <summary>Changes the value of the field.</summary>
+		public FloatDistribution Value
+		{
+			get { return Internal_getValue(mCachedPtr); }
+			set { Internal_setValue(mCachedPtr, value); }
+		}
+
+		/// <summary>Triggered when the user clicks on the GUI element.</summary>
+		partial void OnClicked();
+
+		/// <summary>Triggered when the user clicks on the GUI element.</summary>
+		partial void OnConstantModified();
+
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern FloatDistribution Internal_getValue(IntPtr thisPtr);
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern void Internal_setValue(IntPtr thisPtr, FloatDistribution value);
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern void Internal_create(GUIFloatDistributionField managedInstance, ref GUIContent labelContent, uint labelWidth, string style);
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern void Internal_create0(GUIFloatDistributionField managedInstance, ref GUIContent labelContent, string style);
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern void Internal_create1(GUIFloatDistributionField managedInstance, LocString labelText, uint labelWidth, string style);
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern void Internal_create2(GUIFloatDistributionField managedInstance, LocString labelText, string style);
+		[MethodImpl(MethodImplOptions.InternalCall)]
+		private static extern void Internal_create3(GUIFloatDistributionField managedInstance, string style);
+		private void Internal_onClicked()
+		{
+			OnClicked();
+		}
+		private void Internal_onConstantModified()
+		{
+			OnConstantModified();
+		}
+	}
+
+	/** @} */
+}

+ 1 - 0
Source/Scripting/MBansheeEditor/MBansheeEditor.csproj

@@ -37,6 +37,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="GUI\GUIColorGradient.cs" />
+    <Compile Include="GUI\GUIFloatDistributionField.cs" />
     <Compile Include="Inspectors\AnimationClipInspector.cs" />
     <Compile Include="Inspectors\AnimationInspector.cs" />
     <Compile Include="Inspectors\AudioClipInspector.cs" />

+ 0 - 2
Source/Scripting/MBansheeEditor/Windows/Animation/GUICurveEditor.cs

@@ -1495,8 +1495,6 @@ namespace BansheeEditor
             Vector2 newCurvePos = relativeCurvePos * rangeScale;
             Vector2 diff = newCurvePos - relativeCurvePos;
 
-            Debug.Log(curvePos + " " + Offset + " " + currentRange + " " + newRange);
-
             Offset -= diff;
 
             UpdateScrollBarSize();

+ 117 - 0
Source/Scripting/SBansheeEditor/Generated/BsScriptGUIFloatDistributionField.generated.cpp

@@ -0,0 +1,117 @@
+#include "BsScriptGUIFloatDistributionField.generated.h"
+#include "BsMonoMethod.h"
+#include "BsMonoClass.h"
+#include "BsMonoUtil.h"
+#include "../../../EditorCore/GUI/BsGUIFloatDistributionField.h"
+#include "BsScriptTDistribution.generated.h"
+#include "BsScriptGUIContent.generated.h"
+#include "../../../EditorCore/GUI/BsGUIFloatDistributionField.h"
+#include "BsScriptHString.generated.h"
+
+namespace bs
+{
+	ScriptGUIFloatDistributionField::onClickedThunkDef ScriptGUIFloatDistributionField::onClickedThunk; 
+	ScriptGUIFloatDistributionField::onConstantModifiedThunkDef ScriptGUIFloatDistributionField::onConstantModifiedThunk; 
+
+	ScriptGUIFloatDistributionField::ScriptGUIFloatDistributionField(MonoObject* managedInstance, GUIFloatDistributionField* value)
+		:TScriptGUIElement(managedInstance, value)
+	{
+		value->onClicked.connect(std::bind(&ScriptGUIFloatDistributionField::onClicked, this));
+		value->onConstantModified.connect(std::bind(&ScriptGUIFloatDistributionField::onConstantModified, this));
+	}
+
+	void ScriptGUIFloatDistributionField::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_getValue", (void*)&ScriptGUIFloatDistributionField::Internal_getValue);
+		metaData.scriptClass->addInternalCall("Internal_setValue", (void*)&ScriptGUIFloatDistributionField::Internal_setValue);
+		metaData.scriptClass->addInternalCall("Internal_create", (void*)&ScriptGUIFloatDistributionField::Internal_create);
+		metaData.scriptClass->addInternalCall("Internal_create0", (void*)&ScriptGUIFloatDistributionField::Internal_create0);
+		metaData.scriptClass->addInternalCall("Internal_create1", (void*)&ScriptGUIFloatDistributionField::Internal_create1);
+		metaData.scriptClass->addInternalCall("Internal_create2", (void*)&ScriptGUIFloatDistributionField::Internal_create2);
+		metaData.scriptClass->addInternalCall("Internal_create3", (void*)&ScriptGUIFloatDistributionField::Internal_create3);
+
+		onClickedThunk = (onClickedThunkDef)metaData.scriptClass->getMethodExact("Internal_onClicked", "")->getThunk();
+		onConstantModifiedThunk = (onConstantModifiedThunkDef)metaData.scriptClass->getMethodExact("Internal_onConstantModified", "")->getThunk();
+	}
+
+	void ScriptGUIFloatDistributionField::onClicked()
+	{
+		MonoUtil::invokeThunk(onClickedThunk, getManagedInstance());
+	}
+
+	void ScriptGUIFloatDistributionField::onConstantModified()
+	{
+		MonoUtil::invokeThunk(onConstantModifiedThunk, getManagedInstance());
+	}
+	MonoObject* ScriptGUIFloatDistributionField::Internal_getValue(ScriptGUIFloatDistributionField* thisPtr)
+	{
+		SPtr<TDistribution<float>> tmp__output = bs_shared_ptr_new<TDistribution<float>>();
+		*tmp__output = static_cast<GUIFloatDistributionField*>(thisPtr->getGUIElement())->getValue();
+
+		MonoObject* __output;
+		__output = ScriptTDistributionfloat::create(tmp__output);
+
+		return __output;
+	}
+
+	void ScriptGUIFloatDistributionField::Internal_setValue(ScriptGUIFloatDistributionField* thisPtr, MonoObject* value)
+	{
+		SPtr<TDistribution<float>> tmpvalue;
+		ScriptTDistributionfloat* scriptvalue;
+		scriptvalue = ScriptTDistributionfloat::toNative(value);
+		tmpvalue = scriptvalue->getInternal();
+		static_cast<GUIFloatDistributionField*>(thisPtr->getGUIElement())->setValue(*tmpvalue);
+	}
+
+	void ScriptGUIFloatDistributionField::Internal_create(MonoObject* managedInstance, __GUIContentInterop* labelContent, uint32_t labelWidth, MonoString* style)
+	{
+		GUIContent tmplabelContent;
+		tmplabelContent = ScriptGUIContent::fromInterop(*labelContent);
+		String tmpstyle;
+		tmpstyle = MonoUtil::monoToString(style);
+		GUIFloatDistributionField* instance = GUIFloatDistributionField::create(tmplabelContent, labelWidth, tmpstyle);
+		new (bs_alloc<ScriptGUIFloatDistributionField>())ScriptGUIFloatDistributionField(managedInstance, instance);
+	}
+
+	void ScriptGUIFloatDistributionField::Internal_create0(MonoObject* managedInstance, __GUIContentInterop* labelContent, MonoString* style)
+	{
+		GUIContent tmplabelContent;
+		tmplabelContent = ScriptGUIContent::fromInterop(*labelContent);
+		String tmpstyle;
+		tmpstyle = MonoUtil::monoToString(style);
+		GUIFloatDistributionField* instance = GUIFloatDistributionField::create(tmplabelContent, tmpstyle);
+		new (bs_alloc<ScriptGUIFloatDistributionField>())ScriptGUIFloatDistributionField(managedInstance, instance);
+	}
+
+	void ScriptGUIFloatDistributionField::Internal_create1(MonoObject* managedInstance, MonoObject* labelText, uint32_t labelWidth, MonoString* style)
+	{
+		SPtr<HString> tmplabelText;
+		ScriptHString* scriptlabelText;
+		scriptlabelText = ScriptHString::toNative(labelText);
+		tmplabelText = scriptlabelText->getInternal();
+		String tmpstyle;
+		tmpstyle = MonoUtil::monoToString(style);
+		GUIFloatDistributionField* instance = GUIFloatDistributionField::create(*tmplabelText, labelWidth, tmpstyle);
+		new (bs_alloc<ScriptGUIFloatDistributionField>())ScriptGUIFloatDistributionField(managedInstance, instance);
+	}
+
+	void ScriptGUIFloatDistributionField::Internal_create2(MonoObject* managedInstance, MonoObject* labelText, MonoString* style)
+	{
+		SPtr<HString> tmplabelText;
+		ScriptHString* scriptlabelText;
+		scriptlabelText = ScriptHString::toNative(labelText);
+		tmplabelText = scriptlabelText->getInternal();
+		String tmpstyle;
+		tmpstyle = MonoUtil::monoToString(style);
+		GUIFloatDistributionField* instance = GUIFloatDistributionField::create(*tmplabelText, tmpstyle);
+		new (bs_alloc<ScriptGUIFloatDistributionField>())ScriptGUIFloatDistributionField(managedInstance, instance);
+	}
+
+	void ScriptGUIFloatDistributionField::Internal_create3(MonoObject* managedInstance, MonoString* style)
+	{
+		String tmpstyle;
+		tmpstyle = MonoUtil::monoToString(style);
+		GUIFloatDistributionField* instance = GUIFloatDistributionField::create(tmpstyle);
+		new (bs_alloc<ScriptGUIFloatDistributionField>())ScriptGUIFloatDistributionField(managedInstance, instance);
+	}
+}

+ 37 - 0
Source/Scripting/SBansheeEditor/Generated/BsScriptGUIFloatDistributionField.generated.h

@@ -0,0 +1,37 @@
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "Wrappers/GUI/BsScriptGUIElement.h"
+#include "../../../bsf/Source/Foundation/bsfCore/Particles/BsParticleDistribution.h"
+#include "../../../bsf/Source/Foundation/bsfCore/Localization/BsHString.h"
+
+namespace bs
+{
+	class GUIFloatDistributionField;
+	struct __GUIContentInterop;
+
+	class BS_SCR_BED_EXPORT ScriptGUIFloatDistributionField : public TScriptGUIElement<ScriptGUIFloatDistributionField>
+	{
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "GUIFloatDistributionField")
+
+		ScriptGUIFloatDistributionField(MonoObject* managedInstance, GUIFloatDistributionField* value);
+
+	private:
+		void onClicked();
+		void onConstantModified();
+
+		typedef void(BS_THUNKCALL *onClickedThunkDef) (MonoObject*, MonoException**);
+		static onClickedThunkDef onClickedThunk;
+		typedef void(BS_THUNKCALL *onConstantModifiedThunkDef) (MonoObject*, MonoException**);
+		static onConstantModifiedThunkDef onConstantModifiedThunk;
+
+		static MonoObject* Internal_getValue(ScriptGUIFloatDistributionField* thisPtr);
+		static void Internal_setValue(ScriptGUIFloatDistributionField* thisPtr, MonoObject* value);
+		static void Internal_create(MonoObject* managedInstance, __GUIContentInterop* labelContent, uint32_t labelWidth, MonoString* style);
+		static void Internal_create0(MonoObject* managedInstance, __GUIContentInterop* labelContent, MonoString* style);
+		static void Internal_create1(MonoObject* managedInstance, MonoObject* labelText, uint32_t labelWidth, MonoString* style);
+		static void Internal_create2(MonoObject* managedInstance, MonoObject* labelText, MonoString* style);
+		static void Internal_create3(MonoObject* managedInstance, MonoString* style);
+	};
+}