Browse Source

Laying the groundwork in the Attribute Inspector for showing the UIElement's attributes (still need more work). Enhanced UI subsystem to support modal element, currently only support modal Window. Exposed a new Variant readonly property to test for 'empty' variant. New UIElement's method to get a child by matching the child's user-defined variant map and exposed it to script, also exposed the existing GetVar() method to script.

Wei Tjong Yao 12 years ago
parent
commit
2bf7facaa7

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

@@ -48,7 +48,7 @@ void Start()
     // Create user interface for the editor
     // Create user interface for the editor
     CreateUI();
     CreateUI();
     // Create root UI element where all 'editable' UI elements would be parented to
     // Create root UI element where all 'editable' UI elements would be parented to
-    CreateUIElement();
+    CreateRootUIElement();
     // Load the initial scene if provided
     // Load the initial scene if provided
     ParseArguments();
     ParseArguments();
 }
 }

+ 30 - 50
Bin/Data/Scripts/Editor/AttributeEditor.as

@@ -115,7 +115,7 @@ LineEdit@ CreateAttributeLineEdit(UIElement@ parent, Array<Serializable@>@ seria
     attrEdit.vars["SubIndex"] = subIndex;
     attrEdit.vars["SubIndex"] = subIndex;
     SetAttributeEditorID(attrEdit, serializables);
     SetAttributeEditorID(attrEdit, serializables);
     parent.AddChild(attrEdit);
     parent.AddChild(attrEdit);
-    
+
     return attrEdit;
     return attrEdit;
 }
 }
 
 
@@ -126,7 +126,7 @@ UIElement@ CreateStringAttributeEditor(ListView@ list, Array<Serializable@>@ ser
     attrEdit.dragDropMode = DD_TARGET;
     attrEdit.dragDropMode = DD_TARGET;
     SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
     SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
     SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
     SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
-    
+
     return parent;
     return parent;
 }
 }
 
 
@@ -137,7 +137,7 @@ UIElement@ CreateBoolAttributeEditor(ListView@ list, Array<Serializable@>@ seria
         parent = CreateAttributeEditorParentAsListChild(list, info.name, index, subIndex);
         parent = CreateAttributeEditorParentAsListChild(list, info.name, index, subIndex);
     else
     else
         parent = CreateAttributeEditorParent(list, info.name, index, subIndex);
         parent = CreateAttributeEditorParent(list, info.name, index, subIndex);
-    
+
     CheckBox@ attrEdit = CheckBox();
     CheckBox@ attrEdit = CheckBox();
     attrEdit.style = uiStyle;
     attrEdit.style = uiStyle;
     attrEdit.SetFixedSize(16, 16);
     attrEdit.SetFixedSize(16, 16);
@@ -146,7 +146,7 @@ UIElement@ CreateBoolAttributeEditor(ListView@ list, Array<Serializable@>@ seria
     SetAttributeEditorID(attrEdit, serializables);
     SetAttributeEditorID(attrEdit, serializables);
     parent.AddChild(attrEdit);
     parent.AddChild(attrEdit);
     SubscribeToEvent(attrEdit, "Toggled", "EditAttribute");
     SubscribeToEvent(attrEdit, "Toggled", "EditAttribute");
-    
+
     return parent;
     return parent;
 }
 }
 
 
@@ -161,7 +161,7 @@ UIElement@ CreateNumAttributeEditor(ListView@ list, Array<Serializable@>@ serial
         numCoords = 4;
         numCoords = 4;
     else if (type == VAR_INTVECTOR2)
     else if (type == VAR_INTVECTOR2)
         numCoords = 2;
         numCoords = 2;
-    
+
     for (uint i = 0; i < numCoords; ++i)
     for (uint i = 0; i < numCoords; ++i)
     {
     {
         LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
         LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
@@ -169,8 +169,8 @@ UIElement@ CreateNumAttributeEditor(ListView@ list, Array<Serializable@>@ serial
         SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
         SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
         SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
         SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
     }
     }
-    
-    return parent;    
+
+    return parent;
 }
 }
 
 
 UIElement@ CreateIntAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const AttributeInfo&in info, uint index, uint subIndex)
 UIElement@ CreateIntAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const AttributeInfo&in info, uint index, uint subIndex)
@@ -228,9 +228,9 @@ UIElement@ CreateResourceRefAttributeEditor(ListView@ list, Array<Serializable@>
     container.SetLayout(LM_HORIZONTAL, 4, IntRect(info.name.StartsWith("   ") ? 20 : 10, 0, 4, 0));    // Left margin is indented more when the name is so
     container.SetLayout(LM_HORIZONTAL, 4, IntRect(info.name.StartsWith("   ") ? 20 : 10, 0, 4, 0));    // Left margin is indented more when the name is so
     container.SetFixedHeight(ATTR_HEIGHT);
     container.SetFixedHeight(ATTR_HEIGHT);
     parent.AddChild(container);
     parent.AddChild(container);
-        
+
     LineEdit@ attrEdit = CreateAttributeLineEdit(container, serializables, index, subIndex);
     LineEdit@ attrEdit = CreateAttributeLineEdit(container, serializables, index, subIndex);
-    attrEdit.vars["Type"] = resourceType.value;
+    attrEdit.vars[TYPE_VAR] = resourceType.value;
     SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
     SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
 
 
     Button@ pickButton = Button();
     Button@ pickButton = Button();
@@ -262,14 +262,14 @@ UIElement@ CreateResourceRefAttributeEditor(ListView@ list, Array<Serializable@>
     openButton.AddChild(openButtonText);
     openButton.AddChild(openButtonText);
     container.AddChild(openButton);
     container.AddChild(openButton);
     SubscribeToEvent(openButton, "Released", "OpenResource");
     SubscribeToEvent(openButton, "Released", "OpenResource");
-    
+
     return parent;
     return parent;
 }
 }
 
 
 UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const AttributeInfo&in info, uint index, uint subIndex, bool suppressedSeparatedLabel = false)
 UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const AttributeInfo&in info, uint index, uint subIndex, bool suppressedSeparatedLabel = false)
 {
 {
     UIElement@ parent;
     UIElement@ parent;
-    
+
     VariantType type = info.type;
     VariantType type = info.type;
     if (type == VAR_STRING || type == VAR_BUFFER)
     if (type == VAR_STRING || type == VAR_BUFFER)
         parent = CreateStringAttributeEditor(list, serializables, info, index, subIndex);
         parent = CreateStringAttributeEditor(list, serializables, info, index, subIndex);
@@ -284,7 +284,7 @@ UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializa
     else if (type == VAR_RESOURCEREFLIST)
     else if (type == VAR_RESOURCEREFLIST)
     {
     {
         uint numRefs = serializables[0].attributes[index].GetResourceRefList().length;
         uint numRefs = serializables[0].attributes[index].GetResourceRefList().length;
-        
+
         // Straightly speaking the individual resource reference in the list is not an attribute of the serializable
         // Straightly speaking the individual resource reference in the list is not an attribute of the serializable
         // However, the AttributeInfo structure is used here to reduce the number of parameters being passed in the function
         // However, the AttributeInfo structure is used here to reduce the number of parameters being passed in the function
         AttributeInfo refInfo;
         AttributeInfo refInfo;
@@ -320,7 +320,7 @@ UIElement@ CreateAttributeEditor(ListView@ list, Array<Serializable@>@ serializa
         for (uint i = 0; i < keys.length; ++i)
         for (uint i = 0; i < keys.length; ++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
             AttributeInfo mapInfo;
             AttributeInfo mapInfo;
             mapInfo.name = scene.GetVarName(keys[i]) + " (Var)";
             mapInfo.name = scene.GetVarName(keys[i]) + " (Var)";
@@ -371,7 +371,7 @@ UIElement@ GetAttributeEditorParent(UIElement@ parent, uint index, uint subIndex
 void LoadAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const AttributeInfo&in info, uint index)
 void LoadAttributeEditor(ListView@ list, Array<Serializable@>@ serializables, const AttributeInfo&in info, uint index)
 {
 {
     bool editable = info.mode & AM_NOEDIT == 0;
     bool editable = info.mode & AM_NOEDIT == 0;
-    
+
     UIElement@ parent = GetAttributeEditorParent(list, index, 0);
     UIElement@ parent = GetAttributeEditorParent(list, index, 0);
     if (parent is null)
     if (parent is null)
         return;
         return;
@@ -407,13 +407,13 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
     {
     {
         bool modified;
         bool modified;
         if (info.defaultValue.type == VAR_NONE || info.defaultValue.type == VAR_RESOURCEREFLIST)
         if (info.defaultValue.type == VAR_NONE || info.defaultValue.type == VAR_RESOURCEREFLIST)
-            modified = !value.IsZero();
+            modified = !value.zero;
         else
         else
             modified = value != info.defaultValue;
             modified = value != info.defaultValue;
         cast<Text>(label).color = (editable ? (modified ? modifiedTextColor : normalTextColor) : nonEditableTextColor);
         cast<Text>(label).color = (editable ? (modified ? modifiedTextColor : normalTextColor) : nonEditableTextColor);
     }
     }
-    
-    VariantType type = info.type;    
+
+    VariantType type = info.type;
     if (type == VAR_FLOAT || type == VAR_STRING || type == VAR_BUFFER)
     if (type == VAR_FLOAT || type == VAR_STRING || type == VAR_BUFFER)
         SetEditable(SetValue(parent.children[1], value.ToString(), sameValue), editable && sameValue);
         SetEditable(SetValue(parent.children[1], value.ToString(), sameValue), editable && sameValue);
     else if (type == VAR_BOOL)
     else if (type == VAR_BOOL)
@@ -440,7 +440,7 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
             parent = GetAttributeEditorParent(list, index, subIndex);
             parent = GetAttributeEditorParent(list, index, subIndex);
             if (parent is null)
             if (parent is null)
                 break;
                 break;
-            
+
             StringHash firstID = refList.ids[subIndex];
             StringHash firstID = refList.ids[subIndex];
             bool idSameValue = true;
             bool idSameValue = true;
             if (!sameValue)
             if (!sameValue)
@@ -468,7 +468,7 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
             parent = GetAttributeEditorParent(list, index, subIndex);
             parent = GetAttributeEditorParent(list, index, subIndex);
             if (parent is null)
             if (parent is null)
                 break;
                 break;
-            
+
             Variant firstValue = vector[subIndex];
             Variant firstValue = vector[subIndex];
             bool varSameValue = true;
             bool varSameValue = true;
             if (!sameValue)
             if (!sameValue)
@@ -484,7 +484,7 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
                     }
                     }
                 }
                 }
             }
             }
-            
+
             // The individual variant in the list is not an attribute of the serializable, the structure is reused for convenience
             // The individual variant in the list is not an attribute of the serializable, the structure is reused for convenience
             AttributeInfo info;
             AttributeInfo info;
             info.type = firstValue.type;
             info.type = firstValue.type;
@@ -501,7 +501,7 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
             parent = GetAttributeEditorParent(list, index, subIndex);
             parent = GetAttributeEditorParent(list, index, subIndex);
             if (parent is null)
             if (parent is null)
                 break;
                 break;
-            
+
             Variant firstValue = map[keys[subIndex]];
             Variant firstValue = map[keys[subIndex]];
             bool varSameValue = true;
             bool varSameValue = true;
             if (!sameValue)
             if (!sameValue)
@@ -517,7 +517,7 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
                     }
                     }
                 }
                 }
             }
             }
-            
+
             // 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
             AttributeInfo info;
             AttributeInfo info;
             info.type = firstValue.type;
             info.type = firstValue.type;
@@ -530,11 +530,11 @@ void LoadAttributeEditor(UIElement@ parent, const Variant&in value, const Attrib
         for (uint i = 0; i < values.length; ++i)
         for (uint i = 0; i < values.length; ++i)
         {
         {
             Variant value = values[i];
             Variant value = values[i];
-            
+
             // Convert Quaternion value to Vector3 value first
             // Convert Quaternion value to Vector3 value first
             if (type == VAR_QUATERNION)
             if (type == VAR_QUATERNION)
                 value = value.GetQuaternion().eulerAngles;
                 value = value.GetQuaternion().eulerAngles;
-            
+
             coordinates.Push(value.ToString().Split(' '));
             coordinates.Push(value.ToString().Split(' '));
         }
         }
         for (uint i = 0; i < coordinates[0].length; ++i)
         for (uint i = 0; i < coordinates[0].length; ++i)
@@ -660,7 +660,7 @@ void GetEditorValue(UIElement@ parent, VariantType type, Array<String>@ enumName
         LineEdit@ attrEdit = parent.children[0];
         LineEdit@ attrEdit = parent.children[0];
         ResourceRef ref;
         ResourceRef ref;
         ref.id = StringHash(attrEdit.text.Trimmed());
         ref.id = StringHash(attrEdit.text.Trimmed());
-        ref.type = ShortStringHash(attrEdit.vars["Type"].GetUInt());
+        ref.type = ShortStringHash(attrEdit.vars[TYPE_VAR].GetUInt());
         FillValue(values, Variant(ref));
         FillValue(values, Variant(ref));
     }
     }
     else
     else
@@ -698,16 +698,6 @@ void UpdateAttributes(Array<Serializable@>@ serializables, ListView@ list, bool
             if (!children[i].internal)
             if (!children[i].internal)
                 children[i].Remove();
                 children[i].Remove();
         }
         }
-
-        //\todo Remove the hardcoding
-        // Resize the node editor according to the number of variables, up to a certain maximum
-        if (list.name == "NodeAttributeList")
-        {
-            if ((editNode !is null && editNode.typeName == "Node") || (editNodes.length > 0 && editNodes[0].typeName == "Node"))
-                --count;	// The 'Is Enabled' attribute is not inserted as list item
-            uint maxAttrs = Clamp(count, MIN_NODE_ATTRIBUTES, MAX_NODE_ATTRIBUTES);
-            list.SetFixedHeight(maxAttrs * ATTR_HEIGHT + 2);
-        }
     }
     }
 
 
     if (serializables.empty)
     if (serializables.empty)
@@ -725,7 +715,7 @@ void UpdateAttributes(Array<Serializable@>@ serializables, ListView@ list, bool
 
 
         LoadAttributeEditor(list, serializables, info, i);
         LoadAttributeEditor(list, serializables, info, i);
     }
     }
-    
+
     if (fullUpdate)
     if (fullUpdate)
         list.viewPosition = oldViewPos;
         list.viewPosition = oldViewPos;
 }
 }
@@ -757,7 +747,7 @@ void EditAttribute(StringHash eventType, VariantMap& eventData)
     // 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)
-        UpdateAttributes(false);
+        UpdateAttributeInspector(false);
 }
 }
 
 
 // Resource picker functionality
 // Resource picker functionality
@@ -910,7 +900,7 @@ void PickResourceDone(StringHash eventType, VariantMap& eventData)
         break;
         break;
     }
     }
     if (res is null) {
     if (res is null) {
-        Print("Can not find resource type: " + resourcePicker.resourceType + " Name:" +resourceName);
+        log.Warning("Cannot find resource type: " + resourcePicker.resourceType + " Name:" +resourceName);
         return;
         return;
     }
     }
 
 
@@ -950,27 +940,17 @@ void PickResourceDone(StringHash eventType, VariantMap& eventData)
     }
     }
 
 
     PostEditAttribute(resourceTargets, resourcePickIndex);
     PostEditAttribute(resourceTargets, resourcePickIndex);
-    UpdateAttributes(false);
+    UpdateAttributeInspector(false);
 
 
     resourceTargets.Clear();
     resourceTargets.Clear();
     @resourcePicker = null;
     @resourcePicker = null;
 }
 }
 
 
-void PickResourceCanceled()
-{
-    if (!resourceTargets.empty)
-    {
-        resourceTargets.Clear();
-        @resourcePicker = null;
-        CloseFileSelector();
-    }
-}
-
 void OpenResource(StringHash eventType, VariantMap& eventData)
 void OpenResource(StringHash eventType, VariantMap& eventData)
 {
 {
     UIElement@ button = eventData["Element"].GetUIElement();
     UIElement@ button = eventData["Element"].GetUIElement();
     LineEdit@ attrEdit = button.parent.children[0];
     LineEdit@ attrEdit = button.parent.children[0];
-    
+
     String fileName = attrEdit.text.Trimmed();
     String fileName = attrEdit.text.Trimmed();
     if (fileName.empty)
     if (fileName.empty)
         return;
         return;

+ 13 - 7
Bin/Data/Scripts/Editor/EditorGizmo.as

@@ -31,6 +31,13 @@ class GizmoAxis
 
 
     void Update(Ray cameraRay, float scale, bool drag)
     void Update(Ray cameraRay, float scale, bool drag)
     {
     {
+        // Do not select when UI has modal element
+        if (ui.modalElement !is null)
+        {
+            selected = false;
+            return;
+        }
+        
         Vector3 closest = cameraRay.ClosestPoint(axisRay);
         Vector3 closest = cameraRay.ClosestPoint(axisRay);
         Vector3 projected = axisRay.Project(closest);
         Vector3 projected = axisRay.Project(closest);
         d = axisRay.Distance(closest);
         d = axisRay.Distance(closest);
@@ -49,7 +56,7 @@ class GizmoAxis
             lastD = d;
             lastD = d;
         }
         }
     }
     }
-    
+
     void Moved()
     void Moved()
     {
     {
         lastT = t;
         lastT = t;
@@ -98,7 +105,6 @@ void ShowGizmo()
     }
     }
 }
 }
 
 
-
 void UpdateGizmo()
 void UpdateGizmo()
 {
 {
     UseGizmo();
     UseGizmo();
@@ -270,7 +276,7 @@ void UseGizmo()
 
 
             moved = ScaleNodes(adjust);
             moved = ScaleNodes(adjust);
         }
         }
-        
+
         if (moved)
         if (moved)
         {
         {
             GizmoMoved();
             GizmoMoved();
@@ -330,7 +336,7 @@ bool MoveNodes(Vector3 adjust)
                 moved = true;
                 moved = true;
         }
         }
     }
     }
-    
+
     return moved;
     return moved;
 }
 }
 
 
@@ -368,7 +374,7 @@ bool RotateNodes(Vector3 adjust)
             }
             }
         }
         }
     }
     }
-    
+
     return moved;
     return moved;
 }
 }
 
 
@@ -384,7 +390,7 @@ bool ScaleNodes(Vector3 adjust)
 
 
             Vector3 scale = node.scale;
             Vector3 scale = node.scale;
             Vector3 oldScale = scale;
             Vector3 oldScale = scale;
-            
+
             if (!scaleSnap)
             if (!scaleSnap)
                 scale += adjust;
                 scale += adjust;
             else
             else
@@ -412,6 +418,6 @@ bool ScaleNodes(Vector3 adjust)
             node.scale = scale;
             node.scale = scale;
         }
         }
     }
     }
-    
+
     return moved;
     return moved;
 }
 }

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

@@ -336,7 +336,7 @@ void ImportTundraScene(const String&in fileName)
             childNode.parent = parentNode;
             childNode.parent = parentNode;
     }
     }
 
 
-    UpdateHierarchyWindowItem(editorScene, true);
+    UpdateHierarchyItem(editorScene, true);
     UpdateWindowTitle();
     UpdateWindowTitle();
     assetMappings.Clear();
     assetMappings.Clear();
 }
 }

+ 147 - 125
Bin/Data/Scripts/Editor/EditorNodeWindow.as

@@ -1,93 +1,131 @@
 // Urho3D editor attribute inspector window handling
 // Urho3D editor attribute inspector window handling
 #include "Scripts/Editor/AttributeEditor.as"
 #include "Scripts/Editor/AttributeEditor.as"
 
 
-Window@ nodeWindow;
+Window@ attributeInspectorWindow;
+UIElement@ parentContainer;
 UIElement@ componentParentContainer;
 UIElement@ componentParentContainer;
+UIElement@ nodeContainer;
+XMLFile@ nodeXMLResource;
 XMLFile@ componentXMLResource;
 XMLFile@ componentXMLResource;
 
 
 bool applyMaterialList = true;
 bool applyMaterialList = true;
 bool attributesDirty = false;
 bool attributesDirty = false;
 
 
 const String STRIKED_OUT = "——";   // Two unicode EM DASH (U+2014)
 const String STRIKED_OUT = "——";   // Two unicode EM DASH (U+2014)
+const ShortStringHash NODE_IDS_VAR("NodeIDs");
+const ShortStringHash COMPONENT_IDS_VAR("ComponentIDs");
+
+uint componentContainerStartIndex = 0;
+
+void AddNodeContainer()
+{
+    if (nodeContainer !is null)
+        return;
+
+    uint index = parentContainer.numChildren;
+    parentContainer.LoadXML(nodeXMLResource, uiStyle);
+    nodeContainer = GetContainer(index);
+    SubscribeToEvent(nodeContainer.GetChild("NewVarDropDown", true), "ItemSelected", "CreateNewVariable");
+    SubscribeToEvent(nodeContainer.GetChild("DeleteVarButton", true), "Released", "DeleteVariable");
+    ++componentContainerStartIndex;
+}
 
 
 void AddComponentContainer()
 void AddComponentContainer()
 {
 {
-    componentParentContainer.LoadXML(componentXMLResource, uiStyle);
+    parentContainer.LoadXML(componentXMLResource, uiStyle);
+}
+
+void DeleteAllContainers()
+{
+    parentContainer.RemoveAllChildren();
+    nodeContainer = null;
+    componentContainerStartIndex = 0;
 }
 }
 
 
-void DeleteAllComponentContainers()
+UIElement@ GetContainer(uint index)
 {
 {
-    componentParentContainer.RemoveAllChildren();
+    return parentContainer.children[index];
 }
 }
 
 
 UIElement@ GetComponentContainer(uint index)
 UIElement@ GetComponentContainer(uint index)
 {
 {
-    return componentParentContainer.children[index];
+    return parentContainer.children[componentContainerStartIndex + index];
 }
 }
 
 
-void CreateNodeWindow()
+void CreateAttributeInspectorWindow()
 {
 {
-    if (nodeWindow !is null)
+    if (attributeInspectorWindow !is null)
         return;
         return;
 
 
     InitResourcePicker();
     InitResourcePicker();
     InitVectorStructs();
     InitVectorStructs();
 
 
-    nodeWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorNodeWindow.xml"));
+    attributeInspectorWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorNodeWindow.xml"));
+    nodeXMLResource = cache.GetResource("XMLFile", "UI/EditorNode.xml");
     componentXMLResource = cache.GetResource("XMLFile", "UI/EditorComponent.xml");
     componentXMLResource = cache.GetResource("XMLFile", "UI/EditorComponent.xml");
-    componentParentContainer = nodeWindow.GetChild("ComponentParentContainer", true);
-    AddComponentContainer();
-    ui.root.AddChild(nodeWindow);
+    parentContainer = attributeInspectorWindow.GetChild("ParentContainer");
+    ui.root.AddChild(attributeInspectorWindow);
     int height = Min(ui.root.height - 60, 500);
     int height = Min(ui.root.height - 60, 500);
-    nodeWindow.SetSize(300, height);
-    nodeWindow.SetPosition(ui.root.width - 20 - nodeWindow.width, 40);
-    nodeWindow.opacity = uiMaxOpacity;
-    nodeWindow.BringToFront();
-    UpdateNodeWindow();
-
-    SubscribeToEvent(nodeWindow.GetChild("CloseButton", true), "Released", "HideNodeWindow");
-    SubscribeToEvent(nodeWindow.GetChild("NewVarDropDown", true), "ItemSelected", "CreateNewVariable");
-    SubscribeToEvent(nodeWindow.GetChild("DeleteVarButton", true), "Released", "DeleteVariable");
-    SubscribeToEvent(nodeWindow, "LayoutUpdated", "HandleWindowLayoutUpdated");
+    attributeInspectorWindow.SetSize(300, height);
+    attributeInspectorWindow.SetPosition(ui.root.width - 20 - attributeInspectorWindow.width, 40);
+    attributeInspectorWindow.opacity = uiMaxOpacity;
+    attributeInspectorWindow.BringToFront();
+    UpdateAttributeInspector();
+
+    SubscribeToEvent(attributeInspectorWindow.GetChild("CloseButton", true), "Released", "HideAttributeInspectorWindow");
+    SubscribeToEvent(attributeInspectorWindow, "LayoutUpdated", "HandleWindowLayoutUpdated");
 }
 }
 
 
-void HideNodeWindow()
+void HideAttributeInspectorWindow()
 {
 {
-    nodeWindow.visible = false;
+    attributeInspectorWindow.visible = false;
 }
 }
 
 
-bool ShowNodeWindow()
+bool ShowAttributeInspectorWindow()
 {
 {
-    nodeWindow.visible = true;
-    nodeWindow.BringToFront();
+    attributeInspectorWindow.visible = true;
+    attributeInspectorWindow.BringToFront();
     return true;
     return true;
 }
 }
 
 
-void AdjustListViewChild(ListView@ list)
+void HandleWindowLayoutUpdated()
 {
 {
-    // At the moment, only 'Is Enabled' container (place-holder + check box) is being created as child of the list view instead of as list item
-    int width = list.width;
-    for (uint i = 0; i < list.numChildren; ++i)
+    for (uint i = 0; i < parentContainer.numChildren; ++i)
     {
     {
-        UIElement@ element = list.children[i];
-        if (!element.internal)
-            element.SetFixedWidth(width);
+        ListView@ list = GetContainer(i).GetChild("AttributeList");
+
+        // At the moment, only 'Is Enabled' container (place-holder + check box) is being created as child of the list view instead of as list item
+        // When window resize and so the list's width is changed, adjust the 'Is enabled' container width so that check box stays at the right most position
+        int width = list.width;
+        for (uint i = 0; i < list.numChildren; ++i)
+        {
+            UIElement@ element = list.children[i];
+            if (!element.internal)
+                element.SetFixedWidth(width);
+        }
     }
     }
 }
 }
 
 
-void HandleWindowLayoutUpdated()
+Array<Serializable@> ToSerializableArray(Array<Node@> nodes)
 {
 {
-    AdjustListViewChild(nodeWindow.GetChild("NodeAttributeList"));
-    for (uint i = 0; i < componentParentContainer.numChildren; ++i)
-        AdjustListViewChild(GetComponentContainer(i).GetChild("ComponentAttributeList"));
+    Array<Serializable@> serializables;
+    for (uint i = 0; i < nodes.length; ++i)
+        serializables.Push(nodes[i]);
+    return serializables;
 }
 }
 
 
-void UpdateNodeWindow()
+void UpdateAttributeInspector(bool fullUpdate = true)
 {
 {
-    // If a resource pick was in progress, it cannot be completed now, as component was changed
-    PickResourceCanceled();
+    attributesDirty = false;
 
 
-    Text@ nodeTitle = nodeWindow.GetChild("NodeTitle", true);
+    if (fullUpdate)
+    {
+        DeleteAllContainers();
+        AddNodeContainer();
+        AddComponentContainer();
+    }
+
+    Text@ nodeTitle = nodeContainer.GetChild("NodeTitle");
     String nodeType;
     String nodeType;
     if (editNodes.length == 0)
     if (editNodes.length == 0)
         nodeTitle.text = "No node";
         nodeTitle.text = "No node";
@@ -108,82 +146,65 @@ void UpdateNodeWindow()
     }
     }
     IconizeUIElement(nodeTitle, nodeType);
     IconizeUIElement(nodeTitle, nodeType);
 
 
-    UpdateAttributes(true);
-}
+    ListView@ list = nodeContainer.GetChild("AttributeList");
+    UpdateAttributes(ToSerializableArray(editNodes), list, fullUpdate);
 
 
-Array<Serializable@> ToSerializableArray(Array<Node@> nodes)
-{
-    Array<Serializable@> serializables;
-    for (uint i = 0; i < nodes.length; ++i)
-        serializables.Push(nodes[i]);
-    return serializables;
-}
+    if (fullUpdate)
+    {
+        //\TODO Avoid hardcoding
+        // Resize the node editor according to the number of variables, up to a certain maximum
+        uint maxAttrs = Clamp(list.contentElement.numChildren, MIN_NODE_ATTRIBUTES, MAX_NODE_ATTRIBUTES);
+        list.SetFixedHeight(maxAttrs * ATTR_HEIGHT + 2);
+        nodeContainer.SetFixedHeight(maxAttrs * ATTR_HEIGHT + 60);
+    }
 
 
-void UpdateAttributes(bool fullUpdate)
-{
-    attributesDirty = false;
+    if (editComponents.empty)
+    {
+        Text@ componentTitle = GetComponentContainer(0).GetChild("ComponentTitle");
+        if (selectedComponents.length <= 1)
+            componentTitle.text = "No component";
+        else
+            componentTitle.text = selectedComponents.length + " components";
 
 
-    if (nodeWindow !is null)
+        // Ensure there is no icon
+        IconizeUIElement(componentTitle, "");
+    }
+    else
     {
     {
-        UpdateAttributes(ToSerializableArray(editNodes), nodeWindow.GetChild("NodeAttributeList", true), fullUpdate);
+        uint numEditableComponents = editComponents.length / numEditableComponentsPerNode;
+        String multiplierText;
+        if (numEditableComponents > 1)
+            multiplierText = " (" + numEditableComponents + "x)";
 
 
-        if (fullUpdate)
-            DeleteAllComponentContainers();
-        
-        if (editComponents.empty)
+        for (uint j = 0; j < numEditableComponentsPerNode; ++j)
         {
         {
-            if (componentParentContainer.numChildren == 0)
+            if (j >= parentContainer.numChildren - componentContainerStartIndex)
                 AddComponentContainer();
                 AddComponentContainer();
-            
-            Text@ componentTitle = GetComponentContainer(0).GetChild("ComponentTitle");
-            if (selectedComponents.length <= 1)
-                componentTitle.text = "No component";
-            else
-                componentTitle.text = selectedComponents.length + " components";
-            
-            // Ensure there is no icon
-            IconizeUIElement(componentTitle, "");
-        }
-        else
-        {
-            uint numEditableComponents = editComponents.length / numEditableComponentsPerNode;
-            String multiplierText;
-            if (numEditableComponents > 1)
-                multiplierText = " (" + numEditableComponents + "x)";
-            
-            for (uint j = 0; j < numEditableComponentsPerNode; ++j)
-            {
-                if (j >= componentParentContainer.numChildren)
-                    AddComponentContainer();
-                
-                Text@ componentTitle = GetComponentContainer(j).GetChild("ComponentTitle");
-                componentTitle.text = GetComponentTitle(editComponents[j * numEditableComponents]) + multiplierText;
-                IconizeUIElement(componentTitle, editComponents[j * numEditableComponents].typeName);
-                SetIconEnabledColor(componentTitle, editComponents[j * numEditableComponents].enabledEffective);
-
-                Array<Serializable@> components;
-                for (uint i = 0; i < numEditableComponents; ++i)
-                    components.Push(editComponents[j * numEditableComponents + i]);
-                
-                UpdateAttributes(components, GetComponentContainer(j).GetChild("ComponentAttributeList"), fullUpdate);
-            }
-        }
 
 
-        UpdateNodeWindowIcons();
+            Text@ componentTitle = GetComponentContainer(j).GetChild("ComponentTitle");
+            componentTitle.text = GetComponentTitle(editComponents[j * numEditableComponents]) + multiplierText;
+            IconizeUIElement(componentTitle, editComponents[j * numEditableComponents].typeName);
+            SetIconEnabledColor(componentTitle, editComponents[j * numEditableComponents].enabledEffective);
+
+            Array<Serializable@> components;
+            for (uint i = 0; i < numEditableComponents; ++i)
+                components.Push(editComponents[j * numEditableComponents + i]);
+
+            UpdateAttributes(components, GetComponentContainer(j).GetChild("AttributeList"), fullUpdate);
+        }
     }
     }
+
+    UpdateAttributeInspectorIcons();
 }
 }
 
 
 void UpdateNodeAttributes()
 void UpdateNodeAttributes()
 {
 {
-    if (nodeWindow !is null)
-    {
-        UpdateAttributes(ToSerializableArray(editNodes), nodeWindow.GetChild("NodeAttributeList", true), false);
-    }
+    UpdateAttributes(ToSerializableArray(editNodes), nodeContainer.GetChild("AttributeList"), false);
 }
 }
 
 
-void UpdateNodeWindowIcons()
+void UpdateAttributeInspectorIcons()
 {
 {
-    Text@ nodeTitle = nodeWindow.GetChild("NodeTitle", true);
+    Text@ nodeTitle = attributeInspectorWindow.GetChild("NodeTitle", true);
     if (editNode !is null)
     if (editNode !is null)
         SetIconEnabledColor(nodeTitle, editNode.enabled);
         SetIconEnabledColor(nodeTitle, editNode.enabled);
     else if (editNodes.length > 0)
     else if (editNodes.length > 0)
@@ -198,21 +219,18 @@ void UpdateNodeWindowIcons()
                 break;
                 break;
             }
             }
         }
         }
-        
+
         SetIconEnabledColor(nodeTitle, editNodes[0].enabled, !hasSameEnabledState);
         SetIconEnabledColor(nodeTitle, editNodes[0].enabled, !hasSameEnabledState);
     }
     }
 
 
     if (!editComponents.empty)
     if (!editComponents.empty)
     {
     {
         uint numEditableComponents = editComponents.length / numEditableComponentsPerNode;
         uint numEditableComponents = editComponents.length / numEditableComponentsPerNode;
-        
+
         for (uint j = 0; j < numEditableComponentsPerNode; ++j)
         for (uint j = 0; j < numEditableComponentsPerNode; ++j)
         {
         {
-            if (j >= componentParentContainer.numChildren)
-                return;
-            
             Text@ componentTitle = GetComponentContainer(j).GetChild("ComponentTitle");
             Text@ componentTitle = GetComponentContainer(j).GetChild("ComponentTitle");
-            
+
             bool enabledEffective = editComponents[j * numEditableComponents].enabledEffective;
             bool enabledEffective = editComponents[j * numEditableComponents].enabledEffective;
             bool hasSameEnabledState = true;
             bool hasSameEnabledState = true;
             for (uint i = 1; i < numEditableComponents; ++i)
             for (uint i = 1; i < numEditableComponents; ++i)
@@ -223,7 +241,7 @@ void UpdateNodeWindowIcons()
                     break;
                     break;
                 }
                 }
             }
             }
-            
+
             SetIconEnabledColor(componentTitle, enabledEffective, !hasSameEnabledState);
             SetIconEnabledColor(componentTitle, enabledEffective, !hasSameEnabledState);
         }
         }
     }
     }
@@ -252,23 +270,23 @@ void SetAttributeEditorID(UIElement@ attrEdit, Array<Serializable@>@ serializabl
     {
     {
         for (uint i = 0; i < serializables.length; ++i)
         for (uint i = 0; i < serializables.length; ++i)
             ids.Push(Variant(cast<Node>(serializables[i]).id));
             ids.Push(Variant(cast<Node>(serializables[i]).id));
-        attrEdit.vars["NodeIDs"] = ids;
+        attrEdit.vars[NODE_IDS_VAR] = ids;
     }
     }
     else
     else
     {
     {
         for (uint i = 0; i < serializables.length; ++i)
         for (uint i = 0; i < serializables.length; ++i)
             ids.Push(Variant(cast<Component>(serializables[i]).id));
             ids.Push(Variant(cast<Component>(serializables[i]).id));
-        attrEdit.vars["ComponentIDs"] = ids;
+        attrEdit.vars[COMPONENT_IDS_VAR] = ids;
     }
     }
 }
 }
 
 
 Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit)
 Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit)
 {
 {
     Array<Serializable@> ret;
     Array<Serializable@> ret;
-
-    if (attrEdit.vars.Contains("NodeIDs"))
+    Variant variant = attrEdit.GetVar(NODE_IDS_VAR);
+    if (!variant.empty)
     {
     {
-        Array<Variant>@ ids = attrEdit.vars["NodeIDs"].GetVariantVector();
+        Array<Variant>@ ids = variant.GetVariantVector();
         for (uint i = 0; i < ids.length; ++i)
         for (uint i = 0; i < ids.length; ++i)
         {
         {
             Node@ node = editorScene.GetNode(ids[i].GetUInt());
             Node@ node = editorScene.GetNode(ids[i].GetUInt());
@@ -276,17 +294,21 @@ Array<Serializable@>@ GetAttributeEditorTargets(UIElement@ attrEdit)
                 ret.Push(node);
                 ret.Push(node);
         }
         }
     }
     }
-    if (attrEdit.vars.Contains("ComponentIDs"))
+    else
     {
     {
-        Array<Variant>@ ids = attrEdit.vars["ComponentIDs"].GetVariantVector();
-        for (uint i = 0; i < ids.length; ++i)
+        variant = attrEdit.GetVar(COMPONENT_IDS_VAR);
+        if (!variant.empty)
         {
         {
-            Component@ component = editorScene.GetComponent(ids[i].GetUInt());
-            if (component !is null)
-                ret.Push(component);
+            Array<Variant>@ ids = variant.GetVariantVector();
+            for (uint i = 0; i < ids.length; ++i)
+            {
+                Component@ component = editorScene.GetComponent(ids[i].GetUInt());
+                if (component !is null)
+                    ret.Push(component);
+            }
         }
         }
     }
     }
-    
+
     return ret;
     return ret;
 }
 }
 
 
@@ -296,7 +318,7 @@ void CreateNewVariable(StringHash eventType, VariantMap& eventData)
         return;
         return;
 
 
     DropDownList@ dropDown = eventData["Element"].GetUIElement();
     DropDownList@ dropDown = eventData["Element"].GetUIElement();
-    LineEdit@ nameEdit = nodeWindow.GetChild("VarNameEdit", true);
+    LineEdit@ nameEdit = attributeInspectorWindow.GetChild("VarNameEdit", true);
     String sanitatedVarName = nameEdit.text.Trimmed().Replaced(";", "");
     String sanitatedVarName = nameEdit.text.Trimmed().Replaced(";", "");
     if (sanitatedVarName.empty)
     if (sanitatedVarName.empty)
         return;
         return;
@@ -326,14 +348,14 @@ void CreateNewVariable(StringHash eventType, VariantMap& eventData)
         break;
         break;
     }
     }
 
 
-    // If we overwrite an existing variable, must recreate the 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(sanitatedVarName);
         overwrite = overwrite || editNodes[i].vars.Contains(sanitatedVarName);
         editNodes[i].vars[sanitatedVarName] = newValue;
         editNodes[i].vars[sanitatedVarName] = newValue;
     }
     }
-    UpdateAttributes(overwrite);
+    UpdateAttributeInspector(overwrite);
 }
 }
 
 
 void DeleteVariable(StringHash eventType, VariantMap& eventData)
 void DeleteVariable(StringHash eventType, VariantMap& eventData)
@@ -341,7 +363,7 @@ void DeleteVariable(StringHash eventType, VariantMap& eventData)
     if (editNodes.length == 0)
     if (editNodes.length == 0)
         return;
         return;
 
 
-    LineEdit@ nameEdit = nodeWindow.GetChild("VarNameEdit", true);
+    LineEdit@ nameEdit = attributeInspectorWindow.GetChild("VarNameEdit", true);
     String sanitatedVarName = nameEdit.text.Trimmed().Replaced(";", "");
     String sanitatedVarName = nameEdit.text.Trimmed().Replaced(";", "");
     if (sanitatedVarName.empty)
     if (sanitatedVarName.empty)
         return;
         return;
@@ -353,5 +375,5 @@ void DeleteVariable(StringHash eventType, VariantMap& eventData)
         erased = editNodes[i].vars.Erase(sanitatedVarName) || erased;
         erased = editNodes[i].vars.Erase(sanitatedVarName) || erased;
     }
     }
     if (erased)
     if (erased)
-        UpdateAttributes(false);
+        UpdateAttributeInspector(false);
 }
 }

+ 4 - 4
Bin/Data/Scripts/Editor/EditorPreferences.as

@@ -116,7 +116,7 @@ void EditUIMaxOpacity(StringHash eventType, VariantMap& eventData)
 void ToggleShowNonEditableAttribute(StringHash eventType, VariantMap& eventData)
 void ToggleShowNonEditableAttribute(StringHash eventType, VariantMap& eventData)
 {
 {
     showNonEditableAttribute = cast<CheckBox>(eventData["Element"].GetUIElement()).checked;
     showNonEditableAttribute = cast<CheckBox>(eventData["Element"].GetUIElement()).checked;
-    UpdateAttributes(true);
+    UpdateAttributeInspector(true);
 }
 }
 
 
 void EditOriginalAttributeTextColor(StringHash eventType, VariantMap& eventData)
 void EditOriginalAttributeTextColor(StringHash eventType, VariantMap& eventData)
@@ -129,7 +129,7 @@ void EditOriginalAttributeTextColor(StringHash eventType, VariantMap& eventData)
         edit.text = String(normalTextColor.g);
         edit.text = String(normalTextColor.g);
     else if (edit.name == "OriginalAttributeTextColor.b")
     else if (edit.name == "OriginalAttributeTextColor.b")
         edit.text = String(normalTextColor.b);
         edit.text = String(normalTextColor.b);
-    UpdateAttributes(false);
+    UpdateAttributeInspector(false);
 }
 }
 
 
 void EditModifiedAttributeTextColor(StringHash eventType, VariantMap& eventData)
 void EditModifiedAttributeTextColor(StringHash eventType, VariantMap& eventData)
@@ -142,7 +142,7 @@ void EditModifiedAttributeTextColor(StringHash eventType, VariantMap& eventData)
         edit.text = String(modifiedTextColor.g);
         edit.text = String(modifiedTextColor.g);
     else if (edit.name == "ModifiedAttributeTextColor.b")
     else if (edit.name == "ModifiedAttributeTextColor.b")
         edit.text = String(modifiedTextColor.b);
         edit.text = String(modifiedTextColor.b);
-    UpdateAttributes(false);
+    UpdateAttributeInspector(false);
 }
 }
 
 
 void EditNonEditableAttributeTextColor(StringHash eventType, VariantMap& eventData)
 void EditNonEditableAttributeTextColor(StringHash eventType, VariantMap& eventData)
@@ -155,5 +155,5 @@ void EditNonEditableAttributeTextColor(StringHash eventType, VariantMap& eventDa
         edit.text = String(nonEditableTextColor.g);
         edit.text = String(nonEditableTextColor.g);
     else if (edit.name == "NonEditableAttributeTextColor.b")
     else if (edit.name == "NonEditableAttributeTextColor.b")
         edit.text = String(nonEditableTextColor.b);
         edit.text = String(nonEditableTextColor.b);
-    UpdateAttributes(false);
+    UpdateAttributeInspector(false);
 }
 }

+ 4 - 4
Bin/Data/Scripts/Editor/EditorScene.as

@@ -73,8 +73,8 @@ bool ResetScene()
     runUpdate = false;
     runUpdate = false;
 
 
     UpdateWindowTitle();
     UpdateWindowTitle();
-    UpdateHierarchyWindowItem(editorScene, true);
-    UpdateNodeWindow();
+    UpdateHierarchyItem(editorScene, true);
+    UpdateAttributeInspector();
 
 
     suppressSceneChanges = false;
     suppressSceneChanges = false;
 
 
@@ -145,8 +145,8 @@ bool LoadScene(const String&in fileName)
     runUpdate = false;
     runUpdate = false;
 
 
     UpdateWindowTitle();
     UpdateWindowTitle();
-    UpdateHierarchyWindowItem(editorScene, true);
-    UpdateNodeWindow();
+    UpdateHierarchyItem(editorScene, true);
+    UpdateAttributeInspector();
 
 
     suppressSceneChanges = false;
     suppressSceneChanges = false;
 
 

+ 75 - 42
Bin/Data/Scripts/Editor/EditorSceneWindow.as

@@ -8,17 +8,24 @@ const uint NO_ITEM = M_MAX_UNSIGNED;
 const ShortStringHash SCENE_TYPE("Scene");
 const ShortStringHash SCENE_TYPE("Scene");
 const ShortStringHash NODE_TYPE("Node");
 const ShortStringHash NODE_TYPE("Node");
 const String NO_CHANGE(uint8(0));
 const String NO_CHANGE(uint8(0));
+const ShortStringHash TYPE_VAR("Type");
+const ShortStringHash NODE_ID_VAR("NodeID");
+const ShortStringHash COMPONENT_ID_VAR("ComponentID");
+const ShortStringHash UI_ELEMENT_ID_VAR("__UIElementID");
 
 
 Window@ hierarchyWindow;
 Window@ hierarchyWindow;
 ListView@ hierarchyList;
 ListView@ hierarchyList;
 
 
+// UIElement does not have unique ID, so use a running number to generate a new ID each time an item is inserted into hierarchy list
+uint uiElementNextID = 0;
+
 void CreateHierarchyWindow()
 void CreateHierarchyWindow()
 {
 {
     if (hierarchyWindow !is null)
     if (hierarchyWindow !is null)
         return;
         return;
 
 
     hierarchyWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSceneWindow.xml"));
     hierarchyWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSceneWindow.xml"));
-    hierarchyList = hierarchyWindow.GetChild("NodeList");
+    hierarchyList = hierarchyWindow.GetChild("HierarchyList");
     ui.root.AddChild(hierarchyWindow);
     ui.root.AddChild(hierarchyWindow);
     int height = Min(ui.root.height - 60, 500);
     int height = Min(ui.root.height - 60, 500);
     hierarchyWindow.SetSize(300, height);
     hierarchyWindow.SetSize(300, height);
@@ -26,7 +33,7 @@ void CreateHierarchyWindow()
     hierarchyWindow.opacity = uiMaxOpacity;
     hierarchyWindow.opacity = uiMaxOpacity;
     hierarchyWindow.BringToFront();
     hierarchyWindow.BringToFront();
 
 
-    UpdateHierarchyWindowItem(editorScene);
+    UpdateHierarchyItem(editorScene);
 
 
     DropDownList@ newNodeList = hierarchyWindow.GetChild("NewNodeList", true);
     DropDownList@ newNodeList = hierarchyWindow.GetChild("NewNodeList", true);
     Array<String> newNodeChoices = {"Replicated", "Local"};
     Array<String> newNodeChoices = {"Replicated", "Local"};
@@ -108,7 +115,7 @@ void EnableExpandCollapseButtons(bool enable)
     }
     }
 }
 }
 
 
-void UpdateHierarchyWindowItem(Serializable@ serializable, bool clear = false)
+void UpdateHierarchyItem(Serializable@ serializable, bool clear = false)
 {
 {
     if (clear)
     if (clear)
     {
     {
@@ -126,15 +133,15 @@ void UpdateHierarchyWindowItem(Serializable@ serializable, bool clear = false)
     else if (serializable !is editorUIElement)
     else if (serializable !is editorUIElement)
         parent = cast<UIElement>(serializable).parent;
         parent = cast<UIElement>(serializable).parent;
     UIElement@ parentItem = hierarchyList.items[GetListIndex(parent)];
     UIElement@ parentItem = hierarchyList.items[GetListIndex(parent)];
-    UpdateHierarchyWindowItem(GetListIndex(serializable), serializable, parentItem);
+    UpdateHierarchyItem(GetListIndex(serializable), serializable, parentItem);
 }
 }
 
 
-uint UpdateHierarchyWindowItem(uint itemIndex, Serializable@ serializable, UIElement@ parentItem)
+uint UpdateHierarchyItem(uint itemIndex, Serializable@ serializable, UIElement@ parentItem)
 {
 {
     // Whenever we're updating, disable layout update to optimize speed
     // Whenever we're updating, disable layout update to optimize speed
     hierarchyList.contentElement.DisableLayoutUpdate();
     hierarchyList.contentElement.DisableLayoutUpdate();
 
 
-    String idVar;
+    ShortStringHash idVar;
     Variant id;
     Variant id;
     int itemType = ITEM_NONE;
     int itemType = ITEM_NONE;
     if (serializable !is null)
     if (serializable !is null)
@@ -191,7 +198,7 @@ uint UpdateHierarchyWindowItem(uint itemIndex, Serializable@ serializable, UIEle
         for (uint i = 0; i < node.numChildren; ++i)
         for (uint i = 0; i < node.numChildren; ++i)
         {
         {
             Node@ childNode = node.children[i];
             Node@ childNode = node.children[i];
-            itemIndex = UpdateHierarchyWindowItem(itemIndex, childNode, text);
+            itemIndex = UpdateHierarchyItem(itemIndex, childNode, text);
         }
         }
     }
     }
     else
     else
@@ -205,7 +212,7 @@ uint UpdateHierarchyWindowItem(uint itemIndex, Serializable@ serializable, UIEle
         for (uint i = 0; i < element.numChildren; ++i)
         for (uint i = 0; i < element.numChildren; ++i)
         {
         {
             UIElement@ childElement = element.children[i];
             UIElement@ childElement = element.children[i];
-            itemIndex = UpdateHierarchyWindowItem(itemIndex, childElement, text);
+            itemIndex = UpdateHierarchyItem(itemIndex, childElement, text);
         }
         }
     }
     }
 
 
@@ -216,7 +223,7 @@ uint UpdateHierarchyWindowItem(uint itemIndex, Serializable@ serializable, UIEle
     return itemIndex;
     return itemIndex;
 }
 }
 
 
-void UpdateHierarchyWindowItemText(uint itemIndex, bool iconEnabled, const String&in textTitle = NO_CHANGE)
+void UpdateHierarchyItemText(uint itemIndex, bool iconEnabled, const String&in textTitle = NO_CHANGE)
 {
 {
     Text@ text = hierarchyList.items[itemIndex];
     Text@ text = hierarchyList.items[itemIndex];
     if (text is null)
     if (text is null)
@@ -232,9 +239,9 @@ void AddComponentItem(uint compItemIndex, Component@ component, UIElement@ paren
 {
 {
     Text@ text = Text();
     Text@ text = Text();
     text.SetStyle(uiStyle, "FileSelectorListText");
     text.SetStyle(uiStyle, "FileSelectorListText");
-    text.vars["Type"] = ITEM_COMPONENT;
-    text.vars["NodeID"] = component.node.id;
-    text.vars["ComponentID"] = component.id;
+    text.vars[TYPE_VAR] = ITEM_COMPONENT;
+    text.vars[NODE_ID_VAR] = component.node.id;
+    text.vars[COMPONENT_ID_VAR] = component.id;
     text.text = GetComponentTitle(component);
     text.text = GetComponentTitle(component);
 
 
     hierarchyList.InsertItem(compItemIndex, text, parentItem);
     hierarchyList.InsertItem(compItemIndex, text, parentItem);
@@ -246,35 +253,37 @@ void SetID(Text@ text, Serializable@ serializable)
 {
 {
     if (serializable.type == NODE_TYPE || serializable.type == SCENE_TYPE)
     if (serializable.type == NODE_TYPE || serializable.type == SCENE_TYPE)
     {
     {
-        text.vars["Type"] = ITEM_NODE;
-        text.vars["NodeID"] = cast<Node>(serializable).id;
+        text.vars[TYPE_VAR] = ITEM_NODE;
+        text.vars[NODE_ID_VAR] = cast<Node>(serializable).id;
     }
     }
     else
     else
     {
     {
-        text.vars["Type"] = ITEM_UI_ELEMENT;
-        text.vars["ElementName"] = cast<UIElement>(serializable).name;
+        text.vars[TYPE_VAR] = ITEM_UI_ELEMENT;
+        // Store the generated ID into both the variant map of the actual object and the text item
+        cast<UIElement>(serializable).vars[UI_ELEMENT_ID_VAR] = uiElementNextID;
+        text.vars[UI_ELEMENT_ID_VAR] = uiElementNextID++;
     }
     }
 }
 }
 
 
-void GetID(Serializable@ serializable, String& idVar, Variant& id, int& itemType)
+void GetID(Serializable@ serializable, ShortStringHash& idVar, Variant& id, int& itemType)
 {
 {
     if (serializable.type == NODE_TYPE || serializable.type == SCENE_TYPE)
     if (serializable.type == NODE_TYPE || serializable.type == SCENE_TYPE)
     {
     {
-        idVar = "NodeID";
+        idVar = NODE_ID_VAR;
         id = Variant(cast<Node>(serializable).id);
         id = Variant(cast<Node>(serializable).id);
         itemType = ITEM_NODE;
         itemType = ITEM_NODE;
     }
     }
     else
     else
     {
     {
-        idVar = "ElementName";
-        id = Variant(cast<UIElement>(serializable).name);
+        idVar = UI_ELEMENT_ID_VAR;
+        id = cast<UIElement>(serializable).vars[UI_ELEMENT_ID_VAR];
         itemType = ITEM_UI_ELEMENT;
         itemType = ITEM_UI_ELEMENT;
     }
     }
 }
 }
 
 
-bool MatchID(UIElement@ element, const String&in idVar, const Variant&in id, int itemType)
+bool MatchID(UIElement@ element, const ShortStringHash&in idVar, const Variant&in id, int itemType)
 {
 {
-    return element.vars["Type"].GetInt() == itemType && element.vars[idVar] == id;
+    return element.vars[TYPE_VAR].GetInt() == itemType && element.vars[idVar] == id;
 }
 }
 
 
 uint GetListIndex(Serializable@ serializable)
 uint GetListIndex(Serializable@ serializable)
@@ -284,7 +293,7 @@ uint GetListIndex(Serializable@ serializable)
 
 
     uint numItems = hierarchyList.numItems;
     uint numItems = hierarchyList.numItems;
 
 
-    String idVar;
+    ShortStringHash idVar;
     Variant id;
     Variant id;
     int itemType = ITEM_NONE;
     int itemType = ITEM_NONE;
     GetID(serializable, idVar, id, itemType);
     GetID(serializable, idVar, id, itemType);
@@ -299,13 +308,22 @@ uint GetListIndex(Serializable@ serializable)
     return NO_ITEM;
     return NO_ITEM;
 }
 }
 
 
+UIElement@ GetListUIElement(uint index)
+{
+    UIElement@ item = hierarchyList.items[index];
+    if (item is null)
+        return null;
+
+    return editorUIElement.GetChild(UI_ELEMENT_ID_VAR, item.vars[UI_ELEMENT_ID_VAR], true);
+}
+
 Node@ GetListNode(uint index)
 Node@ GetListNode(uint index)
 {
 {
     UIElement@ item = hierarchyList.items[index];
     UIElement@ item = hierarchyList.items[index];
     if (item is null)
     if (item is null)
         return null;
         return null;
 
 
-    return editorScene.GetNode(item.vars["NodeID"].GetUInt());
+    return editorScene.GetNode(item.vars[NODE_ID_VAR].GetUInt());
 }
 }
 
 
 Component@ GetListComponent(uint index)
 Component@ GetListComponent(uint index)
@@ -319,10 +337,10 @@ Component@ GetListComponent(UIElement@ item)
     if (item is null)
     if (item is null)
         return null;
         return null;
 
 
-    if (item.vars["Type"].GetInt() != ITEM_COMPONENT)
+    if (item.vars[TYPE_VAR].GetInt() != ITEM_COMPONENT)
         return null;
         return null;
 
 
-    return editorScene.GetComponent(item.vars["ComponentID"].GetUInt());
+    return editorScene.GetComponent(item.vars[COMPONENT_ID_VAR].GetUInt());
 }
 }
 
 
 uint GetComponentListIndex(Component@ component)
 uint GetComponentListIndex(Component@ component)
@@ -334,7 +352,7 @@ uint GetComponentListIndex(Component@ component)
     for (uint i = 0; i < numItems; ++i)
     for (uint i = 0; i < numItems; ++i)
     {
     {
         UIElement@ item = hierarchyList.items[i];
         UIElement@ item = hierarchyList.items[i];
-        if (item.vars["Type"].GetInt() == ITEM_COMPONENT && item.vars["ComponentID"].GetUInt() == component.id)
+        if (item.vars[TYPE_VAR].GetInt() == ITEM_COMPONENT && item.vars[COMPONENT_ID_VAR].GetUInt() == component.id)
             return i;
             return i;
     }
     }
 
 
@@ -488,7 +506,7 @@ void HandleHierarchyListSelectionChange()
     {
     {
         uint index = indices[i];
         uint index = indices[i];
         UIElement@ item = hierarchyList.items[index];
         UIElement@ item = hierarchyList.items[index];
-        int type = item.vars["Type"].GetInt();
+        int type = item.vars[TYPE_VAR].GetInt();
         if (type == ITEM_COMPONENT)
         if (type == ITEM_COMPONENT)
         {
         {
             Component@ comp = GetListComponent(index);
             Component@ comp = GetListComponent(index);
@@ -501,11 +519,19 @@ void HandleHierarchyListSelectionChange()
             if (node !is null)
             if (node !is null)
                 selectedNodes.Push(node);
                 selectedNodes.Push(node);
         }
         }
+        else if (type == ITEM_UI_ELEMENT)
+        {
+            UIElement@ element = GetListUIElement(index);
+            if (element !is null && element !is editorUIElement)
+                selectedUIElements.Push(element);
+        }
     }
     }
 
 
-    // If only one node selected, use it for editing
+    // If only one node/UIElement selected, use it for editing
     if (selectedNodes.length == 1)
     if (selectedNodes.length == 1)
         editNode = selectedNodes[0];
         editNode = selectedNodes[0];
+    if (selectedUIElements.length == 1)
+        editUIElement = selectedUIElements[0];
 
 
     // If selection contains only components, and they have a common node, use it for editing
     // If selection contains only components, and they have a common node, use it for editing
     if (selectedNodes.empty && !selectedComponents.empty)
     if (selectedNodes.empty && !selectedComponents.empty)
@@ -589,8 +615,13 @@ void HandleHierarchyListSelectionChange()
             editNodes.Erase(0);
             editNodes.Erase(0);
     }
     }
 
 
+    if (selectedUIElements.empty && editUIElement !is null)
+        editUIElements.Push(editUIElement);
+    else
+        editUIElements = selectedUIElements;
+
     PositionGizmo();
     PositionGizmo();
-    UpdateNodeWindow();
+    UpdateAttributeInspector();
 }
 }
 
 
 void HandleHierarchyListItemDoubleClick(StringHash eventType, VariantMap& eventData)
 void HandleHierarchyListItemDoubleClick(StringHash eventType, VariantMap& eventData)
@@ -615,8 +646,8 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
     if (!accept)
     if (!accept)
         return;
         return;
 
 
-    Node@ sourceNode = editorScene.GetNode(source.vars["NodeID"].GetUInt());
-    Node@ targetNode = editorScene.GetNode(target.vars["NodeID"].GetUInt());
+    Node@ sourceNode = editorScene.GetNode(source.vars[NODE_ID_VAR].GetUInt());
+    Node@ targetNode = editorScene.GetNode(target.vars[NODE_ID_VAR].GetUInt());
 
 
     // If target is null, parent to scene
     // If target is null, parent to scene
     if (targetNode is null)
     if (targetNode is null)
@@ -635,10 +666,12 @@ bool TestDragDrop(UIElement@ source, UIElement@ target)
     // Test for validity of reparenting by drag and drop
     // Test for validity of reparenting by drag and drop
     Node@ sourceNode;
     Node@ sourceNode;
     Node@ targetNode;
     Node@ targetNode;
-    if (source.vars.Contains("NodeID"))
-        sourceNode = editorScene.GetNode(source.vars["NodeID"].GetUInt());
-    if (target.vars.Contains("NodeID"))
-        editorScene.GetNode(target.vars["NodeID"].GetUInt());
+    Variant variant = source.GetVar(NODE_ID_VAR);
+    if (!variant.empty)
+        sourceNode = editorScene.GetNode(variant.GetUInt());
+    variant = target.GetVar(NODE_ID_VAR);
+    if (!variant.empty)
+        targetNode = editorScene.GetNode(variant.GetUInt());
 
 
     if (sourceNode is null)
     if (sourceNode is null)
         return false;
         return false;
@@ -739,7 +772,7 @@ void HandleNodeAdded(StringHash eventType, VariantMap& eventData)
         return;
         return;
 
 
     Node@ node = eventData["Node"].GetNode();
     Node@ node = eventData["Node"].GetNode();
-    UpdateHierarchyWindowItem(node);
+    UpdateHierarchyItem(node);
 }
 }
 
 
 void HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
 void HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
@@ -749,7 +782,7 @@ void HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
 
 
     Node@ node = eventData["Node"].GetNode();
     Node@ node = eventData["Node"].GetNode();
     uint index = GetListIndex(node);
     uint index = GetListIndex(node);
-    UpdateHierarchyWindowItem(index, null, null);
+    UpdateHierarchyItem(index, null, null);
 }
 }
 
 
 void HandleComponentAdded(StringHash eventType, VariantMap& eventData)
 void HandleComponentAdded(StringHash eventType, VariantMap& eventData)
@@ -758,7 +791,7 @@ void HandleComponentAdded(StringHash eventType, VariantMap& eventData)
         return;
         return;
 
 
     Node@ node = eventData["Node"].GetNode();
     Node@ node = eventData["Node"].GetNode();
-    UpdateHierarchyWindowItem(node);
+    UpdateHierarchyItem(node);
 }
 }
 
 
 void HandleComponentRemoved(StringHash eventType, VariantMap& eventData)
 void HandleComponentRemoved(StringHash eventType, VariantMap& eventData)
@@ -781,7 +814,7 @@ void HandleNodeNameChanged(StringHash eventType, VariantMap& eventData)
         return;
         return;
 
 
     Node@ node = eventData["Node"].GetNode();
     Node@ node = eventData["Node"].GetNode();
-    UpdateHierarchyWindowItemText(GetListIndex(node), node.enabled, GetNodeTitle(node));
+    UpdateHierarchyItemText(GetListIndex(node), node.enabled, GetNodeTitle(node));
 }
 }
 
 
 void HandleNodeEnabledChanged(StringHash eventType, VariantMap& eventData)
 void HandleNodeEnabledChanged(StringHash eventType, VariantMap& eventData)
@@ -790,7 +823,7 @@ void HandleNodeEnabledChanged(StringHash eventType, VariantMap& eventData)
         return;
         return;
 
 
     Node@ node = eventData["Node"].GetNode();
     Node@ node = eventData["Node"].GetNode();
-    UpdateHierarchyWindowItemText(GetListIndex(node), node.enabled);
+    UpdateHierarchyItemText(GetListIndex(node), node.enabled);
     attributesDirty = true;
     attributesDirty = true;
 }
 }
 
 
@@ -800,6 +833,6 @@ void HandleComponentEnabledChanged(StringHash eventType, VariantMap& eventData)
         return;
         return;
 
 
     Component@ component = eventData["Component"].GetComponent();
     Component@ component = eventData["Component"].GetComponent();
-    UpdateHierarchyWindowItemText(GetComponentListIndex(component), component.enabledEffective);
+    UpdateHierarchyItemText(GetComponentListIndex(component), component.enabledEffective);
     attributesDirty = true;
     attributesDirty = true;
 }
 }

+ 10 - 13
Bin/Data/Scripts/Editor/EditorUI.as

@@ -12,7 +12,7 @@ const ShortStringHash TEXT_TYPE("Text");
 const ShortStringHash CURSOR_TYPE("Cursor");
 const ShortStringHash CURSOR_TYPE("Cursor");
 
 
 const String TEMP_SCENE_NAME("_tempscene_.xml");
 const String TEMP_SCENE_NAME("_tempscene_.xml");
-const String CALLBACK_VAR("Callback");
+const ShortStringHash CALLBACK_VAR("Callback");
 
 
 const int SHOW_POPUP_INDICATOR = -1;
 const int SHOW_POPUP_INDICATOR = -1;
 
 
@@ -46,7 +46,7 @@ void CreateUI()
     CreateCursor();
     CreateCursor();
     CreateMenuBar();
     CreateMenuBar();
     CreateHierarchyWindow();
     CreateHierarchyWindow();
-    CreateNodeWindow();
+    CreateAttributeInspectorWindow();
     CreateEditorSettingsDialog();
     CreateEditorSettingsDialog();
     CreateEditorPreferencesDialog();
     CreateEditorPreferencesDialog();
     CreateStatsBar();
     CreateStatsBar();
@@ -181,12 +181,9 @@ void CreateMenuBar()
         Menu@ menu = CreateMenu("Create");
         Menu@ menu = CreateMenu("Create");
         Window@ popup = menu.popup;
         Window@ popup = menu.popup;
         popup.vars["Popup"] = "Create";
         popup.vars["Popup"] = "Create";
-        popup.AddChild(CreateMenuItem("Box", @PickBuiltinObject));
-        popup.AddChild(CreateMenuItem("Cone", @PickBuiltinObject));
-        popup.AddChild(CreateMenuItem("Cylinder", @PickBuiltinObject));
-        popup.AddChild(CreateMenuItem("Plane", @PickBuiltinObject));
-        popup.AddChild(CreateMenuItem("Pyramid", @PickBuiltinObject));
-        popup.AddChild(CreateMenuItem("Sphere", @PickBuiltinObject));
+        String[] objects = { "Box", "Cone", "Cylinder", "Plane", "Pyramid", "Sphere" };
+        for (uint i = 0; i < objects.length; ++i)
+            popup.AddChild(CreateMenuItem(objects[i], @PickBuiltinObject));
         uiMenuBar.AddChild(menu);
         uiMenuBar.AddChild(menu);
     }
     }
 
 
@@ -216,7 +213,7 @@ void CreateMenuBar()
         Window@ popup = menu.popup;
         Window@ popup = menu.popup;
         popup.vars["Popup"] = "View";
         popup.vars["Popup"] = "View";
         popup.AddChild(CreateMenuItem("Hierarchy", @ShowHierarchyWindow, 'H', QUAL_CTRL));
         popup.AddChild(CreateMenuItem("Hierarchy", @ShowHierarchyWindow, 'H', QUAL_CTRL));
-        popup.AddChild(CreateMenuItem("Attribute inspector", @ShowNodeWindow, 'I', QUAL_CTRL));
+        popup.AddChild(CreateMenuItem("Attribute inspector", @ShowAttributeInspectorWindow, 'I', QUAL_CTRL));
         popup.AddChild(CreateMenuItem("Editor settings", @ShowEditorSettingsDialog));
         popup.AddChild(CreateMenuItem("Editor settings", @ShowEditorSettingsDialog));
         popup.AddChild(CreateMenuItem("Editor preferences", @ShowEditorPreferencesDialog));
         popup.AddChild(CreateMenuItem("Editor preferences", @ShowEditorPreferencesDialog));
         popup.AddChild(CreateMenuDivider());
         popup.AddChild(CreateMenuDivider());
@@ -364,8 +361,9 @@ void HandleMenuSelected(StringHash eventType, VariantMap& eventData)
     HandlePopup(menu);
     HandlePopup(menu);
 
 
     // Execute the callback if available
     // Execute the callback if available
-    if (menu.vars.Contains(CALLBACK_VAR))
-        menuCallbacks[menu.vars[CALLBACK_VAR].GetUInt()]();
+    Variant variant = menu.GetVar(CALLBACK_VAR);
+    if (!variant.empty)
+        menuCallbacks[variant.GetUInt()]();
 }
 }
 
 
 Menu@ CreateMenuItem(const String&in title, MENU_CALLBACK@ callback = null, int accelKey = 0, int accelQual = 0)
 Menu@ CreateMenuItem(const String&in title, MENU_CALLBACK@ callback = null, int accelKey = 0, int accelQual = 0)
@@ -521,7 +519,6 @@ void CreateFileSelector(const String&in title, const String&in ok, const String&
     uiFileSelector.SetFilters(filters, initialFilter);
     uiFileSelector.SetFilters(filters, initialFilter);
     uiFileSelector.window.vars["Popup"] = "FileSelector";
     uiFileSelector.window.vars["Popup"] = "FileSelector";
     uiFileSelector.window.priority = 1000;    // Ensure when it is visible then it has the highest priority (in front of all others UI)
     uiFileSelector.window.priority = 1000;    // Ensure when it is visible then it has the highest priority (in front of all others UI)
-
     CenterDialog(uiFileSelector.window);
     CenterDialog(uiFileSelector.window);
 }
 }
 
 
@@ -886,5 +883,5 @@ void UpdateDirtyUI()
 {
 {
     // Perform some event-triggered updates latently in case a large hierarchy was changed
     // Perform some event-triggered updates latently in case a large hierarchy was changed
     if (attributesDirty)
     if (attributesDirty)
-        UpdateAttributes(false);
+        UpdateAttributeInspector(false);
 }
 }

+ 18 - 15
Bin/Data/Scripts/Editor/EditorUIElement.as

@@ -11,8 +11,8 @@ Array<UIElement@> editUIElements;
 
 
 bool suppressUIElementChanges = false;
 bool suppressUIElementChanges = false;
 
 
-const String FILENAME_VAR("__fileName");
-const String MODIFIED_VAR("__modified");
+const ShortStringHash FILENAME_VAR("__FileName");
+const ShortStringHash MODIFIED_VAR("__Modified");
 
 
 void ClearUIElementSelection()
 void ClearUIElementSelection()
 {
 {
@@ -21,15 +21,15 @@ void ClearUIElementSelection()
     editUIElements.Clear();
     editUIElements.Clear();
 }
 }
 
 
-void CreateUIElement()
+void CreateRootUIElement()
 {
 {
     // Create a root UIElement only once here, do not confuse this with ui.root itself
     // Create a root UIElement only once here, do not confuse this with ui.root itself
     editorUIElement = ui.root.CreateChild("UIElement");
     editorUIElement = ui.root.CreateChild("UIElement");
     editorUIElement.name = "UI";
     editorUIElement.name = "UI";
     editorUIElement.SetSize(graphics.width, graphics.height);
     editorUIElement.SetSize(graphics.width, graphics.height);
-    editorUIElement.traversalMode = TM_DEPTH_FIRST;
+    editorUIElement.traversalMode = TM_DEPTH_FIRST;     // This is needed for root element to prevent artifacts
 
 
-    UpdateHierarchyWindowItem(editorUIElement);
+    UpdateHierarchyItem(editorUIElement);
 }
 }
 
 
 bool NewUIElement()
 bool NewUIElement()
@@ -74,18 +74,20 @@ void OpenUIElement(const String&in fileName)
 
 
     suppressUIElementChanges = true;
     suppressUIElementChanges = true;
 
 
-    // If uiElementDefaultStyle is not set then use the editor's own default style
+    // If uiElementDefaultStyle is not set then automatically fallback to use the editor's own default style
     UIElement@ element = ui.LoadLayout(xmlFile, uiElementDefaultStyle);
     UIElement@ element = ui.LoadLayout(xmlFile, uiElementDefaultStyle);
     if (element !is null)
     if (element !is null)
     {
     {
         element.vars[FILENAME_VAR] = fileName;
         element.vars[FILENAME_VAR] = fileName;
         element.vars[MODIFIED_VAR] = false;
         element.vars[MODIFIED_VAR] = false;
+
+        // \todo: should not always centered
         CenterDialog(element);
         CenterDialog(element);
         editorUIElement.AddChild(element);
         editorUIElement.AddChild(element);
     }
     }
 
 
-    UpdateHierarchyWindowItem(element, true);
-    UpdateNodeWindow();
+    UpdateHierarchyItem(element, true);
+    UpdateAttributeInspector();
 
 
     suppressSceneChanges = false;
     suppressSceneChanges = false;
 }
 }
@@ -97,27 +99,28 @@ bool CloseUIElement()
     for (uint i = 0; i < selectedUIElements.length; ++i)
     for (uint i = 0; i < selectedUIElements.length; ++i)
     {
     {
         UIElement@ element = selectedUIElements[i];
         UIElement@ element = selectedUIElements[i];
-        while (!element.vars.Contains("FILENAME_VAR"))
+        while (!element.vars.Contains(FILENAME_VAR))
             element = element.parent;
             element = element.parent;
         element.Remove();
         element.Remove();
-        UpdateHierarchyWindowItem(element);
+
+        UpdateHierarchyItem(GetListIndex(element), null, null);
     }
     }
 
 
     suppressUIElementChanges = false;
     suppressUIElementChanges = false;
-    
+
     return true;
     return true;
 }
 }
 
 
 bool CloseAllUIElements()
 bool CloseAllUIElements()
 {
 {
     suppressUIElementChanges = true;
     suppressUIElementChanges = true;
-    
+
     editorUIElement.RemoveAllChildren();
     editorUIElement.RemoveAllChildren();
-    UpdateHierarchyWindowItem(editorUIElement, true);
-    UpdateNodeWindow();
+    UpdateHierarchyItem(editorUIElement, true);
+    UpdateAttributeInspector();
 
 
     suppressUIElementChanges = false;
     suppressUIElementChanges = false;
-    
+
     return true;
     return true;
 }
 }
 
 

+ 4 - 0
Bin/Data/Scripts/Editor/EditorView.as

@@ -378,6 +378,10 @@ void ViewMouseClick()
 
 
 void ViewRaycast(bool mouseClick)
 void ViewRaycast(bool mouseClick)
 {
 {
+    // Ignore if UI has modal element
+    if (ui.modalElement !is null)
+        return;
+
     DebugRenderer@ debug = editorScene.debugRenderer;
     DebugRenderer@ debug = editorScene.debugRenderer;
     IntVector2 pos = ui.cursorPosition;
     IntVector2 pos = ui.cursorPosition;
     Component@ selected;
     Component@ selected;

+ 3 - 0
Bin/Data/UI/DefaultStyle.xml

@@ -427,6 +427,9 @@
         <attribute name="Layout Mode" value="vertical" />
         <attribute name="Layout Mode" value="vertical" />
         <attribute name="Layout Spacing" value="4" />
         <attribute name="Layout Spacing" value="4" />
         <attribute name="Layout Border" value="6 6 6 6" />
         <attribute name="Layout Border" value="6 6 6 6" />
+        <attribute name="Is Modal" value="true" />
+        <attribute name="Modal Frame Color" value="0.45 0.70 0.45" />
+        <attribute name="Modal Frame Size" value="2 2" />
     </element>
     </element>
     <element type="FileSelectorButton">
     <element type="FileSelectorButton">
         <attribute name="Min Size" value="80 22" />
         <attribute name="Min Size" value="80 22" />

+ 2 - 2
Bin/Data/UI/EditorComponent.xml

@@ -1,6 +1,6 @@
 <element>
 <element>
     <element>
     <element>
-        <attribute name="Name" value="ComponentChildContainer" />
+        <attribute name="Name" value="ComponentContainer" />
         <attribute name="Layout Mode" value="Vertical" />
         <attribute name="Layout Mode" value="Vertical" />
         <attribute name="Layout Spacing" value="4" />
         <attribute name="Layout Spacing" value="4" />
         <element type="BorderImage" style="EditorDivider" />
         <element type="BorderImage" style="EditorDivider" />
@@ -10,7 +10,7 @@
             <attribute name="Max Size" value="2147483647 17" />
             <attribute name="Max Size" value="2147483647 17" />
         </element>
         </element>
         <element type="ListView">
         <element type="ListView">
-            <attribute name="Name" value="ComponentAttributeList" />
+            <attribute name="Name" value="AttributeList" />
             <attribute name="Clip Children" value="false" />
             <attribute name="Clip Children" value="false" />
         </element>
         </element>
     </element>
     </element>

+ 76 - 0
Bin/Data/UI/EditorNode.xml

@@ -0,0 +1,76 @@
+<element>
+    <element>
+        <attribute name="Name" value="NodeContainer" />
+        <attribute name="Layout Mode" value="Vertical" />
+        <attribute name="Layout Spacing" value="4" />
+        <element type="BorderImage" style="EditorDivider" />
+        <element type="Text">
+            <attribute name="Name" value="NodeTitle" />
+            <attribute name="Min Size" value="0 17" />
+            <attribute name="Max Size" value="2147483647 17" />
+        </element>
+        <element type="ListView">
+            <attribute name="Name" value="AttributeList" />
+            <attribute name="Highlight Mode" value="Always" />
+            <attribute name="Clip Children" value="false" />
+        </element>
+        <element>
+            <attribute name="Min Size" value="0 17" />
+            <attribute name="Max Size" value="2147483647 17" />
+            <attribute name="Layout Mode" value="Horizontal" />
+            <attribute name="Layout Spacing" value="4" />
+            <element type="LineEdit">
+                <attribute name="Name" value="VarNameEdit" />
+            </element>
+            <element type="DropDownList">
+                <attribute name="Name" value="NewVarDropDown" />
+                <attribute name="Min Size" value="50 17" />
+                <attribute name="Max Size" value="50 17" />
+                <element type="Window" internal="true" popup="true">
+                    <element type="ListView" internal="true">
+                        <element type="BorderImage" internal="true">
+                            <element internal="true">
+                                <element type="Text" style="FileSelectorFilterText">
+                                    <attribute name="Text" value="Int" />
+                                </element>
+                                <element type="Text" style="FileSelectorFilterText">
+                                    <attribute name="Text" value="Bool" />
+                                </element>
+                                <element type="Text" style="FileSelectorFilterText">
+                                    <attribute name="Text" value="Float" />
+                                </element>
+                                <element type="Text" style="FileSelectorFilterText">
+                                    <attribute name="Text" value="String" />
+                                </element>
+                                <element type="Text" style="FileSelectorFilterText">
+                                    <attribute name="Text" value="Vector3" />
+                                </element>
+                                <element type="Text" style="FileSelectorFilterText">
+                                    <attribute name="Text" value="Color" />
+                                </element>
+                            </element>
+                        </element>
+                    </element>
+                </element>
+                <element internal="true">
+                    <attribute name="Is Visible" value="false" />
+                </element>
+                <element type="Text">
+                    <attribute name="Text" value="New" />
+                    <attribute name="Text Alignment" value="Center" />
+                </element>
+            </element>
+            <element type="Button">
+                <attribute name="Name" value="DeleteVarButton" />
+                <attribute name="Min Size" value="50 17" />
+                <attribute name="Max Size" value="50 17" />
+                <attribute name="Layout Mode" value="Vertical" />
+                <attribute name="Layout Border" value="1 1 1 1" />
+                <element type="Text">
+                    <attribute name="Text" value="Del" />
+                    <attribute name="Text Alignment" value="Center" />
+                </element>
+            </element>
+        </element>
+    </element>
+</element>

+ 2 - 73
Bin/Data/UI/EditorNodeWindow.xml

@@ -1,5 +1,5 @@
 <element type="Window">
 <element type="Window">
-    <attribute name="Name" value="NodeWindow" />
+    <attribute name="Name" value="AttributeInspectorWindow" />
     <attribute name="Is Movable" value="true" />
     <attribute name="Is Movable" value="true" />
     <attribute name="Is Resizable" value="true" />
     <attribute name="Is Resizable" value="true" />
     <attribute name="Resize Border" value="6 6 6 6" />
     <attribute name="Resize Border" value="6 6 6 6" />
@@ -17,79 +17,8 @@
             <attribute name="Name" value="CloseButton" />
             <attribute name="Name" value="CloseButton" />
         </element>
         </element>
     </element>
     </element>
-    <element type="BorderImage" style="EditorDivider" />
-    <element type="Text">
-        <attribute name="Name" value="NodeTitle" />
-        <attribute name="Min Size" value="0 17" />
-        <attribute name="Max Size" value="2147483647 17" />
-    </element>
-    <element type="ListView">
-        <attribute name="Name" value="NodeAttributeList" />
-        <attribute name="Highlight Mode" value="Always" />
-        <attribute name="Min Size" value="0 74" />
-        <attribute name="Max Size" value="2147483647 74" />
-        <attribute name="Clip Children" value="false" />
-    </element>
-    <element>
-        <attribute name="Min Size" value="0 17" />
-        <attribute name="Max Size" value="2147483647 17" />
-        <attribute name="Layout Mode" value="Horizontal" />
-        <attribute name="Layout Spacing" value="4" />
-        <element type="LineEdit">
-            <attribute name="Name" value="VarNameEdit" />
-        </element>
-        <element type="DropDownList">
-            <attribute name="Name" value="NewVarDropDown" />
-            <attribute name="Min Size" value="50 17" />
-            <attribute name="Max Size" value="50 17" />
-            <element type="Window" internal="true" popup="true">
-                <element type="ListView" internal="true">
-                    <element type="BorderImage" internal="true">
-                        <element internal="true">
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Int" />
-                            </element>
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Bool" />
-                            </element>
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Float" />
-                            </element>
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="String" />
-                            </element>
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Vector3" />
-                            </element>
-                            <element type="Text" style="FileSelectorFilterText">
-                                <attribute name="Text" value="Color" />
-                            </element>
-                        </element>
-                    </element>
-                </element>
-            </element>    
-            <element internal="true">
-                <attribute name="Is Visible" value="false" />
-            </element>
-            <element type="Text">
-                <attribute name="Text" value="New" />
-                <attribute name="Text Alignment" value="Center" />
-            </element>
-        </element>
-        <element type="Button">
-            <attribute name="Name" value="DeleteVarButton" />
-            <attribute name="Min Size" value="50 17" />
-            <attribute name="Max Size" value="50 17" />
-            <attribute name="Layout Mode" value="Vertical" />
-            <attribute name="Layout Border" value="1 1 1 1" />
-            <element type="Text">
-                <attribute name="Text" value="Del" />
-                <attribute name="Text Alignment" value="Center" />
-            </element>
-        </element>
-    </element>
     <element>
     <element>
-        <attribute name="Name" value="ComponentParentContainer" />
+        <attribute name="Name" value="ParentContainer" />
         <attribute name="Layout Mode" value="Vertical" />
         <attribute name="Layout Mode" value="Vertical" />
         <attribute name="Layout Spacing" value="4" />
         <attribute name="Layout Spacing" value="4" />
     </element>
     </element>

+ 2 - 2
Bin/Data/UI/EditorSceneWindow.xml

@@ -1,5 +1,5 @@
 <element type="Window">
 <element type="Window">
-    <attribute name="Name" value="SceneWindow" />
+    <attribute name="Name" value="HierarchyWindow" />
     <attribute name="Is Movable" value="true" />
     <attribute name="Is Movable" value="true" />
     <attribute name="Is Resizable" value="true" />
     <attribute name="Is Resizable" value="true" />
     <attribute name="Resize Border" value="6 6 6 6" />
     <attribute name="Resize Border" value="6 6 6 6" />
@@ -57,7 +57,7 @@
         </element>
         </element>
     </element>
     </element>
     <element type="ListView">
     <element type="ListView">
-        <attribute name="Name" value="NodeList" />
+        <attribute name="Name" value="HierarchyList" />
         <attribute name="Hierarchy Mode" value="true" />
         <attribute name="Hierarchy Mode" value="true" />
         <attribute name="Base Indent" value="1" />
         <attribute name="Base Indent" value="1" />
         <attribute name="Highlight Mode" value="Always" />
         <attribute name="Highlight Mode" value="Always" />

+ 36 - 1
Docs/ScriptAPI.dox

@@ -746,7 +746,6 @@ Methods:<br>
 - void FromString(const String&, const String&)
 - void FromString(const String&, const String&)
 - void FromString(VariantType, const String&)
 - void FromString(VariantType, const String&)
 - String ToString() const
 - String ToString() const
-- bool IsZero() const
 - VectorBuffer GetBuffer() const
 - VectorBuffer GetBuffer() const
 - Node@ GetNode() const
 - Node@ GetNode() const
 - Component@ GetComponent() const
 - Component@ GetComponent() const
@@ -758,6 +757,8 @@ Methods:<br>
 - PhysicsWorld@ GetPhysicsWorld() const
 - PhysicsWorld@ GetPhysicsWorld() const
 
 
 Properties:<br>
 Properties:<br>
+- bool zero (readonly)
+- bool empty (readonly)
 - VariantType type (readonly)
 - VariantType type (readonly)
 - String typeName (readonly)
 - String typeName (readonly)
 
 
@@ -3150,7 +3151,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -3256,7 +3259,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -3365,7 +3370,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - void SetPosition(float, float)
 - void SetPosition(float, float)
 - void SetHotSpot(int, int)
 - void SetHotSpot(int, int)
 - void SetScale(float, float)
 - void SetScale(float, float)
@@ -3453,7 +3460,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -3574,7 +3583,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -3691,7 +3702,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -3807,7 +3820,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -3927,7 +3942,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4045,7 +4062,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4161,7 +4180,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4306,7 +4327,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4428,7 +4451,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4553,7 +4578,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4681,7 +4708,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4820,7 +4849,9 @@ Methods:<br>
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@ GetChild(const ShortStringHash&, const Variant&, bool arg2 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
 - UIElement@[]@ GetChildren(bool arg0 = false) const
+- const Variant& GetVar(const ShortStringHash&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ScreenToElement(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - IntVector2 ElementToScreen(const IntVector2&)
 - bool IsInside(IntVector2, bool)
 - bool IsInside(IntVector2, bool)
@@ -4896,6 +4927,9 @@ Properties:<br>
 - bool movable
 - bool movable
 - bool resizable
 - bool resizable
 - IntRect resizeBorder
 - IntRect resizeBorder
+- bool modal
+- Color modalFrameColor
+- IntVector2 modalFrameSize
 
 
 
 
 FileSelector
 FileSelector
@@ -4949,6 +4983,7 @@ Properties:<br>
 - Cursor@ cursor
 - Cursor@ cursor
 - IntVector2 cursorPosition (readonly)
 - IntVector2 cursorPosition (readonly)
 - UIElement@ focusElement
 - UIElement@ focusElement
+- UIElement@ modalElement (readonly)
 - UIElement@ frontElement (readonly)
 - UIElement@ frontElement (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - bool nonFocusedMouseWheel
 - bool nonFocusedMouseWheel

+ 87 - 87
Engine/Core/Variant.cpp

@@ -62,38 +62,38 @@ static const String typeNames[] =
 Variant& Variant::operator = (const Variant& rhs)
 Variant& Variant::operator = (const Variant& rhs)
 {
 {
     SetType(rhs.GetType());
     SetType(rhs.GetType());
-    
+
     switch (type_)
     switch (type_)
     {
     {
     case VAR_STRING:
     case VAR_STRING:
         *(reinterpret_cast<String*>(&value_)) = *(reinterpret_cast<const String*>(&rhs.value_));
         *(reinterpret_cast<String*>(&value_)) = *(reinterpret_cast<const String*>(&rhs.value_));
         break;
         break;
-        
+
     case VAR_BUFFER:
     case VAR_BUFFER:
         *(reinterpret_cast<PODVector<unsigned char>*>(&value_)) = *(reinterpret_cast<const PODVector<unsigned char>*>(&rhs.value_));
         *(reinterpret_cast<PODVector<unsigned char>*>(&value_)) = *(reinterpret_cast<const PODVector<unsigned char>*>(&rhs.value_));
         break;
         break;
-    
+
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
         *(reinterpret_cast<ResourceRef*>(&value_)) = *(reinterpret_cast<const ResourceRef*>(&rhs.value_));
         *(reinterpret_cast<ResourceRef*>(&value_)) = *(reinterpret_cast<const ResourceRef*>(&rhs.value_));
         break;
         break;
-        
+
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
         *(reinterpret_cast<ResourceRefList*>(&value_)) = *(reinterpret_cast<const ResourceRefList*>(&rhs.value_));
         *(reinterpret_cast<ResourceRefList*>(&value_)) = *(reinterpret_cast<const ResourceRefList*>(&rhs.value_));
         break;
         break;
-        
+
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         *(reinterpret_cast<VariantVector*>(&value_)) = *(reinterpret_cast<const VariantVector*>(&rhs.value_));
         *(reinterpret_cast<VariantVector*>(&value_)) = *(reinterpret_cast<const VariantVector*>(&rhs.value_));
         break;
         break;
-        
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         *(reinterpret_cast<VariantMap*>(&value_)) = *(reinterpret_cast<const VariantMap*>(&rhs.value_));
         *(reinterpret_cast<VariantMap*>(&value_)) = *(reinterpret_cast<const VariantMap*>(&rhs.value_));
         break;
         break;
-        
+
     default:
     default:
         value_ = rhs.value_;
         value_ = rhs.value_;
         break;
         break;
     }
     }
-    
+
     return *this;
     return *this;
 }
 }
 
 
@@ -101,57 +101,57 @@ bool Variant::operator == (const Variant& rhs) const
 {
 {
     if (type_ != rhs.type_)
     if (type_ != rhs.type_)
         return false;
         return false;
-    
+
     switch (type_)
     switch (type_)
     {
     {
     case VAR_INT:
     case VAR_INT:
         return value_.int_ == rhs.value_.int_;
         return value_.int_ == rhs.value_.int_;
-        
+
     case VAR_BOOL:
     case VAR_BOOL:
         return value_.bool_ == rhs.value_.bool_;
         return value_.bool_ == rhs.value_.bool_;
-        
+
     case VAR_FLOAT:
     case VAR_FLOAT:
         return value_.float_ == rhs.value_.float_;
         return value_.float_ == rhs.value_.float_;
-        
+
     case VAR_VECTOR2:
     case VAR_VECTOR2:
         return *(reinterpret_cast<const Vector2*>(&value_)) == *(reinterpret_cast<const Vector2*>(&rhs.value_));
         return *(reinterpret_cast<const Vector2*>(&value_)) == *(reinterpret_cast<const Vector2*>(&rhs.value_));
-        
+
     case VAR_VECTOR3:
     case VAR_VECTOR3:
         return *(reinterpret_cast<const Vector3*>(&value_)) == *(reinterpret_cast<const Vector3*>(&rhs.value_));
         return *(reinterpret_cast<const Vector3*>(&value_)) == *(reinterpret_cast<const Vector3*>(&rhs.value_));
-        
+
     case VAR_VECTOR4:
     case VAR_VECTOR4:
     case VAR_QUATERNION:
     case VAR_QUATERNION:
     case VAR_COLOR:
     case VAR_COLOR:
         // Hack: use the Vector4 compare for all these classes, as they have the same memory structure
         // Hack: use the Vector4 compare for all these classes, as they have the same memory structure
         return *(reinterpret_cast<const Vector4*>(&value_)) == *(reinterpret_cast<const Vector4*>(&rhs.value_));
         return *(reinterpret_cast<const Vector4*>(&value_)) == *(reinterpret_cast<const Vector4*>(&rhs.value_));
-        
+
     case VAR_STRING:
     case VAR_STRING:
         return *(reinterpret_cast<const String*>(&value_)) == *(reinterpret_cast<const String*>(&rhs.value_));
         return *(reinterpret_cast<const String*>(&value_)) == *(reinterpret_cast<const String*>(&rhs.value_));
-        
+
     case VAR_BUFFER:
     case VAR_BUFFER:
         return *(reinterpret_cast<const PODVector<unsigned char>*>(&value_)) == *(reinterpret_cast<const PODVector<unsigned char>*>(&rhs.value_));
         return *(reinterpret_cast<const PODVector<unsigned char>*>(&value_)) == *(reinterpret_cast<const PODVector<unsigned char>*>(&rhs.value_));
-        
+
     case VAR_PTR:
     case VAR_PTR:
         return value_.ptr_ == rhs.value_.ptr_;
         return value_.ptr_ == rhs.value_.ptr_;
-        
+
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
         return *(reinterpret_cast<const ResourceRef*>(&value_)) == *(reinterpret_cast<const ResourceRef*>(&rhs.value_));
         return *(reinterpret_cast<const ResourceRef*>(&value_)) == *(reinterpret_cast<const ResourceRef*>(&rhs.value_));
-        
+
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
         return *(reinterpret_cast<const ResourceRefList*>(&value_)) == *(reinterpret_cast<const ResourceRefList*>(&rhs.value_));
         return *(reinterpret_cast<const ResourceRefList*>(&value_)) == *(reinterpret_cast<const ResourceRefList*>(&rhs.value_));
-        
+
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         return *(reinterpret_cast<const VariantVector*>(&value_)) == *(reinterpret_cast<const VariantVector*>(&rhs.value_));
         return *(reinterpret_cast<const VariantVector*>(&value_)) == *(reinterpret_cast<const VariantVector*>(&rhs.value_));
-        
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         return *(reinterpret_cast<const VariantMap*>(&value_)) == *(reinterpret_cast<const VariantMap*>(&rhs.value_));
         return *(reinterpret_cast<const VariantMap*>(&value_)) == *(reinterpret_cast<const VariantMap*>(&rhs.value_));
-        
+
     case VAR_INTRECT:
     case VAR_INTRECT:
         return *(reinterpret_cast<const IntRect*>(&value_)) == *(reinterpret_cast<const IntRect*>(&rhs.value_));
         return *(reinterpret_cast<const IntRect*>(&value_)) == *(reinterpret_cast<const IntRect*>(&rhs.value_));
-        
+
     case VAR_INTVECTOR2:
     case VAR_INTVECTOR2:
         return *(reinterpret_cast<const IntVector2*>(&value_)) == *(reinterpret_cast<const IntVector2*>(&rhs.value_));
         return *(reinterpret_cast<const IntVector2*>(&value_)) == *(reinterpret_cast<const IntVector2*>(&rhs.value_));
-        
+
     default:
     default:
         return true;
         return true;
     }
     }
@@ -179,39 +179,39 @@ void Variant::FromString(VariantType type, const char* value)
     case VAR_INT:
     case VAR_INT:
         *this = ToInt(value);
         *this = ToInt(value);
         break;
         break;
-        
+
     case VAR_BOOL:
     case VAR_BOOL:
         *this = ToBool(value);
         *this = ToBool(value);
         break;
         break;
-        
+
     case VAR_FLOAT:
     case VAR_FLOAT:
         *this = ToFloat(value);
         *this = ToFloat(value);
         break;
         break;
-        
+
     case VAR_VECTOR2:
     case VAR_VECTOR2:
         *this = ToVector2(value);
         *this = ToVector2(value);
         break;
         break;
-        
+
     case VAR_VECTOR3:
     case VAR_VECTOR3:
         *this = ToVector3(value);
         *this = ToVector3(value);
         break;
         break;
-        
+
     case VAR_VECTOR4:
     case VAR_VECTOR4:
         *this = ToVector4(value);
         *this = ToVector4(value);
         break;
         break;
-        
+
     case VAR_QUATERNION:
     case VAR_QUATERNION:
         *this = ToQuaternion(value);
         *this = ToQuaternion(value);
         break;
         break;
-        
+
     case VAR_COLOR:
     case VAR_COLOR:
         *this = ToColor(value);
         *this = ToColor(value);
         break;
         break;
-        
+
     case VAR_STRING:
     case VAR_STRING:
         *this = value;
         *this = value;
         break;
         break;
-        
+
     case VAR_BUFFER:
     case VAR_BUFFER:
         {
         {
             SetType(VAR_BUFFER);
             SetType(VAR_BUFFER);
@@ -222,11 +222,11 @@ void Variant::FromString(VariantType type, const char* value)
                 buffer[i] = ToInt(values[i]);
                 buffer[i] = ToInt(values[i]);
         }
         }
         break;
         break;
-        
+
     case VAR_PTR:
     case VAR_PTR:
         *this = (void*)0;
         *this = (void*)0;
         break;
         break;
-        
+
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
         {
         {
             Vector<String> values = String::Split(value, ';');
             Vector<String> values = String::Split(value, ';');
@@ -239,7 +239,7 @@ void Variant::FromString(VariantType type, const char* value)
             }
             }
         }
         }
         break;
         break;
-        
+
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
         {
         {
             Vector<String> values = String::Split(value, ';');
             Vector<String> values = String::Split(value, ';');
@@ -254,15 +254,15 @@ void Variant::FromString(VariantType type, const char* value)
             }
             }
         }
         }
         break;
         break;
-        
+
     case VAR_INTRECT:
     case VAR_INTRECT:
         *this = ToIntRect(value);
         *this = ToIntRect(value);
         break;
         break;
-        
+
     case VAR_INTVECTOR2:
     case VAR_INTVECTOR2:
         *this = ToIntVector2(value);
         *this = ToIntVector2(value);
         break;
         break;
-        
+
     default:
     default:
         SetType(VAR_NONE);
         SetType(VAR_NONE);
     }
     }
@@ -272,7 +272,7 @@ void Variant::SetBuffer(const void* data, unsigned size)
 {
 {
     if (size && !data)
     if (size && !data)
         size = 0;
         size = 0;
-    
+
     SetType(VAR_BUFFER);
     SetType(VAR_BUFFER);
     PODVector<unsigned char>& buffer = *(reinterpret_cast<PODVector<unsigned char>*>(&value_));
     PODVector<unsigned char>& buffer = *(reinterpret_cast<PODVector<unsigned char>*>(&value_));
     buffer.Resize(size);
     buffer.Resize(size);
@@ -291,31 +291,31 @@ String Variant::ToString() const
     {
     {
     case VAR_INT:
     case VAR_INT:
         return String(value_.int_);
         return String(value_.int_);
-        
+
     case VAR_BOOL:
     case VAR_BOOL:
         return String(value_.bool_);
         return String(value_.bool_);
-        
+
     case VAR_FLOAT:
     case VAR_FLOAT:
         return String(value_.float_);
         return String(value_.float_);
-        
+
     case VAR_VECTOR2:
     case VAR_VECTOR2:
         return (reinterpret_cast<const Vector2*>(&value_))->ToString();
         return (reinterpret_cast<const Vector2*>(&value_))->ToString();
-        
+
     case VAR_VECTOR3:
     case VAR_VECTOR3:
         return (reinterpret_cast<const Vector3*>(&value_))->ToString();
         return (reinterpret_cast<const Vector3*>(&value_))->ToString();
-        
+
     case VAR_VECTOR4:
     case VAR_VECTOR4:
         return (reinterpret_cast<const Vector4*>(&value_))->ToString();
         return (reinterpret_cast<const Vector4*>(&value_))->ToString();
-        
+
     case VAR_QUATERNION:
     case VAR_QUATERNION:
         return (reinterpret_cast<const Quaternion*>(&value_))->ToString();
         return (reinterpret_cast<const Quaternion*>(&value_))->ToString();
-        
+
     case VAR_COLOR:
     case VAR_COLOR:
         return (reinterpret_cast<const Color*>(&value_))->ToString();
         return (reinterpret_cast<const Color*>(&value_))->ToString();
-        
+
     case VAR_STRING:
     case VAR_STRING:
         return *(reinterpret_cast<const String*>(&value_));
         return *(reinterpret_cast<const String*>(&value_));
-        
+
     case VAR_BUFFER:
     case VAR_BUFFER:
         {
         {
             const PODVector<unsigned char>& buffer = *(reinterpret_cast<const PODVector<unsigned char>*>(&value_));
             const PODVector<unsigned char>& buffer = *(reinterpret_cast<const PODVector<unsigned char>*>(&value_));
@@ -328,17 +328,17 @@ String Variant::ToString() const
             }
             }
             return ret;
             return ret;
         }
         }
-        
+
     case VAR_PTR:
     case VAR_PTR:
         // Pointer serialization not supported (convert to null)
         // Pointer serialization not supported (convert to null)
         return String(0);
         return String(0);
-        
+
     case VAR_INTRECT:
     case VAR_INTRECT:
         return (reinterpret_cast<const IntRect*>(&value_))->ToString();
         return (reinterpret_cast<const IntRect*>(&value_))->ToString();
-        
+
     case VAR_INTVECTOR2:
     case VAR_INTVECTOR2:
         return (reinterpret_cast<const IntVector2*>(&value_))->ToString();
         return (reinterpret_cast<const IntVector2*>(&value_))->ToString();
-        
+
     default:
     default:
         // VAR_RESOURCEREF, VAR_RESOURCEREFLIST, VAR_VARIANTVECTOR, VAR_VARIANTMAP
         // VAR_RESOURCEREF, VAR_RESOURCEREFLIST, VAR_VARIANTVECTOR, VAR_VARIANTMAP
         // Reference string serialization requires hash-to-name mapping from the context & subsystems. Can not support here
         // Reference string serialization requires hash-to-name mapping from the context & subsystems. Can not support here
@@ -353,41 +353,41 @@ bool Variant::IsZero() const
     {
     {
     case VAR_INT:
     case VAR_INT:
         return value_.int_ == 0;
         return value_.int_ == 0;
-        
+
     case VAR_BOOL:
     case VAR_BOOL:
         return value_.bool_ == false;
         return value_.bool_ == false;
-        
+
     case VAR_FLOAT:
     case VAR_FLOAT:
         return value_.float_ == 0.0f;
         return value_.float_ == 0.0f;
-        
+
     case VAR_VECTOR2:
     case VAR_VECTOR2:
         return *reinterpret_cast<const Vector2*>(&value_) == Vector2::ZERO;
         return *reinterpret_cast<const Vector2*>(&value_) == Vector2::ZERO;
-        
+
     case VAR_VECTOR3:
     case VAR_VECTOR3:
         return *reinterpret_cast<const Vector3*>(&value_) == Vector3::ZERO;
         return *reinterpret_cast<const Vector3*>(&value_) == Vector3::ZERO;
-        
+
     case VAR_VECTOR4:
     case VAR_VECTOR4:
         return *reinterpret_cast<const Vector4*>(&value_) == Vector4::ZERO;
         return *reinterpret_cast<const Vector4*>(&value_) == Vector4::ZERO;
-        
+
     case VAR_QUATERNION:
     case VAR_QUATERNION:
         return *reinterpret_cast<const Quaternion*>(&value_) == Quaternion::IDENTITY;
         return *reinterpret_cast<const Quaternion*>(&value_) == Quaternion::IDENTITY;
-        
+
     case VAR_COLOR:
     case VAR_COLOR:
         // WHITE is considered empty (i.e. default) color in the Color class definition
         // WHITE is considered empty (i.e. default) color in the Color class definition
         return *reinterpret_cast<const Color*>(&value_) == Color::WHITE;
         return *reinterpret_cast<const Color*>(&value_) == Color::WHITE;
-        
+
     case VAR_STRING:
     case VAR_STRING:
         return reinterpret_cast<const String*>(&value_)->Empty();
         return reinterpret_cast<const String*>(&value_)->Empty();
-        
+
     case VAR_BUFFER:
     case VAR_BUFFER:
         return reinterpret_cast<const PODVector<unsigned char>*>(&value_)->Empty();
         return reinterpret_cast<const PODVector<unsigned char>*>(&value_)->Empty();
-        
+
     case VAR_PTR:
     case VAR_PTR:
         return value_.ptr_ == 0;
         return value_.ptr_ == 0;
-        
+
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
         return reinterpret_cast<const ResourceRef*>(&value_)->id_ == StringHash::ZERO;
         return reinterpret_cast<const ResourceRef*>(&value_)->id_ == StringHash::ZERO;
-        
+
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
     {
     {
         Vector<StringHash> ids = reinterpret_cast<const ResourceRefList*>(&value_)->ids_;
         Vector<StringHash> ids = reinterpret_cast<const ResourceRefList*>(&value_)->ids_;
@@ -398,21 +398,21 @@ bool Variant::IsZero() const
         }
         }
         return true;
         return true;
     }
     }
-    
+
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         return reinterpret_cast<const VariantVector*>(&value_)->Empty();
         return reinterpret_cast<const VariantVector*>(&value_)->Empty();
-        
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         return reinterpret_cast<const VariantMap*>(&value_)->Empty();
         return reinterpret_cast<const VariantMap*>(&value_)->Empty();
-        
+
     case VAR_INTRECT:
     case VAR_INTRECT:
         return *reinterpret_cast<const IntRect*>(&value_) == IntRect::ZERO;
         return *reinterpret_cast<const IntRect*>(&value_) == IntRect::ZERO;
-        
+
     case VAR_INTVECTOR2:
     case VAR_INTVECTOR2:
         return *reinterpret_cast<const IntVector2*>(&value_) == IntVector2::ZERO;
         return *reinterpret_cast<const IntVector2*>(&value_) == IntVector2::ZERO;
-        
+
     default:
     default:
-        return false;
+        return true;
     }
     }
 }
 }
 
 
@@ -420,65 +420,65 @@ void Variant::SetType(VariantType newType)
 {
 {
     if (type_ == newType)
     if (type_ == newType)
         return;
         return;
-    
+
     switch (type_)
     switch (type_)
     {
     {
     case VAR_STRING:
     case VAR_STRING:
         (reinterpret_cast<String*>(&value_))->~String();
         (reinterpret_cast<String*>(&value_))->~String();
         break;
         break;
-        
+
     case VAR_BUFFER:
     case VAR_BUFFER:
         (reinterpret_cast<PODVector<unsigned char>*>(&value_))->~PODVector<unsigned char>();
         (reinterpret_cast<PODVector<unsigned char>*>(&value_))->~PODVector<unsigned char>();
         break;
         break;
-        
+
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
         (reinterpret_cast<ResourceRef*>(&value_))->~ResourceRef();
         (reinterpret_cast<ResourceRef*>(&value_))->~ResourceRef();
         break;
         break;
-        
+
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
         (reinterpret_cast<ResourceRefList*>(&value_))->~ResourceRefList();
         (reinterpret_cast<ResourceRefList*>(&value_))->~ResourceRefList();
         break;
         break;
-        
+
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         (reinterpret_cast<VariantVector*>(&value_))->~VariantVector();
         (reinterpret_cast<VariantVector*>(&value_))->~VariantVector();
         break;
         break;
-        
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         (reinterpret_cast<VariantMap*>(&value_))->~VariantMap();
         (reinterpret_cast<VariantMap*>(&value_))->~VariantMap();
         break;
         break;
-        
+
     default:
     default:
         break;
         break;
     }
     }
-    
+
     type_ = newType;
     type_ = newType;
-    
+
     switch (type_)
     switch (type_)
     {
     {
     case VAR_STRING:
     case VAR_STRING:
         new(reinterpret_cast<String*>(&value_)) String();
         new(reinterpret_cast<String*>(&value_)) String();
         break;
         break;
-        
+
     case VAR_BUFFER:
     case VAR_BUFFER:
         new(reinterpret_cast<PODVector<unsigned char>*>(&value_)) PODVector<unsigned char>();
         new(reinterpret_cast<PODVector<unsigned char>*>(&value_)) PODVector<unsigned char>();
         break;
         break;
-        
+
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
         new(reinterpret_cast<ResourceRef*>(&value_)) ResourceRef();
         new(reinterpret_cast<ResourceRef*>(&value_)) ResourceRef();
         break;
         break;
-        
+
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
         new(reinterpret_cast<ResourceRefList*>(&value_)) ResourceRefList();
         new(reinterpret_cast<ResourceRefList*>(&value_)) ResourceRefList();
         break;
         break;
-        
+
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         new(reinterpret_cast<VariantVector*>(&value_)) VariantVector();
         new(reinterpret_cast<VariantVector*>(&value_)) VariantVector();
         break;
         break;
-        
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         new(reinterpret_cast<VariantMap*>(&value_)) VariantMap();
         new(reinterpret_cast<VariantMap*>(&value_)) VariantMap();
         break;
         break;
-        
+
     default:
     default:
         break;
         break;
     }
     }
@@ -648,7 +648,7 @@ VariantType Variant::GetTypeFromName(const char* typeName)
             return (VariantType)index;
             return (VariantType)index;
         ++index;
         ++index;
     }
     }
-    
+
     return VAR_NONE;
     return VAR_NONE;
 }
 }
 
 

+ 2 - 2
Engine/Core/Variant.h

@@ -150,7 +150,7 @@ struct ResourceRefList
     
     
     /// Object type.
     /// Object type.
     ShortStringHash type_;
     ShortStringHash type_;
-    /// List of object identifiers (name hashes.)
+    /// List of object identifiers (name hashes).
     Vector<StringHash> ids_;
     Vector<StringHash> ids_;
     
     
     /// Test for equality with another reference list.
     /// Test for equality with another reference list.
@@ -191,7 +191,7 @@ public:
         *this = (int)value;
         *this = (int)value;
     }
     }
     
     
-    /// Construct from a string hash (convert to integer.)
+    /// Construct from a string hash (convert to integer).
     Variant(const StringHash& value) :
     Variant(const StringHash& value) :
         type_(VAR_NONE)
         type_(VAR_NONE)
     {
     {

+ 2 - 0
Engine/Engine/APITemplates.h

@@ -854,7 +854,9 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "void RemoveAllChildren()", asMETHOD(T, RemoveAllChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void RemoveAllChildren()", asMETHOD(T, RemoveAllChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void Remove()", asMETHOD(T, Remove), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void Remove()", asMETHOD(T, Remove), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ GetChild(const String&in, bool recursive = false) const", asMETHODPR(T, GetChild, (const String&, bool) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ GetChild(const String&in, bool recursive = false) const", asMETHODPR(T, GetChild, (const String&, bool) const, UIElement*), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "UIElement@+ GetChild(const ShortStringHash&in, const Variant&in, bool recursive = false) const", asMETHODPR(T, GetChild, (const ShortStringHash&, const Variant&, bool) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Array<UIElement@>@ GetChildren(bool recursive = false) const", asFUNCTION(UIElementGetChildren), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Array<UIElement@>@ GetChildren(bool recursive = false) const", asFUNCTION(UIElementGetChildren), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "const Variant& GetVar(const ShortStringHash&in)", asMETHOD(T, GetVar), asCALL_THISCALL);
     if (!isSprite)
     if (!isSprite)
     {
     {
         engine->RegisterObjectMethod(className, "IntVector2 ScreenToElement(const IntVector2&in)", asMETHOD(T, ScreenToElement), asCALL_THISCALL);
         engine->RegisterObjectMethod(className, "IntVector2 ScreenToElement(const IntVector2&in)", asMETHOD(T, ScreenToElement), asCALL_THISCALL);

+ 20 - 14
Engine/Engine/CoreAPI.cpp

@@ -108,7 +108,7 @@ static void RegisterStringHash(asIScriptEngine* engine)
     engine->RegisterObjectMethod("StringHash", "StringHash opAdd(const StringHash&in) const", asMETHOD(StringHash, operator +), asCALL_THISCALL);
     engine->RegisterObjectMethod("StringHash", "StringHash opAdd(const StringHash&in) const", asMETHOD(StringHash, operator +), asCALL_THISCALL);
     engine->RegisterObjectMethod("StringHash", "String ToString() const", asMETHOD(StringHash, ToString), asCALL_THISCALL);
     engine->RegisterObjectMethod("StringHash", "String ToString() const", asMETHOD(StringHash, ToString), asCALL_THISCALL);
     engine->RegisterObjectMethod("StringHash", "uint get_value()", asMETHOD(StringHash, Value), asCALL_THISCALL);
     engine->RegisterObjectMethod("StringHash", "uint get_value()", asMETHOD(StringHash, Value), asCALL_THISCALL);
-    
+
     engine->RegisterObjectType("ShortStringHash", sizeof(ShortStringHash), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK);
     engine->RegisterObjectType("ShortStringHash", sizeof(ShortStringHash), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK);
     engine->RegisterObjectBehaviour("ShortStringHash", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructShortStringHash), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ShortStringHash", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructShortStringHash), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ShortStringHash", asBEHAVE_CONSTRUCT, "void f(const ShortStringHash&in)", asFUNCTION(ConstructShortStringHashCopyShort), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ShortStringHash", asBEHAVE_CONSTRUCT, "void f(const ShortStringHash&in)", asFUNCTION(ConstructShortStringHashCopyShort), asCALL_CDECL_OBJLAST);
@@ -174,7 +174,7 @@ static void ResourceRefListSetId(unsigned index, const StringHash& id, ResourceR
         asGetActiveContext()->SetException("Index out of bounds");
         asGetActiveContext()->SetException("Index out of bounds");
         return;
         return;
     }
     }
-    
+
     ptr->ids_[index] = id;
     ptr->ids_[index] = id;
 }
 }
 
 
@@ -185,7 +185,7 @@ static StringHash ResourceRefListGetId(unsigned index, ResourceRefList* ptr)
         asGetActiveContext()->SetException("Index out of bounds");
         asGetActiveContext()->SetException("Index out of bounds");
         return StringHash();
         return StringHash();
     }
     }
-    
+
     return ptr->ids_[index];
     return ptr->ids_[index];
 }
 }
 
 
@@ -334,6 +334,11 @@ static CScriptArray* VariantGetVariantVector(Variant* ptr)
     return VectorToArray<Variant>(ptr->GetVariantVector(), "Array<Variant>");
     return VectorToArray<Variant>(ptr->GetVariantVector(), "Array<Variant>");
 }
 }
 
 
+static bool IsVariantEmpty(Variant* ptr)
+{
+    return ptr->GetType() == VAR_NONE;
+}
+
 static void ConstructVariantMap(VariantMap* ptr)
 static void ConstructVariantMap(VariantMap* ptr)
 {
 {
     new(ptr) VariantMap();
     new(ptr) VariantMap();
@@ -409,7 +414,7 @@ static void RegisterVariant(asIScriptEngine* engine)
     engine->RegisterEnumValue("VariantType", "VAR_VARIANTMAP", VAR_VARIANTMAP);
     engine->RegisterEnumValue("VariantType", "VAR_VARIANTMAP", VAR_VARIANTMAP);
     engine->RegisterEnumValue("VariantType", "VAR_INTRECT", VAR_INTRECT);
     engine->RegisterEnumValue("VariantType", "VAR_INTRECT", VAR_INTRECT);
     engine->RegisterEnumValue("VariantType", "VAR_INTVECTOR2", VAR_INTVECTOR2);
     engine->RegisterEnumValue("VariantType", "VAR_INTVECTOR2", VAR_INTVECTOR2);
-    
+
     engine->RegisterObjectType("ResourceRef", sizeof(ResourceRef), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK);
     engine->RegisterObjectType("ResourceRef", sizeof(ResourceRef), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK);
     engine->RegisterObjectBehaviour("ResourceRef", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructResourceRef), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ResourceRef", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructResourceRef), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ResourceRef", asBEHAVE_CONSTRUCT, "void f(const ResourceRef&in)", asFUNCTION(ConstructResourceRefCopy), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ResourceRef", asBEHAVE_CONSTRUCT, "void f(const ResourceRef&in)", asFUNCTION(ConstructResourceRefCopy), asCALL_CDECL_OBJLAST);
@@ -418,7 +423,7 @@ static void RegisterVariant(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ResourceRef", "bool opEquals(const ResourceRef&in) const", asMETHOD(ResourceRef, operator ==), asCALL_THISCALL);
     engine->RegisterObjectMethod("ResourceRef", "bool opEquals(const ResourceRef&in) const", asMETHOD(ResourceRef, operator ==), asCALL_THISCALL);
     engine->RegisterObjectProperty("ResourceRef", "ShortStringHash type", offsetof(ResourceRef, type_));
     engine->RegisterObjectProperty("ResourceRef", "ShortStringHash type", offsetof(ResourceRef, type_));
     engine->RegisterObjectProperty("ResourceRef", "StringHash id", offsetof(ResourceRef, id_));
     engine->RegisterObjectProperty("ResourceRef", "StringHash id", offsetof(ResourceRef, id_));
-    
+
     engine->RegisterObjectType("ResourceRefList", sizeof(ResourceRefList), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectType("ResourceRefList", sizeof(ResourceRefList), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectBehaviour("ResourceRefList", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructResourceRefList), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ResourceRefList", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructResourceRefList), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ResourceRefList", asBEHAVE_CONSTRUCT, "void f(const ResourceRefList&in)", asFUNCTION(ConstructResourceRefListCopy), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("ResourceRefList", asBEHAVE_CONSTRUCT, "void f(const ResourceRefList&in)", asFUNCTION(ConstructResourceRefListCopy), asCALL_CDECL_OBJLAST);
@@ -431,7 +436,7 @@ static void RegisterVariant(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ResourceRefList", "void set_ids(uint, const StringHash&in) const", asFUNCTION(ResourceRefListSetId), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceRefList", "void set_ids(uint, const StringHash&in) const", asFUNCTION(ResourceRefListSetId), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceRefList", "StringHash get_ids(uint) const", asFUNCTION(ResourceRefListGetId), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ResourceRefList", "StringHash get_ids(uint) const", asFUNCTION(ResourceRefListGetId), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectProperty("ResourceRefList", "ShortStringHash type", offsetof(ResourceRef, type_));
     engine->RegisterObjectProperty("ResourceRefList", "ShortStringHash type", offsetof(ResourceRef, type_));
-    
+
     engine->RegisterObjectType("Variant", sizeof(Variant), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectType("Variant", sizeof(Variant), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectType("VariantMap", sizeof(VariantMap), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectType("VariantMap", sizeof(VariantMap), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructVariant), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructVariant), asCALL_CDECL_OBJLAST);
@@ -517,10 +522,11 @@ static void RegisterVariant(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Variant", "void FromString(const String&in, const String&in)", asMETHODPR(Variant, FromString, (const String&, const String&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "void FromString(const String&in, const String&in)", asMETHODPR(Variant, FromString, (const String&, const String&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "void FromString(VariantType, const String&in)", asMETHODPR(Variant, FromString, (VariantType, const String&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "void FromString(VariantType, const String&in)", asMETHODPR(Variant, FromString, (VariantType, const String&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "String ToString() const", asMETHOD(Variant, ToString), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "String ToString() const", asMETHOD(Variant, ToString), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Variant", "bool IsZero() const", asMETHOD(Variant, IsZero), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Variant", "bool get_zero() const", asMETHOD(Variant, IsZero), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Variant", "bool get_empty() const", asFUNCTION(IsVariantEmpty), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Variant", "VariantType get_type() const", asMETHOD(Variant, GetType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "VariantType get_type() const", asMETHOD(Variant, GetType), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "const String& get_typeName() const", asMETHODPR(Variant, GetTypeName, () const, const String&), asCALL_THISCALL);
     engine->RegisterObjectMethod("Variant", "const String& get_typeName() const", asMETHODPR(Variant, GetTypeName, () const, const String&), asCALL_THISCALL);
-    
+
     engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructVariantMap), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructVariantMap), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_CONSTRUCT, "void f(const VariantMap&in)", asFUNCTION(ConstructVariantMapCopy), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_CONSTRUCT, "void f(const VariantMap&in)", asFUNCTION(ConstructVariantMapCopy), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructVariantMap), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructVariantMap), asCALL_CDECL_OBJLAST);
@@ -611,7 +617,7 @@ static void RegisterTimer(asIScriptEngine* engine)
     engine->RegisterObjectBehaviour("Timer", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructTimer), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("Timer", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructTimer), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Timer", "uint GetMSec(bool)", asMETHOD(Timer, GetMSec), asCALL_THISCALL);
     engine->RegisterObjectMethod("Timer", "uint GetMSec(bool)", asMETHOD(Timer, GetMSec), asCALL_THISCALL);
     engine->RegisterObjectMethod("Timer", "void Reset()", asMETHOD(Timer, Reset), asCALL_THISCALL);
     engine->RegisterObjectMethod("Timer", "void Reset()", asMETHOD(Timer, Reset), asCALL_THISCALL);
-     
+
     RegisterObject<Time>(engine, "Time");
     RegisterObject<Time>(engine, "Time");
     engine->RegisterObjectMethod("Time", "uint get_frameNumber() const", asMETHOD(Time, GetFrameNumber), asCALL_THISCALL);
     engine->RegisterObjectMethod("Time", "uint get_frameNumber() const", asMETHOD(Time, GetFrameNumber), asCALL_THISCALL);
     engine->RegisterObjectMethod("Time", "float get_timeStep() const", asMETHOD(Time, GetTimeStep), asCALL_THISCALL);
     engine->RegisterObjectMethod("Time", "float get_timeStep() const", asMETHOD(Time, GetTimeStep), asCALL_THISCALL);
@@ -715,12 +721,12 @@ static void UnsubscribeFromAllEventsExcept(CScriptArray* exceptions)
     Object* listener = GetScriptContextEventListenerObject();
     Object* listener = GetScriptContextEventListenerObject();
     if (!listener || !exceptions)
     if (!listener || !exceptions)
         return;
         return;
-    
+
     unsigned numExceptions = exceptions->GetSize();
     unsigned numExceptions = exceptions->GetSize();
     PODVector<StringHash> destExceptions(numExceptions);
     PODVector<StringHash> destExceptions(numExceptions);
     for (unsigned i = 0; i < numExceptions; ++i)
     for (unsigned i = 0; i < numExceptions; ++i)
         destExceptions[i] = StringHash(*(static_cast<String*>(exceptions->At(i))));
         destExceptions[i] = StringHash(*(static_cast<String*>(exceptions->At(i))));
-    
+
     listener->UnsubscribeFromAllEventsExcept(destExceptions, true);
     listener->UnsubscribeFromAllEventsExcept(destExceptions, true);
 }
 }
 
 
@@ -766,9 +772,9 @@ void RegisterObject(asIScriptEngine* engine)
     engine->RegisterObjectProperty("AttributeInfo", "String name", offsetof(AttributeInfo, name_));
     engine->RegisterObjectProperty("AttributeInfo", "String name", offsetof(AttributeInfo, name_));
     engine->RegisterObjectProperty("AttributeInfo", "Variant defaultValue", offsetof(AttributeInfo, defaultValue_));
     engine->RegisterObjectProperty("AttributeInfo", "Variant defaultValue", offsetof(AttributeInfo, defaultValue_));
     engine->RegisterObjectProperty("AttributeInfo", "uint mode", offsetof(AttributeInfo, mode_));
     engine->RegisterObjectProperty("AttributeInfo", "uint mode", offsetof(AttributeInfo, mode_));
-    
+
     RegisterObject<Object>(engine, "Object");
     RegisterObject<Object>(engine, "Object");
-    
+
     engine->RegisterGlobalFunction("void SendEvent(const String&in, VariantMap& eventData = VariantMap())", asFUNCTION(SendEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void SendEvent(const String&in, VariantMap& eventData = VariantMap())", asFUNCTION(SendEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void SubscribeToEvent(const String&in, const String&in)", asFUNCTION(SubscribeToEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void SubscribeToEvent(const String&in, const String&in)", asFUNCTION(SubscribeToEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void SubscribeToEvent(Object@+, const String&in, const String&in)", asFUNCTION(SubscribeToSenderEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void SubscribeToEvent(Object@+, const String&in, const String&in)", asFUNCTION(SubscribeToSenderEvent), asCALL_CDECL);
@@ -779,7 +785,7 @@ void RegisterObject(asIScriptEngine* engine)
     engine->RegisterGlobalFunction("void UnsubscribeFromAllEventsExcept(Array<String>@+)", asFUNCTION(UnsubscribeFromAllEventsExcept), asCALL_CDECL);
     engine->RegisterGlobalFunction("void UnsubscribeFromAllEventsExcept(Array<String>@+)", asFUNCTION(UnsubscribeFromAllEventsExcept), asCALL_CDECL);
     engine->RegisterGlobalFunction("Object@+ GetEventSender()", asFUNCTION(GetEventSender), asCALL_CDECL);
     engine->RegisterGlobalFunction("Object@+ GetEventSender()", asFUNCTION(GetEventSender), asCALL_CDECL);
     engine->RegisterGlobalFunction("const String& GetTypeName(ShortStringHash)", asFUNCTION(GetTypeName), asCALL_CDECL);
     engine->RegisterGlobalFunction("const String& GetTypeName(ShortStringHash)", asFUNCTION(GetTypeName), asCALL_CDECL);
-    
+
     engine->RegisterObjectType("WeakHandle", sizeof(WeakPtr<Object>), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectType("WeakHandle", sizeof(WeakPtr<Object>), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
     engine->RegisterObjectBehaviour("WeakHandle", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructWeakHandle), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("WeakHandle", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructWeakHandle), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("WeakHandle", asBEHAVE_CONSTRUCT, "void f(const WeakHandle&in)", asFUNCTION(ConstructWeakHandleCopy), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("WeakHandle", asBEHAVE_CONSTRUCT, "void f(const WeakHandle&in)", asFUNCTION(ConstructWeakHandleCopy), asCALL_CDECL_OBJLAST);

+ 7 - 0
Engine/Engine/UIAPI.cpp

@@ -419,6 +419,12 @@ static void RegisterWindow(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Window", "bool get_resizable() const", asMETHOD(Window, IsResizable), asCALL_THISCALL);
     engine->RegisterObjectMethod("Window", "bool get_resizable() const", asMETHOD(Window, IsResizable), asCALL_THISCALL);
     engine->RegisterObjectMethod("Window", "void set_resizeBorder(const IntRect&in)", asMETHODPR(Window, SetResizeBorder, (const IntRect&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Window", "void set_resizeBorder(const IntRect&in)", asMETHODPR(Window, SetResizeBorder, (const IntRect&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Window", "const IntRect& get_resizeBorder() const", asMETHOD(Window, GetResizeBorder), asCALL_THISCALL);
     engine->RegisterObjectMethod("Window", "const IntRect& get_resizeBorder() const", asMETHOD(Window, GetResizeBorder), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Window", "void set_modal(bool)", asMETHOD(Window, SetModal), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Window", "bool get_modal() const", asMETHOD(Window, IsModal), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Window", "void set_modalFrameColor(const Color&in)", asMETHOD(Window, SetModalFrameColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Window", "const Color& get_modalFrameColor() const", asMETHOD(Window, GetModalFrameColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Window", "void set_modalFrameSize(const IntVector2&in)", asMETHOD(Window, SetModalFrameSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Window", "const IntVector2& get_modalFrameSize() const", asMETHOD(Window, GetModalFrameSize), asCALL_THISCALL);
 }
 }
 
 
 static void FileSelectorSetFilters(CScriptArray* filters, unsigned defaultIndex, FileSelector* ptr)
 static void FileSelectorSetFilters(CScriptArray* filters, unsigned defaultIndex, FileSelector* ptr)
@@ -535,6 +541,7 @@ static void RegisterUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "IntVector2 get_cursorPosition()", asMETHOD(UI, GetCursorPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "IntVector2 get_cursorPosition()", asMETHOD(UI, GetCursorPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_focusElement(UIElement@+)", asMETHOD(UI, SetFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_focusElement(UIElement@+)", asMETHOD(UI, SetFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_focusElement() const", asMETHOD(UI, GetFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_focusElement() const", asMETHOD(UI, GetFocusElement), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "UIElement@+ get_modalElement() const", asMETHOD(UI, GetModalElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_frontElement() const", asMETHOD(UI, GetFrontElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_frontElement() const", asMETHOD(UI, GetFrontElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_root() const", asMETHOD(UI, GetRoot), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_root() const", asMETHOD(UI, GetRoot), asCALL_THISCALL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);

+ 8 - 0
Engine/UI/Menu.cpp

@@ -25,6 +25,7 @@
 #include "InputEvents.h"
 #include "InputEvents.h"
 #include "Log.h"
 #include "Log.h"
 #include "Menu.h"
 #include "Menu.h"
+#include "UI.h"
 #include "UIEvents.h"
 #include "UIEvents.h"
 
 
 #include "DebugNew.h"
 #include "DebugNew.h"
@@ -362,7 +363,14 @@ void Menu::HandleKeyDown(StringHash eventType, VariantMap& eventData)
     // Activate if accelerator key pressed
     // Activate if accelerator key pressed
     if (eventData[P_KEY].GetInt() == acceleratorKey_ && (acceleratorQualifiers_ == QUAL_ANY || eventData[P_QUALIFIERS].GetInt() ==
     if (eventData[P_KEY].GetInt() == acceleratorKey_ && (acceleratorQualifiers_ == QUAL_ANY || eventData[P_QUALIFIERS].GetInt() ==
         acceleratorQualifiers_) && eventData[P_REPEAT].GetBool() == false)
         acceleratorQualifiers_) && eventData[P_REPEAT].GetBool() == false)
+    {
+        // Ignore if UI has modal element
+        UI* ui = GetSubsystem<UI>();
+        if (ui->GetModalElement())
+            return;
+
         HandlePressedReleased(eventType, eventData);
         HandlePressedReleased(eventType, eventData);
+    }
 }
 }
 
 
 }
 }

+ 153 - 115
Engine/UI/UI.cpp

@@ -82,10 +82,10 @@ UI::UI(Context* context) :
     SubscribeToEvent(E_TOUCHMOVE, HANDLER(UI, HandleTouchMove));
     SubscribeToEvent(E_TOUCHMOVE, HANDLER(UI, HandleTouchMove));
     SubscribeToEvent(E_KEYDOWN, HANDLER(UI, HandleKeyDown));
     SubscribeToEvent(E_KEYDOWN, HANDLER(UI, HandleKeyDown));
     SubscribeToEvent(E_CHAR, HANDLER(UI, HandleChar));
     SubscribeToEvent(E_CHAR, HANDLER(UI, HandleChar));
-    
+
     // Delay SubscribeToEvent(E_POSTUPDATE, HANDLER(UI, HandlePostUpdate)) and
     // Delay SubscribeToEvent(E_POSTUPDATE, HANDLER(UI, HandlePostUpdate)) and
     // SubscribeToEvent(E_RENDERUPDATE, HANDLER(UI, HandleRenderUpdate)) until UI is initialized
     // SubscribeToEvent(E_RENDERUPDATE, HANDLER(UI, HandleRenderUpdate)) until UI is initialized
-    
+
     // Try to initialize right now, but skip if screen mode is not yet set
     // Try to initialize right now, but skip if screen mode is not yet set
     Initialize();
     Initialize();
 }
 }
@@ -106,7 +106,7 @@ void UI::SetCursor(Cursor* cursor)
     {
     {
         rootElement_->AddChild(cursor);
         rootElement_->AddChild(cursor);
         cursor_ = cursor;
         cursor_ = cursor;
-        
+
         IntVector2 pos = cursor_->GetPosition();
         IntVector2 pos = cursor_->GetPosition();
         const IntVector2& rootSize = rootElement_->GetSize();
         const IntVector2& rootSize = rootElement_->GetSize();
         pos.x_ = Clamp(pos.x_, 0, rootSize.x_ - 1);
         pos.x_ = Clamp(pos.x_, 0, rootSize.x_ - 1);
@@ -118,47 +118,71 @@ void UI::SetCursor(Cursor* cursor)
 void UI::SetFocusElement(UIElement* element)
 void UI::SetFocusElement(UIElement* element)
 {
 {
     using namespace FocusChanged;
     using namespace FocusChanged;
-    
+
     VariantMap eventData;
     VariantMap eventData;
     eventData[P_CLICKEDELEMENT] = (void*)element;
     eventData[P_CLICKEDELEMENT] = (void*)element;
-    
+
     if (element)
     if (element)
     {
     {
         // Return if already has focus
         // Return if already has focus
         if (focusElement_ == element)
         if (focusElement_ == element)
             return;
             return;
-        
+
+        // Only allow child elements of the modal element to receive focus
+        if (modalElement_)
+        {
+            UIElement* topLevel = element->GetParent();
+            while (topLevel && topLevel->GetParent() != rootElement_)
+                topLevel = topLevel->GetParent();
+            if (topLevel != modalElement_)
+                return;
+        }
+
         // Search for an element in the hierarchy that can alter focus. If none found, exit
         // Search for an element in the hierarchy that can alter focus. If none found, exit
         element = GetFocusableElement(element);
         element = GetFocusableElement(element);
         if (!element)
         if (!element)
             return;
             return;
     }
     }
-    
+
     // Remove focus from the old element
     // Remove focus from the old element
     if (focusElement_)
     if (focusElement_)
     {
     {
         UIElement* oldFocusElement = focusElement_;
         UIElement* oldFocusElement = focusElement_;
         focusElement_.Reset();
         focusElement_.Reset();
-        
+
         VariantMap focusEventData;
         VariantMap focusEventData;
         focusEventData[Defocused::P_ELEMENT] = oldFocusElement;
         focusEventData[Defocused::P_ELEMENT] = oldFocusElement;
         oldFocusElement->SendEvent(E_DEFOCUSED, focusEventData);
         oldFocusElement->SendEvent(E_DEFOCUSED, focusEventData);
     }
     }
-    
+
     // Then set focus to the new
     // Then set focus to the new
     if (element && element->GetFocusMode() >= FM_FOCUSABLE)
     if (element && element->GetFocusMode() >= FM_FOCUSABLE)
     {
     {
         focusElement_ = element;
         focusElement_ = element;
-        
+
         VariantMap focusEventData;
         VariantMap focusEventData;
         focusEventData[Focused::P_ELEMENT] = element;
         focusEventData[Focused::P_ELEMENT] = element;
         element->SendEvent(E_FOCUSED, focusEventData);
         element->SendEvent(E_FOCUSED, focusEventData);
     }
     }
-    
+
     eventData[P_ELEMENT] = (void*)element;
     eventData[P_ELEMENT] = (void*)element;
     SendEvent(E_FOCUSCHANGED, eventData);
     SendEvent(E_FOCUSCHANGED, eventData);
 }
 }
 
 
+bool UI::SetModalElement(UIElement* modalElement)
+{
+    // Only allow one modal element at a time
+    if (modalElement_)
+        return false;
+
+    // Currently only allow modal window
+    if (modalElement && modalElement->GetType() != Window::GetTypeStatic())
+        return false;
+
+    modalElement_ = modalElement;
+    return true;
+}
+
 void UI::Clear()
 void UI::Clear()
 {
 {
     rootElement_->RemoveAllChildren();
     rootElement_->RemoveAllChildren();
@@ -169,18 +193,18 @@ void UI::Clear()
 void UI::Update(float timeStep)
 void UI::Update(float timeStep)
 {
 {
     assert(rootElement_);
     assert(rootElement_);
-    
+
     PROFILE(UpdateUI);
     PROFILE(UpdateUI);
-    
+
     if (cursor_ && cursor_->IsVisible())
     if (cursor_ && cursor_->IsVisible())
     {
     {
         IntVector2 pos = cursor_->GetPosition();
         IntVector2 pos = cursor_->GetPosition();
         WeakPtr<UIElement> element(GetElementAt(pos));
         WeakPtr<UIElement> element(GetElementAt(pos));
-        
+
         bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
         bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
         bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET) != 0;
         bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET) != 0;
         bool dragDropTest = dragSource && dragTarget && element != dragElement_;
         bool dragDropTest = dragSource && dragTarget && element != dragElement_;
-        
+
         // Hover effect
         // Hover effect
         // If a drag is going on, transmit hover only to the element being dragged, unless it's a drop target
         // If a drag is going on, transmit hover only to the element being dragged, unless it's a drop target
         if (element && element->IsEnabled())
         if (element && element->IsEnabled())
@@ -190,7 +214,7 @@ void UI::Update(float timeStep)
         }
         }
         else
         else
             cursor_->SetShape(CS_NORMAL);
             cursor_->SetShape(CS_NORMAL);
-        
+
         // Drag and drop test
         // Drag and drop test
         if (dragDropTest)
         if (dragDropTest)
         {
         {
@@ -198,7 +222,7 @@ void UI::Update(float timeStep)
             if (accept)
             if (accept)
             {
             {
                 using namespace DragDropTest;
                 using namespace DragDropTest;
-                
+
                 VariantMap eventData;
                 VariantMap eventData;
                 eventData[P_SOURCE] = (void*)dragElement_.Get();
                 eventData[P_SOURCE] = (void*)dragElement_.Get();
                 eventData[P_TARGET] = (void*)element.Get();
                 eventData[P_TARGET] = (void*)element.Get();
@@ -206,13 +230,13 @@ void UI::Update(float timeStep)
                 SendEvent(E_DRAGDROPTEST, eventData);
                 SendEvent(E_DRAGDROPTEST, eventData);
                 accept = eventData[P_ACCEPT].GetBool();
                 accept = eventData[P_ACCEPT].GetBool();
             }
             }
-            
+
             cursor_->SetShape(accept ? CS_ACCEPTDROP : CS_REJECTDROP);
             cursor_->SetShape(accept ? CS_ACCEPTDROP : CS_REJECTDROP);
         }
         }
         else if (dragSource)
         else if (dragSource)
             cursor_->SetShape(dragElement_ == element ? CS_ACCEPTDROP : CS_REJECTDROP);
             cursor_->SetShape(dragElement_ == element ? CS_ACCEPTDROP : CS_REJECTDROP);
     }
     }
-    
+
     // Touch hover
     // Touch hover
     Input* input = GetSubsystem<Input>();
     Input* input = GetSubsystem<Input>();
     if (input)
     if (input)
@@ -226,16 +250,16 @@ void UI::Update(float timeStep)
                 element->OnHover(element->ScreenToElement(touch->position_), touch->position_, MOUSEB_LEFT, 0, 0);
                 element->OnHover(element->ScreenToElement(touch->position_), touch->position_, MOUSEB_LEFT, 0, 0);
         }
         }
     }
     }
-    
+
     Update(timeStep, rootElement_);
     Update(timeStep, rootElement_);
 }
 }
 
 
 void UI::RenderUpdate()
 void UI::RenderUpdate()
 {
 {
     assert(rootElement_ && graphics_);
     assert(rootElement_ && graphics_);
-    
+
     PROFILE(GetUIBatches);
     PROFILE(GetUIBatches);
-    
+
     // If the OS cursor is visible, do not render the UI's own cursor
     // If the OS cursor is visible, do not render the UI's own cursor
     bool osCursorVisible = GetSubsystem<Input>()->IsMouseVisible();
     bool osCursorVisible = GetSubsystem<Input>()->IsMouseVisible();
     bool uiCursorVisible = false;
     bool uiCursorVisible = false;
@@ -244,7 +268,7 @@ void UI::RenderUpdate()
         uiCursorVisible = cursor_->IsVisible();
         uiCursorVisible = cursor_->IsVisible();
         cursor_->SetTempVisible(false);
         cursor_->SetTempVisible(false);
     }
     }
-    
+
     // Get rendering batches from the UI elements
     // Get rendering batches from the UI elements
     batches_.Clear();
     batches_.Clear();
     vertexData_.Clear();
     vertexData_.Clear();
@@ -260,24 +284,24 @@ void UI::Render()
 {
 {
     // Engine does not render when window is closed or device is lost
     // Engine does not render when window is closed or device is lost
     assert(graphics_ && graphics_->IsInitialized() && !graphics_->IsDeviceLost());
     assert(graphics_ && graphics_->IsInitialized() && !graphics_->IsDeviceLost());
-    
+
     PROFILE(RenderUI);
     PROFILE(RenderUI);
-    
+
     if (vertexData_.Empty())
     if (vertexData_.Empty())
         return;
         return;
-    
+
     // Update quad geometry into the vertex buffer
     // Update quad geometry into the vertex buffer
     unsigned numVertices = vertexData_.Size() / UI_VERTEX_SIZE;
     unsigned numVertices = vertexData_.Size() / UI_VERTEX_SIZE;
     // Resize the vertex buffer if too small or much too large
     // Resize the vertex buffer if too small or much too large
     if (vertexBuffer_->GetVertexCount() < numVertices || vertexBuffer_->GetVertexCount() > numVertices * 2)
     if (vertexBuffer_->GetVertexCount() < numVertices || vertexBuffer_->GetVertexCount() > numVertices * 2)
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
         vertexBuffer_->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
-    
+
     vertexBuffer_->SetData(&vertexData_[0]);
     vertexBuffer_->SetData(&vertexData_[0]);
-    
+
     Vector2 invScreenSize(1.0f / (float)graphics_->GetWidth(), 1.0f / (float)graphics_->GetHeight());
     Vector2 invScreenSize(1.0f / (float)graphics_->GetWidth(), 1.0f / (float)graphics_->GetHeight());
     Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
     Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
     Vector2 offset(-1.0f, 1.0f);
     Vector2 offset(-1.0f, 1.0f);
-    
+
     Matrix4 projection(Matrix4::IDENTITY);
     Matrix4 projection(Matrix4::IDENTITY);
     projection.m00_ = scale.x_;
     projection.m00_ = scale.x_;
     projection.m03_ = offset.x_;
     projection.m03_ = offset.x_;
@@ -286,25 +310,25 @@ void UI::Render()
     projection.m22_ = 1.0f;
     projection.m22_ = 1.0f;
     projection.m23_ = 0.0f;
     projection.m23_ = 0.0f;
     projection.m33_ = 1.0f;
     projection.m33_ = 1.0f;
-    
+
     graphics_->ClearParameterSources();
     graphics_->ClearParameterSources();
     graphics_->SetCullMode(CULL_CCW);
     graphics_->SetCullMode(CULL_CCW);
     graphics_->SetDepthTest(CMP_ALWAYS);
     graphics_->SetDepthTest(CMP_ALWAYS);
     graphics_->SetDepthWrite(false);
     graphics_->SetDepthWrite(false);
     graphics_->SetStencilTest(false);
     graphics_->SetStencilTest(false);
     graphics_->ResetRenderTargets();
     graphics_->ResetRenderTargets();
-    
+
     ShaderVariation* ps = 0;
     ShaderVariation* ps = 0;
     ShaderVariation* vs = 0;
     ShaderVariation* vs = 0;
-    
+
     unsigned alphaFormat = Graphics::GetAlphaFormat();
     unsigned alphaFormat = Graphics::GetAlphaFormat();
-    
+
     for (unsigned i = 0; i < batches_.Size(); ++i)
     for (unsigned i = 0; i < batches_.Size(); ++i)
     {
     {
         const UIBatch& batch = batches_[i];
         const UIBatch& batch = batches_[i];
         if (batch.vertexStart_ == batch.vertexEnd_)
         if (batch.vertexStart_ == batch.vertexEnd_)
             continue;
             continue;
-        
+
         if (!batch.texture_)
         if (!batch.texture_)
         {
         {
             ps = noTexturePS_;
             ps = noTexturePS_;
@@ -314,7 +338,7 @@ void UI::Render()
         {
         {
             // If texture contains only an alpha channel, use alpha shader (for fonts)
             // If texture contains only an alpha channel, use alpha shader (for fonts)
             vs = diffTextureVS_;
             vs = diffTextureVS_;
-            
+
             if (batch.texture_->GetFormat() == alphaFormat)
             if (batch.texture_->GetFormat() == alphaFormat)
                 ps = alphaTexturePS_;
                 ps = alphaTexturePS_;
             else if (batch.blendMode_ != BLEND_ALPHA && batch.blendMode_ != BLEND_ADDALPHA && batch.blendMode_ != BLEND_PREMULALPHA)
             else if (batch.blendMode_ != BLEND_ALPHA && batch.blendMode_ != BLEND_ADDALPHA && batch.blendMode_ != BLEND_PREMULALPHA)
@@ -322,7 +346,7 @@ void UI::Render()
             else
             else
                 ps = diffTexturePS_;
                 ps = diffTexturePS_;
         }
         }
-        
+
         graphics_->SetShaders(vs, ps);
         graphics_->SetShaders(vs, ps);
         if (graphics_->NeedParameterUpdate(SP_OBJECTTRANSFORM, this))
         if (graphics_->NeedParameterUpdate(SP_OBJECTTRANSFORM, this))
             graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
             graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
@@ -330,7 +354,7 @@ void UI::Render()
             graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
             graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
         if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
         if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
             graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
             graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
-        
+
         graphics_->SetBlendMode(batch.blendMode_);
         graphics_->SetBlendMode(batch.blendMode_);
         graphics_->SetScissorTest(true, batch.scissor_);
         graphics_->SetScissorTest(true, batch.scissor_);
         graphics_->SetTexture(0, batch.texture_);
         graphics_->SetTexture(0, batch.texture_);
@@ -352,28 +376,28 @@ SharedPtr<UIElement> UI::LoadLayout(Deserializer& source, XMLFile* styleFile)
 SharedPtr<UIElement> UI::LoadLayout(XMLFile* file, XMLFile* styleFile)
 SharedPtr<UIElement> UI::LoadLayout(XMLFile* file, XMLFile* styleFile)
 {
 {
     PROFILE(LoadUILayout);
     PROFILE(LoadUILayout);
-    
+
     SharedPtr<UIElement> root;
     SharedPtr<UIElement> root;
-    
+
     if (!file)
     if (!file)
     {
     {
         LOGERROR("Null UI layout XML file");
         LOGERROR("Null UI layout XML file");
         return root;
         return root;
     }
     }
-    
+
     LOGDEBUG("Loading UI layout " + file->GetName());
     LOGDEBUG("Loading UI layout " + file->GetName());
-    
+
     XMLElement rootElem = file->GetRoot("element");
     XMLElement rootElem = file->GetRoot("element");
     if (!rootElem)
     if (!rootElem)
     {
     {
         LOGERROR("No root UI element in " + file->GetName());
         LOGERROR("No root UI element in " + file->GetName());
         return root;
         return root;
     }
     }
-    
+
     String typeName = rootElem.GetAttribute("type");
     String typeName = rootElem.GetAttribute("type");
     if (typeName.Empty())
     if (typeName.Empty())
         typeName = "UIElement";
         typeName = "UIElement";
-    
+
     root = DynamicCast<UIElement>(context_->CreateObject(typeName));
     root = DynamicCast<UIElement>(context_->CreateObject(typeName));
     if (!root)
     if (!root)
     {
     {
@@ -395,7 +419,7 @@ SharedPtr<UIElement> UI::LoadLayout(XMLFile* file, XMLFile* styleFile)
 bool UI::SaveLayout(Serializer& dest, UIElement* element)
 bool UI::SaveLayout(Serializer& dest, UIElement* element)
 {
 {
     PROFILE(SaveUILayout);
     PROFILE(SaveUILayout);
-    
+
     if (element)
     if (element)
         return element->SaveXML(dest);
         return element->SaveXML(dest);
     else
     else
@@ -415,7 +439,22 @@ void UI::SetNonFocusedMouseWheel(bool nonFocusedMouseWheel)
 UIElement* UI::GetElementAt(const IntVector2& position, bool enabledOnly)
 UIElement* UI::GetElementAt(const IntVector2& position, bool enabledOnly)
 {
 {
     UIElement* result = 0;
     UIElement* result = 0;
-    GetElementAt(result, rootElement_, position, enabledOnly);
+    if (modalElement_)
+    {
+        // Create temporary root which only contains the modal element
+        SharedPtr<UIElement> fakeRoot(new UIElement(context_));
+
+        // Special care is needed to insert into fakeRoot without altering the actual modal element's parent
+        Vector<SharedPtr<UIElement> >& children = const_cast<Vector<SharedPtr<UIElement> >& >(fakeRoot->GetChildren());
+        children.Push(SharedPtr<UIElement>(modalElement_));
+
+        GetElementAt(result, fakeRoot, position, enabledOnly);
+
+        // Special care is needed to clear the children vector before the shared pointer goes out of scope to prevent it from detaching modal element
+        children.Clear();
+    }
+    else
+        GetElementAt(result, rootElement_, position, enabledOnly);
     return result;
     return result;
 }
 }
 
 
@@ -424,23 +463,22 @@ UIElement* UI::GetElementAt(int x, int y, bool enabledOnly)
     return GetElementAt(IntVector2(x, y), enabledOnly);
     return GetElementAt(IntVector2(x, y), enabledOnly);
 }
 }
 
 
-UIElement* UI::GetFocusElement() const
-{
-    return focusElement_;
-}
-
 UIElement* UI::GetFrontElement() const
 UIElement* UI::GetFrontElement() const
 {
 {
+    // If modal element is set then return it
+    if (modalElement_)
+        return modalElement_;
+
     const Vector<SharedPtr<UIElement> >& rootChildren = rootElement_->GetChildren();
     const Vector<SharedPtr<UIElement> >& rootChildren = rootElement_->GetChildren();
     int maxPriority = M_MIN_INT;
     int maxPriority = M_MIN_INT;
     UIElement* front = 0;
     UIElement* front = 0;
-    
+
     for (unsigned i = 0; i < rootChildren.Size(); ++i)
     for (unsigned i = 0; i < rootChildren.Size(); ++i)
     {
     {
         // Do not take into account input-disabled elements, hidden elements or those that are always in the front
         // Do not take into account input-disabled elements, hidden elements or those that are always in the front
         if (!rootChildren[i]->IsEnabled() || !rootChildren[i]->IsVisible() || !rootChildren[i]->GetBringToBack())
         if (!rootChildren[i]->IsEnabled() || !rootChildren[i]->IsVisible() || !rootChildren[i]->GetBringToBack())
             continue;
             continue;
-        
+
         int priority = rootChildren[i]->GetPriority();
         int priority = rootChildren[i]->GetPriority();
         if (priority > maxPriority)
         if (priority > maxPriority)
         {
         {
@@ -448,7 +486,7 @@ UIElement* UI::GetFrontElement() const
             front = rootChildren[i];
             front = rootChildren[i];
         }
         }
     }
     }
-    
+
     return front;
     return front;
 }
 }
 
 
@@ -464,27 +502,27 @@ void UI::Initialize()
 {
 {
     Graphics* graphics = GetSubsystem<Graphics>();
     Graphics* graphics = GetSubsystem<Graphics>();
     Renderer* renderer = GetSubsystem<Renderer>();
     Renderer* renderer = GetSubsystem<Renderer>();
-    
+
     if (!graphics || !graphics->IsInitialized() || !renderer)
     if (!graphics || !graphics->IsInitialized() || !renderer)
         return;
         return;
-    
+
     PROFILE(InitUI);
     PROFILE(InitUI);
-    
+
     graphics_ = graphics;
     graphics_ = graphics;
-    
+
     rootElement_->SetSize(graphics->GetWidth(), graphics->GetHeight());
     rootElement_->SetSize(graphics->GetWidth(), graphics->GetHeight());
-    
+
     noTextureVS_ = renderer->GetVertexShader("Basic_VCol");
     noTextureVS_ = renderer->GetVertexShader("Basic_VCol");
     diffTextureVS_ = renderer->GetVertexShader("Basic_DiffVCol");
     diffTextureVS_ = renderer->GetVertexShader("Basic_DiffVCol");
     noTexturePS_ = renderer->GetPixelShader("Basic_VCol");
     noTexturePS_ = renderer->GetPixelShader("Basic_VCol");
     diffTexturePS_ = renderer->GetPixelShader("Basic_DiffVCol");
     diffTexturePS_ = renderer->GetPixelShader("Basic_DiffVCol");
     diffMaskTexturePS_ = renderer->GetPixelShader("Basic_DiffAlphaMaskVCol");
     diffMaskTexturePS_ = renderer->GetPixelShader("Basic_DiffAlphaMaskVCol");
     alphaTexturePS_ = renderer->GetPixelShader("Basic_AlphaVCol");
     alphaTexturePS_ = renderer->GetPixelShader("Basic_AlphaVCol");
-    
+
     vertexBuffer_ = new VertexBuffer(context_);
     vertexBuffer_ = new VertexBuffer(context_);
-    
+
     initialized_ = true;
     initialized_ = true;
-    
+
     SubscribeToEvent(E_POSTUPDATE, HANDLER(UI, HandlePostUpdate));
     SubscribeToEvent(E_POSTUPDATE, HANDLER(UI, HandlePostUpdate));
     SubscribeToEvent(E_RENDERUPDATE, HANDLER(UI, HandleRenderUpdate));
     SubscribeToEvent(E_RENDERUPDATE, HANDLER(UI, HandleRenderUpdate));
 
 
@@ -494,7 +532,7 @@ void UI::Initialize()
 void UI::Update(float timeStep, UIElement* element)
 void UI::Update(float timeStep, UIElement* element)
 {
 {
     element->Update(timeStep);
     element->Update(timeStep);
-    
+
     const Vector<SharedPtr<UIElement> >& children = element->GetChildren();
     const Vector<SharedPtr<UIElement> >& children = element->GetChildren();
     for (Vector<SharedPtr<UIElement> >::ConstIterator i = children.Begin(); i != children.End(); ++i)
     for (Vector<SharedPtr<UIElement> >::ConstIterator i = children.Begin(); i != children.End(); ++i)
         Update(timeStep, *i);
         Update(timeStep, *i);
@@ -506,12 +544,12 @@ void UI::GetBatches(UIElement* element, IntRect currentScissor)
     element->AdjustScissor(currentScissor);
     element->AdjustScissor(currentScissor);
     if (currentScissor.left_ == currentScissor.right_ || currentScissor.top_ == currentScissor.bottom_)
     if (currentScissor.left_ == currentScissor.right_ || currentScissor.top_ == currentScissor.bottom_)
         return;
         return;
-    
+
     element->SortChildren();
     element->SortChildren();
     const Vector<SharedPtr<UIElement> >& children = element->GetChildren();
     const Vector<SharedPtr<UIElement> >& children = element->GetChildren();
     if (children.Empty())
     if (children.Empty())
         return;
         return;
-    
+
     // For non-root elements draw all children of same priority before recursing into their children: assumption is that they have
     // For non-root elements draw all children of same priority before recursing into their children: assumption is that they have
     // same renderstate
     // same renderstate
     Vector<SharedPtr<UIElement> >::ConstIterator i = children.Begin();
     Vector<SharedPtr<UIElement> >::ConstIterator i = children.Begin();
@@ -554,16 +592,16 @@ void UI::GetElementAt(UIElement*& result, UIElement* current, const IntVector2&
 {
 {
     if (!current)
     if (!current)
         return;
         return;
-    
+
     current->SortChildren();
     current->SortChildren();
     const Vector<SharedPtr<UIElement> >& children = current->GetChildren();
     const Vector<SharedPtr<UIElement> >& children = current->GetChildren();
     LayoutMode parentLayoutMode = current->GetLayoutMode();
     LayoutMode parentLayoutMode = current->GetLayoutMode();
-    
+
     for (unsigned i = 0; i < children.Size(); ++i)
     for (unsigned i = 0; i < children.Size(); ++i)
     {
     {
         UIElement* element = children[i];
         UIElement* element = children[i];
         bool hasChildren = element->GetNumChildren() > 0;
         bool hasChildren = element->GetNumChildren() > 0;
-        
+
         if (element != cursor_.Get() && element->IsVisible())
         if (element != cursor_.Get() && element->IsVisible())
         {
         {
             if (element->IsInside(position, true))
             if (element->IsInside(position, true))
@@ -572,7 +610,7 @@ void UI::GetElementAt(UIElement*& result, UIElement* current, const IntVector2&
                 // are sorted from lowest to highest priority, the topmost match should remain
                 // are sorted from lowest to highest priority, the topmost match should remain
                 if (element->IsEnabled() || !enabledOnly)
                 if (element->IsEnabled() || !enabledOnly)
                     result = element;
                     result = element;
-                
+
                 if (hasChildren)
                 if (hasChildren)
                     GetElementAt(result, element, position, enabledOnly);
                     GetElementAt(result, element, position, enabledOnly);
                 // Layout optimization: if the element has no children, can break out after the first match
                 // Layout optimization: if the element has no children, can break out after the first match
@@ -595,7 +633,7 @@ void UI::GetElementAt(UIElement*& result, UIElement* current, const IntVector2&
                         int screenPos = (parentLayoutMode == LM_HORIZONTAL) ? element->GetScreenPosition().x_ :
                         int screenPos = (parentLayoutMode == LM_HORIZONTAL) ? element->GetScreenPosition().x_ :
                             element->GetScreenPosition().y_;
                             element->GetScreenPosition().y_;
                         int layoutMinSize = current->GetLayoutMinSize();
                         int layoutMinSize = current->GetLayoutMinSize();
-                        
+
                         if (screenPos < 0 && layoutMinSize > 0)
                         if (screenPos < 0 && layoutMinSize > 0)
                         {
                         {
                             unsigned toSkip = -screenPos / layoutMinSize;
                             unsigned toSkip = -screenPos / layoutMinSize;
@@ -633,7 +671,7 @@ UIElement* UI::GetFocusableElement(UIElement* element)
 void UI::HandleScreenMode(StringHash eventType, VariantMap& eventData)
 void UI::HandleScreenMode(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace ScreenMode;
     using namespace ScreenMode;
-    
+
     if (!initialized_)
     if (!initialized_)
         Initialize();
         Initialize();
     else
     else
@@ -644,14 +682,14 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 {
 {
     mouseButtons_ = eventData[MouseButtonDown::P_BUTTONS].GetInt();
     mouseButtons_ = eventData[MouseButtonDown::P_BUTTONS].GetInt();
     qualifiers_ = eventData[MouseButtonDown::P_QUALIFIERS].GetInt();
     qualifiers_ = eventData[MouseButtonDown::P_QUALIFIERS].GetInt();
-    
+
     if (cursor_ && cursor_->IsVisible())
     if (cursor_ && cursor_->IsVisible())
     {
     {
         int button = eventData[MouseButtonDown::P_BUTTON].GetInt();
         int button = eventData[MouseButtonDown::P_BUTTON].GetInt();
 
 
         IntVector2 pos = cursor_->GetPosition();
         IntVector2 pos = cursor_->GetPosition();
         WeakPtr<UIElement> element(GetElementAt(pos));
         WeakPtr<UIElement> element(GetElementAt(pos));
-        
+
         if (element)
         if (element)
         {
         {
             // Handle focusing & bringing to front
             // Handle focusing & bringing to front
@@ -660,10 +698,10 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
                 SetFocusElement(element);
                 SetFocusElement(element);
                 element->BringToFront();
                 element->BringToFront();
             }
             }
-            
+
             // Handle click
             // Handle click
             element->OnClick(element->ScreenToElement(pos), pos, mouseButtons_, qualifiers_, cursor_);
             element->OnClick(element->ScreenToElement(pos), pos, mouseButtons_, qualifiers_, cursor_);
-            
+
             // Handle start of drag. OnClick() may have caused destruction of the element, so check the pointer again
             // Handle start of drag. OnClick() may have caused destruction of the element, so check the pointer again
             if (element && !dragElement_ && mouseButtons_ == MOUSEB_LEFT)
             if (element && !dragElement_ && mouseButtons_ == MOUSEB_LEFT)
             {
             {
@@ -676,9 +714,9 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
             // If clicked over no element, or a disabled element, lose focus
             // If clicked over no element, or a disabled element, lose focus
             SetFocusElement(0);
             SetFocusElement(0);
         }
         }
-        
+
         using namespace UIMouseClick;
         using namespace UIMouseClick;
-        
+
         VariantMap eventData;
         VariantMap eventData;
         eventData[UIMouseClick::P_ELEMENT] = (void*)element.Get();
         eventData[UIMouseClick::P_ELEMENT] = (void*)element.Get();
         eventData[UIMouseClick::P_X] = pos.x_;
         eventData[UIMouseClick::P_X] = pos.x_;
@@ -693,20 +731,20 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace MouseButtonUp;
     using namespace MouseButtonUp;
-    
+
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
-    
+
     if (cursor_ && (cursor_->IsVisible() || dragElement_))
     if (cursor_ && (cursor_->IsVisible() || dragElement_))
     {
     {
         IntVector2 pos = cursor_->GetPosition();
         IntVector2 pos = cursor_->GetPosition();
-        
+
         if (dragElement_ && !mouseButtons_)
         if (dragElement_ && !mouseButtons_)
         {
         {
             if (dragElement_->IsEnabled() && dragElement_->IsVisible())
             if (dragElement_->IsEnabled() && dragElement_->IsVisible())
             {
             {
                 dragElement_->OnDragEnd(dragElement_->ScreenToElement(pos), pos, cursor_);
                 dragElement_->OnDragEnd(dragElement_->ScreenToElement(pos), pos, cursor_);
-                
+
                 // Drag and drop finish
                 // Drag and drop finish
                 bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
                 bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
                 if (dragSource)
                 if (dragSource)
@@ -714,16 +752,16 @@ void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
                     WeakPtr<UIElement> target(GetElementAt(pos));
                     WeakPtr<UIElement> target(GetElementAt(pos));
                     bool dragTarget = target && (target->GetDragDropMode() & DD_TARGET) != 0;
                     bool dragTarget = target && (target->GetDragDropMode() & DD_TARGET) != 0;
                     bool dragDropFinish = dragSource && dragTarget && target != dragElement_;
                     bool dragDropFinish = dragSource && dragTarget && target != dragElement_;
-                    
+
                     if (dragDropFinish)
                     if (dragDropFinish)
                     {
                     {
                         bool accept = target->OnDragDropFinish(dragElement_);
                         bool accept = target->OnDragDropFinish(dragElement_);
-                        
+
                         // OnDragDropFinish() may have caused destruction of the elements, so check the pointers again
                         // OnDragDropFinish() may have caused destruction of the elements, so check the pointers again
                         if (accept && dragElement_ && target)
                         if (accept && dragElement_ && target)
                         {
                         {
                             using namespace DragDropFinish;
                             using namespace DragDropFinish;
-                            
+
                             VariantMap eventData;
                             VariantMap eventData;
                             eventData[P_SOURCE] = (void*)dragElement_.Get();
                             eventData[P_SOURCE] = (void*)dragElement_.Get();
                             eventData[P_TARGET] = (void*)target.Get();
                             eventData[P_TARGET] = (void*)target.Get();
@@ -733,7 +771,7 @@ void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
                     }
                     }
                 }
                 }
             }
             }
-            
+
             dragElement_.Reset();
             dragElement_.Reset();
         }
         }
     }
     }
@@ -742,15 +780,15 @@ void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace MouseMove;
     using namespace MouseMove;
-    
+
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
-    
+
     if (cursor_)
     if (cursor_)
     {
     {
         Input* input = GetSubsystem<Input>();
         Input* input = GetSubsystem<Input>();
         const IntVector2& rootSize = rootElement_->GetSize();
         const IntVector2& rootSize = rootElement_->GetSize();
-        
+
         if (!input->IsMouseVisible())
         if (!input->IsMouseVisible())
         {
         {
             // Relative mouse motion: move cursor only when visible
             // Relative mouse motion: move cursor only when visible
@@ -769,7 +807,7 @@ void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
             // Absolute mouse motion: move always
             // Absolute mouse motion: move always
             cursor_->SetPosition(IntVector2(eventData[P_X].GetInt(), eventData[P_Y].GetInt()));
             cursor_->SetPosition(IntVector2(eventData[P_X].GetInt(), eventData[P_Y].GetInt()));
         }
         }
-        
+
         if (dragElement_ && mouseButtons_)
         if (dragElement_ && mouseButtons_)
         {
         {
             IntVector2 pos = cursor_->GetPosition();
             IntVector2 pos = cursor_->GetPosition();
@@ -787,13 +825,13 @@ void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace MouseWheel;
     using namespace MouseWheel;
-    
+
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
     int delta = eventData[P_WHEEL].GetInt();
     int delta = eventData[P_WHEEL].GetInt();
-    
+
     UIElement* element;
     UIElement* element;
-    if (!nonFocusedMouseWheel_&& (element = GetFocusElement()))
+    if (!nonFocusedMouseWheel_&& (element = focusElement_))
         element->OnWheel(delta, mouseButtons_, qualifiers_);
         element->OnWheel(delta, mouseButtons_, qualifiers_);
     else
     else
     {
     {
@@ -827,19 +865,19 @@ void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace TouchBegin;
     using namespace TouchBegin;
-    
+
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     WeakPtr<UIElement> element(GetElementAt(pos));
     WeakPtr<UIElement> element(GetElementAt(pos));
-    
+
     if (element)
     if (element)
     {
     {
         // Handle focusing & bringing to front
         // Handle focusing & bringing to front
         SetFocusElement(element);
         SetFocusElement(element);
         element->BringToFront();
         element->BringToFront();
-        
+
         // Handle click
         // Handle click
         element->OnClick(element->ScreenToElement(pos), pos, MOUSEB_LEFT, 0, 0);
         element->OnClick(element->ScreenToElement(pos), pos, MOUSEB_LEFT, 0, 0);
-        
+
         // Handle start of drag. OnClick() may have caused destruction of the element, so check the pointer again
         // Handle start of drag. OnClick() may have caused destruction of the element, so check the pointer again
         if (element && !dragElement_ )
         if (element && !dragElement_ )
         {
         {
@@ -852,9 +890,9 @@ void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
         // If clicked over no element, or a disabled element, lose focus
         // If clicked over no element, or a disabled element, lose focus
         SetFocusElement(0);
         SetFocusElement(0);
     }
     }
-    
+
     using namespace UIMouseClick;
     using namespace UIMouseClick;
-    
+
     VariantMap clickEventData;
     VariantMap clickEventData;
     clickEventData[UIMouseClick::P_ELEMENT] = (void*)element.Get();
     clickEventData[UIMouseClick::P_ELEMENT] = (void*)element.Get();
     clickEventData[UIMouseClick::P_X] = pos.x_;
     clickEventData[UIMouseClick::P_X] = pos.x_;
@@ -868,20 +906,20 @@ void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace TouchEnd;
     using namespace TouchEnd;
-    
+
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
-    
+
     // Transmit hover end to the position where the finger was lifted
     // Transmit hover end to the position where the finger was lifted
     UIElement* element = GetElementAt(pos);
     UIElement* element = GetElementAt(pos);
     if (element && element->IsEnabled())
     if (element && element->IsEnabled())
         element->OnHover(element->ScreenToElement(pos), pos, 0, 0, 0);
         element->OnHover(element->ScreenToElement(pos), pos, 0, 0, 0);
-    
+
     if (dragElement_)
     if (dragElement_)
     {
     {
         if (dragElement_->IsEnabled() && dragElement_->IsVisible())
         if (dragElement_->IsEnabled() && dragElement_->IsVisible())
         {
         {
             dragElement_->OnDragEnd(dragElement_->ScreenToElement(pos), pos, cursor_);
             dragElement_->OnDragEnd(dragElement_->ScreenToElement(pos), pos, cursor_);
-            
+
             // Drag and drop finish
             // Drag and drop finish
             bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
             bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
             if (dragSource)
             if (dragSource)
@@ -889,16 +927,16 @@ void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
                 WeakPtr<UIElement> target(GetElementAt(pos));
                 WeakPtr<UIElement> target(GetElementAt(pos));
                 bool dragTarget = target && (target->GetDragDropMode() & DD_TARGET) != 0;
                 bool dragTarget = target && (target->GetDragDropMode() & DD_TARGET) != 0;
                 bool dragDropFinish = dragSource && dragTarget && target != dragElement_;
                 bool dragDropFinish = dragSource && dragTarget && target != dragElement_;
-                
+
                 if (dragDropFinish)
                 if (dragDropFinish)
                 {
                 {
                     bool accept = target->OnDragDropFinish(dragElement_);
                     bool accept = target->OnDragDropFinish(dragElement_);
-                    
+
                     // OnDragDropFinish() may have caused destruction of the elements, so check the pointers again
                     // OnDragDropFinish() may have caused destruction of the elements, so check the pointers again
                     if (accept && dragElement_ && target)
                     if (accept && dragElement_ && target)
                     {
                     {
                         using namespace DragDropFinish;
                         using namespace DragDropFinish;
-                        
+
                         VariantMap eventData;
                         VariantMap eventData;
                         eventData[P_SOURCE] = (void*)dragElement_.Get();
                         eventData[P_SOURCE] = (void*)dragElement_.Get();
                         eventData[P_TARGET] = (void*)target.Get();
                         eventData[P_TARGET] = (void*)target.Get();
@@ -908,7 +946,7 @@ void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
                 }
                 }
             }
             }
         }
         }
-        
+
         dragElement_.Reset();
         dragElement_.Reset();
     }
     }
 }
 }
@@ -916,9 +954,9 @@ void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace TouchMove;
     using namespace TouchMove;
-    
+
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
-    
+
     if (dragElement_)
     if (dragElement_)
     {
     {
         if (dragElement_->IsEnabled() && dragElement_->IsVisible())
         if (dragElement_->IsEnabled() && dragElement_->IsVisible())
@@ -934,12 +972,12 @@ void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
 void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
 void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace KeyDown;
     using namespace KeyDown;
-    
+
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
     int key = eventData[P_KEY].GetInt();
     int key = eventData[P_KEY].GetInt();
-    
-    UIElement* element = GetFocusElement();
+
+    UIElement* element = focusElement_;
     if (element)
     if (element)
     {
     {
         // Switch focus between focusable elements in the same top level window
         // Switch focus between focusable elements in the same top level window
@@ -981,11 +1019,11 @@ void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
 void UI::HandleChar(StringHash eventType, VariantMap& eventData)
 void UI::HandleChar(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace Char;
     using namespace Char;
-    
+
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     mouseButtons_ = eventData[P_BUTTONS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
     qualifiers_ = eventData[P_QUALIFIERS].GetInt();
-    
-    UIElement* element = GetFocusElement();
+
+    UIElement* element = focusElement_;
     if (element)
     if (element)
         element->OnChar(eventData[P_CHAR].GetInt(), mouseButtons_, qualifiers_);
         element->OnChar(eventData[P_CHAR].GetInt(), mouseButtons_, qualifiers_);
 }
 }
@@ -993,7 +1031,7 @@ void UI::HandleChar(StringHash eventType, VariantMap& eventData)
 void UI::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
 void UI::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace PostUpdate;
     using namespace PostUpdate;
-    
+
     Update(eventData[P_TIMESTEP].GetFloat());
     Update(eventData[P_TIMESTEP].GetFloat());
 }
 }
 
 
@@ -1005,7 +1043,7 @@ void UI::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
 void RegisterUILibrary(Context* context)
 void RegisterUILibrary(Context* context)
 {
 {
     Font::RegisterObject(context);
     Font::RegisterObject(context);
-    
+
     UIElement::RegisterObject(context);
     UIElement::RegisterObject(context);
     BorderImage::RegisterObject(context);
     BorderImage::RegisterObject(context);
     Sprite::RegisterObject(context);
     Sprite::RegisterObject(context);

+ 12 - 6
Engine/UI/UI.h

@@ -41,17 +41,19 @@ class XMLFile;
 class UI : public Object
 class UI : public Object
 {
 {
     OBJECT(UI);
     OBJECT(UI);
-    
+
 public:
 public:
     /// Construct.
     /// Construct.
     UI(Context* context);
     UI(Context* context);
     /// Destruct.
     /// Destruct.
     virtual ~UI();
     virtual ~UI();
-    
+
     /// Set cursor UI element.
     /// Set cursor UI element.
     void SetCursor(Cursor* cursor);
     void SetCursor(Cursor* cursor);
     /// Set focused UI element.
     /// Set focused UI element.
     void SetFocusElement(UIElement* element);
     void SetFocusElement(UIElement* element);
+    /// Set modal element. Until it is dismissed, all the inputs and events are only sent to this modal element. Return true when successful.
+    bool SetModalElement(UIElement* modalElement);
     /// Clear the UI (excluding the cursor.)
     /// Clear the UI (excluding the cursor.)
     void Clear();
     void Clear();
     /// Update the UI logic. Called by HandlePostUpdate().
     /// Update the UI logic. Called by HandlePostUpdate().
@@ -70,7 +72,7 @@ public:
     void SetClipBoardText(const String& text);
     void SetClipBoardText(const String& text);
     /// Set mouse wheel handling flag.
     /// Set mouse wheel handling flag.
     void SetNonFocusedMouseWheel(bool nonFocusedMouseWheel);
     void SetNonFocusedMouseWheel(bool nonFocusedMouseWheel);
-    
+
     /// Return root UI element.
     /// Return root UI element.
     UIElement* GetRoot() const { return rootElement_; }
     UIElement* GetRoot() const { return rootElement_; }
     /// Return cursor.
     /// Return cursor.
@@ -80,7 +82,9 @@ public:
     /// Return UI element at screen coordinates.
     /// Return UI element at screen coordinates.
     UIElement* GetElementAt(int x, int y, bool enabledOnly = true);
     UIElement* GetElementAt(int x, int y, bool enabledOnly = true);
     /// Return focused element.
     /// Return focused element.
-    UIElement* GetFocusElement() const;
+    UIElement* GetFocusElement() const { return focusElement_; }
+    /// Return modal element.
+    UIElement* GetModalElement() const { return modalElement_; }
     /// Return topmost enabled root-level element.
     /// Return topmost enabled root-level element.
     UIElement* GetFrontElement() const;
     UIElement* GetFrontElement() const;
     /// Return cursor position.
     /// Return cursor position.
@@ -89,7 +93,7 @@ public:
     const String& GetClipBoardText() const { return clipBoard_; }
     const String& GetClipBoardText() const { return clipBoard_; }
     /// Return mouse wheel handling flag.
     /// Return mouse wheel handling flag.
     bool IsNonFocusedMouseWheel() const { return nonFocusedMouseWheel_; }
     bool IsNonFocusedMouseWheel() const { return nonFocusedMouseWheel_; }
-    
+
 private:
 private:
     /// Initialize when screen mode initially se.
     /// Initialize when screen mode initially se.
     void Initialize();
     void Initialize();
@@ -125,7 +129,7 @@ private:
     void HandlePostUpdate(StringHash eventType, VariantMap& eventData);
     void HandlePostUpdate(StringHash eventType, VariantMap& eventData);
     /// Handle render update event.
     /// Handle render update event.
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
-    
+
     /// Graphics subsystem.
     /// Graphics subsystem.
     WeakPtr<Graphics> graphics_;
     WeakPtr<Graphics> graphics_;
     /// Vertex shader for no texture.
     /// Vertex shader for no texture.
@@ -148,6 +152,8 @@ private:
     WeakPtr<UIElement> dragElement_;
     WeakPtr<UIElement> dragElement_;
     /// Currently focused element
     /// Currently focused element
     WeakPtr<UIElement> focusElement_;
     WeakPtr<UIElement> focusElement_;
+    /// Modal element.
+    WeakPtr<UIElement> modalElement_;
     /// UI rendering batches.
     /// UI rendering batches.
     PODVector<UIBatch> batches_;
     PODVector<UIBatch> batches_;
     /// UI rendering vertex data.
     /// UI rendering vertex data.

+ 19 - 1
Engine/UI/UIElement.cpp

@@ -1228,6 +1228,24 @@ UIElement* UIElement::GetChild(const String& name, bool recursive) const
     return 0;
     return 0;
 }
 }
 
 
+UIElement* UIElement::GetChild(const ShortStringHash& key, const Variant& value, bool recursive) const
+{
+    for (Vector<SharedPtr<UIElement> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+    {
+        if ((*i)->GetVar(key) == value)
+            return *i;
+
+        if (recursive)
+        {
+            UIElement* element = (*i)->GetChild(key, value, true);
+            if (element)
+                return element;
+        }
+    }
+
+    return 0;
+}
+
 UIElement* UIElement::GetRoot() const
 UIElement* UIElement::GetRoot() const
 {
 {
     UIElement* root = parent_;
     UIElement* root = parent_;
@@ -1250,7 +1268,7 @@ const Color& UIElement::GetDerivedColor() const
     return derivedColor_;
     return derivedColor_;
 }
 }
 
 
-const Variant& UIElement::GetVar(ShortStringHash key) const
+const Variant& UIElement::GetVar(const ShortStringHash& key) const
 {
 {
     VariantMap::ConstIterator i = vars_.Find(key);
     VariantMap::ConstIterator i = vars_.Find(key);
     if (i != vars_.End())
     if (i != vars_.End())

+ 3 - 1
Engine/UI/UIElement.h

@@ -378,6 +378,8 @@ public:
     UIElement* GetChild(unsigned index) const;
     UIElement* GetChild(unsigned index) const;
     /// Return child element by name.
     /// Return child element by name.
     UIElement* GetChild(const String& name, bool recursive = false) const;
     UIElement* GetChild(const String& name, bool recursive = false) const;
+    /// Return child element by variable.
+    UIElement* GetChild(const ShortStringHash& key, const Variant& value, bool recursive = false) const;
     /// Return immediate child elements.
     /// Return immediate child elements.
     const Vector<SharedPtr<UIElement> >& GetChildren() const { return children_; }
     const Vector<SharedPtr<UIElement> >& GetChildren() const { return children_; }
     /// Return child elements either recursively or non-recursively.
     /// Return child elements either recursively or non-recursively.
@@ -389,7 +391,7 @@ public:
     /// Return derived color. Only valid when no gradient.
     /// Return derived color. Only valid when no gradient.
     const Color& GetDerivedColor() const;
     const Color& GetDerivedColor() const;
     /// Return a user variable.
     /// Return a user variable.
-    const Variant& GetVar(ShortStringHash key) const;
+    const Variant& GetVar(const ShortStringHash& key) const;
     /// Return all user variables.
     /// Return all user variables.
     const VariantMap& GetVars() const { return vars_; }
     const VariantMap& GetVars() const { return vars_; }
     
     

+ 74 - 24
Engine/UI/Window.cpp

@@ -24,6 +24,7 @@
 #include "Context.h"
 #include "Context.h"
 #include "Cursor.h"
 #include "Cursor.h"
 #include "InputEvents.h"
 #include "InputEvents.h"
+#include "UI.h"
 #include "Window.h"
 #include "Window.h"
 
 
 #include "DebugNew.h"
 #include "DebugNew.h"
@@ -40,7 +41,11 @@ Window::Window(Context* context) :
     movable_(false),
     movable_(false),
     resizable_(false),
     resizable_(false),
     resizeBorder_(DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER),
     resizeBorder_(DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER),
-    dragMode_(DRAG_NONE)
+    dragMode_(DRAG_NONE),
+    modal_(false),
+    modalFrameColor_(Color::TRANSPARENT),
+    modalFrameSize_(IntVector2::ZERO)
+
 {
 {
     bringToFront_ = true;
     bringToFront_ = true;
     clipChildren_ = true;
     clipChildren_ = true;
@@ -54,14 +59,42 @@ Window::~Window()
 void Window::RegisterObject(Context* context)
 void Window::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<Window>();
     context->RegisterFactory<Window>();
-    
+
     REF_ACCESSOR_ATTRIBUTE(Window, VAR_INTRECT, "Resize Border", GetResizeBorder, SetResizeBorder, IntRect, IntRect(DEFAULT_RESIZE_BORDER, \
     REF_ACCESSOR_ATTRIBUTE(Window, VAR_INTRECT, "Resize Border", GetResizeBorder, SetResizeBorder, IntRect, IntRect(DEFAULT_RESIZE_BORDER, \
         DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER), AM_FILE);
         DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER, DEFAULT_RESIZE_BORDER), AM_FILE);
     ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Movable", IsMovable, SetMovable, bool, false, AM_FILE);
     ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Movable", IsMovable, SetMovable, bool, false, AM_FILE);
     ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Resizable", IsResizable, SetResizable, bool, false, AM_FILE);
     ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Resizable", IsResizable, SetResizable, bool, false, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Window, VAR_BOOL, "Is Modal", IsModal, SetModal, bool, false, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Window, VAR_COLOR, "Modal Frame Color", GetModalFrameColor, SetModalFrameColor, Color, Color::TRANSPARENT, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Window, VAR_INTVECTOR2, "Modal Frame Size", GetModalFrameSize, SetModalFrameSize, IntVector2, IntVector2::ZERO, AM_FILE);
     COPY_BASE_ATTRIBUTES(Window, BorderImage);
     COPY_BASE_ATTRIBUTES(Window, BorderImage);
 }
 }
 
 
+void Window::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
+{
+    BorderImage::GetBatches(batches, vertexData, currentScissor);
+
+    if (modal_)
+    {
+        UIBatch batch(this, BLEND_ALPHA, currentScissor, 0, &vertexData);
+
+        int x = GetIndentWidth();
+        IntVector2 size = GetSize();
+        size.x_ -= x;
+
+        // Left
+        batch.AddQuad(x - modalFrameSize_.x_, 0, modalFrameSize_.x_, size.y_, 0, 0, 0, 0, modalFrameColor_);
+        // Top
+        batch.AddQuad(x - modalFrameSize_.x_, -modalFrameSize_.y_, size.x_ + 2 * modalFrameSize_.x_, modalFrameSize_.y_, 0, 0, 0, 0, modalFrameColor_);
+        // Right
+        batch.AddQuad(size.x_, 0, modalFrameSize_.x_, size.y_, 0, 0, 0, 0, modalFrameColor_);
+        // Bottom
+        batch.AddQuad(x - modalFrameSize_.x_, size.y_, size.x_ + 2 * modalFrameSize_.x_, modalFrameSize_.y_, 0, 0, 0, 0, modalFrameColor_);
+
+        UIBatch::AddOrMerge(batch, batches);
+    }
+}
+
 void Window::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 void Window::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
 {
     if (dragMode_ == DRAG_NONE)
     if (dragMode_ == DRAG_NONE)
@@ -80,7 +113,7 @@ void Window::OnDragBegin(const IntVector2& position, const IntVector2& screenPos
         dragMode_ = DRAG_NONE;
         dragMode_ = DRAG_NONE;
         return;
         return;
     }
     }
-    
+
     dragBeginCursor_ = screenPosition;
     dragBeginCursor_ = screenPosition;
     dragBeginPosition_ = GetPosition();
     dragBeginPosition_ = GetPosition();
     dragBeginSize_ = GetSize();
     dragBeginSize_ = GetSize();
@@ -92,53 +125,53 @@ void Window::OnDragMove(const IntVector2& position, const IntVector2& screenPosi
 {
 {
     if (dragMode_ == DRAG_NONE)
     if (dragMode_ == DRAG_NONE)
         return;
         return;
-    
+
     IntVector2 delta = screenPosition - dragBeginCursor_;
     IntVector2 delta = screenPosition - dragBeginCursor_;
 
 
     const IntVector2& position_ = GetPosition();
     const IntVector2& position_ = GetPosition();
     const IntVector2& size_ = GetSize();
     const IntVector2& size_ = GetSize();
     const IntVector2& minSize_ = GetMinSize();
     const IntVector2& minSize_ = GetMinSize();
     const IntVector2& maxSize_ = GetMaxSize();
     const IntVector2& maxSize_ = GetMaxSize();
-    
+
     switch (dragMode_)
     switch (dragMode_)
     {
     {
     case DRAG_MOVE:
     case DRAG_MOVE:
         SetPosition(dragBeginPosition_ + delta);
         SetPosition(dragBeginPosition_ + delta);
         break;
         break;
-        
+
     case DRAG_RESIZE_TOPLEFT:
     case DRAG_RESIZE_TOPLEFT:
-        SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)), 
+        SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)),
             Clamp(dragBeginPosition_.y_ + delta.y_, position_.y_ - (maxSize_.y_ - size_.y_), position_.y_ + (size_.y_ - minSize_.y_)));
             Clamp(dragBeginPosition_.y_ + delta.y_, position_.y_ - (maxSize_.y_ - size_.y_), position_.y_ + (size_.y_ - minSize_.y_)));
         SetSize(dragBeginSize_ - delta);
         SetSize(dragBeginSize_ - delta);
         break;
         break;
-        
+
     case DRAG_RESIZE_TOP:
     case DRAG_RESIZE_TOP:
         SetPosition(dragBeginPosition_.x_, Clamp(dragBeginPosition_.y_ + delta.y_, position_.y_ - (maxSize_.y_ - size_.y_), position_.y_ + (size_.y_ - minSize_.y_)));
         SetPosition(dragBeginPosition_.x_, Clamp(dragBeginPosition_.y_ + delta.y_, position_.y_ - (maxSize_.y_ - size_.y_), position_.y_ + (size_.y_ - minSize_.y_)));
         SetSize(dragBeginSize_.x_, dragBeginSize_.y_ - delta.y_);
         SetSize(dragBeginSize_.x_, dragBeginSize_.y_ - delta.y_);
         break;
         break;
-        
+
     case DRAG_RESIZE_TOPRIGHT:
     case DRAG_RESIZE_TOPRIGHT:
         SetPosition(dragBeginPosition_.x_, dragBeginPosition_.y_ + delta.y_);
         SetPosition(dragBeginPosition_.x_, dragBeginPosition_.y_ + delta.y_);
         SetSize(dragBeginSize_.x_ + delta.x_, dragBeginSize_.y_ - delta.y_);
         SetSize(dragBeginSize_.x_ + delta.x_, dragBeginSize_.y_ - delta.y_);
         break;
         break;
-        
+
     case DRAG_RESIZE_RIGHT:
     case DRAG_RESIZE_RIGHT:
         SetSize(dragBeginSize_.x_ + delta.x_, dragBeginSize_.y_);
         SetSize(dragBeginSize_.x_ + delta.x_, dragBeginSize_.y_);
         break;
         break;
-        
+
     case DRAG_RESIZE_BOTTOMRIGHT:
     case DRAG_RESIZE_BOTTOMRIGHT:
         SetSize(dragBeginSize_ + delta);
         SetSize(dragBeginSize_ + delta);
         break;
         break;
-        
+
     case DRAG_RESIZE_BOTTOM:
     case DRAG_RESIZE_BOTTOM:
         SetSize(dragBeginSize_.x_, dragBeginSize_.y_ + delta.y_);
         SetSize(dragBeginSize_.x_, dragBeginSize_.y_ + delta.y_);
         break;
         break;
-        
+
     case DRAG_RESIZE_BOTTOMLEFT:
     case DRAG_RESIZE_BOTTOMLEFT:
         SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)), dragBeginPosition_.y_);
         SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)), dragBeginPosition_.y_);
         SetSize(dragBeginSize_.x_ - delta.x_, dragBeginSize_.y_ + delta.y_);
         SetSize(dragBeginSize_.x_ - delta.x_, dragBeginSize_.y_ + delta.y_);
         break;
         break;
-        
+
     case DRAG_RESIZE_LEFT:
     case DRAG_RESIZE_LEFT:
         SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)), dragBeginPosition_.y_);
         SetPosition(Clamp(dragBeginPosition_.x_ + delta.x_, position_.x_ - (maxSize_.x_ - size_.x_), position_.x_ + (size_.x_ - minSize_.x_)), dragBeginPosition_.y_);
         SetSize(dragBeginSize_.x_ - delta.x_, dragBeginSize_.y_);
         SetSize(dragBeginSize_.x_ - delta.x_, dragBeginSize_.y_);
@@ -147,7 +180,7 @@ void Window::OnDragMove(const IntVector2& position, const IntVector2& screenPosi
     default:
     default:
         break;
         break;
     }
     }
-    
+
     ValidatePosition();
     ValidatePosition();
     SetCursorShape(dragMode_, cursor);
     SetCursorShape(dragMode_, cursor);
 }
 }
@@ -175,10 +208,27 @@ void Window::SetResizeBorder(const IntRect& rect)
     resizeBorder_.bottom_ = Max(rect.bottom_, 0);
     resizeBorder_.bottom_ = Max(rect.bottom_, 0);
 }
 }
 
 
+void Window::SetModal(bool modal)
+{
+    UI* ui = GetSubsystem<UI>();
+    if (ui->SetModalElement(modal ? this : 0))
+        modal_ = modal;
+}
+
+void Window::SetModalFrameColor(const Color& color)
+{
+    modalFrameColor_ = color;
+}
+
+void Window::SetModalFrameSize(const IntVector2& size)
+{
+    modalFrameSize_ = size;
+}
+
 WindowDragMode Window::GetDragMode(const IntVector2& position) const
 WindowDragMode Window::GetDragMode(const IntVector2& position) const
 {
 {
     WindowDragMode mode = DRAG_NONE;
     WindowDragMode mode = DRAG_NONE;
-    
+
     // Top row
     // Top row
     if (position.y_ < resizeBorder_.top_)
     if (position.y_ < resizeBorder_.top_)
     {
     {
@@ -220,7 +270,7 @@ WindowDragMode Window::GetDragMode(const IntVector2& position) const
                 mode = DRAG_RESIZE_RIGHT;
                 mode = DRAG_RESIZE_RIGHT;
         }
         }
     }
     }
-    
+
     return mode;
     return mode;
 }
 }
 
 
@@ -228,7 +278,7 @@ void Window::SetCursorShape(WindowDragMode mode, Cursor* cursor) const
 {
 {
     if (!cursor)
     if (!cursor)
         return;
         return;
-    
+
     switch (mode)
     switch (mode)
     {
     {
     case DRAG_NONE:
     case DRAG_NONE:
@@ -240,7 +290,7 @@ void Window::SetCursorShape(WindowDragMode mode, Cursor* cursor) const
     case DRAG_RESIZE_BOTTOM:
     case DRAG_RESIZE_BOTTOM:
         cursor->SetShape(CS_RESIZEVERTICAL);
         cursor->SetShape(CS_RESIZEVERTICAL);
         break;
         break;
-        
+
     case DRAG_RESIZE_LEFT:
     case DRAG_RESIZE_LEFT:
     case DRAG_RESIZE_RIGHT:
     case DRAG_RESIZE_RIGHT:
         cursor->SetShape(CS_RESIZEHORIZONTAL);
         cursor->SetShape(CS_RESIZEHORIZONTAL);
@@ -250,12 +300,12 @@ void Window::SetCursorShape(WindowDragMode mode, Cursor* cursor) const
     case DRAG_RESIZE_BOTTOMLEFT:
     case DRAG_RESIZE_BOTTOMLEFT:
         cursor->SetShape(CS_RESIZEDIAGONAL_TOPRIGHT);
         cursor->SetShape(CS_RESIZEDIAGONAL_TOPRIGHT);
         break;
         break;
-        
+
     case DRAG_RESIZE_TOPLEFT:
     case DRAG_RESIZE_TOPLEFT:
     case DRAG_RESIZE_BOTTOMRIGHT:
     case DRAG_RESIZE_BOTTOMRIGHT:
         cursor->SetShape(CS_RESIZEDIAGONAL_TOPLEFT);
         cursor->SetShape(CS_RESIZEDIAGONAL_TOPLEFT);
         break;
         break;
-    
+
     default:
     default:
         break;
         break;
     }
     }
@@ -266,14 +316,14 @@ void Window::ValidatePosition()
     // Check that window does not go more than halfway outside its parent in either dimension
     // Check that window does not go more than halfway outside its parent in either dimension
     if (!parent_)
     if (!parent_)
         return;
         return;
-    
+
     const IntVector2& parentSize = parent_->GetSize();
     const IntVector2& parentSize = parent_->GetSize();
     IntVector2 position = GetPosition();
     IntVector2 position = GetPosition();
     IntVector2 halfSize = GetSize() / 2;
     IntVector2 halfSize = GetSize() / 2;
-    
+
     position.x_ = Clamp(position.x_, -halfSize.x_, parentSize.x_ - halfSize.x_);
     position.x_ = Clamp(position.x_, -halfSize.x_, parentSize.x_ - halfSize.x_);
     position.y_ = Clamp(position.y_, -halfSize.y_, parentSize.y_ - halfSize.y_);
     position.y_ = Clamp(position.y_, -halfSize.y_, parentSize.y_ - halfSize.y_);
-    
+
     SetPosition(position);
     SetPosition(position);
 }
 }
 
 

+ 31 - 6
Engine/UI/Window.h

@@ -46,7 +46,7 @@ enum WindowDragMode
 class Window : public BorderImage
 class Window : public BorderImage
 {
 {
     OBJECT(Window);
     OBJECT(Window);
-    
+
 public:
 public:
     /// Construct.
     /// Construct.
     Window(Context* context);
     Window(Context* context);
@@ -54,7 +54,10 @@ public:
     virtual ~Window();
     virtual ~Window();
     /// Register object factory.
     /// Register object factory.
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
-    
+
+    /// Return UI rendering batches.
+    virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor);
+
     /// React to mouse hover.
     /// React to mouse hover.
     virtual void OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     virtual void OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag begin.
     /// React to mouse drag begin.
@@ -63,21 +66,33 @@ public:
     virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag end.
     /// React to mouse drag end.
     virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor);
     virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor);
-    
+
     /// Set whether can be moved.
     /// Set whether can be moved.
     void SetMovable(bool enable);
     void SetMovable(bool enable);
     /// Set whether can be resized.
     /// Set whether can be resized.
     void SetResizable(bool enable);
     void SetResizable(bool enable);
     /// Set resize area width at edges.
     /// Set resize area width at edges.
     void SetResizeBorder(const IntRect& rect);
     void SetResizeBorder(const IntRect& rect);
-    
+    /// Set modal flag. When the modal flag is set, the focused window needs to be dismissed first to allow other UI elements to gain focus.
+    void SetModal(bool modal);
+    /// Set modal frame color.
+    void SetModalFrameColor(const Color& color);
+    /// Set modal frame size.
+    void SetModalFrameSize(const IntVector2& size);
+
     /// Return whether is movable.
     /// Return whether is movable.
     bool IsMovable() const { return movable_; }
     bool IsMovable() const { return movable_; }
     /// Return whether is resizable.
     /// Return whether is resizable.
     bool IsResizable() const { return resizable_; }
     bool IsResizable() const { return resizable_; }
     /// Return resize area width at edges.
     /// Return resize area width at edges.
     const IntRect& GetResizeBorder() const { return resizeBorder_; }
     const IntRect& GetResizeBorder() const { return resizeBorder_; }
-    
+    /// Return modal flag.
+    bool IsModal() const { return modal_; }
+    /// Get modal frame color.
+    const Color& GetModalFrameColor() const { return modalFrameColor_; }
+    /// Get modal frame size.
+    const IntVector2& GetModalFrameSize() const { return modalFrameSize_; }
+
 protected:
 protected:
     /// Identify drag mode (move/resize.)
     /// Identify drag mode (move/resize.)
     WindowDragMode GetDragMode(const IntVector2& position) const;
     WindowDragMode GetDragMode(const IntVector2& position) const;
@@ -87,7 +102,7 @@ protected:
     void ValidatePosition();
     void ValidatePosition();
     /// Check whether alignment supports moving and resizing.
     /// Check whether alignment supports moving and resizing.
     bool CheckAlignment() const;
     bool CheckAlignment() const;
-    
+
     /// Movable flag.
     /// Movable flag.
     bool movable_;
     bool movable_;
     /// Resizable flag.
     /// Resizable flag.
@@ -102,6 +117,16 @@ protected:
     IntVector2 dragBeginPosition_;
     IntVector2 dragBeginPosition_;
     /// Original size at drag begin.
     /// Original size at drag begin.
     IntVector2 dragBeginSize_;
     IntVector2 dragBeginSize_;
+    /// Modal flag.
+    bool modal_;
+    /// Modal frame color, used when modal flag is set.
+    Color modalFrameColor_;
+    /// Modal frame size, used when modal flag is set.
+    IntVector2 modalFrameSize_;
+
+private:
+    /// Handle modal window being focused.
+    void HandleModalFocused(StringHash eventType, VariantMap& eventData);
 };
 };
 
 
 }
 }