Browse Source

Added multi-select list boxes (untested)

BearishSun 10 years ago
parent
commit
e99b926919

+ 5 - 0
BansheeEditor/Include/BsBuiltinEditorResources.h

@@ -334,6 +334,11 @@ namespace BansheeEngine
 		static const WString DropDownBoxEntryNormalTex;
 		static const WString DropDownBoxEntryNormalTex;
 		static const WString DropDownBoxEntryHoverTex;
 		static const WString DropDownBoxEntryHoverTex;
 
 
+		static const WString DropDownBoxEntryToggleNormalTex;
+		static const WString DropDownBoxEntryToggleHoverTex;
+		static const WString DropDownBoxEntryToggleNormalOnTex;
+		static const WString DropDownBoxEntryToggleHoverOnTex;
+
 		static const WString DropDownBoxBtnUpNormalTex;
 		static const WString DropDownBoxBtnUpNormalTex;
 		static const WString DropDownBoxBtnUpHoverTex;
 		static const WString DropDownBoxBtnUpHoverTex;
 
 

+ 45 - 24
BansheeEditor/Include/BsGUIListBoxField.h

@@ -34,6 +34,7 @@ namespace BansheeEngine
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
@@ -42,13 +43,14 @@ namespace BansheeEngine
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const GUIContent& labelContent, UINT32 labelWidth, 
-			const GUIOptions& options, const String& style = StringUtil::BLANK);
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent, 
+			UINT32 labelWidth, const GUIOptions& options, const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 *							This will override any similar options set by style.
 		 *							This will override any similar options set by style.
@@ -56,13 +58,14 @@ namespace BansheeEngine
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const GUIContent& labelContent, 
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent,
 			const GUIOptions& options, const String& style = StringUtil::BLANK);
 			const GUIOptions& options, const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
@@ -71,13 +74,14 @@ namespace BansheeEngine
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const HString& labelText, UINT32 labelWidth, 
-			const GUIOptions& options, const String& style = StringUtil::BLANK);
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const HString& labelText, 
+			UINT32 labelWidth, const GUIOptions& options, const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 *							This will override any similar options set by style.
 		 *							This will override any similar options set by style.
@@ -85,84 +89,91 @@ namespace BansheeEngine
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const HString& labelText, const GUIOptions& options,
-			const String& style = StringUtil::BLANK);
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const HString& labelText, 
+			const GUIOptions& options, const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field without a label.
 		 * @brief	Creates a new GUI list box field without a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 *							This will override any similar options set by style.
 		 *							This will override any similar options set by style.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const GUIOptions& options, 
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const GUIOptions& options,
 			const String& style = StringUtil::BLANK);
 			const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const GUIContent& labelContent, UINT32 labelWidth,
-			const String& style = StringUtil::BLANK);
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent,
+			UINT32 labelWidth, const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	labelContent	Content to display in the editor field label.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const GUIContent& labelContent,
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent,
 			const String& style = StringUtil::BLANK);
 			const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	labelWidth		Width of the label in pixels.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const HString& labelText, UINT32 labelWidth,
-			const String& style = StringUtil::BLANK);
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const HString& labelText, 
+			UINT32 labelWidth, const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field with a label.
 		 * @brief	Creates a new GUI list box field with a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	labelText		String to display in the editor field label.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const HString& labelText,
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, const HString& labelText,
 			const String& style = StringUtil::BLANK);
 			const String& style = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new GUI list box field without a label.
 		 * @brief	Creates a new GUI list box field without a label.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBoxField* create(const Vector<HString>& elements, const String& style = StringUtil::BLANK);
+		static GUIListBoxField* create(const Vector<HString>& elements, bool multiselect, 
+			const String& style = StringUtil::BLANK);
 
 
-		GUIListBoxField(const PrivatelyConstruct& dummy, const Vector<HString>& elements, const GUIContent& labelContent, 
-			UINT32 labelWidth, const String& style, const GUIDimensions& dimensions, bool withLabel);
+		GUIListBoxField(const PrivatelyConstruct& dummy, const Vector<HString>& elements, bool multiselect, 
+			const GUIContent& labelContent, UINT32 labelWidth, const String& style, const GUIDimensions& dimensions, bool withLabel);
 
 
 		/**
 		/**
 		 * @brief	Changes the list box elements.
 		 * @brief	Changes the list box elements.
@@ -170,21 +181,32 @@ namespace BansheeEngine
 		void setElements(const Vector<HString>& elements);
 		void setElements(const Vector<HString>& elements);
 
 
 		/**
 		/**
-		 * @brief	Returns the index of the currently selected element.
+		 * @brief	Selects an element with the specified index.
 		 */
 		 */
-		UINT32 getIndex() const { return mIndex; }
+		void selectElement(UINT32 value);
 
 
 		/**
 		/**
-		 * @brief	Selects an element with the specified index.
+		 * @brief	Deselect element the element with the specified index. Only relevant for multi-select list boxes.
+		 */
+		void deselectElement(UINT32 idx);
+
+		/**
+		 * @brief	Returns states of all element in the list box (enabled or disabled).
+		 */
+		const Vector<bool>& getElementStates() const;
+
+		/**
+		 * @brief	Sets states for all list box elements. Only valid for multi-select list boxes. Number of states
+		 * 			must match number of list box elements.
 		 */
 		 */
-		void setIndex(UINT32 value);
+		void setElementStates(const Vector<bool>& states);
 
 
 		/**
 		/**
 		 * @copydoc	GUIElement::setTint
 		 * @copydoc	GUIElement::setTint
 		 */
 		 */
 		virtual void setTint(const Color& color) override;
 		virtual void setTint(const Color& color) override;
 
 
-		Event<void(UINT32)> onSelectionChanged; /**< Triggers when a new element is selected. Provides index to the element. */
+		Event<void(UINT32, bool)> onSelectionChanged; /**< Triggers when a new element is selected. Provides index to the element. */
 	protected:
 	protected:
 		static const UINT32 DEFAULT_LABEL_WIDTH;
 		static const UINT32 DEFAULT_LABEL_WIDTH;
 
 
@@ -208,11 +230,10 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @brief	Triggered when the selected list box element changes.
 		 * @brief	Triggered when the selected list box element changes.
 		 */
 		 */
-		void selectionChanged(UINT32 newIndex);
+		void selectionChanged(UINT32 newIndex, bool enabled);
 
 
 		GUIListBox* mListBox;
 		GUIListBox* mListBox;
 		GUILayout* mLayout;
 		GUILayout* mLayout;
 		GUILabel* mLabel;
 		GUILabel* mLabel;
-		UINT32 mIndex;
 	};
 	};
 }
 }

+ 32 - 0
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -205,6 +205,11 @@ namespace BansheeEngine
 	const WString BuiltinEditorResources::DropDownBoxEntryNormalTex = L"DropDownBoxEntryNormal.png";
 	const WString BuiltinEditorResources::DropDownBoxEntryNormalTex = L"DropDownBoxEntryNormal.png";
 	const WString BuiltinEditorResources::DropDownBoxEntryHoverTex = L"DropDownBoxEntryHover.png";
 	const WString BuiltinEditorResources::DropDownBoxEntryHoverTex = L"DropDownBoxEntryHover.png";
 
 
+	const WString BuiltinEditorResources::DropDownBoxEntryToggleNormalTex = L"DropDownBoxEntryToggleNormal.png";
+	const WString BuiltinEditorResources::DropDownBoxEntryToggleHoverTex = L"DropDownBoxEntryToggleHover.png";
+	const WString BuiltinEditorResources::DropDownBoxEntryToggleNormalOnTex = L"DropDownBoxEntryToggleNormalOn.png";
+	const WString BuiltinEditorResources::DropDownBoxEntryToggleHoverOnTex = L"DropDownBoxEntryToggleHoverOn.png";
+
 	const WString BuiltinEditorResources::DropDownBoxBtnUpNormalTex = L"DropDownBoxArrowUpNormal.png";
 	const WString BuiltinEditorResources::DropDownBoxBtnUpNormalTex = L"DropDownBoxArrowUpNormal.png";
 	const WString BuiltinEditorResources::DropDownBoxBtnUpHoverTex = L"DropDownBoxArrowUpHover.png";
 	const WString BuiltinEditorResources::DropDownBoxBtnUpHoverTex = L"DropDownBoxArrowUpHover.png";
 
 
@@ -890,6 +895,32 @@ namespace BansheeEngine
 
 
 		skin->setStyle(GUIDropDownContent::ENTRY_STYLE_TYPE, dropDownEntryBtnStyle);
 		skin->setStyle(GUIDropDownContent::ENTRY_STYLE_TYPE, dropDownEntryBtnStyle);
 
 
+		// DropDown toggle entry button
+		GUIElementStyle dropDownToggleEntryBtnStyle;
+		dropDownToggleEntryBtnStyle.normal.texture = getGUITexture(DropDownBoxEntryToggleNormalTex);
+		dropDownToggleEntryBtnStyle.hover.texture = getGUITexture(DropDownBoxEntryToggleHoverTex);
+		dropDownToggleEntryBtnStyle.active.texture = dropDownToggleEntryBtnStyle.hover.texture;
+		dropDownToggleEntryBtnStyle.normalOn.texture = getGUITexture(DropDownBoxEntryToggleNormalOnTex);
+		dropDownToggleEntryBtnStyle.hoverOn.texture = getGUITexture(DropDownBoxEntryToggleHoverOnTex);
+		dropDownToggleEntryBtnStyle.activeOn.texture = dropDownToggleEntryBtnStyle.hoverOn.texture;
+		dropDownToggleEntryBtnStyle.normal.textColor = TextNormalColor;
+		dropDownToggleEntryBtnStyle.hover.textColor = TextNormalColor;
+		dropDownToggleEntryBtnStyle.active.textColor = TextNormalColor;
+		dropDownToggleEntryBtnStyle.normalOn.textColor = TextNormalColor;
+		dropDownToggleEntryBtnStyle.hoverOn.textColor = TextNormalColor;
+		dropDownToggleEntryBtnStyle.activeOn.textColor = TextNormalColor;
+		dropDownToggleEntryBtnStyle.fixedHeight = true;
+		dropDownToggleEntryBtnStyle.fixedWidth = false;
+		dropDownToggleEntryBtnStyle.height = 18;
+		dropDownToggleEntryBtnStyle.width = 30;
+		dropDownToggleEntryBtnStyle.border.left = 17;
+		dropDownToggleEntryBtnStyle.font = font;
+		dropDownToggleEntryBtnStyle.fontSize = DefaultFontSize;
+		dropDownToggleEntryBtnStyle.textHorzAlign = THA_Left;
+		dropDownToggleEntryBtnStyle.textVertAlign = TVA_Center;
+
+		skin->setStyle(GUIDropDownContent::ENTRY_TOGGLE_STYLE_TYPE, dropDownToggleEntryBtnStyle);
+
 		// DropDown entry button with expand
 		// DropDown entry button with expand
 		GUIElementStyle dropDownEntryExpBtnStyle;
 		GUIElementStyle dropDownEntryExpBtnStyle;
 		dropDownEntryExpBtnStyle.normal.texture = getGUITexture(DropDownBoxEntryExpNormalTex);
 		dropDownEntryExpBtnStyle.normal.texture = getGUITexture(DropDownBoxEntryExpNormalTex);
@@ -930,6 +961,7 @@ namespace BansheeEngine
 		GUIElementStyle dropDownContentStyle;
 		GUIElementStyle dropDownContentStyle;
 		dropDownContentStyle.minWidth = 50;
 		dropDownContentStyle.minWidth = 50;
 		dropDownContentStyle.minHeight = 20;
 		dropDownContentStyle.minHeight = 20;
+		dropDownContentStyle.subStyles[GUIDropDownContent::ENTRY_TOGGLE_STYLE_TYPE] = GUIDropDownContent::ENTRY_TOGGLE_STYLE_TYPE;
 		dropDownContentStyle.subStyles[GUIDropDownContent::ENTRY_STYLE_TYPE] = GUIDropDownContent::ENTRY_STYLE_TYPE;
 		dropDownContentStyle.subStyles[GUIDropDownContent::ENTRY_STYLE_TYPE] = GUIDropDownContent::ENTRY_STYLE_TYPE;
 		dropDownContentStyle.subStyles[GUIDropDownContent::ENTRY_EXP_STYLE_TYPE] = GUIDropDownContent::ENTRY_EXP_STYLE_TYPE;
 		dropDownContentStyle.subStyles[GUIDropDownContent::ENTRY_EXP_STYLE_TYPE] = GUIDropDownContent::ENTRY_EXP_STYLE_TYPE;
 		dropDownContentStyle.subStyles[GUIDropDownContent::SEPARATOR_STYLE_TYPE] = GUIDropDownContent::SEPARATOR_STYLE_TYPE;
 		dropDownContentStyle.subStyles[GUIDropDownContent::SEPARATOR_STYLE_TYPE] = GUIDropDownContent::SEPARATOR_STYLE_TYPE;

+ 48 - 35
BansheeEditor/Source/BsGUIListBoxField.cpp

@@ -13,10 +13,9 @@ namespace BansheeEngine
 {
 {
 	const UINT32 GUIListBoxField::DEFAULT_LABEL_WIDTH = 100;
 	const UINT32 GUIListBoxField::DEFAULT_LABEL_WIDTH = 100;
 
 
-	GUIListBoxField::GUIListBoxField(const PrivatelyConstruct& dummy, const Vector<HString>& elements, 
+	GUIListBoxField::GUIListBoxField(const PrivatelyConstruct& dummy, const Vector<HString>& elements, bool multiselect,
 		const GUIContent& labelContent, UINT32 labelWidth, const String& style, const GUIDimensions& dimensions, bool withLabel)
 		const GUIContent& labelContent, UINT32 labelWidth, const String& style, const GUIDimensions& dimensions, bool withLabel)
-		:GUIElementContainer(dimensions, style),
-		mListBox(nullptr), mIndex(0), mLayout(nullptr), mLabel(nullptr)
+		:GUIElementContainer(dimensions, style), mListBox(nullptr), mLayout(nullptr), mLabel(nullptr)
 	{
 	{
 		mLayout = GUILayoutX::create();
 		mLayout = GUILayoutX::create();
 		_registerChildElement(mLayout);
 		_registerChildElement(mLayout);
@@ -27,10 +26,10 @@ namespace BansheeEngine
 			mLayout->addElement(mLabel);
 			mLayout->addElement(mLabel);
 		}
 		}
 
 
-		mListBox = GUIListBox::create(elements, getSubStyleName(getListBoxStyleType()));
+		mListBox = GUIListBox::create(elements, multiselect, getSubStyleName(getListBoxStyleType()));
 		mLayout->addElement(mListBox);
 		mLayout->addElement(mListBox);
 
 
-		mListBox->onSelectionChanged.connect(std::bind(&GUIListBoxField::selectionChanged, this, _1));
+		mListBox->onSelectionToggled.connect(std::bind(&GUIListBoxField::selectionChanged, this, _1, _2));
 	}
 	}
 
 
 	GUIListBoxField::~GUIListBoxField()
 	GUIListBoxField::~GUIListBoxField()
@@ -38,111 +37,112 @@ namespace BansheeEngine
 
 
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const GUIContent& labelContent, UINT32 labelWidth, 
-		const GUIOptions& options, const String& style)
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent, 
+		UINT32 labelWidth, const GUIOptions& options, const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, labelContent, labelWidth, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, labelContent, labelWidth, *curStyle,
 			GUIDimensions::create(options), true);
 			GUIDimensions::create(options), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const GUIContent& labelContent, const GUIOptions& options,
-		const String& style)
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent, 
+		const GUIOptions& options, const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, labelContent, DEFAULT_LABEL_WIDTH, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, labelContent, DEFAULT_LABEL_WIDTH, *curStyle,
 			GUIDimensions::create(options), true);
 			GUIDimensions::create(options), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const HString& labelText, UINT32 labelWidth, const GUIOptions& options,
-		const String& style)
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const HString& labelText, 
+		UINT32 labelWidth, const GUIOptions& options, const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, GUIContent(labelText), labelWidth, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, GUIContent(labelText), labelWidth, *curStyle,
 			GUIDimensions::create(options), true);
 			GUIDimensions::create(options), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const HString& labelText, const GUIOptions& options,
-		const String& style)
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const HString& labelText, 
+		const GUIOptions& options, const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, GUIContent(labelText), DEFAULT_LABEL_WIDTH, *curStyle,
-			GUIDimensions::create(options), true);
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, GUIContent(labelText), 
+			DEFAULT_LABEL_WIDTH, *curStyle, GUIDimensions::create(options), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const GUIOptions& options, const String& style)
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const GUIOptions& options, 
+		const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, GUIContent(), 0, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, GUIContent(), 0, *curStyle,
 			GUIDimensions::create(options), false);
 			GUIDimensions::create(options), false);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const GUIContent& labelContent, UINT32 labelWidth,
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent, UINT32 labelWidth,
 		const String& style)
 		const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, labelContent, labelWidth, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, labelContent, labelWidth, *curStyle,
 			GUIDimensions::create(), true);
 			GUIDimensions::create(), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const GUIContent& labelContent,
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const GUIContent& labelContent,
 		const String& style)
 		const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, labelContent, DEFAULT_LABEL_WIDTH, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, labelContent, DEFAULT_LABEL_WIDTH, *curStyle,
 			GUIDimensions::create(), true);
 			GUIDimensions::create(), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const HString& labelText, UINT32 labelWidth,
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const HString& labelText, UINT32 labelWidth,
 		const String& style)
 		const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, GUIContent(labelText), labelWidth, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, GUIContent(labelText), labelWidth, *curStyle,
 			GUIDimensions::create(), true);
 			GUIDimensions::create(), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const HString& labelText,
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const HString& labelText,
 		const String& style)
 		const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, GUIContent(labelText), DEFAULT_LABEL_WIDTH, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, GUIContent(labelText), DEFAULT_LABEL_WIDTH, *curStyle,
 			GUIDimensions::create(), true);
 			GUIDimensions::create(), true);
 	}
 	}
 
 
-	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, const String& style)
+	GUIListBoxField* GUIListBoxField::create(const Vector<HString>& elements, bool multiselect, const String& style)
 	{
 	{
 		const String* curStyle = &style;
 		const String* curStyle = &style;
 		if (*curStyle == StringUtil::BLANK)
 		if (*curStyle == StringUtil::BLANK)
 			curStyle = &GUIListBoxField::getGUITypeName();
 			curStyle = &GUIListBoxField::getGUITypeName();
 
 
-		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, GUIContent(), 0, *curStyle,
+		return bs_new<GUIListBoxField>(PrivatelyConstruct(), elements, multiselect, GUIContent(), 0, *curStyle,
 			GUIDimensions::create(), false);
 			GUIDimensions::create(), false);
 	}
 	}
 
 
@@ -151,12 +151,26 @@ namespace BansheeEngine
 		mListBox->setElements(elements);
 		mListBox->setElements(elements);
 	}
 	}
 
 
-	void GUIListBoxField::setIndex(UINT32 index)
+	void GUIListBoxField::selectElement(UINT32 index)
 	{
 	{
-		mIndex = index;
 		mListBox->selectElement(index);
 		mListBox->selectElement(index);
 	}
 	}
 
 
+	void GUIListBoxField::deselectElement(UINT32 idx)
+	{
+		mListBox->deselectElement(idx);
+	}
+
+	const Vector<bool>& GUIListBoxField::getElementStates() const
+	{
+		return mListBox->getElementStates();
+	}
+
+	void GUIListBoxField::setElementStates(const Vector<bool>& states)
+	{
+		mListBox->setElementStates(states);
+	}
+
 	void GUIListBoxField::setTint(const Color& color)
 	void GUIListBoxField::setTint(const Color& color)
 	{
 	{
 		if (mLabel != nullptr)
 		if (mLabel != nullptr)
@@ -184,10 +198,9 @@ namespace BansheeEngine
 		mListBox->setStyle(getSubStyleName(getListBoxStyleType()));
 		mListBox->setStyle(getSubStyleName(getListBoxStyleType()));
 	}
 	}
 
 
-	void GUIListBoxField::selectionChanged(UINT32 newIndex)
+	void GUIListBoxField::selectionChanged(UINT32 newIndex, bool enabled)
 	{
 	{
-		mIndex = newIndex;
-		onSelectionChanged(newIndex);
+		onSelectionChanged(newIndex, enabled);
 	}
 	}
 
 
 	const String& GUIListBoxField::getGUITypeName()
 	const String& GUIListBoxField::getGUITypeName()

+ 6 - 6
BansheeEditor/Source/BsGUITabButton.cpp

@@ -76,12 +76,12 @@ namespace BansheeEngine
 			mInactiveState = getState();
 			mInactiveState = getState();
 
 
 			if(mInactiveState != GUIButtonState::Normal)
 			if(mInactiveState != GUIButtonState::Normal)
-				setState(GUIButtonState::Normal);
+				_setState(GUIButtonState::Normal);
 		}
 		}
 		else
 		else
 		{
 		{
 			if(getState() != mInactiveState)
 			if(getState() != mInactiveState)
-				setState(mInactiveState);
+				_setState(mInactiveState);
 		}
 		}
 	}
 	}
 
 
@@ -93,7 +93,7 @@ namespace BansheeEngine
 
 
 			if(!mDraggedState)
 			if(!mDraggedState)
 			{
 			{
-				setState(state);
+				_setState(state);
 
 
 				if(!onHover.empty())
 				if(!onHover.empty())
 					onHover();
 					onHover();
@@ -109,7 +109,7 @@ namespace BansheeEngine
 
 
 			if(!mDraggedState)
 			if(!mDraggedState)
 			{
 			{
-				setState(state);
+				_setState(state);
 
 
 				if(!onOut.empty())
 				if(!onOut.empty())
 					onOut();
 					onOut();
@@ -122,7 +122,7 @@ namespace BansheeEngine
 		else if(ev.getType() == GUIMouseEventType::MouseDown)
 		else if(ev.getType() == GUIMouseEventType::MouseDown)
 		{
 		{
 			if(!mDraggedState)
 			if(!mDraggedState)
-				setState(_isOn() ? GUIButtonState::ActiveOn : GUIButtonState::Active);
+				_setState(_isOn() ? GUIButtonState::ActiveOn : GUIButtonState::Active);
 
 
 			_setElementDepth(0);
 			_setElementDepth(0);
 
 
@@ -132,7 +132,7 @@ namespace BansheeEngine
 		{
 		{
 			if(!mDraggedState)
 			if(!mDraggedState)
 			{
 			{
-				setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
+				_setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
 
 
 				if(!onClick.empty())
 				if(!onClick.empty())
 					onClick();
 					onClick();

+ 5 - 5
BansheeEngine/Include/BsGUIButtonBase.h

@@ -54,6 +54,11 @@ namespace BansheeEngine
 		 */
 		 */
 		bool _isOn() const;
 		bool _isOn() const;
 
 
+		/**
+		 * @brief	Change the internal button state, changing the button look depending on set style.
+		 */
+		void _setState(GUIButtonState state);
+
 		/**
 		/**
 		 * @copydoc	GUIElement::_getOptimalSize
 		 * @copydoc	GUIElement::_getOptimalSize
 		 */
 		 */
@@ -133,11 +138,6 @@ namespace BansheeEngine
 		 */
 		 */
 		TEXT_SPRITE_DESC getTextDesc() const;
 		TEXT_SPRITE_DESC getTextDesc() const;
 
 
-		/**
-		 * @brief	Change the internal button state, changing the button look depending on set style.
-		 */
-		void setState(GUIButtonState state);
-
 		/**
 		/**
 		 * @brief	Retrieves internal button state.
 		 * @brief	Retrieves internal button state.
 		 */
 		 */

+ 3 - 1
BansheeEngine/Include/BsGUIDropDownContent.h

@@ -17,7 +17,7 @@ namespace BansheeEngine
 		struct VisibleElement
 		struct VisibleElement
 		{
 		{
 			UINT32 idx = 0;
 			UINT32 idx = 0;
-			GUIButton* button = nullptr;
+			GUIButtonBase* button = nullptr;
 			GUITexture* separator = nullptr;
 			GUITexture* separator = nullptr;
 			GUILabel* shortcutLabel = nullptr;
 			GUILabel* shortcutLabel = nullptr;
 		};
 		};
@@ -72,6 +72,7 @@ namespace BansheeEngine
 		 */
 		 */
 		void setKeyboardFocus(bool focus);
 		void setKeyboardFocus(bool focus);
 
 
+		static const String ENTRY_TOGGLE_STYLE_TYPE;
 		static const String ENTRY_STYLE_TYPE;
 		static const String ENTRY_STYLE_TYPE;
 		static const String ENTRY_EXP_STYLE_TYPE;
 		static const String ENTRY_EXP_STYLE_TYPE;
 		static const String SEPARATOR_STYLE_TYPE;
 		static const String SEPARATOR_STYLE_TYPE;
@@ -131,5 +132,6 @@ namespace BansheeEngine
 		UINT32 mRangeStart, mRangeEnd;
 		UINT32 mRangeStart, mRangeEnd;
 		GUIDropDownMenu::DropDownSubMenu* mParent;
 		GUIDropDownMenu::DropDownSubMenu* mParent;
 		bool mKeyboardFocus;
 		bool mKeyboardFocus;
+		bool mIsToggle;
 	};
 	};
 }
 }

+ 6 - 0
BansheeEngine/Include/BsGUIDropDownMenu.h

@@ -105,6 +105,7 @@ namespace BansheeEngine
 	enum class GUIDropDownType
 	enum class GUIDropDownType
 	{
 	{
 		ListBox,
 		ListBox,
+		MultiListBox,
 		ContextMenu,
 		ContextMenu,
 		MenuBar
 		MenuBar
 	};
 	};
@@ -216,6 +217,11 @@ namespace BansheeEngine
 			 */
 			 */
 			void close();
 			void close();
 
 
+			/**
+			 * @brief	Returns the type of the displayed drop down menu.
+			 */
+			GUIDropDownType getType() const { return mType; }
+
 			/**
 			/**
 			 * @brief	Returns actual visible bounds of the sub-menu.
 			 * @brief	Returns actual visible bounds of the sub-menu.
 			 */
 			 */

+ 46 - 6
BansheeEngine/Include/BsGUIListBox.h

@@ -24,23 +24,40 @@ namespace BansheeEngine
 		 * @brief	Creates a new listbox with the provided elements.
 		 * @brief	Creates a new listbox with the provided elements.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBox* create(const Vector<HString>& elements, const String& styleName = StringUtil::BLANK);
+		static GUIListBox* create(const Vector<HString>& elements, bool multiselect = false, 
+			const String& styleName = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Creates a new listbox with the provided elements.
 		 * @brief	Creates a new listbox with the provided elements.
 		 *
 		 *
 		 * @param	elements		Elements to display in the list box.
 		 * @param	elements		Elements to display in the list box.
+		 * @param	multiselect		Determines should the listbox allow multiple elements to be selected or just one.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 *							This will override any similar options set by style.
 		 *							This will override any similar options set by style.
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 * @param	styleName		Optional style to use for the element. Style will be retrieved
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							from GUISkin of the GUIWidget the element is used on. If not specified
 		 *							default style is used.
 		 *							default style is used.
 		 */
 		 */
-		static GUIListBox* create(const Vector<HString>& elements, const GUIOptions& options, const String& styleName = StringUtil::BLANK);
+		static GUIListBox* create(const Vector<HString>& elements, bool multiselect, 
+			const GUIOptions& options, const String& styleName = StringUtil::BLANK);
+
+		/**
+		 * @brief	Creates a new single-select listbox with the provided elements.
+		 *
+		 * @param	elements		Elements to display in the list box.
+		 * @param	options			Options that allow you to control how is the element positioned and sized.
+		 *							This will override any similar options set by style.
+		 * @param	styleName		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.
+		 */
+		static GUIListBox* create(const Vector<HString>& elements, const GUIOptions& options, 
+			const String& styleName = StringUtil::BLANK);
 
 
 		/**
 		/**
 		 * @brief	Changes the list box elements.
 		 * @brief	Changes the list box elements.
@@ -52,21 +69,38 @@ namespace BansheeEngine
 		 */
 		 */
 		void selectElement(UINT32 idx);
 		void selectElement(UINT32 idx);
 
 
+		/**
+		 * @brief	Deselect element the element with the specified index. Only relevant for multi-select list boxes.
+		 */
+		void deselectElement(UINT32 idx);
+
+		/**
+		 * @brief	Returns states of all element in the list box (enabled or disabled).
+		 */
+		const Vector<bool>& getElementStates() const { return mElementStates; }
+
+		/**
+		 * @brief	Sets states for all list box elements. Only valid for multi-select list boxes. Number of states
+		 * 			must match number of list box elements.
+		 */
+		void setElementStates(const Vector<bool>& states);
+
 		/**
 		/**
 		 * @copydoc	GUIButtonBase::getElementType
 		 * @copydoc	GUIButtonBase::getElementType
 		 */
 		 */
 		virtual ElementType _getElementType() const override { return ElementType::ListBox; }
 		virtual ElementType _getElementType() const override { return ElementType::ListBox; }
 
 
 		/**
 		/**
-		 * @brief	Triggered whenever user selects a new element in the list box. Returned index
+		 * @brief	Triggered whenever user selects or deselects an element in the list box. Returned index
 		 *			maps to the element in the elements array that the list box was initialized with.
 		 *			maps to the element in the elements array that the list box was initialized with.
 		 */
 		 */
-		Event<void(UINT32)> onSelectionChanged;
+		Event<void(UINT32, bool)> onSelectionToggled;
 	protected:
 	protected:
 		~GUIListBox();
 		~GUIListBox();
 
 
 	private:
 	private:
-		GUIListBox(const String& styleName, const Vector<HString>& elements, const GUIDimensions& dimensions);
+		GUIListBox(const String& styleName, const Vector<HString>& elements, bool isMultiselect, 
+			const GUIDimensions& dimensions);
 
 
 		/**
 		/**
 		 * @copydoc	GUIButtonBase::mouseEvent
 		 * @copydoc	GUIButtonBase::mouseEvent
@@ -94,9 +128,15 @@ namespace BansheeEngine
 		 */
 		 */
 		void onListBoxClosed();
 		void onListBoxClosed();
 
 
+		/**
+		 * @brief	Updates visible contents depending on selected element(s).
+		 */
+		void updateContents();
+
 	private:
 	private:
-		UINT32 mSelectedIdx;
 		Vector<HString> mElements;
 		Vector<HString> mElements;
+		Vector<bool> mElementStates;
 		bool mIsListBoxOpen;
 		bool mIsListBoxOpen;
+		bool mIsMultiselect;
 	};
 	};
 }
 }

+ 1 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -41,6 +41,7 @@ namespace BansheeEngine
 	class GUIElementBase;
 	class GUIElementBase;
 	class GUIElement;
 	class GUIElement;
 	class GUILabel;
 	class GUILabel;
+	class GUIButtonBase;
 	class GUIButton;
 	class GUIButton;
 	class GUITexture;
 	class GUITexture;
 	class GUIToggle;
 	class GUIToggle;

+ 7 - 7
BansheeEngine/Source/BsGUIButtonBase.cpp

@@ -69,9 +69,9 @@ namespace BansheeEngine
 	void GUIButtonBase::_setOn(bool on) 
 	void GUIButtonBase::_setOn(bool on) 
 	{ 
 	{ 
 		if(on)
 		if(on)
-			setState((GUIButtonState)((INT32)mActiveState | 0x10)); 
+			_setState((GUIButtonState)((INT32)mActiveState | 0x10)); 
 		else
 		else
-			setState((GUIButtonState)((INT32)mActiveState & (~0x10))); 
+			_setState((GUIButtonState)((INT32)mActiveState & (~0x10))); 
 	}
 	}
 
 
 	bool GUIButtonBase::_isOn() const
 	bool GUIButtonBase::_isOn() const
@@ -307,7 +307,7 @@ namespace BansheeEngine
 	{
 	{
 		if(ev.getType() == GUIMouseEventType::MouseOver)
 		if(ev.getType() == GUIMouseEventType::MouseOver)
 		{
 		{
-			setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
+			_setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
 
 
 			if(!onHover.empty())
 			if(!onHover.empty())
 				onHover();
 				onHover();
@@ -316,7 +316,7 @@ namespace BansheeEngine
 		}
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseOut)
 		else if(ev.getType() == GUIMouseEventType::MouseOut)
 		{
 		{
-			setState(_isOn() ? GUIButtonState::NormalOn : GUIButtonState::Normal);
+			_setState(_isOn() ? GUIButtonState::NormalOn : GUIButtonState::Normal);
 
 
 			if(!onOut.empty())
 			if(!onOut.empty())
 				onOut();
 				onOut();
@@ -325,13 +325,13 @@ namespace BansheeEngine
 		}
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseDown)
 		else if(ev.getType() == GUIMouseEventType::MouseDown)
 		{
 		{
-			setState(_isOn() ? GUIButtonState::ActiveOn : GUIButtonState::Active);
+			_setState(_isOn() ? GUIButtonState::ActiveOn : GUIButtonState::Active);
 
 
 			return true;
 			return true;
 		}
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseUp)
 		else if(ev.getType() == GUIMouseEventType::MouseUp)
 		{
 		{
-			setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
+			_setState(_isOn() ? GUIButtonState::HoverOn : GUIButtonState::Hover);
 
 
 			if(!onClick.empty())
 			if(!onClick.empty())
 				onClick();
 				onClick();
@@ -365,7 +365,7 @@ namespace BansheeEngine
 		return textDesc;
 		return textDesc;
 	}
 	}
 
 
-	void GUIButtonBase::setState(GUIButtonState state)
+	void GUIButtonBase::_setState(GUIButtonState state)
 	{
 	{
 		Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
 		Vector2I origSize = mDimensions.calculateSizeRange(_getOptimalSize()).optimal;
 		mActiveState = state;
 		mActiveState = state;

+ 36 - 8
BansheeEngine/Source/BsGUIDropDownContent.cpp

@@ -14,16 +14,17 @@ using namespace std::placeholders;
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	const String GUIDropDownContent::ENTRY_TOGGLE_STYLE_TYPE = "DropDownEntryToggleBtn";
 	const String GUIDropDownContent::ENTRY_STYLE_TYPE = "DropDownEntryBtn";
 	const String GUIDropDownContent::ENTRY_STYLE_TYPE = "DropDownEntryBtn";
 	const String GUIDropDownContent::ENTRY_EXP_STYLE_TYPE = "DropDownEntryExpBtn";
 	const String GUIDropDownContent::ENTRY_EXP_STYLE_TYPE = "DropDownEntryExpBtn";
 	const String GUIDropDownContent::SEPARATOR_STYLE_TYPE = "DropDownSeparator";
 	const String GUIDropDownContent::SEPARATOR_STYLE_TYPE = "DropDownSeparator";
 
 
 	GUIDropDownContent::GUIDropDownContent(GUIDropDownMenu::DropDownSubMenu* parent, const GUIDropDownData& dropDownData, 
 	GUIDropDownContent::GUIDropDownContent(GUIDropDownMenu::DropDownSubMenu* parent, const GUIDropDownData& dropDownData, 
 		const String& style, const GUIDimensions& dimensions)
 		const String& style, const GUIDimensions& dimensions)
-		:GUIElementContainer(dimensions, style), mDropDownData(dropDownData),
-		mSelectedIdx(UINT_MAX), mRangeStart(0), mRangeEnd(0), mParent(parent)
+		:GUIElementContainer(dimensions, style), mDropDownData(dropDownData), mKeyboardFocus(true),
+		mSelectedIdx(UINT_MAX), mRangeStart(0), mRangeEnd(0), mParent(parent), mIsToggle(false)
 	{
 	{
-		
+		mIsToggle = mParent->getType() == GUIDropDownType::MultiListBox;
 	}
 	}
 
 
 	GUIDropDownContent::~GUIDropDownContent()
 	GUIDropDownContent::~GUIDropDownContent()
@@ -63,7 +64,12 @@ namespace BansheeEngine
 			else if (element.isSubMenu())
 			else if (element.isSubMenu())
 				visElem.button->setStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE));
 				visElem.button->setStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE));
 			else
 			else
-				visElem.button->setStyle(getSubStyleName(ENTRY_STYLE_TYPE));
+			{
+				if (mIsToggle)
+					visElem.button->setStyle(getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE));
+				else
+					visElem.button->setStyle(getSubStyleName(ENTRY_STYLE_TYPE));
+			}
 		}
 		}
 	}
 	}
 
 
@@ -117,7 +123,11 @@ namespace BansheeEngine
 			}
 			}
 			else
 			else
 			{
 			{
-				visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_STYLE_TYPE));
+				if (mIsToggle)
+					visElem.button = GUIToggle::create(getElementLocalizedName(i), getSubStyleName(ENTRY_STYLE_TYPE));
+				else
+					visElem.button = GUIButton::create(getElementLocalizedName(i), getSubStyleName(ENTRY_STYLE_TYPE));
+
 				visElem.button->onHover.connect(std::bind(onHover, i, curVisIdx));
 				visElem.button->onHover.connect(std::bind(onHover, i, curVisIdx));
 				visElem.button->onClick.connect(std::bind(onClick, i, curVisIdx));
 				visElem.button->onClick.connect(std::bind(onClick, i, curVisIdx));
 				_registerChildElement(visElem.button);
 				_registerChildElement(visElem.button);
@@ -146,7 +156,12 @@ namespace BansheeEngine
 		else if (mDropDownData.entries[idx].isSubMenu())
 		else if (mDropDownData.entries[idx].isSubMenu())
 			return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE))->height;
 			return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_EXP_STYLE_TYPE))->height;
 		else
 		else
-			return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_STYLE_TYPE))->height;
+		{
+			if (mIsToggle)
+				return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_TOGGLE_STYLE_TYPE))->height;
+			else
+				return _getParentWidget()->getSkin().getStyle(getSubStyleName(ENTRY_STYLE_TYPE))->height;
+		}
 	}
 	}
 
 
 	HString GUIDropDownContent::getElementLocalizedName(UINT32 idx) const
 	HString GUIDropDownContent::getElementLocalizedName(UINT32 idx) const
@@ -209,7 +224,12 @@ namespace BansheeEngine
 			if (mSelectedIdx == UINT_MAX)
 			if (mSelectedIdx == UINT_MAX)
 				selectNext(0);
 				selectNext(0);
 			else
 			else
+			{
+				if (mIsToggle)
+					mVisibleElements[mSelectedIdx].button->_setOn(!mVisibleElements[mSelectedIdx].button->_isOn());
+
 				mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getLayoutData().area);
 				mParent->elementActivated(mVisibleElements[mSelectedIdx].idx, mVisibleElements[mSelectedIdx].button->_getLayoutData().area);
+			}
 			return true;
 			return true;
 		}
 		}
 
 
@@ -219,10 +239,18 @@ namespace BansheeEngine
 	void GUIDropDownContent::setSelected(UINT32 idx)
 	void GUIDropDownContent::setSelected(UINT32 idx)
 	{
 	{
 		if (mSelectedIdx != UINT_MAX)
 		if (mSelectedIdx != UINT_MAX)
-			mVisibleElements[mSelectedIdx].button->_setOn(false);
+		{
+			if (mVisibleElements[mSelectedIdx].button->_isOn())
+				mVisibleElements[mSelectedIdx].button->_setState(GUIButtonState::NormalOn);
+			else
+				mVisibleElements[mSelectedIdx].button->_setState(GUIButtonState::Normal);
+		}
 
 
 		mSelectedIdx = idx;
 		mSelectedIdx = idx;
-		mVisibleElements[mSelectedIdx].button->_setOn(true);
+		if (mVisibleElements[mSelectedIdx].button->_isOn())
+			mVisibleElements[mSelectedIdx].button->_setState(GUIButtonState::HoverOn);
+		else
+			mVisibleElements[mSelectedIdx].button->_setState(GUIButtonState::Hover);
 
 
 		mParent->elementSelected(mVisibleElements[mSelectedIdx].idx);
 		mParent->elementSelected(mVisibleElements[mSelectedIdx].idx);
 	}
 	}

+ 3 - 1
BansheeEngine/Source/BsGUIDropDownMenu.cpp

@@ -63,6 +63,7 @@ namespace BansheeEngine
 			stylePrefix = "ContextMenu";
 			stylePrefix = "ContextMenu";
 			break;
 			break;
 		case GUIDropDownType::ListBox:
 		case GUIDropDownType::ListBox:
+		case GUIDropDownType::MultiListBox:
 			stylePrefix = "ListBox";
 			stylePrefix = "ListBox";
 			break;
 			break;
 		case GUIDropDownType::MenuBar:
 		case GUIDropDownType::MenuBar:
@@ -468,7 +469,8 @@ namespace BansheeEngine
 			if (callback != nullptr)
 			if (callback != nullptr)
 				callback();
 				callback();
 
 
-			GUIDropDownBoxManager::instance().closeDropDownBox();
+			if (mType != GUIDropDownType::MultiListBox)
+				GUIDropDownBoxManager::instance().closeDropDownBox();
 		}
 		}
 		else
 		else
 		{
 		{

+ 109 - 18
BansheeEngine/Source/BsGUIListBox.cpp

@@ -19,11 +19,15 @@ namespace BansheeEngine
 		return name;
 		return name;
 	}
 	}
 
 
-	GUIListBox::GUIListBox(const String& styleName, const Vector<HString>& elements, const GUIDimensions& dimensions)
-		:GUIButtonBase(styleName, GUIContent(HString(L"")), dimensions), mElements(elements), mSelectedIdx(0), mIsListBoxOpen(false)
+	GUIListBox::GUIListBox(const String& styleName, const Vector<HString>& elements, bool isMultiselect, const GUIDimensions& dimensions)
+		:GUIButtonBase(styleName, GUIContent(HString(L"")), dimensions), mElements(elements), mIsListBoxOpen(false),
+		mIsMultiselect(isMultiselect)
 	{
 	{
-		if(elements.size() > 0)
-			setContent(GUIContent(mElements[mSelectedIdx]));
+		mElementStates.resize(elements.size(), false);
+		if (!mIsMultiselect && mElementStates.size() > 0)
+			mElementStates[0] = true;
+
+		updateContents();
 	}
 	}
 
 
 	GUIListBox::~GUIListBox()
 	GUIListBox::~GUIListBox()
@@ -31,14 +35,19 @@ namespace BansheeEngine
 		closeListBox();
 		closeListBox();
 	}
 	}
 
 
-	GUIListBox* GUIListBox::create(const Vector<HString>& elements, const String& styleName)
+	GUIListBox* GUIListBox::create(const Vector<HString>& elements, bool isMultiselect, const String& styleName)
+	{
+		return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, isMultiselect, GUIDimensions::create());
+	}
+
+	GUIListBox* GUIListBox::create(const Vector<HString>& elements, bool isMultiselect, const GUIOptions& options, const String& styleName)
 	{
 	{
-		return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, GUIDimensions::create());
+		return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, isMultiselect, GUIDimensions::create(options));
 	}
 	}
 
 
 	GUIListBox* GUIListBox::create(const Vector<HString>& elements, const GUIOptions& options, const String& styleName)
 	GUIListBox* GUIListBox::create(const Vector<HString>& elements, const GUIOptions& options, const String& styleName)
 	{
 	{
-		return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, GUIDimensions::create(options));
+		return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, false, GUIDimensions::create(options));
 	}
 	}
 
 
 	void GUIListBox::setElements(const Vector<HString>& elements)
 	void GUIListBox::setElements(const Vector<HString>& elements)
@@ -49,10 +58,13 @@ namespace BansheeEngine
 			closeListBox();
 			closeListBox();
 
 
 		mElements = elements;
 		mElements = elements;
-		mSelectedIdx = 0;
 
 
-		if(elements.size() > 0)
-			setContent(GUIContent(mElements[mSelectedIdx]));
+		mElementStates.clear();
+		mElementStates.resize(mElements.size(), false);
+		if (!mIsMultiselect && mElementStates.size() > 0)
+			mElementStates[0] = true;
+
+		updateContents();
 
 
 		if(wasOpen)
 		if(wasOpen)
 			openListBox();
 			openListBox();
@@ -60,7 +72,34 @@ namespace BansheeEngine
 
 
 	void GUIListBox::selectElement(UINT32 idx)
 	void GUIListBox::selectElement(UINT32 idx)
 	{
 	{
-		elementSelected(idx);
+		if (idx >= (UINT32)mElements.size())
+			return;
+
+		if (mElementStates[idx] != true)
+			elementSelected(idx);
+	}
+
+	void GUIListBox::deselectElement(UINT32 idx)
+	{
+		if (!mIsMultiselect || idx >= (UINT32)mElements.size())
+			return;
+
+		if (mElementStates[idx] != false)
+			elementSelected(idx);
+	}
+
+	void GUIListBox::setElementStates(const Vector<bool>& states)
+	{
+		if (!mIsMultiselect)
+			return;
+
+		UINT32 min = (UINT32)std::min(mElementStates.size(), states.size());
+
+		for (UINT32 i = 0; i < min; i++)
+		{
+			if (mElementStates[i] != states[i])
+				elementSelected(i);
+		}
 	}
 	}
 
 
 	bool GUIListBox::_mouseEvent(const GUIMouseEvent& ev)
 	bool GUIListBox::_mouseEvent(const GUIMouseEvent& ev)
@@ -85,13 +124,28 @@ namespace BansheeEngine
 		if (idx >= (UINT32)mElements.size())
 		if (idx >= (UINT32)mElements.size())
 			return;
 			return;
 
 
-		if(!onSelectionChanged.empty())
-			onSelectionChanged(idx);
+		if (mIsMultiselect)
+		{
+			bool selected = mElementStates[idx];
+			mElementStates[idx] = !selected;
 
 
-		mSelectedIdx = idx;
-		setContent(GUIContent(mElements[idx]));
+			if (!onSelectionToggled.empty())
+				onSelectionToggled(idx, !selected);
+		}
+		else
+		{
+			for (UINT32 i = 0; i < (UINT32)mElementStates.size(); i++)
+				mElementStates[i] = false;
 
 
-		closeListBox();
+			mElementStates[idx] = true;
+
+			if (!onSelectionToggled.empty())
+				onSelectionToggled(idx, true);
+
+			closeListBox();
+		}
+
+		updateContents();
 	}
 	}
 
 
 	void GUIListBox::openListBox()
 	void GUIListBox::openListBox()
@@ -113,10 +167,16 @@ namespace BansheeEngine
 
 
 		desc.camera = widget->getCamera();
 		desc.camera = widget->getCamera();
 		desc.skin = widget->getSkinResource();
 		desc.skin = widget->getSkinResource();
-		desc.placement = DropDownAreaPlacement::aroundBoundsHorz(_getLayoutData().area);
+		desc.placement = DropDownAreaPlacement::aroundBoundsHorz(mClippedBounds);
 		
 		
+		GUIDropDownType type;
+		if (mIsMultiselect)
+			type = GUIDropDownType::MultiListBox;
+		else
+			type = GUIDropDownType::ListBox;
+
 		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(
 		GameObjectHandle<GUIDropDownMenu> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(
-			desc, GUIDropDownType::MenuBar, std::bind(&GUIListBox::onListBoxClosed, this));
+			desc, type, std::bind(&GUIListBox::onListBoxClosed, this));
 
 
 		_setOn(true);
 		_setOn(true);
 		mIsListBoxOpen = true;
 		mIsListBoxOpen = true;
@@ -133,6 +193,37 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void GUIListBox::updateContents()
+	{
+		UINT32 selectedIdx = 0;
+		UINT32 numSelected = 0;
+		for (UINT32 i = 0; i < (UINT32)mElementStates.size(); i++)
+		{
+			if (mElementStates[i])
+			{
+				selectedIdx = i;
+				numSelected++;
+			}
+		}
+
+		if (mIsMultiselect)
+		{
+			if (numSelected == 1)
+				setContent(GUIContent(mElements[selectedIdx]));
+			else if (numSelected == 0)
+				setContent(GUIContent(HEString(L"None")));
+			else
+				setContent(GUIContent(HEString(L"Multiple")));
+		}
+		else
+		{
+			for (UINT32 i = 0; i < (UINT32)mElementStates.size(); i++)
+				mElementStates[i] = false;
+
+			setContent(GUIContent(mElements[selectedIdx]));
+		}
+	}
+
 	void GUIListBox::onListBoxClosed()
 	void GUIListBox::onListBoxClosed()
 	{
 	{
 		_setOn(false);
 		_setOn(false);

+ 6 - 0
BansheeMono/Include/BsMonoArray.h

@@ -105,6 +105,12 @@ namespace BansheeEngine
 			return ScriptArray(mono_get_single_class(), size);
 			return ScriptArray(mono_get_single_class(), size);
 		}
 		}
 
 
+		template<>
+		static ScriptArray create<bool>(UINT32 size)
+		{
+			return ScriptArray(mono_get_boolean_class(), size);
+		}
+
 		UINT32 size() const;
 		UINT32 size() const;
 		UINT32 elementSize() const;
 		UINT32 elementSize() const;
 
 

+ 2 - 2
ExampleProject/Main/Main.cpp

@@ -402,7 +402,7 @@ namespace BansheeEngine
 		videoModeListBox->selectElement(selectedVideoModeIdx);
 		videoModeListBox->selectElement(selectedVideoModeIdx);
 
 
 		// Set up a callback to be notified when video mode changes
 		// Set up a callback to be notified when video mode changes
-		videoModeListBox->onSelectionChanged.connect(&videoModeChanged);
+		videoModeListBox->onSelectionToggled.connect(&videoModeChanged);
 	}
 	}
 
 
 	void toggleFullscreen()
 	void toggleFullscreen()
@@ -445,7 +445,7 @@ namespace BansheeEngine
 		sceneCamera->setAspectRatio(rwProps.getWidth() / (float)rwProps.getHeight());
 		sceneCamera->setAspectRatio(rwProps.getWidth() / (float)rwProps.getHeight());
 	}
 	}
 
 
-	void videoModeChanged(UINT32 idx)
+	void videoModeChanged(UINT32 idx, bool enabled)
 	{
 	{
 		selectedVideoMode = videoModes[idx];
 		selectedVideoMode = videoModes[idx];
 
 

+ 97 - 14
MBansheeEditor/GUI/GUIEnumField.cs

@@ -9,7 +9,7 @@ namespace BansheeEditor
     /// </summary>
     /// </summary>
     public sealed class GUIEnumField : GUIElement
     public sealed class GUIEnumField : GUIElement
     {
     {
-        public delegate void OnSelectionChangedDelegate(int value);
+        public delegate void OnSelectionChangedDelegate(UInt64 value);
 
 
         /// <summary>
         /// <summary>
         /// Triggered whenever user selects a new element in the list box. Returns the value of the enumeration entry that 
         /// Triggered whenever user selects a new element in the list box. Returns the value of the enumeration entry that 
@@ -18,18 +18,30 @@ namespace BansheeEditor
         public event OnSelectionChangedDelegate OnSelectionChanged;
         public event OnSelectionChangedDelegate OnSelectionChanged;
 
 
         /// <summary>
         /// <summary>
-        /// Value of the enumeration entry currently selected.
+        /// Value of the enumeration entry currently selected. This is a combination of all selected values in case a
+        /// multiselect list is used.
         /// </summary>
         /// </summary>
-        public int Value
+        public UInt64 Value
         {
         {
             get { return Internal_GetValue(mCachedPtr); }
             get { return Internal_GetValue(mCachedPtr); }
             set { Internal_SetValue(mCachedPtr, value); }
             set { Internal_SetValue(mCachedPtr, value); }
         }
         }
 
 
+        /// <summary>
+        /// States of all element in the list box (enabled or disabled).
+        /// </summary>
+        public bool[] States
+        {
+            get { return Internal_GetElementStates(mCachedPtr); }
+            set { Internal_SetElementStates(mCachedPtr, value); }
+        }
+
         /// <summary>
         /// <summary>
         /// Creates a new list box with enumeration entries as its elements and a label.
         /// Creates a new list box with enumeration entries as its elements and a label.
         /// </summary>
         /// </summary>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
@@ -37,47 +49,106 @@ namespace BansheeEditor
         ///                     default element style is used.</param>
         ///                     default element style is used.</param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIEnumField(Type enumType, GUIContent title, int titleWidth = 100, string style = "", params GUIOption[] options)
+        public GUIEnumField(Type enumType, bool multiselect, GUIContent title, int titleWidth = 100, string style = "", params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), title, titleWidth, style, options, true);
+            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), multiselect,
+                title, titleWidth, style, options, true);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Creates a new list box with enumeration entries as its elements and a label.
         /// Creates a new list box with enumeration entries as its elements and a label.
         /// </summary>
         /// </summary>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIEnumField(Type enumType, bool multiselect, GUIContent title, int titleWidth, params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), multiselect,
+                title, titleWidth, "", options, true);
+        }
+
+        /// <summary>
+        /// Creates a new single selection list box with enumeration entries as its elements and a label.
+        /// </summary>
+        /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
+        ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
+        ///                     default element style is used.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIEnumField(Type enumType, GUIContent title, int titleWidth = 100, string style = "", params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), false,
+                title, titleWidth, style, options, true);
+        }
+
+        /// <summary>
+        /// Creates a new single selection list box with enumeration entries as its elements and a label.
+        /// </summary>
+        /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
         public GUIEnumField(Type enumType, GUIContent title, int titleWidth, params GUIOption[] options)
         public GUIEnumField(Type enumType, GUIContent title, int titleWidth, params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), title, titleWidth, "", options, true);
+            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), false, 
+                title, titleWidth, "", options, true);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Creates a new list box with enumeration entries as its elements and no label.
         /// Creates a new list box with enumeration entries as its elements and no label.
         /// </summary>
         /// </summary>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
         ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
         ///                     default element style is used.</param>
         ///                     default element style is used.</param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIEnumField(Type enumType, string style = "", params GUIOption[] options)
+        public GUIEnumField(Type enumType, bool multiselect = false, string style = "", params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), null, 0, style, options, false);
+            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), multiselect, 
+                null, 0, style, options, false);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Creates a new list box with enumeration entries as its elements and no label.
         /// Creates a new list box with enumeration entries as its elements and no label.
         /// </summary>
         /// </summary>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
         /// <param name="enumType">Type of enum of whose entries to display in the list box.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIEnumField(Type enumType, params GUIOption[] options)
+        public GUIEnumField(Type enumType, bool multiselect = false, params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), multiselect, 
+                null, 0, "", options, false);
+        }
+
+        /// <summary>
+        /// Makes the element with the specified index selected.
+        /// </summary>
+        /// <param name="idx">Sequential index in the elements array provided on construction.</param>
+        public void SelectElement(int idx)
+        {
+            Internal_SelectElement(mCachedPtr, idx);
+        }
+
+        /// <summary>
+        /// Deselect element the element with the specified index. Only relevant for multi-select list boxes.
+        /// </summary>
+        /// <param name="idx">Sequential index in the elements array provided on construction.</param>
+        public void DeselectElement(int idx)
         {
         {
-            Internal_CreateInstance(this, Enum.GetNames(enumType), Enum.GetValues(enumType), null, 0, "", options, false);
+            Internal_DeselectElement(mCachedPtr, idx);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -92,7 +163,7 @@ namespace BansheeEditor
         /// <summary>
         /// <summary>
         /// Triggered by the native interop object when a user selects an object in the list.
         /// Triggered by the native interop object when a user selects an object in the list.
         /// </summary>
         /// </summary>
-        private void DoOnSelectionChanged(int index)
+        private void DoOnSelectionChanged(UInt64 index)
         {
         {
             if (OnSelectionChanged != null)
             if (OnSelectionChanged != null)
                 OnSelectionChanged(index);
                 OnSelectionChanged(index);
@@ -100,13 +171,25 @@ namespace BansheeEditor
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIEnumField instance, string[] names, Array values, 
         private static extern void Internal_CreateInstance(GUIEnumField instance, string[] names, Array values, 
-            GUIContent title, int titleWidth, string style, GUIOption[] options, bool withTitle);
+            bool multiselect, GUIContent title, int titleWidth, string style, GUIOption[] options, bool withTitle);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern UInt64 Internal_GetValue(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetValue(IntPtr nativeInstance, UInt64 value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool[] Internal_GetElementStates(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetElementStates(IntPtr nativeInstance, bool[] states);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern int Internal_GetValue(IntPtr nativeInstance);
+        private static extern void Internal_SelectElement(IntPtr nativeInstance, int idx);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetValue(IntPtr nativeInstance, int value);
+        private static extern void Internal_DeselectElement(IntPtr nativeInstance, int idx);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetTint(IntPtr nativeInstance, Color color);
         private static extern void Internal_SetTint(IntPtr nativeInstance, Color color);

+ 216 - 9
MBansheeEditor/GUI/GUIListBoxField.cs

@@ -5,7 +5,9 @@ using BansheeEngine;
 namespace BansheeEditor
 namespace BansheeEditor
 {
 {
     /// <summary>
     /// <summary>
-    /// Editor GUI element that displays a list box with user-specified elements and an optional label.
+    /// Editor GUI element that displays a list box with user-specified elements and an optional label. List box can be
+    /// a standard list-box that allows a single element to be selected, or a multi-select list box where any number of
+    /// elements can be selected at the same time.
     /// </summary>
     /// </summary>
     public sealed class GUIListBoxField : GUIElement
     public sealed class GUIListBoxField : GUIElement
     {
     {
@@ -26,11 +28,22 @@ namespace BansheeEditor
             set { Internal_SetValue(mCachedPtr, value); }
             set { Internal_SetValue(mCachedPtr, value); }
         }
         }
 
 
+        /// <summary>
+        /// States of all element in the list box (enabled or disabled).
+        /// </summary>
+        public bool[] States
+        {
+            get { return Internal_GetElementStates(mCachedPtr); }
+            set { Internal_SetElementStates(mCachedPtr, value); }
+        }
+
         /// <summary>
         /// <summary>
         /// Creates a new list box with the specified elements and a label.
         /// Creates a new list box with the specified elements and a label.
         /// </summary>
         /// </summary>
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         ///                        order as in the array.</param>
         ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
@@ -38,10 +51,10 @@ namespace BansheeEditor
         ///                     default element style is used.</param>
         ///                     default element style is used.</param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIListBoxField(LocString[] elements, GUIContent title, int titleWidth = 100, string style = "", 
+        public GUIListBoxField(LocString[] elements, bool multiselect, GUIContent title, int titleWidth, string style = "",
             params GUIOption[] options)
             params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, elements, title, titleWidth, style, options, true);
+            Internal_CreateInstance(this, elements, multiselect, title, titleWidth, style, options, true);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -49,13 +62,47 @@ namespace BansheeEditor
         /// </summary>
         /// </summary>
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         ///                        order as in the array.</param>
         ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(LocString[] elements, bool multiselect, GUIContent title, int titleWidth = 100, params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, elements, multiselect, title, titleWidth, "", options, true);
+        }
+
+        /// <summary>
+        /// Creates a new single-selection list box with the specified elements and a label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
+        ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
+        ///                     default element style is used.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(LocString[] elements, GUIContent title, int titleWidth, string style = "", 
+            params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, elements, false, title, titleWidth, style, options, true);
+        }
+
+        /// <summary>
+        /// Creates a new single-selection list box with the specified elements and a label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="title">Content to display on the label.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="titleWidth">Width of the title label in pixels.</param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
         public GUIListBoxField(LocString[] elements, GUIContent title, int titleWidth = 100, params GUIOption[] options)
         public GUIListBoxField(LocString[] elements, GUIContent title, int titleWidth = 100, params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, elements, title, titleWidth, "", options, true);
+            Internal_CreateInstance(this, elements, false, title, titleWidth, "", options, true);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -63,14 +110,16 @@ namespace BansheeEditor
         /// </summary>
         /// </summary>
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         ///                        order as in the array.</param>
         ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
         ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
         ///                     default element style is used.</param>
         ///                     default element style is used.</param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIListBoxField(LocString[] elements, string style = "", params GUIOption[] options)
+        public GUIListBoxField(LocString[] elements, bool multiselect = false, string style = "", params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, elements, null, 0, style, options, false);
+            Internal_CreateInstance(this, elements, multiselect, null, 0, style, options, false);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -78,11 +127,112 @@ namespace BansheeEditor
         /// </summary>
         /// </summary>
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         ///                        order as in the array.</param>
         ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIListBoxField(LocString[] elements, params GUIOption[] options)
+        public GUIListBoxField(LocString[] elements, bool multiselect = false, params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, elements, null, 0, "", options, false);
+            Internal_CreateInstance(this, elements, multiselect, null, 0, "", options, false);
+        }
+
+        /// <summary>
+        /// Creates a new list box with the specified elements and a label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
+        ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
+        ///                     default element style is used.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(string[] elements, bool multiselect, GUIContent title, int titleWidth, string style = "",
+            params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, ToLocalizedElements(elements), multiselect, title, titleWidth, style, options, true);
+        }
+
+        /// <summary>
+        /// Creates a new list box with the specified elements and a label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(string[] elements, bool multiselect, GUIContent title, int titleWidth = 100, params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, ToLocalizedElements(elements), multiselect, title, titleWidth, "", options, true);
+        }
+
+        /// <summary>
+        /// Creates a new single-selection list box with the specified elements and a label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
+        ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
+        ///                     default element style is used.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(string[] elements, GUIContent title, int titleWidth, string style = "",
+            params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, ToLocalizedElements(elements), false, title, titleWidth, style, options, true);
+        }
+
+        /// <summary>
+        /// Creates a new single-selection list box with the specified elements and a label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        /// <param name="title">Content to display on the label.</param>
+        /// <param name="titleWidth">Width of the title label in pixels.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(string[] elements, GUIContent title, int titleWidth = 100, params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, ToLocalizedElements(elements), false, title, titleWidth, "", options, true);
+        }
+
+        /// <summary>
+        /// Creates a new list box with the specified elements and no label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
+        /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
+        ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
+        ///                     default element style is used.</param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(string[] elements, bool multiselect = false, string style = "", params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, ToLocalizedElements(elements), multiselect, null, 0, style, options, false);
+        }
+
+        /// <summary>
+        /// Creates a new list box with the specified elements and no label.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
+        /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
+        ///                       override any similar options set by style.</param>
+        public GUIListBoxField(string[] elements, bool multiselect = false, params GUIOption[] options)
+        {
+            Internal_CreateInstance(this, ToLocalizedElements(elements), multiselect, null, 0, "", options, false);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -95,6 +245,34 @@ namespace BansheeEditor
             Internal_SetElements(mCachedPtr, elements);
             Internal_SetElements(mCachedPtr, elements);
         }
         }
 
 
+        /// <summary>
+        /// Updates the list box with a new set of elements.
+        /// </summary>
+        /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
+        ///                        order as in the array.</param>
+        public void SetElements(string[] elements)
+        {
+            Internal_SetElements(mCachedPtr, ToLocalizedElements(elements));
+        }
+
+        /// <summary>
+        /// Makes the element with the specified index selected.
+        /// </summary>
+        /// <param name="idx">Sequential index in the elements array provided on construction.</param>
+        public void SelectElement(int idx)
+        {
+            Internal_SelectElement(mCachedPtr, idx);
+        }
+
+        /// <summary>
+        /// Deselect element the element with the specified index. Only relevant for multi-select list boxes.
+        /// </summary>
+        /// <param name="idx">Sequential index in the elements array provided on construction.</param>
+        public void DeselectElement(int idx)
+        {
+            Internal_DeselectElement(mCachedPtr, idx);
+        }
+
         /// <summary>
         /// <summary>
         /// Colors the element with a specific tint.
         /// Colors the element with a specific tint.
         /// </summary>
         /// </summary>
@@ -104,6 +282,23 @@ namespace BansheeEditor
             Internal_SetTint(mCachedPtr, color);
             Internal_SetTint(mCachedPtr, color);
         }
         }
 
 
+        /// <summary>
+        /// Converts a set of normal strings to a set of localized strings.
+        /// </summary>
+        /// <param name="elements">Strings to convert.</param>
+        /// <returns>Localized strings created from input strings.</returns>
+        private LocString[] ToLocalizedElements(string[] elements)
+        {
+            if (elements == null)
+                return null;
+
+            LocString[] locElements = new LocString[elements.Length];
+            for (int i = 0; i < locElements.Length; i++)
+                locElements[i] = elements[i];
+
+            return locElements;
+        }
+
         /// <summary>
         /// <summary>
         /// Triggered by the native interop object when a user selects an object in the list.
         /// Triggered by the native interop object when a user selects an object in the list.
         /// </summary>
         /// </summary>
@@ -114,7 +309,7 @@ namespace BansheeEditor
         }
         }
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(GUIListBoxField instance, LocString[] entries,
+        private static extern void Internal_CreateInstance(GUIListBoxField instance, LocString[] entries, bool multiselect,
             GUIContent title, int titleWidth, string style, GUIOption[] options, bool withTitle);
             GUIContent title, int titleWidth, string style, GUIOption[] options, bool withTitle);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
@@ -126,6 +321,18 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetValue(IntPtr nativeInstance, int value);
         private static extern void Internal_SetValue(IntPtr nativeInstance, int value);
 
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool[] Internal_GetElementStates(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetElementStates(IntPtr nativeInstance, bool[] states);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SelectElement(IntPtr nativeInstance, int idx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_DeselectElement(IntPtr nativeInstance, int idx);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetTint(IntPtr nativeInstance, Color color);
         private static extern void Internal_SetTint(IntPtr nativeInstance, Color color);
     }
     }

+ 115 - 84
MBansheeEditor/Inspectors/CameraInspector.cs

@@ -10,21 +10,24 @@ namespace BansheeEditor
     public class CameraInspector : Inspector
     public class CameraInspector : Inspector
     {
     {
         private bool isInitialized;
         private bool isInitialized;
-        private GUIEnumField mProjectionTypeField = new GUIEnumField(typeof(ProjectionType), new LocEdString("Projection type"));
-        private GUISliderField mFieldOfView = new GUISliderField(1, 360, new LocEdString("Field of view"));
-        private GUIFloatField mOrthoHeight = new GUIFloatField(new LocEdString("Orthographic height"));
-        private GUIFloatField mAspectField = new GUIFloatField(new LocEdString("Aspect ratio"));
-        private GUIFloatField mNearPlaneField = new GUIFloatField(new LocEdString("Near plane"));
-        private GUIFloatField mFarPlaneField = new GUIFloatField(new LocEdString("Far plane"));
-        private GUIFloatField mViewportXField = new GUIFloatField(new LocEdString("X"), 30);
-        private GUIFloatField mViewportYField = new GUIFloatField(new LocEdString("Y"), 30);
-        private GUIFloatField mViewportWidthField = new GUIFloatField(new LocEdString("Width"), 30);
-        private GUIFloatField mViewportHeightField = new GUIFloatField(new LocEdString("Height"), 30);
-        private GUIEnumField mClearFlagsFields = new GUIEnumField(typeof (ClearFlags), new LocEdString("Clear flags"));
-        private GUIIntField mClearStencilField = new GUIIntField(new LocEdString("Clear stencil"));
-        private GUIFloatField mClearDepthField = new GUIFloatField(new LocEdString("Clear depth"));
-        private GUIColorField mClearColorField = new GUIColorField(new LocEdString("Clear color"));
-        private GUIIntField mPriorityField = new GUIIntField(new LocEdString("Render priority"));
+        private GUIEnumField projectionTypeField = new GUIEnumField(typeof(ProjectionType), new LocEdString("Projection type"));
+        private GUISliderField fieldOfView = new GUISliderField(1, 360, new LocEdString("Field of view"));
+        private GUIFloatField orthoHeight = new GUIFloatField(new LocEdString("Orthographic height"));
+        private GUIFloatField aspectField = new GUIFloatField(new LocEdString("Aspect ratio"));
+        private GUIFloatField nearPlaneField = new GUIFloatField(new LocEdString("Near plane"));
+        private GUIFloatField farPlaneField = new GUIFloatField(new LocEdString("Far plane"));
+        private GUIFloatField viewportXField = new GUIFloatField(new LocEdString("X"), 30);
+        private GUIFloatField viewportYField = new GUIFloatField(new LocEdString("Y"), 30);
+        private GUIFloatField viewportWidthField = new GUIFloatField(new LocEdString("Width"), 30);
+        private GUIFloatField viewportHeightField = new GUIFloatField(new LocEdString("Height"), 30);
+        private GUIEnumField clearFlagsFields = new GUIEnumField(typeof (ClearFlags), true, new LocEdString("Clear flags"));
+        private GUIIntField clearStencilField = new GUIIntField(new LocEdString("Clear stencil"));
+        private GUIFloatField clearDepthField = new GUIFloatField(new LocEdString("Clear depth"));
+        private GUIColorField clearColorField = new GUIColorField(new LocEdString("Clear color"));
+        private GUIIntField priorityField = new GUIIntField(new LocEdString("Render priority"));
+        private GUIListBoxField layersField = new GUIListBoxField(Layers.Names, true, new LocEdString("Layers"));
+
+        private ulong layersValue = 0;
 
 
         /// <summary>
         /// <summary>
         /// Initializes required data the first time <see cref="Refresh"/> is called.
         /// Initializes required data the first time <see cref="Refresh"/> is called.
@@ -35,71 +38,89 @@ namespace BansheeEditor
             {
             {
                 Camera camera = (Camera) referencedObject;
                 Camera camera = (Camera) referencedObject;
 
 
-                mProjectionTypeField.OnSelectionChanged += x =>
+                projectionTypeField.OnSelectionChanged += x =>
                 {
                 {
                     camera.ProjectionType = (ProjectionType) x;
                     camera.ProjectionType = (ProjectionType) x;
 
 
                     if (camera.ProjectionType == ProjectionType.Orthographic)
                     if (camera.ProjectionType == ProjectionType.Orthographic)
                     {
                     {
-                        mFieldOfView.Visible = false;
-                        mOrthoHeight.Visible = true;
+                        fieldOfView.Visible = false;
+                        orthoHeight.Visible = true;
                     }
                     }
                     else
                     else
                     {
                     {
-                        mFieldOfView.Visible = true;
-                        mOrthoHeight.Visible = false;
+                        fieldOfView.Visible = true;
+                        orthoHeight.Visible = false;
                     }
                     }
                 };
                 };
 
 
-                Debug.Log(camera.FieldOfView);
-                
-                mFieldOfView.OnChanged += x => camera.FieldOfView = x;
-                mOrthoHeight.OnChanged += x => camera.OrthoHeight = x;
-                mAspectField.OnChanged += x => camera.AspectRatio = x;
-                mNearPlaneField.OnChanged += x => camera.NearClipPlane = x;
-                mFarPlaneField.OnChanged += x => camera.FarClipPlane = x;
-                mViewportXField.OnChanged += x => 
+                {
+                    ulong layers = 0;
+                    bool[] states = layersField.States;
+                    for (int i = 0; i < states.Length; i++)
+                        layers |= states[i] ? Layers.Values[i] : 0;
+
+                    layersValue = layers;
+                }
+
+                fieldOfView.OnChanged += x => camera.FieldOfView = x;
+                orthoHeight.OnChanged += x => camera.OrthoHeight = x;
+                aspectField.OnChanged += x => camera.AspectRatio = x;
+                nearPlaneField.OnChanged += x => camera.NearClipPlane = x;
+                farPlaneField.OnChanged += x => camera.FarClipPlane = x;
+                viewportXField.OnChanged += x => 
                     { Rect2 rect = camera.ViewportRect; rect.x = x; camera.ViewportRect = rect; };
                     { Rect2 rect = camera.ViewportRect; rect.x = x; camera.ViewportRect = rect; };
-                mViewportYField.OnChanged += x => 
+                viewportYField.OnChanged += x => 
                     { Rect2 rect = camera.ViewportRect; rect.y = x; camera.ViewportRect = rect; };
                     { Rect2 rect = camera.ViewportRect; rect.y = x; camera.ViewportRect = rect; };
-                mViewportWidthField.OnChanged += x => 
+                viewportWidthField.OnChanged += x => 
                     { Rect2 rect = camera.ViewportRect; rect.width = x; camera.ViewportRect = rect; };
                     { Rect2 rect = camera.ViewportRect; rect.width = x; camera.ViewportRect = rect; };
-                mViewportHeightField.OnChanged += x => 
+                viewportHeightField.OnChanged += x => 
                     { Rect2 rect = camera.ViewportRect; rect.height = x; camera.ViewportRect = rect; };
                     { Rect2 rect = camera.ViewportRect; rect.height = x; camera.ViewportRect = rect; };
-                mClearFlagsFields.OnSelectionChanged += x => camera.ClearFlags = (ClearFlags)x;
-                mClearStencilField.OnChanged += x => camera.ClearStencil = (ushort)x;
-                mClearDepthField.OnChanged += x => camera.ClearDepth = x;
-                mClearColorField.OnChanged += x => camera.ClearColor = x;
-                mPriorityField.OnChanged += x => camera.Priority = x;
-
-                layout.AddElement(mProjectionTypeField);
-                layout.AddElement(mFieldOfView);
-                layout.AddElement(mOrthoHeight);
-                layout.AddElement(mAspectField);
-                layout.AddElement(mNearPlaneField);
-                layout.AddElement(mFarPlaneField);
+                clearFlagsFields.OnSelectionChanged += x => camera.ClearFlags = (ClearFlags)x;
+                clearStencilField.OnChanged += x => camera.ClearStencil = (ushort)x;
+                clearDepthField.OnChanged += x => camera.ClearDepth = x;
+                clearColorField.OnChanged += x => camera.ClearColor = x;
+                priorityField.OnChanged += x => camera.Priority = x;
+                layersField.OnSelectionChanged += x =>
+                {
+                    ulong layers = 0;
+                    bool[] states = layersField.States;
+                    for (int i = 0; i < states.Length; i++)
+                        layers |= states[i] ? Layers.Values[i] : 0;
+
+                    layersValue = layers;
+                    camera.Layers = layers;
+                };
+
+                layout.AddElement(projectionTypeField);
+                layout.AddElement(fieldOfView);
+                layout.AddElement(orthoHeight);
+                layout.AddElement(aspectField);
+                layout.AddElement(nearPlaneField);
+                layout.AddElement(farPlaneField);
                 GUILayoutX viewportTopLayout = layout.AddLayoutX();
                 GUILayoutX viewportTopLayout = layout.AddLayoutX();
                 viewportTopLayout.AddElement(new GUILabel(new LocEdString("Viewport"), GUIOption.FixedWidth(100)));
                 viewportTopLayout.AddElement(new GUILabel(new LocEdString("Viewport"), GUIOption.FixedWidth(100)));
                 GUILayoutY viewportContentLayout = viewportTopLayout.AddLayoutY();
                 GUILayoutY viewportContentLayout = viewportTopLayout.AddLayoutY();
 
 
                 GUILayoutX viewportTopRow = viewportContentLayout.AddLayoutX();
                 GUILayoutX viewportTopRow = viewportContentLayout.AddLayoutX();
-                viewportTopRow.AddElement(mViewportXField);
-                viewportTopRow.AddElement(mViewportWidthField);
+                viewportTopRow.AddElement(viewportXField);
+                viewportTopRow.AddElement(viewportWidthField);
 
 
                 GUILayoutX viewportBotRow = viewportContentLayout.AddLayoutX();
                 GUILayoutX viewportBotRow = viewportContentLayout.AddLayoutX();
-                viewportBotRow.AddElement(mViewportYField);
-                viewportBotRow.AddElement(mViewportHeightField);
+                viewportBotRow.AddElement(viewportYField);
+                viewportBotRow.AddElement(viewportHeightField);
 
 
-                layout.AddElement(mClearFlagsFields);
-                layout.AddElement(mClearColorField);
-                layout.AddElement(mClearDepthField);
-                layout.AddElement(mClearStencilField);
-                layout.AddElement(mPriorityField);
+                layout.AddElement(clearFlagsFields);
+                layout.AddElement(clearColorField);
+                layout.AddElement(clearDepthField);
+                layout.AddElement(clearStencilField);
+                layout.AddElement(priorityField);
+                layout.AddElement(layersField);
 
 
                 if (camera.ProjectionType == ProjectionType.Orthographic)
                 if (camera.ProjectionType == ProjectionType.Orthographic)
-                    mFieldOfView.Visible = false;
+                    fieldOfView.Visible = false;
                 else
                 else
-                    mOrthoHeight.Visible = false;
+                    orthoHeight.Visible = false;
             }
             }
 
 
             isInitialized = true;
             isInitialized = true;
@@ -117,93 +138,103 @@ namespace BansheeEditor
 
 
             bool anythingModified = false;
             bool anythingModified = false;
 
 
-            if (mProjectionTypeField.Value != (int) camera.ProjectionType)
+            if (projectionTypeField.Value != (ulong)camera.ProjectionType)
             {
             {
-                mProjectionTypeField.Value = (int)camera.ProjectionType;
+                projectionTypeField.Value = (ulong)camera.ProjectionType;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mFieldOfView.Value != camera.FieldOfView.Degrees)
+            if (fieldOfView.Value != camera.FieldOfView.Degrees)
             {
             {
-                mFieldOfView.Value = camera.FieldOfView.Degrees;
+                fieldOfView.Value = camera.FieldOfView.Degrees;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mOrthoHeight.Value != camera.OrthoHeight)
+            if (orthoHeight.Value != camera.OrthoHeight)
             {
             {
-                mOrthoHeight.Value = camera.OrthoHeight;
+                orthoHeight.Value = camera.OrthoHeight;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mAspectField.Value != camera.AspectRatio)
+            if (aspectField.Value != camera.AspectRatio)
             {
             {
-                mAspectField.Value = camera.AspectRatio;
+                aspectField.Value = camera.AspectRatio;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mNearPlaneField.Value != camera.NearClipPlane)
+            if (nearPlaneField.Value != camera.NearClipPlane)
             {
             {
-                mNearPlaneField.Value = camera.NearClipPlane;
+                nearPlaneField.Value = camera.NearClipPlane;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mFarPlaneField.Value != camera.FarClipPlane)
+            if (farPlaneField.Value != camera.FarClipPlane)
             {
             {
-                mFarPlaneField.Value = camera.FarClipPlane;
+                farPlaneField.Value = camera.FarClipPlane;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mViewportXField.Value != camera.ViewportRect.x)
+            if (viewportXField.Value != camera.ViewportRect.x)
             {
             {
-                mViewportXField.Value = camera.ViewportRect.x;
+                viewportXField.Value = camera.ViewportRect.x;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mViewportYField.Value != camera.ViewportRect.y)
+            if (viewportYField.Value != camera.ViewportRect.y)
             {
             {
-                mViewportYField.Value = camera.ViewportRect.y;
+                viewportYField.Value = camera.ViewportRect.y;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mViewportWidthField.Value != camera.ViewportRect.width)
+            if (viewportWidthField.Value != camera.ViewportRect.width)
             {
             {
-                mViewportWidthField.Value = camera.ViewportRect.width;
+                viewportWidthField.Value = camera.ViewportRect.width;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mViewportHeightField.Value != camera.ViewportRect.height)
+            if (viewportHeightField.Value != camera.ViewportRect.height)
             {
             {
-                mViewportHeightField.Value = camera.ViewportRect.height;
+                viewportHeightField.Value = camera.ViewportRect.height;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mClearFlagsFields.Value != (int)camera.ClearFlags)
+            if (clearFlagsFields.Value != (ulong)camera.ClearFlags)
             {
             {
-                mClearFlagsFields.Value = (int)camera.ClearFlags;
+                clearFlagsFields.Value = (ulong)camera.ClearFlags;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mClearStencilField.Value != camera.ClearStencil)
+            if (clearStencilField.Value != camera.ClearStencil)
             {
             {
-                mClearStencilField.Value = camera.ClearStencil;
+                clearStencilField.Value = camera.ClearStencil;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mClearDepthField.Value != camera.ClearDepth)
+            if (clearDepthField.Value != camera.ClearDepth)
             {
             {
-                mClearDepthField.Value = camera.ClearDepth;
+                clearDepthField.Value = camera.ClearDepth;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mClearColorField.Value != camera.ClearColor)
+            if (clearColorField.Value != camera.ClearColor)
             {
             {
-                mClearColorField.Value = camera.ClearColor;
+                clearColorField.Value = camera.ClearColor;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            if (mPriorityField.Value != camera.Priority)
+            if (priorityField.Value != camera.Priority)
             {
             {
-                mPriorityField.Value = camera.Priority;
+                priorityField.Value = camera.Priority;
+                anythingModified = true;
+            }
+
+            if (layersValue != camera.Layers)
+            {
+                bool[] states = new bool[64];
+                for (int i = 0; i < states.Length; i++)
+                    states[i] = (camera.Layers & Layers.Values[i]) == Layers.Values[i];
+
+                layersField.States = states;
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 

+ 49 - 6
MBansheeEngine/GUI/GUIListBox.cs

@@ -16,19 +16,30 @@ namespace BansheeEngine
         /// </summary>
         /// </summary>
         public event OnSelectionChangedDelegate OnSelectionChanged;
         public event OnSelectionChangedDelegate OnSelectionChanged;
 
 
+        /// <summary>
+        /// States of all element in the list box (enabled or disabled).
+        /// </summary>
+        public bool[] States
+        {
+            get { return Internal_GetElementStates(mCachedPtr); }
+            set { Internal_SetElementStates(mCachedPtr, value); }
+        }
+
         /// <summary>
         /// <summary>
         /// Creates a new list box with the specified elements.
         /// Creates a new list box with the specified elements.
         /// </summary>
         /// </summary>
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         ///                        order as in the array.</param>
         ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         /// <param name="style">Optional style to use for the element. Style controls the look of the element, as well as 
         ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
         ///                     default layout options. Style will be retrieved from the active GUISkin. If not specified 
         ///                     default element style is used.</param>
         ///                     default element style is used.</param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIListBox(LocString[] elements, string style = "", params GUIOption[] options)
+        public GUIListBox(LocString[] elements, bool multiselect = false, string style = "", params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, elements, style, options);
+            Internal_CreateInstance(this, elements, multiselect, style, options);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -36,11 +47,13 @@ namespace BansheeEngine
         /// </summary>
         /// </summary>
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         /// <param name="elements">Array of elements to display in the list box. Elements will be displayed in the same 
         ///                        order as in the array.</param>
         ///                        order as in the array.</param>
+        /// <param name="multiselect">Determines should the listbox allow multiple elements to be selected or just one.
+        ///                           </param>
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         /// <param name="options">Options that allow you to control how is the element positioned and sized. This will 
         ///                       override any similar options set by style.</param>
         ///                       override any similar options set by style.</param>
-        public GUIListBox(LocString[] elements, params GUIOption[] options)
+        public GUIListBox(LocString[] elements, bool multiselect = false, params GUIOption[] options)
         {
         {
-            Internal_CreateInstance(this, elements, "", options);
+            Internal_CreateInstance(this, elements, multiselect, "", options);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -53,6 +66,24 @@ namespace BansheeEngine
             Internal_SetElements(mCachedPtr, elements);
             Internal_SetElements(mCachedPtr, elements);
         }
         }
 
 
+        /// <summary>
+        /// Makes the element with the specified index selected.
+        /// </summary>
+        /// <param name="idx">Sequential index in the elements array provided on construction.</param>
+        public void SelectElement(int idx)
+        {
+            Internal_SelectElement(mCachedPtr, idx);
+        }
+
+        /// <summary>
+        /// Deselect element the element with the specified index. Only relevant for multi-select list boxes.
+        /// </summary>
+        /// <param name="idx">Sequential index in the elements array provided on construction.</param>
+        public void DeselectElement(int idx)
+        {
+            Internal_DeselectElement(mCachedPtr, idx);
+        }
+
         /// <summary>
         /// <summary>
         /// Colors the element with a specific tint.
         /// Colors the element with a specific tint.
         /// </summary>
         /// </summary>
@@ -72,12 +103,24 @@ namespace BansheeEngine
         }
         }
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(GUIListBox instance, LocString[] elements, string style, 
-            GUIOption[] options);
+        private static extern void Internal_CreateInstance(GUIListBox instance, LocString[] elements, bool multiselect,
+            string style, GUIOption[] options);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetElements(IntPtr nativeInstance, LocString[] elements);
         private static extern void Internal_SetElements(IntPtr nativeInstance, LocString[] elements);
 
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SelectElement(IntPtr nativeInstance, int idx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_DeselectElement(IntPtr nativeInstance, int idx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool[] Internal_GetElementStates(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetElementStates(IntPtr nativeInstance, bool[] states);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetTint(IntPtr nativeInstance, Color color);
         private static extern void Internal_SetTint(IntPtr nativeInstance, Color color);
     }
     }

+ 53 - 0
MBansheeEngine/Layers.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Contains a list of layers that can be used for controlling which <see cref="Renderable"/> is output to which
+    /// camera. A maximum of 64 layers are supported.
+    /// </summary>
+    public static class Layers // Note: Placeholder class, need functionality to edit and persist layer names
+    {
+        private static string[] names;
+        private static UInt64[] values;
+
+        /// <summary>
+        /// Returns the names of all available layers.
+        /// </summary>
+        public static string[] Names
+        {
+            get
+            {
+                if (names == null)
+                {
+                    names = new string[64];
+                    for (int i = 0; i < names.Length; i++)
+                        names[i] = "Layer_" + i;
+                }
+
+                return names;
+            }
+        }
+
+        /// <summary>
+        /// Returns the values of all available layers.
+        /// </summary>
+        public static UInt64[] Values
+        {
+            get
+            {
+                if (values == null)
+                {
+                    values = new UInt64[64];
+                    for (int i = 0; i < values.Length; i++)
+                        values[i] = 1UL << i;
+                }
+
+                return values;
+            }
+        }
+    }
+}

+ 1 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -46,6 +46,7 @@
     <Compile Include="Bounds.cs" />
     <Compile Include="Bounds.cs" />
     <Compile Include="Builtin.cs" />
     <Compile Include="Builtin.cs" />
     <Compile Include="Camera.cs" />
     <Compile Include="Camera.cs" />
+    <Compile Include="Layers.cs" />
     <Compile Include="NativeCamera.cs" />
     <Compile Include="NativeCamera.cs" />
     <Compile Include="ContextMenu.cs" />
     <Compile Include="ContextMenu.cs" />
     <Compile Include="Cursor.cs" />
     <Compile Include="Cursor.cs" />

+ 14 - 9
SBansheeEditor/Include/BsScriptGUIEnumField.h

@@ -19,25 +19,30 @@ namespace BansheeEngine
 		 * @brief	Triggered when the value in the native list box selection changes.
 		 * @brief	Triggered when the value in the native list box selection changes.
 		 *
 		 *
 		 * @param	instance	Managed GUIEnumField instance.
 		 * @param	instance	Managed GUIEnumField instance.
-		 * @param	newIndex	New selection index.
+		 * @param	newIndex	Index of the selected element.
+		 * @param	enabled		Determines whether the element at the selection index was enabled or disabled.
 		 */
 		 */
-		static void onSelectionChanged(MonoObject* instance, UINT32 newIndex);
+		static void onSelectionChanged(MonoObject* instance, UINT64 newIndex, bool enabled);
 
 
-		ScriptGUIEnumField(MonoObject* instance, GUIListBoxField* listBoxField, const Vector<UINT32>& values);
+		ScriptGUIEnumField(MonoObject* instance, GUIListBoxField* listBoxField, const Vector<UINT64>& values);
 
 
-		Vector<UINT32> mValues;
+		Vector<UINT64> mValues;
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		/************************************************************************/
-		static void internal_createInstance(MonoObject* instance, MonoArray* names, MonoArray* values, MonoObject* title, 
-			UINT32 titleWidth, MonoString* style, MonoArray* guiOptions, bool withTitle);
+		static void internal_createInstance(MonoObject* instance, MonoArray* names, MonoArray* values, bool multiselect,
+			MonoObject* title, UINT32 titleWidth, MonoString* style, MonoArray* guiOptions, bool withTitle);
 
 
-		static UINT32 internal_getValue(ScriptGUIEnumField* nativeInstance);
-		static void internal_setValue(ScriptGUIEnumField* nativeInstance, UINT32 value);
+		static UINT64 internal_getValue(ScriptGUIEnumField* nativeInstance);
+		static void internal_setValue(ScriptGUIEnumField* nativeInstance, UINT64 value);
 		static void internal_setTint(ScriptGUIEnumField* nativeInstance, Color color);
 		static void internal_setTint(ScriptGUIEnumField* nativeInstance, Color color);
+		static void internal_selectElement(ScriptGUIEnumField* nativeInstance, int idx);
+		static void internal_deselectElement(ScriptGUIEnumField* nativeInstance, int idx);
+		static MonoArray* internal_getElementStates(ScriptGUIEnumField* nativeInstance);
+		static void internal_setElementStates(ScriptGUIEnumField* nativeInstance, MonoArray* states);
 
 
-		typedef void(__stdcall *OnSelectionChangedThunkDef) (MonoObject*, UINT32, MonoException**);
+		typedef void(__stdcall *OnSelectionChangedThunkDef) (MonoObject*, UINT64, MonoException**);
 
 
 		static OnSelectionChangedThunkDef onSelectionChangedThunk;
 		static OnSelectionChangedThunkDef onSelectionChangedThunk;
 	};
 	};

+ 6 - 2
SBansheeEditor/Include/BsScriptGUIListBoxField.h

@@ -27,12 +27,16 @@ namespace BansheeEngine
 		/************************************************************************/
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		/************************************************************************/
-		static void internal_createInstance(MonoObject* instance, MonoArray* elements, MonoObject* title, UINT32 titleWidth,
-			MonoString* style, MonoArray* guiOptions, bool withTitle);
+		static void internal_createInstance(MonoObject* instance, MonoArray* elements, bool multiselect, 
+			MonoObject* title, UINT32 titleWidth, MonoString* style, MonoArray* guiOptions, bool withTitle);
 
 
 		static void internal_setElements(ScriptGUIListBoxField* nativeInstance, MonoArray* elements);
 		static void internal_setElements(ScriptGUIListBoxField* nativeInstance, MonoArray* elements);
 		static UINT32 internal_getValue(ScriptGUIListBoxField* nativeInstance);
 		static UINT32 internal_getValue(ScriptGUIListBoxField* nativeInstance);
 		static void internal_setValue(ScriptGUIListBoxField* nativeInstance, UINT32 value);
 		static void internal_setValue(ScriptGUIListBoxField* nativeInstance, UINT32 value);
+		static void internal_selectElement(ScriptGUIListBoxField* nativeInstance, int idx);
+		static void internal_deselectElement(ScriptGUIListBoxField* nativeInstance, int idx);
+		static MonoArray* internal_getElementStates(ScriptGUIListBoxField* nativeInstance);
+		static void internal_setElementStates(ScriptGUIListBoxField* nativeInstance, MonoArray* states);
 		static void internal_setTint(ScriptGUIListBoxField* nativeInstance, Color color);
 		static void internal_setTint(ScriptGUIListBoxField* nativeInstance, Color color);
 
 
 		typedef void(__stdcall *OnSelectionChangedThunkDef) (MonoObject*, UINT32, MonoException**);
 		typedef void(__stdcall *OnSelectionChangedThunkDef) (MonoObject*, UINT32, MonoException**);

+ 97 - 34
SBansheeEditor/Source/BsScriptGUIEnumField.cpp

@@ -17,7 +17,7 @@ namespace BansheeEngine
 {
 {
 	ScriptGUIEnumField::OnSelectionChangedThunkDef ScriptGUIEnumField::onSelectionChangedThunk;
 	ScriptGUIEnumField::OnSelectionChangedThunkDef ScriptGUIEnumField::onSelectionChangedThunk;
 
 
-	ScriptGUIEnumField::ScriptGUIEnumField(MonoObject* instance, GUIListBoxField* listBoxField, const Vector<UINT32>& values)
+	ScriptGUIEnumField::ScriptGUIEnumField(MonoObject* instance, GUIListBoxField* listBoxField, const Vector<UINT64>& values)
 		:TScriptGUIElement(instance, listBoxField), mValues(values)
 		:TScriptGUIElement(instance, listBoxField), mValues(values)
 	{
 	{
 
 
@@ -29,12 +29,16 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetValue", &ScriptGUIEnumField::internal_getValue);
 		metaData.scriptClass->addInternalCall("Internal_GetValue", &ScriptGUIEnumField::internal_getValue);
 		metaData.scriptClass->addInternalCall("Internal_SetValue", &ScriptGUIEnumField::internal_setValue);
 		metaData.scriptClass->addInternalCall("Internal_SetValue", &ScriptGUIEnumField::internal_setValue);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIEnumField::internal_setTint);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIEnumField::internal_setTint);
+		metaData.scriptClass->addInternalCall("Internal_SelectElement", &ScriptGUIEnumField::internal_selectElement);
+		metaData.scriptClass->addInternalCall("Internal_DeselectElement", &ScriptGUIEnumField::internal_deselectElement);
+		metaData.scriptClass->addInternalCall("Internal_GetElementStates", &ScriptGUIEnumField::internal_getElementStates);
+		metaData.scriptClass->addInternalCall("Internal_SetElementStates", &ScriptGUIEnumField::internal_setElementStates);
 
 
 		onSelectionChangedThunk = (OnSelectionChangedThunkDef)metaData.scriptClass->getMethod("DoOnSelectionChanged", 1)->getThunk();
 		onSelectionChangedThunk = (OnSelectionChangedThunkDef)metaData.scriptClass->getMethod("DoOnSelectionChanged", 1)->getThunk();
 	}
 	}
 
 
 	void ScriptGUIEnumField::internal_createInstance(MonoObject* instance, MonoArray* names, MonoArray* values, 
 	void ScriptGUIEnumField::internal_createInstance(MonoObject* instance, MonoArray* names, MonoArray* values, 
-		MonoObject* title, UINT32 titleWidth, MonoString* style, MonoArray* guiOptions, bool withTitle)
+		bool multiselect, MonoObject* title, UINT32 titleWidth, MonoString* style, MonoArray* guiOptions, bool withTitle)
 	{
 	{
 		GUIOptions options;
 		GUIOptions options;
 
 
@@ -53,34 +57,52 @@ namespace BansheeEngine
 		if (withTitle)
 		if (withTitle)
 		{
 		{
 			GUIContent nativeContent(ScriptGUIContent::getText(title), ScriptGUIContent::getImage(title), ScriptGUIContent::getTooltip(title));
 			GUIContent nativeContent(ScriptGUIContent::getText(title), ScriptGUIContent::getImage(title), ScriptGUIContent::getTooltip(title));
-			guiField = GUIListBoxField::create(nativeNames, nativeContent, titleWidth, options, styleName);
+			guiField = GUIListBoxField::create(nativeNames, multiselect, nativeContent, titleWidth, options, styleName);
 		}
 		}
 		else
 		else
 		{
 		{
-			guiField = GUIListBoxField::create(nativeNames, options, styleName);
+			guiField = GUIListBoxField::create(nativeNames, multiselect, options, styleName);
 		}
 		}
 
 
-		guiField->onSelectionChanged.connect(std::bind(&ScriptGUIEnumField::onSelectionChanged, instance, _1));
+		guiField->onSelectionChanged.connect(std::bind(&ScriptGUIEnumField::onSelectionChanged, instance, _1, _2));
 
 
 		ScriptArray valuesArr(values);
 		ScriptArray valuesArr(values);
 		UINT32 elemSize = valuesArr.elementSize();
 		UINT32 elemSize = valuesArr.elementSize();
 
 
-		Vector<UINT32> nativeValues;
+		Vector<UINT64> nativeValues;
 		for (UINT32 i = 0; i < valuesArr.size(); i++)
 		for (UINT32 i = 0; i < valuesArr.size(); i++)
 		{
 		{
-			// Truncate, as we don't check exact value type for enums
-			UINT32 nativeValue = 0;
+			UINT64 nativeValue = 0;
 
 
-#if BS_ENDIAN == BS_ENDIAN_LITTLE
-			memcpy(&nativeValue, valuesArr.getRawPtr(elemSize, i), std::min(elemSize, (UINT32)sizeof(UINT32)));
-#else
-			INT32 diff = (INT32)sizeof(UINT32) - (INT32)elemSize;
-			UINT32 offsetDst = diff > 0 ? (UINT32)diff : 0;
-			UINT32 offsetSrc = diff < 0 ? (UINT32)-diff : 0;
-
-			memcpy(((UINT8*)&nativeValue) + offsetDst, valuesArr.getRawPtr(elemSize, i) + offsetSrc, 
-				std::min(elemSize, (UINT32)sizeof(UINT32)));
-#endif
+			switch (elemSize)
+			{
+			case sizeof(UINT8):
+			{
+				UINT8 value = 0;
+				memcpy(&value, valuesArr.getRawPtr(elemSize, i), elemSize);
+				nativeValue = (UINT64)value;
+				break;
+			}
+			case sizeof(UINT16) :
+			{
+				UINT16 value = 0;
+				memcpy(&value, valuesArr.getRawPtr(elemSize, i), elemSize);
+				nativeValue = (UINT64)value;
+				break;
+			}
+			case sizeof(UINT32):
+			{
+				UINT32 value = 0;
+				memcpy(&value, valuesArr.getRawPtr(elemSize, i), elemSize);
+				nativeValue = (UINT64)value;
+				break;
+			}
+			case sizeof(UINT64) :
+			{
+				memcpy(&nativeValue, valuesArr.getRawPtr(elemSize, i), elemSize);
+				break;
+			}
+			}
 
 
 			nativeValues.push_back(nativeValue);
 			nativeValues.push_back(nativeValue);
 		}
 		}
@@ -89,31 +111,72 @@ namespace BansheeEngine
 		ScriptGUIEnumField* nativeInstance = new (bs_alloc<ScriptGUIEnumField>()) ScriptGUIEnumField(instance, guiField, nativeValues);
 		ScriptGUIEnumField* nativeInstance = new (bs_alloc<ScriptGUIEnumField>()) ScriptGUIEnumField(instance, guiField, nativeValues);
 	}
 	}
 
 
-	UINT32 ScriptGUIEnumField::internal_getValue(ScriptGUIEnumField* nativeInstance)
+	UINT64 ScriptGUIEnumField::internal_getValue(ScriptGUIEnumField* nativeInstance)
 	{
 	{
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
-		UINT32 idx = field->getIndex();
 
 
-		const Vector<UINT32>& values = nativeInstance->mValues;
-		if (idx < (UINT32)values.size())
-			return values[idx];
+		UINT32 outValue = 0;
+
+		const Vector<UINT64>& values = nativeInstance->mValues;
+		Vector<bool> states = field->getElementStates();
+		for (UINT32 i = 0; i < (UINT32)values.size(); i++)
+			outValue |= states[i] ? values[i] : 0;
 
 
-		return 0;
+		return outValue;
 	}
 	}
 
 
-	void ScriptGUIEnumField::internal_setValue(ScriptGUIEnumField* nativeInstance, UINT32 value)
+	void ScriptGUIEnumField::internal_setValue(ScriptGUIEnumField* nativeInstance, UINT64 value)
 	{
 	{
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
 
 
-		const Vector<UINT32>& values = nativeInstance->mValues;
+		const Vector<UINT64>& values = nativeInstance->mValues;
+		Vector<bool> states = field->getElementStates();
 		for (UINT32 i = 0; i < (UINT32)values.size(); i++)
 		for (UINT32 i = 0; i < (UINT32)values.size(); i++)
-		{
-			if (values[i] == value)
-			{
-				field->setIndex(i);
-				break;
-			}
-		}
+			states[i] = (values[i] & value) == values[i];
+
+		field->setElementStates(states);
+	}
+
+	void ScriptGUIEnumField::internal_selectElement(ScriptGUIEnumField* nativeInstance, int idx)
+	{
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		listBox->selectElement(idx);
+	}
+
+	void ScriptGUIEnumField::internal_deselectElement(ScriptGUIEnumField* nativeInstance, int idx)
+	{
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		listBox->deselectElement(idx);
+	}
+
+	MonoArray* ScriptGUIEnumField::internal_getElementStates(ScriptGUIEnumField* nativeInstance)
+	{
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		const Vector<bool>& states = listBox->getElementStates();
+
+		UINT32 numElements = (UINT32)states.size();
+		ScriptArray outStates = ScriptArray::create<bool>(numElements);
+
+		for (UINT32 i = 0; i < numElements; i++)
+			outStates.set(i, states[i]);
+
+		return outStates.getInternal();
+	}
+
+	void ScriptGUIEnumField::internal_setElementStates(ScriptGUIEnumField* nativeInstance, MonoArray* monoStates)
+	{
+		if (monoStates == nullptr)
+			return;
+
+		ScriptArray inStates(monoStates);
+		UINT32 numElements = inStates.size();
+
+		Vector<bool> states(numElements);
+		for (UINT32 i = 0; i < numElements; i++)
+			states[i] = inStates.get<bool>(i);
+
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		listBox->setElementStates(states);
 	}
 	}
 
 
 	void ScriptGUIEnumField::internal_setTint(ScriptGUIEnumField* nativeInstance, Color color)
 	void ScriptGUIEnumField::internal_setTint(ScriptGUIEnumField* nativeInstance, Color color)
@@ -122,7 +185,7 @@ namespace BansheeEngine
 		field->setTint(color);
 		field->setTint(color);
 	}
 	}
 
 
-	void ScriptGUIEnumField::onSelectionChanged(MonoObject* instance, UINT32 newIndex)
+	void ScriptGUIEnumField::onSelectionChanged(MonoObject* instance, UINT64 newIndex, bool enabled)
 	{
 	{
 		MonoUtil::invokeThunk(onSelectionChangedThunk, instance, newIndex);
 		MonoUtil::invokeThunk(onSelectionChangedThunk, instance, newIndex);
 	}
 	}

+ 73 - 5
SBansheeEditor/Source/BsScriptGUIListBoxField.cpp

@@ -30,11 +30,15 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetValue", &ScriptGUIListBoxField::internal_getValue);
 		metaData.scriptClass->addInternalCall("Internal_GetValue", &ScriptGUIListBoxField::internal_getValue);
 		metaData.scriptClass->addInternalCall("Internal_SetValue", &ScriptGUIListBoxField::internal_setValue);
 		metaData.scriptClass->addInternalCall("Internal_SetValue", &ScriptGUIListBoxField::internal_setValue);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIListBoxField::internal_setTint);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIListBoxField::internal_setTint);
+		metaData.scriptClass->addInternalCall("Internal_SelectElement", &ScriptGUIListBoxField::internal_selectElement);
+		metaData.scriptClass->addInternalCall("Internal_DeselectElement", &ScriptGUIListBoxField::internal_deselectElement);
+		metaData.scriptClass->addInternalCall("Internal_GetElementStates", &ScriptGUIListBoxField::internal_getElementStates);
+		metaData.scriptClass->addInternalCall("Internal_SetElementStates", &ScriptGUIListBoxField::internal_setElementStates);
 
 
 		onSelectionChangedThunk = (OnSelectionChangedThunkDef)metaData.scriptClass->getMethod("DoOnSelectionChanged", 1)->getThunk();
 		onSelectionChangedThunk = (OnSelectionChangedThunkDef)metaData.scriptClass->getMethod("DoOnSelectionChanged", 1)->getThunk();
 	}
 	}
 
 
-	void ScriptGUIListBoxField::internal_createInstance(MonoObject* instance, MonoArray* elements, MonoObject* title, 
+	void ScriptGUIListBoxField::internal_createInstance(MonoObject* instance, MonoArray* elements, bool multiselect, MonoObject* title, 
 		UINT32 titleWidth, MonoString* style, MonoArray* guiOptions, bool withTitle)
 		UINT32 titleWidth, MonoString* style, MonoArray* guiOptions, bool withTitle)
 	{
 	{
 		GUIOptions options;
 		GUIOptions options;
@@ -64,11 +68,11 @@ namespace BansheeEngine
 		if (withTitle)
 		if (withTitle)
 		{
 		{
 			GUIContent nativeContent(ScriptGUIContent::getText(title), ScriptGUIContent::getImage(title), ScriptGUIContent::getTooltip(title));
 			GUIContent nativeContent(ScriptGUIContent::getText(title), ScriptGUIContent::getImage(title), ScriptGUIContent::getTooltip(title));
-			guiField = GUIListBoxField::create(nativeElements, nativeContent, titleWidth, options, styleName);
+			guiField = GUIListBoxField::create(nativeElements, multiselect, nativeContent, titleWidth, options, styleName);
 		}
 		}
 		else
 		else
 		{
 		{
-			guiField = GUIListBoxField::create(nativeElements, options, styleName);
+			guiField = GUIListBoxField::create(nativeElements, multiselect, options, styleName);
 		}
 		}
 
 
 		guiField->onSelectionChanged.connect(std::bind(&ScriptGUIListBoxField::onSelectionChanged, instance, _1));
 		guiField->onSelectionChanged.connect(std::bind(&ScriptGUIListBoxField::onSelectionChanged, instance, _1));
@@ -79,13 +83,35 @@ namespace BansheeEngine
 	UINT32 ScriptGUIListBoxField::internal_getValue(ScriptGUIListBoxField* nativeInstance)
 	UINT32 ScriptGUIListBoxField::internal_getValue(ScriptGUIListBoxField* nativeInstance)
 	{
 	{
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
-		return field->getIndex();
+
+		const Vector<bool>& states = field->getElementStates();
+		for (UINT32 i = 0; i < (UINT32)states.size(); i++)
+		{
+			if (states[i])
+				return i;
+		}
+
+		return UINT_MAX;
 	}
 	}
 
 
 	void ScriptGUIListBoxField::internal_setValue(ScriptGUIListBoxField* nativeInstance, UINT32 index)
 	void ScriptGUIListBoxField::internal_setValue(ScriptGUIListBoxField* nativeInstance, UINT32 index)
 	{
 	{
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
 		GUIListBoxField* field = static_cast<GUIListBoxField*>(nativeInstance->getGUIElement());
-		return field->setIndex(index);
+		Vector<bool> states = field->getElementStates();
+
+		UINT32 numElements = (UINT32)states.size();
+		if (index >= numElements)
+			return;
+
+		for (UINT32 i = 0; i < numElements; i++)
+		{
+			if (states[i])
+				states[i] = true;
+			else
+				states[i] = false;
+		}
+
+		field->setElementStates(states);
 	}
 	}
 
 
 	void ScriptGUIListBoxField::internal_setElements(ScriptGUIListBoxField* nativeInstance, MonoArray* elements)
 	void ScriptGUIListBoxField::internal_setElements(ScriptGUIListBoxField* nativeInstance, MonoArray* elements)
@@ -109,6 +135,48 @@ namespace BansheeEngine
 		field->setElements(nativeElements);
 		field->setElements(nativeElements);
 	}
 	}
 
 
+	void ScriptGUIListBoxField::internal_selectElement(ScriptGUIListBoxField* nativeInstance, int idx)
+	{
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		listBox->selectElement(idx);
+	}
+
+	void ScriptGUIListBoxField::internal_deselectElement(ScriptGUIListBoxField* nativeInstance, int idx)
+	{
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		listBox->deselectElement(idx);
+	}
+
+	MonoArray* ScriptGUIListBoxField::internal_getElementStates(ScriptGUIListBoxField* nativeInstance)
+	{
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		const Vector<bool>& states = listBox->getElementStates();
+
+		UINT32 numElements = (UINT32)states.size();
+		ScriptArray outStates = ScriptArray::create<bool>(numElements);
+
+		for (UINT32 i = 0; i < numElements; i++)
+			outStates.set(i, states[i]);
+
+		return outStates.getInternal();
+	}
+
+	void ScriptGUIListBoxField::internal_setElementStates(ScriptGUIListBoxField* nativeInstance, MonoArray* monoStates)
+	{
+		if (monoStates == nullptr)
+			return;
+
+		ScriptArray inStates(monoStates);
+		UINT32 numElements = inStates.size();
+
+		Vector<bool> states(numElements);
+		for (UINT32 i = 0; i < numElements; i++)
+			states[i] = inStates.get<bool>(i);
+
+		GUIListBoxField* listBox = (GUIListBoxField*)nativeInstance->getGUIElement();
+		listBox->setElementStates(states);
+	}
+
 	void ScriptGUIListBoxField::internal_setTint(ScriptGUIListBoxField* nativeInstance, Color color)
 	void ScriptGUIListBoxField::internal_setTint(ScriptGUIListBoxField* nativeInstance, Color color)
 	{
 	{
 		GUIListBoxField* field = (GUIListBoxField*)nativeInstance->getGUIElement();
 		GUIListBoxField* field = (GUIListBoxField*)nativeInstance->getGUIElement();

+ 6 - 2
SBansheeEngine/Include/BsScriptGUIListBox.h

@@ -20,14 +20,18 @@ namespace BansheeEngine
 		/**
 		/**
 		 * @brief	Triggered when the selected index in the native list box changes.
 		 * @brief	Triggered when the selected index in the native list box changes.
 		 */
 		 */
-		static void onSelectionChanged(MonoObject* instance, UINT32 index);
+		static void onSelectionChanged(MonoObject* instance, UINT32 index, bool enabled);
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		/************************************************************************/
-		static void internal_createInstance(MonoObject* instance, MonoArray* elements, MonoString* style, MonoArray* guiOptions);
+		static void internal_createInstance(MonoObject* instance, MonoArray* elements, bool multiselect, MonoString* style, MonoArray* guiOptions);
 		static void internal_setElements(ScriptGUIListBox* nativeInstance, MonoArray* elements);
 		static void internal_setElements(ScriptGUIListBox* nativeInstance, MonoArray* elements);
 		static void internal_setTint(ScriptGUIListBox* nativeInstance, Color color);
 		static void internal_setTint(ScriptGUIListBox* nativeInstance, Color color);
+		static void internal_selectElement(ScriptGUIListBox* nativeInstance, int idx);
+		static void internal_deselectElement(ScriptGUIListBox* nativeInstance, int idx);
+		static MonoArray* internal_getElementStates(ScriptGUIListBox* nativeInstance);
+		static void internal_setElementStates(ScriptGUIListBox* nativeInstance, MonoArray* states);
 
 
 		typedef void (__stdcall *OnSelectionChangedThunkDef) (MonoObject*, UINT32, MonoException**);
 		typedef void (__stdcall *OnSelectionChangedThunkDef) (MonoObject*, UINT32, MonoException**);
 		static OnSelectionChangedThunkDef onSelectionChangedThunk;
 		static OnSelectionChangedThunkDef onSelectionChangedThunk;

+ 51 - 4
SBansheeEngine/Source/BsScriptGUIListBox.cpp

@@ -30,11 +30,16 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptGUIListBox::internal_createInstance);
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptGUIListBox::internal_createInstance);
 		metaData.scriptClass->addInternalCall("Internal_SetElements", &ScriptGUIListBox::internal_setElements);
 		metaData.scriptClass->addInternalCall("Internal_SetElements", &ScriptGUIListBox::internal_setElements);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIListBox::internal_setTint);
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIListBox::internal_setTint);
+		metaData.scriptClass->addInternalCall("Internal_SelectElement", &ScriptGUIListBox::internal_selectElement);
+		metaData.scriptClass->addInternalCall("Internal_DeselectElement", &ScriptGUIListBox::internal_deselectElement);
+		metaData.scriptClass->addInternalCall("Internal_GetElementStates", &ScriptGUIListBox::internal_getElementStates);
+		metaData.scriptClass->addInternalCall("Internal_SetElementStates", &ScriptGUIListBox::internal_setElementStates);
 
 
 		onSelectionChangedThunk = (OnSelectionChangedThunkDef)metaData.scriptClass->getMethod("DoOnSelectionChanged", 1)->getThunk();
 		onSelectionChangedThunk = (OnSelectionChangedThunkDef)metaData.scriptClass->getMethod("DoOnSelectionChanged", 1)->getThunk();
 	}
 	}
 
 
-	void ScriptGUIListBox::internal_createInstance(MonoObject* instance, MonoArray* elements, MonoString* style, MonoArray* guiOptions)
+	void ScriptGUIListBox::internal_createInstance(MonoObject* instance, MonoArray* elements, bool multiselect,
+		MonoString* style, MonoArray* guiOptions)
 	{
 	{
 		GUIOptions options;
 		GUIOptions options;
 
 
@@ -57,8 +62,8 @@ namespace BansheeEngine
 			}
 			}
 		}
 		}
 
 
-		GUIListBox* guiListBox = GUIListBox::create(nativeElements, options, toString(MonoUtil::monoToWString(style)));
-		guiListBox->onSelectionChanged.connect(std::bind(&ScriptGUIListBox::onSelectionChanged, instance, std::placeholders::_1));
+		GUIListBox* guiListBox = GUIListBox::create(nativeElements, multiselect, options, toString(MonoUtil::monoToWString(style)));
+		guiListBox->onSelectionToggled.connect(std::bind(&ScriptGUIListBox::onSelectionChanged, instance, _1, _2));
 
 
 		ScriptGUIListBox* nativeInstance = new (bs_alloc<ScriptGUIListBox>()) ScriptGUIListBox(instance, guiListBox);
 		ScriptGUIListBox* nativeInstance = new (bs_alloc<ScriptGUIListBox>()) ScriptGUIListBox(instance, guiListBox);
 	}
 	}
@@ -90,7 +95,49 @@ namespace BansheeEngine
 		listBox->setTint(color);
 		listBox->setTint(color);
 	}
 	}
 
 
-	void ScriptGUIListBox::onSelectionChanged(MonoObject* instance, UINT32 index)
+	void ScriptGUIListBox::internal_selectElement(ScriptGUIListBox* nativeInstance, int idx)
+	{
+		GUIListBox* listBox = (GUIListBox*)nativeInstance->getGUIElement();
+		listBox->selectElement(idx);
+	}
+
+	void ScriptGUIListBox::internal_deselectElement(ScriptGUIListBox* nativeInstance, int idx)
+	{
+		GUIListBox* listBox = (GUIListBox*)nativeInstance->getGUIElement();
+		listBox->deselectElement(idx);
+	}
+
+	MonoArray* ScriptGUIListBox::internal_getElementStates(ScriptGUIListBox* nativeInstance)
+	{
+		GUIListBox* listBox = (GUIListBox*)nativeInstance->getGUIElement();
+		const Vector<bool>& states = listBox->getElementStates();
+
+		UINT32 numElements = (UINT32)states.size();
+		ScriptArray outStates = ScriptArray::create<bool>(numElements);
+
+		for (UINT32 i = 0; i < numElements; i++)
+			outStates.set(i, states[i]);
+
+		return outStates.getInternal();
+	}
+
+	void ScriptGUIListBox::internal_setElementStates(ScriptGUIListBox* nativeInstance, MonoArray* monoStates)
+	{
+		if (monoStates == nullptr)
+			return;
+
+		ScriptArray inStates(monoStates);
+		UINT32 numElements = inStates.size();
+
+		Vector<bool> states(numElements);
+		for (UINT32 i = 0; i < numElements; i++)
+			states[i] = inStates.get<bool>(i);
+
+		GUIListBox* listBox = (GUIListBox*)nativeInstance->getGUIElement();
+		listBox->setElementStates(states);
+	}
+
+	void ScriptGUIListBox::onSelectionChanged(MonoObject* instance, UINT32 index, bool enabled)
 	{
 	{
 		MonoUtil::invokeThunk(onSelectionChangedThunk, instance, index);
 		MonoUtil::invokeThunk(onSelectionChangedThunk, instance, index);
 	}
 	}

+ 0 - 3
TODO.txt

@@ -54,13 +54,11 @@ Polish
 
 
 Ribek use:
 Ribek use:
  - Hook up color picker to guicolor field
  - Hook up color picker to guicolor field
- - When drop down opens in a list box that is horizontally scrolled is opens are what feels like an incorrect offset
  - When hiding a component in inspector, it doesn't immediately reposition the components below it
  - When hiding a component in inspector, it doesn't immediately reposition the components below it
  - Having en empty component in inspector shows a small empty background, it shouldn't show anything
  - Having en empty component in inspector shows a small empty background, it shouldn't show anything
  - Component inspectors for: Camera, Renderable, Point/Spot/Directional lights
  - Component inspectors for: Camera, Renderable, Point/Spot/Directional lights
  - Resource inspectors for: Material, Texture, Mesh, Font, Shader, Script Code, Plain Text, Sprite Texture, GUISkin, StringTable, Prefab (just something basic for now)
  - Resource inspectors for: Material, Texture, Mesh, Font, Shader, Script Code, Plain Text, Sprite Texture, GUISkin, StringTable, Prefab (just something basic for now)
  - Test release mode
  - Test release mode
- - Order top-level menu entries according to priority and set valid priorities
  - Add temporary icon textures too all icon buttons currently containing only text so that Ribek can modify them
  - Add temporary icon textures too all icon buttons currently containing only text so that Ribek can modify them
   - Also add dummy icons to toolbar (New Project, Open Project, Save Scene, Undo, Redo, Basic shapes, Camera, Renderable, Lights)
   - Also add dummy icons to toolbar (New Project, Open Project, Save Scene, Undo, Redo, Basic shapes, Camera, Renderable, Lights)
 
 
@@ -97,7 +95,6 @@ Optional:
  - Move all the code files into subfolders so their hierarchy is similar to VS filters
  - Move all the code files into subfolders so their hierarchy is similar to VS filters
  - GUI tabbing to switch between elements
  - GUI tabbing to switch between elements
  - Better Prefab inspector - display SceneObject inspector of top-level object, and possibly prefab hierarchy?
  - Better Prefab inspector - display SceneObject inspector of top-level object, and possibly prefab hierarchy?
- - GUI element that shows a multi-select drop down + an editor for Layers used in Camera and Renderable
  - Undo/Redo
  - Undo/Redo
   - CmdRecordSO records an SO and all its children but it should only record a single SO
   - CmdRecordSO records an SO and all its children but it should only record a single SO
   - CmdRecordSO should instead of recording the entire object record a diff
   - CmdRecordSO should instead of recording the entire object record a diff