浏览代码

Support a "select on click end" mode in ListView. Support defining a separate drag'n'drop content for elements dropped into a LineEdit. Enable drag'n'drop to node ID attribute fields. Closes #261.

Lasse Öörni 11 年之前
父节点
当前提交
b8185f973c

+ 5 - 2
Bin/Data/Scripts/Editor/AttributeEditor.as

@@ -195,6 +195,9 @@ UIElement@ CreateIntAttributeEditor(ListView@ list, Array<Serializable@>@ serial
         LineEdit@ attrEdit = CreateAttributeLineEdit(parent, serializables, index, subIndex);
         SubscribeToEvent(attrEdit, "TextChanged", "EditAttribute");
         SubscribeToEvent(attrEdit, "TextFinished", "EditAttribute");
+        // If the attribute is a node ID, make it a drag/drop target
+        if (info.name.Contains("NodeID", false) || info.name.Contains("Node ID", false) || (info.mode & AM_NODEID) != 0)
+            attrEdit.dragDropMode = DD_TARGET;
     }
     else
     {
@@ -1221,10 +1224,10 @@ void InitVectorStructs()
         "   NodeID"
     };
     vectorStructs.Push(VectorStruct("StaticModelGroup", "Instance Nodes", staticModelGroupInstanceVariables, 1));
-    
+
     Array<String> splinePathInstanceVariables = {
         "Control Point Count",
-        "   Node ID"
+        "   NodeID"
     };
     vectorStructs.Push(VectorStruct("SplinePath", "Control Points", splinePathInstanceVariables, 1));
 }

+ 45 - 22
Bin/Data/Scripts/Editor/EditorHierarchyWindow.as

@@ -15,6 +15,7 @@ const ShortStringHash TYPE_VAR("Type");
 const ShortStringHash NODE_ID_VAR("NodeID");
 const ShortStringHash COMPONENT_ID_VAR("ComponentID");
 const ShortStringHash UI_ELEMENT_ID_VAR("UIElementID");
+const ShortStringHash DRAGDROPCONTENT_VAR("DragDropContent");
 const ShortStringHash[] ID_VARS = { ShortStringHash(""), NODE_ID_VAR, COMPONENT_ID_VAR, UI_ELEMENT_ID_VAR };
 Color nodeTextColor(1.0f, 1.0f, 1.0f);
 Color componentTextColor(0.7f, 1.0f, 0.7f);
@@ -64,6 +65,9 @@ void CreateHierarchyWindow()
 
     UpdateHierarchyItem(editorScene);
 
+    // Set selection to happen on click end, so that we can drag nodes to the inspector without resetting the inspector view
+    hierarchyList.selectOnClickEnd = true;
+
     // 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;
@@ -288,7 +292,8 @@ void AddComponentItem(uint compItemIndex, Component@ component, UIElement@ paren
     text.vars[COMPONENT_ID_VAR] = component.id;
     text.text = GetComponentTitle(component);
     text.color = componentTextColor;
-    text.dragDropMode = DD_SOURCE_AND_TARGET;
+    // Components currently act only as drag targets
+    text.dragDropMode = DD_TARGET;
 
     IconizeUIElement(text, component.typeName);
     SetIconEnabledColor(text, component.enabledEffective);
@@ -314,6 +319,10 @@ void SetID(Text@ text, Serializable@ serializable, int itemType = ITEM_NONE)
 
     text.vars[TYPE_VAR] = itemType;
     text.vars[ID_VARS[itemType]] = GetID(serializable, itemType);
+    
+    // Set node ID as drag and drop content for node ID editing
+    if (itemType == ITEM_NODE)
+        text.vars[DRAGDROPCONTENT_VAR] = String(text.vars[NODE_ID_VAR].GetUInt());
 
     switch (itemType)
     {
@@ -805,7 +814,7 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
 
                 for (uint i = 0; i < sourceNodes.length; ++i)
                     smg.AddInstanceNode(sourceNodes[i]);
-                    
+
                 action.Define(smg, attrIndex, oldIDs);
                 SaveEditAction(action);
                 SetSceneModified();
@@ -822,12 +831,12 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
 
                 for (uint i = 0; i < sourceNodes.length; ++i)
                     spline.AddControlPoint(sourceNodes[i]);
-                    
+
                 action.Define(spline, attrIndex, oldIDs);
                 SaveEditAction(action);
                 SetSceneModified();
             }
-            
+
             // Drag a node to Constraint to make it the remote end of the constraint
             Constraint@ constraint = cast<Constraint>(targetComponent);
             RigidBody@ rigidBody = sourceNodes[0].GetComponent("RigidBody");
@@ -839,7 +848,7 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
                 Variant oldID = constraint.attributes[attrIndex];
 
                 constraint.otherBody = rigidBody;
-                
+
                 action.Define(constraint, attrIndex, oldID);
                 SaveEditAction(action);
                 SetSceneModified();
@@ -851,31 +860,45 @@ void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
 Array<Node@> GetMultipleSourceNodes(UIElement@ source)
 {
     Array<Node@> nodeList;
+    
+    Node@ node = editorScene.GetNode(source.vars[NODE_ID_VAR].GetUInt());
+    if (node !is null)
+        nodeList.Push(node);
 
-    // Handle multiple selected children from a ListView
+    // Handle additional selected children from a ListView
     if (source.parent !is null && source.parent.typeName == "HierarchyContainer")
     {
         ListView@ listView_ = cast<ListView>(source.parent.parent.parent);
-
         if (listView_ is null)
             return nodeList;
-
+        
+        bool sourceIsSelected = false;
         for (uint i = 0; i < listView_.selectedItems.length; i++)
         {
-            UIElement@ item_ = listView_.selectedItems[i];
-            if (item_.vars[TYPE_VAR] == ITEM_NODE)
+            if (listView_.selectedItems[i] is source)
             {
-                Node@ node = editorScene.GetNode(item_.vars[NODE_ID_VAR].GetUInt());
-                if (node !is null)
-                    nodeList.Push(node);
+                sourceIsSelected = true;
+                break;
+            }
+        }
+
+        if (sourceIsSelected)
+        {
+            for (uint i = 0; i < listView_.selectedItems.length; i++)
+            {
+                UIElement@ item_ = listView_.selectedItems[i];
+                // The source item is already added
+                if (item_ is source)
+                    continue;
+
+                if (item_.vars[TYPE_VAR] == ITEM_NODE)
+                {
+                    Node@ node = editorScene.GetNode(item_.vars[NODE_ID_VAR].GetUInt());
+                    if (node !is null)
+                        nodeList.Push(node);
+                }
             }
         }
-    }
-    else
-    {
-        Node@ node = editorScene.GetNode(source.vars[NODE_ID_VAR].GetUInt());
-        if (node !is null)
-            nodeList.Push(node);
     }
 
     return nodeList;
@@ -895,7 +918,7 @@ bool TestDragDrop(UIElement@ source, UIElement@ target, int& itemType)
         variant = target.GetVar(NODE_ID_VAR);
         if (!variant.empty)
             targetNode = editorScene.GetNode(variant.GetUInt());
-    
+
         if (sourceNode !is null && targetNode !is null)
         {
             itemType = ITEM_NODE;
@@ -918,7 +941,7 @@ bool TestDragDrop(UIElement@ source, UIElement@ target, int& itemType)
         variant = target.GetVar(UI_ELEMENT_ID_VAR);
         if (!variant.empty)
             targetElement = GetUIElementByID(variant.GetUInt());
-  
+
         if (sourceElement !is null && targetElement !is null)
         {
             itemType = ITEM_UI_ELEMENT;
@@ -933,7 +956,7 @@ bool TestDragDrop(UIElement@ source, UIElement@ target, int& itemType)
     }
     else if (targetItemType == ITEM_COMPONENT)
     {
-        // Now only support dragging of nodes to StaticModelGroup or Constraint. Can be expanded to support others
+        // Now only support dragging of nodes to StaticModelGroup, SplinePath or Constraint. Can be expanded to support others
         Node@ sourceNode;
         Component@ targetComponent;
         Variant variant = source.GetVar(NODE_ID_VAR);

+ 8 - 5
Source/Engine/LuaScript/pkgs/UI/ListView.pkg

@@ -32,30 +32,32 @@ class ListView : public ScrollView
     void SetHierarchyMode(bool enable);
     void SetBaseIndent(int baseIndent);
     void SetClearSelectionOnDefocus(bool enable);
+    void SetSelectOnClickEnd(bool enable);
 
     void Expand(unsigned index, bool enable, bool recursive = false);
     void ToggleExpand(unsigned index, bool recursive = false);
-    
+
     unsigned GetNumItems() const;
     UIElement* GetItem(unsigned index) const;
-    
+
     // PODVector<UIElement*> GetItems() const;
     tolua_outside const PODVector<UIElement*>& ListViewGetItems @ GetItems() const;
-    
+
     unsigned FindItem(UIElement* item) const;
     unsigned GetSelection() const;
     const PODVector<unsigned>& GetSelections() const;
     void CopySelectedItemsToClipboard() const;
     UIElement* GetSelectedItem() const;
-    
+
     // PODVector<UIElement*> GetSelectedItems() const;
     tolua_outside const PODVector<UIElement*>& ListViewGetSelectedItems @ GetSelectedItems() const;
-    
+
     bool IsSelected(unsigned index) const;
     bool IsExpanded(unsigned index) const;
     HighlightMode GetHighlightMode() const;
     bool GetMultiselect() const;
     bool GetClearSelectionOnDefocus() const;
+    bool GetSelectOnClickEnd() const;
     bool GetHierarchyMode() const;
     int GetBaseIndent() const;
 
@@ -65,6 +67,7 @@ class ListView : public ScrollView
     tolua_property__get_set HighlightMode highlightMode;
     tolua_property__get_set bool multiselect;
     tolua_property__get_set bool clearSelectionOnDefocus;
+    tolua_property__get_set bool selectOnClickEnd;
     tolua_property__get_set bool hierarchyMode;
     tolua_property__get_set int baseIndent;
 };

+ 2 - 0
Source/Engine/Script/UIAPI.cpp

@@ -313,6 +313,8 @@ static void RegisterListView(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ListView", "int get_baseIndent() const", asMETHOD(ListView, GetBaseIndent), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "void set_clearSelectionOnDefocus(bool)", asMETHOD(ListView, SetClearSelectionOnDefocus), asCALL_THISCALL);
     engine->RegisterObjectMethod("ListView", "bool get_clearSelectionOnDefocus() const", asMETHOD(ListView, GetClearSelectionOnDefocus), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ListView", "void set_selectOnClickEnd(bool)", asMETHOD(ListView, SetSelectOnClickEnd), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ListView", "bool get_selectOnClickEnd() const", asMETHOD(ListView, GetSelectOnClickEnd), asCALL_THISCALL);
 }
 
 static void RegisterText(asIScriptEngine* engine)

+ 9 - 0
Source/Engine/UI/LineEdit.cpp

@@ -33,6 +33,8 @@
 namespace Urho3D
 {
 
+ShortStringHash VAR_DRAGDROPCONTENT("DragDropContent");
+
 extern const char* UI_CATEGORY;
 
 LineEdit::LineEdit(Context* context) :
@@ -168,6 +170,13 @@ bool LineEdit::OnDragDropFinish(UIElement* source)
 {
     if (source && editable_)
     {
+        // If the UI element in question has a drag-and-drop content string defined, use it instead of element text
+        if (source->GetVars().Contains(VAR_DRAGDROPCONTENT))
+        {
+            SetText(source->GetVar(VAR_DRAGDROPCONTENT).GetString());
+            return true;
+        }
+        
         ShortStringHash sourceType = source->GetType();
         if (sourceType == LineEdit::GetTypeStatic())
         {

+ 28 - 2
Source/Engine/UI/ListView.cpp

@@ -167,18 +167,20 @@ ListView::ListView(Context* context) :
     multiselect_(false),
     hierarchyMode_(true),    // Init to true here so that the setter below takes effect
     baseIndent_(0),
-    clearSelectionOnDefocus_(false)
+    clearSelectionOnDefocus_(false),
+    selectOnClickEnd_(false)
 {
     resizeContentWidth_ = true;
 
     // By default list view is set to non-hierarchy mode
     SetHierarchyMode(false);
 
-    SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
     SubscribeToEvent(E_UIMOUSEDOUBLECLICK, HANDLER(ListView, HandleUIMouseDoubleClick));
     SubscribeToEvent(E_FOCUSCHANGED, HANDLER(ListView, HandleItemFocusChanged));
     SubscribeToEvent(this, E_DEFOCUSED, HANDLER(ListView, HandleFocusChanged));
     SubscribeToEvent(this, E_FOCUSED, HANDLER(ListView, HandleFocusChanged));
+    
+    UpdateUIClickSubscription();
 }
 
 ListView::~ListView()
@@ -196,6 +198,7 @@ void ListView::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(ListView, VAR_BOOL, "Hierarchy Mode", GetHierarchyMode, SetHierarchyMode, bool, false, AM_FILE);
     ACCESSOR_ATTRIBUTE(ListView, VAR_INT, "Base Indent", GetBaseIndent, SetBaseIndent, int, 0, AM_FILE);
     ACCESSOR_ATTRIBUTE(ListView, VAR_BOOL, "Clear Sel. On Defocus", GetClearSelectionOnDefocus, SetClearSelectionOnDefocus, bool, false, AM_FILE);
+    ACCESSOR_ATTRIBUTE(ListView, VAR_BOOL, "Select On Click End", GetSelectOnClickEnd, SetSelectOnClickEnd, bool, false, AM_FILE);
 }
 
 void ListView::OnKey(int key, int buttons, int qualifiers)
@@ -728,6 +731,15 @@ void ListView::SetClearSelectionOnDefocus(bool enable)
     }
 }
 
+void ListView::SetSelectOnClickEnd(bool enable)
+{
+    if (enable != selectOnClickEnd_)
+    {
+        selectOnClickEnd_ = enable;
+        UpdateUIClickSubscription();
+    }
+}
+
 void ListView::Expand(unsigned index, bool enable, bool recursive)
 {
     if (!hierarchyMode_)
@@ -975,6 +987,10 @@ void ListView::EnsureItemVisibility(UIElement* item)
 
 void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
 {
+    // Disregard the click end if a drag is going on
+    if (selectOnClickEnd_ && GetSubsystem<UI>()->GetDragElement())
+        return;
+    
     int button = eventData[UIMouseClick::P_BUTTON].GetInt();
     int buttons = eventData[UIMouseClick::P_BUTTONS].GetInt();
     int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt();
@@ -1108,4 +1124,14 @@ void ListView::HandleFocusChanged(StringHash eventType, VariantMap& eventData)
         UpdateSelectionEffect();
 }
 
+void ListView::UpdateUIClickSubscription()
+{
+    UnsubscribeFromEvent(E_UIMOUSECLICK);
+    UnsubscribeFromEvent(E_UIMOUSECLICKEND);
+    if (selectOnClickEnd_)
+        SubscribeToEvent(E_UIMOUSECLICKEND, HANDLER(ListView, HandleUIMouseClick));
+    else
+        SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
+}
+
 }

+ 8 - 0
Source/Engine/UI/ListView.h

@@ -94,6 +94,8 @@ public:
     void SetBaseIndent(int baseIndent);
     /// Enable clearing of selection on defocus.
     void SetClearSelectionOnDefocus(bool enable);
+    /// Enable reacting to click end instead of click start for item selection. Default false.
+    void SetSelectOnClickEnd(bool enable);
 
     /// Expand item at index. Only has effect in hierarchy mode.
     void Expand(unsigned index, bool enable, bool recursive = false);
@@ -128,6 +130,8 @@ public:
     bool GetMultiselect() const { return multiselect_; }
     /// Return whether selection is cleared on defocus.
     bool GetClearSelectionOnDefocus() const { return clearSelectionOnDefocus_; }
+    /// Return whether reacts to click end instead of click start for item selection.
+    bool GetSelectOnClickEnd() const { return selectOnClickEnd_; }
     /// Return whether hierarchy mode enabled.
     bool GetHierarchyMode() const { return hierarchyMode_; }
     /// Return base indent.
@@ -157,6 +161,8 @@ protected:
     SharedPtr<UIElement> overlayContainer_;
     /// Clear selection on defocus flag.
     bool clearSelectionOnDefocus_;
+    /// React to click end instead of click start flag.
+    bool selectOnClickEnd_;
 
 private:
     /// Handle global UI mouseclick to check for selection change.
@@ -167,6 +173,8 @@ private:
     void HandleItemFocusChanged(StringHash eventType, VariantMap& eventData);
     /// Handle focus changed.
     void HandleFocusChanged(StringHash eventType, VariantMap& eventData);
+    /// Update subscription to UI click events
+    void UpdateUIClickSubscription();
 };
 
 }