Explorar el Código

Changes to UI drag behavior:
- Multi-touch drag support
- Combo touch support -- you get id mask of pressed buttons + number of buttons + the average of their positions
- OnDrag* signature updates, with more information available and improved OnDragCancel / E_DRAGCANCEL support.

hdunderscore hace 11 años
padre
commit
007d3acc1a

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

@@ -1369,7 +1369,7 @@ void DrawNodeDebug(Node@ node, DebugRenderer@ debug, bool drawNode = true)
 void ViewMouseMove()
 void ViewMouseMove()
 {
 {
     // setting mouse position based on mouse position
     // setting mouse position based on mouse position
-    if (ui.dragElement !is null) { }
+    if (ui.IsDragging()) { }
     else if (ui.focusElement !is null || input.mouseButtonDown[MOUSEB_LEFT|MOUSEB_MIDDLE|MOUSEB_RIGHT])
     else if (ui.focusElement !is null || input.mouseButtonDown[MOUSEB_LEFT|MOUSEB_MIDDLE|MOUSEB_RIGHT])
         return;
         return;
 
 

+ 3 - 2
Source/Engine/LuaScript/pkgs/UI/UI.pkg

@@ -36,7 +36,8 @@ class UI : public Object
 
 
     UIElement* GetFocusElement() const;
     UIElement* GetFocusElement() const;
     UIElement* GetFrontElement() const;
     UIElement* GetFrontElement() const;
-    UIElement* GetDragElement() const;
+    unsigned GetNumDragElements() const;
+    UIElement* GetDragElement(unsigned index);
     const String GetClipboardText() const;
     const String GetClipboardText() const;
     float GetDoubleClickInterval() const;
     float GetDoubleClickInterval() const;
     float GetDragBeginInterval() const;
     float GetDragBeginInterval() const;
@@ -49,6 +50,7 @@ class UI : public Object
     bool GetUseMutableGlyphs() const;
     bool GetUseMutableGlyphs() const;
     bool GetForceAutoHint() const;
     bool GetForceAutoHint() const;
     bool HasModalElement() const;
     bool HasModalElement() const;
+    bool IsDragging() const;
 
 
     tolua_readonly tolua_property__get_set UIElement* root;
     tolua_readonly tolua_property__get_set UIElement* root;
     tolua_readonly tolua_property__get_set UIElement* rootModalElement;
     tolua_readonly tolua_property__get_set UIElement* rootModalElement;
@@ -56,7 +58,6 @@ class UI : public Object
     tolua_readonly tolua_property__get_set IntVector2 cursorPosition;
     tolua_readonly tolua_property__get_set IntVector2 cursorPosition;
     tolua_readonly tolua_property__get_set UIElement* focusElement;
     tolua_readonly tolua_property__get_set UIElement* focusElement;
     tolua_readonly tolua_property__get_set UIElement* frontElement;
     tolua_readonly tolua_property__get_set UIElement* frontElement;
-    tolua_readonly tolua_property__get_set UIElement* dragElement;
 
 
     tolua_property__get_set String clipboardText;
     tolua_property__get_set String clipboardText;
     tolua_property__get_set float doubleClickInterval;
     tolua_property__get_set float doubleClickInterval;

+ 1 - 0
Source/Engine/Script/InputAPI.cpp

@@ -23,6 +23,7 @@
 #include "Precompiled.h"
 #include "Precompiled.h"
 #include "APITemplates.h"
 #include "APITemplates.h"
 #include "Input.h"
 #include "Input.h"
+#include "Cursor.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {

+ 8 - 1
Source/Engine/Script/UIAPI.cpp

@@ -665,6 +665,11 @@ static void UISetFocusElement(UIElement* element, UI* ptr)
     ptr->SetFocusElement(element);
     ptr->SetFocusElement(element);
 }
 }
 
 
+static CScriptArray* UIGetDragElements(UI* ptr)
+{
+    return VectorToHandleArray(ptr->GetDragElements(), "const Array<UIElement@>@");
+}
+
 static void RegisterUI(asIScriptEngine* engine)
 static void RegisterUI(asIScriptEngine* engine)
 {
 {
     RegisterObject<UI>(engine, "UI");
     RegisterObject<UI>(engine, "UI");
@@ -688,7 +693,8 @@ static void RegisterUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "void set_focusElement(UIElement@+)", asFUNCTION(UISetFocusElement), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("UI", "void set_focusElement(UIElement@+)", asFUNCTION(UISetFocusElement), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_focusElement() const", asMETHOD(UI, GetFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_focusElement() const", asMETHOD(UI, GetFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_frontElement() const", asMETHOD(UI, GetFrontElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_frontElement() const", asMETHOD(UI, GetFrontElement), asCALL_THISCALL);
-    engine->RegisterObjectMethod("UI", "UIElement@+ get_dragElement() const", asMETHOD(UI, GetDragElement), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "const Array<UIElement@>@ GetDragElements()", asFUNCTION(UIGetDragElements), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("UI", "bool IsDragging() const", asMETHOD(UI, IsDragging), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_root() const", asMETHOD(UI, GetRoot), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_root() const", asMETHOD(UI, GetRoot), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_modalRoot() const", asMETHOD(UI, GetRootModalElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ get_modalRoot() const", asMETHOD(UI, GetRootModalElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_clipBoardText(const String&in)", asMETHOD(UI, SetClipboardText), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_clipBoardText(const String&in)", asMETHOD(UI, SetClipboardText), asCALL_THISCALL);
@@ -713,6 +719,7 @@ static void RegisterUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "bool get_useMutableGlyphs() const", asMETHOD(UI, GetUseMutableGlyphs), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "bool get_useMutableGlyphs() const", asMETHOD(UI, GetUseMutableGlyphs), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_forceAutoHint(bool)", asMETHOD(UI, SetForceAutoHint), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_forceAutoHint(bool)", asMETHOD(UI, SetForceAutoHint), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "bool get_forceAutoHint() const", asMETHOD(UI, GetForceAutoHint), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "bool get_forceAutoHint() const", asMETHOD(UI, GetForceAutoHint), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "uint get_numDragButtons(int buttons) const", asMETHOD(UI, GetNumDragButtons), asCALL_THISCALL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
 }
 }
 
 

+ 3 - 1
Source/Engine/UI/LineEdit.cpp

@@ -135,10 +135,12 @@ void LineEdit::OnDoubleClick(const IntVector2& position, const IntVector2& scree
 
 
 void LineEdit::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 void LineEdit::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
 {
+    UIElement::OnDragBegin(position, screenPosition, buttons, qualifiers, cursor);
+    
     dragBeginCursor_ = GetCharIndex(position);
     dragBeginCursor_ = GetCharIndex(position);
 }
 }
 
 
-void LineEdit::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
+void LineEdit::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor)
 {
 {
     if (cursorMovable_ && textSelectable_)
     if (cursorMovable_ && textSelectable_)
     {
     {

+ 1 - 1
Source/Engine/UI/LineEdit.h

@@ -54,7 +54,7 @@ public:
     /// React to mouse drag begin.
     /// React to mouse drag begin.
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag motion.
     /// React to mouse drag motion.
-    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
+    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor);
     /// React to drag and drop test. Return true to signal that the drop is acceptable.
     /// React to drag and drop test. Return true to signal that the drop is acceptable.
     virtual bool OnDragDropTest(UIElement* source);
     virtual bool OnDragDropTest(UIElement* source);
     /// React to drag and drop finish. Return true to signal that the drop was accepted.
     /// React to drag and drop finish. Return true to signal that the drop was accepted.

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

@@ -988,9 +988,9 @@ void ListView::EnsureItemVisibility(UIElement* item)
 void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
 void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
 {
 {
     // Disregard the click end if a drag is going on
     // Disregard the click end if a drag is going on
-    if (selectOnClickEnd_ && GetSubsystem<UI>()->GetDragElement())
+    if (selectOnClickEnd_ && GetSubsystem<UI>()->IsDragging())
         return;
         return;
-    
+
     int button = eventData[UIMouseClick::P_BUTTON].GetInt();
     int button = eventData[UIMouseClick::P_BUTTON].GetInt();
     int buttons = eventData[UIMouseClick::P_BUTTONS].GetInt();
     int buttons = eventData[UIMouseClick::P_BUTTONS].GetInt();
     int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt();
     int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt();

+ 25 - 15
Source/Engine/UI/ScrollView.cpp

@@ -118,27 +118,37 @@ void ScrollView::Update(float timeStep)
         return;
         return;
     }
     }
 
 
-    UIElement* dragElement = GetSubsystem<UI>()->GetDragElement();
-    if (dragElement)
+    if (GetSubsystem<UI>()->IsDragging())
     {
     {
-        UIElement* dragParent = dragElement->GetParent();
-        bool dragElementIsChild = false;
+        Vector<UIElement*> dragElements = GetSubsystem<UI>()->GetDragElements();
 
 
-        while (dragParent)
+        for (unsigned i = 0; i< dragElements.Size(); i++)
         {
         {
-            if (dragParent == this)
+            UIElement* dragElement = dragElements[i];
+            int dragButtons = dragElement->GetDragButtonCombo();
+
+            if (dragButtons != MOUSEB_LEFT)
+                continue;
+
+            UIElement* dragParent = dragElement->GetParent();
+            bool dragElementIsChild = false;
+
+            while (dragParent)
             {
             {
-                dragElementIsChild = true;
-                break;
+                if (dragParent == this)
+                {
+                    dragElementIsChild = true;
+                    break;
+                }
+                dragParent = dragParent->GetParent();
             }
             }
-            dragParent = dragParent->GetParent();
-        }
 
 
-        if (!dragElementIsChild || dragElement == horizontalScrollBar_->GetSlider() || dragElement == verticalScrollBar_->GetSlider())
-        {
-            touchScrollSpeed_ = Vector2::ZERO;
-            touchScrollSpeedMax_ = Vector2::ZERO;
-            return;
+            if (!dragElementIsChild || dragElement == horizontalScrollBar_->GetSlider() || dragElement == verticalScrollBar_->GetSlider())
+            {
+                touchScrollSpeed_ = Vector2::ZERO;
+                touchScrollSpeedMax_ = Vector2::ZERO;
+                return;
+            }
         }
         }
     }
     }
 
 

+ 17 - 7
Source/Engine/UI/Slider.cpp

@@ -116,12 +116,17 @@ void Slider::OnClickEnd(const IntVector2& position, const IntVector2& screenPosi
 
 
 void Slider::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 void Slider::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
 {
-    dragBeginCursor_ = position;
-    dragBeginPosition_ = knob_->GetPosition();
-    dragSlider_ = knob_->IsInside(screenPosition, true);
+    UIElement::OnDragBegin(position, screenPosition, buttons, qualifiers, cursor);
+
+    if (buttons == MOUSEB_LEFT)
+    {
+        dragBeginCursor_ = position;
+        dragBeginPosition_ = knob_->GetPosition();
+        dragSlider_ = knob_->IsInside(screenPosition, true);
+    }
 }
 }
 
 
-void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
+void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor)
 {
 {
     if (!editable_ || !dragSlider_ || GetSize() == knob_->GetSize())
     if (!editable_ || !dragSlider_ || GetSize() == knob_->GetSize())
         return;
         return;
@@ -145,10 +150,15 @@ void Slider::OnDragMove(const IntVector2& position, const IntVector2& screenPosi
     SetValue(newValue);
     SetValue(newValue);
 }
 }
 
 
-void Slider::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
+void Slider::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor)
 {
 {
-    dragSlider_ = false;
-    selected_ = false;
+    UIElement::OnDragEnd(position, screenPosition, dragButtons, buttons, cursor);
+
+    if (dragButtons == MOUSEB_LEFT)
+    {
+        dragSlider_ = false;
+        selected_ = false;
+    }
 }
 }
 
 
 void Slider::OnResize()
 void Slider::OnResize()

+ 3 - 3
Source/Engine/UI/Slider.h

@@ -51,12 +51,12 @@ public:
     /// React to mouse drag begin.
     /// React to mouse drag begin.
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag motion.
     /// React to mouse drag motion.
-    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
+    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag end.
     /// React to mouse drag end.
-    virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor);
+    virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor);
     /// React to resize.
     /// React to resize.
     virtual void OnResize();
     virtual void OnResize();
-    
+
     /// Set orientation type.
     /// Set orientation type.
     void SetOrientation(Orientation orientation);
     void SetOrientation(Orientation orientation);
     /// Set slider range maximum value (minimum value is always 0.)
     /// Set slider range maximum value (minimum value is always 0.)

+ 402 - 134
Source/Engine/UI/UI.cpp

@@ -87,6 +87,8 @@ UI::UI(Context* context) :
     lastMouseButtons_(0),
     lastMouseButtons_(0),
     qualifiers_(0),
     qualifiers_(0),
     maxFontTextureSize_(DEFAULT_FONT_TEXTURE_MAX_SIZE),
     maxFontTextureSize_(DEFAULT_FONT_TEXTURE_MAX_SIZE),
+    dragElementsCount_(0),
+    dragConfirmedCount_(0),
     initialized_(false),
     initialized_(false),
     usingTouchInput_(false),
     usingTouchInput_(false),
     #ifdef WIN32
     #ifdef WIN32
@@ -102,7 +104,6 @@ UI::UI(Context* context) :
     #endif
     #endif
     useMutableGlyphs_(false),
     useMutableGlyphs_(false),
     forceAutoHint_(false),
     forceAutoHint_(false),
-    dragBeginPending_(false),
     nonModalBatchSize_(0)
     nonModalBatchSize_(0)
 {
 {
     rootElement_->SetTraversalMode(TM_DEPTH_FIRST);
     rootElement_->SetTraversalMode(TM_DEPTH_FIRST);
@@ -300,30 +301,51 @@ void UI::Update(float timeStep)
     for (HashMap<WeakPtr<UIElement>, bool>::Iterator i = hoveredElements_.Begin(); i != hoveredElements_.End(); ++i)
     for (HashMap<WeakPtr<UIElement>, bool>::Iterator i = hoveredElements_.Begin(); i != hoveredElements_.End(); ++i)
         i->second_ = false;
         i->second_ = false;
 
 
+    Input* input = GetSubsystem<Input>();
+    IntVector2 cursorPos;
+    bool cursorVisible;
+    GetCursorPositionAndVisible(cursorPos, cursorVisible);
+
     // Drag begin based on time
     // Drag begin based on time
-    if (dragElement_ && dragBeginPending_)
+    if (dragElementsCount_ > 0)
     {
     {
-        if (dragBeginTimer_.GetMSec(false) >= (unsigned)(dragBeginInterval_ * 1000))
+        for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End(); )
         {
         {
-            dragBeginPending_ = false;
-            if (!usingTouchInput_)
+            WeakPtr<UIElement> dragElement = i->first_;
+            UI::DragData* dragData = i->second_;
+
+            if (!dragElement)
             {
             {
-                dragElement_->OnDragBegin(dragElement_->ScreenToElement(dragBeginPos_), dragBeginPos_, mouseButtons_, qualifiers_,
-                    cursor_);
+                i = dragElementErase(i);
+                continue;
             }
             }
-            else
-                dragElement_->OnDragBegin(dragElement_->ScreenToElement(dragBeginPos_), dragBeginPos_, MOUSEB_LEFT, 0, 0);
-            SendDragOrHoverEvent(E_DRAGBEGIN, dragElement_, dragBeginPos_);
+
+            if (!dragData->dragBeginPending)
+            {
+                ++i;
+                continue;
+            }
+
+            if (dragData->dragBeginTimer.GetMSec(false) >= (unsigned)(dragBeginInterval_ * 1000))
+            {
+                dragData->dragBeginPending = false;
+                IntVector2 beginSendPos = dragData->dragBeginSumPos / dragData->numDragButtons;
+                dragConfirmedCount_ ++;
+                if (!usingTouchInput_)
+                    dragElement->OnDragBegin(dragElement->ScreenToElement(beginSendPos), beginSendPos, dragData->dragButtons, qualifiers_,cursor_);
+                else
+                    dragElement->OnDragBegin(dragElement->ScreenToElement(beginSendPos), beginSendPos, dragData->dragButtons, 0, 0);
+
+                SendDragOrHoverEvent(E_DRAGBEGIN, dragElement, beginSendPos, IntVector2::ZERO, dragData);
+            }
+
+            ++i;
         }
         }
     }
     }
 
 
     // Mouse hover
     // Mouse hover
-    Input* input = GetSubsystem<Input>();
     if (!input->IsMouseGrabbed() && !input->GetTouchEmulation())
     if (!input->IsMouseGrabbed() && !input->GetTouchEmulation())
     {
     {
-        IntVector2 cursorPos;
-        bool cursorVisible;
-        GetCursorPositionAndVisible(cursorPos, cursorVisible);
         if (!usingTouchInput_ && cursorVisible)
         if (!usingTouchInput_ && cursorVisible)
             ProcessHover(cursorPos, mouseButtons_, qualifiers_, cursor_);
             ProcessHover(cursorPos, mouseButtons_, qualifiers_, cursor_);
     }
     }
@@ -333,7 +355,7 @@ void UI::Update(float timeStep)
     for (unsigned i = 0; i < numTouches; ++i)
     for (unsigned i = 0; i < numTouches; ++i)
     {
     {
         TouchState* touch = input->GetTouch(i);
         TouchState* touch = input->GetTouch(i);
-        ProcessHover(touch->position_, MOUSEB_LEFT, 0, 0);
+        ProcessHover(touch->position_, TOUCHID_MASK(i), 0, 0);
     }
     }
 
 
     // End hovers that expired without refreshing
     // End hovers that expired without refreshing
@@ -595,10 +617,36 @@ UIElement* UI::GetFrontElement() const
     return front;
     return front;
 }
 }
 
 
-UIElement* UI::GetDragElement() const
+const Vector<UIElement*> UI::GetDragElements()
 {
 {
     // Do not return the element until drag begin event has actually been posted
     // Do not return the element until drag begin event has actually been posted
-    return dragBeginPending_ ? (UIElement*)0 : dragElement_;
+    if (!dragElementsConfirmed_.Empty())
+        return dragElementsConfirmed_;
+
+    for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End(); )
+    {
+        WeakPtr<UIElement> dragElement = i->first_;
+        UI::DragData* dragData = i->second_;
+
+        if (!dragElement)
+        {
+            i = dragElementErase(i);
+            continue;
+        }
+
+        if (!dragData->dragBeginPending)
+            dragElementsConfirmed_.Push(dragElement);
+
+        ++i;
+    }
+
+    return dragElementsConfirmed_;
+}
+
+UIElement* UI::GetDragElement(unsigned index)
+{
+    GetDragElements();
+    return dragElementsConfirmed_[index];
 }
 }
 
 
 const String& UI::GetClipboardText() const
 const String& UI::GetClipboardText() const
@@ -619,6 +667,16 @@ bool UI::HasModalElement() const
     return rootModalElement_->GetNumChildren() > 0;
     return rootModalElement_->GetNumChildren() > 0;
 }
 }
 
 
+unsigned UI::GetNumDragButtons(unsigned buttons)
+{
+    // Counting set bits in mask
+    // Brian Kernighan's method
+    unsigned count = 0;
+    for (count = 0; buttons; count++)
+        buttons &= buttons - 1;
+    return count;
+}
+
 void UI::Initialize()
 void UI::Initialize()
 {
 {
     Graphics* graphics = GetSubsystem<Graphics>();
     Graphics* graphics = GetSubsystem<Graphics>();
@@ -929,25 +987,81 @@ void UI::ProcessHover(const IntVector2& cursorPos, int buttons, int qualifiers,
 {
 {
     WeakPtr<UIElement> element(GetElementAt(cursorPos));
     WeakPtr<UIElement> element(GetElementAt(cursorPos));
 
 
-    bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
-    bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET) != 0;
-    bool dragDropTest = dragSource && dragTarget && element != dragElement_;
-    // If drag start event has not been posted yet, do not do drag handling here
-    if (dragBeginPending_)
-        dragSource = dragTarget = dragDropTest = false;
+    for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
+    {
+        WeakPtr<UIElement> dragElement = i->first_;
+        UI::DragData* dragData = i->second_;
+
+        if (!dragElement)
+        {
+            i = dragElementErase(i);
+            continue;
+        }
+
+        bool dragSource = dragElement && (dragElement->GetDragDropMode() & DD_SOURCE) != 0;
+        bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET) != 0;
+        bool dragDropTest = dragSource && dragTarget && element != dragElement;
+        // If drag start event has not been posted yet, do not do drag handling here
+        if (dragData->dragBeginPending)
+            dragSource = dragTarget = dragDropTest = false;
+
+        // Hover effect
+        // If a drag is going on, transmit hover only to the element being dragged, unless it's a drop target
+        if (element && element->IsEnabled())
+        {
+            if (dragElement == element || dragDropTest)
+            {
+                element->OnHover(element->ScreenToElement(cursorPos), cursorPos, buttons, qualifiers, cursor);
+
+                // Begin hover event
+                if (!hoveredElements_.Contains(element))
+                {
+                    SendDragOrHoverEvent(E_HOVERBEGIN, element, cursorPos, IntVector2::ZERO, 0);
+                    // Exit if element is destroyed by the event handling
+                    if (!element)
+                        return;
+                }
+                hoveredElements_[element] = true;
+            }
+        }
+
+        // Drag and drop test
+        if (dragDropTest)
+        {
+            bool accept = element->OnDragDropTest(dragElement);
+            if (accept)
+            {
+                using namespace DragDropTest;
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[P_SOURCE] = dragElement.Get();
+                eventData[P_TARGET] = element.Get();
+                eventData[P_ACCEPT] = accept;
+                SendEvent(E_DRAGDROPTEST, eventData);
+                accept = eventData[P_ACCEPT].GetBool();
+            }
+
+            if (cursor)
+                cursor->SetShape(accept ? CS_ACCEPTDROP : CS_REJECTDROP);
+        }
+        else if (dragSource && cursor)
+            cursor->SetShape(dragElement == element ? CS_ACCEPTDROP : CS_REJECTDROP);
+
+        ++i;
+    }
 
 
     // Hover effect
     // Hover effect
-    // If a drag is going on, transmit hover only to the element being dragged, unless it's a drop target
+    // If no drag is going on, transmit hover event.
     if (element && element->IsEnabled())
     if (element && element->IsEnabled())
     {
     {
-        if (!dragElement_ || dragElement_ == element || dragDropTest)
+        if (dragElementsCount_ == 0)
         {
         {
             element->OnHover(element->ScreenToElement(cursorPos), cursorPos, buttons, qualifiers, cursor);
             element->OnHover(element->ScreenToElement(cursorPos), cursorPos, buttons, qualifiers, cursor);
 
 
             // Begin hover event
             // Begin hover event
             if (!hoveredElements_.Contains(element))
             if (!hoveredElements_.Contains(element))
             {
             {
-                SendDragOrHoverEvent(E_HOVERBEGIN, element, cursorPos);
+                SendDragOrHoverEvent(E_HOVERBEGIN, element, cursorPos, IntVector2::ZERO, 0);
                 // Exit if element is destroyed by the event handling
                 // Exit if element is destroyed by the event handling
                 if (!element)
                 if (!element)
                     return;
                     return;
@@ -955,28 +1069,6 @@ void UI::ProcessHover(const IntVector2& cursorPos, int buttons, int qualifiers,
             hoveredElements_[element] = true;
             hoveredElements_[element] = true;
         }
         }
     }
     }
-
-    // Drag and drop test
-    if (dragDropTest)
-    {
-        bool accept = element->OnDragDropTest(dragElement_);
-        if (accept)
-        {
-            using namespace DragDropTest;
-
-            VariantMap& eventData = GetEventDataMap();
-            eventData[P_SOURCE] = dragElement_.Get();
-            eventData[P_TARGET] = element.Get();
-            eventData[P_ACCEPT] = accept;
-            SendEvent(E_DRAGDROPTEST, eventData);
-            accept = eventData[P_ACCEPT].GetBool();
-        }
-
-        if (cursor)
-            cursor->SetShape(accept ? CS_ACCEPTDROP : CS_REJECTDROP);
-    }
-    else if (dragSource && cursor)
-        cursor->SetShape(dragElement_ == element ? CS_ACCEPTDROP : CS_REJECTDROP);
 }
 }
 
 
 void UI::ProcessClickBegin(const IntVector2& cursorPos, int button, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible)
 void UI::ProcessClickBegin(const IntVector2& cursorPos, int button, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible)
@@ -985,21 +1077,25 @@ void UI::ProcessClickBegin(const IntVector2& cursorPos, int button, int buttons,
     {
     {
         WeakPtr<UIElement> element(GetElementAt(cursorPos));
         WeakPtr<UIElement> element(GetElementAt(cursorPos));
 
 
+        bool newButton;
+        if (usingTouchInput_)
+            newButton = !((bool)(button & buttons));
+        else
+            newButton = true;
+        buttons |= button;
+
+        if (element)
+            SetFocusElement (element);
+
+        // Focus change events may destroy the element, check again.
         if (element)
         if (element)
         {
         {
             // Handle focusing & bringing to front
             // Handle focusing & bringing to front
-            if (button == MOUSEB_LEFT)
-            {
-                SetFocusElement(element);
-                element->BringToFront();
-            }
+            element->BringToFront();
 
 
             // Handle click
             // Handle click
             element->OnClickBegin(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor);
             element->OnClickBegin(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor);
-            SendClickEvent(E_UIMOUSECLICK, element, cursorPos, button, buttons, qualifiers);
-
-            // Remember element clicked on for the click end
-            clickElement_ = element;
+            SendClickEvent(E_UIMOUSECLICK, NULL, element, cursorPos, button, buttons, qualifiers);
 
 
             // Fire double click event if element matches and is in time
             // Fire double click event if element matches and is in time
             if (doubleClickElement_ && element == doubleClickElement_ && clickTimer_.GetMSec(true) <
             if (doubleClickElement_ && element == doubleClickElement_ && clickTimer_.GetMSec(true) <
@@ -1007,7 +1103,7 @@ void UI::ProcessClickBegin(const IntVector2& cursorPos, int button, int buttons,
             {
             {
                 element->OnDoubleClick(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor);
                 element->OnDoubleClick(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor);
                 doubleClickElement_.Reset();
                 doubleClickElement_.Reset();
-                SendClickEvent(E_UIMOUSEDOUBLECLICK, element, cursorPos, button, buttons, qualifiers);
+                SendClickEvent(E_UIMOUSEDOUBLECLICK, NULL, element, cursorPos, button, buttons, qualifiers);
             }
             }
             else
             else
             {
             {
@@ -1016,12 +1112,28 @@ void UI::ProcessClickBegin(const IntVector2& cursorPos, int button, int buttons,
             }
             }
 
 
             // Handle start of drag. Click handling may have caused destruction of the element, so check the pointer again
             // Handle start of drag. Click handling may have caused destruction of the element, so check the pointer again
-            if (element && !dragElement_ && buttons == MOUSEB_LEFT)
+            bool dragElementsContain = dragElements_.Contains(element);
+            if (element && !dragElementsContain)
             {
             {
-                dragElement_ = element;
-                dragBeginPending_ = true;
-                dragBeginPos_ = cursorPos;
-                dragBeginTimer_.Reset();
+                DragData* dragData = new DragData();
+                dragElements_[element] = dragData;
+                dragData->dragBeginPending = true;
+                dragData->sumPos = cursorPos;
+                dragData->dragBeginSumPos = cursorPos;
+                dragData->dragBeginTimer.Reset();
+                dragData->dragButtons = button;
+                dragData->numDragButtons = GetNumDragButtons(dragData->dragButtons);
+                dragElementsCount_++;
+
+                dragElementsContain = dragElements_.Contains(element);
+            }
+            else if (element && dragElementsContain && newButton)
+            {
+                DragData* dragData = dragElements_[element];
+                dragData->sumPos += cursorPos;
+                dragData->dragBeginSumPos += cursorPos;
+                dragData->dragButtons |= button;
+                dragData->numDragButtons = GetNumDragButtons(dragData->dragButtons);
             }
             }
         }
         }
         else
         else
@@ -1029,7 +1141,7 @@ void UI::ProcessClickBegin(const IntVector2& cursorPos, int button, int buttons,
             // If clicked over no element, or a disabled element, lose focus (but not if there is a modal element)
             // If clicked over no element, or a disabled element, lose focus (but not if there is a modal element)
             if (!HasModalElement())
             if (!HasModalElement())
                 SetFocusElement(0);
                 SetFocusElement(0);
-            SendClickEvent(E_UIMOUSECLICK, element, cursorPos, button, buttons, qualifiers);
+            SendClickEvent(E_UIMOUSECLICK, NULL, element, cursorPos, button, buttons, qualifiers);
         }
         }
 
 
         lastMouseButtons_ = buttons;
         lastMouseButtons_ = buttons;
@@ -1042,94 +1154,145 @@ void UI::ProcessClickEnd(const IntVector2& cursorPos, int button, int buttons, i
     {
     {
         WeakPtr<UIElement> element(GetElementAt(cursorPos));
         WeakPtr<UIElement> element(GetElementAt(cursorPos));
 
 
-        // Handle end of click
-        if (element)
-            element->OnClickEnd(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor, clickElement_);
-
-        SendClickEvent(E_UIMOUSECLICKEND, element, cursorPos, button, buttons, qualifiers);
-
         // Handle end of drag
         // Handle end of drag
-        if (dragElement_ && !buttons)
+        for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
         {
         {
-            if (dragElement_->IsEnabled() && dragElement_->IsVisible() && !dragBeginPending_)
+            WeakPtr<UIElement> dragElement = i->first_;
+            UI::DragData* dragData = i->second_;
+
+            if (!dragElement)
+            {
+                i = dragElementErase(i);
+                continue;
+            }
+
+            if (dragData->dragButtons & button)
             {
             {
-                dragElement_->OnDragEnd(dragElement_->ScreenToElement(cursorPos), cursorPos, cursor);
-                SendDragOrHoverEvent(E_DRAGEND, dragElement_, cursorPos);
+                // Handle end of click
+                if (element)
+                    element->OnClickEnd(element->ScreenToElement(cursorPos), cursorPos, button, buttons, qualifiers, cursor, dragElement);
+
+                SendClickEvent(E_UIMOUSECLICKEND, dragElement, element, cursorPos, button, buttons, qualifiers);
 
 
-                bool dragSource = dragElement_ && (dragElement_->GetDragDropMode() & DD_SOURCE) != 0;
-                if (dragSource)
+                if (dragElement && dragElement->IsEnabled() && dragElement->IsVisible() && !dragData->dragBeginPending)
                 {
                 {
-                    bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET) != 0;
-                    bool dragDropFinish = dragSource && dragTarget && element != dragElement_;
+                    dragElement->OnDragEnd(dragElement->ScreenToElement(cursorPos), cursorPos, dragData->dragButtons, buttons, cursor);
+                    SendDragOrHoverEvent(E_DRAGEND, dragElement, cursorPos, IntVector2::ZERO, dragData);
 
 
-                    if (dragDropFinish)
+                    bool dragSource = dragElement && (dragElement->GetDragDropMode() & DD_SOURCE) != 0;
+                    if (dragSource)
                     {
                     {
-                        bool accept = element->OnDragDropFinish(dragElement_);
+                        bool dragTarget = element && (element->GetDragDropMode() & DD_TARGET) != 0;
+                        bool dragDropFinish = dragSource && dragTarget && element != dragElement;
 
 
-                        // OnDragDropFinish() may have caused destruction of the elements, so check the pointers again
-                        if (accept && dragElement_ && element)
+                        if (dragDropFinish)
                         {
                         {
-                            using namespace DragDropFinish;
-
-                            VariantMap& eventData = GetEventDataMap();
-                            eventData[P_SOURCE] = dragElement_.Get();
-                            eventData[P_TARGET] = element.Get();
-                            eventData[P_ACCEPT] = accept;
-                            SendEvent(E_DRAGDROPFINISH, eventData);
+                            bool accept = element->OnDragDropFinish(dragElement);
+
+                            // OnDragDropFinish() may have caused destruction of the elements, so check the pointers again
+                            if (accept && dragElement && element)
+                            {
+                                using namespace DragDropFinish;
+
+                                VariantMap& eventData = GetEventDataMap();
+                                eventData[P_SOURCE] = dragElement.Get();
+                                eventData[P_TARGET] = element.Get();
+                                eventData[P_ACCEPT] = accept;
+                                SendEvent(E_DRAGDROPFINISH, eventData);
+                            }
                         }
                         }
                     }
                     }
                 }
                 }
-            }
 
 
-            dragElement_.Reset();
-            dragBeginPending_ = false;
+                i = dragElementErase(i);
+            }
+            else
+                ++i;
         }
         }
-
-        clickElement_.Reset();
     }
     }
 }
 }
 
 
-void UI::ProcessMove(const IntVector2& cursorPos, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible)
+void UI::ProcessMove(const IntVector2& cursorPos, const IntVector2& cursorDeltaPos, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible)
 {
 {
-    if (cursorVisible && dragElement_ && buttons)
+    if (cursorVisible && dragElementsCount_ > 0 && buttons)
+    if (dragElementsCount_ > 0 && buttons)
     {
     {
-        if (dragElement_->IsEnabled() && dragElement_->IsVisible())
+        for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
         {
         {
-            // Signal drag begin if distance threshold was exceeded
-            if (dragBeginPending_)
+            WeakPtr<UIElement> dragElement = i->first_;
+            UI::DragData* dragData = i->second_;
+
+            if (!dragElement)
+            {
+                i = dragElementErase(i);
+                continue;
+            }
+
+            if (!(dragData->dragButtons & buttons))
+            {
+                ++i;
+                continue;
+            }
+
+            // Calculate the position that we should send for this drag event.
+            IntVector2 sendPos;
+            if (usingTouchInput_)
             {
             {
-                IntVector2 offset = cursorPos - dragBeginPos_;
-                if (Abs(offset.x_) >= dragBeginDistance_ || Abs(offset.y_) >= dragBeginDistance_)
+                dragData->sumPos += cursorDeltaPos;
+                sendPos.x_ = dragData->sumPos.x_ / dragData->numDragButtons;
+                sendPos.y_ = dragData->sumPos.y_ / dragData->numDragButtons;
+            }
+            else
+            {
+                dragData->sumPos = cursorPos;
+                sendPos = cursorPos;
+            }
+
+            if (dragElement->IsEnabled() && dragElement->IsVisible())
+            {
+                // Signal drag begin if distance threshold was exceeded
+                if (dragData->dragBeginPending)
                 {
                 {
-                    dragBeginPending_ = false;
-                    dragElement_->OnDragBegin(dragElement_->ScreenToElement(dragBeginPos_), dragBeginPos_, buttons, qualifiers, cursor);
-                    SendDragOrHoverEvent(E_DRAGBEGIN, dragElement_, dragBeginPos_);
+                    IntVector2 beginSendPos;
+                    beginSendPos.x_ = dragData->dragBeginSumPos.x_ / dragData->numDragButtons;
+                    beginSendPos.y_ = dragData->dragBeginSumPos.y_ / dragData->numDragButtons;
+
+                    IntVector2 offset = cursorPos - beginSendPos;
+                    if (Abs(offset.x_) >= dragBeginDistance_ || Abs(offset.y_) >= dragBeginDistance_)
+                    {
+                        dragData->dragBeginPending = false;
+                        dragConfirmedCount_ ++;
+                        dragElement->OnDragBegin(dragElement->ScreenToElement(beginSendPos), beginSendPos, buttons, qualifiers, cursor);
+                        SendDragOrHoverEvent(E_DRAGBEGIN, dragElement, beginSendPos, IntVector2::ZERO, dragData);
+                    }
                 }
                 }
-            }
 
 
-            if (!dragBeginPending_)
+                if (!dragData->dragBeginPending)
+                {
+                    dragElement->OnDragMove(dragElement->ScreenToElement(sendPos), sendPos, cursorDeltaPos, buttons, qualifiers, cursor);
+                    SendDragOrHoverEvent(E_DRAGMOVE, dragElement, sendPos, cursorDeltaPos, dragData);
+                }
+            }
+            else
             {
             {
-                dragElement_->OnDragMove(dragElement_->ScreenToElement(cursorPos), cursorPos, buttons, qualifiers, cursor);
-                SendDragOrHoverEvent(E_DRAGMOVE, dragElement_, cursorPos);
+                dragElement->OnDragEnd(dragElement->ScreenToElement(sendPos), sendPos, dragData->dragButtons, buttons, cursor);
+                SendDragOrHoverEvent(E_DRAGEND, dragElement, sendPos, IntVector2::ZERO, dragData);
+                dragElement.Reset();
             }
             }
-        }
-        else
-        {
-            dragElement_->OnDragEnd(dragElement_->ScreenToElement(cursorPos), cursorPos, cursor);
-            SendDragOrHoverEvent(E_DRAGEND, dragElement_, cursorPos);
-            dragElement_.Reset();
+
+            ++i;
         }
         }
     }
     }
 }
 }
 
 
-void UI::SendDragOrHoverEvent(StringHash eventType, UIElement* element, const IntVector2& screenPos)
+void UI::SendDragOrHoverEvent(StringHash eventType, UIElement* element, const IntVector2& screenPos, const IntVector2& deltaPos, UI::DragData* dragData)
 {
 {
     if (!element)
     if (!element)
         return;
         return;
 
 
     IntVector2 relativePos = element->ScreenToElement(screenPos);
     IntVector2 relativePos = element->ScreenToElement(screenPos);
 
 
-    using namespace DragBegin;
+    using namespace DragMove;
 
 
     VariantMap& eventData = GetEventDataMap();
     VariantMap& eventData = GetEventDataMap();
     eventData[P_ELEMENT] = element;
     eventData[P_ELEMENT] = element;
@@ -1138,13 +1301,25 @@ void UI::SendDragOrHoverEvent(StringHash eventType, UIElement* element, const In
     eventData[P_ELEMENTX] = relativePos.x_;
     eventData[P_ELEMENTX] = relativePos.x_;
     eventData[P_ELEMENTY] = relativePos.y_;
     eventData[P_ELEMENTY] = relativePos.y_;
 
 
+    if (eventType == E_DRAGMOVE)
+    {
+        eventData[P_DX] = deltaPos.x_;
+        eventData[P_DY] = deltaPos.y_;
+    }
+
+    if (dragData)
+    {
+        eventData[P_BUTTONS] = dragData->dragButtons;
+        eventData[P_NUMBUTTONS] = dragData->numDragButtons;
+    }
+
     element->SendEvent(eventType, eventData);
     element->SendEvent(eventType, eventData);
 }
 }
 
 
-void UI::SendClickEvent(StringHash eventType, UIElement* element, const IntVector2& pos, int button, int buttons, int qualifiers)
+void UI::SendClickEvent(StringHash eventType, UIElement* beginElement, UIElement* endElement, const IntVector2& pos, int button, int buttons, int qualifiers)
 {
 {
     VariantMap& eventData = GetEventDataMap();
     VariantMap& eventData = GetEventDataMap();
-    eventData[UIMouseClick::P_ELEMENT] = element;
+    eventData[UIMouseClick::P_ELEMENT] = endElement;
     eventData[UIMouseClick::P_X] = pos.x_;
     eventData[UIMouseClick::P_X] = pos.x_;
     eventData[UIMouseClick::P_Y] = pos.y_;
     eventData[UIMouseClick::P_Y] = pos.y_;
     eventData[UIMouseClick::P_BUTTON] = button;
     eventData[UIMouseClick::P_BUTTON] = button;
@@ -1153,7 +1328,7 @@ void UI::SendClickEvent(StringHash eventType, UIElement* element, const IntVecto
 
 
     // For click end events, send also the element the click began on
     // For click end events, send also the element the click began on
     if (eventType == E_UIMOUSECLICKEND)
     if (eventType == E_UIMOUSECLICKEND)
-        eventData[UIMouseClickEnd::P_BEGINELEMENT] = clickElement_;
+        eventData[UIMouseClickEnd::P_BEGINELEMENT] = beginElement;
 
 
     SendEvent(eventType, eventData);
     SendEvent(eventType, eventData);
 }
 }
@@ -1174,7 +1349,8 @@ void UI::HandleScreenMode(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 {
 {
     Input* input = GetSubsystem<Input>();
     Input* input = GetSubsystem<Input>();
-    if (input->IsMouseGrabbed())
+    bool mouseGrabbed = input->IsMouseGrabbed();
+    if (dragElementsCount_ == 0 && mouseGrabbed)
         return;
         return;
 
 
     using namespace MouseButtonDown;
     using namespace MouseButtonDown;
@@ -1187,7 +1363,11 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
     bool cursorVisible;
     bool cursorVisible;
     GetCursorPositionAndVisible(cursorPos, cursorVisible);
     GetCursorPositionAndVisible(cursorPos, cursorVisible);
 
 
-    ProcessClickBegin(cursorPos, eventData[P_BUTTON].GetInt(), mouseButtons_, qualifiers_, cursor_, cursorVisible);
+    // Handle drag cancelling
+    ProcessDragCancel();
+
+    if (!mouseGrabbed)
+        ProcessClickBegin(cursorPos, eventData[P_BUTTON].GetInt(), mouseButtons_, qualifiers_, cursor_, cursorVisible);
 }
 }
 
 
 void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
@@ -1219,6 +1399,8 @@ void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
     Input* input = GetSubsystem<Input>();
     Input* input = GetSubsystem<Input>();
     const IntVector2& rootSize = rootElement_->GetSize();
     const IntVector2& rootSize = rootElement_->GetSize();
 
 
+    IntVector2 DeltaP = IntVector2(eventData[P_DX].GetInt(), eventData[P_DY].GetInt());
+
     if (cursor_)
     if (cursor_)
     {
     {
         if (!input->IsMouseVisible())
         if (!input->IsMouseVisible())
@@ -1245,7 +1427,7 @@ void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
     bool cursorVisible;
     bool cursorVisible;
     GetCursorPositionAndVisible(cursorPos, cursorVisible);
     GetCursorPositionAndVisible(cursorPos, cursorVisible);
 
 
-    ProcessMove(cursorPos, mouseButtons_, qualifiers_, cursor_, cursorVisible);
+    ProcessMove(cursorPos, DeltaP, mouseButtons_, qualifiers_, cursor_, cursorVisible);
 }
 }
 
 
 void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
 void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
@@ -1303,7 +1485,15 @@ void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     usingTouchInput_ = true;
     usingTouchInput_ = true;
 
 
-    ProcessClickBegin(pos, MOUSEB_LEFT, MOUSEB_LEFT, 0, 0, true);
+    int touchId = TOUCHID_MASK(eventData[P_TOUCHID].GetInt());
+    WeakPtr<UIElement> element(GetElementAt(pos));
+    if (element)
+    {
+        ProcessClickBegin(pos, touchId, touchDragElements_[element], 0, 0, true);
+        touchDragElements_[element] |= touchId;
+    }
+    else
+        ProcessClickBegin(pos, touchId, touchId, 0, 0, true);
 }
 }
 
 
 void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
@@ -1311,13 +1501,25 @@ void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
     using namespace TouchEnd;
     using namespace TouchEnd;
 
 
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
+    int touchId = TOUCHID_MASK(eventData[P_TOUCHID].GetInt());
 
 
     // Transmit hover end to the position where the finger was lifted
     // Transmit hover end to the position where the finger was lifted
-    UIElement* element = GetElementAt(pos);
+    WeakPtr<UIElement> element(GetElementAt(pos));
+
+    // Clear any drag events that were using the touch id
+    for (HashMap<WeakPtr<UIElement>, int>::Iterator i = touchDragElements_.Begin(); i != touchDragElements_.End(); )
+    {
+        int touches = i->second_;
+        if (touches & touchId)
+            i = touchDragElements_.Erase(i);
+        else
+            ++i;
+    }
+
     if (element && element->IsEnabled())
     if (element && element->IsEnabled())
         element->OnHover(element->ScreenToElement(pos), pos, 0, 0, 0);
         element->OnHover(element->ScreenToElement(pos), pos, 0, 0, 0);
 
 
-    ProcessClickEnd(pos, MOUSEB_LEFT, 0, 0, 0, true);
+    ProcessClickEnd(pos, touchId, 0, 0, 0, true);
 }
 }
 
 
 void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
 void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
@@ -1325,9 +1527,12 @@ void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
     using namespace TouchMove;
     using namespace TouchMove;
 
 
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
     IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
+    IntVector2 deltaPos(eventData[P_DX].GetInt(), eventData[P_DY].GetInt());
     usingTouchInput_ = true;
     usingTouchInput_ = true;
 
 
-    ProcessMove(pos, MOUSEB_LEFT, 0, 0, true);
+    int touchId = TOUCHID_MASK(eventData[P_TOUCHID].GetInt());
+
+    ProcessMove(pos, deltaPos, touchId, 0, 0, true);
 }
 }
 
 
 void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
 void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
@@ -1339,15 +1544,10 @@ void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
     int key = eventData[P_KEY].GetInt();
     int key = eventData[P_KEY].GetInt();
 
 
     // Cancel UI dragging
     // Cancel UI dragging
-    if (key == KEY_ESC && dragElement_)
+    if (key == KEY_ESC && dragElementsCount_ > 0)
     {
     {
-        IntVector2 cursorPos;
-        bool cursorVisible;
-        GetCursorPositionAndVisible(cursorPos, cursorVisible);
-        SendDragOrHoverEvent(E_DRAGCANCEL, dragElement_, cursorPos);
+        ProcessDragCancel();
 
 
-        dragElement_.Reset();
-        dragBeginPending_ = false;
         return;
         return;
     }
     }
 
 
@@ -1426,7 +1626,7 @@ void UI::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
 {
 {
     // If have a cursor, and a drag is not going on, reset the cursor shape. Application logic that wants to apply
     // If have a cursor, and a drag is not going on, reset the cursor shape. Application logic that wants to apply
     // custom shapes can do it after this, but needs to do it each frame
     // custom shapes can do it after this, but needs to do it each frame
-    if (cursor_ && !dragElement_)
+    if (cursor_ && dragElementsCount_ == 0)
         cursor_->SetShape(CS_NORMAL);
         cursor_->SetShape(CS_NORMAL);
 }
 }
 
 
@@ -1471,6 +1671,74 @@ void UI::HandleDropFile(StringHash eventType, VariantMap& eventData)
     }
     }
 }
 }
 
 
+HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator UI::dragElementErase(HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i)
+{
+    dragElementsConfirmed_.Clear();
+
+    WeakPtr<UIElement> dragElement = i->first_;
+    DragData* dragData = i->second_;
+
+    if (!dragData->dragBeginPending)
+        dragConfirmedCount_ --;
+    i = dragElements_.Erase(i);
+    dragElementsCount_ --;
+
+    delete dragData;
+    return i;
+}
+
+void UI::ProcessDragCancel()
+{
+    // How to tell difference between drag cancel and new selection on multi-touch?
+    if (usingTouchInput_)
+        return;
+
+    IntVector2 cursorPos;
+    bool cursorVisible;
+    GetCursorPositionAndVisible(cursorPos, cursorVisible);
+
+    for (HashMap<WeakPtr<UIElement>, UI::DragData*>::Iterator i = dragElements_.Begin(); i != dragElements_.End();)
+    {
+        WeakPtr<UIElement> dragElement = i->first_;
+        UI::DragData* dragData = i->second_;
+
+        if (dragElement && dragElement->IsEnabled() && dragElement->IsVisible() && !dragData->dragBeginPending)
+        {
+            dragElement->OnDragCancel(dragElement->ScreenToElement(cursorPos), cursorPos, dragData->dragButtons, mouseButtons_, cursor_);
+            SendDragOrHoverEvent(E_DRAGCANCEL, dragElement, cursorPos, IntVector2::ZERO, dragData);
+            i = dragElementErase(i);
+        }
+        else
+            ++i;
+    }
+}
+
+IntVector2 UI::SumTouchPositions(UI::DragData* dragData, const IntVector2& oldSendPos)
+{
+    IntVector2 sendPos = oldSendPos;
+    if (usingTouchInput_)
+    {
+        int buttons = dragData->dragButtons;
+        dragData->sumPos = IntVector2::ZERO;
+        Input* input = GetSubsystem<Input>();
+        for (int i = 0; (1 << i) <= buttons; i++)
+        {
+            if ((1 << i) & buttons)
+            {
+                TouchState* ts = input->GetTouch(i);
+                if (!ts)
+                    break;
+                IntVector2 pos = ts->position_;
+                dragData->sumPos.x_ += pos.x_;
+                dragData->sumPos.y_ += pos.y_;
+            }
+        }
+        sendPos.x_ = dragData->sumPos.x_ / dragData->numDragButtons;
+        sendPos.y_ = dragData->sumPos.y_ / dragData->numDragButtons;
+    }
+    return sendPos;
+}
+
 void RegisterUILibrary(Context* context)
 void RegisterUILibrary(Context* context)
 {
 {
     Font::RegisterObject(context);
     Font::RegisterObject(context);

+ 49 - 15
Source/Engine/UI/UI.h

@@ -112,8 +112,12 @@ public:
     UIElement* GetFocusElement() const { return focusElement_; }
     UIElement* GetFocusElement() const { return focusElement_; }
     /// Return topmost enabled root-level non-modal element.
     /// Return topmost enabled root-level non-modal element.
     UIElement* GetFrontElement() const;
     UIElement* GetFrontElement() const;
-    /// Return currently dragged element.
-    UIElement* GetDragElement() const;
+    /// Return currently dragged elements.
+    const Vector<UIElement*> GetDragElements();
+    /// Return the number of currently dragged elements.
+    unsigned GetNumDragElements() const { return dragConfirmedCount_; }
+    /// Return the drag element at index.
+    UIElement* GetDragElement(unsigned index);
     /// Return clipboard text.
     /// Return clipboard text.
     const String& GetClipboardText() const;
     const String& GetClipboardText() const;
     /// Return UI element double click interval in seconds.
     /// Return UI element double click interval in seconds.
@@ -138,6 +142,27 @@ public:
     bool GetForceAutoHint() const { return forceAutoHint_; }
     bool GetForceAutoHint() const { return forceAutoHint_; }
     /// Return true when UI has modal element(s).
     /// Return true when UI has modal element(s).
     bool HasModalElement() const;
     bool HasModalElement() const;
+    /// Return whether a drag is in progress.
+    bool IsDragging() const { return dragConfirmedCount_ > 0; };
+    /// Return number of buttons pressed in a button mask.
+    unsigned GetNumDragButtons(unsigned buttons);
+
+    /// Data structure used to represent the drag data associated to a UIElement.
+    struct DragData
+    {
+        /// Which button combo initiated the drag.
+        int dragButtons;
+        /// How many buttons initiated the drag.
+        int numDragButtons;
+        /// Sum of all touch locations
+        IntVector2 sumPos;
+        /// Flag for a drag start event pending.
+        bool dragBeginPending;
+        /// Timer used to trigger drag begin event.
+        Timer dragBeginTimer;
+        /// Drag start position.
+        IntVector2 dragBeginSumPos;
+    };
 
 
 private:
 private:
     /// Initialize when screen mode initially set.
     /// Initialize when screen mode initially set.
@@ -167,11 +192,11 @@ private:
     /// Handle button or touch end.
     /// Handle button or touch end.
     void ProcessClickEnd(const IntVector2& cursorPos, int button, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible);
     void ProcessClickEnd(const IntVector2& cursorPos, int button, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible);
     /// Handle mouse or touch move.
     /// Handle mouse or touch move.
-    void ProcessMove(const IntVector2& cursorPos, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible);
+    void ProcessMove(const IntVector2& cursorPos, const IntVector2& cursorDeltaPos, int buttons, int qualifiers, Cursor* cursor, bool cursorVisible);
     /// Send a UI element drag or hover begin event.
     /// Send a UI element drag or hover begin event.
-    void SendDragOrHoverEvent(StringHash eventType, UIElement* element, const IntVector2& screenPos);
+    void SendDragOrHoverEvent(StringHash eventType, UIElement* element, const IntVector2& screenPos, const IntVector2& deltaPos, UI::DragData* dragData);
     /// Send a UI click or double click event.
     /// Send a UI click or double click event.
-    void SendClickEvent(StringHash eventType, UIElement* element, const IntVector2& pos, int button, int buttons, int qualifiers);
+    void SendClickEvent(StringHash eventType, UIElement* beginElement, UIElement* endElement, const IntVector2& pos, int button, int buttons, int qualifiers);
     /// Handle screen mode event.
     /// Handle screen mode event.
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
     /// Handle mouse button down event.
     /// Handle mouse button down event.
@@ -200,6 +225,12 @@ private:
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     /// Handle a file being drag-dropped into the application window.
     /// Handle a file being drag-dropped into the application window.
     void HandleDropFile(StringHash eventType, VariantMap& eventData);
     void HandleDropFile(StringHash eventType, VariantMap& eventData);
+    /// Remove drag data and return next iterator.
+    HashMap<WeakPtr<UIElement>, DragData*>::Iterator dragElementErase(HashMap<WeakPtr<UIElement>, DragData*>::Iterator dragElement);
+    /// Handle clean up on a drag cancel.
+    void ProcessDragCancel();
+    /// Sum touch positions and return the begin position ready to send.
+    IntVector2 SumTouchPositions(UI::DragData* dragData, const IntVector2& oldSendPos);
 
 
     /// Graphics subsystem.
     /// Graphics subsystem.
     WeakPtr<Graphics> graphics_;
     WeakPtr<Graphics> graphics_;
@@ -209,8 +240,6 @@ private:
     SharedPtr<UIElement> rootModalElement_;
     SharedPtr<UIElement> rootModalElement_;
     /// Cursor.
     /// Cursor.
     SharedPtr<Cursor> cursor_;
     SharedPtr<Cursor> cursor_;
-    /// UI element being dragged.
-    WeakPtr<UIElement> dragElement_;
     /// Currently focused element.
     /// Currently focused element.
     WeakPtr<UIElement> focusElement_;
     WeakPtr<UIElement> focusElement_;
     /// UI rendering batches.
     /// UI rendering batches.
@@ -259,26 +288,31 @@ private:
     bool useMutableGlyphs_;
     bool useMutableGlyphs_;
     /// Flag for forcing FreeType autohinting.
     /// Flag for forcing FreeType autohinting.
     bool forceAutoHint_;
     bool forceAutoHint_;
-    /// Flag for a drag start event pending.
-    bool dragBeginPending_;
     /// Non-modal batch size (used internally for rendering).
     /// Non-modal batch size (used internally for rendering).
     unsigned nonModalBatchSize_;
     unsigned nonModalBatchSize_;
     /// Timer used to trigger double click.
     /// Timer used to trigger double click.
     Timer clickTimer_;
     Timer clickTimer_;
-    /// Timer used to trigger drag begin event.
-    Timer dragBeginTimer_;
-    /// Drag start position.
-    IntVector2 dragBeginPos_;
-    /// Timer used for drag begin.
     /// UI element last clicked for tracking click end.
     /// UI element last clicked for tracking click end.
-    WeakPtr<UIElement> clickElement_;
+    /*WeakPtr<UIElement> clickElement_;*/
     /// UI element last clicked for tracking double clicks.
     /// UI element last clicked for tracking double clicks.
     WeakPtr<UIElement> doubleClickElement_;
     WeakPtr<UIElement> doubleClickElement_;
     /// Currently hovered elements.
     /// Currently hovered elements.
     HashMap<WeakPtr<UIElement>, bool> hoveredElements_;
     HashMap<WeakPtr<UIElement>, bool> hoveredElements_;
+    /// Currently dragged elements.
+    HashMap<WeakPtr<UIElement>, DragData*> dragElements_;
+    /// Number of elements in dragElements_.
+    int dragElementsCount_;
+    /// Number of elements in dragElements_ with dragPending = false.
+    int dragConfirmedCount_;
+    /// UI elements that are being touched with touch input.
+    HashMap<WeakPtr<UIElement>, int> touchDragElements_;
+    /// Confirmed drag elements cache.
+    Vector<UIElement*> dragElementsConfirmed_;
 };
 };
 
 
 /// Register UI library objects.
 /// Register UI library objects.
 void URHO3D_API RegisterUILibrary(Context* context);
 void URHO3D_API RegisterUILibrary(Context* context);
 
 
 }
 }
+
+#define TOUCHID_MASK(id) (1 << id)

+ 16 - 3
Source/Engine/UI/UIElement.cpp

@@ -148,7 +148,9 @@ UIElement::UIElement(Context* context) :
     sortOrderDirty_(false),
     sortOrderDirty_(false),
     colorGradient_(false),
     colorGradient_(false),
     traversalMode_(TM_BREADTH_FIRST),
     traversalMode_(TM_BREADTH_FIRST),
-    elementEventSender_(false)
+    elementEventSender_(false),
+    dragButtonCombo_(0),
+    dragButtonCount_(0)
 {
 {
     SetEnabled(false);
     SetEnabled(false);
 }
 }
@@ -498,15 +500,26 @@ void UIElement::OnDoubleClick(const IntVector2& position, const IntVector2& scre
 }
 }
 
 
 void UIElement::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 void UIElement::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
+{
+    dragButtonCombo_ = buttons;
+    UI* ui = GetSubsystem<UI>();
+    dragButtonCount_ = ui->GetNumDragButtons(dragButtonCombo_);
+}
+
+void UIElement::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor)
 {
 {
 }
 }
 
 
-void UIElement::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
+void UIElement::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor)
 {
 {
+    dragButtonCombo_ = 0;
+    dragButtonCount_ = 0;
 }
 }
 
 
-void UIElement::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
+void UIElement::OnDragCancel(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor)
 {
 {
+    dragButtonCombo_ = 0;
+    dragButtonCount_ = 0;
 }
 }
 
 
 bool UIElement::OnDragDropTest(UIElement* source)
 bool UIElement::OnDragDropTest(UIElement* source)

+ 12 - 2
Source/Engine/UI/UIElement.h

@@ -156,9 +156,11 @@ public:
     /// React to mouse drag begin.
     /// React to mouse drag begin.
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag motion.
     /// React to mouse drag motion.
-    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
+    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag end.
     /// React to mouse drag end.
-    virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor);
+    virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor);
+    /// React to a mouse drag cancel event (ie, when an extra button is pressed)
+    virtual void OnDragCancel(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor);
     /// React to drag and drop test. Return true to signal that the drop is acceptable.
     /// React to drag and drop test. Return true to signal that the drop is acceptable.
     virtual bool OnDragDropTest(UIElement* source);
     virtual bool OnDragDropTest(UIElement* source);
     /// React to drag and drop finish. Return true to signal that the drop was accepted.
     /// React to drag and drop finish. Return true to signal that the drop was accepted.
@@ -435,6 +437,10 @@ public:
     const Variant& GetVar(const StringHash& key) const;
     const Variant& GetVar(const StringHash& key) const;
     /// Return all user variables.
     /// Return all user variables.
     const VariantMap& GetVars() const { return vars_; }
     const VariantMap& GetVars() const { return vars_; }
+    /// Return the drag button combo if this element is being dragged.
+    const int GetDragButtonCombo() { return dragButtonCombo_; }
+    /// Return the number of buttons dragging this element.
+    const unsigned GetDragButtonCount() { return dragButtonCount_; }
 
 
     /// Convert screen coordinates to element coordinates.
     /// Convert screen coordinates to element coordinates.
     IntVector2 ScreenToElement(const IntVector2& screenPosition);
     IntVector2 ScreenToElement(const IntVector2& screenPosition);
@@ -559,6 +565,10 @@ protected:
     mutable bool positionDirty_;
     mutable bool positionDirty_;
     /// Applied style.
     /// Applied style.
     String appliedStyle_;
     String appliedStyle_;
+    /// Drag button combo.
+    int dragButtonCombo_;
+    /// Drag button count.
+    unsigned dragButtonCount_;
 
 
 private:
 private:
     /// Return child elements recursively.
     /// Return child elements recursively.

+ 11 - 0
Source/Engine/UI/UIEvents.h

@@ -209,6 +209,7 @@ EVENT(E_TEXTFINISHED, TextFinished)
 {
 {
     PARAM(P_ELEMENT, Element);              // UIElement pointer
     PARAM(P_ELEMENT, Element);              // UIElement pointer
     PARAM(P_TEXT, Text);                    // String
     PARAM(P_TEXT, Text);                    // String
+    PARAM(P_VALUE, Value);                 // Float
 }
 }
 
 
 /// Menu selected.
 /// Menu selected.
@@ -322,6 +323,8 @@ EVENT(E_DRAGBEGIN, DragBegin)
     PARAM(P_Y, Y);                          // int
     PARAM(P_Y, Y);                          // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
+    PARAM(P_BUTTONS, Buttons);              // int
+    PARAM(P_NUMBUTTONS, NumButtons);        // int
 }
 }
 
 
 /// Drag behavior of a UI Element when the input device has moved
 /// Drag behavior of a UI Element when the input device has moved
@@ -330,8 +333,12 @@ EVENT(E_DRAGMOVE, DragMove)
     PARAM(P_ELEMENT, Element);              // UIElement pointer
     PARAM(P_ELEMENT, Element);              // UIElement pointer
     PARAM(P_X, X);                          // int
     PARAM(P_X, X);                          // int
     PARAM(P_Y, Y);                          // int
     PARAM(P_Y, Y);                          // int
+    PARAM(P_DX, DX);                        // int
+    PARAM(P_DY, DY);                        // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
+    PARAM(P_BUTTONS, Buttons);              // int
+    PARAM(P_NUMBUTTONS, NumButtons);        // int
 }
 }
 
 
 /// Drag behavior of a UI Element has finished
 /// Drag behavior of a UI Element has finished
@@ -342,6 +349,8 @@ EVENT(E_DRAGEND, DragEnd)
     PARAM(P_Y, Y);                          // int
     PARAM(P_Y, Y);                          // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
+    PARAM(P_BUTTONS, Buttons);              // int
+    PARAM(P_NUMBUTTONS, NumButtons);        // int
 }
 }
 
 
 /// Drag of a UI Element was canceled by pressing ESC
 /// Drag of a UI Element was canceled by pressing ESC
@@ -352,6 +361,8 @@ EVENT(E_DRAGCANCEL, DragCancel)
     PARAM(P_Y, Y);                          // int
     PARAM(P_Y, Y);                          // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTX, ElementX);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
     PARAM(P_ELEMENTY, ElementY);            // int
+    PARAM(P_BUTTONS, Buttons);              // int
+    PARAM(P_NUMBUTTONS, NumButtons);        // int
 }
 }
 
 
 /// A file was drag-dropped into the application window. Includes also coordinates and UI element if applicable
 /// A file was drag-dropped into the application window. Includes also coordinates and UI element if applicable

+ 20 - 2
Source/Engine/UI/Window.cpp

@@ -116,6 +116,8 @@ void Window::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexDat
 
 
 void Window::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 void Window::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
 {
+    UIElement::OnHover(position, screenPosition, buttons, qualifiers, cursor);
+
     if (dragMode_ == DRAG_NONE)
     if (dragMode_ == DRAG_NONE)
     {
     {
         WindowDragMode mode = GetDragMode(position);
         WindowDragMode mode = GetDragMode(position);
@@ -127,6 +129,8 @@ void Window::OnHover(const IntVector2& position, const IntVector2& screenPositio
 
 
 void Window::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 void Window::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
 {
+    UIElement::OnDragBegin(position, screenPosition, buttons, qualifiers, cursor);
+
     if (buttons != MOUSEB_LEFT || !CheckAlignment())
     if (buttons != MOUSEB_LEFT || !CheckAlignment())
     {
     {
         dragMode_ = DRAG_NONE;
         dragMode_ = DRAG_NONE;
@@ -140,7 +144,7 @@ void Window::OnDragBegin(const IntVector2& position, const IntVector2& screenPos
     SetCursorShape(dragMode_, cursor);
     SetCursorShape(dragMode_, cursor);
 }
 }
 
 
-void Window::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
+void Window::OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor)
 {
 {
     if (dragMode_ == DRAG_NONE)
     if (dragMode_ == DRAG_NONE)
         return;
         return;
@@ -218,11 +222,25 @@ void Window::OnDragMove(const IntVector2& position, const IntVector2& screenPosi
     SetCursorShape(dragMode_, cursor);
     SetCursorShape(dragMode_, cursor);
 }
 }
 
 
-void Window::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor)
+void Window::OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor)
 {
 {
+    UIElement::OnDragEnd(position, screenPosition, dragButtons, buttons, cursor);
+
     dragMode_ = DRAG_NONE;
     dragMode_ = DRAG_NONE;
 }
 }
 
 
+void Window::OnDragCancel(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor)
+{
+    UIElement::OnDragCancel(position, screenPosition, dragButtons, buttons, cursor);
+
+    if (dragButtons == MOUSEB_LEFT)
+    {
+        dragMode_ = DRAG_NONE;
+        SetPosition(dragBeginPosition_);
+        SetSize(dragBeginSize_);
+    }
+}
+
 void Window::SetMovable(bool enable)
 void Window::SetMovable(bool enable)
 {
 {
     movable_ = enable;
     movable_ = enable;

+ 4 - 2
Source/Engine/UI/Window.h

@@ -63,9 +63,11 @@ public:
     /// React to mouse drag begin.
     /// React to mouse drag begin.
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag motion.
     /// React to mouse drag motion.
-    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
+    virtual void OnDragMove(const IntVector2& position, const IntVector2& screenPosition, const IntVector2& deltaPos, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag end.
     /// React to mouse drag end.
-    virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, Cursor* cursor);
+    virtual void OnDragEnd(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor);
+    /// React to mouse drag cancel.
+    virtual void OnDragCancel(const IntVector2& position, const IntVector2& screenPosition, int dragButtons, int buttons, Cursor* cursor);
 
 
     /// Set whether can be moved.
     /// Set whether can be moved.
     void SetMovable(bool enable);
     void SetMovable(bool enable);