Browse Source

Added localized string support to menus

Marko Pintera 12 years ago
parent
commit
11b2bcfbd7

+ 20 - 10
BansheeEngine/Include/BsGUIDropDownBox.h

@@ -7,7 +7,13 @@
 
 namespace BansheeEngine
 {
-	class BS_EXPORT GUIDropDownData
+	struct BS_EXPORT GUIDropDownData
+	{
+		CM::Vector<GUIDropDownDataEntry>::type entries;
+		CM::UnorderedMap<CM::WString, CM::HString>::type localizedNames;
+	};
+
+	class BS_EXPORT GUIDropDownDataEntry
 	{
 		enum class Type
 		{
@@ -17,21 +23,21 @@ namespace BansheeEngine
 		};
 
 	public:
-		static GUIDropDownData separator();
-		static GUIDropDownData button(const CM::WString& label, std::function<void()> callback);
-		static GUIDropDownData subMenu(const CM::WString& label, const CM::Vector<GUIDropDownData>::type& entries);
+		static GUIDropDownDataEntry separator();
+		static GUIDropDownDataEntry button(const CM::WString& label, std::function<void()> callback);
+		static GUIDropDownDataEntry subMenu(const CM::WString& label, const GUIDropDownData& data);
 
 		bool isSeparator() const { return mType == Type::Separator; }
 		bool isSubMenu() const { return mType == Type::SubMenu; }
 
 		const CM::WString& getLabel() const { return mLabel; }
 		std::function<void()> getCallback() const { return mCallback; }
-		const CM::Vector<GUIDropDownData>::type& getSubMenuEntries() const { return mChildEntries; }
+		const GUIDropDownData& getSubMenuData() const { return mChildData; }
 	private:
-		GUIDropDownData() { }
+		GUIDropDownDataEntry() { }
 
 		std::function<void()> mCallback;
-		CM::Vector<GUIDropDownData>::type mChildEntries;
+		GUIDropDownData mChildData;
 		CM::WString mLabel;
 		Type mType; 
 	};
@@ -102,7 +108,7 @@ namespace BansheeEngine
 	{
 	public:
 		GUIDropDownBox(const CM::HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
-			const CM::Vector<GUIDropDownData>::type& elements, const GUISkin& skin, GUIDropDownType type);
+			const GUIDropDownData& dropDownData, const GUISkin& skin, GUIDropDownType type);
 		~GUIDropDownBox();
 
 	private:
@@ -111,7 +117,7 @@ namespace BansheeEngine
 			GUIDropDownBox* mOwner;
 
 			GUIDropDownType mType;
-			CM::Vector<GUIDropDownData>::type mElements;
+			GUIDropDownData mData;
 			CM::UINT32 mPage;
 			CM::INT32 x, y;
 			CM::UINT32 width, height;
@@ -133,7 +139,7 @@ namespace BansheeEngine
 			DropDownSubMenu* mSubMenu;
 
 			DropDownSubMenu(GUIDropDownBox* owner, const GUIDropDownAreaPlacement& placement, 
-				const CM::Rect& availableBounds, const CM::Vector<GUIDropDownData>::type& elements, GUIDropDownType type, CM::UINT32 depthOffset);
+				const CM::Rect& availableBounds, const GUIDropDownData& dropDownData, GUIDropDownType type, CM::UINT32 depthOffset);
 			~DropDownSubMenu();
 
 			void updateGUIElements();
@@ -146,6 +152,8 @@ namespace BansheeEngine
 			void elementClicked(CM::UINT32 idx);
 			void openSubMenu(GUIButton* source, CM::UINT32 elementIdx);
 			void closeSubMenu();
+
+			CM::HString getElementLocalizedName(CM::UINT32 idx) const;
 		};
 
 		static const CM::UINT32 DROP_DOWN_BOX_WIDTH;
@@ -160,5 +168,7 @@ namespace BansheeEngine
 		SpriteTexturePtr mScrollDownBtnArrow;
 
 		DropDownSubMenu* mRootMenu;
+
+		CM::UnorderedMap<CM::WString, CM::HString>::type mLocalizedEntryNames;
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUIDropDownBoxManager.h

@@ -12,7 +12,7 @@ namespace BansheeEngine
 		~GUIDropDownBoxManager();
 
 		CM::GameObjectHandle<GUIDropDownBox> openDropDownBox(CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
-			const CM::Vector<GUIDropDownData>::type& elements, const GUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback);
+			const GUIDropDownData& dropDownData, const GUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback);
 		void closeDropDownBox();
 
 	private:

+ 15 - 2
BansheeEngine/Include/BsGUIMenu.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "BsPrerequisites.h"
+#include "BsGUIDropDownBox.h"
 
 namespace BansheeEngine
 {
@@ -45,11 +46,23 @@ namespace BansheeEngine
 		const GUIMenuItem* getMenuItem(const CM::WString& path) const;
 		void removeMenuItem(const GUIMenuItem* item);
 
-		CM::Vector<GUIDropDownData>::type getDropDownData() const;
+		GUIDropDownData getDropDownData() const;
+
+		/**
+		 * @brief	Normally menu items use values from their paths as their names. However path labels don't provide a way of
+		 * 			localizing the menu item. This method allows you to set specific names (different from path labels)
+		 * 			to each menu item. All the values are localized so they will also be updated according to the string table.
+		 *
+		 * @param	menuItemLabel	The menu item label. (e.g. if you have a menu like "View/Toolbars/Find, this parameter would be either
+		 * 							"View", "Toolbars" or "Find" depending which entry you want to localize)
+		 * @param	localizedName	Localized string with the name.
+		 */
+		void setLocalizedName(const CM::WString& menuItemLabel, const CM::HString& localizedName);
 	protected:
 		GUIMenuItem mRootElement;
+		CM::UnorderedMap<CM::WString, CM::HString>::type mLocalizedEntryNames;
 
 		const GUIMenuItem* addMenuItemInternal(const CM::WString& path, std::function<void()> callback, bool isSeparator);
-		CM::Vector<GUIDropDownData>::type getDropDownDataInternal(const GUIMenuItem& menu) const;
+		GUIDropDownData getDropDownDataInternal(const GUIMenuItem& menu) const;
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsPrerequisites.h

@@ -54,7 +54,7 @@ namespace BansheeEngine
 	struct GUILayoutOptions;
 	class GUIToggleGroup;
 	class GUIListBox;
-	class GUIDropDownData;
+	class GUIDropDownDataEntry;
 	class GUIDropDownBox;
 	class DragAndDropManager;
 	class GUIMenu;

+ 36 - 23
BansheeEngine/Source/BsGUIDropDownBox.cpp

@@ -16,18 +16,18 @@ namespace BansheeEngine
 {
 	const UINT32 GUIDropDownBox::DROP_DOWN_BOX_WIDTH = 150;
 
-	GUIDropDownData GUIDropDownData::separator()
+	GUIDropDownDataEntry GUIDropDownDataEntry::separator()
 	{
-		GUIDropDownData data;
+		GUIDropDownDataEntry data;
 		data.mType = Type::Separator;
 		data.mCallback = nullptr;
 
 		return data;
 	}
 
-	GUIDropDownData GUIDropDownData::button(const WString& label, std::function<void()> callback)
+	GUIDropDownDataEntry GUIDropDownDataEntry::button(const WString& label, std::function<void()> callback)
 	{
-		GUIDropDownData data;
+		GUIDropDownDataEntry data;
 		data.mLabel = label;
 		data.mType = Type::Entry;
 		data.mCallback = callback;
@@ -35,14 +35,14 @@ namespace BansheeEngine
 		return data;
 	}
 
-	GUIDropDownData GUIDropDownData::subMenu(const WString& label, const Vector<GUIDropDownData>::type& entries)
+	GUIDropDownDataEntry GUIDropDownDataEntry::subMenu(const WString& label, const GUIDropDownData& data)
 	{
-		GUIDropDownData data;
-		data.mLabel = label;
-		data.mType = Type::SubMenu;
-		data.mChildEntries = entries;
+		GUIDropDownDataEntry dataEntry;
+		dataEntry.mLabel = label;
+		dataEntry.mType = Type::SubMenu;
+		dataEntry.mChildData = data;
 
-		return data;
+		return dataEntry;
 	}
 
 	GUIDropDownAreaPlacement GUIDropDownAreaPlacement::aroundPosition(const CM::Int2& position)
@@ -73,7 +73,7 @@ namespace BansheeEngine
 	}
 
 	GUIDropDownBox::GUIDropDownBox(const HSceneObject& parent, CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
-		const CM::Vector<GUIDropDownData>::type& elements, const GUISkin& skin, GUIDropDownType type)
+		const GUIDropDownData& dropDownData, const GUISkin& skin, GUIDropDownType type)
 		:GUIWidget(parent, target, window), mScrollUpStyle(nullptr),
 		mScrollDownStyle(nullptr), mEntryBtnStyle(nullptr), mEntryExpBtnStyle(nullptr), 
 		mSeparatorStyle(nullptr), mBackgroundStyle(nullptr)
@@ -105,8 +105,10 @@ namespace BansheeEngine
 		setDepth(0); // Needs to be in front of everything
 		setSkin(skin);
 
+		mLocalizedEntryNames = dropDownData.localizedNames;
+
 		Rect availableBounds(target->getLeft(), target->getTop(), target->getWidth(), target->getHeight());
-		mRootMenu = cm_new<DropDownSubMenu>(this, placement, availableBounds, elements, type, 0);
+		mRootMenu = cm_new<DropDownSubMenu>(this, placement, availableBounds, dropDownData, type, 0);
 	}
 
 	GUIDropDownBox::~GUIDropDownBox()
@@ -115,10 +117,10 @@ namespace BansheeEngine
 	}
 
 	GUIDropDownBox::DropDownSubMenu::DropDownSubMenu(GUIDropDownBox* owner, const GUIDropDownAreaPlacement& placement, 
-		const Rect& availableBounds, const CM::Vector<GUIDropDownData>::type& elements, GUIDropDownType type, UINT32 depthOffset)
+		const Rect& availableBounds, const GUIDropDownData& dropDownData, GUIDropDownType type, UINT32 depthOffset)
 		:mOwner(owner), mPage(0), mBackgroundFrame(nullptr), mBackgroundArea(nullptr), mContentArea(nullptr), 
 		mContentLayout(nullptr), mScrollUpBtn(nullptr), mScrollDownBtn(nullptr), x(0), y(0), width(0), height(0), 
-		mType(type), mSubMenu(nullptr), mElements(elements), mOpenedUpward(false), mDepthOffset(depthOffset)
+		mType(type), mSubMenu(nullptr), mData(dropDownData), mOpenedUpward(false), mDepthOffset(depthOffset)
 	{
 		mAvailableBounds = availableBounds;
 
@@ -181,7 +183,7 @@ namespace BansheeEngine
 			mOwner->mBackgroundStyle->margins.top + mOwner->mBackgroundStyle->margins.bottom;
 
 		UINT32 maxNeededHeight = helperElementHeight;
-		UINT32 numElements = (UINT32)mElements.size();
+		UINT32 numElements = (UINT32)dropDownData.entries.size();
 		for(UINT32 i = 0; i < numElements; i++)
 			maxNeededHeight += getElementHeight(i);
 
@@ -261,7 +263,7 @@ namespace BansheeEngine
 
 		// Determine if we need scroll up and/or down buttons, number of visible elements and actual height
 		bool needsScrollUp = mPage > 0;
-		UINT32 numElements = (UINT32)mElements.size();
+		UINT32 numElements = (UINT32)mData.entries.size();
 
 		UINT32 usedHeight = mOwner->mBackgroundStyle->margins.top + mOwner->mBackgroundStyle->margins.bottom;
 
@@ -333,7 +335,7 @@ namespace BansheeEngine
 		CM::Vector<GUIButton*>::type newExpEntryBtns;
 		for(UINT32 i = pageStart; i < pageEnd; i++)
 		{
-			GUIDropDownData& element = mElements[i];
+			GUIDropDownDataEntry& element = mData.entries[i];
 
 			if(element.isSeparator())
 			{
@@ -361,7 +363,7 @@ namespace BansheeEngine
 				}
 				else
 				{
-					expEntryBtn = GUIButton::create(*mOwner, HString(mElements[i].getLabel()), mOwner->mEntryExpBtnStyle);
+					expEntryBtn = GUIButton::create(*mOwner, getElementLocalizedName(i), mOwner->mEntryExpBtnStyle);
 					expEntryBtn->onHover.connect(boost::bind(&DropDownSubMenu::openSubMenu, this, expEntryBtn, i));
 				}
 
@@ -378,7 +380,7 @@ namespace BansheeEngine
 				}
 				else
 				{
-					entryBtn = GUIButton::create(*mOwner, HString(mElements[i].getLabel()), mOwner->mEntryBtnStyle);
+					entryBtn = GUIButton::create(*mOwner, getElementLocalizedName(i), mOwner->mEntryBtnStyle);
 					entryBtn->onHover.connect(boost::bind(&DropDownSubMenu::closeSubMenu, this));
 					entryBtn->onClick.connect(boost::bind(&DropDownSubMenu::elementClicked, this,  i));
 				}
@@ -439,14 +441,25 @@ namespace BansheeEngine
 
 	UINT32 GUIDropDownBox::DropDownSubMenu::getElementHeight(CM::UINT32 idx) const
 	{
-		if(mElements[idx].isSeparator())
+		if(mData.entries[idx].isSeparator())
 			return mOwner->mSeparatorStyle->height;
-		else if(mElements[idx].isSubMenu())
+		else if(mData.entries[idx].isSubMenu())
 			return mOwner->mEntryExpBtnStyle->height;
 		else
 			return mOwner->mEntryBtnStyle->height;
 	}
 
+	HString GUIDropDownBox::DropDownSubMenu::getElementLocalizedName(CM::UINT32 idx) const
+	{
+		const WString& label = mData.entries[idx].getLabel();
+
+		auto findLocalizedName = mOwner->mLocalizedEntryNames.find(label);
+		if(findLocalizedName != mOwner->mLocalizedEntryNames.end())
+			return findLocalizedName->second;
+		else
+			return HString(label);
+	}
+
 	void GUIDropDownBox::DropDownSubMenu::scrollDown()
 	{
 		mPage++;
@@ -479,7 +492,7 @@ namespace BansheeEngine
 	{
 		closeSubMenu();
 
-		mElements[idx].getCallback()();
+		mData.entries[idx].getCallback()();
 
 		GUIDropDownBoxManager::instance().closeDropDownBox();
 	}
@@ -489,6 +502,6 @@ namespace BansheeEngine
 		closeSubMenu();
 
 		mSubMenu = cm_new<DropDownSubMenu>(mOwner, GUIDropDownAreaPlacement::aroundBoundsVert(source->getBounds()), 
-			mAvailableBounds, mElements[idx].getSubMenuEntries(), mType, mDepthOffset + 1);
+			mAvailableBounds, mData.entries[idx].getSubMenuData(), mType, mDepthOffset + 1);
 	}
 }

+ 2 - 2
BansheeEngine/Source/BsGUIDropDownBoxManager.cpp

@@ -11,12 +11,12 @@ namespace BansheeEngine
 	}
 
 	CM::GameObjectHandle<GUIDropDownBox> GUIDropDownBoxManager::openDropDownBox(CM::Viewport* target, CM::RenderWindow* window, const GUIDropDownAreaPlacement& placement,
-		const CM::Vector<GUIDropDownData>::type& elements, const GUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback)
+		const GUIDropDownData& dropDownData, const GUISkin& skin, GUIDropDownType type, std::function<void()> onClosedCallback)
 	{
 		closeDropDownBox();
 
 		mDropDownSO = SceneObject::create("DropDownBox");
-		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>(target, window, placement, elements, skin, type);
+		mDropDownBox = mDropDownSO->addComponent<GUIDropDownBox>(target, window, placement, dropDownData, skin, type);
 		mOnClosedCallback = onClosedCallback;
 
 		return mDropDownBox;

+ 4 - 0
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -948,6 +948,10 @@ namespace BansheeEngine
 			mContextMenu.addMenuItem(L"Copy", boost::bind(&GUIInputBox::copyText, const_cast<GUIInputBox*>(this)));
 			mContextMenu.addMenuItem(L"Paste", boost::bind(&GUIInputBox::pasteText, const_cast<GUIInputBox*>(this)));
 
+			mContextMenu.setLocalizedName(L"Cut", HString(L"Cut"));
+			mContextMenu.setLocalizedName(L"Copy", HString(L"Copy"));
+			mContextMenu.setLocalizedName(L"Paste", HString(L"Paste"));
+
 			initialized = true;
 		}
 

+ 4 - 2
BansheeEngine/Source/BsGUIListBox.cpp

@@ -87,11 +87,13 @@ namespace BansheeEngine
 	{
 		closeListBox();
 
-		Vector<GUIDropDownData>::type dropDownData;
+		GUIDropDownData dropDownData;
 		UINT32 i = 0;
 		for(auto& elem : mElements)
 		{
-			dropDownData.push_back(GUIDropDownData::button(elem, boost::bind(&GUIListBox::elementSelected, this, i)));
+			WString identifier = toWString(i);
+			dropDownData.entries.push_back(GUIDropDownDataEntry::button(identifier, boost::bind(&GUIListBox::elementSelected, this, i)));
+			dropDownData.localizedNames[identifier] = elem;
 			i++;
 		}
 

+ 13 - 6
BansheeEngine/Source/BsGUIMenu.cpp

@@ -146,34 +146,41 @@ namespace BansheeEngine
 		parent->removeChild(item->getName());
 	}
 
-	Vector<GUIDropDownData>::type GUIMenu::getDropDownData() const
+	GUIDropDownData GUIMenu::getDropDownData() const
 	{
 		return getDropDownDataInternal(mRootElement);
 	}
 
-	Vector<GUIDropDownData>::type GUIMenu::getDropDownDataInternal(const GUIMenuItem& menu) const
+	void GUIMenu::setLocalizedName(const WString& menuItemLabel, const HString& localizedName)
 	{
-		Vector<GUIDropDownData>::type dropDownData;
+		mLocalizedEntryNames[menuItemLabel] = localizedName;
+	}
+
+	GUIDropDownData GUIMenu::getDropDownDataInternal(const GUIMenuItem& menu) const
+	{
+		GUIDropDownData dropDownData;
 
 		for(auto& menuItem : menu.mChildren)
 		{
 			if(menuItem->isSeparator())
 			{
-				dropDownData.push_back(GUIDropDownData::separator());
+				dropDownData.entries.push_back(GUIDropDownDataEntry::separator());
 			}
 			else
 			{
 				if(menuItem->getNumChildren() == 0)
 				{
-					dropDownData.push_back(GUIDropDownData::button(menuItem->getName(), menuItem->getCallback()));
+					dropDownData.entries.push_back(GUIDropDownDataEntry::button(menuItem->getName(), menuItem->getCallback()));
 				}
 				else
 				{
-					dropDownData.push_back(GUIDropDownData::subMenu(menuItem->getName(), getDropDownDataInternal(*menuItem)));
+					dropDownData.entries.push_back(GUIDropDownDataEntry::subMenu(menuItem->getName(), getDropDownDataInternal(*menuItem)));
 				}
 			}
 		}
 
+		dropDownData.localizedNames = mLocalizedEntryNames;
+
 		return dropDownData;
 	}
 }

+ 1 - 1
CamelotClient/Source/BsGUIMenuBar.cpp

@@ -246,7 +246,7 @@ namespace BansheeEditor
 				return;
 		}
 
-		Vector<GUIDropDownData>::type dropDownData = subMenu->menu->getDropDownData();
+		GUIDropDownData dropDownData = subMenu->menu->getDropDownData();
 		GUIWidget& widget = subMenu->button->_getParentWidget();
 
 		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(subMenu->button->getBounds());

+ 1 - 0
TODO.txt

@@ -5,6 +5,7 @@ LONGTERM TODO:
 2. Finish docking manager
 3. Window drag'n'drop detect
 4. Basic profiler
+  - When building a profiler have main Profiler class which just does measurements, then ProfilerOverlay for data display on-screen, ProfilerEditor for Unity-like Profiler, etc.
 
 I still re-create GUIWidget mesh every frame instead of just updating it.