Просмотр исходного кода

Added basic float and int fields

Marko Pintera 12 лет назад
Родитель
Сommit
b5772b5e95

+ 4 - 0
BansheeEditor/BansheeEditor.vcxproj

@@ -275,6 +275,8 @@
     <ClInclude Include="Include\BsEditorWidgetLayout.h" />
     <ClInclude Include="Include\BsEditorWidgetLayoutRTTI.h" />
     <ClInclude Include="Include\BsEditorWidgetManager.h" />
+    <ClInclude Include="Include\BsGUIFloatField.h" />
+    <ClInclude Include="Include\BsGUIIntField.h" />
     <ClInclude Include="Include\BsProjectLibraryEntriesRTTI.h" />
     <ClInclude Include="Include\BsEditorPrerequisites.h" />
     <ClInclude Include="Include\BsEditorWidget.h" />
@@ -319,6 +321,8 @@
     <ClCompile Include="Source\BsEditorWindowBase.cpp" />
     <ClCompile Include="Source\BsEditorWindowManager.cpp" />
     <ClCompile Include="Source\BsGUIDockSlider.cpp" />
+    <ClCompile Include="Source\BsGUIFloatField.cpp" />
+    <ClCompile Include="Source\BsGUIIntField.cpp" />
     <ClCompile Include="Source\BsGUIMenuBar.cpp" />
     <ClCompile Include="Source\BsGUIResourceTreeView.cpp" />
     <ClCompile Include="Source\BsGUISceneTreeView.cpp" />

+ 12 - 0
BansheeEditor/BansheeEditor.vcxproj.filters

@@ -153,6 +153,12 @@
     <ClInclude Include="Include\BsEditorWidgetLayoutRTTI.h">
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIIntField.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsGUIFloatField.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsEditorWidgetContainer.cpp">
@@ -257,5 +263,11 @@
     <ClCompile Include="Source\BsEditorWidgetLayout.cpp">
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIIntField.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsGUIFloatField.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 57 - 0
BansheeEditor/Include/BsGUIFloatField.h

@@ -0,0 +1,57 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIElementContainer.h"
+
+namespace BansheeEditor
+{
+	class BS_ED_EXPORT GUIFloatField : public BS::GUIElementContainer
+	{
+		struct PrivatelyConstruct {};
+
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUIFloatField* create(BS::GUIWidget& parent, const BS::GUIContent& labelContent, 
+			const BS::GUIOptions& layoutOptions, BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIFloatField* create(BS::GUIWidget& parent, const CM::HString& labelText, 
+			const BS::GUIOptions& layoutOptions, BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIFloatField* create(BS::GUIWidget& parent, 
+			const BS::GUIOptions& layoutOptions, BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIFloatField* create(BS::GUIWidget& parent, const BS::GUIContent& labelContent, 
+			BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIFloatField* create(BS::GUIWidget& parent, const CM::HString& labelText, 
+			BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIFloatField* create(BS::GUIWidget& parent, 
+			BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		GUIFloatField(const PrivatelyConstruct& dummy, BS::GUIWidget& parent, const BS::GUIContent& labelContent, 
+			BS::GUIElementStyle* labelStyle, BS::GUIElementStyle* inputBoxStyle, const BS::GUILayoutOptions& layoutOptions);
+
+		GUIFloatField(const PrivatelyConstruct& dummy, BS::GUIWidget& parent, 
+			BS::GUIElementStyle* labelStyle, BS::GUIElementStyle* inputBoxStyle, const BS::GUILayoutOptions& layoutOptions);
+
+		float getValue() const;
+		void setValue(float value);
+
+	protected:
+		virtual ~GUIFloatField();
+
+		void _updateLayoutInternal(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height,
+			CM::RectI clipRect, CM::UINT8 widgetDepth, CM::UINT16 areaDepth);
+	protected:
+		static const float SPLIT_POSITION;
+
+		BS::GUILabel* mLabel;
+		BS::GUIInputBox* mInputBox;
+
+		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
+
+		static bool floatFilter(const CM::WString& str);
+	};
+}

+ 57 - 0
BansheeEditor/Include/BsGUIIntField.h

@@ -0,0 +1,57 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIElementContainer.h"
+
+namespace BansheeEditor
+{
+	class BS_ED_EXPORT GUIIntField : public BS::GUIElementContainer
+	{
+		struct PrivatelyConstruct {};
+
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUIIntField* create(BS::GUIWidget& parent, const BS::GUIContent& labelContent, 
+			const BS::GUIOptions& layoutOptions, BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIIntField* create(BS::GUIWidget& parent, const CM::HString& labelText, 
+			const BS::GUIOptions& layoutOptions, BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIIntField* create(BS::GUIWidget& parent, 
+			const BS::GUIOptions& layoutOptions, BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIIntField* create(BS::GUIWidget& parent, const BS::GUIContent& labelContent, 
+			BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIIntField* create(BS::GUIWidget& parent, const CM::HString& labelText, 
+			BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		static GUIIntField* create(BS::GUIWidget& parent, 
+			BS::GUIElementStyle* labelStyle = nullptr, BS::GUIElementStyle* inputBoxStyle = nullptr);
+
+		GUIIntField(const PrivatelyConstruct& dummy, BS::GUIWidget& parent, const BS::GUIContent& labelContent, 
+			BS::GUIElementStyle* labelStyle, BS::GUIElementStyle* inputBoxStyle, const BS::GUILayoutOptions& layoutOptions);
+
+		GUIIntField(const PrivatelyConstruct& dummy, BS::GUIWidget& parent, 
+			BS::GUIElementStyle* labelStyle, BS::GUIElementStyle* inputBoxStyle, const BS::GUILayoutOptions& layoutOptions);
+
+		CM::INT32 getValue() const;
+		void setValue(CM::INT32 value);
+
+	protected:
+		virtual ~GUIIntField();
+
+		void _updateLayoutInternal(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height,
+			CM::RectI clipRect, CM::UINT8 widgetDepth, CM::UINT16 areaDepth);
+	protected:
+		static const float SPLIT_POSITION;
+
+		BS::GUILabel* mLabel;
+		BS::GUIInputBox* mInputBox;
+
+		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
+
+		static bool intFilter(const CM::WString& str);
+	};
+}

+ 198 - 0
BansheeEditor/Source/BsGUIFloatField.cpp

@@ -0,0 +1,198 @@
+#include "BsGUIFloatField.h"
+#include "BsGUIArea.h"
+#include "BsGUILayout.h"
+#include "BsGUILabel.h"
+#include "BsGUIInputBox.h"
+#include "BsGUISpace.h"
+#include "BsBuiltinResources.h"
+#include "BsGUIWidget.h"
+#include "BsGUIMouseEvent.h"
+#include <regex>
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	const float GUIFloatField::SPLIT_POSITION = 0.5f;
+
+	GUIFloatField::GUIFloatField(const PrivatelyConstruct& dummy, GUIWidget& parent, const GUIContent& labelContent, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle, const GUILayoutOptions& layoutOptions)
+		:GUIElementContainer(parent, layoutOptions), mLabel(nullptr), mInputBox(nullptr)
+	{
+		const GUIElementStyle* curLabelStyle = labelStyle;
+		const GUIElementStyle* curInputBoxStyle = inputBoxStyle;
+
+		if(curLabelStyle == nullptr)
+			curLabelStyle = parent.getSkin().getStyle("Label");
+
+		if(curInputBoxStyle == nullptr)
+			curInputBoxStyle = parent.getSkin().getStyle("InputBox");
+
+		mLabel = GUILabel::create(parent, labelContent, curLabelStyle);
+		mInputBox = GUIInputBox::create(parent, false, inputBoxStyle);
+		mInputBox->setFilter(&GUIFloatField::floatFilter);
+
+		_registerChildElement(mLabel);
+		_registerChildElement(mInputBox);
+	}
+
+	GUIFloatField::GUIFloatField(const PrivatelyConstruct& dummy, GUIWidget& parent, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle, const GUILayoutOptions& layoutOptions)
+		:GUIElementContainer(parent, layoutOptions), mLabel(nullptr), mInputBox(nullptr)
+	{
+		const GUIElementStyle* curInputBoxStyle = inputBoxStyle;
+
+		if(curInputBoxStyle == nullptr)
+			curInputBoxStyle = parent.getSkin().getStyle("InputBox");
+
+		mInputBox = GUIInputBox::create(parent, false, inputBoxStyle);
+		mInputBox->setFilter(&GUIFloatField::floatFilter);
+
+		_registerChildElement(mInputBox);
+	}
+
+	GUIFloatField::~GUIFloatField()
+	{
+
+	}
+
+	GUIFloatField* GUIFloatField::create(GUIWidget& parent, const GUIContent& labelContent, const GUIOptions& layoutOptions, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIFloatField>(PrivatelyConstruct(), parent, labelContent, labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(layoutOptions, &GUISkin::DefaultStyle));
+	}
+
+	GUIFloatField* GUIFloatField::create(GUIWidget& parent, const GUIContent& labelContent, GUIElementStyle* labelStyle, 
+		GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIFloatField>(PrivatelyConstruct(), parent, labelContent, labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(&GUISkin::DefaultStyle));
+	}
+
+	GUIFloatField* GUIFloatField::create(GUIWidget& parent, const HString& labelContent, const GUIOptions& layoutOptions, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIFloatField>(PrivatelyConstruct(), parent, GUIContent(labelContent), labelStyle, 
+			inputBoxStyle, GUILayoutOptions::create(layoutOptions, &GUISkin::DefaultStyle));
+	}
+
+	GUIFloatField* GUIFloatField::create(GUIWidget& parent, const HString& labelContent, GUIElementStyle* labelStyle, 
+		GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIFloatField>(PrivatelyConstruct(), parent, GUIContent(labelContent), labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(&GUISkin::DefaultStyle));
+	}
+
+	GUIFloatField* GUIFloatField::create(GUIWidget& parent, const GUIOptions& layoutOptions, GUIElementStyle* labelStyle, 
+		GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIFloatField>(PrivatelyConstruct(), parent, labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(layoutOptions, &GUISkin::DefaultStyle));
+	}
+
+	GUIFloatField* GUIFloatField::create(GUIWidget& parent, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIFloatField>(PrivatelyConstruct(), parent, labelStyle, inputBoxStyle, GUILayoutOptions::create(&GUISkin::DefaultStyle));
+	}
+
+	bool GUIFloatField::mouseEvent(const GUIMouseEvent& event)
+	{
+		GUIElementContainer::mouseEvent(event);
+
+		if(event.getType() == GUIMouseEventType::MouseDragStart)
+		{
+			// TODO -If over draggable area start drag
+
+			return true;
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDrag)
+		{
+			// TODO - If drag is started increase/lower the value
+
+			return true;
+		}
+		else if(event.getType() == GUIMouseEventType::MouseOut)
+		{
+			// TODO - Ensure cursor is set back to arrow
+
+			return true;
+		}
+		else if(event.getType() == GUIMouseEventType::MouseMove)
+		{
+			// TODO - If mouse is over drag area change cursor to Arrow Left/Right
+
+			return true;
+		}
+
+		return false;
+	}
+
+	float GUIFloatField::getValue() const
+	{
+		return parseFloat(mInputBox->getText());
+	}
+
+	void GUIFloatField::setValue(float value)
+	{
+		mInputBox->setText(toWString(value));
+	}
+
+	void GUIFloatField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
+		RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	{
+		UINT32 inputBoxOffset = 0;
+		UINT32 inputBoxWidth = width;
+
+		if(mLabel != nullptr)
+		{
+			UINT32 labelWidth = Math::roundToInt(width * SPLIT_POSITION);
+
+			Vector2I optimalSize = mLabel->_getOptimalSize();
+			INT32 yOffset = Math::roundToInt((height - optimalSize.y) * 0.5f);
+
+			Vector2I offset(x, y + yOffset);
+			mLabel->_setOffset(offset);
+			mLabel->_setWidth(labelWidth);
+			mLabel->_setHeight(optimalSize.y);
+			mLabel->_setAreaDepth(areaDepth);
+			mLabel->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			mLabel->_setClipRect(elemClipRect);
+
+			inputBoxOffset = labelWidth;
+			inputBoxWidth = width - labelWidth;
+		}
+
+		Vector2I inputBoxSize = mInputBox->_getOptimalSize();
+
+		{
+			Vector2I optimalSize = mInputBox->_getOptimalSize();
+			INT32 yOffset = Math::roundToInt((height - optimalSize.y) * 0.5f);
+
+			Vector2I offset(x + inputBoxOffset, y + yOffset);
+			mInputBox->_setOffset(offset);
+			mInputBox->_setWidth(inputBoxWidth);
+			mInputBox->_setHeight(optimalSize.y);
+			mInputBox->_setAreaDepth(areaDepth);
+			mInputBox->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			mInputBox->_setClipRect(elemClipRect);
+		}
+	}
+
+	const String& GUIFloatField::getGUITypeName()
+	{
+		static String typeName = "GUIFloatField";
+		return typeName;
+	}
+
+	bool GUIFloatField::floatFilter(const CM::WString& str)
+	{
+		return std::regex_match(str, std::wregex(L"-?(\\d+(\\.\\d*)?)?"));
+	}
+}

+ 198 - 0
BansheeEditor/Source/BsGUIIntField.cpp

@@ -0,0 +1,198 @@
+#include "BsGUIIntField.h"
+#include "BsGUIArea.h"
+#include "BsGUILayout.h"
+#include "BsGUILabel.h"
+#include "BsGUIInputBox.h"
+#include "BsGUISpace.h"
+#include "BsBuiltinResources.h"
+#include "BsGUIWidget.h"
+#include "BsGUIMouseEvent.h"
+#include <regex>
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	const float GUIIntField::SPLIT_POSITION = 0.5f;
+
+	GUIIntField::GUIIntField(const PrivatelyConstruct& dummy, GUIWidget& parent, const GUIContent& labelContent, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle, const GUILayoutOptions& layoutOptions)
+		:GUIElementContainer(parent, layoutOptions), mLabel(nullptr), mInputBox(nullptr)
+	{
+		const GUIElementStyle* curLabelStyle = labelStyle;
+		const GUIElementStyle* curInputBoxStyle = inputBoxStyle;
+
+		if(curLabelStyle == nullptr)
+			curLabelStyle = parent.getSkin().getStyle("Label");
+
+		if(curInputBoxStyle == nullptr)
+			curInputBoxStyle = parent.getSkin().getStyle("InputBox");
+
+		mLabel = GUILabel::create(parent, labelContent, curLabelStyle);
+		mInputBox = GUIInputBox::create(parent, false, inputBoxStyle);
+		mInputBox->setFilter(&GUIIntField::intFilter);
+
+		_registerChildElement(mLabel);
+		_registerChildElement(mInputBox);
+	}
+
+	GUIIntField::GUIIntField(const PrivatelyConstruct& dummy, GUIWidget& parent, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle, const GUILayoutOptions& layoutOptions)
+		:GUIElementContainer(parent, layoutOptions), mLabel(nullptr), mInputBox(nullptr)
+	{
+		const GUIElementStyle* curInputBoxStyle = inputBoxStyle;
+
+		if(curInputBoxStyle == nullptr)
+			curInputBoxStyle = parent.getSkin().getStyle("InputBox");
+
+		mInputBox = GUIInputBox::create(parent, false, inputBoxStyle);
+		mInputBox->setFilter(&GUIIntField::intFilter);
+
+		_registerChildElement(mInputBox);
+	}
+
+	GUIIntField::~GUIIntField()
+	{
+
+	}
+
+	GUIIntField* GUIIntField::create(GUIWidget& parent, const GUIContent& labelContent, const GUIOptions& layoutOptions, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIIntField>(PrivatelyConstruct(), parent, labelContent, labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(layoutOptions, &GUISkin::DefaultStyle));
+	}
+
+	GUIIntField* GUIIntField::create(GUIWidget& parent, const GUIContent& labelContent, GUIElementStyle* labelStyle, 
+		GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIIntField>(PrivatelyConstruct(), parent, labelContent, labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(&GUISkin::DefaultStyle));
+	}
+
+	GUIIntField* GUIIntField::create(GUIWidget& parent, const HString& labelContent, const GUIOptions& layoutOptions, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIIntField>(PrivatelyConstruct(), parent, GUIContent(labelContent), labelStyle, 
+			inputBoxStyle, GUILayoutOptions::create(layoutOptions, &GUISkin::DefaultStyle));
+	}
+
+	GUIIntField* GUIIntField::create(GUIWidget& parent, const HString& labelContent, GUIElementStyle* labelStyle, 
+		GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIIntField>(PrivatelyConstruct(), parent, GUIContent(labelContent), labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(&GUISkin::DefaultStyle));
+	}
+
+	GUIIntField* GUIIntField::create(GUIWidget& parent, const GUIOptions& layoutOptions, GUIElementStyle* labelStyle, 
+		GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIIntField>(PrivatelyConstruct(), parent, labelStyle, inputBoxStyle, 
+			GUILayoutOptions::create(layoutOptions, &GUISkin::DefaultStyle));
+	}
+
+	GUIIntField* GUIIntField::create(GUIWidget& parent, 
+		GUIElementStyle* labelStyle, GUIElementStyle* inputBoxStyle)
+	{
+		return cm_new<GUIIntField>(PrivatelyConstruct(), parent, labelStyle, inputBoxStyle, GUILayoutOptions::create(&GUISkin::DefaultStyle));
+	}
+
+	bool GUIIntField::mouseEvent(const GUIMouseEvent& event)
+	{
+		GUIElementContainer::mouseEvent(event);
+
+		if(event.getType() == GUIMouseEventType::MouseDragStart)
+		{
+			// TODO -If over draggable area start drag
+
+			return true;
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDrag)
+		{
+			// TODO - If drag is started increase/lower the value
+
+			return true;
+		}
+		else if(event.getType() == GUIMouseEventType::MouseOut)
+		{
+			// TODO - Ensure cursor is set back to arrow
+			
+			return true;
+		}
+		else if(event.getType() == GUIMouseEventType::MouseMove)
+		{
+			// TODO - If mouse is over drag area change cursor to Arrow Left/Right
+
+			return true;
+		}
+
+		return false;
+	}
+
+	CM::INT32 GUIIntField::getValue() const
+	{
+		return parseInt(mInputBox->getText());
+	}
+
+	void GUIIntField::setValue(CM::INT32 value)
+	{
+		mInputBox->setText(toWString(value));
+	}
+
+	void GUIIntField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
+		RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	{
+		UINT32 inputBoxOffset = 0;
+		UINT32 inputBoxWidth = width;
+
+		if(mLabel != nullptr)
+		{
+			UINT32 labelWidth = Math::roundToInt(width * SPLIT_POSITION);
+
+			Vector2I optimalSize = mLabel->_getOptimalSize();
+			INT32 yOffset = Math::roundToInt((height - optimalSize.y) * 0.5f);
+
+			Vector2I offset(x, y + yOffset);
+			mLabel->_setOffset(offset);
+			mLabel->_setWidth(labelWidth);
+			mLabel->_setHeight(optimalSize.y);
+			mLabel->_setAreaDepth(areaDepth);
+			mLabel->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			mLabel->_setClipRect(elemClipRect);
+
+			inputBoxOffset = labelWidth;
+			inputBoxWidth = width - labelWidth;
+		}
+
+		Vector2I inputBoxSize = mInputBox->_getOptimalSize();
+		
+		{
+			Vector2I optimalSize = mInputBox->_getOptimalSize();
+			INT32 yOffset = Math::roundToInt((height - optimalSize.y) * 0.5f);
+			
+			Vector2I offset(x + inputBoxOffset, y + yOffset);
+			mInputBox->_setOffset(offset);
+			mInputBox->_setWidth(inputBoxWidth);
+			mInputBox->_setHeight(optimalSize.y);
+			mInputBox->_setAreaDepth(areaDepth);
+			mInputBox->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			mInputBox->_setClipRect(elemClipRect);
+		}
+	}
+
+	const String& GUIIntField::getGUITypeName()
+	{
+		static String typeName = "GUIIntField";
+		return typeName;
+	}
+
+	bool GUIIntField::intFilter(const CM::WString& str)
+	{
+		return std::regex_match(str, std::wregex(L"-?(\\d+)?"));
+	}
+}

+ 6 - 0
BansheeEditor/Source/CmTestTextSprite.cpp

@@ -27,6 +27,8 @@
 #include "BsGUISceneTreeView.h"
 #include "BsGUIResourceTreeView.h"
 #include "BsGUIScrollArea.h"
+#include "BsGUIIntField.h"
+#include "BsGUIFloatField.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -88,6 +90,10 @@ namespace BansheeEditor
 		scrollArea->getLayout().addElement(mSceneTreeView);
 		area->getLayout().addElement(mResourceTreeView);
 
+		GUIIntField* intField = GUIIntField::create(*this, HString(L"Int Field"), GUIOptions(GUIOption::fixedWidth(200)));
+		GUIFloatField* floatField = GUIFloatField::create(*this, HString(L"Float Field"), GUIOptions(GUIOption::fixedWidth(200)));
+		area->getLayout().addElement(intField);
+		area->getLayout().addElement(floatField);
 		//GUIButton* button = GUIButton::create(*this, HString(L"dbgBtn"));
 		//button->onClick.connect(boost::bind(&TestTextSprite::dbgBtn, this));
 		//area->getLayout().addElement(button);

+ 3 - 0
BansheeEngine/Include/BsGUIInputBox.h

@@ -20,6 +20,8 @@ namespace BansheeEngine
 		const CM::WString& getText() const { return mText; }
 		void setText(const CM::WString& text);
 
+		void setFilter(std::function<bool(const CM::WString&)> filter) { mFilter = filter; }
+
 		virtual ElementType getElementType() const { return ElementType::InputBox; }
 
 		virtual CM::Vector2I _getOptimalSize() const;
@@ -86,6 +88,7 @@ namespace BansheeEngine
 		HSpriteTexture mActiveTexture;
 		IMAGE_SPRITE_DESC mImageDesc;
 		CM::WString mText;
+		std::function<bool(const CM::WString&)> mFilter;
 
 		bool mCaretShown;
 		bool mSelectionShown;

+ 135 - 46
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -98,22 +98,31 @@ namespace BansheeEngine
 
 	void GUIInputBox::setText(const CM::WString& text)
 	{
-		mText = text;
+		bool filterOkay = true;
+		if(mFilter != nullptr)
+		{
+			filterOkay = mFilter(text);
+		}
 
-		TEXT_SPRITE_DESC textDesc = getTextDesc();
-		Vector2I offset = getTextOffset();
+		if(filterOkay)
+		{
+			mText = text;
 
-		gGUIManager().getInputCaretTool()->updateText(this, textDesc);
-		gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
+			TEXT_SPRITE_DESC textDesc = getTextDesc();
+			Vector2I offset = getTextOffset();
+
+			gGUIManager().getInputCaretTool()->updateText(this, textDesc);
+			gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
 
-		if(mText.size() > 0)
-			gGUIManager().getInputCaretTool()->moveCaretToChar((UINT32)mText.size() - 1, CARET_AFTER);
-		else
-			gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
+			if(mText.size() > 0)
+				gGUIManager().getInputCaretTool()->moveCaretToChar((UINT32)mText.size() - 1, CARET_AFTER);
+			else
+				gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
 
-		scrollTextToCaret();
+			scrollTextToCaret();
 
-		markContentAsDirty();
+			markContentAsDirty();
+		}
 	}
 
 	UINT32 GUIInputBox::getNumRenderElements() const
@@ -521,13 +530,26 @@ namespace BansheeEngine
 			deleteSelectedText();
 
 		UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
-		insertChar(charIdx, ev.getInputChar());
 
-		gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+		bool filterOkay = true;
+		if(mFilter != nullptr)
+		{
+			WString newText = mText;
+			newText.insert(newText.begin() + charIdx, ev.getInputChar());
+
+			filterOkay = mFilter(newText);
+		}
 
-		scrollTextToCaret();
+		if(filterOkay)
+		{
+			insertChar(charIdx, ev.getInputChar());
+
+			gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+			scrollTextToCaret();
+
+			markContentAsDirty();
+		}
 
-		markContentAsDirty();
 		return true;
 	}
 
@@ -577,14 +599,26 @@ namespace BansheeEngine
 
 					if(charIdx < (UINT32)mText.size())
 					{
-						eraseChar(charIdx);
+						bool filterOkay = true;
+						if(mFilter != nullptr)
+						{
+							WString newText = mText;
+							newText.erase(charIdx, 1);
+
+							filterOkay = mFilter(newText);
+						}
 
-						if(charIdx > 0)
-							charIdx--;
+						if(filterOkay)
+						{
+							eraseChar(charIdx);
 
-						gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+							if(charIdx > 0)
+								charIdx--;
 
-						scrollTextToCaret();
+							gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+
+							scrollTextToCaret();
+						}
 					}
 				}
 
@@ -607,14 +641,26 @@ namespace BansheeEngine
 					UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
 					if(charIdx < (UINT32)mText.size())
 					{
-						eraseChar(charIdx);
+						bool filterOkay = true;
+						if(mFilter != nullptr)
+						{
+							WString newText = mText;
+							newText.erase(charIdx, 1);
+
+							filterOkay = mFilter(newText);
+						}
+
+						if(filterOkay)
+						{
+							eraseChar(charIdx);
 
-						if(charIdx > 0)
-							charIdx--;
+							if(charIdx > 0)
+								charIdx--;
 
-						gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
+							gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx, CARET_AFTER);
 
-						scrollTextToCaret();
+							scrollTextToCaret();
+						}
 					}
 				}
 
@@ -745,12 +791,27 @@ namespace BansheeEngine
 				if(mSelectionShown)
 					deleteSelectedText();
 
-				insertChar(gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos(), '\n');
+				UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
 
-				gGUIManager().getInputCaretTool()->moveCaretRight();
-				scrollTextToCaret();
+				bool filterOkay = true;
+				if(mFilter != nullptr)
+				{
+					WString newText = mText;
+					newText.insert(newText.begin() + charIdx, '\n');
+
+					filterOkay = mFilter(newText);
+				}
+
+				if(filterOkay)
+				{
+					insertChar(charIdx, '\n');
+
+					gGUIManager().getInputCaretTool()->moveCaretRight();
+					scrollTextToCaret();
+
+					markContentAsDirty();
+				}
 
-				markContentAsDirty();
 				return true;
 			}
 
@@ -930,24 +991,39 @@ namespace BansheeEngine
 	void GUIInputBox::deleteSelectedText()
 	{
 		UINT32 selStart = gGUIManager().getInputSelectionTool()->getSelectionStart();
-		mText.erase(mText.begin() + selStart, mText.begin() + gGUIManager().getInputSelectionTool()->getSelectionEnd());
-
-		TEXT_SPRITE_DESC textDesc = getTextDesc();
-		Vector2I offset = getTextOffset();
-		gGUIManager().getInputCaretTool()->updateText(this, textDesc);
-		gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
+		UINT32 selEnd = gGUIManager().getInputSelectionTool()->getSelectionEnd();
 
-		if(selStart > 0)
+		bool filterOkay = true;
+		if(mFilter != nullptr)
 		{
-			UINT32 newCaretPos = selStart - 1;
-			gGUIManager().getInputCaretTool()->moveCaretToChar(newCaretPos, CARET_AFTER);
+			WString newText = mText;
+			newText.erase(newText.begin() + selStart, newText.begin() + selEnd);
+
+			filterOkay = mFilter(newText);
 		}
-		else
+
+		if(filterOkay)
 		{
-			gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
+			mText.erase(mText.begin() + selStart, mText.begin() + selEnd);
+
+			TEXT_SPRITE_DESC textDesc = getTextDesc();
+			Vector2I offset = getTextOffset();
+			gGUIManager().getInputCaretTool()->updateText(this, textDesc);
+			gGUIManager().getInputSelectionTool()->updateText(this, textDesc);
+
+			if(selStart > 0)
+			{
+				UINT32 newCaretPos = selStart - 1;
+				gGUIManager().getInputCaretTool()->moveCaretToChar(newCaretPos, CARET_AFTER);
+			}
+			else
+			{
+				gGUIManager().getInputCaretTool()->moveCaretToChar(0, CARET_BEFORE);
+			}
+
+			scrollTextToCaret();
 		}
 
-		scrollTextToCaret();
 		clearSelection();
 	}
 
@@ -1027,13 +1103,26 @@ namespace BansheeEngine
 		WString textInClipboard = Platform::copyFromClipboard();
 
 		UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
-		insertString(charIdx, textInClipboard);
 
-		if(textInClipboard.size() > 0)
-			gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx + ((UINT32)textInClipboard.size() - 1), CARET_AFTER);
+		bool filterOkay = true;
+		if(mFilter != nullptr)
+		{
+			WString newText = mText;
+			newText.insert(newText.begin() + charIdx, textInClipboard.begin(), textInClipboard.end());
 
-		scrollTextToCaret();
+			filterOkay = mFilter(newText);
+		}
 
-		markContentAsDirty();
+		if(filterOkay)
+		{
+			insertString(charIdx, textInClipboard);
+
+			if(textInClipboard.size() > 0)
+				gGUIManager().getInputCaretTool()->moveCaretToChar(charIdx + ((UINT32)textInClipboard.size() - 1), CARET_AFTER);
+
+			scrollTextToCaret();
+
+			markContentAsDirty();
+		}
 	}
 }

+ 4 - 0
CamelotCore/Include/CmResources.h

@@ -116,6 +116,10 @@ namespace CamelotFramework
 		 * @param	filePath 	Full pathname of the file.
 		 * @param	overwrite	(optional) If true, any existing resource at the specified location will
 		 * 						be overwritten.
+		 * 						
+		 * @note	If the resource is a GpuResource and you are in some way modifying it from the Core thread, make
+		 * 			sure all those commands are submitted before you call this method. Otherwise an obsolete
+		 * 			version of the resource might get saved.
 		 */
 		void save(HResource resource, const WString& filePath, bool overwrite);