Jelajahi Sumber

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 tahun lalu
induk
melakukan
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.
 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.
 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, "Vector2 get_worldScale2D()", asMETHOD(T, GetWorldScale2D), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Matrix3x4 get_transform() const", asMETHOD(T, GetTransform), 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, "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_id()", asMETHOD(T, GetID), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "uint get_numChildren() const", asFUNCTION(NodeGetNumChildrenNonRecursive), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "uint get_numChildren() const", asFUNCTION(NodeGetNumChildrenNonRecursive), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "uint get_numAllChildren() const", asFUNCTION(NodeGetNumChildrenRecursive), 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;
     // 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; 
     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_readonly tolua_property__get_set unsigned ID;
     tolua_property__get_set String name;
     tolua_property__get_set String name;
     tolua_readonly tolua_property__get_set StringHash nameHash;
     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
 class CreateComponentAction : EditAction
 {
 {
     uint nodeID;
     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
 class ApplyUIElementStyleAction : EditAction
 {
 {
     Variant elementID;
     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);
         Array<Node@> sourceNodes = GetMultipleSourceNodes(source);
         if (sourceNodes.length > 0)
         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
             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
             // Focus the node at its new position in the list which in turn should trigger a refresh in attribute inspector
             FocusNode(sourceNodes[0]);
             FocusNode(sourceNodes[0]);
@@ -1031,9 +1039,16 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
         if (targetElement is null)
         if (targetElement is null)
             return;
             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
         // Focus the element at its new position in the list which in turn should trigger a refresh in attribute inspector
         FocusUIElement(sourceElement);
         FocusUIElement(sourceElement);
@@ -1164,10 +1179,21 @@ bool TestDragDrop(UIElement@ source, UIElement@ target, int& itemType)
         {
         {
             itemType = ITEM_NODE;
             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
         // Resource browser
@@ -1199,10 +1225,21 @@ bool TestDragDrop(UIElement@ source, UIElement@ target, int& itemType)
         {
         {
             itemType = ITEM_UI_ELEMENT;
             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;
         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;
         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()
 bool SceneResetPosition()
 {
 {
     if (editNode !is null)
     if (editNode !is null)

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

@@ -691,3 +691,24 @@ bool UIElementChangeParent(UIElement@ sourceElement, UIElement@ targetElement)
     SetUIElementModified(targetElement);
     SetUIElementModified(targetElement);
     return sourceElement.parent is 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;
+}