Explorar el Código

Serialized UI-element's user variable names in its own UI-element file instead of in scene file. Automatically determine the right UI-element level to perform the serialization when it is being requested. Fixed a bug in Scene class to unregister all the scene variable names when the scene is being cleared.

Wei Tjong Yao hace 12 años
padre
commit
e57bc3987b

+ 8 - 2
Bin/Data/Scripts/Editor/AttributeEditor.as

@@ -5,6 +5,7 @@
 // - bool PreEditAttribute(Array<Serializable@>@ serializables, uint index);
 // - bool PreEditAttribute(Array<Serializable@>@ serializables, uint index);
 // - void PostEditAttribute(Array<Serializable@>@ serializables, uint index, const Array<Variant>& oldValues);
 // - void PostEditAttribute(Array<Serializable@>@ serializables, uint index, const Array<Variant>& oldValues);
 // - Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit);
 // - Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit);
+// - String GetVariableName(ShortStringHash hash);
 
 
 const uint MIN_NODE_ATTRIBUTES = 4;
 const uint MIN_NODE_ATTRIBUTES = 4;
 const uint MAX_NODE_ATTRIBUTES = 8;
 const uint MAX_NODE_ATTRIBUTES = 8;
@@ -13,6 +14,7 @@ const int ATTR_HEIGHT = 19;
 const StringHash TEXT_CHANGED_EVENT_TYPE("TextChanged");
 const StringHash TEXT_CHANGED_EVENT_TYPE("TextChanged");
 
 
 bool inLoadAttributeEditor = false;
 bool inLoadAttributeEditor = false;
+bool inEditAttribute = false;
 bool showNonEditableAttribute = false;
 bool showNonEditableAttribute = false;
 
 
 Color normalTextColor(1.0f, 1.0f, 1.0f);
 Color normalTextColor(1.0f, 1.0f, 1.0f);
@@ -321,7 +323,7 @@ UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializa
         Array<ShortStringHash>@ keys = map.keys;
         Array<ShortStringHash>@ keys = map.keys;
         for (uint i = 0; i < keys.length; ++i)
         for (uint i = 0; i < keys.length; ++i)
         {
         {
-            String varName = scene.GetVarName(keys[i]);
+            String varName = GetVariableName(keys[i]);
             Variant value = map[keys[i]];
             Variant value = map[keys[i]];
 
 
             // The individual variant in the map is not an attribute of the serializable, the structure is reused for convenience
             // The individual variant in the map is not an attribute of the serializable, the structure is reused for convenience
@@ -523,7 +525,7 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
             if (parent is null)
             if (parent is null)
                 break;
                 break;
 
 
-            String varName = scene.GetVarName(keys[subIndex]);
+            String varName = GetVariableName(keys[subIndex]);
             if (varName.empty)
             if (varName.empty)
                 continue;
                 continue;
 
 
@@ -771,6 +773,8 @@ void EditAttribute(StringHash eventType, VariantMap& eventData)
     if (!PreEditAttribute(serializables, index))
     if (!PreEditAttribute(serializables, index))
         return;
         return;
 
 
+    inEditAttribute = true;
+
     // Store old values so that PostEditAttribute can create undo actions
     // Store old values so that PostEditAttribute can create undo actions
     Array<Variant> oldValues;
     Array<Variant> oldValues;
     for (uint i = 0; i < serializables.length; ++i)
     for (uint i = 0; i < serializables.length; ++i)
@@ -783,6 +787,8 @@ void EditAttribute(StringHash eventType, VariantMap& eventData)
     // Do the editor post logic after attribute has been modified.
     // Do the editor post logic after attribute has been modified.
     PostEditAttribute(serializables, index, oldValues);
     PostEditAttribute(serializables, index, oldValues);
 
 
+    inEditAttribute = false;
+
     // If not an intermediate edit, reload the editor fields with validated values
     // If not an intermediate edit, reload the editor fields with validated values
     // (attributes may have interactions; therefore we load everything, not just the value being edited)
     // (attributes may have interactions; therefore we load everything, not just the value being edited)
     if (!intermediateEdit)
     if (!intermediateEdit)

+ 55 - 65
Bin/Data/Scripts/Editor/EditorNodeWindow.as

@@ -298,14 +298,6 @@ void UpdateAttributeInspectorIcons()
 
 
 bool PreEditAttribute(Array<Serializable@>@ serializables, uint index)
 bool PreEditAttribute(Array<Serializable@>@ serializables, uint index)
 {
 {
-    // If resizing UI element by attribute editor, prevent hierarchy window element-resized event handler to update the attribute inspector
-    if (GetType(serializables[0]) == ITEM_UI_ELEMENT)
-    {
-        String name(serializables[0].attributeInfos[index].name);
-        if (name == "Position" || name == "Size" || name == "Text")
-            suppressUIElementChanges = true;
-    }
-
     return true;
     return true;
 }
 }
 
 
@@ -321,19 +313,12 @@ void PostEditAttribute(Array<Serializable@>@ serializables, uint index, const Ar
     }
     }
     SaveEditActionGroup(group);
     SaveEditActionGroup(group);
 
 
+    // If a UI-element changing its 'Is Modal' attribute, clear the hierarchy list selection
     bool testModalElement = false;
     bool testModalElement = false;
-    if (GetType(serializables[0]) == ITEM_UI_ELEMENT)
+    if (GetType(serializables[0]) == ITEM_UI_ELEMENT && serializables[0].attributeInfos[index].name == "Is Modal")
     {
     {
-        String name(serializables[0].attributeInfos[index].name);
-
-        // If a UI-element changing its 'Is Modal' attribute, clear the hierarchy list selection
-        if (name == "Is Modal")
-        {
-            hierarchyList.ClearSelection();
-            testModalElement = true;
-        }
-        else if (name == "Position" || name == "Size" || name == "Text")
-            suppressUIElementChanges = false;
+        hierarchyList.ClearSelection();
+        testModalElement = true;
     }
     }
 
 
     for (uint i = 0; i < serializables.length; ++i)
     for (uint i = 0; i < serializables.length; ++i)
@@ -438,18 +423,21 @@ void CreateNodeVariable(StringHash eventType, VariantMap& eventData)
     if (editNodes.length == 0)
     if (editNodes.length == 0)
         return;
         return;
 
 
-    String newKey;
-    Variant newValue;
-    CreateNewVariable(eventData, newKey, newValue);
-    if (newKey.empty)
+    String newName = ExtractVariableName(eventData);
+    if (newName.empty)
         return;
         return;
 
 
+    // Create scene variable
+    editorScene.RegisterVar(newName);
+
+    Variant newValue = ExtractVariantType(eventData);
+
     // If we overwrite an existing variable, must recreate the attribute-editor(s) for the correct type
     // If we overwrite an existing variable, must recreate the attribute-editor(s) for the correct type
     bool overwrite = false;
     bool overwrite = false;
     for (uint i = 0; i < editNodes.length; ++i)
     for (uint i = 0; i < editNodes.length; ++i)
     {
     {
-        overwrite = overwrite || editNodes[i].vars.Contains(newKey);
-        editNodes[i].vars[newKey] = newValue;
+        overwrite = overwrite || editNodes[i].vars.Contains(newName);
+        editNodes[i].vars[newName] = newValue;
     }
     }
     if (overwrite)
     if (overwrite)
         attributesFullDirty = true;
         attributesFullDirty = true;
@@ -462,16 +450,17 @@ void DeleteNodeVariable(StringHash eventType, VariantMap& eventData)
     if (editNodes.length == 0)
     if (editNodes.length == 0)
         return;
         return;
 
 
-    String delKey;
-    DeleteVariable(eventData, delKey);
-    if (delKey.empty)
+    String delName = ExtractVariableName(eventData);
+    if (delName.empty)
         return;
         return;
 
 
+    // Note: intentionally do not unregister the variable name here as the same variable name may still be used by other attribute list
+
     bool erased = false;
     bool erased = false;
     for (uint i = 0; i < editNodes.length; ++i)
     for (uint i = 0; i < editNodes.length; ++i)
     {
     {
         // \todo Should first check whether var in question is editable
         // \todo Should first check whether var in question is editable
-        erased = editNodes[i].vars.Erase(delKey) || erased;
+        erased = editNodes[i].vars.Erase(delName) || erased;
     }
     }
     if (erased)
     if (erased)
         attributesDirty = true;
         attributesDirty = true;
@@ -482,19 +471,22 @@ void CreateUIElementVariable(StringHash eventType, VariantMap& eventData)
     if (editUIElements.length == 0)
     if (editUIElements.length == 0)
         return;
         return;
 
 
-    String newKey;
-    Variant newValue;
-    CreateNewVariable(eventData, newKey, newValue);
-    if (newKey.empty)
+    String newName = ExtractVariableName(eventData);
+    if (newName.empty)
         return;
         return;
 
 
+    // Create UIElement variable
+    uiElementVarNames[newName] = newName;
+
+    Variant newValue = ExtractVariantType(eventData);
+
     // If we overwrite an existing variable, must recreate the attribute-editor(s) for the correct type
     // If we overwrite an existing variable, must recreate the attribute-editor(s) for the correct type
     bool overwrite = false;
     bool overwrite = false;
     for (uint i = 0; i < editUIElements.length; ++i)
     for (uint i = 0; i < editUIElements.length; ++i)
     {
     {
         UIElement@ element = cast<UIElement>(editUIElements[i]);
         UIElement@ element = cast<UIElement>(editUIElements[i]);
-        overwrite = overwrite || element.vars.Contains(newKey);
-        element.vars[newKey] = newValue;
+        overwrite = overwrite || element.vars.Contains(newName);
+        element.vars[newName] = newValue;
     }
     }
     if (overwrite)
     if (overwrite)
         attributesFullDirty = true;
         attributesFullDirty = true;
@@ -507,59 +499,57 @@ void DeleteUIElementVariable(StringHash eventType, VariantMap& eventData)
     if (editUIElements.length == 0)
     if (editUIElements.length == 0)
         return;
         return;
 
 
-    String delKey;
-    DeleteVariable(eventData, delKey);
-    if (delKey.empty)
+    String delName = ExtractVariableName(eventData);
+    if (delName.empty)
         return;
         return;
 
 
+    // Note: intentionally do not unregister the variable name here as the same variable name may still be used by other attribute list
+
     bool erased = false;
     bool erased = false;
     for (uint i = 0; i < editUIElements.length; ++i)
     for (uint i = 0; i < editUIElements.length; ++i)
     {
     {
         // \todo Should first check whether var in question is editable
         // \todo Should first check whether var in question is editable
-        erased = cast<UIElement>(editUIElements[i]).vars.Erase(delKey) || erased;
+        erased = cast<UIElement>(editUIElements[i]).vars.Erase(delName) || erased;
     }
     }
     if (erased)
     if (erased)
         attributesDirty = true;
         attributesDirty = true;
 }
 }
 
 
-void CreateNewVariable(VariantMap& eventData, String& newKey, Variant& newValue)
+String ExtractVariableName(VariantMap& eventData)
 {
 {
-    DropDownList@ dropDown = eventData["Element"].GetUIElement();
-    LineEdit@ nameEdit = dropDown.parent.GetChild("VarNameEdit");
-    newKey = nameEdit.text.Trimmed().Replaced(";", "");
-    if (newKey.empty)
-        return;
-
-    editorScene.RegisterVar(newKey);
+    UIElement@ element = eventData["Element"].GetUIElement();
+    LineEdit@ nameEdit = element.parent.GetChild("VarNameEdit");
+    return nameEdit.text.Trimmed();
+}
 
 
+Variant ExtractVariantType(VariantMap& eventData)
+{
+    DropDownList@ dropDown = eventData["Element"].GetUIElement();
     switch (dropDown.selection)
     switch (dropDown.selection)
     {
     {
     case 0:
     case 0:
-        newValue = int(0);
-        break;
+        return int(0);
     case 1:
     case 1:
-        newValue = false;
-        break;
+        return false;
     case 2:
     case 2:
-        newValue = float(0.0);
-        break;
+        return float(0.0);
     case 3:
     case 3:
-        newValue = String();
-        break;
+        return Variant(String());
     case 4:
     case 4:
-        newValue = Vector3();
-        break;
+        return Variant(Vector3());
     case 5:
     case 5:
-        newValue = Color();
-        break;
+        return Variant(Color());
     }
     }
+
+    return Variant();   // This should not happen
 }
 }
 
 
-void DeleteVariable(VariantMap& eventData, String& delKey)
+String GetVariableName(ShortStringHash hash)
 {
 {
-    Button@ button = eventData["Element"].GetUIElement();
-    LineEdit@ nameEdit = button.parent.GetChild("VarNameEdit", true);
-    delKey = nameEdit.text.Trimmed().Replaced(";", "");
-
-    // Do not actually unregister the variable name as the same variable name may still be used by other attribute list
+    // First try to get it from scene
+    String name = editorScene.GetVarName(hash);
+    // Then from the UIElement variable names
+    if (name.empty && uiElementVarNames.Contains(hash))
+        name = uiElementVarNames[hash].ToString();
+    return name;    // Since this is a reverse mapping, it does not really matter from which side the name is retrieved back
 }
 }

+ 3 - 2
Bin/Data/Scripts/Editor/EditorSceneWindow.as

@@ -927,7 +927,8 @@ void HandleElementVisibilityChanged(StringHash eventType, VariantMap& eventData)
 
 
 void HandleElementAttributeChanged(StringHash eventType, VariantMap& eventData)
 void HandleElementAttributeChanged(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (suppressUIElementChanges)
+    // Do not refresh the attribute inspector while the attribute is being edited via the attribute-editors
+    if (suppressUIElementChanges || inEditAttribute)
         return;
         return;
 
 
     UIElement@ element = eventData["Element"].GetUIElement();
     UIElement@ element = eventData["Element"].GetUIElement();
@@ -935,7 +936,7 @@ void HandleElementAttributeChanged(StringHash eventType, VariantMap& eventData)
     {
     {
         if (editUIElements[i] is element)
         if (editUIElements[i] is element)
             attributesDirty = true;
             attributesDirty = true;
-    }    
+    }
 }
 }
 
 
 // Hierarchy window edit functions
 // Hierarchy window edit functions

+ 9 - 3
Bin/Data/Scripts/Editor/EditorUI.as

@@ -333,10 +333,16 @@ bool PickFile()
     }
     }
     else if (action == "Save UI-element as..." || action == "Save UI-element")
     else if (action == "Save UI-element as..." || action == "Save UI-element")
     {
     {
-        if (editUIElement !is null && editUIElement.vars.Contains(FILENAME_VAR))
+        if (editUIElement !is null)
         {
         {
+            UIElement@ element = editUIElement;
+            while (element !is null && !element.vars.Contains(FILENAME_VAR))
+                element = element.parent;
+            if (element is null)
+                return false;
+
             CreateFileSelector("Save UI-element as", "Save", "Cancel", uiElementPath, uiElementFilters, uiElementFilter);
             CreateFileSelector("Save UI-element as", "Save", "Cancel", uiElementPath, uiElementFilters, uiElementFilter);
-            uiFileSelector.fileName = GetFileNameAndExtension(editUIElement.vars[FILENAME_VAR].GetString());
+            uiFileSelector.fileName = GetFileNameAndExtension(element.GetVar(FILENAME_VAR).GetString());
             SubscribeToEvent(uiFileSelector, "FileSelected", "HandleSaveUIElementFile");
             SubscribeToEvent(uiFileSelector, "FileSelected", "HandleSaveUIElementFile");
         }
         }
     }
     }
@@ -350,7 +356,7 @@ bool PickFile()
     }
     }
     else if (action == "Save child element as...")
     else if (action == "Save child element as...")
     {
     {
-        if (editUIElement !is null && !editUIElement.vars.Contains(FILENAME_VAR))
+        if (editUIElement !is null)
         {
         {
             CreateFileSelector("Save child element", "Save", "Cancel", uiElementPath, uiElementFilters, uiElementFilter);
             CreateFileSelector("Save child element", "Save", "Cancel", uiElementPath, uiElementFilters, uiElementFilter);
             uiFileSelector.fileName = GetFileNameAndExtension(childElementFileName);
             uiFileSelector.fileName = GetFileNameAndExtension(childElementFileName);

+ 59 - 13
Bin/Data/Scripts/Editor/EditorUIElement.as

@@ -13,6 +13,9 @@ Array<XMLFile@> uiElementCopyBuffer;
 
 
 bool suppressUIElementChanges = false;
 bool suppressUIElementChanges = false;
 
 
+// Registered UIElement user variable reverse mappings
+VariantMap uiElementVarNames;
+
 const ShortStringHash FILENAME_VAR("__FileName");
 const ShortStringHash FILENAME_VAR("__FileName");
 const ShortStringHash MODIFIED_VAR("__Modified");
 const ShortStringHash MODIFIED_VAR("__Modified");
 
 
@@ -121,6 +124,8 @@ void OpenUIElement(const String&in fileName)
 
 
         // Do not allow UI subsystem to reorder children while editing the element in the editor
         // Do not allow UI subsystem to reorder children while editing the element in the editor
         ResetSortChildren(element);
         ResetSortChildren(element);
+        // Register variable names from the 'enriched' variant XMLElement, if any
+        RegisterUIElementVar(xmlFile.root);
 
 
         editorUIElement.AddChild(element);
         editorUIElement.AddChild(element);
 
 
@@ -189,9 +194,15 @@ bool SaveUIElement(const String&in fileName)
     if (!file.open)
     if (!file.open)
         return false;
         return false;
 
 
+    UIElement@ element = editUIElement;
+    while (element !is null && !element.vars.Contains(FILENAME_VAR))
+        element = element.parent;
+    if (element is null)
+        return false;
+
     XMLFile@ elementData = XMLFile();
     XMLFile@ elementData = XMLFile();
     XMLElement rootElem = elementData.CreateRoot("element");
     XMLElement rootElem = elementData.CreateRoot("element");
-    bool success = editUIElement.SaveXML(rootElem);
+    bool success = element.SaveXML(rootElem);
     if (success)
     if (success)
     {
     {
         FilterInternalVars(rootElem);
         FilterInternalVars(rootElem);
@@ -199,8 +210,8 @@ bool SaveUIElement(const String&in fileName)
     }
     }
     if (success)
     if (success)
     {
     {
-        editUIElement.vars[FILENAME_VAR] = fileName;
-        editUIElement.vars[MODIFIED_VAR] = false;
+        element.vars[FILENAME_VAR] = fileName;
+        element.vars[MODIFIED_VAR] = false;
 
 
         sceneModified = false;
         sceneModified = false;
         UpdateWindowTitle();
         UpdateWindowTitle();
@@ -211,17 +222,19 @@ bool SaveUIElement(const String&in fileName)
 
 
 bool SaveUIElementWithExistingName()
 bool SaveUIElementWithExistingName()
 {
 {
-    if (editUIElement is null)
-        return false;
+    ui.cursor.shape = CS_BUSY;
 
 
-    Variant fileNameVar = editUIElement.GetVar(FILENAME_VAR);
-    if (fileNameVar.empty)  // Only top level UI-element has this variable
+    UIElement@ element = editUIElement;
+    while (element !is null && !element.vars.Contains(FILENAME_VAR))
+        element = element.parent;
+    if (element is null)
         return false;
         return false;
 
 
-    if (fileNameVar.GetString().empty)
+    String fileName = element.GetVar(FILENAME_VAR).GetString();
+    if (fileName.empty)
         return PickFile();  // No name yet, so pick one
         return PickFile();  // No name yet, so pick one
     else
     else
-        return SaveUIElement(fileNameVar.GetString());
+        return SaveUIElement(fileName);
 }
 }
 
 
 void LoadChildUIElement(const String&in fileName)
 void LoadChildUIElement(const String&in fileName)
@@ -250,6 +263,7 @@ void LoadChildUIElement(const String&in fileName)
     {
     {
         UIElement@ element = editUIElement.children[editUIElement.numChildren - 1];
         UIElement@ element = editUIElement.children[editUIElement.numChildren - 1];
         ResetSortChildren(element);
         ResetSortChildren(element);
+        RegisterUIElementVar(xmlFile.root);
         UpdateHierarchyItem(element);
         UpdateHierarchyItem(element);
 
 
         // Create an undo action for the load
         // Create an undo action for the load
@@ -312,7 +326,8 @@ void SetUIElementDefaultStyle(const String&in fileName)
 
 
 void FilterInternalVars(XMLElement source)
 void FilterInternalVars(XMLElement source)
 {
 {
-    // Remove unregistered var for each 'attribute' tag
+    // If variable name is empty (or unregistered) then it is an internal variable and should be removed
+    // If it is registered then 'enrich' the XMLElement to store the variable name in plaintext
     XMLElement childElem = source.GetChild("attribute");
     XMLElement childElem = source.GetChild("attribute");
     while (childElem.notNull)
     while (childElem.notNull)
     {
     {
@@ -321,9 +336,12 @@ void FilterInternalVars(XMLElement source)
             XMLElement variantElem = childElem.GetChild("variant");
             XMLElement variantElem = childElem.GetChild("variant");
             while (variantElem.notNull)
             while (variantElem.notNull)
             {
             {
-                // If variable name is empty (unregistered) then it is an internal variable
-                bool removed = scene.GetVarName(variantElem.GetUInt("hash")).empty && childElem.RemoveChild(variantElem);
-                variantElem = removed ? childElem.GetChild("variant") : childElem.GetNext("variant");
+                String name = GetVariableName(variantElem.GetUInt("hash"));
+                if (name.empty)
+                    childElem.RemoveChild(variantElem);
+                else
+                    variantElem.SetAttribute("name", name);
+                variantElem = variantElem.GetNext("variant");
             }
             }
         }
         }
         childElem = childElem.GetNext("attribute");
         childElem = childElem.GetNext("attribute");
@@ -338,6 +356,34 @@ void FilterInternalVars(XMLElement source)
     }
     }
 }
 }
 
 
+void RegisterUIElementVar(XMLElement source)
+{
+    XMLElement childElem = source.GetChild("attribute");
+    while (childElem.notNull)
+    {
+        if (childElem.GetAttribute("name") == "Variables")
+        {
+            XMLElement variantElem = childElem.GetChild("variant");
+            while (variantElem.notNull)
+            {
+                String name = variantElem.GetAttribute("name");
+                if (!name.empty)
+                    uiElementVarNames[name] = name;
+                variantElem = variantElem.GetNext("variant");
+            }
+        }
+        childElem = childElem.GetNext("attribute");
+    }
+
+    // Perform the action recursively for each 'element' tag
+    childElem = source.GetChild("element");
+    while (childElem.notNull)
+    {
+        RegisterUIElementVar(childElem);
+        childElem = childElem.GetNext("element");
+    }
+}
+
 bool UIElementCut()
 bool UIElementCut()
 {
 {
     if (UIElementCopy())
     if (UIElementCopy())

+ 1 - 0
Engine/Scene/Scene.cpp

@@ -378,6 +378,7 @@ void Scene::Clear()
     StopAsyncLoading();
     StopAsyncLoading();
     RemoveAllChildren();
     RemoveAllChildren();
     RemoveAllComponents();
     RemoveAllComponents();
+    UnregisterAllVars();
     SetName(String::EMPTY);
     SetName(String::EMPTY);
     fileName_.Clear();
     fileName_.Clear();
     checksum_ = 0;
     checksum_ = 0;