Browse Source

Applied global UI double click patch from Chris Friesen. Removed individual double click handling in LineEdit & ListView elements.
Fixed RigidBody potentially moving too fast when its rotation is being forced.
Fixed NinjaSnowWar server freelook camera movement speed.

Lasse Öörni 12 years ago
parent
commit
fc841ce8d1

+ 5 - 5
Bin/Data/Scripts/NinjaSnowWar.as

@@ -1066,7 +1066,7 @@ void UpdateCamera()
     Vector3 pos = playerNode.position;
     Quaternion dir;
 
-    // Make controls seem more immediate by forcing the current mouse yaw into player ninja's Y-axis rotation
+    // Make controls seem more immediate by forcing the current mouse yaw to player ninja's Y-axis rotation
     if (playerNode.vars["Health"].GetInt() > 0)
         playerNode.rotation = Quaternion(0, playerControls.yaw, 0);
 
@@ -1098,13 +1098,13 @@ void UpdateFreelookCamera()
         speedMultiplier = 0.1;
 
     if (input.keyDown['W'])
-        gameCameraNode.TranslateRelative(Vector3(0, 0, 1000) * timeStep * speedMultiplier);
+        gameCameraNode.TranslateRelative(Vector3(0, 0, 10) * timeStep * speedMultiplier);
     if (input.keyDown['S'])
-        gameCameraNode.TranslateRelative(Vector3(0, 0, -1000) * timeStep * speedMultiplier);
+        gameCameraNode.TranslateRelative(Vector3(0, 0, -10) * timeStep * speedMultiplier);
     if (input.keyDown['A'])
-        gameCameraNode.TranslateRelative(Vector3(-1000, 0, 0) * timeStep * speedMultiplier);
+        gameCameraNode.TranslateRelative(Vector3(-10, 0, 0) * timeStep * speedMultiplier);
     if (input.keyDown['D'])
-        gameCameraNode.TranslateRelative(Vector3(1000, 0, 0) * timeStep * speedMultiplier);
+        gameCameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
     playerControls.yaw += mouseSensitivity * input.mouseMoveX;
     playerControls.pitch += mouseSensitivity * input.mouseMoveY;

+ 1 - 1
Engine/Graphics/View.cpp

@@ -1183,7 +1183,7 @@ void View::ExecuteRenderPathCommands()
     bool needResolve = !deferred_ && !renderTarget_ && graphics_->GetMultiSample() > 1 && screenBuffers_.Size();
     
     {
-        PROFILE(RenderCommands);
+        PROFILE(ExecuteRenderPath);
         
         unsigned lastCommandIndex = 0;
         for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)

+ 7 - 7
Engine/Physics/RigidBody.cpp

@@ -246,15 +246,17 @@ void RigidBody::SetRotation(Quaternion rotation)
 {
     if (body_)
     {
-        // Due to center of mass offset, we need to adjust position also
+        // Due to center of mass offset, we may need to adjust position also
         Vector3 oldPosition = GetPosition();
         btTransform& worldTrans = body_->getWorldTransform();
         worldTrans.setRotation(ToBtQuaternion(rotation));
-        worldTrans.setOrigin(ToBtVector3(oldPosition + rotation * centerOfMass_));
+        if (!centerOfMass_.Equals(Vector3::ZERO))
+            worldTrans.setOrigin(ToBtVector3(oldPosition + rotation * centerOfMass_));
 
         btTransform interpTrans = body_->getInterpolationWorldTransform();
         interpTrans.setRotation(worldTrans.getRotation());
-        interpTrans.setOrigin(worldTrans.getOrigin());
+        if (!centerOfMass_.Equals(Vector3::ZERO))
+            interpTrans.setOrigin(worldTrans.getOrigin());
         body_->setInterpolationWorldTransform(interpTrans);
         body_->updateInertiaTensor();
 
@@ -892,12 +894,10 @@ void RigidBody::OnMarkedDirty(Node* node)
 
         if (!newRotation.Equals(lastRotation_))
         {
-            // Due to possible center of mass offset, if rotation changes, update position also
             lastRotation_ = newRotation;
-            lastPosition_ = newPosition;
-            SetTransform(newPosition, newRotation);
+            SetRotation(newRotation);
         }
-        else if (!newPosition.Equals(lastPosition_))
+        if (!newPosition.Equals(lastPosition_))
         {
             lastPosition_ = newPosition;
             SetPosition(newPosition);

+ 5 - 5
Engine/Script/UIAPI.cpp

@@ -308,8 +308,6 @@ 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_doubleClickInterval(float)", asMETHOD(ListView, SetDoubleClickInterval), asCALL_THISCALL);
-    engine->RegisterObjectMethod("ListView", "float get_doubleClickInterval() const", asMETHOD(ListView, GetDoubleClickInterval), asCALL_THISCALL);
 }
 
 static void RegisterText(asIScriptEngine* engine)
@@ -395,8 +393,6 @@ static void RegisterLineEdit(asIScriptEngine* engine)
     engine->RegisterObjectMethod("LineEdit", "bool get_textCopyable() const", asMETHOD(LineEdit, IsTextCopyable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "Text@+ get_textElement() const", asMETHOD(LineEdit, GetTextElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "BorderImage@+ get_cursor() const", asMETHOD(LineEdit, GetCursor), asCALL_THISCALL);
-    engine->RegisterObjectMethod("LineEdit", "void set_doubleClickInterval(float)", asMETHOD(LineEdit, SetDoubleClickInterval), asCALL_THISCALL);
-    engine->RegisterObjectMethod("LineEdit", "float get_doubleClickInterval() const", asMETHOD(LineEdit, GetDoubleClickInterval), asCALL_THISCALL);
 }
 
 static void RegisterMenu(asIScriptEngine* engine)
@@ -583,9 +579,13 @@ static void RegisterUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "UIElement@+ get_frontElement() const", asMETHOD(UI, GetFrontElement), 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->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
+    engine->RegisterObjectMethod("UI", "void set_clipBoardText(const String&in)", asMETHOD(UI, SetClipBoardText), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "const String& get_clipBoardText() const", asMETHOD(UI, GetClipBoardText), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "void set_doubleClickInterval(float)", asMETHOD(UI, SetDoubleClickInterval), asCALL_THISCALL);
+    engine->RegisterObjectMethod("UI", "float get_doubleClickInterval() const", asMETHOD(UI, GetDoubleClickInterval), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "void set_nonFocusedMouseWheel(bool)", asMETHOD(UI, SetNonFocusedMouseWheel), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "bool get_nonFocusedMouseWheel() const", asMETHOD(UI, IsNonFocusedMouseWheel), asCALL_THISCALL);
+    engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);
 }
 
 void RegisterUIAPI(asIScriptEngine* engine)

+ 2 - 2
Engine/UI/FileSelector.cpp

@@ -375,7 +375,7 @@ bool FileSelector::EnterFile()
 
     if (fileEntries_[index].directory_)
     {
-        // If a directory doubleclicked, enter it. Recognize . and .. as a special case
+        // If a directory double clicked, enter it. Recognize . and .. as a special case
         const String& newPath = fileEntries_[index].name_;
         if ((newPath != ".") &&  (newPath != ".."))
             SetPath(path_ + newPath);
@@ -389,7 +389,7 @@ bool FileSelector::EnterFile()
     }
     else
     {
-        // Doubleclicking a file is the same as pressing OK
+        // Double clicking a file is the same as pressing OK
         if (!directoryMode_)
         {
             using namespace FileSelected;

+ 10 - 22
Engine/UI/LineEdit.cpp

@@ -47,8 +47,7 @@ LineEdit::LineEdit(Context* context) :
     echoCharacter_(0),
     cursorMovable_(true),
     textSelectable_(true),
-    textCopyable_(true),
-    doubleClickInterval_(500)
+    textCopyable_(true)
 {
     clipChildren_ = true;
     enabled_ = true;
@@ -83,7 +82,6 @@ void LineEdit::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(LineEdit, VAR_BOOL, "Is Text Copyable", IsTextCopyable, SetTextCopyable, bool, true, AM_FILE);
     ACCESSOR_ATTRIBUTE(LineEdit, VAR_FLOAT, "Cursor Blink Rate", GetCursorBlinkRate, SetCursorBlinkRate, float, 1.0f, AM_FILE);
     ATTRIBUTE(LineEdit, VAR_INT, "Echo Character", echoCharacter_, 0, AM_FILE);
-    ACCESSOR_ATTRIBUTE(LineEdit, VAR_FLOAT, "Double Click Interval", GetDoubleClickInterval, SetDoubleClickInterval, float, 0.5f, AM_FILE);
 }
 
 void LineEdit::ApplyAttributes()
@@ -118,20 +116,20 @@ void LineEdit::OnClick(const IntVector2& position, const IntVector2& screenPosit
 {
     if (buttons & MOUSEB_LEFT && cursorMovable_)
     {
-        if (doubleClickTimer_.GetMSec(true) < doubleClickInterval_)
-            text_->SetSelection(0);
-        else
+        unsigned pos = GetCharIndex(position);
+        if (pos != M_MAX_UNSIGNED)
         {
-            unsigned pos = GetCharIndex(position);
-            if (pos != M_MAX_UNSIGNED)
-            {
-                SetCursorPosition(pos);
-                text_->ClearSelection();
-            }
+            SetCursorPosition(pos);
+            text_->ClearSelection();
         }
     }
 }
 
+void LineEdit::OnDoubleClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
+{
+    text_->SetSelection(0);
+}
+
 void LineEdit::OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
     dragBeginCursor_ = GetCharIndex(position);
@@ -497,16 +495,6 @@ void LineEdit::SetTextCopyable(bool enable)
     textCopyable_ = enable;
 }
 
-void LineEdit::SetDoubleClickInterval(float interval)
-{
-    doubleClickInterval_ = Max((int)(interval * 1000.0f), 0);
-}
-
-float LineEdit::GetDoubleClickInterval() const
-{
-    return (float)doubleClickInterval_ / 1000.0f;
-}
-
 bool LineEdit::FilterImplicitAttributes(XMLElement& dest) const
 {
     if (!BorderImage::FilterImplicitAttributes(dest))

+ 4 - 10
Engine/UI/LineEdit.h

@@ -50,6 +50,8 @@ public:
 
     /// React to mouse click.
     virtual void OnClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
+    /// React to mouse doubleclick.
+    virtual void OnDoubleClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag begin.
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag motion.
@@ -79,9 +81,7 @@ public:
     void SetTextSelectable(bool enable);
     /// Set whether copy-paste operations are allowed, default true.
     void SetTextCopyable(bool enable);
-    /// Set text selection doubleclick interval in seconds.
-    void SetDoubleClickInterval(float interval);
-
+    
     /// Return text.
     const String& GetText() const { return line_; }
     /// Return cursor position.
@@ -102,9 +102,7 @@ public:
     Text* GetTextElement() const { return text_; }
     /// Return cursor element.
     BorderImage* GetCursor() const { return cursor_; }
-    /// Return text selection doubleclick interval in seconds.
-    float GetDoubleClickInterval() const;
-
+    
 protected:
     /// Filter implicit attributes in serialization process.
     virtual bool FilterImplicitAttributes(XMLElement& dest) const;
@@ -143,10 +141,6 @@ protected:
     bool textSelectable_;
     /// Copy-paste enable flag.
     bool textCopyable_;
-    /// Doubleclick interval.
-    unsigned doubleClickInterval_;
-    /// Doubleclick timer.
-    Timer doubleClickTimer_;
 
 private:
     /// Handle being focused.

+ 23 - 27
Engine/UI/ListView.cpp

@@ -162,9 +162,7 @@ ListView::ListView(Context* context) :
     multiselect_(false),
     hierarchyMode_(true),    // Init to true here so that the setter below takes effect
     baseIndent_(0),
-    clearSelectionOnDefocus_(false),
-    doubleClickInterval_(500),
-    lastClickedItem_(M_MAX_UNSIGNED)
+    clearSelectionOnDefocus_(false)
 {
     resizeContentWidth_ = true;
 
@@ -172,6 +170,7 @@ ListView::ListView(Context* context) :
     SetHierarchyMode(false);
 
     SubscribeToEvent(E_UIMOUSECLICK, HANDLER(ListView, HandleUIMouseClick));
+    SubscribeToEvent(E_UIMOUSEDOUBLECLICK, HANDLER(ListView, HandleUIMouseDoubleClick));
     SubscribeToEvent(E_FOCUSCHANGED, HANDLER(ListView, HandleFocusChanged));
 }
 
@@ -190,7 +189,6 @@ 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_FLOAT, "Double Click Interval", GetDoubleClickInterval, SetDoubleClickInterval, float, 0.5f, AM_FILE);
 }
 
 void ListView::OnKey(int key, int buttons, int qualifiers)
@@ -716,11 +714,6 @@ void ListView::SetClearSelectionOnDefocus(bool enable)
     }
 }
 
-void ListView::SetDoubleClickInterval(float interval)
-{
-    doubleClickInterval_ = Max((int)(interval * 1000.0f), 0);
-}
-
 void ListView::Expand(unsigned index, bool enable, bool recursive)
 {
     if (!hierarchyMode_)
@@ -840,11 +833,6 @@ bool ListView::IsExpanded(unsigned index) const
     return GetItemExpanded(contentElement_->GetChild(index));
 }
 
-float ListView::GetDoubleClickInterval() const
-{
-    return (float)doubleClickInterval_ / 1000.0f;
-}
-
 bool ListView::FilterImplicitAttributes(XMLElement& dest) const
 {
     if (!ScrollView::FilterImplicitAttributes(dest))
@@ -940,14 +928,9 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
     {
         if (element == GetItem(i))
         {
-            // Check doubleclick
-            bool isDoubleClick = false;
+            // Single selection
             if (!multiselect_ || !qualifiers)
-            {
-                if (!(isDoubleClick = doubleClickTimer_.GetMSec(true) < doubleClickInterval_ && lastClickedItem_ == i))
-                    lastClickedItem_ = i;
                 SetSelection(i);
-            }
 
             // Check multiselect with shift & ctrl
             if (multiselect_)
@@ -996,19 +979,32 @@ void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
                     ToggleSelection(i);
             }
 
-            if (isDoubleClick)
-            {
-                VariantMap eventData;
-                eventData[ItemDoubleClicked::P_ELEMENT] = (void*)this;
-                eventData[ItemDoubleClicked::P_SELECTION] = i;
-                SendEvent(E_ITEMDOUBLECLICKED, eventData);
-            }
+            return;
+        }
+    }
+}
 
+void ListView::HandleUIMouseDoubleClick(StringHash eventType, VariantMap& eventData)
+{
+    if (eventData[UIMouseClick::P_BUTTON].GetInt() != MOUSEB_LEFT)
+        return;
+    UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());
+
+    unsigned numItems = GetNumItems();
+    for (unsigned i = 0; i < numItems; ++i)
+    {
+        if (element == GetItem(i))
+        {
+            VariantMap eventData;
+            eventData[ItemDoubleClicked::P_ELEMENT] = (void*)this;
+            eventData[ItemDoubleClicked::P_SELECTION] = i;
+            SendEvent(E_ITEMDOUBLECLICKED, eventData);
             return;
         }
     }
 }
 
+
 void ListView::HandleFocusChanged(StringHash eventType, VariantMap& eventData)
 {
     using namespace FocusChanged;

+ 2 - 10
Engine/UI/ListView.h

@@ -94,8 +94,6 @@ public:
     void SetBaseIndent(int baseIndent);
     /// Enable clearing of selection on defocus.
     void SetClearSelectionOnDefocus(bool enable);
-    /// Set item doubleclick interval in seconds.
-    void SetDoubleClickInterval(float interval);
 
     /// Expand item at index. Only has effect in hierarchy mode.
     void Expand(unsigned index, bool enable, bool recursive = false);
@@ -132,8 +130,6 @@ public:
     bool GetHierarchyMode() const { return hierarchyMode_; }
     /// Return base indent.
     int GetBaseIndent() const { return baseIndent_; }
-    /// Return item doubleclick interval in seconds.
-    float GetDoubleClickInterval() const;
 
 protected:
     /// Filter implicit attributes in serialization process.
@@ -159,16 +155,12 @@ protected:
     SharedPtr<UIElement> overlayContainer_;
     /// Clear selection on defocus flag.
     bool clearSelectionOnDefocus_;
-    /// Doubleclick interval.
-    unsigned doubleClickInterval_;
-    /// Doubleclick timer.
-    Timer doubleClickTimer_;
-    /// Last clicked item.
-    unsigned lastClickedItem_;
 
 private:
     /// Handle global UI mouseclick to check for selection change.
     void HandleUIMouseClick(StringHash eventType, VariantMap& eventData);
+    /// Handle global UI mouse doubleclick.
+    void HandleUIMouseDoubleClick(StringHash eventType, VariantMap& eventData);
     /// Handle global focus change to check whether an invisible item was focused.
     void HandleFocusChanged(StringHash eventType, VariantMap& eventData);
     /// Handle being defocused.

+ 36 - 1
Engine/UI/UI.cpp

@@ -62,6 +62,8 @@ const ShortStringHash VAR_ORIGINAL_PARENT("OriginalParent");
 const ShortStringHash VAR_ORIGINAL_CHILD_INDEX("OriginalChildIndex");
 const ShortStringHash VAR_PARENT_CHANGED("ParentChanged");
 
+const float DEFAULT_DOUBLECLICK_INTERVAL = 0.5f;
+
 const char* UI_CATEGORY = "UI";
 
 UI::UI(Context* context) :
@@ -70,6 +72,7 @@ UI::UI(Context* context) :
     rootModalElement_(new UIElement(context)),
     mouseButtons_(0),
     qualifiers_(0),
+    doubleClickInterval_(DEFAULT_DOUBLECLICK_INTERVAL),
     initialized_(false),
     usingTouchInput_(false),
     #ifdef WIN32
@@ -81,7 +84,8 @@ UI::UI(Context* context) :
 {
     rootElement_->SetTraversalMode(TM_DEPTH_FIRST);
     rootModalElement_->SetTraversalMode(TM_DEPTH_FIRST);
-
+    clickTimer_ = new Timer();
+    
     // Register UI library object factories
     RegisterUILibrary(context_);
 
@@ -102,6 +106,7 @@ UI::UI(Context* context) :
 
 UI::~UI()
 {
+    delete clickTimer_;
 }
 
 void UI::SetCursor(Cursor* cursor)
@@ -451,6 +456,11 @@ void UI::SetClipBoardText(const String& text)
     clipBoard_ = text;
 }
 
+void UI::SetDoubleClickInterval(float interval)
+{
+    doubleClickInterval_ = Max(interval, 0.0f);
+}
+
 void UI::SetNonFocusedMouseWheel(bool nonFocusedMouseWheel)
 {
     nonFocusedMouseWheel_ = nonFocusedMouseWheel;
@@ -849,6 +859,30 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
                 element->OnDragBegin(element->ScreenToElement(cursorPos), cursorPos, mouseButtons_, qualifiers_, cursor_);
                 SendDragEvent(E_DRAGBEGIN, element, cursorPos);
             }
+
+            // Fire double click event if element matches and is in time
+            if (doubleClickElement_ && element == doubleClickElement_ && clickTimer_->GetMSec(true) <
+                (unsigned)(doubleClickInterval_ * 1000) && lastMouseButtons_ == mouseButtons_)
+            {
+                element->OnDoubleClick(element->ScreenToElement(cursorPos), cursorPos, mouseButtons_, qualifiers_, cursor_);
+                doubleClickElement_.Reset();
+                
+                using namespace UIMouseDoubleClick;
+                
+                VariantMap eventData;
+                eventData[P_ELEMENT] = (void*)element.Get();
+                eventData[P_X] = cursorPos.x_;
+                eventData[P_Y] = cursorPos.y_;
+                eventData[P_BUTTON] = button;
+                eventData[P_BUTTONS] = mouseButtons_;
+                eventData[P_QUALIFIERS] = qualifiers_;
+                element->SendEvent(E_UIMOUSEDOUBLECLICK, eventData);
+            } 
+            else 
+            {
+                doubleClickElement_ = element;
+                clickTimer_->Reset();
+            }
         }
         else
         {
@@ -864,6 +898,7 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
         eventData[UIMouseClick::P_BUTTONS] = mouseButtons_;
         eventData[UIMouseClick::P_QUALIFIERS] = qualifiers_;
         SendEvent(E_UIMOUSECLICK, eventData);
+        lastMouseButtons_ = mouseButtons_;
     }
 }
 

+ 12 - 0
Engine/UI/UI.h

@@ -76,6 +76,8 @@ public:
     bool SaveLayout(Serializer& dest, UIElement* element);
     /// Set clipboard text.
     void SetClipBoardText(const String& text);
+    /// Set UI element double click interval in seconds.
+    void SetDoubleClickInterval(float interval);
     /// Set mouse wheel handling flag.
     void SetNonFocusedMouseWheel(bool nonFocusedMouseWheel);
 
@@ -97,6 +99,8 @@ public:
     IntVector2 GetCursorPosition() const;
     /// Return clipboard text.
     const String& GetClipBoardText() const { return clipBoard_; }
+    /// Return UI element double click interval in seconds.
+    float GetDoubleClickInterval() const { return doubleClickInterval_; }
     /// Return mouse wheel handling flag.
     bool IsNonFocusedMouseWheel() const { return nonFocusedMouseWheel_; }
     /// Return true when UI has modal element(s).
@@ -200,6 +204,14 @@ private:
     bool nonFocusedMouseWheel_;
     /// Non-modal batch size (used internally for rendering).
     unsigned nonModalBatchSize_;
+    /// Timer used to trigger double click.
+    Timer* clickTimer_;
+    /// UI element last clicked for tracking double clicks.
+    WeakPtr<UIElement> doubleClickElement_;
+    /// Last mouse button pressed.
+    int lastMouseButtons_;
+    /// Seconds between clicks to register a double click.
+    float doubleClickInterval_;
 };
 
 /// Register UI library objects.

+ 4 - 0
Engine/UI/UIElement.cpp

@@ -486,6 +486,10 @@ void UIElement::OnClick(const IntVector2& position, const IntVector2& screenPosi
 {
 }
 
+void UIElement::OnDoubleClick(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)
 {
 }

+ 2 - 0
Engine/UI/UIElement.h

@@ -148,6 +148,8 @@ public:
     virtual void OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse click.
     virtual void OnClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
+    /// React to double mouse click.
+    virtual void OnDoubleClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag begin.
     virtual void OnDragBegin(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     /// React to mouse drag motion.

+ 12 - 1
Engine/UI/UIEvents.h

@@ -38,6 +38,17 @@ EVENT(E_UIMOUSECLICK, UIMouseClick)
     PARAM(P_QUALIFIERS, Qualifiers);        // int
 }
 
+/// Mouse double click in the UI.
+EVENT(E_UIMOUSEDOUBLECLICK, UIMouseDoubleClick)
+{
+    PARAM(P_ELEMENT, Element);              // UIElement pointer
+    PARAM(P_X, X);                          // int
+    PARAM(P_Y, Y);                          // int
+    PARAM(P_BUTTON, Button);                // int
+    PARAM(P_BUTTONS, Buttons);              // int
+    PARAM(P_QUALIFIERS, Qualifiers);        // int
+}
+
 /// Drag and drop test.
 EVENT(E_DRAGDROPTEST, DragDropTest)
 {
@@ -205,7 +216,7 @@ EVENT(E_SELECTIONCHANGED, SelectionChanged)
     PARAM(P_ELEMENT, Element);              // UIElement pointer
 }
 
-/// Listview item doubleclicked.
+/// Listview item double clicked.
 EVENT(E_ITEMDOUBLECLICKED, ItemDoubleClicked)
 {
     PARAM(P_ELEMENT, Element);              // UIElement pointer

+ 2 - 4
Extras/LuaScript/pkgs/UI/LineEdit.pkg

@@ -15,7 +15,7 @@ class LineEdit : public BorderImage
     void SetCursorMovable(bool enable);
     void SetTextSelectable(bool enable);
     void SetTextCopyable(bool enable);
-    void SetDoubleClickInterval(float interval);
+
 
     const String& GetText() const;
     unsigned GetCursorPosition() const;
@@ -27,8 +27,7 @@ class LineEdit : public BorderImage
     bool IsTextCopyable() const;
     Text* GetTextElement() const;
     BorderImage* GetCursor() const;
-    float GetDoubleClickInterval() const;
-    
+
     tolua_property__get_set String& text;
     tolua_property__get_set unsigned cursorPosition;
     tolua_property__get_set float cursorBlinkRate;
@@ -39,5 +38,4 @@ class LineEdit : public BorderImage
     tolua_property__is_set bool textCopyable;
     tolua_readonly tolua_property__get_set Text* textElement;
     tolua_readonly tolua_property__get_set BorderImage* cursor;
-    tolua_property__get_set float doubleClickInterval;
 };

+ 1 - 4
Extras/LuaScript/pkgs/UI/ListView.pkg

@@ -31,7 +31,6 @@ class ListView : public ScrollView
     void SetHierarchyMode(bool enable);
     void SetBaseIndent(int baseIndent);
     void SetClearSelectionOnDefocus(bool enable);
-    void SetDoubleClickInterval(float interval);
 
     void Expand(unsigned index, bool enable, bool recursive = false);
     void ToggleExpand(unsigned index, bool recursive = false);
@@ -48,8 +47,7 @@ class ListView : public ScrollView
     bool GetClearSelectionOnDefocus() const;
     bool GetHierarchyMode() const;
     int GetBaseIndent() const;
-    float GetDoubleClickInterval() const;
-    
+
     tolua_readonly tolua_property__get_set unsigned numItems;
     tolua_property__get_set unsigned selection;
     tolua_readonly tolua_property__get_set UIElement* selectedItem;
@@ -58,5 +56,4 @@ class ListView : public ScrollView
     tolua_property__get_set bool clearSelectionOnDefocus;
     tolua_property__get_set bool hierarchyMode;
     tolua_property__get_set int baseIndent;
-    tolua_property__get_set float doubleClickInterval;
 };

+ 4 - 1
Extras/LuaScript/pkgs/UI/UI.pkg

@@ -15,8 +15,9 @@ class UI : public Object
     void SetClipBoardText(const String& text);
     void SetClipBoardText(const char* text);
     
+    void SetDoubleClickInterval(float interval);
     void SetNonFocusedMouseWheel(bool nonFocusedMouseWheel);
-    
+
     UIElement* GetRoot() const;
     UIElement* GetRootModalElement() const;
     Cursor* GetCursor() const;
@@ -28,6 +29,7 @@ class UI : public Object
     UIElement* GetFrontElement() const;
     IntVector2 GetCursorPosition() const;
     const String& GetClipBoardText() const;
+    float GetDoubleClickInterval() const;
     bool IsNonFocusedMouseWheel() const;
     bool HasModalElement() const;
     
@@ -38,6 +40,7 @@ class UI : public Object
     tolua_readonly tolua_property__get_set UIElement* frontElement;
     tolua_readonly tolua_property__get_set IntVector2 cursorPosition;
     tolua_property__get_set String& clipBoardText;
+    tolua_property__get_set float doubleClickInterval;
     tolua_readonly tolua_property__is_set bool nonFocusedMouseWheel;
     tolua_readonly tolua_property__has_set bool modalElement;
 };