Przeglądaj źródła

Applied attribute editor refactoring from Magic.Lixin.

Lasse Öörni 13 lat temu
rodzic
commit
14ccd3b1f1

+ 1088 - 0
Bin/Data/Scripts/Editor/AttributeEditor.as

@@ -0,0 +1,1088 @@
+// Attribute editor
+//
+// Functions that you must implement:
+// - void SetAttributeEditorID(UIElement@ attrEdit, Array<Serializable@>@ serializables);
+// - void PostEditAttribute(Array<Serializable@>@ serializables, uint index);
+// - Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit);
+
+const uint MIN_NODE_ATTRIBUTES = 4;
+const uint MAX_NODE_ATTRIBUTES = 8;
+const int ATTRNAME_WIDTH = 150;
+const int ATTR_HEIGHT = 19;
+bool inLoadAttributeEditor = false;
+
+// If you want to show AM_NOEDIT in attribute editor set this flag = true. This will show attribute as String.
+bool showNonEditAttribute = false;
+
+const ShortStringHash textType("Text");
+const ShortStringHash containerType("UIElement");
+const StringHash textChangedEventType("TextChanged");
+
+Color normalTextColor(1.0f, 1.0f, 1.0f);
+Color modifiedTextColor(1.0f, 0.8f, 0.5f);
+
+String sceneResourcePath;
+
+UIElement@ CreateAttributeEditorParentWithSeparatedLabel(ListView@ list, String name, uint index, uint subIndex, bool suppressedSeparatedLabel = false)
+{
+    UIElement@ editorParent = UIElement("Edit" + String(index) + "_" + String(subIndex));
+    editorParent.vars["Index"] = index;
+    editorParent.vars["SubIndex"] = subIndex;
+    editorParent.SetLayout(LM_VERTICAL, 2);
+    list.AddItem(editorParent);
+
+    if (suppressedSeparatedLabel)
+    {
+        UIElement@ placeHolder = UIElement();
+        editorParent.AddChild(placeHolder);
+    }
+    else
+    {
+        Text@ attrNameText = Text();
+        attrNameText.SetStyle(uiStyle, "EditorAttributeText");
+        attrNameText.text = name;
+        editorParent.AddChild(attrNameText);
+    }
+
+    return editorParent;
+}
+
+UIElement@ CreateAttributeEditorParent(ListView@ list, String name, uint index, uint subIndex)
+{
+    UIElement@ editorParent = UIElement("Edit" + String(index) + "_" + String(subIndex));
+    editorParent.vars["Index"] = index;
+    editorParent.vars["SubIndex"] = subIndex;
+    editorParent.SetLayout(LM_HORIZONTAL);
+    editorParent.SetFixedHeight(ATTR_HEIGHT);
+    list.AddItem(editorParent);
+
+    Text@ attrNameText = Text();
+    attrNameText.SetStyle(uiStyle, "EditorAttributeText");
+    attrNameText.text = name;
+    attrNameText.SetFixedWidth(ATTRNAME_WIDTH);
+    editorParent.AddChild(attrNameText);
+
+    return editorParent;
+}
+
+LineEdit@ CreateAttributeLineEdit(UIElement@ parent, Array<Serializable@>@ serializables, uint index, uint subIndex)
+{
+    LineEdit@ attrEdit = LineEdit();
+    attrEdit.SetStyle(uiStyle, "EditorAttributeEdit");
+    attrEdit.SetFixedHeight(ATTR_HEIGHT - 2);
+    attrEdit.vars["Index"] = index;
+    attrEdit.vars["SubIndex"] = subIndex;
+    SetAttributeEditorID(attrEdit, serializables);
+    parent.AddChild(attrEdit);
+    return attrEdit;
+}
+
+void CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, uint index)
+{
+    AttributeInfo info = serializables[0].attributeInfos[index];
+    CreateAttributeEditor(list, serializables, info.name, info.type, info.enumNames, index, 0);
+}
+
+UIElement@ CreateStringAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name,
+                                       VariantType type, Array<String>@ enumNames, uint index, uint subIndex)
+{
+    UIElement@ parent = CreateAttributeEditorParent(list, name, index, subIndex);
+    LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
+    attrEdit.dragDropMode = DD_TARGET;
+    SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
+    SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
+    return parent;
+}
+
+UIElement@ CreateNonEditAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name, 
+                                        VariantType type, Array<String>@ enumNames, uint index, uint subIndex)
+{
+    UIElement@ parent = CreateAttributeEditorParent(list, name, index, subIndex);
+    Text@ text = Text();
+    text.style = uiStyle;
+    parent.AddChild(text);
+    text.vars["Index"] = index;
+    text.vars["SubIndex"] = subIndex;
+    SetAttributeEditorID(text, serializables);
+    return parent;
+}
+
+UIElement@ CreateBoolAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name, 
+                                     VariantType type, Array<String>@ enumNames, uint index, uint subIndex)
+{
+    UIElement@ parent = CreateAttributeEditorParent(list, name, index, subIndex);
+    CheckBox@ attrEdit = CheckBox();
+    attrEdit.style = uiStyle;
+    attrEdit.SetFixedSize(16, 16);
+    attrEdit.vars["Index"] = index;
+    attrEdit.vars["SubIndex"] = subIndex;
+    SetAttributeEditorID(attrEdit, serializables);
+    parent.AddChild(attrEdit);
+    SubscribeToEvent(attrEdit, "Toggled", "EditAttribute");
+    return parent;
+}
+
+UIElement@ CreateNumAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name, 
+                                     VariantType type, Array<String>@ enumNames, uint index, uint subIndex)
+{
+    UIElement@ parent = CreateAttributeEditorParent(list, name, index, subIndex);
+    uint numCoords = type - VAR_FLOAT + 1;
+    if (type == VAR_QUATERNION)
+        numCoords = 3;
+    if (type == VAR_COLOR)
+        numCoords = 4;
+    if(type == VAR_INTVECTOR2)
+        numCoords = 2;
+    if(type == VAR_INTRECT)
+        numCoords = 4;
+    
+    for (uint i = 0; i < numCoords; ++i)
+    {
+        LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
+        SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
+        SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
+    }     
+    return parent;    
+}
+
+UIElement@ CreateIntAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name, 
+                                     VariantType type, Array<String>@ enumNames, uint index, uint subIndex)
+{
+    UIElement@ parent = CreateAttributeEditorParent(list, name, index, subIndex);
+    // Check for enums
+    if (enumNames is null || enumNames.empty)
+    {
+        // No enums, create a numeric editor
+        LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
+        SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
+        SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
+    }
+    else
+    {
+        DropDownList@ attrEdit = DropDownList();
+        attrEdit.style = uiStyle;
+        attrEdit.SetFixedHeight(ATTR_HEIGHT - 2);
+        attrEdit.resizePopup = true;
+        attrEdit.vars["Index"] = index;
+        attrEdit.vars["SubIndex"] = subIndex;
+        attrEdit.SetLayout(LM_HORIZONTAL, 0, IntRect(4, 1, 4, 1));
+        SetAttributeEditorID(attrEdit, serializables);
+
+        for (uint i = 0; i < enumNames.length; ++i)
+        {
+            Text@ choice = Text();
+            choice.SetStyle(uiStyle, "EditorEnumAttributeText");
+            choice.text = enumNames[i];
+            attrEdit.AddItem(choice);
+        }
+        parent.AddChild(attrEdit);
+        SubscribeToEvent(attrEdit, "ItemSelected", "EditAttribute");
+    }
+
+    if(parent is null)
+    {
+        Print("ERROR");
+    }
+    return parent;
+}
+
+UIElement@ CreateResourceRefAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name, 
+                                            VariantType type, Array<String>@ enumNames, uint index, uint subIndex,
+                                            bool suppressedSeparatedLabel = false)
+{
+    UIElement@ parent;
+    ShortStringHash resourceType;
+    VariantType attrType = serializables[0].attributeInfos[index].type;
+    if (attrType == VAR_RESOURCEREF)
+        resourceType = serializables[0].attributes[index].GetResourceRef().type;
+    else if (attrType == VAR_RESOURCEREFLIST)
+        resourceType = serializables[0].attributes[index].GetResourceRefList().type;
+    else if (attrType == VAR_VARIANTVECTOR)
+        resourceType = serializables[0].attributes[index].GetVariantVector()[subIndex].GetResourceRef().type;
+
+     // Create the resource name on a separate non-interactive line to allow for more space
+    parent = CreateAttributeEditorParentWithSeparatedLabel(list, name, index, subIndex, suppressedSeparatedLabel);
+
+    UIElement@ container = UIElement();
+    container.SetLayout(LM_HORIZONTAL, 4, IntRect(name.StartsWith("   ") ? 20 : 10, 0, 4, 0));    // Left margin is indented more when the name is so
+    container.SetFixedHeight(ATTR_HEIGHT);
+    parent.AddChild(container);
+        
+    LineEdit@ attrEdit = CreateAttributeLineEdit(container, serializables, index, subIndex);
+    attrEdit.vars["Type"] = resourceType.value;
+    SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
+
+    Button@ pickButton = Button();
+    pickButton.style = uiStyle;
+    pickButton.SetFixedSize(36, ATTR_HEIGHT - 2);
+    pickButton.vars["Index"] = index;
+    pickButton.vars["SubIndex"] = subIndex;
+    SetAttributeEditorID(pickButton, serializables);
+
+    Text@ pickButtonText = Text();
+    pickButtonText.SetStyle(uiStyle, "EditorAttributeText");
+    pickButtonText.SetAlignment(HA_CENTER, VA_CENTER);
+    pickButtonText.text = "Pick";
+    pickButton.AddChild(pickButtonText);
+    container.AddChild(pickButton);
+    SubscribeToEvent(pickButton, "Released", "PickResource");
+
+    Button@ openButton = Button();
+    openButton.style = uiStyle;
+    openButton.SetFixedSize(36, ATTR_HEIGHT - 2);
+    openButton.vars["Index"] = index;
+    openButton.vars["SubIndex"] = subIndex;
+    SetAttributeEditorID(openButton, serializables);
+
+    Text@ openButtonText = Text();
+    openButtonText.SetStyle(uiStyle, "EditorAttributeText");
+    openButtonText.SetAlignment(HA_CENTER, VA_CENTER);
+    openButtonText.text = "Open";
+    openButton.AddChild(openButtonText);
+    container.AddChild(openButton);
+    SubscribeToEvent(openButton, "Released", "OpenResource");
+    return parent;
+}
+
+UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name, VariantType type, Array<String>@ enumNames, uint index, uint subIndex)
+{
+    UIElement@ parent;
+
+    AttributeInfo info = serializables[0].attributeInfos[index];
+    if((info.mode & AM_NOEDIT != 0 && showNonEditAttribute))
+    {
+        parent = CreateNonEditAttributeEditor(list, serializables, name, type, enumNames, index, subIndex);
+        return parent;
+    }
+
+    if (type == VAR_STRING)
+    {
+        parent = CreateStringAttributeEditor(list, serializables, name, type, enumNames, index, subIndex);
+    }
+    if (type == VAR_BOOL)
+    {
+        parent = CreateBoolAttributeEditor(list, serializables, name, type, enumNames, index, subIndex);
+    }
+    if ((type >= VAR_FLOAT && type <= VAR_VECTOR4) || type == VAR_QUATERNION || type == VAR_COLOR  
+        || type == VAR_INTVECTOR2 || type == VAR_INTRECT)
+    {
+        parent = CreateNumAttributeEditor(list, serializables, name, type, enumNames, index, subIndex);
+    }
+    if (type == VAR_INT)
+    {
+        parent = CreateIntAttributeEditor(list, serializables, name, type, enumNames, index, subIndex);
+    }
+    if (type == VAR_RESOURCEREF)
+    {
+        parent = CreateResourceRefAttributeEditor(list, serializables, name, type, enumNames, index, subIndex);
+    }
+    if (type == VAR_RESOURCEREFLIST)
+    {
+        uint numRefs = serializables[0].attributes[index].GetResourceRefList().length;
+        for (uint i = 0; i < numRefs; ++i)
+            CreateAttributeEditor(list, serializables, name, VAR_RESOURCEREF, null, index, i);
+    }
+    if (type == VAR_VARIANTVECTOR)
+    {
+        VectorStruct@ vectorStruct = GetVectorStruct(serializables, index);
+        if (vectorStruct is null)
+            return null;
+        uint nameIndex = 0;
+
+        Array<Variant>@ vector = serializables[0].attributes[index].GetVariantVector();
+        for (uint i = 0; i < vector.length; ++i)
+        {
+            CreateAttributeEditor(list, serializables, vectorStruct.variableNames[nameIndex], vector[i].type, null, index, i);
+            ++nameIndex;
+            if (nameIndex >= vectorStruct.variableNames.length)
+                nameIndex = vectorStruct.restartIndex;
+        }
+    }
+    if (type == VAR_VARIANTMAP)
+    {
+        VariantMap map = serializables[0].attributes[index].GetVariantMap();
+        Array<ShortStringHash>@ keys = map.keys;
+        for (uint i = 0; i < keys.length; ++i)
+        {
+            Variant value = map[keys[i]];
+            parent = CreateAttributeEditor(list, serializables, scene.GetVarName(keys[i]) + " (Var)", value.type, null, index, i);
+           if(parent is null)
+            {
+                Print("error parent is null!!!! + " + scene.GetVarName(keys[i]));
+            }
+            else
+            {
+                // Add the variant key to the parent
+                parent.vars["Key"] = keys[i].value;
+            }
+        }
+    }
+
+    return parent;
+}
+
+uint GetAttributeEditorCount(Array<Serializable@>@ serializables)
+{
+    uint count = 0;
+
+    if (!serializables.empty)
+    {
+        /// \todo When multi-editing, this only counts the editor count of the first serializable
+        for (uint i = 0; i < serializables[0].numAttributes; ++i)
+        {
+            AttributeInfo info = serializables[0].attributeInfos[i];
+            if(info.mode & AM_NOEDIT != 0)
+            {
+                if(showNonEditAttribute)
+                {
+                    ++count;
+                }
+                else
+                {
+                    continue;
+                }
+            }
+            else
+            {
+                if (info.type == VAR_RESOURCEREFLIST)
+                    count += serializables[0].attributes[i].GetResourceRefList().length;
+                else if (info.type == VAR_VARIANTVECTOR && GetVectorStruct(serializables, i) !is null)
+                    count += serializables[0].attributes[i].GetVariantVector().length;
+                else if (info.type == VAR_VARIANTMAP)
+                    count += serializables[0].attributes[i].GetVariantMap().length;
+                else
+                    ++count;
+            }
+        }
+    }
+
+    return count;
+}
+
+UIElement@ GetAttributeEditorParent(UIElement@ parent, uint index, uint subIndex)
+{
+    return parent.GetChild("Edit" + String(index) + "_" + String(subIndex), true);
+}
+
+void LoadAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, uint index)
+{
+    UIElement@ parent = GetAttributeEditorParent(list, index, 0);
+    if (parent is null)
+        return;
+
+    inLoadAttributeEditor = true;
+
+    AttributeInfo info = serializables[0].attributeInfos[index];
+    bool sameValue = true;
+    Variant value = serializables[0].attributes[index];
+    for (uint i = 1; i < serializables.length; ++i)
+    {
+        if (serializables[i].attributes[index] != value)
+        {
+            sameValue = false;
+            break;
+        }
+    }
+
+    if (sameValue) {
+        if((info.mode & AM_NOEDIT != 0 && showNonEditAttribute))
+        {
+            Text@ text = parent.children[1];
+            text.text = value.GetString();
+        }
+        else {
+            LoadAttributeEditor(parent, value, value.type, info.enumNames, info.defaultValue);
+        }
+    }
+        
+
+    inLoadAttributeEditor = false;
+}
+
+void LoadAttributeEditor(UIElement@ parent, Variant value, VariantType type, Array<String>@ enumNames, Variant defaultValue)
+{
+    uint index = parent.vars["Index"].GetUInt();
+
+    // Assume the first child is always a text label element or a container that containing a text label element
+    UIElement@ label = parent.children[0];
+    if (label.type == containerType)
+        label = label.children[0];
+    if (label.type == textType)
+    {
+        bool modified;
+        if (defaultValue.type == VAR_NONE || defaultValue.type == VAR_RESOURCEREFLIST)
+            modified = !value.IsZero();
+        else
+            modified = value != defaultValue;
+        cast<Text>(label).color = (modified ? modifiedTextColor : normalTextColor);
+    }
+    
+    if (type == VAR_STRING)
+    {
+        LineEdit@ attrEdit = parent.children[1];
+        attrEdit.text = value.GetString();
+    }
+    if (type == VAR_BOOL)
+    {
+        CheckBox@ attrEdit = parent.children[1];
+        attrEdit.checked = value.GetBool();
+    }
+    if (type == VAR_FLOAT)
+    {
+        LineEdit@ attrEdit = parent.children[1];
+        attrEdit.text = String(value.GetFloat());
+    }
+    if (type == VAR_VECTOR2)
+    {
+        Vector2 vec = value.GetVector2();
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        attrEditX.text = String(vec.x);
+        attrEditY.text = String(vec.y);
+        attrEditX.cursorPosition = 0;
+        attrEditY.cursorPosition = 0;
+    }
+    if (type == VAR_INTVECTOR2)
+    {
+        IntVector2 vec = value.GetIntVector2();
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        attrEditX.text = String(vec.x);
+        attrEditY.text = String(vec.y);
+        attrEditX.cursorPosition = 0;
+        attrEditY.cursorPosition = 0;
+    }
+    if(type == VAR_INTRECT)
+    {
+        IntRect rect = value.GetIntRect();
+        LineEdit@ attrEditLeft = parent.children[1];
+        LineEdit@ attrEditTop = parent.children[2];
+        LineEdit@ attrEditRight = parent.children[3];
+        LineEdit@ attrEditBottom = parent.children[4];
+        attrEditLeft.text = String(rect.left);
+        attrEditTop.text = String(rect.top);
+        attrEditRight.text = String(rect.right);
+        attrEditBottom.text = String(rect.bottom);
+        attrEditLeft.cursorPosition = 0;
+        attrEditTop.cursorPosition = 0;
+        attrEditRight.cursorPosition = 0;
+        attrEditBottom.cursorPosition = 0;
+    }
+    if (type == VAR_VECTOR3)
+    {
+        Vector3 vec = value.GetVector3();
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        LineEdit@ attrEditZ = parent.children[3];
+        attrEditX.text = String(vec.x);
+        attrEditY.text = String(vec.y);
+        attrEditZ.text = String(vec.z);
+        attrEditX.cursorPosition = 0;
+        attrEditY.cursorPosition = 0;
+        attrEditZ.cursorPosition = 0;     
+    }
+    if (type == VAR_VECTOR4)
+    {
+        Vector4 vec = value.GetVector4();
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        LineEdit@ attrEditZ = parent.children[3];
+        LineEdit@ attrEditW = parent.children[4];
+        attrEditX.text = String(vec.x);
+        attrEditY.text = String(vec.y);
+        attrEditZ.text = String(vec.z);
+        attrEditW.text = String(vec.w);
+        attrEditX.cursorPosition = 0;
+        attrEditY.cursorPosition = 0;
+        attrEditZ.cursorPosition = 0;
+        attrEditW.cursorPosition = 0;
+    }
+    if (type == VAR_COLOR)
+    {
+        Color col = value.GetColor();
+        LineEdit@ attrEditR = parent.children[1];
+        LineEdit@ attrEditG = parent.children[2];
+        LineEdit@ attrEditB = parent.children[3];
+        LineEdit@ attrEditA = parent.children[4];
+        attrEditR.text = String(col.r);
+        attrEditG.text = String(col.g);
+        attrEditB.text = String(col.b);
+        attrEditA.text = String(col.a);
+    }
+    if (type == VAR_QUATERNION)
+    {
+        Vector3 vec = value.GetQuaternion().eulerAngles;
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        LineEdit@ attrEditZ = parent.children[3];
+        attrEditX.text = String(vec.x);
+        attrEditY.text = String(vec.y);
+        attrEditZ.text = String(vec.z);
+        attrEditX.cursorPosition = 0;
+        attrEditY.cursorPosition = 0;
+        attrEditZ.cursorPosition = 0;
+    }
+    if (type == VAR_INT)
+    {
+        if (enumNames is null || enumNames.empty)
+        {
+            LineEdit@ attrEdit = parent.children[1];
+            attrEdit.text = String(value.GetInt());
+        }
+        else
+        {
+            DropDownList@ attrEdit = parent.children[1];
+            attrEdit.selection = value.GetInt();
+        }
+    }
+    if (type == VAR_RESOURCEREF)
+    {
+        LineEdit@ attrEdit = parent.children[1].children[0];
+        attrEdit.text = cache.GetResourceName(value.GetResourceRef().id);
+        attrEdit.cursorPosition = 0;
+    }
+    if (type == VAR_RESOURCEREFLIST)
+    {
+        UIElement@ list = parent.parent;
+        ResourceRefList refList = value.GetResourceRefList();
+        for (uint subIndex = 0; subIndex < refList.length; ++subIndex)
+        {
+            parent = GetAttributeEditorParent(list, index, subIndex);
+            if (parent is null)
+                break;
+            LineEdit@ attrEdit = parent.children[1].children[0];
+            attrEdit.text = cache.GetResourceName(refList.ids[subIndex]);
+            attrEdit.cursorPosition = 0;
+        }
+    }
+    if (type == VAR_VARIANTVECTOR)
+    {
+        UIElement@ list = parent.parent;
+        Array<Variant>@ vector = value.GetVariantVector();
+        for (uint i = 0; i < vector.length; ++i)
+        {
+            parent = GetAttributeEditorParent(list, index, i);
+            if (parent is null)
+                break;
+            LoadAttributeEditor(parent, vector[i], vector[i].type, null, Variant());
+        }
+    }
+    if (type == VAR_VARIANTMAP)
+    {
+        UIElement@ list = parent.parent;
+        VariantMap map = value.GetVariantMap();
+        Array<ShortStringHash>@ keys = map.keys;
+        for (uint i = 0; i < keys.length; ++i)
+        {
+            parent = GetAttributeEditorParent(list, index, i);
+            if (parent is null)
+                break;
+            Variant value = map[keys[i]];
+            LoadAttributeEditor(parent, value, value.type, null, Variant());
+        }
+    }
+}
+
+void StoreAttributeEditor(UIElement@ parent, Array<Serializable@>@ serializables, uint index, uint subIndex)
+{
+    AttributeInfo info = serializables[0].attributeInfos[index];
+
+    if (info.type == VAR_RESOURCEREFLIST)
+    {
+        for (uint i = 0; i < serializables.length; ++i)
+        {
+            ResourceRefList refList = serializables[i].attributes[index].GetResourceRefList();
+            Variant newValue = GetEditorValue(parent, VAR_RESOURCEREF, null);
+            ResourceRef ref = newValue.GetResourceRef();
+            refList.ids[subIndex] = ref.id;
+            serializables[i].attributes[index] = Variant(refList);
+        }
+    }
+    else if (info.type == VAR_VARIANTVECTOR)
+    {
+        for (uint i = 0; i < serializables.length; ++i)
+        {
+            Array<Variant>@ vector = serializables[i].attributes[index].GetVariantVector();
+            Variant newValue = GetEditorValue(parent, vector[subIndex].type, null);
+            vector[subIndex] = newValue;
+            serializables[i].attributes[index] = Variant(vector);
+        }
+    }
+    else if (info.type == VAR_VARIANTMAP)
+    {
+        for (uint i = 0; i < serializables.length; ++i)
+        {
+            VariantMap map = serializables[0].attributes[index].GetVariantMap();
+            ShortStringHash key(parent.vars["Key"].GetUInt());
+            Variant newValue = GetEditorValue(parent, map[key].type, null);
+            map[key] = newValue;
+            serializables[i].attributes[index] = Variant(map);
+        }
+    }
+    else
+    {
+        Variant newValue = GetEditorValue(parent, info.type, info.enumNames);
+        for (uint i = 0; i < serializables.length; ++i) {
+            serializables[i].attributes[index] = newValue;
+        }
+    }
+}
+
+Variant GetEditorValue(UIElement@ parent, VariantType type, Array<String>@ enumNames)
+{
+    if (type == VAR_STRING)
+    {
+        LineEdit@ attrEdit = parent.children[1];
+        return Variant(attrEdit.text.Trimmed());
+    }
+    if (type == VAR_BOOL)
+    {
+        CheckBox@ attrEdit = parent.children[1];
+        return Variant(attrEdit.checked);
+    }
+    if (type == VAR_FLOAT)
+    {
+        LineEdit@ attrEdit = parent.children[1];
+        return Variant(attrEdit.text.ToFloat());
+    }
+    if (type == VAR_VECTOR2)
+    {
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        Vector2 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat());
+        return Variant(vec);
+    }
+    if (type == VAR_VECTOR3)
+    {
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        LineEdit@ attrEditZ = parent.children[3];
+        Vector3 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat(), attrEditZ.text.ToFloat());
+        return Variant(vec);
+    }
+    if (type == VAR_VECTOR4)
+    {
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        LineEdit@ attrEditZ = parent.children[3];
+        LineEdit@ attrEditW = parent.children[4];
+        Vector4 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat(), attrEditZ.text.ToFloat(), attrEditW.text.ToFloat());
+        return Variant(vec);
+    }
+    if (type == VAR_COLOR)
+    {
+        LineEdit@ attrEditR = parent.children[1];
+        LineEdit@ attrEditG = parent.children[2];
+        LineEdit@ attrEditB = parent.children[3];
+        LineEdit@ attrEditA = parent.children[4];
+        Color col(attrEditR.text.ToFloat(), attrEditG.text.ToFloat(), attrEditB.text.ToFloat(), attrEditA.text.ToFloat());
+        return Variant(col);
+    }
+    if (type == VAR_QUATERNION)
+    {
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        LineEdit@ attrEditZ = parent.children[3];
+        Vector3 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat(), attrEditZ.text.ToFloat());
+        return Variant(Quaternion(vec));
+    }
+    if (type == VAR_INT)
+    {
+        if (enumNames is null || enumNames.empty)
+        {
+            LineEdit@ attrEdit = parent.children[1];
+            return Variant(attrEdit.text.ToInt());
+        }
+        else
+        {
+            DropDownList@ attrEdit = parent.children[1];
+            return Variant(attrEdit.selection);
+        }
+    }
+    if (type == VAR_RESOURCEREF)
+    {
+        LineEdit@ attrEdit = parent.children[0];
+        ResourceRef ref;
+        ref.id = StringHash(attrEdit.text.Trimmed());
+        ref.type = ShortStringHash(attrEdit.vars["Type"].GetUInt());
+        return Variant(ref);
+    }
+    if (type == VAR_INTVECTOR2)
+    {
+        LineEdit@ attrEditX = parent.children[1];
+        LineEdit@ attrEditY = parent.children[2];
+        IntVector2 vec(attrEditX.text.ToInt(), attrEditY.text.ToInt());
+        return Variant(vec);
+    }
+    if (type == VAR_INTRECT)
+    {
+        LineEdit@ attrEditLeft = parent.children[1];
+        LineEdit@ attrEditTop = parent.children[2];
+        LineEdit@ attrEditRight = parent.children[3];
+        LineEdit@ attrEditBottom = parent.children[3];
+        IntRect vec(attrEditLeft.text.ToInt(), attrEditTop.text.ToInt(),
+                       attrEditRight.text.ToInt(), attrEditBottom.text.ToInt());
+        return Variant(vec);
+    }
+    return Variant();
+}
+
+void UpdateAttributes(Array<Serializable@>@ serializables, ListView@ list, bool fullUpdate)
+{
+    // If attributes have changed structurally, do a full update
+    uint count = GetAttributeEditorCount(serializables);
+    if (fullUpdate == false)
+    {
+        uint oldCount = list.contentElement.numChildren;
+        //Print("oldCount:"+oldCount+" count:"+count);
+        if (oldCount != count)
+            fullUpdate = true;
+    }
+
+    // Remember the old scroll position so that a full update does not feel as jarring
+    IntVector2 oldViewPos = list.viewPosition;
+
+    if (fullUpdate)
+    {
+        list.RemoveAllItems();
+
+        // Resize the node editor according to the number of variables, up to a certain maximum
+        if (list.name == "NodeAttributeList")
+        {
+            uint maxAttrs = Clamp(count, MIN_NODE_ATTRIBUTES, MAX_NODE_ATTRIBUTES);
+            list.SetFixedHeight(maxAttrs * ATTR_HEIGHT + 2);
+        }
+    }
+
+    if (serializables.empty)
+        return;
+
+    // If there are many serializables, they must share same attribute structure
+    for (uint i = 0; i < serializables[0].numAttributes; ++i)
+    {
+        AttributeInfo info = serializables[0].attributeInfos[i];
+        if (info.mode & AM_NOEDIT != 0 && !showNonEditAttribute)
+            continue;
+
+        if (fullUpdate)
+            CreateAttributeEditor(list, serializables, i);
+
+        LoadAttributeEditor(list, serializables, i);
+    }
+    
+    if (fullUpdate)
+        list.viewPosition = oldViewPos;
+}
+
+void EditAttribute(StringHash eventType, VariantMap& eventData)
+{
+    // Changing elements programmatically may cause events to be sent. Stop possible infinite loop in that case.
+    if (inLoadAttributeEditor)
+    {
+        return;
+    }
+
+    UIElement@ attrEdit = eventData["Element"].GetUIElement();
+    UIElement@ parent = attrEdit.parent;
+    Array<Serializable@>@ serializables = GetAttributeEditorTargets(attrEdit);
+    if (serializables.empty)
+        return;
+
+    uint index = attrEdit.vars["Index"].GetUInt();
+    uint subIndex = attrEdit.vars["SubIndex"].GetUInt();
+    bool intermediateEdit = eventType == textChangedEventType;
+
+    StoreAttributeEditor(parent, serializables, index, subIndex);
+    for (uint i = 0; i < serializables.length; ++i)
+        serializables[i].ApplyAttributes();
+
+    // 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)
+    if (!intermediateEdit)
+        UpdateAttributes(false);
+
+    // do the editor logic after attribute has been edited.
+    PostEditAttribute(serializables, index);
+}
+
+// Resource picker functionality
+
+class ResourcePicker
+{
+    String resourceType;
+    String lastPath;
+    uint lastFilter;
+    Array<String> filters;
+
+    ResourcePicker(const String&in resourceType_, const String&in filter_)
+    {
+        resourceType = resourceType_;
+        filters.Push(filter_);
+        filters.Push("*.*");
+        lastFilter = 0;
+    }
+
+    ResourcePicker(const String&in resourceType_, const Array<String>@ filters_)
+    {
+        resourceType = resourceType_;
+        filters = filters_;
+        filters.Push("*.*");
+        lastFilter = 0;
+    }
+};
+
+Array<ResourcePicker@> resourcePickers;
+Array<Serializable@> resourceTargets;
+uint resourcePickIndex = 0;
+uint resourcePickSubIndex = 0;
+ResourcePicker@ resourcePicker = null;
+
+void InitResourcePicker()
+{
+    // Fill resource picker data
+    Array<String> imageFilters = {"*.png", "*.jpg"};
+    Array<String> textureFilters = {"*.dds", "*.png", "*.jpg", "*.bmp", "*.ktx", "*.pvr"};
+    Array<String> soundFilters = {"*.wav","*.ogg"};
+    resourcePickers.Push(ResourcePicker("Animation", "*.ani"));
+    resourcePickers.Push(ResourcePicker("Image", imageFilters));
+    resourcePickers.Push(ResourcePicker("Model", "*.mdl"));
+    resourcePickers.Push(ResourcePicker("Material", "*.xml"));
+    resourcePickers.Push(ResourcePicker("Texture2D", textureFilters));
+    resourcePickers.Push(ResourcePicker("TextureCube", "*.xml"));
+    resourcePickers.Push(ResourcePicker("ScriptFile", "*.as"));
+    resourcePickers.Push(ResourcePicker("XMLFile", "*.xml"));
+    resourcePickers.Push(ResourcePicker("Sound", soundFilters));
+    sceneResourcePath = fileSystem.programDir + "Data";
+}
+
+ResourcePicker@ GetResourcePicker(const String&in resourceType)
+{
+    for (uint i = 0; i < resourcePickers.length; ++i)
+    {
+        if (resourcePickers[i].resourceType.Compare(resourceType, false) == 0)
+            return resourcePickers[i];
+    }
+    return null;
+}
+
+void PickResource(StringHash eventType, VariantMap& eventData)
+{
+    if (uiFileSelector !is null)
+        return;
+
+    UIElement@ button = eventData["Element"].GetUIElement();
+    LineEdit@ attrEdit = button.parent.children[0];
+
+    Array<Serializable@>@ targets = GetAttributeEditorTargets(attrEdit);
+    if (targets.empty)
+        return;
+
+    resourcePickIndex = attrEdit.vars["Index"].GetUInt();
+    resourcePickSubIndex = attrEdit.vars["SubIndex"].GetUInt();
+    AttributeInfo info = targets[0].attributeInfos[resourcePickIndex];
+
+    if (info.type == VAR_RESOURCEREF)
+    {
+        String resourceType = GetTypeName(targets[0].attributes[resourcePickIndex].GetResourceRef().type);
+        // Hack: if the resource is a light's shape texture, change resource type according to light type
+        // (TextureCube for point light)
+        if (info.name == "Light Shape Texture" && cast<Light>(targets[0]).lightType == LIGHT_POINT)
+            resourceType = "TextureCube";
+        @resourcePicker = GetResourcePicker(resourceType);
+    }
+    else if (info.type == VAR_RESOURCEREFLIST)
+    {
+        String resourceType = GetTypeName(targets[0].attributes[resourcePickIndex].GetResourceRefList().type);
+        @resourcePicker = GetResourcePicker(resourceType);
+    }
+    else if (info.type == VAR_VARIANTVECTOR)
+    {
+        String resourceType = GetTypeName(targets[0].attributes[resourcePickIndex].GetVariantVector()
+            [resourcePickSubIndex].GetResourceRef().type);
+        @resourcePicker = GetResourcePicker(resourceType);
+    }
+    else
+        @resourcePicker = null;
+
+    if (resourcePicker is null)
+        return;
+
+    resourceTargets.Clear();
+    for (uint i = 0; i < targets.length; ++i)
+        resourceTargets.Push(targets[i]);
+
+    String lastPath = resourcePicker.lastPath;
+    if (lastPath.empty)
+        lastPath = sceneResourcePath;
+    CreateFileSelector("Pick " + resourcePicker.resourceType, "OK", "Cancel", lastPath, resourcePicker.filters, resourcePicker.lastFilter);
+    SubscribeToEvent(uiFileSelector, "FileSelected", "PickResourceDone");
+}
+
+void PickResourceDone(StringHash eventType, VariantMap& eventData)
+{
+    // Store filter and directory for next time
+    if (resourcePicker !is null)
+    {
+        resourcePicker.lastPath = uiFileSelector.path;
+        resourcePicker.lastFilter = uiFileSelector.filterIndex;
+    }
+
+    CloseFileSelector();
+
+    if (!eventData["OK"].GetBool())
+    {
+        resourceTargets.Clear();
+        @resourcePicker = null;
+        return;
+    }
+
+    if (resourcePicker is null)
+        return;
+
+    // Validate the resource. It must come from within a registered resource directory, and be loaded successfully
+    String resourceName = eventData["FileName"].GetString();
+    Array<String>@ resourceDirs = cache.resourceDirs;
+    Resource@ res;
+
+    for (uint i = 0; i < resourceDirs.length; ++i)
+    {
+        if (!resourceName.ToLower().StartsWith(resourceDirs[i].ToLower()))
+            continue;
+        resourceName = resourceName.Substring(resourceDirs[i].length);
+        res = cache.GetResource(resourcePicker.resourceType, resourceName);
+        break;
+    }
+    if (res is null) {
+        Print("Can not find resource type: " + resourcePicker.resourceType + " Name:" +resourceName);
+        return;
+    }
+
+    for (uint i = 0; i < resourceTargets.length; ++i)
+    {
+        Serializable@ target = resourceTargets[i];
+
+        AttributeInfo info = target.attributeInfos[resourcePickIndex];
+        if (info.type == VAR_RESOURCEREF)
+        {
+            ResourceRef ref = target.attributes[resourcePickIndex].GetResourceRef();
+            ref.type = ShortStringHash(resourcePicker.resourceType);
+            ref.id = StringHash(resourceName);
+            //Print("Set type: " + resourcePicker.resourceType + " Name:" +resourceName);
+            target.attributes[resourcePickIndex] = Variant(ref);
+            target.ApplyAttributes();
+        }
+        else if (info.type == VAR_RESOURCEREFLIST)
+        {
+            ResourceRefList refList = target.attributes[resourcePickIndex].GetResourceRefList();
+            if (resourcePickSubIndex < refList.length)
+            {
+                refList.ids[resourcePickSubIndex] = StringHash(resourceName);
+                target.attributes[resourcePickIndex] = Variant(refList);
+                target.ApplyAttributes();
+            }
+        }
+        else if (info.type == VAR_VARIANTVECTOR)
+        {
+            Array<Variant>@ attrs = target.attributes[resourcePickIndex].GetVariantVector();
+            ResourceRef ref = attrs[resourcePickSubIndex].GetResourceRef();
+            ref.type = ShortStringHash(resourcePicker.resourceType);
+            ref.id = StringHash(resourceName);
+            attrs[resourcePickSubIndex] = ref;
+            target.attributes[resourcePickIndex] = Variant(attrs);
+            target.ApplyAttributes();
+        }
+    }
+
+    UpdateAttributes(false);
+
+    resourceTargets.Clear();
+    @resourcePicker = null;
+}
+
+void PickResourceCanceled()
+{
+    if (!resourceTargets.empty)
+    {
+        resourceTargets.Clear();
+        @resourcePicker = null;
+        CloseFileSelector();
+    }
+}
+
+void OpenResource(StringHash eventType, VariantMap& eventData)
+{
+    UIElement@ button = eventData["Element"].GetUIElement();
+    LineEdit@ attrEdit = button.parent.children[0];
+    
+    String fileName = attrEdit.text.Trimmed();
+    if (fileName.empty)
+        return;
+
+    Array<String>@ resourceDirs = cache.resourceDirs;
+    for (uint i = 0; i < resourceDirs.length; ++i)
+    {
+        String fullPath = resourceDirs[i] + fileName;
+        if (fileSystem.FileExists(fullPath))
+        {
+            fileSystem.SystemOpen(fullPath, "");
+            return;
+        }
+    }
+}
+
+// VariantVector decoding & editing for certain components
+
+class VectorStruct
+{
+    String componentTypeName;
+    String attributeName;
+    Array<String> variableNames;
+    uint restartIndex;
+
+    VectorStruct(const String&in componentTypeName_, const String&in attributeName_, const Array<String>@ variableNames_, uint restartIndex_)
+    {
+        componentTypeName = componentTypeName_;
+        attributeName = attributeName_;
+        variableNames = variableNames_;
+        restartIndex = restartIndex_;
+    }
+};
+
+Array<VectorStruct@> vectorStructs;
+
+void InitVectorStructs()
+{
+    // Fill vector structure data
+    Array<String> billboardVariables = {
+        "Billboard Count",
+        "   Position",
+        "   Size",
+        "   UV Coordinates",
+        "   Color",
+        "   Rotation",
+        "   Is Enabled"
+    };
+    vectorStructs.Push(VectorStruct("BillboardSet", "Billboards", billboardVariables, 1));
+
+    Array<String> animationStateVariables = {
+        "Anim State Count",
+        "   Animation",
+        "   Start Bone",
+        "   Is Looped",
+        "   Weight",
+        "   Time",
+        "   Layer"
+    };
+    vectorStructs.Push(VectorStruct("AnimatedModel", "Animation States", animationStateVariables, 1));
+}
+
+VectorStruct@ GetVectorStruct(Array<Serializable@>@ serializables, uint index)
+{
+    AttributeInfo info = serializables[0].attributeInfos[index];
+    for (uint i = 0; i < vectorStructs.length; ++i)
+    {
+        if (vectorStructs[i].componentTypeName == serializables[0].typeName && vectorStructs[i].attributeName == info.name)
+            return vectorStructs[i];
+    }
+    return null;
+}

+ 5 - 912
Bin/Data/Scripts/Editor/EditorNodeWindow.as

@@ -1,110 +1,16 @@
-// Urho3D editor component edit window handling
+// Urho3D editor node/component edit window handling
+#include "Scripts/Editor/AttributeEditor.as"
 
 Window@ nodeWindow;
 
-const uint MIN_NODE_ATTRIBUTES = 4;
-const uint MAX_NODE_ATTRIBUTES = 8;
-const int ATTRNAME_WIDTH = 135;
-const int ATTR_HEIGHT = 19;
-
-const ShortStringHash textType("Text");
-const ShortStringHash containerType("UIElement");
-const StringHash textChangedEventType("TextChanged");
-
-Color normalTextColor(1.0f, 1.0f, 1.0f);
-Color modifiedTextColor(1.0f, 0.8f, 0.5f);
-
-class ResourcePicker
-{
-    String resourceType;
-    String lastPath;
-    uint lastFilter;
-    Array<String> filters;
-
-    ResourcePicker(const String&in resourceType_, const String&in filter_)
-    {
-        resourceType = resourceType_;
-        filters.Push(filter_);
-        filters.Push("*.*");
-        lastFilter = 0;
-    }
-
-    ResourcePicker(const String&in resourceType_, const Array<String>@ filters_)
-    {
-        resourceType = resourceType_;
-        filters = filters_;
-        filters.Push("*.*");
-        lastFilter = 0;
-    }
-}
-
-class VectorStruct
-{
-    String componentTypeName;
-    String attributeName;
-    Array<String> variableNames;
-    uint restartIndex;
-
-    VectorStruct(const String&in componentTypeName_, const String&in attributeName_, const Array<String>@ variableNames_, uint restartIndex_)
-    {
-        componentTypeName = componentTypeName_;
-        attributeName = attributeName_;
-        variableNames = variableNames_;
-        restartIndex = restartIndex_;
-    }
-}
-
-Array<ResourcePicker@> resourcePickers;
-Array<VectorStruct@> vectorStructs;
-
-bool inLoadAttributeEditor = false;
-Array<uint> resourcePickIDs;
-uint resourcePickIndex = 0;
-uint resourcePickSubIndex = 0;
-ResourcePicker@ resourcePicker;
-String resourcePickEditorName;
 
 void CreateNodeWindow()
 {
     if (nodeWindow !is null)
         return;
 
-    // Fill resource picker data
-    Array<String> imageFilters = {"*.png", "*.jpg"};
-    Array<String> textureFilters = {"*.dds", "*.png", "*.jpg", "*.bmp", "*.ktx", "*.pvr"};
-    Array<String> soundFilters = {"*.wav","*.ogg"};
-    resourcePickers.Push(ResourcePicker("Animation", "*.ani"));
-    resourcePickers.Push(ResourcePicker("Image", imageFilters));
-    resourcePickers.Push(ResourcePicker("Model", "*.mdl"));
-    resourcePickers.Push(ResourcePicker("Material", "*.xml"));
-    resourcePickers.Push(ResourcePicker("Texture2D", textureFilters));
-    resourcePickers.Push(ResourcePicker("TextureCube", "*.xml"));
-    resourcePickers.Push(ResourcePicker("ScriptFile", "*.as"));
-    resourcePickers.Push(ResourcePicker("XMLFile", "*.xml"));
-    resourcePickers.Push(ResourcePicker("Sound", soundFilters));
-
-    // Fill vector structure data
-    Array<String> billboardVariables = {
-        "Billboard Count",
-        "   Position",
-        "   Size", 
-        "   UV Coordinates", 
-        "   Color", 
-        "   Rotation", 
-        "   Is Enabled"
-    };
-    vectorStructs.Push(VectorStruct("BillboardSet", "Billboards", billboardVariables, 1));
-    
-    Array<String> animationStateVariables = {
-        "Anim State Count",
-        "   Animation",
-        "   Start Bone",
-        "   Is Looped",
-        "   Weight",
-        "   Time",
-        "   Layer"
-    };
-    vectorStructs.Push(VectorStruct("AnimatedModel", "Animation States", animationStateVariables, 1));
+    InitResourcePicker();
+    InitVectorStructs();
 
     nodeWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorNodeWindow.xml"), uiStyle);
     ui.root.AddChild(nodeWindow);
@@ -201,160 +107,14 @@ void UpdateNodeAttributes()
     }
 }
 
-void UpdateAttributes(Array<Serializable@>@ serializables, ListView@ list, bool fullUpdate)
-{
-    // If attributes have changed structurally, do a full update
-    uint count = GetAttributeEditorCount(serializables);
-    if (fullUpdate == false)
-    {
-        if (list.contentElement.numChildren != count)
-            fullUpdate = true;
-    }
-
-    // Remember the old scroll position so that a full update does not feel as jarring
-    IntVector2 oldViewPos = list.viewPosition;
-
-    if (fullUpdate)
-    {
-        list.RemoveAllItems();
-
-        // Resize the node editor according to the number of variables, up to a certain maximum
-        if (list.name == "NodeAttributeList")
-        {
-            uint maxAttrs = Clamp(count, MIN_NODE_ATTRIBUTES, MAX_NODE_ATTRIBUTES);
-            list.SetFixedHeight(maxAttrs * ATTR_HEIGHT + 2);
-        }
-    }
-
-    if (serializables.empty)
-        return;
-
-    // If there are many serializables, they must share same attribute structure
-    for (uint i = 0; i < serializables[0].numAttributes; ++i)
-    {
-        AttributeInfo info = serializables[0].attributeInfos[i];
-        if (info.mode & AM_NOEDIT != 0)
-            continue;
-
-        if (fullUpdate)
-            CreateAttributeEditor(list, serializables, i);
-
-        LoadAttributeEditor(list, serializables, i);
-    }
-
-    if (fullUpdate)
-        list.viewPosition = oldViewPos;
-}
 
-void EditAttribute(StringHash eventType, VariantMap& eventData)
+void PostEditAttribute(Array<Serializable@>@ serializables, uint index)
 {
-    // Changing elements programmatically may cause events to be sent. Stop possible infinite loop in that case.
-    if (inLoadAttributeEditor)
-        return;
-
-    UIElement@ attrEdit = eventData["Element"].GetUIElement();
-    UIElement@ parent = attrEdit.parent;
-    Array<Serializable@>@ serializables = GetAttributeEditorTargets(attrEdit);
-    if (serializables.empty)
-        return;
-
-    uint index = attrEdit.vars["Index"].GetUInt();
-    uint subIndex = attrEdit.vars["SubIndex"].GetUInt();
-    bool intermediateEdit = eventType == textChangedEventType;
-
-    StoreAttributeEditor(parent, serializables, index, subIndex);
-    for (uint i = 0; i < serializables.length; ++i)
-        serializables[i].ApplyAttributes();
-
-    // 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)
-    if (!intermediateEdit)
-        UpdateAttributes(false);
-
     // If node name changed, update it in the scene window also
     if (serializables[0] is editNode && serializables[0].attributeInfos[index].name == "Name")
         UpdateSceneWindowNodeOnly(editNode);
 }
 
-uint GetAttributeEditorCount(Array<Serializable@>@ serializables)
-{
-    uint count = 0;
-
-    if (!serializables.empty)
-    {
-        /// \todo When multi-editing, this only counts the editor count of the first serializable
-        for (uint i = 0; i < serializables[0].numAttributes; ++i)
-        {
-            AttributeInfo info = serializables[0].attributeInfos[i];
-            if (info.mode & AM_NOEDIT != 0)
-                continue;
-            if (info.type == VAR_RESOURCEREFLIST)
-                count += serializables[0].attributes[i].GetResourceRefList().length;
-            else if (info.type == VAR_VARIANTVECTOR && GetVectorStruct(serializables, i) !is null)
-                count += serializables[0].attributes[i].GetVariantVector().length;
-            else if (info.type == VAR_VARIANTMAP)
-                count += serializables[0].attributes[i].GetVariantMap().length;
-            else
-                ++count;
-        }
-    }
-
-    return count;
-}
-
-UIElement@ CreateAttributeEditorParentWithSeparatedLabel(ListView@ list, String name, uint index, uint subIndex, bool suppressedSeparatedLabel = false)
-{
-    UIElement@ editorParent = UIElement("Edit" + String(index) + "_" + String(subIndex));
-    editorParent.vars["Index"] = index;
-    editorParent.vars["SubIndex"] = subIndex;
-    editorParent.SetLayout(LM_VERTICAL, 2);
-    list.AddItem(editorParent);
-
-    if (suppressedSeparatedLabel)
-    {
-        UIElement@ placeHolder = UIElement();
-        editorParent.AddChild(placeHolder);
-    }
-    else
-    {
-        Text@ attrNameText = Text();
-        attrNameText.SetStyle(uiStyle, "EditorAttributeText");
-        attrNameText.text = name;
-        editorParent.AddChild(attrNameText);
-    }
-
-    return editorParent;
-}
-
-UIElement@ CreateAttributeEditorParent(ListView@ list, String name, uint index, uint subIndex)
-{
-    UIElement@ editorParent = UIElement("Edit" + String(index) + "_" + String(subIndex));
-    editorParent.vars["Index"] = index;
-    editorParent.vars["SubIndex"] = subIndex;
-    editorParent.SetLayout(LM_HORIZONTAL);
-    editorParent.SetFixedHeight(ATTR_HEIGHT);
-    list.AddItem(editorParent);
-
-    Text@ attrNameText = Text();
-    attrNameText.SetStyle(uiStyle, "EditorAttributeText");
-    attrNameText.text = name;
-    attrNameText.SetFixedWidth(ATTRNAME_WIDTH);
-    editorParent.AddChild(attrNameText);
-
-    return editorParent;
-}
-
-LineEdit@ CreateAttributeLineEdit(UIElement@ parent, Array<Serializable@>@ serializables, uint index, uint subIndex)
-{
-    LineEdit@ attrEdit = LineEdit();
-    attrEdit.SetStyle(uiStyle, "EditorAttributeEdit");
-    attrEdit.SetFixedHeight(ATTR_HEIGHT - 2);
-    attrEdit.vars["Index"] = index;
-    attrEdit.vars["SubIndex"] = subIndex;
-    SetAttributeEditorID(attrEdit, serializables);
-    parent.AddChild(attrEdit);
-    return attrEdit;
-}
 
 void SetAttributeEditorID(UIElement@ attrEdit, Array<Serializable@>@ serializables)
 {
@@ -403,674 +163,7 @@ Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit)
     return ret;
 }
 
-UIElement@ GetAttributeEditorParent(UIElement@ parent, uint index, uint subIndex)
-{
-    return parent.GetChild("Edit" + String(index) + "_" + String(subIndex), true);
-}
-
-VectorStruct@ GetVectorStruct(Array<Serializable@>@ serializables, uint index)
-{
-    AttributeInfo info = serializables[0].attributeInfos[index];
-    for (uint i = 0; i < vectorStructs.length; ++i)
-    {
-        if (vectorStructs[i].componentTypeName == serializables[0].typeName && vectorStructs[i].attributeName ==
-            info.name)
-            return vectorStructs[i];
-    }
-    return null;
-}
-
-void CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, uint index)
-{
-    AttributeInfo info = serializables[0].attributeInfos[index];
-    CreateAttributeEditor(list, serializables, info.name, info.type, info.enumNames, index, 0);
-}
-
-UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const String&in name, VariantType type, Array<String>@ enumNames, uint index, uint subIndex, bool suppressedSeparatedLabel = false)
-{
-    UIElement@ parent;
-
-    if (type == VAR_STRING)
-    {
-        parent = CreateAttributeEditorParent(list, name, index, subIndex);
-        LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
-        SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
-        SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
-    }
-    if (type == VAR_BOOL)
-    {
-        parent = CreateAttributeEditorParent(list, name, index, subIndex);
-        CheckBox@ attrEdit = CheckBox();
-        attrEdit.style = uiStyle;
-        attrEdit.SetFixedSize(16, 16);
-        attrEdit.vars["Index"] = index;
-        attrEdit.vars["SubIndex"] = subIndex;
-        SetAttributeEditorID(attrEdit, serializables);
-        parent.AddChild(attrEdit);
-        SubscribeToEvent(attrEdit, "Toggled", "EditAttribute");
-    }
-    if ((type >= VAR_FLOAT && type <= VAR_VECTOR4) || type == VAR_QUATERNION || type == VAR_COLOR)
-    {
-        parent = CreateAttributeEditorParent(list, name, index, subIndex);
-        uint numCoords = type - VAR_FLOAT + 1;
-        if (type == VAR_QUATERNION)
-            numCoords = 3;
-        if (type == VAR_COLOR)
-            numCoords = 4;
-
-        for (uint i = 0; i < numCoords; ++i)
-        {
-            LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
-            SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
-            SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
-        }
-    }
-    if (type == VAR_INT)
-    {
-        parent = CreateAttributeEditorParent(list, name, index, subIndex);
-        // Check for enums
-        if (enumNames is null || enumNames.empty)
-        {
-            // No enums, create a numeric editor
-            LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
-            SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
-            SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
-        }
-        else
-        {
-            DropDownList@ attrEdit = DropDownList();
-            attrEdit.style = uiStyle;
-            attrEdit.SetFixedHeight(ATTR_HEIGHT - 2);
-            attrEdit.resizePopup = true;
-            attrEdit.vars["Index"] = index;
-            attrEdit.vars["SubIndex"] = subIndex;
-            attrEdit.SetLayout(LM_HORIZONTAL, 0, IntRect(4, 1, 4, 1));
-            SetAttributeEditorID(attrEdit, serializables);
-
-            for (uint i = 0; i < enumNames.length; ++i)
-            {
-                Text@ choice = Text();
-                choice.SetStyle(uiStyle, "EditorEnumAttributeText");
-                choice.text = enumNames[i];
-                attrEdit.AddItem(choice);
-            }
-            parent.AddChild(attrEdit);
-            SubscribeToEvent(attrEdit, "ItemSelected", "EditAttribute");
-        }
-    }
-    if (type == VAR_RESOURCEREF)
-    {
-        ShortStringHash resourceType;
-        VariantType attrType = serializables[0].attributeInfos[index].type;
-        if (attrType == VAR_RESOURCEREF)
-            resourceType = serializables[0].attributes[index].GetResourceRef().type;
-        else if (attrType == VAR_RESOURCEREFLIST)
-            resourceType = serializables[0].attributes[index].GetResourceRefList().type;
-        else if (attrType == VAR_VARIANTVECTOR)
-            resourceType = serializables[0].attributes[index].GetVariantVector()[subIndex].GetResourceRef().type;
-
-        // Create the resource name on a separate non-interactive line to allow for more space
-        parent = CreateAttributeEditorParentWithSeparatedLabel(list, name, index, subIndex, suppressedSeparatedLabel);
-
-        UIElement@ container = UIElement();
-        container.SetLayout(LM_HORIZONTAL, 4, IntRect(name.StartsWith("   ") ? 20 : 10, 0, 4, 0));    // Left margin is indented more when the name is so
-        container.SetFixedHeight(ATTR_HEIGHT);
-        parent.AddChild(container);
-        
-        LineEdit@ attrEdit = CreateAttributeLineEdit(container, serializables, index, subIndex);
-        attrEdit.vars["Type"] = resourceType.value;
-        SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
-
-        Button@ pickButton = Button();
-        pickButton.style = uiStyle;
-        pickButton.SetFixedSize(36, ATTR_HEIGHT - 2);
-        pickButton.vars["Index"] = index;
-        pickButton.vars["SubIndex"] = subIndex;
-        SetAttributeEditorID(pickButton, serializables);
-
-        Text@ pickButtonText = Text();
-        pickButtonText.SetStyle(uiStyle, "EditorAttributeText");
-        pickButtonText.SetAlignment(HA_CENTER, VA_CENTER);
-        pickButtonText.text = "Pick";
-        pickButton.AddChild(pickButtonText);
-        container.AddChild(pickButton);
-        SubscribeToEvent(pickButton, "Released", "PickResource");
-
-        Button@ openButton = Button();
-        openButton.style = uiStyle;
-        openButton.SetFixedSize(36, ATTR_HEIGHT - 2);
-        openButton.vars["Index"] = index;
-        openButton.vars["SubIndex"] = subIndex;
-        SetAttributeEditorID(openButton, serializables);
-
-        Text@ openButtonText = Text();
-        openButtonText.SetStyle(uiStyle, "EditorAttributeText");
-        openButtonText.SetAlignment(HA_CENTER, VA_CENTER);
-        openButtonText.text = "Open";
-        openButton.AddChild(openButtonText);
-        container.AddChild(openButton);
-        SubscribeToEvent(openButton, "Released", "OpenResource");
-    }
-    if (type == VAR_RESOURCEREFLIST)
-    {
-        uint numRefs = serializables[0].attributes[index].GetResourceRefList().length;
-        for (uint i = 0; i < numRefs; ++i)
-            CreateAttributeEditor(list, serializables, name, VAR_RESOURCEREF, null, index, i, i > 0);
-    }
-    if (type == VAR_VARIANTVECTOR)
-    {
-        VectorStruct@ vectorStruct = GetVectorStruct(serializables, index);
-        if (vectorStruct is null)
-            return null;
-        uint nameIndex = 0;
 
-        Array<Variant>@ vector = serializables[0].attributes[index].GetVariantVector();
-        for (uint i = 0; i < vector.length; ++i)
-        {
-            CreateAttributeEditor(list, serializables, vectorStruct.variableNames[nameIndex], vector[i].type, null, index, i);
-            ++nameIndex;
-            if (nameIndex >= vectorStruct.variableNames.length)
-                nameIndex = vectorStruct.restartIndex;
-        }
-    }
-    if (type == VAR_VARIANTMAP)
-    {
-        VariantMap map = serializables[0].attributes[index].GetVariantMap();
-        Array<ShortStringHash>@ keys = map.keys;
-        for (uint i = 0; i < keys.length; ++i)
-        {
-            Variant value = map[keys[i]];
-            parent = CreateAttributeEditor(list, serializables, editorScene.GetVarName(keys[i]) + " (Var)", value.type, null, index, i);
-            // Add the variant key to the parent
-            parent.vars["Key"] = keys[i].value;
-        }
-    }
-
-    return parent;
-}
-
-void LoadAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, uint index)
-{
-    UIElement@ parent = GetAttributeEditorParent(list, index, 0);
-    if (parent is null)
-        return;
-
-    inLoadAttributeEditor = true;
-
-    AttributeInfo info = serializables[0].attributeInfos[index];
-    bool sameValue = true;
-    Variant value = serializables[0].attributes[index];
-    for (uint i = 1; i < serializables.length; ++i)
-    {
-        if (serializables[i].attributes[index] != value)
-        {
-            sameValue = false;
-            break;
-        }
-    }
-
-    if (sameValue)
-        LoadAttributeEditor(parent, value, value.type, info.enumNames, info.defaultValue);
-
-    inLoadAttributeEditor = false;
-}
-
-void LoadAttributeEditor(UIElement@ parent, Variant value, VariantType type, Array<String>@ enumNames, Variant defaultValue)
-{
-    uint index = parent.vars["Index"].GetUInt();
-
-    // Assume the first child is always a text label element or a container that containing a text label element
-    UIElement@ label = parent.children[0];
-    if (label.type == containerType)
-        label = label.children[0];
-    if (label.type == textType)
-    {
-        bool modified;
-        if (defaultValue.type == VAR_NONE || defaultValue.type == VAR_RESOURCEREFLIST)
-            modified = !value.IsZero();
-        else
-            modified = value != defaultValue;
-        cast<Text>(label).color = (modified ? modifiedTextColor : normalTextColor);
-    }
-    
-    if (type == VAR_STRING)
-    {
-        LineEdit@ attrEdit = parent.children[1];
-        attrEdit.text = value.GetString();
-    }
-    if (type == VAR_BOOL)
-    {
-        CheckBox@ attrEdit = parent.children[1];
-        attrEdit.checked = value.GetBool();
-    }
-    if (type == VAR_FLOAT)
-    {
-        LineEdit@ attrEdit = parent.children[1];
-        attrEdit.text = String(value.GetFloat());
-    }
-    if (type == VAR_VECTOR2)
-    {
-        Vector2 vec = value.GetVector2();
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        attrEditX.text = String(vec.x);
-        attrEditY.text = String(vec.y);
-        attrEditX.cursorPosition = 0;
-        attrEditY.cursorPosition = 0;
-    }
-    if (type == VAR_VECTOR3)
-    {
-        Vector3 vec = value.GetVector3();
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        LineEdit@ attrEditZ = parent.children[3];
-        attrEditX.text = String(vec.x);
-        attrEditY.text = String(vec.y);
-        attrEditZ.text = String(vec.z);
-        attrEditX.cursorPosition = 0;
-        attrEditY.cursorPosition = 0;
-        attrEditZ.cursorPosition = 0;     
-    }
-    if (type == VAR_VECTOR4)
-    {
-        Vector4 vec = value.GetVector4();
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        LineEdit@ attrEditZ = parent.children[3];
-        LineEdit@ attrEditW = parent.children[4];
-        attrEditX.text = String(vec.x);
-        attrEditY.text = String(vec.y);
-        attrEditZ.text = String(vec.z);
-        attrEditW.text = String(vec.w);
-        attrEditX.cursorPosition = 0;
-        attrEditY.cursorPosition = 0;
-        attrEditZ.cursorPosition = 0;
-        attrEditW.cursorPosition = 0;      
-    }
-    if (type == VAR_COLOR)
-    {
-        Color col = value.GetColor();
-        LineEdit@ attrEditR = parent.children[1];
-        LineEdit@ attrEditG = parent.children[2];
-        LineEdit@ attrEditB = parent.children[3];
-        LineEdit@ attrEditA = parent.children[4];
-        attrEditR.text = String(col.r);
-        attrEditG.text = String(col.g);
-        attrEditB.text = String(col.b);
-        attrEditA.text = String(col.a);
-    }
-    if (type == VAR_QUATERNION)
-    {
-        Vector3 vec = value.GetQuaternion().eulerAngles;
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        LineEdit@ attrEditZ = parent.children[3];
-        attrEditX.text = String(vec.x);
-        attrEditY.text = String(vec.y);
-        attrEditZ.text = String(vec.z);
-        attrEditX.cursorPosition = 0;
-        attrEditY.cursorPosition = 0;
-        attrEditZ.cursorPosition = 0;
-    }
-    if (type == VAR_INT)
-    {
-        if (enumNames is null || enumNames.empty)
-        {
-            LineEdit@ attrEdit = parent.children[1];
-            attrEdit.text = String(value.GetInt());
-        }
-        else
-        {
-            DropDownList@ attrEdit = parent.children[1];
-            attrEdit.selection = value.GetInt();
-        }
-    }
-    if (type == VAR_RESOURCEREF)
-    {
-        LineEdit@ attrEdit = parent.children[1].children[0];
-        attrEdit.text = cache.GetResourceName(value.GetResourceRef().id);
-        attrEdit.cursorPosition = 0;
-    }
-    if (type == VAR_RESOURCEREFLIST)
-    {
-        UIElement@ list = parent.parent;
-        ResourceRefList refList = value.GetResourceRefList();
-        for (uint subIndex = 0; subIndex < refList.length; ++subIndex)
-        {
-            parent = GetAttributeEditorParent(list, index, subIndex);
-            if (parent is null)
-                break;
-            LineEdit@ attrEdit = parent.children[1].children[0];
-            attrEdit.text = cache.GetResourceName(refList.ids[subIndex]);
-            attrEdit.cursorPosition = 0;
-        }
-    }
-    if (type == VAR_VARIANTVECTOR)
-    {
-        UIElement@ list = parent.parent;
-        Array<Variant>@ vector = value.GetVariantVector();
-        for (uint i = 0; i < vector.length; ++i)
-        {
-            parent = GetAttributeEditorParent(list, index, i);
-            if (parent is null)
-                break;
-            LoadAttributeEditor(parent, vector[i], vector[i].type, null, Variant());
-        }
-    }
-    if (type == VAR_VARIANTMAP)
-    {
-        UIElement@ list = parent.parent;
-        VariantMap map = value.GetVariantMap();
-        Array<ShortStringHash>@ keys = map.keys;
-        for (uint i = 0; i < keys.length; ++i)
-        {
-            parent = GetAttributeEditorParent(list, index, i);
-            if (parent is null)
-                break;
-            Variant value = map[keys[i]];
-            LoadAttributeEditor(parent, value, value.type, null, Variant());
-        }
-    }
-}
-
-void StoreAttributeEditor(UIElement@ parent, Array<Serializable@>@ serializables, uint index, uint subIndex)
-{
-    AttributeInfo info = serializables[0].attributeInfos[index];
-
-    if (info.type == VAR_RESOURCEREFLIST)
-    {
-        for (uint i = 0; i < serializables.length; ++i)
-        {
-            ResourceRefList refList = serializables[i].attributes[index].GetResourceRefList();
-            Variant newValue = GetEditorValue(parent, VAR_RESOURCEREF, null);
-            ResourceRef ref = newValue.GetResourceRef();
-            refList.ids[subIndex] = ref.id;
-            serializables[i].attributes[index] = Variant(refList);
-        }
-    }
-    else if (info.type == VAR_VARIANTVECTOR)
-    {
-        for (uint i = 0; i < serializables.length; ++i)
-        {
-            Array<Variant>@ vector = serializables[i].attributes[index].GetVariantVector();
-            Variant newValue = GetEditorValue(parent, vector[subIndex].type, null);
-            vector[subIndex] = newValue;
-            serializables[i].attributes[index] = Variant(vector);
-        }
-    }
-    else if (info.type == VAR_VARIANTMAP)
-    {
-        for (uint i = 0; i < serializables.length; ++i)
-        {
-            VariantMap map = serializables[0].attributes[index].GetVariantMap();
-            ShortStringHash key(parent.vars["Key"].GetUInt());
-            Variant newValue = GetEditorValue(parent, map[key].type, null);
-            map[key] = newValue;
-            serializables[i].attributes[index] = Variant(map);
-        }
-    }
-    else
-    {
-        Variant newValue = GetEditorValue(parent, info.type, info.enumNames);
-        for (uint i = 0; i < serializables.length; ++i)
-            serializables[i].attributes[index] = newValue;
-    }
-}
-
-Variant GetEditorValue(UIElement@ parent, VariantType type, Array<String>@ enumNames)
-{
-    if (type == VAR_STRING)
-    {
-        LineEdit@ attrEdit = parent.children[1];
-        return Variant(attrEdit.text.Trimmed());
-    }
-    if (type == VAR_BOOL)
-    {
-        CheckBox@ attrEdit = parent.children[1];
-        return Variant(attrEdit.checked);
-    }
-    if (type == VAR_FLOAT)
-    {
-        LineEdit@ attrEdit = parent.children[1];
-        return Variant(attrEdit.text.ToFloat());
-    }
-    if (type == VAR_VECTOR2)
-    {
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        Vector2 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat());
-        return Variant(vec);
-    }
-    if (type == VAR_VECTOR3)
-    {
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        LineEdit@ attrEditZ = parent.children[3];
-        Vector3 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat(), attrEditZ.text.ToFloat());
-        return Variant(vec);
-    }
-    if (type == VAR_VECTOR4)
-    {
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        LineEdit@ attrEditZ = parent.children[3];
-        LineEdit@ attrEditW = parent.children[4];
-        Vector4 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat(), attrEditZ.text.ToFloat(), attrEditW.text.ToFloat());
-        return Variant(vec);
-    }
-    if (type == VAR_COLOR)
-    {
-        LineEdit@ attrEditR = parent.children[1];
-        LineEdit@ attrEditG = parent.children[2];
-        LineEdit@ attrEditB = parent.children[3];
-        LineEdit@ attrEditA = parent.children[4];
-        Color col(attrEditR.text.ToFloat(), attrEditG.text.ToFloat(), attrEditB.text.ToFloat(), attrEditA.text.ToFloat());
-        return Variant(col);
-    }
-    if (type == VAR_QUATERNION)
-    {
-        LineEdit@ attrEditX = parent.children[1];
-        LineEdit@ attrEditY = parent.children[2];
-        LineEdit@ attrEditZ = parent.children[3];
-        Vector3 vec(attrEditX.text.ToFloat(), attrEditY.text.ToFloat(), attrEditZ.text.ToFloat());
-        return Variant(Quaternion(vec));
-    }
-    if (type == VAR_INT)
-    {
-        if (enumNames is null || enumNames.empty)
-        {
-            LineEdit@ attrEdit = parent.children[1];
-            return Variant(attrEdit.text.ToInt());
-        }
-        else
-        {
-            DropDownList@ attrEdit = parent.children[1];
-            return Variant(attrEdit.selection);
-        }
-    }
-    if (type == VAR_RESOURCEREF)
-    {
-        LineEdit@ attrEdit = parent.children[1].children[0];
-        ResourceRef ref;
-        ref.id = StringHash(attrEdit.text.Trimmed());
-        ref.type = ShortStringHash(attrEdit.vars["Type"].GetUInt());
-        return Variant(ref);
-    }
-    
-    return Variant();
-}
-
-ResourcePicker@ GetResourcePicker(const String&in resourceType)
-{
-    for (uint i = 0; i < resourcePickers.length; ++i)
-    {
-        if (resourcePickers[i].resourceType.Compare(resourceType, false) == 0)
-            return resourcePickers[i];
-    }
-
-    return null;
-}
-
-void PickResource(StringHash eventType, VariantMap& eventData)
-{
-    if (uiFileSelector !is null)
-        return;
-
-    UIElement@ button = eventData["Element"].GetUIElement();
-    LineEdit@ attrEdit = button.parent.children[0];
-
-    Array<Serializable@>@ targets = GetAttributeEditorTargets(attrEdit);
-    if (targets.empty)
-        return;
-
-    resourcePickIndex = attrEdit.vars["Index"].GetUInt();
-    resourcePickSubIndex = attrEdit.vars["SubIndex"].GetUInt();
-    AttributeInfo info = targets[0].attributeInfos[resourcePickIndex];
-
-    if (info.type == VAR_RESOURCEREF)
-    {
-        String resourceType = GetTypeName(targets[0].attributes[resourcePickIndex].GetResourceRef().type);
-        // Hack: if the resource is a light's shape texture, change resource type according to light type
-        // (TextureCube for point light)
-        if (info.name == "Light Shape Texture" && cast<Light>(targets[0]).lightType == LIGHT_POINT)
-            resourceType = "TextureCube";
-        @resourcePicker = GetResourcePicker(resourceType);
-    }
-    else if (info.type == VAR_RESOURCEREFLIST)
-    {
-        String resourceType = GetTypeName(targets[0].attributes[resourcePickIndex].GetResourceRefList().type);
-        @resourcePicker = GetResourcePicker(resourceType);
-    }
-    else if (info.type == VAR_VARIANTVECTOR)
-    {
-        String resourceType = GetTypeName(targets[0].attributes[resourcePickIndex].GetVariantVector()
-            [resourcePickSubIndex].GetResourceRef().type);
-        @resourcePicker = GetResourcePicker(resourceType);
-    }
-    else
-        @resourcePicker = null;
-
-    if (resourcePicker is null)
-        return;
-
-    resourcePickIDs.Clear();
-    for (uint i = 0; i < targets.length; ++i)
-        resourcePickIDs.Push(cast<Component>(targets[i]).id);
-
-    String lastPath = resourcePicker.lastPath;
-    if (lastPath.empty)
-        lastPath = sceneResourcePath;
-    CreateFileSelector("Pick " + resourcePicker.resourceType, "OK", "Cancel", lastPath, resourcePicker.filters, resourcePicker.lastFilter);
-    SubscribeToEvent(uiFileSelector, "FileSelected", "PickResourceDone");
-}
-
-void PickResourceDone(StringHash eventType, VariantMap& eventData)
-{
-    // Store filter and directory for next time
-    if (resourcePicker !is null)
-    {
-        resourcePicker.lastPath = uiFileSelector.path;
-        resourcePicker.lastFilter = uiFileSelector.filterIndex;
-    }
-
-    CloseFileSelector();
-
-    if (!eventData["OK"].GetBool())
-    {
-        resourcePickIDs.Clear();
-        @resourcePicker = null;
-        return;
-    }
-
-    if (resourcePicker is null)
-        return;
-
-    // Validate the resource. It must come from within a registered resource directory, and be loaded successfully
-    String resourceName = eventData["FileName"].GetString();
-    Array<String>@ resourceDirs = cache.resourceDirs;
-    Resource@ res;
-    for (uint i = 0; i < resourceDirs.length; ++i)
-    {
-        if (!resourceName.ToLower().StartsWith(resourceDirs[i].ToLower()))
-            continue;
-        resourceName = resourceName.Substring(resourceDirs[i].length);
-        res = cache.GetResource(resourcePicker.resourceType, resourceName);
-        break;
-    }
-    if (res is null)
-        return;
-
-    for (uint i = 0; i < resourcePickIDs.length; ++i)
-    {
-        Component@ target = editorScene.GetComponent(resourcePickIDs[i]);
-
-        AttributeInfo info = target.attributeInfos[resourcePickIndex];
-        if (info.type == VAR_RESOURCEREF)
-        {
-            ResourceRef ref = target.attributes[resourcePickIndex].GetResourceRef();
-            ref.type = ShortStringHash(resourcePicker.resourceType);
-            ref.id = StringHash(resourceName);
-            target.attributes[resourcePickIndex] = Variant(ref);
-            target.ApplyAttributes();
-        }
-        else if (info.type == VAR_RESOURCEREFLIST)
-        {
-            ResourceRefList refList = target.attributes[resourcePickIndex].GetResourceRefList();
-            if (resourcePickSubIndex < refList.length)
-            {
-                refList.ids[resourcePickSubIndex] = StringHash(resourceName);
-                target.attributes[resourcePickIndex] = Variant(refList);
-                target.ApplyAttributes();
-            }
-        }
-        else if (info.type == VAR_VARIANTVECTOR)
-        {
-            Array<Variant>@ attrs = target.attributes[resourcePickIndex].GetVariantVector();
-            ResourceRef ref = attrs[resourcePickSubIndex].GetResourceRef();
-            ref.type = ShortStringHash(resourcePicker.resourceType);
-            ref.id = StringHash(resourceName);
-            attrs[resourcePickSubIndex] = ref;
-            target.attributes[resourcePickIndex] = Variant(attrs);
-            target.ApplyAttributes();
-        }
-    }
-
-    UpdateAttributes(false);
-
-    resourcePickIDs.Clear();
-    @resourcePicker = null;
-}
-
-void PickResourceCanceled()
-{
-    if (!resourcePickIDs.empty)
-    {
-        resourcePickIDs.Clear();
-        @resourcePicker = null;
-        CloseFileSelector();
-    }
-}
-
-void OpenResource(StringHash eventType, VariantMap& eventData)
-{
-    UIElement@ button = eventData["Element"].GetUIElement();
-    LineEdit@ attrEdit = button.parent.children[0];
-    
-    String fileName = attrEdit.text.Trimmed();
-    if (fileName.empty)
-        return;
-
-    Array<String>@ resourceDirs = cache.resourceDirs;
-    for (uint i = 0; i < resourceDirs.length; ++i)
-    {
-        String fullPath = resourceDirs[i] + fileName;
-        if (fileSystem.FileExists(fullPath))
-        {
-            fileSystem.SystemOpen(fullPath, "");
-            return;
-        }
-    }
-}
 
 void CreateNewVariable(StringHash eventType, VariantMap& eventData)
 {

+ 0 - 1
Bin/Data/Scripts/Editor/EditorScene.as

@@ -12,7 +12,6 @@ const int MAX_PICK_MODES = 4;
 Scene@ editorScene;
 
 String sceneFileName;
-String sceneResourcePath = fileSystem.programDir + "Data";
 String instantiateFileName;
 CreateMode instantiateMode = REPLICATED;
 bool sceneModified = false;

+ 1 - 1
Bin/Data/Scripts/Editor/EditorUI.as

@@ -63,7 +63,7 @@ void ResizeUI()
     
     // Relayout windows
     Array<UIElement@> children = ui.root.GetChildren();
-    for (int i = 0; i < children.length; ++i)
+    for (uint i = 0; i < children.length; ++i)
     {
         if (children[i].type == windowType)
             AdjustPosition(children[i]);