Browse Source

Initial implementation of two extra mouse modes:
- MM_RELATIVE: sets mouse invisible, continues to work with UI if ui cursor is invisible to allow for continuous motion.
- MM_WRAP: wraps OS cursor around the window to allow for continuous motion.

hdunderscore 11 years ago
parent
commit
c293f170ed

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

@@ -50,7 +50,6 @@ void Start()
     input.mouseVisible = true;
     // Use system clipboard to allow transport of text in & out from the editor
     ui.useSystemClipboard = true;
-
 }
 
 void FirstFrame()
@@ -142,6 +141,8 @@ void LoadConfig()
         if (cameraElem.HasAttribute("mousewheelcameraposition")) mouseWheelCameraPosition = cameraElem.GetBool("mousewheelcameraposition");
         if (cameraElem.HasAttribute("viewportmode")) viewportMode = cameraElem.GetUInt("viewportmode");
         UpdateViewParameters();
+        if (cameraElem.HasAttribute("mouseorbitmode")) mouseOrbitMode = cameraElem.GetInt("mouseorbitmode");
+        UpdateViewParameters();
     }
 
     if (!objectElem.isNull)
@@ -258,6 +259,7 @@ void SaveConfig()
     cameraElem.SetBool("limitrotation", limitRotation);
     cameraElem.SetBool("mousewheelcameraposition", mouseWheelCameraPosition);
     cameraElem.SetUInt("viewportmode", viewportMode);
+    cameraElem.SetInt("mouseorbitmode", mouseOrbitMode);
 
     objectElem.SetFloat("newnodedistance", newNodeDistance);
     objectElem.SetFloat("movestep", moveStep);

+ 25 - 6
Bin/Data/Scripts/Editor/AttributeEditor.as

@@ -806,6 +806,8 @@ void CreateDragSlider(LineEdit@ parent)
 
     SubscribeToEvent(dragSld, "DragBegin", "LineDragBegin");
     SubscribeToEvent(dragSld, "DragMove", "LineDragMove");
+    SubscribeToEvent(dragSld, "DragEnd", "LineDragEnd");
+    SubscribeToEvent(dragSld, "DragCancel", "LineDragCancel");
 }
 
 void EditAttribute(StringHash eventType, VariantMap& eventData)
@@ -869,20 +871,21 @@ void LineDragBegin(StringHash eventType, VariantMap& eventData)
     LineEdit@ selectedNumEditor = label.parent;
     //not convenient way to trigger EditAttribute event
     selectedNumEditor.text = selectedNumEditor.text;
-}
-
+    selectedNumEditor.vars["DragBeginValue"] = selectedNumEditor.text;
 
+    SetMouseMode(true);
+}
 
 void LineDragMove(StringHash eventTypem, VariantMap& eventData)
 {
     UIElement@ label = eventData["Element"].GetPtr();
     LineEdit@ selectedNumEditor = label.parent;
-    
+
     int x = eventData["X"].GetInt();
     int posx = label.vars["posX"].GetInt();
-    float val = x - posx;
-    
-    float fieldVal = selectedNumEditor.text.ToFloat(); 
+    float val = input.mouseMoveX;
+
+    float fieldVal = selectedNumEditor.text.ToFloat();
     fieldVal += val/100;
     label.vars["posX"] = x;
     selectedNumEditor.text = fieldVal;
@@ -890,6 +893,22 @@ void LineDragMove(StringHash eventTypem, VariantMap& eventData)
     dragEditAttribute = true;
 }
 
+void LineDragEnd(StringHash eventType, VariantMap& eventData)
+{
+    dragEditAttribute = false;
+    SetMouseMode(false);
+}
+
+void LineDragCancel(StringHash eventType, VariantMap& eventData)
+{
+    UIElement@ label = eventData["Element"].GetPtr();
+
+    //prevent undo triggering
+    dragEditAttribute = true;
+    LineEdit@ selectedNumEditor = label.parent;
+    selectedNumEditor.text = selectedNumEditor.vars["DragBeginValue"].GetString();
+    SetMouseMode(false);
+}
 
 // Resource picker functionality
 const uint ACTION_PICK = 1;

+ 10 - 0
Bin/Data/Scripts/Editor/EditorSettings.as

@@ -40,6 +40,9 @@ void UpdateEditorSettingsDialog()
     CheckBox@ mouseWheelCameraPositionToggle = settingsDialog.GetChild("MouseWheelCameraPositionToggle", true);
     mouseWheelCameraPositionToggle.checked = mouseWheelCameraPosition;
 
+    DropDownList@ MouseOrbitEdit = settingsDialog.GetChild("MouseOrbitEdit", true);
+    MouseOrbitEdit.selection = mouseOrbitMode;
+
     LineEdit@ distanceEdit = settingsDialog.GetChild("DistanceEdit", true);
     distanceEdit.text = String(newNodeDistance);
 
@@ -106,6 +109,7 @@ void UpdateEditorSettingsDialog()
         SubscribeToEvent(speedEdit, "TextFinished", "EditCameraSpeed");
         SubscribeToEvent(limitRotationToggle, "Toggled", "EditLimitRotation");
         SubscribeToEvent(mouseWheelCameraPositionToggle, "Toggled", "EditMouseWheelCameraPosition");
+        SubscribeToEvent(MouseOrbitEdit, "ItemSelected", "EditMouseOrbitMode");
         SubscribeToEvent(distanceEdit, "TextChanged", "EditNewNodeDistance");
         SubscribeToEvent(distanceEdit, "TextFinished", "EditNewNodeDistance");
         SubscribeToEvent(moveStepEdit, "TextChanged", "EditMoveStep");
@@ -196,6 +200,12 @@ void EditMouseWheelCameraPosition(StringHash eventType, VariantMap& eventData)
     mouseWheelCameraPosition = edit.checked;
 }
 
+void EditMouseOrbitMode(StringHash eventType, VariantMap& eventData)
+{
+    DropDownList@ edit = eventData["Element"].GetPtr();
+    mouseOrbitMode = edit.selection;
+}
+
 void EditNewNodeDistance(StringHash eventType, VariantMap& eventData)
 {
     LineEdit@ edit = eventData["Element"].GetPtr();

+ 50 - 7
Bin/Data/Scripts/Editor/EditorView.as

@@ -343,6 +343,15 @@ bool octreeDebug = false;
 int pickMode = PICK_GEOMETRIES;
 bool orbiting = false;
 
+enum MouseOrbitMode
+{
+    ORBIT_RELATIVE = 0,
+    ORBIT_WRAP
+}
+
+bool toggledMouseLock = false;
+int mouseOrbitMode = ORBIT_RELATIVE;
+
 bool showGrid = true;
 bool grid2DMode = false;
 uint gridSize = 16;
@@ -506,7 +515,6 @@ BorderImage@ CreateViewportDragBorder(uint value, int posX, int posY, int sizeX,
     BorderImage@ border = BorderImage();
     viewportUI.AddChild(border);
     border.name = "border";
-    border.enabled = true;
     border.style = "ViewportBorder";
     border.vars["VIEWMODE"] = value;
     border.SetFixedSize(sizeX, sizeY); // relevant size gets set by viewport later
@@ -1086,10 +1094,48 @@ void UpdateViewports(float timeStep)
     }
 }
 
+void SetMouseMode(bool enable)
+{
+    if (enable)
+    {
+        if (mouseOrbitMode == ORBIT_RELATIVE)
+        {
+            input.mouseMode = MM_RELATIVE;
+            ui.cursor.visible = false;
+        }
+        else if (mouseOrbitMode == ORBIT_WRAP)
+            input.mouseMode = MM_WRAP;
+    }
+    else
+    {
+        input.mouseMode = MM_ABSOLUTE;
+        ui.cursor.visible = true;
+    }
+}
+
+void SetMouseLock()
+{
+    toggledMouseLock = true;
+    SetMouseMode(true);
+    FadeUI();
+}
+
+void ReleaseMouseLock()
+{
+    if (toggledMouseLock)
+    {
+        toggledMouseLock = false;
+        SetMouseMode(false);
+    }
+}
+
 void UpdateView(float timeStep)
 {
     if (ui.HasModalElement() || ui.focusElement !is null)
+    {
+        ReleaseMouseLock();
         return;
+    }
 
     // Move camera
     if (!input.keyDown[KEY_LCTRL])
@@ -1146,6 +1192,8 @@ void UpdateView(float timeStep)
     // Rotate/orbit/pan camera
     if (input.mouseButtonDown[MOUSEB_RIGHT] || input.mouseButtonDown[MOUSEB_MIDDLE])
     {
+        SetMouseLock();
+
         IntVector2 mouseMove = input.mouseMove;
         if (mouseMove.x != 0 || mouseMove.y != 0)
         {
@@ -1172,14 +1220,9 @@ void UpdateView(float timeStep)
                 }
             }
         }
-        else
-        {
-            FadeUI();
-            input.mouseGrabbed = true;
-        }
     }
     else
-        input.mouseGrabbed = false;
+        ReleaseMouseLock();
 
     if (orbiting && !input.mouseButtonDown[MOUSEB_MIDDLE])
         orbiting = false;

+ 27 - 0
Bin/Data/UI/EditorSettingsDialog.xml

@@ -86,6 +86,33 @@
                         <attribute name="Text" value="Mouse wheel camera position" />
                     </element>
                 </element>
+                <element style="ListRow">
+                    <attribute name="Layout Spacing" value="8" />
+                    <element type="Text">
+                        <attribute name="Text" value="Mouse orbit and drag behavior" />
+                    </element>
+                    <element type="DropDownList">
+                        <attribute name="Name" value="MouseOrbitEdit" />
+                        <attribute name="Min Size" value="100 0" />
+                        <attribute name="Max Size" value="100 2147483647" />
+                        <attribute name="Resize Popup" value="true" />
+                        <!-- Skip style processing as the purpose of below tags is to populate the content element -->
+                        <element type="Window" internal="true" popup="true" style="none">
+                            <element type="ListView" internal="true" style="none">
+                                <element type="BorderImage" internal="true" style="none">
+                                    <element internal="true" style="none">
+                                        <element type="Text" style="FileSelectorFilterText">
+                                            <attribute name="Text" value="Relative" />
+                                        </element>
+                                        <element type="Text" style="FileSelectorFilterText">
+                                            <attribute name="Text" value="Window Wrap" />
+                                        </element>
+                                    </element>
+                                </element>
+                            </element>
+                        </element>
+                    </element>
+                </element>
                 <element type="BorderImage" style="EditorDivider" />
                 <element style="ListRow">
                     <attribute name="Layout Spacing" value="20" />

+ 112 - 5
Source/Engine/Input/Input.cpp

@@ -107,6 +107,9 @@ Input::Input(Context* context) :
     toggleFullscreen_(true),
     mouseVisible_(false),
     mouseGrabbed_(false),
+    mouseMode_(MM_ABSOLUTE),
+    lastVisibleMousePosition_(MOUSE_POSITION_OFFSCREEN),
+    supressNextVisibleChangeEvent_(false),
     touchEmulation_(false),
     inputFocus_(false),
     minimized_(false),
@@ -156,6 +159,47 @@ void Input::Update()
     while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) > 0)
         HandleSDLEvent(&evt);
 
+    if (mouseVisible_ && mouseMode_ == MM_WRAP)
+    {
+        IntVector2 mpos;
+        SDL_GetMouseState(&mpos.x_, &mpos.y_);
+
+        int buffer = 5;
+        int width = graphics_->GetWidth() - buffer * 2;
+        int height = graphics_->GetHeight() - buffer * 2;
+
+        bool warp = false;
+        if (mpos.x_ < buffer)
+        {
+            warp = true;
+            mpos.x_ += width;
+        }
+
+        if (mpos.x_ > width)
+        {
+            warp = true;
+            mpos.x_ -= width;
+        }
+
+        if (mpos.y_ < buffer)
+        {
+            warp = true;
+            mpos.y_ += height;
+        }
+
+        if (mpos.y_ > height)
+        {
+            warp = true;
+            mpos.y_ -= height;
+        }
+
+        if (warp)
+        {
+            SetMousePosition(mpos);
+            SDL_FlushEvent(SDL_MOUSEMOTION);
+        }
+    }
+
     // Check for activation and inactivation from SDL window flags. Must nullcheck the window pointer because it may have
     // been closed due to input events
     SDL_Window* window = graphics_->GetImpl()->GetWindow();
@@ -251,19 +295,28 @@ void Input::SetMouseVisible(bool enable)
             {
                 SDL_ShowCursor(SDL_FALSE);
                 // Recenter the mouse cursor manually when hiding it to avoid erratic mouse move for one frame
+                lastVisibleMousePosition_ = GetMousePosition();
                 IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
                 SetMousePosition(center);
                 lastMousePosition_ = center;
             }
             else
+            {
                 SDL_ShowCursor(SDL_TRUE);
+                if (lastVisibleMousePosition_.x_ != MOUSE_POSITION_OFFSCREEN.x_ && lastVisibleMousePosition_.y_ != MOUSE_POSITION_OFFSCREEN.y_)
+                    SetMousePosition(lastVisibleMousePosition_);
+            }
         }
 
-        using namespace MouseVisibleChanged;
+        if (supressNextVisibleChangeEvent_)
+        {
+            supressNextVisibleChangeEvent_ = false;
+            using namespace MouseVisibleChanged;
 
-        VariantMap& eventData = GetEventDataMap();
-        eventData[P_VISIBLE] = mouseVisible_;
-        SendEvent(E_MOUSEVISIBLECHANGED, eventData);
+            VariantMap& eventData = GetEventDataMap();
+            eventData[P_VISIBLE] = mouseVisible_;
+            SendEvent(E_MOUSEVISIBLECHANGED, eventData);
+        }
     }
     #endif
 }
@@ -273,6 +326,58 @@ void Input::SetMouseGrabbed(bool grab)
     mouseGrabbed_ = grab;
 }
 
+void Input::SetMouseMode(MouseMode mode)
+{
+    if (mode != mouseMode_)
+    {
+        if (mouseMode_ == MM_RELATIVE)
+        {
+            // Todo: Use SDL_SetRelativeMouseMode()
+            supressNextVisibleChangeEvent_ = true;
+            SetMouseVisible(true);
+
+            // Send updated mouse position:
+            {
+                using namespace MouseMove;
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[P_X] = lastVisibleMousePosition_.x_;
+                eventData[P_Y] = lastVisibleMousePosition_.y_;
+                eventData[P_DX] = mouseMove_.x_;
+                eventData[P_DY] = mouseMove_.y_;
+                eventData[P_BUTTONS] = mouseButtonDown_;
+                eventData[P_QUALIFIERS] = GetQualifiers();
+                SendEvent(E_MOUSEMOVE, eventData);
+            }
+        }
+        else if (mouseMode_ == MM_WRAP)
+        {
+            SDL_Window* window = graphics_->GetImpl()->GetWindow();
+            SDL_SetWindowGrab(window, SDL_FALSE);
+        }
+
+        if (mode == MM_ABSOLUTE)
+            SetMouseGrabbed(false);
+        else
+        {
+            SetMouseGrabbed(true);
+
+            if (mode == MM_RELATIVE)
+            {
+                supressNextVisibleChangeEvent_ = true;
+                SetMouseVisible(false);
+            }
+            else if (mode == MM_WRAP)
+            {
+                // Todo: When SDL 2.0.4 is integrated, use SDL_CaptureMouse() and global mouse functions.
+                SDL_Window* window = graphics_->GetImpl()->GetWindow();
+                SDL_SetWindowGrab(window, SDL_TRUE);
+            }
+        }
+        mouseMode_ = mode;
+    }
+}
+
 void Input::SetToggleFullscreen(bool enable)
 {
     toggleFullscreen_ = enable;
@@ -884,6 +989,8 @@ void Input::LoseFocus()
     // Show the mouse cursor when inactive
     SDL_ShowCursor(SDL_TRUE);
 
+    SetMouseMode(MM_ABSOLUTE);
+
     SendInputFocusEvent();
 }
 
@@ -1126,7 +1233,7 @@ void Input::HandleSDLEvent(void* sdlEvent)
         break;
 
     case SDL_MOUSEMOTION:
-        if (mouseVisible_ && !touchEmulation_)
+        if ((mouseVisible_ || mouseMode_ == MM_RELATIVE) && !touchEmulation_)
         {
             mouseMove_.x_ += evt.motion.xrel;
             mouseMove_.y_ += evt.motion.yrel;

+ 23 - 1
Source/Engine/Input/Input.h

@@ -27,21 +27,33 @@
 #include "Mutex.h"
 #include "Object.h"
 
+#include "Cursor.h"
+
 namespace Urho3D
 {
 
+/// %Input Mouse Modes.
+enum MouseMode
+{
+    MM_ABSOLUTE = 0,
+    MM_RELATIVE,
+    MM_WRAP
+};
+
 class Deserializer;
 class Graphics;
 class Serializer;
 class UIElement;
 class XMLFile;
 
+const IntVector2 MOUSE_POSITION_OFFSCREEN = IntVector2(M_MIN_INT, M_MIN_INT);
+
 /// %Input state for a finger touch.
 struct TouchState
 {
     /// Return last touched UI element, used by scripting integration.
     UIElement* GetTouchedElement();
-    
+
     /// Touch (finger) ID.
     int touchID_;
     /// Position in screen coordinates.
@@ -126,6 +138,8 @@ public:
     void SetMouseVisible(bool enable);
     /// Set whether the mouse is currently being grabbed by an operation.
     void SetMouseGrabbed(bool grab);
+    /// Set the mouse mode.
+    void SetMouseMode(MouseMode mode);
     /// Add screen joystick.
     /** Return the joystick instance ID when successful or negative on error.
      *  If layout file is not given, use the default screen joystick layout.
@@ -223,6 +237,8 @@ public:
     bool IsMouseVisible() const { return mouseVisible_; }
     /// Return whether the mouse is currently being grabbed by an operation.
     bool IsMouseGrabbed() const { return mouseGrabbed_; }
+    /// Return the mouse mode.
+    MouseMode GetMouseMode() const { return mouseMode_; }
     /// Return whether application window has input focus.
     bool HasFocus() { return inputFocus_; }
     /// Return whether application window is minimized.
@@ -284,6 +300,8 @@ private:
     unsigned mouseButtonPress_;
     /// Last mouse position for calculating movement.
     IntVector2 lastMousePosition_;
+    /// Last mouse position before being set to not visible.
+    IntVector2 lastVisibleMousePosition_;
     /// Mouse movement since last frame.
     IntVector2 mouseMove_;
     /// Mouse wheel movement since last frame.
@@ -296,6 +314,8 @@ private:
     bool mouseVisible_;
     /// Flag to indicate the mouse is being grabbed by an operation. Subsystems like UI that uses mouse should temporarily ignore the mouse hover or click events.
     bool mouseGrabbed_;
+    /// Determines the mode of mouse behaviour.
+    MouseMode mouseMode_;
     /// Touch emulation mode flag.
     bool touchEmulation_;
     /// Input focus flag.
@@ -306,6 +326,8 @@ private:
     bool focusedThisFrame_;
     /// Next mouse move suppress flag.
     bool suppressNextMouseMove_;
+    /// Next visible event suppress flag.
+    bool supressNextVisibleChangeEvent_;
     /// Initialized flag.
     bool initialized_;
 };

+ 9 - 0
Source/Engine/LuaScript/pkgs/Input/Input.pkg

@@ -1,6 +1,13 @@
 $#include "File.h"
 $#include "Input.h"
 
+enum MouseMode
+{
+    MM_ABSOLUTE = 0,
+    MM_RELATIVE,
+    MM_WRAP
+};
+
 struct TouchState
 {
     UIElement* GetTouchedElement();
@@ -40,6 +47,7 @@ class Input : public Object
     void SetToggleFullscreen(bool enable);
     void SetMouseVisible(bool enable);
     void SetMouseGrabbed(bool grab);
+    void SetMouseMode(MouseMode mode);
     int AddScreenJoystick(XMLFile* layoutFile = 0, XMLFile* styleFile = 0);
     bool RemoveScreenJoystick(int id);
     void SetScreenJoystickVisible(int id, bool enable);
@@ -87,6 +95,7 @@ class Input : public Object
     bool GetTouchEmulation() const;
     bool IsMouseVisible() const;
     bool IsMouseGrabbed() const;
+    MouseMode GetMouseMode() const;
     bool HasFocus();
     bool IsMinimized() const;
 

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

@@ -460,6 +460,11 @@ static unsigned InputLoadGesturesVectorBuffer(VectorBuffer& buffer, Input* ptr)
 
 static void RegisterInput(asIScriptEngine* engine)
 {
+    engine->RegisterEnum("MouseMode");
+    engine->RegisterEnumValue("MouseMode", "MM_ABSOLUTE", MM_ABSOLUTE);
+    engine->RegisterEnumValue("MouseMode", "MM_RELATIVE", MM_RELATIVE);
+    engine->RegisterEnumValue("MouseMode", "MM_WRAP", MM_WRAP);
+
     engine->RegisterObjectType("TouchState", 0, asOBJ_REF);
     engine->RegisterObjectBehaviour("TouchState", asBEHAVE_ADDREF, "void f()", asFUNCTION(FakeAddRef), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("TouchState", asBEHAVE_RELEASE, "void f()", asFUNCTION(FakeReleaseRef), asCALL_CDECL_OBJLAST);
@@ -505,6 +510,8 @@ static void RegisterInput(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Input", "bool get_mouseVisible() const", asMETHOD(Input, IsMouseVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "void set_mouseGrabbed(bool)", asMETHOD(Input, SetMouseGrabbed), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool get_mouseGrabbed() const", asMETHOD(Input, IsMouseGrabbed), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "void set_mouseMode(MouseMode) const", asMETHOD(Input, SetMouseMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "MouseMode get_mouseMode() const", asMETHOD(Input, GetMouseMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "void set_screenJoystickVisible(int, bool)", asMETHOD(Input, SetScreenJoystickVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool get_screenJoystickVisible(int)", asMETHOD(Input, IsScreenJoystickVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "void set_screenKeyboardVisible(bool)", asMETHOD(Input, SetScreenKeyboardVisible), asCALL_THISCALL);

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

@@ -952,6 +952,8 @@ void UI::GetCursorPositionAndVisible(IntVector2& pos, bool& visible)
         pos = cursor_->GetPosition();
         visible = true;
     }
+    else if (GetSubsystem<Input>()->GetMouseMode() == MM_RELATIVE)
+        visible = true;
     else
     {
         Input* input = GetSubsystem<Input>();