Browse Source

Enhanced UIElement to be able to set a default style file for later reference by children elements, added horizontal indentation feature, and added new E_LAYOUTUPDATED event. Enhanced ListView to better support hierarchy mode and added feature to show 'expansion triangle' in hierarchy mode. Fixed a bug in ScrollView to update view size correctly when resizing content element. Fixed a bug in Log's WriteRaw() method so it sends error message to stderr even in quite mode. Refactored Editor to take advantage of the enhancement.

Wei Tjong Yao 12 years ago
parent
commit
d7f6b4d0b6

+ 2 - 2
Bin/Data/Scripts/Editor/EditorNodeWindow.as

@@ -32,7 +32,7 @@ void CreateNodeWindow()
     InitResourcePicker();
     InitResourcePicker();
     InitVectorStructs();
     InitVectorStructs();
 
 
-    nodeWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorNodeWindow.xml"), uiStyle);
+    nodeWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorNodeWindow.xml"));
     componentXMLResource = cache.GetResource("XMLFile", "UI/EditorComponent.xml");
     componentXMLResource = cache.GetResource("XMLFile", "UI/EditorComponent.xml");
     componentParentContainer = nodeWindow.GetChild("ComponentParentContainer", true);
     componentParentContainer = nodeWindow.GetChild("ComponentParentContainer", true);
     AddComponentContainer();
     AddComponentContainer();
@@ -133,7 +133,7 @@ void UpdateAttributes(bool fullUpdate)
                     AddComponentContainer();
                     AddComponentContainer();
                 
                 
                 Text@ componentTitle = GetComponentContainer(j).GetChild("ComponentTitle");
                 Text@ componentTitle = GetComponentContainer(j).GetChild("ComponentTitle");
-                componentTitle.text = GetComponentTitle(editComponents[j * numEditableComponents], 0) + multiplierText;
+                componentTitle.text = GetComponentTitle(editComponents[j * numEditableComponents]) + multiplierText;
                 
                 
                 Array<Serializable@> components;
                 Array<Serializable@> components;
                 for (uint i = 0; i < numEditableComponents; ++i)
                 for (uint i = 0; i < numEditableComponents; ++i)

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

@@ -18,7 +18,7 @@ void CreateEditorPreferencesDialog()
     if (preferencesDialog !is null)
     if (preferencesDialog !is null)
         return;
         return;
     
     
-    preferencesDialog = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorPreferencesDialog.xml"), uiStyle);
+    preferencesDialog = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorPreferencesDialog.xml"));
     ui.root.AddChild(preferencesDialog);
     ui.root.AddChild(preferencesDialog);
     preferencesDialog.opacity = uiMaxOpacity;
     preferencesDialog.opacity = uiMaxOpacity;
     CenterDialog(preferencesDialog);
     CenterDialog(preferencesDialog);

+ 6 - 12
Bin/Data/Scripts/Editor/EditorScene.as

@@ -277,10 +277,9 @@ bool SceneDelete()
         return false;
         return false;
 
 
     BeginSelectionModify();
     BeginSelectionModify();
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
     
     
     // Clear the selection now to prevent repopulation of selectedNodes and selectedComponents combo
     // Clear the selection now to prevent repopulation of selectedNodes and selectedComponents combo
-    list.ClearSelection();
+    hierarchyList.ClearSelection();
 
 
     // Remove nodes
     // Remove nodes
     for (uint i = 0; i < selectedNodes.length; ++i)
     for (uint i = 0; i < selectedNodes.length; ++i)
@@ -298,7 +297,7 @@ bool SceneDelete()
 
 
         // If deleting only one node, select the next item in the same index
         // If deleting only one node, select the next item in the same index
         if (selectedNodes.length == 1 && selectedComponents.empty)
         if (selectedNodes.length == 1 && selectedComponents.empty)
-            list.selection = nodeIndex;
+            hierarchyList.selection = nodeIndex;
     }
     }
 
 
     // Then remove components, if they still remain
     // Then remove components, if they still remain
@@ -326,7 +325,7 @@ bool SceneDelete()
 
 
         // If deleting only one component, select the next item in the same index
         // If deleting only one component, select the next item in the same index
         if (selectedComponents.length == 1 && selectedNodes.empty)
         if (selectedComponents.length == 1 && selectedNodes.empty)
-            list.selection = index;
+            hierarchyList.selection = index;
     }
     }
 
 
     EndSelectionModify();
     EndSelectionModify();
@@ -350,7 +349,6 @@ bool SceneCopy()
     if (!selectedNodes.empty && !selectedComponents.empty)
     if (!selectedNodes.empty && !selectedComponents.empty)
         return false;
         return false;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
     copyBuffer.Clear();
     copyBuffer.Clear();
 
 
     // Copy components
     // Copy components
@@ -393,7 +391,6 @@ bool ScenePaste()
     if (editNode is null || !CheckSceneWindowFocus() || copyBuffer.empty)
     if (editNode is null || !CheckSceneWindowFocus() || copyBuffer.empty)
         return false;
         return false;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
     bool pasteComponents = false;
     bool pasteComponents = false;
 
 
     for (uint i = 0; i < copyBuffer.length; ++i)
     for (uint i = 0; i < copyBuffer.length; ++i)
@@ -444,8 +441,6 @@ void SceneUnparent()
     if (!CheckSceneWindowFocus() || !selectedComponents.empty || selectedNodes.empty)
     if (!CheckSceneWindowFocus() || !selectedComponents.empty || selectedNodes.empty)
         return;
         return;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-
     // Parent selected nodes to root
     // Parent selected nodes to root
     for (uint i = 0; i < selectedNodes.length; ++i)
     for (uint i = 0; i < selectedNodes.length; ++i)
     {
     {
@@ -493,11 +488,10 @@ void SceneResetScale()
 
 
 void SceneSelectAll()
 void SceneSelectAll()
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    if (!list.selections.empty)
+    if (!hierarchyList.selections.empty)
     {
     {
         BeginSelectionModify();
         BeginSelectionModify();
-        list.ClearSelection();
+        hierarchyList.ClearSelection();
         EndSelectionModify();
         EndSelectionModify();
     }
     }
     else
     else
@@ -507,7 +501,7 @@ void SceneSelectAll()
         Array<uint> indices;
         Array<uint> indices;
         for (uint i = 0; i < rootLevelNodes.length; ++i)
         for (uint i = 0; i < rootLevelNodes.length; ++i)
             indices.Push(GetNodeListIndex(rootLevelNodes[i]));
             indices.Push(GetNodeListIndex(rootLevelNodes[i]));
-        list.SetSelections(indices);
+        hierarchyList.SetSelections(indices);
         EndSelectionModify();
         EndSelectionModify();
     }
     }
 }
 }

+ 85 - 153
Bin/Data/Scripts/Editor/EditorSceneWindow.as

@@ -3,9 +3,10 @@
 const int ITEM_NONE = 0;
 const int ITEM_NONE = 0;
 const int ITEM_NODE = 1;
 const int ITEM_NODE = 1;
 const int ITEM_COMPONENT = 2;
 const int ITEM_COMPONENT = 2;
-const uint NO_ITEM = 0xffffffff;
+const uint NO_ITEM = M_MAX_UNSIGNED;
 
 
 Window@ sceneWindow;
 Window@ sceneWindow;
+ListView@ hierarchyList;
 
 
 bool suppressSceneChanges = false;
 bool suppressSceneChanges = false;
 
 
@@ -14,7 +15,8 @@ void CreateSceneWindow()
     if (sceneWindow !is null)
     if (sceneWindow !is null)
         return;
         return;
 
 
-    @sceneWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSceneWindow.xml"), uiStyle);
+    sceneWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSceneWindow.xml"));
+    hierarchyList = sceneWindow.GetChild("NodeList");
     ui.root.AddChild(sceneWindow);
     ui.root.AddChild(sceneWindow);
     int height = Min(ui.root.height - 60, 500);
     int height = Min(ui.root.height - 60, 500);
     sceneWindow.SetSize(300, height);
     sceneWindow.SetSize(300, height);
@@ -43,18 +45,16 @@ void CreateSceneWindow()
         newComponentList.AddItem(choice);
         newComponentList.AddItem(choice);
     }
     }
 
 
-    // Set drag & drop target mode on the node list background, which is used to parent
-    // nodes back to the root node
-    ListView@ list = sceneWindow.GetChild("NodeList");
-    list.contentElement.dragDropMode = DD_TARGET;
-    list.scrollPanel.dragDropMode = DD_TARGET;
+    // Set drag & drop target mode on the node list background, which is used to parent nodes back to the root node
+    hierarchyList.contentElement.dragDropMode = DD_TARGET;
+    hierarchyList.scrollPanel.dragDropMode = DD_TARGET;
 
 
     SubscribeToEvent(sceneWindow.GetChild("CloseButton", true), "Released", "HideSceneWindow");
     SubscribeToEvent(sceneWindow.GetChild("CloseButton", true), "Released", "HideSceneWindow");
-    SubscribeToEvent(sceneWindow.GetChild("ExpandAllButton", true), "Released", "ExpandCollapseHierarchy");
-    SubscribeToEvent(sceneWindow.GetChild("CollapseAllButton", true), "Released", "ExpandCollapseHierarchy");
-    SubscribeToEvent(sceneWindow.GetChild("NodeList", true), "SelectionChanged", "HandleSceneWindowSelectionChange");
-    SubscribeToEvent(sceneWindow.GetChild("NodeList", true), "ItemDoubleClicked", "HandleSceneWindowItemDoubleClick");
-    SubscribeToEvent(sceneWindow.GetChild("NodeList", true), "UnhandledKey", "HandleSceneWindowKey");
+    SubscribeToEvent(sceneWindow.GetChild("ExpandButton", true), "Released", "ExpandCollapseHierarchy");
+    SubscribeToEvent(sceneWindow.GetChild("CollapseButton", true), "Released", "ExpandCollapseHierarchy");
+    SubscribeToEvent(hierarchyList, "SelectionChanged", "HandleSceneWindowSelectionChange");
+    SubscribeToEvent(hierarchyList, "ItemDoubleClicked", "HandleSceneWindowItemDoubleClick");
+    SubscribeToEvent(hierarchyList, "UnhandledKey", "HandleSceneWindowKey");
     SubscribeToEvent(newNodeList, "ItemSelected", "HandleCreateNode");
     SubscribeToEvent(newNodeList, "ItemSelected", "HandleCreateNode");
     SubscribeToEvent(newComponentList, "ItemSelected", "HandleCreateComponent");
     SubscribeToEvent(newComponentList, "ItemSelected", "HandleCreateComponent");
     SubscribeToEvent("DragDropTest", "HandleDragDropTest");
     SubscribeToEvent("DragDropTest", "HandleDragDropTest");
@@ -80,17 +80,17 @@ void HideSceneWindow()
 void ExpandCollapseHierarchy(StringHash eventType, VariantMap& eventData)
 void ExpandCollapseHierarchy(StringHash eventType, VariantMap& eventData)
 {
 {
     Button@ button = eventData["Element"].GetUIElement();
     Button@ button = eventData["Element"].GetUIElement();
-    bool enable = button.name == "ExpandAllButton";
+    bool enable = button.name == "ExpandButton";
+    bool all = cast<CheckBox>(sceneWindow.GetChild("AllCheckBox", true)).checked;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    Array<uint> selections = list.selections;
+    Array<uint> selections = hierarchyList.selections;
     for (uint i = 0; i < selections.length; ++i)
     for (uint i = 0; i < selections.length; ++i)
-        list.Expand(selections[i], enable, true);
+        hierarchyList.Expand(selections[i], enable, all);
 }
 }
 
 
 void EnableExpandCollapseButtons(bool enable)
 void EnableExpandCollapseButtons(bool enable)
 {
 {
-    String[] buttons = { "ExpandAllButton", "CollapseAllButton" };
+    String[] buttons = { "ExpandButton", "CollapseButton", "AllCheckBox" };
     for (uint i = 0; i < buttons.length; ++i)
     for (uint i = 0; i < buttons.length; ++i)
     {
     {
         UIElement@ element = sceneWindow.GetChild(buttons[i], true);
         UIElement@ element = sceneWindow.GetChild(buttons[i], true);
@@ -104,60 +104,56 @@ void ClearSceneWindow()
     if (sceneWindow is null)
     if (sceneWindow is null)
         return;
         return;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    list.RemoveAllItems();
+    hierarchyList.RemoveAllItems();
 }
 }
 
 
 void UpdateSceneWindow()
 void UpdateSceneWindow()
 {
 {
     ClearSceneWindow();
     ClearSceneWindow();
-    UpdateSceneWindowNode(0, editorScene);
+    UpdateSceneWindowNode(0, editorScene, null);
+
+    // Re-enable layout update
+    hierarchyList.EnableLayoutUpdate();
 
 
     // Clear copybuffer when whole window refreshed
     // Clear copybuffer when whole window refreshed
     copyBuffer.Clear();
     copyBuffer.Clear();
 }
 }
 
 
-uint UpdateSceneWindowNode(uint itemIndex, Node@ node)
-{
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-
+uint UpdateSceneWindowNode(uint itemIndex, Node@ node, UIElement@ parentItem)
+{  
     // Whenever we're updating, disable layout update to optimize speed
     // Whenever we're updating, disable layout update to optimize speed
-    list.contentElement.DisableLayoutUpdate();
+    hierarchyList.contentElement.DisableLayoutUpdate();
 
 
     // Remove old item if exists
     // Remove old item if exists
-    if (itemIndex < list.numItems && (node is null || (list.items[itemIndex].vars["Type"].GetInt() == ITEM_NODE &&
-        list.items[itemIndex].vars["NodeID"].GetUInt() == node.id)))
-        list.RemoveItem(itemIndex);
+    if (itemIndex < hierarchyList.numItems && (node is null || (hierarchyList.items[itemIndex].vars["Type"].GetInt() == ITEM_NODE &&
+        hierarchyList.items[itemIndex].vars["NodeID"].GetUInt() == node.id)))
+        hierarchyList.RemoveItem(itemIndex);
     if (node is null)
     if (node is null)
     {
     {
-        list.contentElement.EnableLayoutUpdate();
-        list.contentElement.UpdateLayout();
+        hierarchyList.contentElement.EnableLayoutUpdate();
+        hierarchyList.contentElement.UpdateLayout();
         return itemIndex;
         return itemIndex;
     }
     }
 
 
-    int indent = GetNodeIndent(node);
-
     Text@ text = Text();
     Text@ text = Text();
     text.SetStyle(uiStyle, "FileSelectorListText");
     text.SetStyle(uiStyle, "FileSelectorListText");
     text.vars["Type"] = ITEM_NODE;
     text.vars["Type"] = ITEM_NODE;
     text.vars["NodeID"] = node.id;
     text.vars["NodeID"] = node.id;
-    text.vars["Indent"] = indent;
-    text.text = GetNodeTitle(node, indent);
+    text.text = GetNodeTitle(node);
+    
     // Nodes can be moved by drag and drop. The root node (scene) can not.
     // Nodes can be moved by drag and drop. The root node (scene) can not.
     if (node.typeName == "Node")
     if (node.typeName == "Node")
         text.dragDropMode = DD_SOURCE_AND_TARGET;
         text.dragDropMode = DD_SOURCE_AND_TARGET;
     else
     else
         text.dragDropMode = DD_TARGET;
         text.dragDropMode = DD_TARGET;
 
 
-    list.InsertItem(itemIndex, text);
-
-    ++itemIndex;
+    hierarchyList.InsertItem(itemIndex++, text, parentItem);
 
 
     // Update components first
     // Update components first
-    for (uint j = 0; j < node.numComponents; ++j)
+    for (uint i = 0; i < node.numComponents; ++i)
     {
     {
-        Component@ component = node.components[j];
-        AddComponentToSceneWindow(component, indent + 1, itemIndex);
+        Component@ component = node.components[i];
+        AddComponentToSceneWindow(component, itemIndex, text);
         ++itemIndex;
         ++itemIndex;
     }
     }
 
 
@@ -165,35 +161,28 @@ uint UpdateSceneWindowNode(uint itemIndex, Node@ node)
     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 = UpdateSceneWindowNode(itemIndex, childNode);
+        itemIndex = UpdateSceneWindowNode(itemIndex, childNode, text);
     }
     }
 
 
     // Re-enable layout update (and do manual layout) now
     // Re-enable layout update (and do manual layout) now
-    list.contentElement.EnableLayoutUpdate();
-    list.contentElement.UpdateLayout();
+    hierarchyList.contentElement.EnableLayoutUpdate();
+    hierarchyList.contentElement.UpdateLayout();
 
 
     return itemIndex;
     return itemIndex;
 }
 }
 
 
 void UpdateSceneWindowNodeOnly(uint itemIndex, Node@ node)
 void UpdateSceneWindowNodeOnly(uint itemIndex, Node@ node)
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-
-    int indent = GetNodeIndent(node);
-
-    Text@ text = list.items[itemIndex];
+    Text@ text = hierarchyList.items[itemIndex];
     if (text is null)
     if (text is null)
         return;
         return;
-    text.text = GetNodeTitle(node, indent);
+    text.text = GetNodeTitle(node);
 }
 }
 
 
 void UpdateSceneWindowNode(Node@ node)
 void UpdateSceneWindowNode(Node@ node)
 {
 {
-    uint index = GetNodeListIndex(node);
-    if (index == NO_ITEM)
-        index = GetParentAddIndex(node);
-
-    UpdateSceneWindowNode(index, node);
+    // In case of node's parent is not found in the hierarchy list then the node will inserted at the root level, but it should not happen
+    UpdateSceneWindowNode(M_MAX_UNSIGNED, node, hierarchyList.items[GetNodeListIndex(node.parent)]);
 }
 }
 
 
 void UpdateSceneWindowNodeOnly(Node@ node)
 void UpdateSceneWindowNodeOnly(Node@ node)
@@ -202,18 +191,15 @@ void UpdateSceneWindowNodeOnly(Node@ node)
     UpdateSceneWindowNodeOnly(index, node);
     UpdateSceneWindowNodeOnly(index, node);
 }
 }
 
 
-void AddComponentToSceneWindow(Component@ component, int indent, uint compItemIndex)
+void AddComponentToSceneWindow(Component@ component, uint compItemIndex, UIElement@ parentItem)
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-
     Text@ text = Text();
     Text@ text = Text();
     text.SetStyle(uiStyle, "FileSelectorListText");
     text.SetStyle(uiStyle, "FileSelectorListText");
     text.vars["Type"] = ITEM_COMPONENT;
     text.vars["Type"] = ITEM_COMPONENT;
     text.vars["NodeID"] = component.node.id;
     text.vars["NodeID"] = component.node.id;
     text.vars["ComponentID"] = component.id;
     text.vars["ComponentID"] = component.id;
-    text.vars["Indent"] = indent;
-    text.text = GetComponentTitle(component, indent);
-    list.InsertItem(compItemIndex, text);
+    text.text = GetComponentTitle(component);
+    hierarchyList.InsertItem(compItemIndex, text, parentItem);
 }
 }
 
 
 uint GetNodeListIndex(Node@ node)
 uint GetNodeListIndex(Node@ node)
@@ -221,13 +207,12 @@ uint GetNodeListIndex(Node@ node)
     if (node is null)
     if (node is null)
         return NO_ITEM;
         return NO_ITEM;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    uint numItems = list.numItems;
+    uint numItems = hierarchyList.numItems;
     uint nodeID = node.id;
     uint nodeID = node.id;
 
 
     for (uint i = 0; i < numItems; ++i)
     for (uint i = 0; i < numItems; ++i)
     {
     {
-        UIElement@ item = list.items[i];
+        UIElement@ item = hierarchyList.items[i];
         if (item.vars["Type"].GetInt() == ITEM_NODE && item.vars["NodeID"].GetUInt() == nodeID)
         if (item.vars["Type"].GetInt() == ITEM_NODE && item.vars["NodeID"].GetUInt() == nodeID)
             return i;
             return i;
     }
     }
@@ -235,46 +220,17 @@ uint GetNodeListIndex(Node@ node)
     return NO_ITEM;
     return NO_ITEM;
 }
 }
 
 
-uint GetParentAddIndex(Node@ node)
-{
-    if (node is null || node.parent is null)
-        return NO_ITEM;
-
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    uint numItems = list.numItems;
-    uint parentID = node.parent.id;
-
-    for (uint i = 0; i < numItems; ++i)
-    {
-        UIElement@ item = list.items[i];
-        if (item.vars["Type"].GetInt() == ITEM_NODE && item.vars["NodeID"].GetUInt() == parentID)
-        {
-            int indent = item.vars["Indent"].GetInt();
-            for (uint j = i + 1; j < numItems; ++j)
-            {
-                // Scan for the next node on this or lower level; that is the place to insert the new child node
-                if (list.items[j].vars["Indent"].GetInt() <= indent)
-                    return j;
-            }
-            return numItems;
-        }
-    }
-
-    return NO_ITEM;
-}
-
 uint GetNodeListIndex(Node@ node, uint startPos)
 uint GetNodeListIndex(Node@ node, uint startPos)
 {
 {
     if (node is null)
     if (node is null)
         return NO_ITEM;
         return NO_ITEM;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    uint numItems = list.numItems;
+    uint numItems = hierarchyList.numItems;
     uint nodeID = node.id;
     uint nodeID = node.id;
 
 
     for (uint i = startPos; i < numItems; --i)
     for (uint i = startPos; i < numItems; --i)
     {
     {
-        UIElement@ item = list.items[i];
+        UIElement@ item = hierarchyList.items[i];
         if (item.vars["Type"].GetInt() == ITEM_NODE && item.vars["NodeID"].GetInt() == int(nodeID))
         if (item.vars["Type"].GetInt() == ITEM_NODE && item.vars["NodeID"].GetInt() == int(nodeID))
             return i;
             return i;
     }
     }
@@ -284,8 +240,7 @@ uint GetNodeListIndex(Node@ node, uint startPos)
 
 
 Node@ GetListNode(uint index)
 Node@ GetListNode(uint index)
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    UIElement@ item = list.items[index];
+    UIElement@ item = hierarchyList.items[index];
     if (item is null)
     if (item is null)
         return null;
         return null;
 
 
@@ -294,8 +249,7 @@ Node@ GetListNode(uint index)
 
 
 Component@ GetListComponent(uint index)
 Component@ GetListComponent(uint index)
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    UIElement@ item = list.items[index];
+    UIElement@ item = hierarchyList.items[index];
     return GetListComponent(item);
     return GetListComponent(item);
 }
 }
 
 
@@ -315,12 +269,10 @@ uint GetComponentListIndex(Component@ component)
     if (component is null)
     if (component is null)
         return NO_ITEM;
         return NO_ITEM;
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    uint numItems = list.numItems;
-
+    uint numItems = hierarchyList.numItems;
     for (uint i = 0; i < numItems; ++i)
     for (uint i = 0; i < numItems; ++i)
     {
     {
-        UIElement@ item = list.items[i];
+        UIElement@ item = hierarchyList.items[i];
         if (item.vars["Type"].GetInt() == ITEM_COMPONENT && item.vars["ComponentID"].GetUInt() == component.id)
         if (item.vars["Type"].GetInt() == ITEM_COMPONENT && item.vars["ComponentID"].GetUInt() == component.id)
             return i;
             return i;
     }
     }
@@ -341,13 +293,8 @@ int GetNodeIndent(Node@ node)
     return indent;
     return indent;
 }
 }
 
 
-String GetNodeTitle(Node@ node, int indent)
+String GetNodeTitle(Node@ node)
 {
 {
-    String indentStr;
-    indentStr.Resize(indent);
-    for (int i = 0; i < indent; ++i)
-        indentStr[i] = ' ';
-
     String idStr;
     String idStr;
     if (node.id >= FIRST_LOCAL_ID)
     if (node.id >= FIRST_LOCAL_ID)
         idStr = "Local " + String(node.id - FIRST_LOCAL_ID);
         idStr = "Local " + String(node.id - FIRST_LOCAL_ID);
@@ -355,32 +302,25 @@ String GetNodeTitle(Node@ node, int indent)
         idStr = String(node.id);
         idStr = String(node.id);
 
 
     if (node.name.empty)
     if (node.name.empty)
-        return indentStr + node.typeName + " (" + idStr + ")";
+        return node.typeName + " (" + idStr + ")";
     else
     else
-        return indentStr + node.name + " (" + idStr + ")";
+        return node.name + " (" + idStr + ")";
 }
 }
 
 
-String GetComponentTitle(Component@ component, int indent)
+String GetComponentTitle(Component@ component)
 {
 {
-    String indentStr;
     String localStr;
     String localStr;
-    indentStr.Resize(indent);
-    for (int i = 0; i < indent; ++i)
-        indentStr[i] = ' ';
-
     if (component.id >= FIRST_LOCAL_ID)
     if (component.id >= FIRST_LOCAL_ID)
         localStr = " (Local)";
         localStr = " (Local)";
 
 
-    return indentStr + component.typeName + localStr;
+    return component.typeName + localStr;
 }
 }
 
 
 void SelectNode(Node@ node, bool multiselect)
 void SelectNode(Node@ node, bool multiselect)
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-
     if (node is null && !multiselect)
     if (node is null && !multiselect)
     {
     {
-        list.ClearSelection();
+        hierarchyList.ClearSelection();
         return;
         return;
     }
     }
 
 
@@ -395,45 +335,43 @@ void SelectNode(Node@ node, bool multiselect)
         node = parent;
         node = parent;
     }
     }
     
     
-    uint numItems = list.numItems;
+    uint numItems = hierarchyList.numItems;
     uint parentItem = GetNodeListIndex(node);
     uint parentItem = GetNodeListIndex(node);
 
 
     if (nodeItem < numItems)
     if (nodeItem < numItems)
     {
     {
         // Expand the node chain now
         // Expand the node chain now
-        if (!multiselect || !list.IsSelected(nodeItem))
+        if (!multiselect || !hierarchyList.IsSelected(nodeItem))
         {
         {
             if (parentItem < numItems)
             if (parentItem < numItems)
-                list.Expand(parentItem, true);
-            list.Expand(nodeItem, true);
+                hierarchyList.Expand(parentItem, true);
+            hierarchyList.Expand(nodeItem, true);
         }
         }
         // This causes an event to be sent, in response we set the node/component selections, and refresh editors
         // This causes an event to be sent, in response we set the node/component selections, and refresh editors
         if (!multiselect)
         if (!multiselect)
-            list.selection = nodeItem;
+            hierarchyList.selection = nodeItem;
         else
         else
-            list.ToggleSelection(nodeItem);
+            hierarchyList.ToggleSelection(nodeItem);
     }
     }
     else
     else
     {
     {
         if (!multiselect)
         if (!multiselect)
-            list.ClearSelection();
+            hierarchyList.ClearSelection();
     }
     }
 }
 }
 
 
 void SelectComponent(Component@ component, bool multiselect)
 void SelectComponent(Component@ component, bool multiselect)
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-
     if (component is null && !multiselect)
     if (component is null && !multiselect)
     {
     {
-        list.ClearSelection();
+        hierarchyList.ClearSelection();
         return;
         return;
     }
     }
     
     
     Node@ node = component.node;
     Node@ node = component.node;
     if (node is null && !multiselect)
     if (node is null && !multiselect)
     {
     {
-        list.ClearSelection();
+        hierarchyList.ClearSelection();
         return;
         return;
     }
     }
 
 
@@ -449,34 +387,34 @@ void SelectComponent(Component@ component, bool multiselect)
         node = parent;
         node = parent;
     }
     }
 
 
-    uint numItems = list.numItems;
+    uint numItems = hierarchyList.numItems;
     uint parentItem = GetNodeListIndex(node);
     uint parentItem = GetNodeListIndex(node);
 
 
-    if (parentItem >= list.numItems && !multiselect)
+    if (parentItem >= hierarchyList.numItems && !multiselect)
     {
     {
-        list.ClearSelection();
+        hierarchyList.ClearSelection();
         return;
         return;
     }
     }
 
 
     if (nodeItem < numItems && componentItem < numItems)
     if (nodeItem < numItems && componentItem < numItems)
     {
     {
         // Expand the node chain now
         // Expand the node chain now
-        if (!multiselect || !list.IsSelected(componentItem))
+        if (!multiselect || !hierarchyList.IsSelected(componentItem))
         {
         {
             if (parentItem < numItems)
             if (parentItem < numItems)
-                list.Expand(parentItem, true);
-            list.Expand(nodeItem, true);
+                hierarchyList.Expand(parentItem, true);
+            hierarchyList.Expand(nodeItem, true);
         }
         }
         // This causes an event to be sent, in response we set the node/component selections, and refresh editors
         // This causes an event to be sent, in response we set the node/component selections, and refresh editors
         if (!multiselect)
         if (!multiselect)
-            list.selection = componentItem;
+            hierarchyList.selection = componentItem;
         else
         else
-            list.ToggleSelection(componentItem);
+            hierarchyList.ToggleSelection(componentItem);
     }
     }
     else
     else
     {
     {
         if (!multiselect)
         if (!multiselect)
-            list.ClearSelection();
+            hierarchyList.ClearSelection();
     }
     }
 }
 }
 
 
@@ -487,8 +425,7 @@ void HandleSceneWindowSelectionChange()
     
     
     ClearSelection();
     ClearSelection();
 
 
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    Array<uint> indices = list.selections;
+    Array<uint> indices = hierarchyList.selections;
 
 
     // Enable Expand/Collapse button when there is selection
     // Enable Expand/Collapse button when there is selection
     EnableExpandCollapseButtons(indices.length > 0);
     EnableExpandCollapseButtons(indices.length > 0);
@@ -496,7 +433,7 @@ void HandleSceneWindowSelectionChange()
     for (uint i = 0; i < indices.length; ++i)
     for (uint i = 0; i < indices.length; ++i)
     {
     {
         uint index = indices[i];
         uint index = indices[i];
-        UIElement@ item = list.items[index];
+        UIElement@ item = hierarchyList.items[index];
         int type = item.vars["Type"].GetInt();
         int type = item.vars["Type"].GetInt();
         if (type == ITEM_COMPONENT)
         if (type == ITEM_COMPONENT)
         {
         {
@@ -606,10 +543,8 @@ void HandleSceneWindowSelectionChange()
 
 
 void HandleSceneWindowItemDoubleClick(StringHash eventType, VariantMap& eventData)
 void HandleSceneWindowItemDoubleClick(StringHash eventType, VariantMap& eventData)
 {
 {
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-
     uint index = eventData["Selection"].GetUInt();
     uint index = eventData["Selection"].GetUInt();
-    list.ToggleExpand(index);
+    hierarchyList.ToggleExpand(index);
 }
 }
 
 
 void HandleSceneWindowKey(StringHash eventType, VariantMap& eventData)
 void HandleSceneWindowKey(StringHash eventType, VariantMap& eventData)
@@ -686,15 +621,13 @@ bool TestSceneWindowElements(UIElement@ source, UIElement@ target)
 void FocusNode(Node@ node)
 void FocusNode(Node@ node)
 {
 {
     uint index = GetNodeListIndex(node);
     uint index = GetNodeListIndex(node);
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    list.selection = index;
+    hierarchyList.selection = index;
 }
 }
 
 
 void FocusComponent(Component@ component)
 void FocusComponent(Component@ component)
 {
 {
     uint index = GetComponentListIndex(component);
     uint index = GetComponentListIndex(component);
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    list.selection = index;
+    hierarchyList.selection = index;
 }
 }
 
 
 void HandleCreateNode(StringHash eventType, VariantMap& eventData)
 void HandleCreateNode(StringHash eventType, VariantMap& eventData)
@@ -748,8 +681,7 @@ bool CheckSceneWindowFocus()
 {
 {
     // When we do scene operations based on key shortcuts, make sure either the 3D scene or the node list is focused,
     // When we do scene operations based on key shortcuts, make sure either the 3D scene or the node list is focused,
     // not for example a file selector
     // not for example a file selector
-    ListView@ list = sceneWindow.GetChild("NodeList", true);
-    if (ui.focusElement is list || ui.focusElement is null)
+    if (ui.focusElement is hierarchyList || ui.focusElement is null)
         return true;
         return true;
     else
     else
         return false;
         return false;
@@ -779,7 +711,7 @@ void HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
 
 
     Node@ node = eventData["Node"].GetNode();
     Node@ node = eventData["Node"].GetNode();
     uint index = GetNodeListIndex(node);
     uint index = GetNodeListIndex(node);
-    UpdateSceneWindowNode(index, null);
+    UpdateSceneWindowNode(index, null, null);
 }
 }
 
 
 void HandleComponentAdded(StringHash eventType, VariantMap& eventData)
 void HandleComponentAdded(StringHash eventType, VariantMap& eventData)

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

@@ -8,7 +8,7 @@ void CreateEditorSettingsDialog()
     if (settingsDialog !is null)
     if (settingsDialog !is null)
         return;
         return;
     
     
-    settingsDialog = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSettingsDialog.xml"), uiStyle);
+    settingsDialog = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSettingsDialog.xml"));
     ui.root.AddChild(settingsDialog);
     ui.root.AddChild(settingsDialog);
     settingsDialog.opacity = uiMaxOpacity;
     settingsDialog.opacity = uiMaxOpacity;
     CenterDialog(settingsDialog);
     CenterDialog(settingsDialog);

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

@@ -25,6 +25,7 @@ float uiMaxOpacity = 0.7;
 void CreateUI()
 void CreateUI()
 {
 {
     uiStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
     uiStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
+    ui.root.defaultStyle = uiStyle;
 
 
     CreateCursor();
     CreateCursor();
     CreateMenuBar();
     CreateMenuBar();

BIN
Bin/Data/Textures/UI.png


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

@@ -245,6 +245,14 @@
             <attribute name="Clip Border" value="1 1 1 1" />
             <attribute name="Clip Border" value="1 1 1 1" />
         </element>
         </element>
     </element>
     </element>
+    <element type="ListViewHierarchyOverlay">
+        <attribute name="Min Size" value="16 16" />
+        <attribute name="Max Size" value="16 16" />
+        <attribute name="Texture" value="Texture2D;Textures/UI.png" />
+        <attribute name="Image Rect" value="160 0 176 16" />
+        <attribute name="Checked Image Offset" value="16 0" />
+        <attribute name="Hover Image Offset" value="0 16" />
+    </element>
     <element type="Menu">
     <element type="Menu">
         <attribute name="Texture" value="Texture2D;Textures/UI.png" />
         <attribute name="Texture" value="Texture2D;Textures/UI.png" />
         <attribute name="Image Rect" value="96 0 112 16" />
         <attribute name="Image Rect" value="96 0 112 16" />

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

@@ -24,7 +24,7 @@
         <attribute name="Layout Mode" value="Horizontal" />
         <attribute name="Layout Mode" value="Horizontal" />
         <attribute name="Layout Spacing" value="4" />
         <attribute name="Layout Spacing" value="4" />
         <element type="Button">
         <element type="Button">
-            <attribute name="Name" value="ExpandAllButton" />
+            <attribute name="Name" value="ExpandButton" />
             <attribute name="Min Size" value="70 17" />
             <attribute name="Min Size" value="70 17" />
             <attribute name="Max Size" value="70 17" />
             <attribute name="Max Size" value="70 17" />
             <attribute name="Layout Mode" value="Vertical" />
             <attribute name="Layout Mode" value="Vertical" />
@@ -35,7 +35,7 @@
             </element>
             </element>
         </element>
         </element>
         <element type="Button">
         <element type="Button">
-            <attribute name="Name" value="CollapseAllButton" />
+            <attribute name="Name" value="CollapseButton" />
             <attribute name="Min Size" value="70 17" />
             <attribute name="Min Size" value="70 17" />
             <attribute name="Max Size" value="70 17" />
             <attribute name="Max Size" value="70 17" />
             <attribute name="Layout Mode" value="Vertical" />
             <attribute name="Layout Mode" value="Vertical" />
@@ -45,6 +45,16 @@
                 <attribute name="Text Alignment" value="Center" />
                 <attribute name="Text Alignment" value="Center" />
             </element>
             </element>
         </element>
         </element>
+        <element type="CheckBox">
+            <attribute name="Name" value="AllCheckBox" />
+            <attribute name="Min Size" value="15 15" />
+            <attribute name="Max Size" value="15 15" />
+            <attribute name="Layout Mode" value="Horizontal" />
+            <attribute name="Layout Border" value="20 0 0 0" />
+            <element type="Text">
+                <attribute name="Text" value="All" />
+            </element>
+        </element>
     </element>
     </element>
     <element type="ListView">
     <element type="ListView">
         <attribute name="Name" value="NodeList" />
         <attribute name="Name" value="NodeList" />

+ 87 - 15
Docs/ScriptAPI.dox

@@ -3101,7 +3101,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3151,9 +3152,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3202,7 +3207,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3254,9 +3260,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3311,7 +3321,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3366,9 +3377,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3427,7 +3442,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3480,9 +3496,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3539,7 +3559,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3592,9 +3613,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3650,7 +3675,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3703,9 +3729,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3765,7 +3795,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3818,9 +3849,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3878,7 +3913,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -3930,9 +3966,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -3989,7 +4029,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -4001,7 +4042,7 @@ Methods:<br>
 - void SetViewPosition(int, int)
 - void SetViewPosition(int, int)
 - void SetScrollBarsVisible(bool, bool)
 - void SetScrollBarsVisible(bool, bool)
 - void AddItem(UIElement@)
 - void AddItem(UIElement@)
-- void InsertItem(uint, UIElement@)
+- void InsertItem(uint, UIElement@, UIElement@ arg2 = null)
 - void RemoveItem(UIElement@, uint arg1 = 0)
 - void RemoveItem(UIElement@, uint arg1 = 0)
 - void RemoveItem(uint)
 - void RemoveItem(uint)
 - void RemoveAllItems()
 - void RemoveAllItems()
@@ -4014,7 +4055,9 @@ Methods:<br>
 - void Expand(uint, bool, bool arg2 = false)
 - void Expand(uint, bool, bool arg2 = false)
 - void ToggleExpand(uint, bool arg1 = false)
 - void ToggleExpand(uint, bool arg1 = false)
 - bool IsSelected(uint) const
 - bool IsSelected(uint) const
+- bool IsExpanded(uint) const
 - UIElement@[]@ GetItems() const
 - UIElement@[]@ GetItems() const
+- uint FindItem(UIElement@)
 
 
 Properties:<br>
 Properties:<br>
 - ShortStringHash type (readonly)
 - ShortStringHash type (readonly)
@@ -4056,9 +4099,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -4126,7 +4173,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -4180,9 +4228,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -4243,7 +4295,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -4295,9 +4348,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -4363,7 +4420,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -4420,9 +4478,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -4486,7 +4548,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -4549,9 +4612,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
@@ -4620,7 +4687,8 @@ Methods:<br>
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
 - void AddChild(UIElement@)
 - void AddChild(UIElement@)
 - void InsertChild(uint, UIElement@)
 - void InsertChild(uint, UIElement@)
-- void RemoveChild(UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
 - void RemoveAllChildren()
 - void RemoveAllChildren()
 - void Remove()
 - void Remove()
 - UIElement@ GetChild(const String&, bool arg1 = false) const
 - UIElement@ GetChild(const String&, bool arg1 = false) const
@@ -4672,9 +4740,13 @@ Properties:<br>
 - bool colorGradient (readonly)
 - bool colorGradient (readonly)
 - FocusMode focusMode
 - FocusMode focusMode
 - uint dragDropMode
 - uint dragDropMode
+- XMLFile@ defaultStyle
 - LayoutMode layoutMode
 - LayoutMode layoutMode
 - int layoutSpacing
 - int layoutSpacing
 - IntRect layoutBorder
 - IntRect layoutBorder
+- int indent
+- int indentSpacing
+- int indentWidth (readonly)
 - IntVector2 rotationPivot
 - IntVector2 rotationPivot
 - float rotation
 - float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)

+ 16 - 3
Engine/Engine/APITemplates.h

@@ -776,9 +776,14 @@ static UIElement* UIElementCreateChild(const String& typeName, const String& nam
     return ptr->CreateChild(ShortStringHash(typeName), name);
     return ptr->CreateChild(ShortStringHash(typeName), name);
 }
 }
 
 
-static void UIElementRemoveChild(UIElement* child, UIElement* ptr)
+static void UIElementRemoveChild(UIElement* child, unsigned index, UIElement* ptr)
 {
 {
-    ptr->RemoveChild(child);
+    ptr->RemoveChild(child, index);
+}
+
+static void UIElementRemoveChild(unsigned index, UIElement* ptr)
+{
+    ptr->RemoveChildAtIndex(index);
 }
 }
 
 
 static CScriptArray* UIElementGetChildren(bool recursive, UIElement* ptr)
 static CScriptArray* UIElementGetChildren(bool recursive, UIElement* ptr)
@@ -837,7 +842,8 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "UIElement@+ CreateChild(const String&in, const String&in name = String())", asFUNCTION(UIElementCreateChild), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "UIElement@+ CreateChild(const String&in, const String&in name = String())", asFUNCTION(UIElementCreateChild), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "void AddChild(UIElement@+)", asMETHOD(T, AddChild), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void AddChild(UIElement@+)", asMETHOD(T, AddChild), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void InsertChild(uint, UIElement@+)", asMETHOD(T, InsertChild), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void InsertChild(uint, UIElement@+)", asMETHOD(T, InsertChild), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void RemoveChild(UIElement@+)", asFUNCTION(UIElementRemoveChild), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "void RemoveChild(UIElement@+, uint arg1 = 0)", asFUNCTIONPR(UIElementRemoveChild, (UIElement*, unsigned, UIElement*), void), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "void RemoveChild(uint)", asFUNCTIONPR(UIElementRemoveChild, (unsigned, UIElement*), void), asCALL_CDECL_OBJLAST);
     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);
@@ -906,12 +912,19 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "FocusMode get_focusMode() const", asMETHOD(T, GetFocusMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "FocusMode get_focusMode() const", asMETHOD(T, GetFocusMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_dragDropMode(uint)", asMETHOD(T, SetDragDropMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_dragDropMode(uint)", asMETHOD(T, SetDragDropMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint get_dragDropMode() const", asMETHOD(T, GetDragDropMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint get_dragDropMode() const", asMETHOD(T, GetDragDropMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_defaultStyle(XMLFile@+)", asMETHOD(T, SetDefaultStyle), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "XMLFile@+ get_defaultStyle()", asMETHOD(T, GetDefaultStyle), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_layoutMode(LayoutMode)", asMETHOD(T, SetLayoutMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_layoutMode(LayoutMode)", asMETHOD(T, SetLayoutMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "LayoutMode get_layoutMode() const", asMETHOD(T, GetLayoutMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "LayoutMode get_layoutMode() const", asMETHOD(T, GetLayoutMode), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_layoutSpacing(int)", asMETHOD(T, SetLayoutSpacing), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_layoutSpacing(int)", asMETHOD(T, SetLayoutSpacing), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "int get_layoutSpacing() const", asMETHOD(T, GetLayoutSpacing), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "int get_layoutSpacing() const", asMETHOD(T, GetLayoutSpacing), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_layoutBorder(const IntRect&)", asMETHOD(T, SetLayoutBorder), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_layoutBorder(const IntRect&)", asMETHOD(T, SetLayoutBorder), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const IntRect& get_layoutBorder() const", asMETHOD(T, GetLayoutBorder), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const IntRect& get_layoutBorder() const", asMETHOD(T, GetLayoutBorder), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_indent(int)", asMETHOD(T, SetIndent), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "int get_indent() const", asMETHOD(T, GetIndent), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_indentSpacing(int)", asMETHOD(T, SetIndentSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "int get_indentSpacing() const", asMETHOD(T, GetIndentSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "int get_indentWidth() const", asMETHOD(T, GetIndentWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_rotationPivot(const IntVector2&)", asMETHOD(T, SetRotationPivot), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_rotationPivot(const IntVector2&)", asMETHOD(T, SetRotationPivot), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const IntVector2& get_rotationPivot() const", asMETHOD(T, GetRotationPivot), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const IntVector2& get_rotationPivot() const", asMETHOD(T, GetRotationPivot), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_rotation(float)", asMETHOD(T, SetRotation), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_rotation(float)", asMETHOD(T, SetRotation), asCALL_THISCALL);

+ 3 - 1
Engine/Engine/UIAPI.cpp

@@ -233,7 +233,7 @@ static void RegisterListView(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ListView", "void SetViewPosition(int, int)", asMETHODPR(ListView, SetViewPosition, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void SetViewPosition(int, int)", asMETHODPR(ListView, SetViewPosition, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void SetScrollBarsVisible(bool, bool)", asMETHOD(ListView, SetScrollBarsVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void SetScrollBarsVisible(bool, bool)", asMETHOD(ListView, SetScrollBarsVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void AddItem(UIElement@+)", asMETHOD(ListView, AddItem), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void AddItem(UIElement@+)", asMETHOD(ListView, AddItem), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ListView", "void InsertItem(uint, UIElement@+)", asMETHOD(ListView, InsertItem), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ListView", "void InsertItem(uint, UIElement@+, UIElement@+ arg2 = null)", asMETHOD(ListView, InsertItem), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveItem(UIElement@+, uint index = 0)", asMETHODPR(ListView, RemoveItem, (UIElement*, unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveItem(UIElement@+, uint index = 0)", asMETHODPR(ListView, RemoveItem, (UIElement*, unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveItem(uint)", asMETHODPR(ListView, RemoveItem, (unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveItem(uint)", asMETHODPR(ListView, RemoveItem, (unsigned), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveAllItems()", asMETHOD(ListView, RemoveAllItems), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void RemoveAllItems()", asMETHOD(ListView, RemoveAllItems), asCALL_THISCALL);
@@ -246,7 +246,9 @@ static void RegisterListView(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ListView", "void Expand(uint, bool, bool arg2 = false)", asMETHOD(ListView, Expand), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void Expand(uint, bool, bool arg2 = false)", asMETHOD(ListView, Expand), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void ToggleExpand(uint, bool arg1 = false)", asMETHOD(ListView, ToggleExpand), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void ToggleExpand(uint, bool arg1 = false)", asMETHOD(ListView, ToggleExpand), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "bool IsSelected(uint) const", asMETHOD(ListView, IsSelected), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "bool IsSelected(uint) const", asMETHOD(ListView, IsSelected), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ListView", "bool IsExpanded(uint) const", asMETHOD(ListView, IsExpanded), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "Array<UIElement@>@ GetItems() const", asFUNCTION(ListViewGetItems), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("ListView", "Array<UIElement@>@ GetItems() const", asFUNCTION(ListViewGetItems), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("ListView", "uint FindItem(UIElement@+)", asMETHOD(ListView, FindItem), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void set_viewPosition(const IntVector2&in)", asMETHODPR(ListView, SetViewPosition, (const IntVector2&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void set_viewPosition(const IntVector2&in)", asMETHODPR(ListView, SetViewPosition, (const IntVector2&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "const IntVector2& get_viewPosition() const", asMETHOD(ListView, GetViewPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "const IntVector2& get_viewPosition() const", asMETHOD(ListView, GetViewPosition), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "UIElement@+ get_contentElement() const", asMETHOD(ListView, GetContentElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "UIElement@+ get_contentElement() const", asMETHOD(ListView, GetContentElement), asCALL_THISCALL);

+ 8 - 2
Engine/IO/Log.cpp

@@ -190,8 +190,14 @@ void Log::WriteRaw(const String& message, bool error)
         #elif defined(IOS)
         #elif defined(IOS)
         SDL_IOS_LogMessage(message.CString());
         SDL_IOS_LogMessage(message.CString());
         #else
         #else
-        if (!quiet_)
-            PrintUnicode(message, error);
+        if (quiet_)
+        {
+            // If in quiet mode, still print the error message to the standard error stream
+            if (error)
+            	PrintUnicode(message, true);
+        }
+        else
+        	PrintUnicode(message, error);
         #endif
         #endif
         
         
         if (logFile_)
         if (logFile_)

+ 13 - 11
Engine/UI/BorderImage.cpp

@@ -150,7 +150,9 @@ void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& qua
     UIBatch batch(GetBatchTransform(), blendMode_ == BLEND_REPLACE && !allOpaque ? BLEND_ALPHA : blendMode_, currentScissor, texture_, &quads);
     UIBatch batch(GetBatchTransform(), blendMode_ == BLEND_REPLACE && !allOpaque ? BLEND_ALPHA : blendMode_, currentScissor, texture_, &quads);
     
     
     // Calculate size of the inner rect, and texture dimensions of the inner rect
     // Calculate size of the inner rect, and texture dimensions of the inner rect
-    const IntVector2& size = GetSize();
+    int x = GetIndentWidth();
+    IntVector2 size = GetSize();
+    size.x_ -= x;
     IntVector2 innerSize(
     IntVector2 innerSize(
         Max(size.x_ - border_.left_ - border_.right_, 0), 
         Max(size.x_ - border_.left_ - border_.right_, 0), 
         Max(size.y_ - border_.top_ - border_.bottom_, 0));
         Max(size.y_ - border_.top_ - border_.bottom_, 0));
@@ -160,30 +162,30 @@ void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& qua
     
     
     IntVector2 topLeft(imageRect_.left_, imageRect_.top_);
     IntVector2 topLeft(imageRect_.left_, imageRect_.top_);
     topLeft += offset;
     topLeft += offset;
-    
+
     // Top
     // Top
     if (border_.top_)
     if (border_.top_)
     {
     {
         if (border_.left_)
         if (border_.left_)
-            batch.AddQuad(*this, 0, 0, border_.left_, border_.top_, topLeft.x_, topLeft.y_);
+            batch.AddQuad(*this, x, 0, border_.left_, border_.top_, topLeft.x_, topLeft.y_);
         if (innerSize.x_)
         if (innerSize.x_)
-            batch.AddQuad(*this, border_.left_, 0, innerSize.x_, border_.top_,
+            batch.AddQuad(*this, x + border_.left_, 0, innerSize.x_, border_.top_,
             topLeft.x_ + border_.left_, topLeft.y_, innerTextureSize.x_, border_.top_, tiled_);
             topLeft.x_ + border_.left_, topLeft.y_, innerTextureSize.x_, border_.top_, tiled_);
         if (border_.right_)
         if (border_.right_)
-            batch.AddQuad(*this, border_.left_ + innerSize.x_, 0, border_.right_, border_.top_,
+            batch.AddQuad(*this, x + border_.left_ + innerSize.x_, 0, border_.right_, border_.top_,
             topLeft.x_ + border_.left_ + innerTextureSize.x_, topLeft.y_);
             topLeft.x_ + border_.left_ + innerTextureSize.x_, topLeft.y_);
     }
     }
     // Middle
     // Middle
     if (innerSize.y_)
     if (innerSize.y_)
     {
     {
         if (border_.left_)
         if (border_.left_)
-            batch.AddQuad(*this, 0, border_.top_, border_.left_, innerSize.y_,
+            batch.AddQuad(*this, x, border_.top_, border_.left_, innerSize.y_,
             topLeft.x_, topLeft.y_ + border_.top_, border_.left_, innerTextureSize.y_, tiled_);
             topLeft.x_, topLeft.y_ + border_.top_, border_.left_, innerTextureSize.y_, tiled_);
         if (innerSize.x_)
         if (innerSize.x_)
-            batch.AddQuad(*this, border_.left_, border_.top_, innerSize.x_, innerSize.y_,
+            batch.AddQuad(*this, x + border_.left_, border_.top_, innerSize.x_, innerSize.y_,
             topLeft.x_ + border_.left_, topLeft.y_ + border_.top_, innerTextureSize.x_, innerTextureSize.y_, tiled_);
             topLeft.x_ + border_.left_, topLeft.y_ + border_.top_, innerTextureSize.x_, innerTextureSize.y_, tiled_);
         if (border_.right_)
         if (border_.right_)
-            batch.AddQuad(*this, border_.left_ + innerSize.x_, border_.top_, border_.right_,
+            batch.AddQuad(*this, x + border_.left_ + innerSize.x_, border_.top_, border_.right_,
             innerSize.y_, topLeft.x_ + border_.left_ + innerTextureSize.x_, topLeft.y_ + border_.top_,
             innerSize.y_, topLeft.x_ + border_.left_ + innerTextureSize.x_, topLeft.y_ + border_.top_,
             border_.right_, innerTextureSize.y_, tiled_);
             border_.right_, innerTextureSize.y_, tiled_);
     }
     }
@@ -191,14 +193,14 @@ void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& qua
     if (border_.bottom_)
     if (border_.bottom_)
     {
     {
         if (border_.left_)
         if (border_.left_)
-            batch.AddQuad(*this, 0, border_.top_ + innerSize.y_, border_.left_, border_.bottom_,
+            batch.AddQuad(*this, x, border_.top_ + innerSize.y_, border_.left_, border_.bottom_,
             topLeft.x_, topLeft.y_ + border_.top_ + innerTextureSize.y_);
             topLeft.x_, topLeft.y_ + border_.top_ + innerTextureSize.y_);
         if (innerSize.x_)
         if (innerSize.x_)
-            batch.AddQuad(*this, border_.left_, border_.top_ + innerSize.y_, innerSize.x_,
+            batch.AddQuad(*this, x + border_.left_, border_.top_ + innerSize.y_, innerSize.x_,
             border_.bottom_, topLeft.x_ + border_.left_, topLeft.y_ + border_.top_ + innerTextureSize.y_,
             border_.bottom_, topLeft.x_ + border_.left_, topLeft.y_ + border_.top_ + innerTextureSize.y_,
             innerTextureSize.x_, border_.bottom_, tiled_);
             innerTextureSize.x_, border_.bottom_, tiled_);
         if (border_.right_)
         if (border_.right_)
-            batch.AddQuad(*this, border_.left_ + innerSize.x_, border_.top_ + innerSize.y_,
+            batch.AddQuad(*this, x + border_.left_ + innerSize.x_, border_.top_ + innerSize.y_,
             border_.right_, border_.bottom_, topLeft.x_ + border_.left_ + innerTextureSize.x_, 
             border_.right_, border_.bottom_, topLeft.x_ + border_.left_ + innerTextureSize.x_, 
             topLeft.y_ + border_.top_ + innerTextureSize.y_);
             topLeft.y_ + border_.top_ + innerTextureSize.y_);
     }
     }

+ 1 - 1
Engine/UI/DropDownList.cpp

@@ -121,7 +121,7 @@ void DropDownList::OnShowPopup()
 
 
 void DropDownList::AddItem(UIElement* item)
 void DropDownList::AddItem(UIElement* item)
 {
 {
-    InsertItem(listView_->GetNumItems(), item);
+    InsertItem(M_MAX_UNSIGNED, item);
 }
 }
 
 
 void DropDownList::InsertItem(unsigned index, UIElement* item)
 void DropDownList::InsertItem(unsigned index, UIElement* item)

+ 301 - 106
Engine/UI/ListView.cpp

@@ -21,7 +21,7 @@
 //
 //
 
 
 #include "Precompiled.h"
 #include "Precompiled.h"
-#include "BorderImage.h"
+#include "CheckBox.h"
 #include "Context.h"
 #include "Context.h"
 #include "InputEvents.h"
 #include "InputEvents.h"
 #include "ListView.h"
 #include "ListView.h"
@@ -35,9 +35,6 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-static const ShortStringHash indentHash("Indent");
-static const ShortStringHash expandedHash("Expanded");
-
 static const char* highlightModes[] =
 static const char* highlightModes[] =
 {
 {
     "Never",
     "Never",
@@ -51,41 +48,128 @@ template<> HighlightMode Variant::Get<HighlightMode>() const
     return (HighlightMode)GetInt();
     return (HighlightMode)GetInt();
 }
 }
 
 
-int GetItemIndent(UIElement* item)
-{
-    return item ? item->GetVar(indentHash).GetInt() : 0;
-}
+static const ShortStringHash expandedHash("Expanded");
 
 
 bool GetItemExpanded(UIElement* item)
 bool GetItemExpanded(UIElement* item)
 {
 {
-    return item ? item->GetVar(expandedHash).GetBool() : true;
+    return item ? item->GetVar(expandedHash).GetBool() : false;
 }
 }
-    
+
 void SetItemExpanded(UIElement* item, bool enable)
 void SetItemExpanded(UIElement* item, bool enable)
 {
 {
     item->SetVar(expandedHash, enable);
     item->SetVar(expandedHash, enable);
 }
 }
 
 
+static const ShortStringHash hierarchyParentHash("HierarchyParent");
+
+bool GetItemHierarchyParent(UIElement* item)
+{
+    return item ? item->GetVar(hierarchyParentHash).GetBool() : false;
+}
+
+void SetItemHierarchyParent(UIElement* item, bool enable)
+{
+    item->SetVar(hierarchyParentHash, enable);
+}
+
+/// Hierarchy container (used by ListView internally when in hierarchy mode).
+class HierarchyContainer : public UIElement
+{
+    OBJECT(HierarchyContainer);
+
+public:
+    /// Construct.
+    HierarchyContainer(Context* context, UIElement* overlayContainer) :
+        UIElement(context),
+        overlayContainer_(overlayContainer)
+    {
+        SubscribeToEvent(this, E_LAYOUTUPDATED, HANDLER(HierarchyContainer, HandleLayoutUpdated));
+        SubscribeToEvent(overlayContainer->GetParent(), E_VIEWCHANGED, HANDLER(HierarchyContainer, HandleViewChanged));
+        SubscribeToEvent(E_UIMOUSECLICK, HANDLER(HierarchyContainer, HandleUIMouseClick));
+    }
+
+    /// Handle layout updated by adjusting the position of the overlays.
+    void HandleLayoutUpdated(StringHash eventType, VariantMap& eventData)
+    {
+        // Adjust the container size for child clipping effect
+        overlayContainer_->SetSize(GetParent()->GetSize());
+
+        for (unsigned i = 0; i < children_.Size(); ++i)
+        {
+            const IntVector2& position = children_[i]->GetPosition();
+            CheckBox* overlay = static_cast<CheckBox*>(overlayContainer_->GetChild(i));
+            bool visible = children_[i]->IsVisible() && GetItemHierarchyParent(children_[i]);
+            overlay->SetVisible(visible);
+            if (visible)
+            {
+                overlay->SetPosition(position.x_, position.y_);
+                overlay->SetChecked(GetItemExpanded(children_[i]));
+            }
+        }
+    }
+
+    /// Handle view changed by scrolling the overlays in tandem.
+    void HandleViewChanged(StringHash eventType, VariantMap& eventData)
+    {
+        using namespace ViewChanged;
+
+        int x = eventData[P_X].GetInt();
+        int y = eventData[P_Y].GetInt();
+
+        IntRect panelBorder = GetParent()->GetClipBorder();
+        overlayContainer_->SetChildOffset(IntVector2(-x + panelBorder.left_, -y + panelBorder.top_));
+    }
+
+    /// Handle mouse click on overlays by toggling the expansion state of the corresponding item
+    void HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
+    {
+        using namespace UIMouseClick;
+
+        UIElement* overlay = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());
+        if (overlay)
+        {
+            const Vector<SharedPtr<UIElement> >& children = overlayContainer_->GetChildren();
+            Vector<SharedPtr<UIElement> >::ConstIterator i = children.Find(SharedPtr<UIElement>(overlay));
+            if (i != children.End())
+                static_cast<ListView*>(overlayContainer_->GetParent())->ToggleExpand(i - children.Begin());
+        }
+    }
+
+    /// Insert a child element into a specific position in the child list.
+    void InsertChild(unsigned index, UIElement* element)
+    {
+        // Insert the overlay at the same index position to the overlay container
+        CheckBox* overlay = new CheckBox(context_);
+        overlay->SetStyle(overlayContainer_->GetDefaultStyle(), "ListViewHierarchyOverlay");
+        overlay->SetIndent(element->GetIndent() - 1);
+        overlay->SetFixedWidth(element->GetIndentWidth());
+        overlayContainer_->InsertChild(index, overlay);
+
+        // Then insert the element as child as per normal
+        UIElement::InsertChild(index, element);
+    }
+
+private:
+    UIElement* overlayContainer_;
+};
+
+OBJECTTYPESTATIC(HierarchyContainer);
 OBJECTTYPESTATIC(ListView);
 OBJECTTYPESTATIC(ListView);
 
 
 ListView::ListView(Context* context) :
 ListView::ListView(Context* context) :
     ScrollView(context),
     ScrollView(context),
     highlightMode_(HM_FOCUS),
     highlightMode_(HM_FOCUS),
     multiselect_(false),
     multiselect_(false),
-    hierarchyMode_(false),
+    hierarchyMode_(true),	// Init to true here so that the setter below takes effect
     clearSelectionOnDefocus_(false),
     clearSelectionOnDefocus_(false),
     doubleClickInterval_(500),
     doubleClickInterval_(500),
     lastClickedItem_(M_MAX_UNSIGNED)
     lastClickedItem_(M_MAX_UNSIGNED)
 {
 {
     resizeContentWidth_ = true;
     resizeContentWidth_ = true;
-    
-    UIElement* container = new UIElement(context_);
-    container->SetInternal(true);
-    container->SetActive(true);
-    container->SetLayout(LM_VERTICAL);
-    container->SetSortChildren(false);
-    SetContentElement(container);
-    
+
+    // By default list view is set to non-hierarchy mode
+    SetHierarchyMode(false);
+
     SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
     SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
     SubscribeToEvent(E_FOCUSCHANGED, HANDLER(ListView, HandleFocusChanged));
     SubscribeToEvent(E_FOCUSCHANGED, HANDLER(ListView, HandleFocusChanged));
 }
 }
@@ -97,7 +181,7 @@ ListView::~ListView()
 void ListView::RegisterObject(Context* context)
 void ListView::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<ListView>();
     context->RegisterFactory<ListView>();
-    
+
     ENUM_ACCESSOR_ATTRIBUTE(ListView, "Highlight Mode", GetHighlightMode, SetHighlightMode, HighlightMode, highlightModes, HM_FOCUS, AM_FILE);
     ENUM_ACCESSOR_ATTRIBUTE(ListView, "Highlight Mode", GetHighlightMode, SetHighlightMode, HighlightMode, highlightModes, HM_FOCUS, AM_FILE);
     ACCESSOR_ATTRIBUTE(ListView, VAR_BOOL, "Multiselect", GetMultiselect, SetMultiselect, bool, false, AM_FILE);
     ACCESSOR_ATTRIBUTE(ListView, VAR_BOOL, "Multiselect", GetMultiselect, SetMultiselect, bool, false, AM_FILE);
     ACCESSOR_ATTRIBUTE(ListView, VAR_BOOL, "Hierarchy Mode", GetHierarchyMode, SetHierarchyMode, bool, false, AM_FILE);
     ACCESSOR_ATTRIBUTE(ListView, VAR_BOOL, "Hierarchy Mode", GetHierarchyMode, SetHierarchyMode, bool, false, AM_FILE);
@@ -116,7 +200,7 @@ void ListView::OnKey(int key, int buttons, int qualifiers)
     bool additive = multiselect_ && qualifiers & (QUAL_SHIFT | QUAL_CTRL);
     bool additive = multiselect_ && qualifiers & (QUAL_SHIFT | QUAL_CTRL);
     int delta = 0;
     int delta = 0;
     int pageDirection = 1;
     int pageDirection = 1;
-    
+
     if (selection != M_MAX_UNSIGNED && numItems)
     if (selection != M_MAX_UNSIGNED && numItems)
     {
     {
         switch (key)
         switch (key)
@@ -129,7 +213,7 @@ void ListView::OnKey(int key, int buttons, int qualifiers)
                 return;
                 return;
             }
             }
             break;
             break;
-            
+
         case KEY_RETURN:
         case KEY_RETURN:
         case KEY_RETURN2:
         case KEY_RETURN2:
         case KEY_KP_ENTER:
         case KEY_KP_ENTER:
@@ -139,19 +223,19 @@ void ListView::OnKey(int key, int buttons, int qualifiers)
                 return;
                 return;
             }
             }
             break;
             break;
-            
+
         case KEY_UP:
         case KEY_UP:
             delta = -1;
             delta = -1;
             break;
             break;
-            
+
         case KEY_DOWN:
         case KEY_DOWN:
             delta = 1;
             delta = 1;
             break;
             break;
-            
+
         case KEY_PAGEUP:
         case KEY_PAGEUP:
             pageDirection = -1;
             pageDirection = -1;
             // Fallthru
             // Fallthru
-            
+
         case KEY_PAGEDOWN:
         case KEY_PAGEDOWN:
             {
             {
                 // Convert page step to pixels and see how many items have to be skipped to reach that many pixels
                 // Convert page step to pixels and see how many items have to be skipped to reach that many pixels
@@ -178,25 +262,25 @@ void ListView::OnKey(int key, int buttons, int qualifiers)
                 delta = okSelection - selection - pageDirection * invisible;
                 delta = okSelection - selection - pageDirection * invisible;
             }
             }
             break;
             break;
-            
+
         case KEY_HOME:
         case KEY_HOME:
             delta = -(int)GetNumItems();
             delta = -(int)GetNumItems();
             break;
             break;
-            
+
         case KEY_END:
         case KEY_END:
             delta = GetNumItems();
             delta = GetNumItems();
             break;
             break;
         }
         }
     }
     }
-    
+
     if (delta)
     if (delta)
     {
     {
         ChangeSelection(delta, additive);
         ChangeSelection(delta, additive);
         return;
         return;
     }
     }
-    
+
     using namespace UnhandledKey;
     using namespace UnhandledKey;
-    
+
     VariantMap eventData;
     VariantMap eventData;
     eventData[P_ELEMENT] = (void*)this;
     eventData[P_ELEMENT] = (void*)this;
     eventData[P_KEY] = key;
     eventData[P_KEY] = key;
@@ -205,21 +289,67 @@ void ListView::OnKey(int key, int buttons, int qualifiers)
     SendEvent(E_UNHANDLEDKEY, eventData);
     SendEvent(E_UNHANDLEDKEY, eventData);
 }
 }
 
 
+void ListView::OnResize()
+{
+    ScrollView::OnResize();
+
+    // When in hierarchy mode also need to resize the overlay container
+    if (hierarchyMode_)
+        overlayContainer_->SetSize(scrollPanel_->GetSize());
+}
+
 void ListView::AddItem(UIElement* item)
 void ListView::AddItem(UIElement* item)
 {
 {
-    InsertItem(contentElement_->GetNumChildren(), item);
+    InsertItem(M_MAX_UNSIGNED, item);
 }
 }
 
 
-void ListView::InsertItem(unsigned index, UIElement* item)
+void ListView::InsertItem(unsigned index, UIElement* item, UIElement* parentItem)
 {
 {
     if (!item || item->GetParent() == contentElement_)
     if (!item || item->GetParent() == contentElement_)
         return;
         return;
-    
+
     // Enable input so that clicking the item can be detected
     // Enable input so that clicking the item can be detected
     item->SetActive(true);
     item->SetActive(true);
     item->SetSelected(false);
     item->SetSelected(false);
-    contentElement_->InsertChild(index, item);
-    
+
+    unsigned numItems = contentElement_->GetNumChildren();
+    if (hierarchyMode_)
+    {
+        int baseIndent = 0;
+        if (parentItem)
+        {
+            baseIndent = parentItem->GetIndent();
+            SetItemHierarchyParent(parentItem, true);
+        }
+        item->SetIndent(baseIndent + 1);
+        SetItemExpanded(item, item->IsVisible());
+
+        // Adjust the index to ensure it is within the children index limit of the parent item
+        unsigned indexLimit = FindItem(parentItem);
+        if (index <= indexLimit)
+            index = indexLimit + 1;
+        else
+        {
+            while (++indexLimit < numItems)
+            {
+                if (contentElement_->GetChild(indexLimit)->GetIndent() <= baseIndent)
+                    break;
+            }
+            if (index > indexLimit)
+                index = indexLimit;
+        }
+
+        // Use the 'overrided' version to insert the child item
+        static_cast<HierarchyContainer*>(contentElement_.Get())->InsertChild(index, item);
+    }
+    else
+    {
+        if (index > numItems)
+            index = numItems;
+
+        contentElement_->InsertChild(index, item);
+    }
+
     // If necessary, shift the following selections
     // If necessary, shift the following selections
     if (!selections_.Empty())
     if (!selections_.Empty())
     {
     {
@@ -228,18 +358,15 @@ void ListView::InsertItem(unsigned index, UIElement* item)
             if (selections_[i] >= index)
             if (selections_[i] >= index)
                 ++selections_[i];
                 ++selections_[i];
         }
         }
-        
+
         UpdateSelectionEffect();
         UpdateSelectionEffect();
     }
     }
-    
-    if (hierarchyMode_)
-        SetItemExpanded(item, item->IsVisible());
 }
 }
 
 
 void ListView::RemoveItem(UIElement* item, unsigned index)
 void ListView::RemoveItem(UIElement* item, unsigned index)
 {
 {
     unsigned numItems = GetNumItems();
     unsigned numItems = GetNumItems();
-    
+
     for (unsigned i = index; i < numItems; ++i)
     for (unsigned i = index; i < numItems; ++i)
     {
     {
         if (GetItem(i) == item)
         if (GetItem(i) == item)
@@ -247,26 +374,49 @@ void ListView::RemoveItem(UIElement* item, unsigned index)
             item->SetSelected(false);
             item->SetSelected(false);
             selections_.Remove(i);
             selections_.Remove(i);
 
 
-            // Remove any child items in hierarchy mode
             unsigned removed = 1;
             unsigned removed = 1;
             if (hierarchyMode_)
             if (hierarchyMode_)
             {
             {
-                int baseIndent = GetItemIndent(item);
-                for (unsigned j = i + 1; ; ++j)
+                // Remove any child items in hierarchy mode
+                if (GetItemHierarchyParent(item))
                 {
                 {
-                    UIElement* childItem = GetItem(i + 1);
-                    if (!childItem)
-                        break;
-                    if (GetItemIndent(childItem) > baseIndent)
+                    int baseIndent = item->GetIndent();
+                    for (unsigned j = i + 1; ; ++j)
                     {
                     {
-                        childItem->SetSelected(false);
-                        selections_.Erase(j);
-                        contentElement_->RemoveChild(childItem, i + 1);
-                        ++removed;
+                        UIElement* childItem = GetItem(i + 1);
+                        if (!childItem)
+                            break;
+                        if (childItem->GetIndent() > baseIndent)
+                        {
+                            childItem->SetSelected(false);
+                            selections_.Erase(j);
+                            contentElement_->RemoveChildAtIndex(i + 1);
+                            overlayContainer_->RemoveChildAtIndex(i + 1);
+                            ++removed;
+                        }
+                        else
+                            break;
+                    }
+                }
+
+                // Check if the parent of removed item still has other children
+                if (i > 0)
+                {
+                    int baseIndent = item->GetIndent();
+                    UIElement* prevKin = GetItem(i - 1);		// Could be parent or sibling
+                    if (prevKin->GetIndent() < baseIndent)
+                    {
+                        UIElement* nextKin = GetItem(i + 1);	// Could be sibling or parent-sibling or 0 if index out of bound
+                        if (!nextKin || nextKin->GetIndent() < baseIndent)
+                        {
+                            // If we reach here then the parent has no other children
+                            SetItemHierarchyParent(prevKin, false);
+                        }
                     }
                     }
-                    else
-                        break;
                 }
                 }
+
+                // Remove the overlay at the same index
+                overlayContainer_->RemoveChildAtIndex(i);
             }
             }
 
 
             // If necessary, shift the following selections
             // If necessary, shift the following selections
@@ -277,11 +427,11 @@ void ListView::RemoveItem(UIElement* item, unsigned index)
                     if (selections_[j] > i)
                     if (selections_[j] > i)
                         selections_[j] -= removed;
                         selections_[j] -= removed;
                 }
                 }
-                
+
                 UpdateSelectionEffect();
                 UpdateSelectionEffect();
             }
             }
-            
-            contentElement_->RemoveChild(item, i);
+
+            contentElement_->RemoveChildAtIndex(i);
             break;
             break;
         }
         }
     }
     }
@@ -295,13 +445,14 @@ void ListView::RemoveItem(unsigned index)
 void ListView::RemoveAllItems()
 void ListView::RemoveAllItems()
 {
 {
     contentElement_->DisableLayoutUpdate();
     contentElement_->DisableLayoutUpdate();
-    
+
     ClearSelection();
     ClearSelection();
     contentElement_->RemoveAllChildren();
     contentElement_->RemoveAllChildren();
-    
+    if (hierarchyMode_)
+        overlayContainer_->RemoveAllChildren();
+
     contentElement_->EnableLayoutUpdate();
     contentElement_->EnableLayoutUpdate();
     contentElement_->UpdateLayout();
     contentElement_->UpdateLayout();
-    UpdateViewSize();
 }
 }
 
 
 void ListView::SetSelection(unsigned index)
 void ListView::SetSelection(unsigned index)
@@ -315,7 +466,7 @@ void ListView::SetSelection(unsigned index)
 void ListView::SetSelections(const PODVector<unsigned>& indices)
 void ListView::SetSelections(const PODVector<unsigned>& indices)
 {
 {
     unsigned numItems = GetNumItems();
     unsigned numItems = GetNumItems();
-    
+
     // Remove first items that should no longer be selected
     // Remove first items that should no longer be selected
     for (PODVector<unsigned>::Iterator i = selections_.Begin(); i != selections_.End();)
     for (PODVector<unsigned>::Iterator i = selections_.Begin(); i != selections_.End();)
     {
     {
@@ -323,9 +474,9 @@ void ListView::SetSelections(const PODVector<unsigned>& indices)
         if (!indices.Contains(index))
         if (!indices.Contains(index))
         {
         {
             i = selections_.Erase(i);
             i = selections_.Erase(i);
-            
+
             using namespace ItemSelected;
             using namespace ItemSelected;
-            
+
             VariantMap eventData;
             VariantMap eventData;
             eventData[P_ELEMENT] = (void*)this;
             eventData[P_ELEMENT] = (void*)this;
             eventData[P_SELECTION] = index;
             eventData[P_SELECTION] = index;
@@ -334,9 +485,9 @@ void ListView::SetSelections(const PODVector<unsigned>& indices)
         else
         else
             ++i;
             ++i;
     }
     }
-    
+
     bool added = false;
     bool added = false;
-    
+
     // Then add missing items
     // Then add missing items
     for (PODVector<unsigned>::ConstIterator i = indices.Begin(); i != indices.End(); ++i)
     for (PODVector<unsigned>::ConstIterator i = indices.Begin(); i != indices.End(); ++i)
     {
     {
@@ -352,9 +503,9 @@ void ListView::SetSelections(const PODVector<unsigned>& indices)
                     selections_.Push(index);
                     selections_.Push(index);
                     added = true;
                     added = true;
                 }
                 }
-                
+
                 using namespace ItemSelected;
                 using namespace ItemSelected;
-                
+
                 VariantMap eventData;
                 VariantMap eventData;
                 eventData[P_ELEMENT] = (void*)this;
                 eventData[P_ELEMENT] = (void*)this;
                 eventData[P_SELECTION] = *i;
                 eventData[P_SELECTION] = *i;
@@ -365,11 +516,11 @@ void ListView::SetSelections(const PODVector<unsigned>& indices)
         if (!multiselect_)
         if (!multiselect_)
             break;
             break;
     }
     }
-    
+
     // Re-sort selections if necessary
     // Re-sort selections if necessary
     if (added)
     if (added)
         Sort(selections_.Begin(), selections_.End());
         Sort(selections_.Begin(), selections_.End());
-    
+
     UpdateSelectionEffect();
     UpdateSelectionEffect();
     SendEvent(E_SELECTIONCHANGED);
     SendEvent(E_SELECTIONCHANGED);
 }
 }
@@ -382,21 +533,21 @@ void ListView::AddSelection(unsigned index)
     {
     {
         if (index >= GetNumItems())
         if (index >= GetNumItems())
             return;
             return;
-        
+
         if (!selections_.Contains(index))
         if (!selections_.Contains(index))
         {
         {
             selections_.Push(index);
             selections_.Push(index);
-            
+
             using namespace ItemSelected;
             using namespace ItemSelected;
-            
+
             VariantMap eventData;
             VariantMap eventData;
             eventData[P_ELEMENT] = (void*)this;
             eventData[P_ELEMENT] = (void*)this;
             eventData[P_SELECTION] = index;
             eventData[P_SELECTION] = index;
             SendEvent(E_ITEMSELECTED, eventData);
             SendEvent(E_ITEMSELECTED, eventData);
-            
+
             Sort(selections_.Begin(), selections_.End());
             Sort(selections_.Begin(), selections_.End());
         }
         }
-        
+
         EnsureItemVisibility(index);
         EnsureItemVisibility(index);
         UpdateSelectionEffect();
         UpdateSelectionEffect();
         SendEvent(E_SELECTIONCHANGED);
         SendEvent(E_SELECTIONCHANGED);
@@ -407,17 +558,17 @@ void ListView::RemoveSelection(unsigned index)
 {
 {
     if (index >= GetNumItems())
     if (index >= GetNumItems())
         return;
         return;
-    
+
     if (selections_.Remove(index))
     if (selections_.Remove(index))
     {
     {
         using namespace ItemSelected;
         using namespace ItemSelected;
-        
+
         VariantMap eventData;
         VariantMap eventData;
         eventData[P_ELEMENT] = (void*)this;
         eventData[P_ELEMENT] = (void*)this;
         eventData[P_SELECTION] = index;
         eventData[P_SELECTION] = index;
         SendEvent(E_ITEMDESELECTED, eventData);
         SendEvent(E_ITEMDESELECTED, eventData);
     }
     }
-    
+
     EnsureItemVisibility(index);
     EnsureItemVisibility(index);
     UpdateSelectionEffect();
     UpdateSelectionEffect();
     SendEvent(E_SELECTIONCHANGED);
     SendEvent(E_SELECTIONCHANGED);
@@ -428,7 +579,7 @@ void ListView::ToggleSelection(unsigned index)
     unsigned numItems = GetNumItems();
     unsigned numItems = GetNumItems();
     if (index >= numItems)
     if (index >= numItems)
         return;
         return;
-    
+
     if (selections_.Contains(index))
     if (selections_.Contains(index))
         RemoveSelection(index);
         RemoveSelection(index);
     else
     else
@@ -441,7 +592,7 @@ void ListView::ChangeSelection(int delta, bool additive)
         return;
         return;
     if (!multiselect_)
     if (!multiselect_)
         additive = false;
         additive = false;
-    
+
     // If going downwards, use the last selection as a base. Otherwise use first
     // If going downwards, use the last selection as a base. Otherwise use first
     unsigned selection = delta > 0 ? selections_.Back() : selections_.Front();
     unsigned selection = delta > 0 ? selections_.Back() : selections_.Front();
     int direction = delta > 0 ? 1 : -1;
     int direction = delta > 0 ? 1 : -1;
@@ -460,10 +611,10 @@ void ListView::ChangeSelection(int delta, bool additive)
         if (item->IsVisible())
         if (item->IsVisible())
         {
         {
             indices.Push(okSelection = newSelection);
             indices.Push(okSelection = newSelection);
-            delta -= direction;            
+            delta -= direction;
         }
         }
     }
     }
-    
+
     if (!additive)
     if (!additive)
         SetSelection(okSelection);
         SetSelection(okSelection);
     else
     else
@@ -488,7 +639,37 @@ void ListView::SetMultiselect(bool enable)
 
 
 void ListView::SetHierarchyMode(bool enable)
 void ListView::SetHierarchyMode(bool enable)
 {
 {
+    if (enable == hierarchyMode_)
+        return;
+
     hierarchyMode_ = enable;
     hierarchyMode_ = enable;
+    UIElement* container;
+    if (enable)
+    {
+        overlayContainer_ = CreateChild<UIElement>();
+        overlayContainer_->SetInternal(true);
+        overlayContainer_->SetLayoutMode(LM_FREE);
+        overlayContainer_->SetSortChildren(false);
+        overlayContainer_->SetClipChildren(true);
+
+        container = new HierarchyContainer(context_, overlayContainer_);
+    }
+    else
+    {
+        if (overlayContainer_)
+        {
+            RemoveChild(overlayContainer_);
+            overlayContainer_.Reset();
+        }
+
+        container = new UIElement(context_);
+    }
+
+    SetContentElement(container);
+    container->SetInternal(true);
+    container->SetActive(true);
+    container->SetLayout(LM_VERTICAL);
+    container->SetSortChildren(false);
 }
 }
 
 
 void ListView::SetClearSelectionOnDefocus(bool enable)
 void ListView::SetClearSelectionOnDefocus(bool enable)
@@ -496,7 +677,7 @@ void ListView::SetClearSelectionOnDefocus(bool enable)
     if (enable != clearSelectionOnDefocus_)
     if (enable != clearSelectionOnDefocus_)
     {
     {
         clearSelectionOnDefocus_ = enable;
         clearSelectionOnDefocus_ = enable;
-    
+
         if (clearSelectionOnDefocus_)
         if (clearSelectionOnDefocus_)
         {
         {
             SubscribeToEvent(this, E_DEFOCUSED, HANDLER(ListView, HandleDefocused));
             SubscribeToEvent(this, E_DEFOCUSED, HANDLER(ListView, HandleDefocused));
@@ -518,31 +699,31 @@ void ListView::Expand(unsigned index, bool enable, bool recursive)
 {
 {
     if (!hierarchyMode_)
     if (!hierarchyMode_)
         return;
         return;
-    
+
     unsigned numItems = GetNumItems();
     unsigned numItems = GetNumItems();
     if (index >= numItems)
     if (index >= numItems)
         return;
         return;
-    
+
     UIElement* item = GetItem(index++);
     UIElement* item = GetItem(index++);
     SetItemExpanded(item, enable);
     SetItemExpanded(item, enable);
-    int baseIndent = GetItemIndent(item);
-    
+    int baseIndent = item->GetIndent();
+
     PODVector<bool> expanded(baseIndent + 1);
     PODVector<bool> expanded(baseIndent + 1);
     expanded[baseIndent] = enable;
     expanded[baseIndent] = enable;
-    
+
     contentElement_->DisableLayoutUpdate();
     contentElement_->DisableLayoutUpdate();
-    
+
     while (index < numItems)
     while (index < numItems)
     {
     {
         item = GetItem(index++);
         item = GetItem(index++);
-        int indent = GetItemIndent(item);
+        int indent = item->GetIndent();
         if (indent <= baseIndent)
         if (indent <= baseIndent)
             break;
             break;
 
 
         // Propagate the state to children when it is recursive
         // Propagate the state to children when it is recursive
         if (recursive)
         if (recursive)
             SetItemExpanded(item, enable);
             SetItemExpanded(item, enable);
-        
+
         // Use the parent expanded flag to influence the visibility of its children
         // Use the parent expanded flag to influence the visibility of its children
         bool visible = enable && expanded[indent - 1];
         bool visible = enable && expanded[indent - 1];
         item->SetVisible(visible);
         item->SetVisible(visible);
@@ -551,21 +732,20 @@ void ListView::Expand(unsigned index, bool enable, bool recursive)
             expanded.Resize(indent + 1);
             expanded.Resize(indent + 1);
         expanded[indent] = visible && GetItemExpanded(item);
         expanded[indent] = visible && GetItemExpanded(item);
     }
     }
-    
+
     contentElement_->EnableLayoutUpdate();
     contentElement_->EnableLayoutUpdate();
     contentElement_->UpdateLayout();
     contentElement_->UpdateLayout();
-    UpdateViewSize();
 }
 }
 
 
 void ListView::ToggleExpand(unsigned index, bool recursive)
 void ListView::ToggleExpand(unsigned index, bool recursive)
 {
 {
     if (!hierarchyMode_)
     if (!hierarchyMode_)
         return;
         return;
-    
+
     unsigned numItems = GetNumItems();
     unsigned numItems = GetNumItems();
     if (index >= numItems)
     if (index >= numItems)
         return;
         return;
-    
+
     UIElement* item = GetItem(index);
     UIElement* item = GetItem(index);
     Expand(index, !GetItemExpanded(item), recursive);
     Expand(index, !GetItemExpanded(item), recursive);
 }
 }
@@ -587,6 +767,16 @@ PODVector<UIElement*> ListView::GetItems() const
     return items;
     return items;
 }
 }
 
 
+unsigned ListView::FindItem(UIElement* item) const
+{
+    const Vector<SharedPtr<UIElement> >& children = contentElement_->GetChildren();
+    Vector<SharedPtr<UIElement> >::ConstIterator i = children.Find(SharedPtr<UIElement>(item));
+    if (i != children.End())
+        return i - children.Begin();
+    else
+        return M_MAX_UNSIGNED;
+}
+
 unsigned ListView::GetSelection() const
 unsigned ListView::GetSelection() const
 {
 {
     if (selections_.Empty())
     if (selections_.Empty())
@@ -603,14 +793,14 @@ UIElement* ListView::GetSelectedItem() const
 PODVector<UIElement*> ListView::GetSelectedItems() const
 PODVector<UIElement*> ListView::GetSelectedItems() const
 {
 {
     PODVector<UIElement*> ret;
     PODVector<UIElement*> ret;
-    
+
     for (PODVector<unsigned>::ConstIterator i = selections_.Begin(); i != selections_.End(); ++i)
     for (PODVector<unsigned>::ConstIterator i = selections_.Begin(); i != selections_.End(); ++i)
     {
     {
         UIElement* item = GetItem(*i);
         UIElement* item = GetItem(*i);
         if (item)
         if (item)
             ret.Push(item);
             ret.Push(item);
     }
     }
-    
+
     return ret;
     return ret;
 }
 }
 
 
@@ -619,6 +809,11 @@ bool ListView::IsSelected(unsigned index) const
     return selections_.Contains(index);
     return selections_.Contains(index);
 }
 }
 
 
+bool ListView::IsExpanded(unsigned index) const
+{
+    return GetItemExpanded(contentElement_->GetChild(index));
+}
+
 float ListView::GetDoubleClickInterval() const
 float ListView::GetDoubleClickInterval() const
 {
 {
     return (float)doubleClickInterval_ / 1000.0f;
     return (float)doubleClickInterval_ / 1000.0f;
@@ -628,7 +823,7 @@ void ListView::UpdateSelectionEffect()
 {
 {
     unsigned numItems = GetNumItems();
     unsigned numItems = GetNumItems();
     bool highlighted = highlightMode_ == HM_ALWAYS || HasFocus();
     bool highlighted = highlightMode_ == HM_ALWAYS || HasFocus();
-    
+
     for (unsigned i = 0; i < numItems; ++i)
     for (unsigned i = 0; i < numItems; ++i)
     {
     {
         UIElement* item = GetItem(i);
         UIElement* item = GetItem(i);
@@ -648,18 +843,18 @@ void ListView::EnsureItemVisibility(UIElement* item)
 {
 {
     if (!item || !item->IsVisible())
     if (!item || !item->IsVisible())
         return;
         return;
-    
+
     IntVector2 newView = GetViewPosition();
     IntVector2 newView = GetViewPosition();
     IntVector2 currentOffset = item->GetPosition() - newView;
     IntVector2 currentOffset = item->GetPosition() - newView;
     const IntRect& clipBorder = scrollPanel_->GetClipBorder();
     const IntRect& clipBorder = scrollPanel_->GetClipBorder();
     IntVector2 windowSize(scrollPanel_->GetWidth() - clipBorder.left_ - clipBorder.right_, scrollPanel_->GetHeight() -
     IntVector2 windowSize(scrollPanel_->GetWidth() - clipBorder.left_ - clipBorder.right_, scrollPanel_->GetHeight() -
         clipBorder.top_ - clipBorder.bottom_);
         clipBorder.top_ - clipBorder.bottom_);
-    
+
     if (currentOffset.y_ < 0)
     if (currentOffset.y_ < 0)
         newView.y_ += currentOffset.y_;
         newView.y_ += currentOffset.y_;
     if (currentOffset.y_ + item->GetHeight() > windowSize.y_)
     if (currentOffset.y_ + item->GetHeight() > windowSize.y_)
         newView.y_ += currentOffset.y_ + item->GetHeight() - windowSize.y_;
         newView.y_ += currentOffset.y_ + item->GetHeight() - windowSize.y_;
-    
+
     SetViewPosition(newView);
     SetViewPosition(newView);
 }
 }
 
 
@@ -668,9 +863,9 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
     if (eventData[UIMouseClick::P_BUTTON].GetInt() != MOUSEB_LEFT)
     if (eventData[UIMouseClick::P_BUTTON].GetInt() != MOUSEB_LEFT)
         return;
         return;
     int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt();
     int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt();
-    
+
     UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());
     UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());
-    
+
     unsigned numItems = GetNumItems();
     unsigned numItems = GetNumItems();
     for (unsigned i = 0; i < numItems; ++i)
     for (unsigned i = 0; i < numItems; ++i)
     {
     {
@@ -684,7 +879,7 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
                     lastClickedItem_ = i;
                     lastClickedItem_ = i;
                 SetSelection(i);
                 SetSelection(i);
             }
             }
-            
+
             // Check multiselect with shift & ctrl
             // Check multiselect with shift & ctrl
             if (multiselect_)
             if (multiselect_)
             {
             {
@@ -731,7 +926,7 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
                 else if (qualifiers & QUAL_CTRL)
                 else if (qualifiers & QUAL_CTRL)
                     ToggleSelection(i);
                     ToggleSelection(i);
             }
             }
-            
+
             if (isDoubleClick)
             if (isDoubleClick)
             {
             {
                 VariantMap eventData;
                 VariantMap eventData;
@@ -739,7 +934,7 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
                 eventData[ItemDoubleClicked::P_SELECTION] = i;
                 eventData[ItemDoubleClicked::P_SELECTION] = i;
                 SendEvent(E_ITEMDOUBLECLICKED, eventData);
                 SendEvent(E_ITEMDOUBLECLICKED, eventData);
             }
             }
-            
+
             return;
             return;
         }
         }
     }
     }
@@ -748,7 +943,7 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
 void ListView::HandleFocusChanged(StringHash eventType, VariantMap& eventData)
 void ListView::HandleFocusChanged(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace FocusChanged;
     using namespace FocusChanged;
-    
+
     UIElement* element = static_cast<UIElement*>(eventData[P_ELEMENT].GetPtr());
     UIElement* element = static_cast<UIElement*>(eventData[P_ELEMENT].GetPtr());
     while (element)
     while (element)
     {
     {

+ 16 - 5
Engine/UI/ListView.h

@@ -23,7 +23,6 @@
 #pragma once
 #pragma once
 
 
 #include "ScrollView.h"
 #include "ScrollView.h"
-#include "HashSet.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {
@@ -54,11 +53,16 @@ public:
     
     
     /// React to a key press.
     /// React to a key press.
     virtual void OnKey(int key, int buttons, int qualifiers);
     virtual void OnKey(int key, int buttons, int qualifiers);
-    
+    /// React to resize.
+    virtual void OnResize();
+
     /// Add item to the end of the list.
     /// Add item to the end of the list.
     void AddItem(UIElement* item);
     void AddItem(UIElement* item);
-    /// Insert item at a specific position.
-    void InsertItem(unsigned index, UIElement* item);
+    /// \brief Insert item at a specific index. In hierarchy mode, the optional parameter will be used to determine the child's indent level in respect to its parent.
+    /// If index is greater than the total items then the new item is inserted at the end of the list.
+    /// In hierarchy mode, if index is greater than the index of last children of the specified parent item then the new item is inserted next to the last children.
+    /// And if the index is lesser than the index of the parent item itself then the new item is inserted before the first child item.
+    void InsertItem(unsigned index, UIElement* item, UIElement* parentItem = 0);
     /// Remove specific item, starting search at the specified index if provided. In hierarchy mode will also remove any children.
     /// Remove specific item, starting search at the specified index if provided. In hierarchy mode will also remove any children.
     void RemoveItem(UIElement* item, unsigned index = 0);
     void RemoveItem(UIElement* item, unsigned index = 0);
     /// Remove item at index. In hierarchy mode will also remove any children.
     /// Remove item at index. In hierarchy mode will also remove any children.
@@ -83,7 +87,8 @@ public:
     void SetHighlightMode(HighlightMode mode);
     void SetHighlightMode(HighlightMode mode);
     /// Enable multiselect.
     /// Enable multiselect.
     void SetMultiselect(bool enable);
     void SetMultiselect(bool enable);
-    /// Enable hierarchy mode. Allows hiding/showing items that have "indent" userdata.
+    /// \brief Enable hierarchy mode. Allows items to have parent-child relationship at different indent level and the ability to expand/collapse child items.
+    /// All items in the list will be lost during mode change.
     void SetHierarchyMode(bool enable);
     void SetHierarchyMode(bool enable);
     /// Enable clearing of selection on defocus.
     /// Enable clearing of selection on defocus.
     void SetClearSelectionOnDefocus(bool enable);
     void SetClearSelectionOnDefocus(bool enable);
@@ -101,6 +106,8 @@ public:
     UIElement* GetItem(unsigned index) const;
     UIElement* GetItem(unsigned index) const;
     /// Return all items.
     /// Return all items.
     PODVector<UIElement*> GetItems() const;
     PODVector<UIElement*> GetItems() const;
+    /// Return index of item, or M_MAX_UNSIGNED If not found.
+    unsigned FindItem(UIElement* item) const;
     /// Return first selected index, or M_MAX_UNSIGNED if none selected.
     /// Return first selected index, or M_MAX_UNSIGNED if none selected.
     unsigned GetSelection() const;
     unsigned GetSelection() const;
     /// Return all selected indices.
     /// Return all selected indices.
@@ -111,6 +118,8 @@ public:
     PODVector<UIElement*> GetSelectedItems() const;
     PODVector<UIElement*> GetSelectedItems() const;
     /// Return whether an item at index is seleccted.
     /// Return whether an item at index is seleccted.
     bool IsSelected(unsigned index) const;
     bool IsSelected(unsigned index) const;
+    /// Return whether an item at index has its children expanded (in hierachy mode only).
+    bool IsExpanded(unsigned index) const;
     /// Return highlight mode.
     /// Return highlight mode.
     HighlightMode GetHighlightMode() const { return highlightMode_; }
     HighlightMode GetHighlightMode() const { return highlightMode_; }
     /// Return whether multiselect enabled.
     /// Return whether multiselect enabled.
@@ -138,6 +147,8 @@ protected:
     bool multiselect_;
     bool multiselect_;
     /// Hierarchy mode flag.
     /// Hierarchy mode flag.
     bool hierarchyMode_;
     bool hierarchyMode_;
+    /// Overlay container, used in hierarchy mode only.
+    SharedPtr<UIElement> overlayContainer_;
     /// Clear selection on defocus flag.
     /// Clear selection on defocus flag.
     bool clearSelectionOnDefocus_;
     bool clearSelectionOnDefocus_;
     /// Doubleclick interval.
     /// Doubleclick interval.

+ 1 - 0
Engine/UI/ScrollView.cpp

@@ -284,6 +284,7 @@ void ScrollView::UpdatePanelSize()
     {
     {
         IntRect panelBorder = scrollPanel_->GetClipBorder();
         IntRect panelBorder = scrollPanel_->GetClipBorder();
         contentElement_->SetWidth(scrollPanel_->GetWidth() - panelBorder.left_ - panelBorder.right_);
         contentElement_->SetWidth(scrollPanel_->GetWidth() - panelBorder.left_ - panelBorder.right_);
+        UpdateViewSize();
     }
     }
     
     
     ignoreEvents_ = false;
     ignoreEvents_ = false;

+ 6 - 4
Engine/UI/Text.cpp

@@ -520,14 +520,16 @@ int Text::GetRowStartPosition(unsigned rowIndex) const
     if (rowIndex < rowWidths_.Size())
     if (rowIndex < rowWidths_.Size())
         rowWidth = rowWidths_[rowIndex];
         rowWidth = rowWidths_[rowIndex];
     
     
+    int ret = GetIndentWidth();
+
     switch (textAlignment_)
     switch (textAlignment_)
     {
     {
-    default:
-        return 0;
+    case HA_LEFT:
+        return ret;
     case HA_CENTER:
     case HA_CENTER:
-        return (GetSize().x_ - rowWidth) / 2;
+        return ret + (GetSize().x_ - rowWidth) / 2;
     case HA_RIGHT:
     case HA_RIGHT:
-        return GetSize().x_ - rowWidth;
+        return ret + GetSize().x_ - rowWidth;
     }
     }
 }
 }
 
 

+ 10 - 2
Engine/UI/UI.cpp

@@ -393,7 +393,14 @@ SharedPtr<UIElement> UI::LoadLayout(XMLFile* file, XMLFile* styleFile)
         LOGERROR("Could not create unknown UI element " + typeName);
         LOGERROR("Could not create unknown UI element " + typeName);
         return root;
         return root;
     }
     }
-    
+
+    if (styleFile)
+        // Set it as default for later use by children elements
+        root->SetDefaultStyle(styleFile);
+    else
+        // Use default style file of the root element if it has one
+        styleFile = rootElement_->GetDefaultStyle(false);
+
     root->LoadXML(rootElem, styleFile);
     root->LoadXML(rootElem, styleFile);
     return root;
     return root;
 }
 }
@@ -670,10 +677,11 @@ 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();
-    int button = eventData[MouseButtonDown::P_BUTTON].GetInt();
     
     
     if (cursor_ && cursor_->IsVisible())
     if (cursor_ && cursor_->IsVisible())
     {
     {
+        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));
         
         

+ 0 - 2
Engine/UI/UIBatch.cpp

@@ -26,8 +26,6 @@
 #include "Texture.h"
 #include "Texture.h"
 #include "UIElement.h"
 #include "UIElement.h"
 
 
-#include <cstring>
-
 #include "DebugNew.h"
 #include "DebugNew.h"
 
 
 namespace Urho3D
 namespace Urho3D

+ 64 - 11
Engine/UI/UIElement.cpp

@@ -131,6 +131,7 @@ UIElement::UIElement(Context* context) :
     resizeNestingLevel_(0),
     resizeNestingLevel_(0),
     layoutNestingLevel_(0),
     layoutNestingLevel_(0),
     layoutMinSize_(0),
     layoutMinSize_(0),
+    indent_(0),
     position_(IntVector2::ZERO),
     position_(IntVector2::ZERO),
     size_(IntVector2::ZERO),
     size_(IntVector2::ZERO),
     minSize_(IntVector2::ZERO),
     minSize_(IntVector2::ZERO),
@@ -143,7 +144,8 @@ UIElement::UIElement(Context* context) :
     opacityDirty_(true),
     opacityDirty_(true),
     derivedColorDirty_(true),
     derivedColorDirty_(true),
     sortOrderDirty_(false),
     sortOrderDirty_(false),
-    colorGradient_(false)
+    colorGradient_(false),
+    indentSpacing_(16)
 {
 {
 }
 }
 
 
@@ -188,6 +190,8 @@ void UIElement::RegisterObject(Context* context)
     ENUM_ACCESSOR_ATTRIBUTE(UIElement, "Layout Mode", GetLayoutMode, SetLayoutMode, LayoutMode, layoutModes, LM_FREE, AM_FILE);
     ENUM_ACCESSOR_ATTRIBUTE(UIElement, "Layout Mode", GetLayoutMode, SetLayoutMode, LayoutMode, layoutModes, LM_FREE, AM_FILE);
     ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Layout Spacing", GetLayoutSpacing, SetLayoutSpacing, int, 0, AM_FILE);
     ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Layout Spacing", GetLayoutSpacing, SetLayoutSpacing, int, 0, AM_FILE);
     REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTRECT, "Layout Border", GetLayoutBorder, SetLayoutBorder, IntRect, IntRect::ZERO, AM_FILE);
     REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTRECT, "Layout Border", GetLayoutBorder, SetLayoutBorder, IntRect, IntRect::ZERO, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Indent", GetIndent, SetIndent, int, 0, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Indent Spacing", GetIndentSpacing, SetIndentSpacing, int, 16, AM_FILE);
     REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTVECTOR2, "Rotation Pivot", GetRotationPivot, SetRotationPivot, IntVector2, IntVector2::ZERO, AM_FILE);
     REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTVECTOR2, "Rotation Pivot", GetRotationPivot, SetRotationPivot, IntVector2, IntVector2::ZERO, AM_FILE);
     ACCESSOR_ATTRIBUTE(UIElement, VAR_FLOAT, "Rotation", GetRotation, SetRotation, float, 0.0f, AM_FILE);
     ACCESSOR_ATTRIBUTE(UIElement, VAR_FLOAT, "Rotation", GetRotation, SetRotation, float, 0.0f, AM_FILE);
     ATTRIBUTE(UIElement, VAR_VARIANTMAP, "Variables", vars_, Variant::emptyVariantMap, AM_FILE);
     ATTRIBUTE(UIElement, VAR_VARIANTMAP, "Variables", vars_, Variant::emptyVariantMap, AM_FILE);
@@ -690,6 +694,11 @@ void UIElement::SetStyleAuto(XMLFile* file)
     SetStyle(file, GetTypeName());
     SetStyle(file, GetTypeName());
 }
 }
 
 
+void UIElement::SetDefaultStyle(XMLFile* style)
+{
+    defaultStyle_ = style;
+}
+
 void UIElement::SetLayout(LayoutMode mode, int spacing, const IntRect& border)
 void UIElement::SetLayout(LayoutMode mode, int spacing, const IntRect& border)
 {
 {
     layoutMode_ = mode;
     layoutMode_ = mode;
@@ -716,6 +725,16 @@ void UIElement::SetLayoutBorder(const IntRect& border)
     UpdateLayout();
     UpdateLayout();
 }
 }
 
 
+void UIElement::SetIndent(int indent)
+{
+    indent_ = indent;
+}
+
+void UIElement::SetIndentSpacing(int indentSpacing)
+{
+    indentSpacing_ = Max(indentSpacing, 0);
+}
+
 void UIElement::SetRotationPivot(const IntVector2& pivot)
 void UIElement::SetRotationPivot(const IntVector2& pivot)
 {
 {
     rotationPivot_ = pivot;
     rotationPivot_ = pivot;
@@ -739,6 +758,8 @@ void UIElement::UpdateLayout()
     PODVector<int> minSizes;
     PODVector<int> minSizes;
     PODVector<int> maxSizes;
     PODVector<int> maxSizes;
     
     
+    int baseIndent = GetIndentWidth();
+
     if (layoutMode_ == LM_HORIZONTAL)
     if (layoutMode_ == LM_HORIZONTAL)
     {
     {
         int minChildHeight = 0;
         int minChildHeight = 0;
@@ -747,15 +768,15 @@ void UIElement::UpdateLayout()
         {
         {
             if (!children_[i]->IsVisible())
             if (!children_[i]->IsVisible())
                 continue;
                 continue;
-            positions.Push(0);
-            sizes.Push(children_[i]->GetWidth());
-            minSizes.Push(children_[i]->GetMinWidth());
-            maxSizes.Push(children_[i]->GetMaxWidth());
+            positions.Push(baseIndent);
+            unsigned indent = children_[i]->GetIndentWidth();
+            sizes.Push(children_[i]->GetWidth() + indent);
+            minSizes.Push(children_[i]->GetMinWidth() + indent);
+            maxSizes.Push(children_[i]->GetMaxWidth() + indent);
             minChildHeight = Max(minChildHeight, children_[i]->GetMinHeight());
             minChildHeight = Max(minChildHeight, children_[i]->GetMinHeight());
         }
         }
         
         
-        CalculateLayout(positions, sizes, minSizes, maxSizes, GetWidth(), layoutBorder_.left_, layoutBorder_.right_,
-            layoutSpacing_);
+        CalculateLayout(positions, sizes, minSizes, maxSizes, GetWidth(), layoutBorder_.left_, layoutBorder_.right_, layoutSpacing_);
         
         
         int width = CalculateLayoutParentSize(sizes, layoutBorder_.left_, layoutBorder_.right_, layoutSpacing_);
         int width = CalculateLayoutParentSize(sizes, layoutBorder_.left_, layoutBorder_.right_, layoutSpacing_);
         int height = Max(GetHeight(), minChildHeight + layoutBorder_.top_ + layoutBorder_.bottom_);
         int height = Max(GetHeight(), minChildHeight + layoutBorder_.top_ + layoutBorder_.bottom_);
@@ -790,15 +811,14 @@ void UIElement::UpdateLayout()
         {
         {
             if (!children_[i]->IsVisible())
             if (!children_[i]->IsVisible())
                 continue;
                 continue;
-            positions.Push(0);
+            positions.Push(baseIndent);
             sizes.Push(children_[i]->GetHeight());
             sizes.Push(children_[i]->GetHeight());
             minSizes.Push(children_[i]->GetMinHeight());
             minSizes.Push(children_[i]->GetMinHeight());
             maxSizes.Push(children_[i]->GetMaxHeight());
             maxSizes.Push(children_[i]->GetMaxHeight());
-            minChildWidth = Max(minChildWidth, children_[i]->GetMinWidth());
+            minChildWidth = Max(minChildWidth, children_[i]->GetMinWidth() + children_[i]->GetIndentWidth());
         }
         }
         
         
-        CalculateLayout(positions, sizes, minSizes, maxSizes, GetHeight(), layoutBorder_.top_, layoutBorder_.bottom_,
-            layoutSpacing_);
+        CalculateLayout(positions, sizes, minSizes, maxSizes, GetHeight(), layoutBorder_.top_, layoutBorder_.bottom_, layoutSpacing_);
         
         
         int height = CalculateLayoutParentSize(sizes, layoutBorder_.top_, layoutBorder_.bottom_, layoutSpacing_);
         int height = CalculateLayoutParentSize(sizes, layoutBorder_.top_, layoutBorder_.bottom_, layoutSpacing_);
         int width = Max(GetWidth(), minChildWidth + layoutBorder_.left_ + layoutBorder_.right_);
         int width = Max(GetWidth(), minChildWidth + layoutBorder_.left_ + layoutBorder_.right_);
@@ -824,6 +844,12 @@ void UIElement::UpdateLayout()
         }
         }
     }
     }
     
     
+    using namespace LayoutUpdated;
+
+    VariantMap eventData;
+    eventData[P_ELEMENT] = (void*)this;
+    SendEvent(E_LAYOUTUPDATED, eventData);
+
     EnableLayoutUpdate();
     EnableLayoutUpdate();
 }
 }
 
 
@@ -953,6 +979,16 @@ void UIElement::RemoveChild(UIElement* element, unsigned index)
     }
     }
 }
 }
 
 
+void UIElement::RemoveChildAtIndex(unsigned index)
+{
+    if (index >= children_.Size())
+        return;
+
+    children_[index]->Detach();
+    children_.Erase(index);
+    UpdateLayout();
+}
+
 void UIElement::RemoveAllChildren()
 void UIElement::RemoveAllChildren()
 {
 {
     for (Vector<SharedPtr<UIElement> >::Iterator i = children_.Begin(); i < children_.End(); )
     for (Vector<SharedPtr<UIElement> >::Iterator i = children_.Begin(); i < children_.End(); )
@@ -1065,6 +1101,23 @@ bool UIElement::HasFocus() const
         return ui->GetFocusElement() == this;
         return ui->GetFocusElement() == this;
 }
 }
 
 
+XMLFile* UIElement::GetDefaultStyle(bool recursiveUp) const
+{
+    if (recursiveUp)
+    {
+        const UIElement* element = this;
+        while (element)
+        {
+            if (element->defaultStyle_)
+                return element->defaultStyle_;
+            element = element->parent_;
+        }
+        return 0;
+    }
+    else
+        return defaultStyle_;
+}
+
 void UIElement::GetChildren(PODVector<UIElement*>& dest, bool recursive) const
 void UIElement::GetChildren(PODVector<UIElement*>& dest, bool recursive) const
 {
 {
     dest.Clear();
     dest.Clear();

+ 24 - 2
Engine/UI/UIElement.h

@@ -235,6 +235,8 @@ public:
     void SetStyle(const XMLElement& element);
     void SetStyle(const XMLElement& element);
     /// Set style from an XML file. Find the style element automatically.
     /// Set style from an XML file. Find the style element automatically.
     void SetStyleAuto(XMLFile* file);
     void SetStyleAuto(XMLFile* file);
+    /// Set default style for later use by children elements.
+    void SetDefaultStyle(XMLFile* style);
     /// Set layout.
     /// Set layout.
     void SetLayout(LayoutMode mode, int spacing = 0, const IntRect& border = IntRect::ZERO);
     void SetLayout(LayoutMode mode, int spacing = 0, const IntRect& border = IntRect::ZERO);
     /// Set layout mode only.
     /// Set layout mode only.
@@ -243,6 +245,10 @@ public:
     void SetLayoutSpacing(int spacing);
     void SetLayoutSpacing(int spacing);
     /// Set layout border.
     /// Set layout border.
     void SetLayoutBorder(const IntRect& border);
     void SetLayoutBorder(const IntRect& border);
+    /// Set horizontal indentation.
+    void SetIndent(int indent);
+    /// Set indent spacing (number of pixels per indentation level).
+    void SetIndentSpacing(int indentSpacing);
     /// Set content rotation pivot offset.
     /// Set content rotation pivot offset.
     void SetRotationPivot(const IntVector2& pivot);
     void SetRotationPivot(const IntVector2& pivot);
     /// Set content rotation angle in degrees. Positive is clockwise. Note: child elements do not rotate.
     /// Set content rotation angle in degrees. Positive is clockwise. Note: child elements do not rotate.
@@ -263,6 +269,8 @@ public:
     void InsertChild(unsigned index, UIElement* element);
     void InsertChild(unsigned index, UIElement* element);
     /// Remove a child element. Starting search at specified index if provided.
     /// Remove a child element. Starting search at specified index if provided.
     void RemoveChild(UIElement* element, unsigned index = 0);
     void RemoveChild(UIElement* element, unsigned index = 0);
+    /// Remove a child element at index.
+    void RemoveChildAtIndex(unsigned index);
     /// Remove all child elements.
     /// Remove all child elements.
     void RemoveAllChildren();
     void RemoveAllChildren();
     /// Remove from the parent element. If no other shared pointer references exist, causes immediate deletion.
     /// Remove from the parent element. If no other shared pointer references exist, causes immediate deletion.
@@ -344,6 +352,8 @@ public:
     FocusMode GetFocusMode() const { return focusMode_; }
     FocusMode GetFocusMode() const { return focusMode_; }
     /// Return drag and drop flags.
     /// Return drag and drop flags.
     unsigned GetDragDropMode() const { return dragDropMode_; }
     unsigned GetDragDropMode() const { return dragDropMode_; }
+    /// Return default style.
+    XMLFile* GetDefaultStyle(bool recursiveUp = true) const;
     /// Return layout mode.
     /// Return layout mode.
     LayoutMode GetLayoutMode() const { return layoutMode_; }
     LayoutMode GetLayoutMode() const { return layoutMode_; }
     /// Return layout spacing.
     /// Return layout spacing.
@@ -389,6 +399,12 @@ public:
     void SortChildren();
     void SortChildren();
     /// Return minimum layout element size in the layout direction. Only valid after layout has been calculated.
     /// Return minimum layout element size in the layout direction. Only valid after layout has been calculated.
     int GetLayoutMinSize() const { return layoutMinSize_; }
     int GetLayoutMinSize() const { return layoutMinSize_; }
+    /// Return horizontal indentation.
+    int GetIndent() const { return indent_; }
+    /// Return indent spacing (number of pixels per indentation level).
+    int GetIndentSpacing() const { return indentSpacing_; }
+    /// Return indent width in pixels.
+    int GetIndentWidth() const { return indent_ * indentSpacing_; }
     
     
     /// Set child offset.
     /// Set child offset.
     void SetChildOffset(const IntVector2& offset);
     void SetChildOffset(const IntVector2& offset);
@@ -405,7 +421,7 @@ public:
         currentScissor);
         currentScissor);
     /// Get color attribute. Uses just the top-left color.
     /// Get color attribute. Uses just the top-left color.
     const Color& GetColorAttr() const { return color_[0]; }
     const Color& GetColorAttr() const { return color_[0]; }
-    
+
 protected:
 protected:
     /// Mark screen position as needing an update.
     /// Mark screen position as needing an update.
     void MarkDirty();
     void MarkDirty();
@@ -464,7 +480,9 @@ protected:
     unsigned layoutNestingLevel_;
     unsigned layoutNestingLevel_;
     /// Layout element minimum size in layout direction.
     /// Layout element minimum size in layout direction.
     int layoutMinSize_;
     int layoutMinSize_;
-    
+    /// Horizontal indentation.
+    int indent_;
+
 private:
 private:
     /// Return child elements recursively.
     /// Return child elements recursively.
     void GetChildrenRecursive(PODVector<UIElement*>& dest) const;
     void GetChildrenRecursive(PODVector<UIElement*>& dest) const;
@@ -509,6 +527,10 @@ private:
     bool sortOrderDirty_;
     bool sortOrderDirty_;
     /// Has color gradient flag.
     /// Has color gradient flag.
     bool colorGradient_;
     bool colorGradient_;
+    /// Indent spacing (number of pixels per indentation level).
+    int indentSpacing_;
+    /// Default style file.
+    SharedPtr<XMLFile> defaultStyle_;
 };
 };
 
 
 template <class T> T* UIElement::CreateChild(const String& name) { return static_cast<T*>(CreateChild(T::GetTypeStatic(), name)); }
 template <class T> T* UIElement::CreateChild(const String& name) { return static_cast<T*>(CreateChild(T::GetTypeStatic(), name)); }

+ 6 - 0
Engine/UI/UIEvents.h

@@ -88,6 +88,12 @@ EVENT(E_DEFOCUSED, Defocused)
     PARAM(P_ELEMENT, Element);              // UIElement pointer
     PARAM(P_ELEMENT, Element);              // UIElement pointer
 }
 }
 
 
+/// UI element layout updated.
+EVENT(E_LAYOUTUPDATED, LayoutUpdated)
+{
+    PARAM(P_ELEMENT, Element);              // UIElement pointer
+}
+
 /// UI button pressed.
 /// UI button pressed.
 EVENT(E_PRESSED, Pressed)
 EVENT(E_PRESSED, Pressed)
 {
 {