浏览代码

Scene node and UI element reordering (within the same parent) by Ctrl-dragging. Closes #1088. Expose Node::SetID() in script, as it was necessary for this, though not recommended to be called.

Lasse Öörni 9 年之前
父节点
当前提交
b3e492e18c

+ 3 - 1
Docs/GettingStarted.dox

@@ -878,7 +878,9 @@ New scene nodes and components are created from the Create menu at the top. Thei
 
 As an alternative to using the transform gizmo, scene nodes can be moved/rotated/scaled by Ctrl + arrow keys and Page Up / Page Down. Press Ctrl+1,2,3 to change the manipulation mode, and Ctrl+4 to toggle between world relative and scene node relative movement.
 
-To reparent scene nodes, drag and drop them onto the new parent scene node in the scene hierarchy window. Reparenting should retain the effective world transform, so check afterwards from the component window that the local transform is what you expect it to be. Components can not be dragged between nodes, but can be duplicated with cut/copy/paste operations.
+To reparent scene nodes or UI elements, drag and drop them onto the new parent in the scene hierarchy window. Reparenting should retain the effective world transform, so check afterwards from the component window that the local transform is what you expect it to be. Components can not be dragged between nodes, but can be duplicated with cut/copy/paste operations.
+
+To reorder nodes or UI elements within the same parent instead of reparenting, hold down Ctrl while dragging and dropping.
 
 To locate a scene node from the scene, double-click it in the hierarchy window.
 

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

@@ -762,6 +762,7 @@ template <class T> void RegisterNode(asIScriptEngine* engine, const char* classN
     engine->RegisterObjectMethod(className, "Vector2 get_worldScale2D()", asMETHOD(T, GetWorldScale2D), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Matrix3x4 get_transform() const", asMETHOD(T, GetTransform), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Matrix3x4& get_worldTransform() const", asMETHOD(T, GetWorldTransform), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "void set_id(uint)", asMETHOD(T, SetID), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint get_id()", asMETHOD(T, GetID), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint get_numChildren() const", asFUNCTION(NodeGetNumChildrenNonRecursive), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "uint get_numAllChildren() const", asFUNCTION(NodeGetNumChildrenRecursive), asCALL_CDECL_OBJLAST);

+ 2 - 0
Source/Urho3D/LuaScript/pkgs/Scene/Node.pkg

@@ -202,6 +202,8 @@ class Node : public Animatable
     // void GetChildrenWithTag(PODVector<Node*>& dest, const String& tag, bool recursive = false) const;
     tolua_outside const PODVector<Node*>& NodeGetChildrenWithTag @ GetChildrenWithTag(const String& tag, bool recursive = false) const; 
 
+    void SetID(unsigned id);
+    
     tolua_readonly tolua_property__get_set unsigned ID;
     tolua_property__get_set String name;
     tolua_readonly tolua_property__get_set StringHash nameHash;

+ 82 - 0
bin/Data/Scripts/Editor/EditorActions.as

@@ -268,6 +268,46 @@ class ReparentNodeAction : EditAction
     }
 }
 
+class ReorderNodeAction : EditAction
+{
+    uint nodeID;
+    uint parentID;
+    uint oldChildIndex;
+    uint newChildIndex;
+
+    void Define(Node@ node, uint newIndex)
+    {
+        nodeID = node.id;
+        parentID = node.parent.id;
+        oldChildIndex = SceneFindChild(node.parent, node);
+        newChildIndex = newIndex;
+    }
+
+    void Undo()
+    {
+        Node@ parent = editorScene.GetNode(parentID);
+        Node@ node = editorScene.GetNode(nodeID);
+        if (parent !is null && node !is null)
+        {
+            PerformReorder(parent, node, oldChildIndex);
+            UpdateHierarchyItem(parent); // Force update to make sure the order is current
+            SetSceneModified();
+        }
+    }
+
+    void Redo()
+    {
+        Node@ parent = editorScene.GetNode(parentID);
+        Node@ node = editorScene.GetNode(nodeID);
+        if (parent !is null && node !is null)
+        {
+            PerformReorder(parent, node, newChildIndex);
+            UpdateHierarchyItem(parent); // Force update to make sure the order is current
+            SetSceneModified();
+        }
+    }
+}
+
 class CreateComponentAction : EditAction
 {
     uint nodeID;
@@ -776,6 +816,48 @@ class ReparentUIElementAction : EditAction
     }
 }
 
+class ReorderUIElementAction : EditAction
+{
+    Variant elementID;
+    Variant parentID;
+    uint oldChildIndex;
+    uint newChildIndex;
+
+    void Define(UIElement@ element, uint newIndex)
+    {
+        elementID = GetUIElementID(element);
+        parentID = GetUIElementID(element.parent);
+        oldChildIndex = element.parent.FindChild(element);
+        newChildIndex = newIndex;
+    }
+
+    void Undo()
+    {
+        UIElement@ parent = GetUIElementByID(parentID);
+        UIElement@ element = GetUIElementByID(elementID);
+        if (parent !is null && element !is null)
+        {
+            parent.RemoveChild(element);
+            parent.InsertChild(oldChildIndex, element);
+            UpdateHierarchyItem(parent); // Force update to make sure the order is current
+            SetUIElementModified(parent);
+        }
+    }
+
+    void Redo()
+    {
+        UIElement@ parent = GetUIElementByID(parentID);
+        UIElement@ element = GetUIElementByID(elementID);
+        if (parent !is null && element !is null)
+        {
+            parent.RemoveChild(element);
+            parent.InsertChild(newChildIndex, element);
+            UpdateHierarchyItem(parent); // Force update to make sure the order is current
+            SetUIElementModified(parent);
+        }
+    }
+}
+
 class ApplyUIElementStyleAction : EditAction
 {
     Variant elementID;

+ 51 - 14
bin/Data/Scripts/Editor/EditorHierarchyWindow.as

@@ -1013,10 +1013,18 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
         Array<Node@> sourceNodes = GetMultipleSourceNodes(source);
         if (sourceNodes.length > 0)
         {
-            if (sourceNodes.length > 1)
-                SceneChangeParent(sourceNodes[0], sourceNodes, targetNode);
+            if (!input.qualifierDown[QUAL_CTRL])
+            {
+                if (sourceNodes.length > 1)
+                    SceneChangeParent(sourceNodes[0], sourceNodes, targetNode);
+                else
+                    SceneChangeParent(sourceNodes[0], targetNode);
+            }
             else
-                SceneChangeParent(sourceNodes[0], targetNode);
+            {
+                if (sourceNodes.length == 1)
+                    SceneReorder(sourceNodes[0], targetNode);
+            }
 
             // Focus the node at its new position in the list which in turn should trigger a refresh in attribute inspector
             FocusNode(sourceNodes[0]);
@@ -1031,9 +1039,16 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
         if (targetElement is null)
             return;
 
-        // Perform the reparenting
-        if (!UIElementChangeParent(sourceElement, targetElement))
-            return;
+        if (!input.qualifierDown[QUAL_CTRL])
+        {
+            if (!UIElementChangeParent(sourceElement, targetElement))
+                return;
+        }
+        else
+        {
+            if (!UIElementReorder(sourceElement, targetElement))
+                return;
+        }
 
         // Focus the element at its new position in the list which in turn should trigger a refresh in attribute inspector
         FocusUIElement(sourceElement);
@@ -1164,10 +1179,21 @@ bool TestDragDrop(UIElement@ source, UIElement@ target, int& itemType)
         {
             itemType = ITEM_NODE;
 
-            if (sourceNode.parent is targetNode)
-                return false;
-            if (targetNode.parent is sourceNode)
-                return false;
+            // No ctrl: Reparent
+            if (!input.qualifierDown[QUAL_CTRL])
+            {
+                if (sourceNode.parent is targetNode)
+                    return false;
+                if (targetNode.parent is sourceNode)
+                    return false;
+            }
+            // Ctrl pressed: reorder
+            else
+            {
+                // Must be within the same parent
+                if (sourceNode.parent is null || sourceNode.parent !is targetNode.parent)
+                    return false;
+            }
         }
 
         // Resource browser
@@ -1199,10 +1225,21 @@ bool TestDragDrop(UIElement@ source, UIElement@ target, int& itemType)
         {
             itemType = ITEM_UI_ELEMENT;
 
-            if (sourceElement.parent is targetElement)
-                return false;
-            if (targetElement.parent is sourceElement)
-                return false;
+            // No ctrl: Reparent
+            if (!input.qualifierDown[QUAL_CTRL])
+            {
+                if (sourceElement.parent is targetElement)
+                    return false;
+                if (targetElement.parent is sourceElement)
+                    return false;
+            }
+            // Ctrl pressed: reorder
+            else
+            {
+                // Must be within the same parent
+                if (sourceElement.parent is null || sourceElement.parent !is targetElement.parent)
+                    return false;
+            }
         }
 
         return true;

+ 40 - 0
bin/Data/Scripts/Editor/EditorScene.as

@@ -1062,6 +1062,46 @@ bool SceneChangeParent(Node@ sourceNode, Array<Node@> sourceNodes, Node@ targetN
         return false;
 }
 
+bool SceneReorder(Node@ sourceNode, Node@ targetNode)
+{
+    if (sourceNode is null || targetNode is null || sourceNode.parent is null || sourceNode.parent !is targetNode.parent)
+        return false;
+    if (sourceNode is targetNode)
+        return true; // No-op
+
+    Node@ parent = sourceNode.parent;
+    uint destIndex = SceneFindChild(parent, targetNode);
+
+    ReorderNodeAction action;
+    action.Define(sourceNode, destIndex);
+    SaveEditAction(action);
+
+    PerformReorder(parent, sourceNode, destIndex);
+    UpdateHierarchyItem(parent); // Force update to make sure the order is current
+    SetSceneModified();
+    return true;
+}
+
+void PerformReorder(Node@ parent, Node@ child, uint destIndex)
+{
+    // Removal from scene zeroes the ID. Be prepared to restore it
+    uint oldId = child.id;
+    parent.RemoveChild(child);
+    child.id = oldId;
+    parent.AddChild(child, destIndex);
+}
+
+uint SceneFindChild(Node@ parent, Node@ child)
+{
+    for (uint i = 0; i < parent.numChildren; ++i)
+    {
+        if (parent.children[i] is child)
+            return i;
+    }
+    
+    return -1;
+}
+
 bool SceneResetPosition()
 {
     if (editNode !is null)

+ 21 - 0
bin/Data/Scripts/Editor/EditorUIElement.as

@@ -691,3 +691,24 @@ bool UIElementChangeParent(UIElement@ sourceElement, UIElement@ targetElement)
     SetUIElementModified(targetElement);
     return sourceElement.parent is targetElement;
 }
+
+bool UIElementReorder(UIElement@ sourceElement, UIElement@ targetElement)
+{
+    if (sourceElement is null || targetElement is null || sourceElement.parent is null || sourceElement.parent !is targetElement.parent)
+        return false;
+    if (sourceElement is targetElement)
+        return true; // No-op
+    UIElement@ parent = sourceElement.parent;
+    uint destIndex = parent.FindChild(targetElement);
+    Print("Reorder to dest index " + destIndex);
+
+    ReorderUIElementAction action;
+    action.Define(sourceElement, destIndex);
+    SaveEditAction(action);
+
+    parent.RemoveChild(sourceElement);
+    parent.InsertChild(destIndex, sourceElement);
+    UpdateHierarchyItem(parent); // Force update to make sure the order is current
+    SetUIElementModified(parent);
+    return true;
+}