Selaa lähdekoodia

Code convention & minor logic edits. Unify tag API between UIElement & Node. Use ; as the default tag separator to match the editor. Do not allow adding empty tags.

Lasse Öörni 10 vuotta sitten
vanhempi
sitoutus
474276fc17

+ 14 - 12
Source/Urho3D/AngelScript/APITemplates.h

@@ -701,10 +701,14 @@ template <class T> void RegisterNode(asIScriptEngine* engine, const char* classN
     engine->RegisterObjectMethod(className, "void RemoveComponents(bool, bool)", asMETHODPR(T, RemoveComponents, (bool, bool), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void RemoveComponents(const String&in)", asFUNCTION(NodeRemoveComponents), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "void RemoveAllComponents()", asMETHOD(T, RemoveAllComponents), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void AddTag(const String&in)", asMETHOD(T, AddTag), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void AddTags(const String&in, int8 separator = ';')", asMETHODPR(T, AddTags, (const String&, char), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "bool RemoveTag(const String&in)", asMETHOD(T, RemoveTag), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void RemoveAllTags()", asMETHOD(T, RemoveAllTags), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Array<Node@>@ GetChildren(bool recursive = false) const", asFUNCTION(NodeGetChildren), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Array<Node@>@ GetChildrenWithComponent(const String&in, bool recursive = false) const", asFUNCTION(NodeGetChildrenWithComponent), asCALL_CDECL_OBJLAST);
-	engine->RegisterObjectMethod(className, "Array<Node@>@ GetChildrenWithTag(const String&in, bool recursive = false) const", asFUNCTION(NodeGetChildrenWithTag), asCALL_CDECL_OBJLAST);
-	engine->RegisterObjectMethod(className, "Array<Node@>@ GetChildrenWithScript(bool recursive = false) const", asFUNCTION(NodeGetChildrenWithScript), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "Array<Node@>@ GetChildrenWithTag(const String&in, bool recursive = false) const", asFUNCTION(NodeGetChildrenWithTag), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "Array<Node@>@ GetChildrenWithScript(bool recursive = false) const", asFUNCTION(NodeGetChildrenWithScript), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Array<Node@>@ GetChildrenWithScript(const String&in, bool recursive = false) const", asFUNCTION(NodeGetChildrenWithClassName), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Node@+ GetChild(const String&in, bool recursive = false) const", asMETHODPR(T, GetChild, (const String&, bool) const, Node*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Array<Component@>@ GetComponents() const", asFUNCTION(NodeGetComponents), asCALL_CDECL_OBJLAST);
@@ -712,6 +716,8 @@ template <class T> void RegisterNode(asIScriptEngine* engine, const char* classN
     engine->RegisterObjectMethod(className, "Component@+ GetComponent(const String&in, bool recursive = false) const", asFUNCTION(NodeGetComponentWithType), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Component@+ GetParentComponent(const String&in, bool fullTraversal = false) const", asFUNCTION(NodeGetParentComponentWithType), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "bool HasComponent(const String&in) const", asFUNCTION(NodeHasComponent), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "bool HasTag(const String&in)", asMETHOD(T, HasTag), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "Array<String>@ get_tags()", asFUNCTION(NodeGetTags), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Vector3 LocalToWorld(const Vector3&in) const", asMETHODPR(T, LocalToWorld, (const Vector3&) const, Vector3), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Vector3 LocalToWorld(const Vector4&in) const", asMETHODPR(T, LocalToWorld, (const Vector4&) const, Vector3), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Vector2 LocalToWorld2D(const Vector2&in) const", asMETHODPR(T, LocalToWorld2D, (const Vector2&) const, Vector2), asCALL_THISCALL);
@@ -763,11 +769,6 @@ template <class T> void RegisterNode(asIScriptEngine* engine, const char* classN
     engine->RegisterObjectMethod(className, "void set_parent(Node@+)", asMETHOD(T, SetParent), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Node@+ get_parent() const", asMETHOD(T, GetParent), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "VariantMap& get_vars()", asFUNCTION(NodeGetVars), asCALL_CDECL_OBJLAST);
-	engine->RegisterObjectMethod(className, "bool HasTag(const String&in)", asMETHOD(T, HasTag), asCALL_THISCALL);
-	engine->RegisterObjectMethod(className, "Array<String>@ get_tags()", asFUNCTION(NodeGetTags), asCALL_CDECL_OBJLAST);
-	engine->RegisterObjectMethod(className, "void AddTag(const String&in)", asMETHOD(T, AddTag), asCALL_THISCALL);
-	engine->RegisterObjectMethod(className, "void RemoveTag(const String&in)", asMETHOD(T, RemoveTag), asCALL_THISCALL);
-	engine->RegisterObjectMethod(className, "void RemoveAllTags()", asMETHOD(T, RemoveAllTags), asCALL_THISCALL);
 }
 
 static bool ResourceLoad(File* file, XMLFile* ptr)
@@ -1074,11 +1075,17 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "void Remove()", asMETHOD(T, Remove), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint FindChild(UIElement@+) const", asMETHOD(T, FindChild), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetParent(UIElement@+, uint index = M_MAX_UNSIGNED)", asMETHOD(T, SetParent), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void AddTag(const String&in)", asMETHOD(T, AddTag), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void AddTags(const String&in, int8 separator = ';')", asMETHODPR(T, AddTags, (const String&, char), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void RemoveAllTags()", asMETHOD(T, RemoveAllTags), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "bool RemoveTag(const String&in)", asMETHOD(T, RemoveTag), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ GetChild(const String&in, bool recursive = false) const", asMETHODPR(T, GetChild, (const String&, bool) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ GetChild(const StringHash&in, const Variant&in value = Variant(), bool recursive = false) const", asMETHODPR(T, GetChild, (const StringHash&, const Variant&, bool) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Array<UIElement@>@ GetChildren(bool recursive = false) const", asFUNCTION(UIElementGetChildren), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "UIElement@+ GetElementEventSender() const", asMETHOD(T, GetElementEventSender), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Variant& GetVar(const StringHash&in)", asMETHOD(T, GetVar), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "bool HasTag(const String&in) const", asMETHOD(T, HasTag), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "Array<UIElement@>@ GetChildrenWithTag(const String&in, bool recursive = false) const", asFUNCTION(UIElementGetChildrenWithTag), asCALL_CDECL_OBJLAST);
     if (!isSprite)
     {
         engine->RegisterObjectMethod(className, "IntVector2 ScreenToElement(const IntVector2&in)", asMETHOD(T, ScreenToElement), asCALL_THISCALL);
@@ -1219,11 +1226,6 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     }
     engine->RegisterObjectMethod(className, "float get_derivedOpacity()", asMETHOD(T, GetDerivedOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "VariantMap& get_vars()", asFUNCTION(UIElementGetVars), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod(className, "void AddTag(const String&in)", asMETHOD(T, AddTag), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void ClearTags()", asMETHOD(T, ClearTags), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool RemoveTag(const String&in)", asMETHOD(T, RemoveTag), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool HasTag(const String&in) const", asMETHOD(T, HasTag), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "Array<UIElement@>@ GetChildrenWithTag(const String&in, bool recursive = false) const", asFUNCTION(UIElementGetChildrenWithTag), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Array<String>@ get_tags() const", asFUNCTION(UIElementGetTags), asCALL_CDECL_OBJLAST);
 }
 

+ 6 - 5
Source/Urho3D/LuaScript/pkgs/Scene/Node.pkg

@@ -25,6 +25,10 @@ class Node : public Animatable
     tolua_outside bool NodeSaveXML @ SaveXML(File* dest, const String indentation = "\t") const;
     tolua_outside bool NodeSaveJSON @ SaveJSON(File* dest, const String indentation = "\t") const;
     void SetName(const String name);
+    void AddTag(const String tag);
+    void AddTags(const String tags, char separator);
+    bool RemoveTag(const String tag);
+    void RemoveAllTags();
 
     void SetPosition(const Vector3& position);
     void SetPosition2D(const Vector2& position);
@@ -192,13 +196,10 @@ class Node : public Animatable
     Node* CreateChild(unsigned id, CreateMode mode);
     void AddComponent(Component* component, unsigned id, CreateMode mode);
 
-	void AddTag(const String& tag);
-    void RemoveTag(const String& tag);
-	void RemoveAllTags();
-    bool HasTag(const String& tag) const;
+    bool HasTag(const String tag) const;
     const StringVector& GetTags() const;
 
-	// void GetChildrenWithTag(PODVector<Node*>& dest, const String& tag, bool recursive = false) const;
+    // void GetChildrenWithTag(PODVector<Node*>& dest, const String& tag, bool recursive = false) const;
     tolua_outside const PODVector<Node*>& NodeGetChildrenWithTag @ GetChildrenWithTag(const String& tag, bool recursive = false) const; 
 
     tolua_readonly tolua_property__get_set unsigned ID;

+ 10 - 8
Source/Urho3D/LuaScript/pkgs/UI/UIElement.pkg

@@ -147,6 +147,10 @@ class UIElement : public Animatable
     void SetInternal(bool enable);
     void SetTraversalMode(TraversalMode traversalMode);
     void SetElementEventSender(bool flag);
+    void AddTag(const String tag);
+    void AddTags(const String tags, char separator);
+    bool RemoveTag(const String tag);
+    void RemoveAllTags();
 
     const String GetName() const;
     const IntVector2& GetPosition() const;
@@ -205,6 +209,12 @@ class UIElement : public Animatable
     const Color& GetDerivedColor() const;
     const Variant& GetVar(StringHash key) const;
     const VariantMap& GetVars() const;
+
+    bool HasTag(const String tag) const;
+    const StringVector& GetTags() const;
+    // void GetChildrenWithTag(PODVector<UIElement*>& dest, const String& tag, bool recursive = false) const;
+    tolua_outside const PODVector<UIElement*>& UIElementGetChildrenWithTag @ GetChildrenWithTag(const String& tag, bool recursive = false) const;
+
     IntVector2 ScreenToElement(const IntVector2& screenPosition);
     IntVector2 ElementToScreen(const IntVector2& position);
     bool IsInside(IntVector2 position, bool isScreen);
@@ -221,14 +231,6 @@ class UIElement : public Animatable
     TraversalMode GetTraversalMode() const;
     bool IsElementEventSender() const;
     UIElement* GetElementEventSender() const;
-    void AddTag(const String tag);
-    void ClearTags();
-    bool RemoveTag(const String tag);
-    bool HasTag(const String tag) const;
-    const StringVector& GetTags() const;
-
-    // void GetChildrenWithTag(PODVector<UIElement*>& dest, const String& tag, bool recursive = false) const;
-    tolua_outside const PODVector<UIElement*>& UIElementGetChildrenWithTag @ GetChildrenWithTag(const String& tag, bool recursive = false) const; 
 
     tolua_readonly tolua_property__get_set IntVector2& screenPosition;
     tolua_property__get_set String name;

+ 95 - 125
Source/Urho3D/Scene/Node.cpp

@@ -79,9 +79,8 @@ void Node::RegisterObject(Context* context)
 
     URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Name", GetName, SetName, String, String::EMPTY, AM_DEFAULT);
-	URHO3D_ATTRIBUTE("Tags", StringVector, tags_, Variant::emptyStringVector, AM_DEFAULT);
-	URHO3D_ACCESSOR_ATTRIBUTE("Tags", GetTags, SetTags, StringVector, Variant::emptyStringVector, AM_DEFAULT );
-	URHO3D_ACCESSOR_ATTRIBUTE("Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_FILE);
+    URHO3D_ACCESSOR_ATTRIBUTE("Tags", GetTags, SetTags, StringVector, Variant::emptyStringVector, AM_DEFAULT);
+    URHO3D_ACCESSOR_ATTRIBUTE("Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_FILE);
     URHO3D_ACCESSOR_ATTRIBUTE("Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_FILE);
     URHO3D_ACCESSOR_ATTRIBUTE("Scale", GetScale, SetScale, Vector3, Vector3::ONE, AM_DEFAULT);
     URHO3D_ATTRIBUTE("Variables", VariantMap, vars_, Variant::emptyVariantMap, AM_FILE); // Network replication of vars uses custom data
@@ -344,126 +343,97 @@ void Node::SetName(const String& name)
 
 void Node::SetTags(const StringVector& tags)
 {
-	RemoveAllTags();
-	AddTags(tags);
-	// sync
-	// MarkNetworkUpdate(); already called in RemoveAllTags() / AddTags()
+    RemoveAllTags();
+    AddTags(tags);
+    // MarkNetworkUpdate() already called in RemoveAllTags() / AddTags()
 }
 
 void Node::AddTag(const String& tag)
 {
-	// check if tag already added. 
-	StringVector::Iterator it = tags_.Find(tag);
-	if (it != tags_.End())
-		return;
-	
-		// add tag 
-		tags_.Push(tag);
-
-		// cache 
-		scene_->NodeTagAdded(this, tag);
-
-		// send event
-		using namespace NodeTagAdded;
-		VariantMap& eventData = GetEventDataMap();
-		eventData[P_SCENE] = scene_;
-		eventData[P_NODE] = this;
-		eventData[P_TAG] = tag;
-		scene_->SendEvent(E_NODETAGADDED, eventData);
-		
-	// sync
-	MarkNetworkUpdate();
+    // Check if tag empty or already added
+    if (tag.Empty() || HasTag(tag))
+        return;
+    
+    // Add tag
+    tags_.Push(tag);
+
+    // Cache
+    scene_->NodeTagAdded(this, tag);
+
+    // Send event
+    using namespace NodeTagAdded;
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_SCENE] = scene_;
+    eventData[P_NODE] = this;
+    eventData[P_TAG] = tag;
+    scene_->SendEvent(E_NODETAGADDED, eventData);
+
+    // Sync
+    MarkNetworkUpdate();
 }
 
-void Node::AddTags(const String& tags, char s /*= ','*/)
+void Node::AddTags(const String& tags, char separator)
 {
-	if (tags.Empty())
-		return;
-	StringVector tags_temp = tags.Split(s);
-	AddTags(tags_temp);
+    StringVector tagVector = tags.Split(separator);
+    AddTags(tagVector);
 }
 
 void Node::AddTags(const StringVector& tags)
 {
-	bool added = false;
-
-	for (unsigned i = 0; i < tags.Size(); ++i)
-	{
-		// check if tag already added. 
-		StringVector::Iterator it = tags_.Find(tags[i]);
-		if (it != tags_.End())
-		{
-			// add tag 
-			tags_.Push(tags[i]);
-
-			// cache 
-			scene_->NodeTagAdded(this, tags[i]);
-			// send event
-			using namespace NodeTagAdded;
-			VariantMap& eventData = GetEventDataMap();
-			eventData[P_SCENE] = scene_;
-			eventData[P_NODE] = this;
-			eventData[P_TAG] = tags[i];
-			scene_->SendEvent(E_NODETAGADDED, eventData);
-			added = true;
-		}
-	}
-
-	// sync
-	if (added)
-		MarkNetworkUpdate();
-}
-
-void Node::RemoveTag(const String& tag)
-{
-	// early out
-	if (tags_.Empty())
-		return;
-	bool removed = tags_.Remove(tag);
-
-	// nothing to do
-	if (!removed)
-		return;
-	
-	// scene cache update
-	if (scene_)
-	{
-		scene_->NodeTagRemoved(this, tag);
-		// send event
-		using namespace NodeTagRemoved;
-		VariantMap& eventData = GetEventDataMap();
-		eventData[P_SCENE] = scene_;
-		eventData[P_NODE] = this;
-		eventData[P_TAG] = tag;
-		scene_->SendEvent(E_NODETAGREMOVED, eventData);
-	}
-	// sync
-	MarkNetworkUpdate();
+    // This is OK, as MarkNetworkUpdate() early-outs when called multiple times
+    for (unsigned i = 0; i < tags.Size(); ++i)
+        AddTag(tags[i]);
+}
+
+bool Node::RemoveTag(const String& tag)
+{
+    bool removed = tags_.Remove(tag);
+
+    // Nothing to do
+    if (!removed)
+        return false;
+
+    // Scene cache update
+    if (scene_)
+    {
+        scene_->NodeTagRemoved(this, tag);
+        // Send event
+        using namespace NodeTagRemoved;
+        VariantMap& eventData = GetEventDataMap();
+        eventData[P_SCENE] = scene_;
+        eventData[P_NODE] = this;
+        eventData[P_TAG] = tag;
+        scene_->SendEvent(E_NODETAGREMOVED, eventData);
+    }
+    
+    // Sync
+    MarkNetworkUpdate();
+    return true;
 }
 
 void Node::RemoveAllTags()
 {
-	// clear old scene cache
-	if (scene_)
-	{
-		for (unsigned i = 0; i < tags_.Size(); ++i)
-		{
-			scene_->NodeTagRemoved(this, tags_[i]);
+    // Clear old scene cache
+    if (scene_)
+    {
+        for (unsigned i = 0; i < tags_.Size(); ++i)
+        {
+            scene_->NodeTagRemoved(this, tags_[i]);
 
-			// send event
-			using namespace NodeTagRemoved;
-			VariantMap& eventData = GetEventDataMap();
-			eventData[P_SCENE] = scene_;
-			eventData[P_NODE] = this;
-			eventData[P_TAG] = tags_[i];
-			scene_->SendEvent(E_NODETAGREMOVED, eventData);
-		}
-	}
+            // Send event
+            using namespace NodeTagRemoved;
+            VariantMap& eventData = GetEventDataMap();
+            eventData[P_SCENE] = scene_;
+            eventData[P_NODE] = this;
+            eventData[P_TAG] = tags_[i];
+            scene_->SendEvent(E_NODETAGREMOVED, eventData);
+        }
+    }
 
-	tags_.Clear();
+    tags_.Clear();
 
-	// sync
-	MarkNetworkUpdate();
+    // Sync
+    MarkNetworkUpdate();
 }
 
 void Node::SetPosition(const Vector3& position)
@@ -1240,18 +1210,18 @@ void Node::GetChildrenWithComponent(PODVector<Node*>& dest, StringHash type, boo
 
 void Node::GetChildrenWithTag(PODVector<Node*>& dest, const String& tag, bool recursive /*= true*/) const
 {
-	dest.Clear();
+    dest.Clear();
 
-	if (!recursive)
-	{
-		for (Vector<SharedPtr<Node> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
-		{
-			if ((*i)->HasTag(tag))
-				dest.Push(*i);
-		}
-	}
-	else
-		GetChildrenWithTagRecursive(dest, tag);
+    if (!recursive)
+    {
+        for (Vector<SharedPtr<Node> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+        {
+            if ((*i)->HasTag(tag))
+                dest.Push(*i);
+        }
+    }
+    else
+        GetChildrenWithTagRecursive(dest, tag);
 }
 
 Node* Node::GetChild(unsigned index) const
@@ -1327,7 +1297,7 @@ bool Node::HasComponent(StringHash type) const
 
 bool Node::HasTag(const String& tag) const
 {
-	return tags_.Empty() ? false : tags_.Contains(tag);
+    return tags_.Contains(tag);
 }
 
 const Variant& Node::GetVar(StringHash key) const
@@ -2102,14 +2072,14 @@ void Node::GetComponentsRecursive(PODVector<Component*>& dest, StringHash type)
 
 void Node::GetChildrenWithTagRecursive(PODVector<Node*>& dest, const String& tag) const
 {
-	for (Vector<SharedPtr<Node> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
-	{
-		Node* node = *i;
-		if (node->HasTag(tag))
-			dest.Push(node);
-		if (!node->children_.Empty())
-			node->GetChildrenWithTagRecursive(dest, tag);
-	}
+    for (Vector<SharedPtr<Node> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+    {
+        Node* node = *i;
+        if (node->HasTag(tag))
+            dest.Push(node);
+        if (!node->children_.Empty())
+            node->GetChildrenWithTagRecursive(dest, tag);
+    }
 }
 
 Node* Node::CloneRecursive(Node* parent, SceneResolver& resolver, CreateMode mode)

+ 28 - 26
Source/Urho3D/Scene/Node.h

@@ -96,20 +96,20 @@ public:
     /// Set name of the scene node. Names are not required to be unique.
     void SetName(const String& name);
 
-	/// Set Tags, overrites old tags.
-	void SetTags(const StringVector& tags);
-	/// Add tag. 
-	void AddTag(const String& tag);
-	/// Add tags. 
-	void AddTags(const String& tags, char s = ',');
-	/// Add tags.
-	void AddTags(const StringVector& tags);
-	/// Remove Tag.
-	void RemoveTag(const String& tag);
-	/// Remove all Tags.
-	void RemoveAllTags();
-
-	/// Set position in parent space. If the scene node is on the root level (is child of the scene itself), this is same as world space.
+    /// Set tags. Old tags are overwritten.
+    void SetTags(const StringVector& tags);
+    /// Add a tag.
+    void AddTag(const String& tag);
+    /// Add tags with the specified separator, by default ;
+    void AddTags(const String& tags, char separator = ';');
+    /// Add tags.
+    void AddTags(const StringVector& tags);
+    /// Remove tag. Return true if existed.
+    bool RemoveTag(const String& tag);
+    /// Remove all tags.
+    void RemoveAllTags();
+
+    /// Set position in parent space. If the scene node is on the root level (is child of the scene itself), this is same as world space.
     void SetPosition(const Vector3& position);
 
     /// Set position in parent space (for Urho2D).
@@ -319,8 +319,12 @@ public:
     /// Return name hash.
     StringHash GetNameHash() const { return nameHash_; }
 
-	/// Return the tags.
-	const StringVector& GetTags() const { return tags_; }
+    /// Return all tags.
+    const StringVector& GetTags() const { return tags_; }
+
+    /// Return whether has a specific tag.
+    bool HasTag(const String& tag) const;
+
     /// Return parent scene node.
     Node* GetParent() const { return parent_; }
 
@@ -475,10 +479,10 @@ public:
     void GetChildren(PODVector<Node*>& dest, bool recursive = false) const;
     /// Return child scene nodes with a specific component.
     void GetChildrenWithComponent(PODVector<Node*>& dest, StringHash type, bool recursive = false) const;
-	/// Return child scene nodes with a specific tag.
-	void GetChildrenWithTag(PODVector<Node*>& dest, const String& tag, bool recursive = false) const;
+    /// Return child scene nodes with a specific tag.
+    void GetChildrenWithTag(PODVector<Node*>& dest, const String& tag, bool recursive = false) const;
 
-	/// Return child scene node by index.
+    /// Return child scene node by index.
     Node* GetChild(unsigned index) const;
     /// Return child scene node by name.
     Node* GetChild(const String& name, bool recursive = false) const;
@@ -504,8 +508,6 @@ public:
     Component* GetParentComponent(StringHash type, bool fullTraversal = false) const;
     /// Return whether has a specific component.
     bool HasComponent(StringHash type) const;
-	/// Return whether has a specific tag.
-	bool HasTag(const String& tag) const;
     /// Return listener components.
     const Vector<WeakPtr<Component> > GetListeners() const { return listeners_; }
 
@@ -615,9 +617,9 @@ private:
     void GetChildrenRecursive(PODVector<Node*>& dest) const;
     /// Return child nodes with a specific component recursively.
     void GetChildrenWithComponentRecursive(PODVector<Node*>& dest, StringHash type) const;
-	/// Return child nodes with a specific tag recursively.
-	void GetChildrenWithTagRecursive(PODVector<Node*>& dest, const String& tag) const;
-	/// Return specific components recursively.
+    /// Return child nodes with a specific tag recursively.
+    void GetChildrenWithTagRecursive(PODVector<Node*>& dest, const String& tag) const;
+    /// Return specific components recursively.
     void GetComponentsRecursive(PODVector<Component*>& dest, StringHash type) const;
     /// Clone node recursively.
     Node* CloneRecursive(Node* parent, SceneResolver& resolver, CreateMode mode);
@@ -660,8 +662,8 @@ private:
     Connection* owner_;
     /// Name.
     String name_;
-	/// Tag String.
-	StringVector tags_;
+    /// Tag strings.
+    StringVector tags_;
     /// Name hash.
     StringHash nameHash_;
     /// Attribute buffer for network updates.

+ 26 - 30
Source/Urho3D/Scene/Scene.cpp

@@ -719,15 +719,15 @@ Node* Scene::GetNode(unsigned id) const
 
 bool Scene::GetNodesWithTag(PODVector<Node*>& dest, const String& tag) const
 {
-	dest.Clear();
-	HashMap<StringHash, PODVector<Node*> >::ConstIterator it = tagedNodes_.Find(tag);
-	if (it != tagedNodes_.End())
-	{
-		dest = it->second_;
-		return true;
-	}
-	else
-		return false;
+    dest.Clear();
+    HashMap<StringHash, PODVector<Node*> >::ConstIterator it = taggedNodes_.Find(tag);
+    if (it != taggedNodes_.End())
+    {
+        dest = it->second_;
+        return true;
+    }
+    else
+        return false;
 }
 
 Component* Scene::GetComponent(unsigned id) const
@@ -949,15 +949,13 @@ void Scene::NodeAdded(Node* node)
         localNodes_[id] = node;
     }
 
-	// cache tag if already tagged.
-	if (!node->GetTags().Empty())
-	{
-		const StringVector& tags = node->GetTags();
-		for (unsigned i = 0; i < tags.Size(); ++i)
-		{
-			tagedNodes_[tags[i]].Push(node);
-		}
-	}
+    // Cache tag if already tagged.
+    if (!node->GetTags().Empty())
+    {
+        const StringVector& tags = node->GetTags();
+        for (unsigned i = 0; i < tags.Size(); ++i)
+            taggedNodes_[tags[i]].Push(node);
+    }
 
     // Add already created components and child nodes now
     const Vector<SharedPtr<Component> >& components = node->GetComponents();
@@ -970,12 +968,12 @@ void Scene::NodeAdded(Node* node)
 
 void Scene::NodeTagAdded(Node* node, const String& tag)
 {
-	tagedNodes_[tag].Push(node);
+    taggedNodes_[tag].Push(node);
 }
 
 void Scene::NodeTagRemoved(Node* node, const String& tag)
 {
-	tagedNodes_[tag].Remove(node);
+    taggedNodes_[tag].Remove(node);
 }
 
 void Scene::NodeRemoved(Node* node)
@@ -994,16 +992,14 @@ void Scene::NodeRemoved(Node* node)
 
     node->ResetScene();
 
-	// Remove node from tag cache 
-	if (!node->GetTags().Empty())
-	{
-		const StringVector& tags = node->GetTags();
-		for (unsigned i = 0; i < tags.Size(); ++i)
-		{
-			tagedNodes_[tags[i]].Remove(node);
-		}
-	}
-		
+    // Remove node from tag cache
+    if (!node->GetTags().Empty())
+    {
+        const StringVector& tags = node->GetTags();
+        for (unsigned i = 0; i < tags.Size(); ++i)
+            taggedNodes_[tags[i]].Remove(node);
+    }
+
     // Remove components and child nodes as well
     const Vector<SharedPtr<Component> >& components = node->GetComponents();
     for (Vector<SharedPtr<Component> >::ConstIterator i = components.Begin(); i != components.End(); ++i)

+ 2 - 2
Source/Urho3D/Scene/Scene.h

@@ -285,8 +285,8 @@ private:
     HashMap<unsigned, Component*> replicatedComponents_;
     /// Local components by ID.
     HashMap<unsigned, Component*> localComponents_;
-	/// Cached taged nodes by tag.
-	HashMap<StringHash, PODVector<Node*> > tagedNodes_;
+    /// Cached tagged nodes by tag.
+    HashMap<StringHash, PODVector<Node*> > taggedNodes_;
     /// Asynchronous loading progress.
     AsyncProgress asyncProgress_;
     /// Node and component ID resolver for asynchronous loading.

+ 70 - 49
Source/Urho3D/UI/UIElement.cpp

@@ -1403,6 +1403,42 @@ void UIElement::SetElementEventSender(bool flag)
     elementEventSender_ = flag;
 }
 
+void UIElement::SetTags(const StringVector& tags)
+{
+    RemoveAllTags();
+    AddTags(tags);
+}
+
+void UIElement::AddTag(const String& tag)
+{
+    if (tag.Empty() || HasTag(tag))
+        return;
+
+    tags_.Push(tag);
+}
+
+void UIElement::AddTags(const String& tags, char separator)
+{
+    StringVector tagVector = tags.Split(separator);
+    AddTags(tagVector);
+}
+
+void UIElement::AddTags(const StringVector& tags)
+{
+    for (unsigned i = 0; i < tags.Size(); ++i)
+        AddTag(tags[i]);
+}
+
+bool UIElement::RemoveTag(const String& tag)
+{
+    return tags_.Remove(tag);
+}
+
+void UIElement::RemoveAllTags()
+{
+    tags_.Clear();
+}
+
 float UIElement::GetDerivedOpacity() const
 {
     if (!useDerivedOpacity_)
@@ -1566,6 +1602,40 @@ const Variant& UIElement::GetVar(const StringHash& key) const
     return i != vars_.End() ? i->second_ : Variant::EMPTY;
 }
 
+bool UIElement::HasTag(const String& tag) const
+{
+    return tags_.Contains(tag);
+}
+
+void UIElement::GetChildrenWithTag(PODVector<UIElement*>& dest, const String& tag, bool recursive) const
+{
+    dest.Clear();
+
+    if (!recursive)
+    {
+        for (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+        {
+            UIElement* element = *i;
+            if (element->HasTag(tag))
+                dest.Push(element);
+        }
+    }
+    else
+        GetChildrenWithTagRecursive(dest, tag);
+}
+
+void UIElement::GetChildrenWithTagRecursive(PODVector<UIElement*>& dest, const String& tag) const
+{
+    for (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+    {
+        UIElement* element = *i;
+        if (element->HasTag(tag))
+            dest.Push(element);
+        if (!element->children_.Empty())
+            element->GetChildrenWithTagRecursive(dest, tag);
+    }
+}
+
 IntVector2 UIElement::ScreenToElement(const IntVector2& screenPosition)
 {
     return screenPosition - GetScreenPosition();
@@ -2054,53 +2124,4 @@ void UIElement::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
     UpdateAttributeAnimations(eventData[P_TIMESTEP].GetFloat());
 }
 
-void UIElement::AddTag(const String& tag)
-{
-    tags_.Push(tag);
-}
-
-void UIElement::ClearTags()
-{
-    tags_.Clear();
-}
-
-bool UIElement::RemoveTag(const String& tag)
-{
-    return tags_.Remove(tag);
-}
-
-bool UIElement::HasTag(const String& tag) const
-{
-    return tags_.Contains(tag);
-}
-
-void UIElement::GetChildrenWithTag(PODVector<UIElement*>& dest, const String& tag, bool recursive) const
-{
-    dest.Clear();
-
-    if (!recursive)
-    {
-        for (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
-        {
-            UIElement* element = *i;
-            if (element->HasTag(tag))
-                dest.Push(element);
-        }
-    }
-    else
-        GetChildrenWithTagRecursive(dest, tag);
-}
-
-void UIElement::GetChildrenWithTagRecursive(PODVector<UIElement*>& dest, const String& tag) const
-{
-    for (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
-    {
-        UIElement* element = *i;
-        if (element->HasTag(tag))
-            dest.Push(element);
-        if (!element->children_.Empty())
-            element->GetChildrenWithTagRecursive(dest, tag);
-    }
-}
-
 }

+ 22 - 18
Source/Urho3D/UI/UIElement.h

@@ -338,6 +338,19 @@ public:
     /// Set element event sender flag. When child element is added or deleted, the event would be sent using UIElement found in the parental chain having this flag set. If not set, the event is sent using UI's root as per normal.
     void SetElementEventSender(bool flag);
 
+    /// Set tags. Old tags are overwritten.
+    void SetTags(const StringVector& tags);
+    /// Add a tag.
+    void AddTag(const String& tag);
+    /// Add tags with the specified separator, by default ;
+    void AddTags(const String& tags, char separator = ';');
+    /// Add tags.
+    void AddTags(const StringVector& tags);
+    // Remove specific tag. Return true if existed.
+    bool RemoveTag(const String& tag);
+    // Remove all tags.
+    void RemoveAllTags();
+
     /// Template version of creating a child element.
     template <class T> T* CreateChild(const String& name = String::EMPTY, unsigned index = M_MAX_UNSIGNED);
 
@@ -503,6 +516,15 @@ public:
     /// Return all user variables.
     const VariantMap& GetVars() const { return vars_; }
 
+    /// Return whether element is tagged by a specific tag.
+    bool HasTag(const String& tag) const;
+
+    /// Return all tags.
+    const StringVector& GetTags() const { return tags_; }
+
+    /// Return child elements with a specific tag either recursively or non-recursively.
+    void GetChildrenWithTag(PODVector<UIElement*>& dest, const String& tag, bool recursive = false) const;
+
     /// Return the drag button combo if this element is being dragged.
     int GetDragButtonCombo() const { return dragButtonCombo_; }
 
@@ -558,24 +580,6 @@ public:
     /// Return effective minimum size, also considering layout. Used internally.
     IntVector2 GetEffectiveMinSize() const;
     
-    /// Add tag.
-    void AddTag(const String& tag);
-    
-    // Remove tag if found.
-    bool RemoveTag(const String& tag);
-
-    // Remove all tags.
-    void ClearTags();
-        
-    /// Return whether element is tagged by a specific tag.
-    bool HasTag(const String& tag) const;
-
-    /// Return tags.
-    const StringVector& GetTags() const { return tags_; }
-
-    /// Return child elements with a specific tag either recursively or non-recursively.
-    void GetChildrenWithTag(PODVector<UIElement*>& dest, const String& tag, bool recursive = false) const;
-    
 protected:
     /// Handle attribute animation added.
     virtual void OnAttributeAnimationAdded();

+ 1 - 1
bin/Data/Scripts/Editor/EditorInspectorWindow.as

@@ -672,7 +672,7 @@ void HandleTagsEdit(StringHash eventType, VariantMap& eventData)
 {
     LineEdit@ lineEdit = eventData["Element"].GetPtr();
     Array<String> tags = lineEdit.text.Split(';');
-    editUIElement.ClearTags();
+    editUIElement.RemoveAllTags();
     for (uint i = 0; i < tags.length; i++)
         editUIElement.AddTag(tags[i]);
 }