浏览代码

Add tags to UIElement

Ivan K 10 年之前
父节点
当前提交
68668266d2

+ 17 - 1
Source/Samples/37_UIDrag/UIDrag.cpp

@@ -73,6 +73,9 @@ void UIDrag::CreateGUI()
         b->SetSize(300, 100);
         b->SetSize(300, 100);
         b->SetPosition(IntVector2(50*i, 50*i));
         b->SetPosition(IntVector2(50*i, 50*i));
 
 
+        if (i % 2 == 0)
+            b->AddTag("SomeTag");
+
         SubscribeToEvent(b, E_DRAGMOVE, URHO3D_HANDLER(UIDrag, HandleDragMove));
         SubscribeToEvent(b, E_DRAGMOVE, URHO3D_HANDLER(UIDrag, HandleDragMove));
         SubscribeToEvent(b, E_DRAGBEGIN, URHO3D_HANDLER(UIDrag, HandleDragBegin));
         SubscribeToEvent(b, E_DRAGBEGIN, URHO3D_HANDLER(UIDrag, HandleDragBegin));
         SubscribeToEvent(b, E_DRAGCANCEL, URHO3D_HANDLER(UIDrag, HandleDragCancel));
         SubscribeToEvent(b, E_DRAGCANCEL, URHO3D_HANDLER(UIDrag, HandleDragCancel));
@@ -123,7 +126,9 @@ void UIDrag::CreateInstructions()
 
 
     // Construct new Text object, set string to display and font to use
     // Construct new Text object, set string to display and font to use
     Text* instructionText = ui->GetRoot()->CreateChild<Text>();
     Text* instructionText = ui->GetRoot()->CreateChild<Text>();
-    instructionText->SetText("Drag on the buttons to move them around.\nTouch input allows also multi-drag.");
+    instructionText->SetText("Drag on the buttons to move them around.\n"
+                             "Touch input allows also multi-drag.\n"
+                             "Press SPACE to show/hide tagged UI elements.");
     instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);
     instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);
     instructionText->SetTextAlignment(HA_CENTER);
     instructionText->SetTextAlignment(HA_CENTER);
 
 
@@ -217,4 +222,15 @@ void UIDrag::HandleUpdate(StringHash eventType, VariantMap& eventData)
         Text* t = (Text*)root->GetChild("Touch " + String(i));
         Text* t = (Text*)root->GetChild("Touch " + String(i));
         t->SetVisible(false);
         t->SetVisible(false);
     }
     }
+    
+    if (input->GetKeyPress(KEY_SPACE))
+    {
+        PODVector<UIElement*> elements;
+        root->GetChildrenWithTag(elements, "SomeTag");
+        for (PODVector<UIElement*>::ConstIterator i = elements.Begin(); i != elements.End(); ++i)
+        {
+            UIElement* element = *i;
+            element->SetVisible(!element->IsVisible());
+        }
+    }
 }
 }

+ 2 - 1
Source/Samples/37_UIDrag/UIDrag.h

@@ -34,7 +34,8 @@ namespace Urho3D
 /// This sample demonstrates:
 /// This sample demonstrates:
 ///     - Creating GUI elements from C++
 ///     - Creating GUI elements from C++
 ///     - Loading GUI Style from xml
 ///     - Loading GUI Style from xml
-///     - Subscribing to GUI drag events and handling them.
+///     - Subscribing to GUI drag events and handling them
+///     - Working with GUI elements with specific tags.
 class UIDrag : public Sample
 class UIDrag : public Sample
 {
 {
     URHO3D_OBJECT(UIDrag, Sample);
     URHO3D_OBJECT(UIDrag, Sample);

+ 18 - 0
Source/Urho3D/AngelScript/APITemplates.h

@@ -963,6 +963,18 @@ static CScriptArray* UIElementGetChildren(bool recursive, UIElement* ptr)
     return VectorToHandleArray<UIElement>(elements, "Array<UIElement@>");
     return VectorToHandleArray<UIElement>(elements, "Array<UIElement@>");
 }
 }
 
 
+static CScriptArray* UIElementGetChildrenWithTag(const String& tag, bool recursive, UIElement* ptr)
+{
+    PODVector<UIElement*> elements;
+    ptr->GetChildrenWithTag(elements, tag, recursive);
+    return VectorToHandleArray<UIElement>(elements, "Array<UIElement@>");
+}
+
+static CScriptArray* UIElementGetTags(UIElement* ptr)
+{
+    return VectorToArray<String>(ptr->GetTags(), "Array<String>");
+}
+
 static unsigned UIElementGetNumChildrenNonRecursive(UIElement* ptr)
 static unsigned UIElementGetNumChildrenNonRecursive(UIElement* ptr)
 {
 {
     return ptr->GetNumChildren(false);
     return ptr->GetNumChildren(false);
@@ -1189,6 +1201,12 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     }
     }
     engine->RegisterObjectMethod(className, "float get_derivedOpacity()", asMETHOD(T, GetDerivedOpacity), asCALL_THISCALL);
     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, "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 IsTagged(const String&in) const", asMETHOD(T, IsTagged), 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);
 }
 }
 
 
 /// Template function for registering a class derived from BorderImage.
 /// Template function for registering a class derived from BorderImage.

+ 15 - 0
Source/Urho3D/LuaScript/pkgs/UI/UIElement.pkg

@@ -221,6 +221,14 @@ class UIElement : public Animatable
     TraversalMode GetTraversalMode() const;
     TraversalMode GetTraversalMode() const;
     bool IsElementEventSender() const;
     bool IsElementEventSender() const;
     UIElement* GetElementEventSender() const;
     UIElement* GetElementEventSender() const;
+    void AddTag(const String& tag);
+    void ClearTags();
+    bool RemoveTag(const String& tag);
+    bool IsTagged(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_readonly tolua_property__get_set IntVector2& screenPosition;
     tolua_property__get_set String name;
     tolua_property__get_set String name;
@@ -317,6 +325,13 @@ static bool UIElementSaveXML(const UIElement* element, const String& fileName, c
     return element->SaveXML(file, indentation);
     return element->SaveXML(file, indentation);
 }
 }
 
 
+static const PODVector<UIElement*>& UIElementGetChildrenWithTag(const UIElement* element, const String& tag, bool recursive = false)
+{
+    static PODVector<UIElement*> dest;
+    element->GetChildrenWithTag(dest, tag, recursive);
+    return dest;
+}
+
 #define GetStyle GetAppliedStyle
 #define GetStyle GetAppliedStyle
 #define SetColorAttr SetColor
 #define SetColorAttr SetColor
 $}
 $}

+ 50 - 0
Source/Urho3D/UI/UIElement.cpp

@@ -185,6 +185,7 @@ void UIElement::RegisterObject(Context* context)
     URHO3D_ACCESSOR_ATTRIBUTE("Indent", GetIndent, SetIndent, int, 0, AM_FILE);
     URHO3D_ACCESSOR_ATTRIBUTE("Indent", GetIndent, SetIndent, int, 0, AM_FILE);
     URHO3D_ACCESSOR_ATTRIBUTE("Indent Spacing", GetIndentSpacing, SetIndentSpacing, int, 16, AM_FILE);
     URHO3D_ACCESSOR_ATTRIBUTE("Indent Spacing", GetIndentSpacing, SetIndentSpacing, int, 16, AM_FILE);
     URHO3D_ATTRIBUTE("Variables", VariantMap, vars_, Variant::emptyVariantMap, AM_FILE);
     URHO3D_ATTRIBUTE("Variables", VariantMap, vars_, Variant::emptyVariantMap, AM_FILE);
+    URHO3D_ATTRIBUTE("Tags", StringVector, tags_, Variant::emptyStringVector, AM_FILE);
 }
 }
 
 
 void UIElement::ApplyAttributes()
 void UIElement::ApplyAttributes()
@@ -2053,4 +2054,53 @@ void UIElement::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
     UpdateAttributeAnimations(eventData[P_TIMESTEP].GetFloat());
     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::IsTagged(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->IsTagged(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->IsTagged(tag))
+            dest.Push(element);
+        if (!element->children_.Empty())
+            element->GetChildrenWithTagRecursive(dest, tag);
+    }
+}
+
 }
 }

+ 23 - 2
Source/Urho3D/UI/UIElement.h

@@ -541,8 +541,7 @@ public:
     /// Adjust scissor for rendering.
     /// Adjust scissor for rendering.
     void AdjustScissor(IntRect& currentScissor);
     void AdjustScissor(IntRect& currentScissor);
     /// Get UI rendering batches with a specified offset. Also recurse to child elements.
     /// Get UI rendering batches with a specified offset. Also recurse to child elements.
-    void
-        GetBatchesWithOffset(IntVector2& offset, PODVector<UIBatch>& batches, PODVector<float>& vertexData, IntRect currentScissor);
+    void GetBatchesWithOffset(IntVector2& offset, PODVector<UIBatch>& batches, PODVector<float>& vertexData, IntRect currentScissor);
 
 
     /// Return color attribute. Uses just the top-left color.
     /// Return color attribute. Uses just the top-left color.
     const Color& GetColorAttr() const { return color_[0]; }
     const Color& GetColorAttr() const { return color_[0]; }
@@ -558,7 +557,25 @@ public:
 
 
     /// Return effective minimum size, also considering layout. Used internally.
     /// Return effective minimum size, also considering layout. Used internally.
     IntVector2 GetEffectiveMinSize() const;
     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 IsTagged(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:
 protected:
     /// Handle attribute animation added.
     /// Handle attribute animation added.
     virtual void OnAttributeAnimationAdded();
     virtual void OnAttributeAnimationAdded();
@@ -653,6 +670,8 @@ protected:
 private:
 private:
     /// Return child elements recursively.
     /// Return child elements recursively.
     void GetChildrenRecursive(PODVector<UIElement*>& dest) const;
     void GetChildrenRecursive(PODVector<UIElement*>& dest) const;
+    /// Return child elements with a specific tag recursively.
+    void GetChildrenWithTagRecursive(PODVector<UIElement*>& dest, const String& tag) const;
     /// Recursively apply style to a child element hierarchy when adding to an element.
     /// Recursively apply style to a child element hierarchy when adding to an element.
     void ApplyStyleRecursive(UIElement* element);
     void ApplyStyleRecursive(UIElement* element);
     /// Calculate layout width for resizing the parent element.
     /// Calculate layout width for resizing the parent element.
@@ -708,6 +727,8 @@ private:
     bool elementEventSender_;
     bool elementEventSender_;
     /// XPath query for selecting UI-style.
     /// XPath query for selecting UI-style.
     static XPathQuery styleXPathQuery_;
     static XPathQuery styleXPathQuery_;
+    /// Tag list.
+    StringVector tags_;
 };
 };
 
 
 template <class T> T* UIElement::CreateChild(const String& name, unsigned index)
 template <class T> T* UIElement::CreateChild(const String& name, unsigned index)

+ 14 - 1
bin/Data/LuaScripts/37_UIDrag.lua

@@ -30,7 +30,9 @@ end
 function CreateInstructions()
 function CreateInstructions()
     -- Construct new Text object, set string to display and font to use
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
     local instructionText = ui.root:CreateChild("Text")
-    instructionText:SetText("Drag on the buttons to move them around.\nTouch input allows also multi-drag.")
+    instructionText:SetText("Drag on the buttons to move them around.\n"..
+                            "Touch input allows also multi-drag.\n"..
+                            "Press SPACE to show/hide tagged UI elements.")
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     instructionText.textAlignment = HA_CENTER
     instructionText.textAlignment = HA_CENTER
 
 
@@ -52,6 +54,10 @@ function CreateGUI()
         b:SetMinSize(IntVector2(300, 100))
         b:SetMinSize(IntVector2(300, 100))
         b:SetPosition(IntVector2(50*i, 50*i))
         b:SetPosition(IntVector2(50*i, 50*i))
 
 
+        if i % 2 == 0 then
+             b:AddTag("SomeTag")
+        end
+
         SubscribeToEvent(b, "DragMove", "HandleDragMove")
         SubscribeToEvent(b, "DragMove", "HandleDragMove")
         SubscribeToEvent(b, "DragBegin", "HandleDragBegin")
         SubscribeToEvent(b, "DragBegin", "HandleDragBegin")
         SubscribeToEvent(b, "DragCancel", "HandleDragCancel")
         SubscribeToEvent(b, "DragCancel", "HandleDragCancel")
@@ -155,6 +161,13 @@ function HandleUpdate(eventType, eventData)
         t:SetVisible(false)
         t:SetVisible(false)
         i = i + 1
         i = i + 1
     end
     end
+    
+    if input:GetKeyPress(KEY_SPACE) then
+        elements = ui.root:GetChildrenWithTag("SomeTag")
+        for i, element in ipairs(elements) do
+            element.visible = not element.visible
+        end
+    end
 end
 end
 
 
 -- Create XML patch instructions for screen joystick layout specific to this sample app
 -- Create XML patch instructions for screen joystick layout specific to this sample app

+ 13 - 1
bin/Data/Scripts/37_UIDrag.as

@@ -42,6 +42,9 @@ void CreateGUI()
         b.size = IntVector2(300, 100);
         b.size = IntVector2(300, 100);
         b.position = IntVector2(50*i, 50*i);
         b.position = IntVector2(50*i, 50*i);
 
 
+        if (i % 2 == 0)
+            b.AddTag("SomeTag");
+
         SubscribeToEvent(b, "DragMove", "HandleDragMove");
         SubscribeToEvent(b, "DragMove", "HandleDragMove");
         SubscribeToEvent(b, "DragBegin", "HandleDragBegin");
         SubscribeToEvent(b, "DragBegin", "HandleDragBegin");
         SubscribeToEvent(b, "DragCancel", "HandleDragCancel");
         SubscribeToEvent(b, "DragCancel", "HandleDragCancel");
@@ -88,7 +91,9 @@ void CreateInstructions()
 {
 {
     // Construct new Text object, set string to display and font to use
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     Text@ instructionText = ui.root.CreateChild("Text");
-    instructionText.text = "Drag on the buttons to move them around.\nTouch input allows also multi-drag.";
+    instructionText.text = "Drag on the buttons to move them around.\n" + 
+                           "Touch input allows also multi-drag.\n" + 
+                           "Press SPACE to show/hide tagged UI elements.";
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
     instructionText.textAlignment = HA_CENTER;
     instructionText.textAlignment = HA_CENTER;
 
 
@@ -171,6 +176,13 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
         Text@ t = root.GetChild("Touch " + String(i));
         Text@ t = root.GetChild("Touch " + String(i));
         t.visible = false;
         t.visible = false;
     }
     }
+    
+    if (input.keyPress[KEY_SPACE])
+    {
+        Array<UIElement@>@ elements = root.GetChildrenWithTag("SomeTag");
+        for (uint i = 0; i < elements.length; ++i)
+            elements[i].visible = !elements[i].visible;
+    }
 }
 }
 
 
 // Create XML patch instructions for screen joystick layout specific to this sample app
 // Create XML patch instructions for screen joystick layout specific to this sample app

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

@@ -20,6 +20,7 @@ Array<XMLFile@> xmlResources;
 const uint ATTRIBUTE_RES = 0;
 const uint ATTRIBUTE_RES = 0;
 const uint VARIABLE_RES = 1;
 const uint VARIABLE_RES = 1;
 const uint STYLE_RES = 2;
 const uint STYLE_RES = 2;
+const uint TAGS_RES = 3;
 
 
 uint nodeContainerIndex = M_MAX_UNSIGNED;
 uint nodeContainerIndex = M_MAX_UNSIGNED;
 uint componentContainerStartIndex = 0;
 uint componentContainerStartIndex = 0;
@@ -37,7 +38,8 @@ bool inspectorLocked = false;
 
 
 void InitXMLResources()
 void InitXMLResources()
 {
 {
-    String[] resources = { "UI/EditorInspector_Attribute.xml", "UI/EditorInspector_Variable.xml", "UI/EditorInspector_Style.xml" };
+    String[] resources = { "UI/EditorInspector_Attribute.xml", "UI/EditorInspector_Variable.xml",
+                           "UI/EditorInspector_Style.xml", "UI/EditorInspector_Tags.xml" };
     for (uint i = 0; i < resources.length; ++i)
     for (uint i = 0; i < resources.length; ++i)
         xmlResources.Push(cache.GetResource("XMLFile", resources[i]));
         xmlResources.Push(cache.GetResource("XMLFile", resources[i]));
 }
 }
@@ -98,6 +100,8 @@ UIElement@ GetUIElementContainer()
 
 
     elementContainerIndex = parentContainer.numChildren;
     elementContainerIndex = parentContainer.numChildren;
     parentContainer.LoadChildXML(xmlResources[ATTRIBUTE_RES], uiStyle);
     parentContainer.LoadChildXML(xmlResources[ATTRIBUTE_RES], uiStyle);
+    parentContainer.LoadChildXML(xmlResources[TAGS_RES], uiStyle);
+    parentContainer.GetChild("TagsLabel", true).SetFixedWidth(LABEL_WIDTH);
     UIElement@ container = GetContainer(elementContainerIndex);
     UIElement@ container = GetContainer(elementContainerIndex);
     container.LoadChildXML(xmlResources[VARIABLE_RES], uiStyle);
     container.LoadChildXML(xmlResources[VARIABLE_RES], uiStyle);
     container.LoadChildXML(xmlResources[STYLE_RES], uiStyle);
     container.LoadChildXML(xmlResources[STYLE_RES], uiStyle);
@@ -109,6 +113,8 @@ UIElement@ GetUIElementContainer()
     SubscribeToEvent(container.GetChild("NewVarDropDown", true), "ItemSelected", "CreateUIElementVariable");
     SubscribeToEvent(container.GetChild("NewVarDropDown", true), "ItemSelected", "CreateUIElementVariable");
     SubscribeToEvent(container.GetChild("DeleteVarButton", true), "Released", "DeleteUIElementVariable");
     SubscribeToEvent(container.GetChild("DeleteVarButton", true), "Released", "DeleteUIElementVariable");
     SubscribeToEvent(styleList, "ItemSelected", "HandleStyleItemSelected");
     SubscribeToEvent(styleList, "ItemSelected", "HandleStyleItemSelected");
+    LineEdit@ tagEdit = parentContainer.GetChild("TagsEdit", true);
+    SubscribeToEvent(tagEdit, "TextChanged", "HandleTagsEdit");
     return container;
     return container;
 }
 }
 
 
@@ -327,6 +333,8 @@ void UpdateAttributeInspector(bool fullUpdate = true)
             elementType = editUIElement.typeName;
             elementType = editUIElement.typeName;
             titleText.text = elementType + " [ID " + GetUIElementID(editUIElement).ToString() + "]";
             titleText.text = elementType + " [ID " + GetUIElementID(editUIElement).ToString() + "]";
             SetStyleListSelection(styleList, editUIElement.style);
             SetStyleListSelection(styleList, editUIElement.style);
+            LineEdit@ tagEdit = parentContainer.GetChild("TagsEdit", true);
+            tagEdit.text = Join(editUIElement.tags, ";");
         }
         }
         else
         else
         {
         {
@@ -660,6 +668,15 @@ Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit)
     return ret;
     return ret;
 }
 }
 
 
+void HandleTagsEdit(StringHash eventType, VariantMap& eventData)
+{
+    LineEdit@ lineEdit = eventData["Element"].GetPtr();
+    Array<String> tags = lineEdit.text.Split(';');
+    editUIElement.ClearTags();
+    for (uint i = 0; i < tags.length; i++)
+        editUIElement.AddTag(tags[i]);
+}
+
 /// Handle reset to default event, sent when reset icon in the icon-panel is clicked.
 /// Handle reset to default event, sent when reset icon in the icon-panel is clicked.
 void HandleResetToDefault(StringHash eventType, VariantMap& eventData)
 void HandleResetToDefault(StringHash eventType, VariantMap& eventData)
 {
 {

+ 14 - 0
bin/Data/UI/EditorInspector_Tags.xml

@@ -0,0 +1,14 @@
+<element>
+    <attribute name="Name" value="TagContainer" />
+    <attribute name="Max Size" value="2147483647 17" />
+    <attribute name="Layout Mode" value="Horizontal" />
+    <attribute name="Layout Spacing" value="4" />
+    <element type="Text" style="EditorAttributeText">
+        <attribute name="Name" value="TagsLabel" />
+        <attribute name="Text" value="Tags" />
+        <attribute name="Auto Localizable" value="true" />
+    </element>
+    <element type="LineEdit">
+        <attribute name="Name" value="TagsEdit" />
+    </element>
+</element>