From 3f6bf155540bbaa76fd1dc6384cda30c576ef709 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 25 Jul 2024 21:29:38 +0300 Subject: [PATCH] Implement relative mouse mode (raw input) for SDL platform --- Source/Editor/GUI/Input/ValueBox.cs | 31 +++++++++++++++ .../Editor/Managed/ManagedEditor.Internal.cpp | 3 ++ Source/Editor/Viewport/EditorViewport.cs | 38 ++++++++++++++++++- Source/Engine/Engine/Screen.cpp | 23 +++++++++++ Source/Engine/Input/Input.cpp | 22 +++++++++++ Source/Engine/Input/Input.h | 5 +++ Source/Engine/Input/InputDevice.h | 6 +++ Source/Engine/Input/Mouse.h | 27 +++++++++++++ Source/Engine/Platform/Base/WindowBase.cpp | 7 ++++ Source/Engine/Platform/Base/WindowBase.h | 6 +++ Source/Engine/Platform/Linux/LinuxWindow.cpp | 2 +- Source/Engine/Platform/Mac/MacWindow.cpp | 6 +-- Source/Engine/Platform/SDL/SDLInput.cpp | 32 +++++++++++++--- Source/Engine/Platform/SDL/SDLWindow.cpp | 10 +++++ Source/Engine/Platform/Window.cs | 35 ++++++++++++----- .../Engine/Platform/Windows/WindowsInput.cpp | 25 ++++++++++-- Source/Engine/UI/GUI/Control.cs | 9 +++++ Source/Engine/UI/GUI/WindowRootControl.cs | 12 ++++++ 18 files changed, 276 insertions(+), 23 deletions(-) diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 674ee0697..13e8ef5ce 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input return base.OnMouseDown(location, button); } +#if !PLATFORM_SDL /// public override void OnMouseMove(Float2 location) { @@ -292,6 +293,36 @@ namespace FlaxEditor.GUI.Input base.OnMouseMove(location); } +#else + + /// + public override void OnMouseMoveRelative(Float2 mouseMotion) + { + var location = Root.TrackingMouseOffset; + if (_isSliding) + { + // Update sliding + ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed); + return; + } + + // Update cursor type so user knows they can slide value + if (CanUseSliding && SlideRect.Contains(location) && !_isSliding) + { + Cursor = CursorType.SizeWE; + _cursorChanged = true; + } + else if (_cursorChanged && !_isSliding) + { + Cursor = CursorType.Default; + _cursorChanged = false; + } + + base.OnMouseMoveRelative(mouseMotion); + } + +#endif + /// public override bool OnMouseUp(Float2 location, MouseButton button) { diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index de7532921..9ae0b71e9 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -435,6 +435,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa case InputDevice::EventType::MouseMove: window->OnMouseMove(window->ScreenToClient(e.MouseData.Position)); break; + case InputDevice::EventType::MouseMoveRelative: + window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative); + break; case InputDevice::EventType::MouseLeave: window->OnMouseLeave(); break; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index eaf243726..0df9f8aa1 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -158,18 +158,22 @@ namespace FlaxEditor.Viewport private float _movementSpeed; private float _minMovementSpeed; private float _maxMovementSpeed; +#if !PLATFORM_SDL private float _mouseAccelerationScale; private bool _useMouseFiltering; private bool _useMouseAcceleration; +#endif // Input internal bool _disableInputUpdate; private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown; - private int _deltaFilteringStep; private Float2 _startPos; +#if !PLATFORM_SDL private Float2 _mouseDeltaLast; + private int _deltaFilteringStep; private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames]; +#endif /// /// The previous input (from the previous update). @@ -522,10 +526,11 @@ namespace FlaxEditor.Viewport : base(task) { _editor = Editor.Instance; - +#if !PLATFORM_SDL _mouseAccelerationScale = 0.1f; _useMouseFiltering = false; _useMouseAcceleration = false; +#endif _camera = camera; if (_camera != null) _camera.Viewport = this; @@ -1460,7 +1465,9 @@ namespace FlaxEditor.Viewport // Hide cursor and start tracking mouse movement win.StartTrackingMouse(false); win.Cursor = CursorType.Hidden; + win.MouseMoveRelative += OnMouseMoveRelative; +#if !PLATFORM_SDL // Center mouse position if it's too close to the edge var size = Size; var center = Float2.Round(size * 0.5f); @@ -1469,6 +1476,7 @@ namespace FlaxEditor.Viewport _viewMousePos = center; win.MousePosition = PointToWindow(_viewMousePos); } +#endif } /// @@ -1480,6 +1488,7 @@ namespace FlaxEditor.Viewport // Restore cursor and stop tracking mouse movement win.Cursor = CursorType.Default; win.EndTrackingMouse(); + win.MouseMoveRelative -= OnMouseMoveRelative; } /// @@ -1584,6 +1593,14 @@ namespace FlaxEditor.Viewport else EndMouseCapture(); } +#if PLATFORM_SDL + bool useMouse = IsControllingMouse || true; + _prevInput = _input; + if (canUseInput && ContainsFocus) + _input.Gather(win.Window, useMouse, ref _prevInput); + else + _input.Clear(); +#else bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height)); _prevInput = _input; var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot)); @@ -1591,6 +1608,7 @@ namespace FlaxEditor.Viewport _input.Gather(win.Window, useMouse, ref _prevInput); else _input.Clear(); +#endif // Track controlling mouse state change bool wasControllingMouse = _prevInput.IsControllingMouse; @@ -1699,6 +1717,10 @@ namespace FlaxEditor.Viewport if (_input.IsControlDown) moveDelta *= 0.3f; +#if PLATFORM_SDL + var mouseDelta = _mouseDelta; + _mouseDelta = Float2.Zero; +#else // Calculate smooth mouse delta not dependant on viewport size var offset = _viewMousePos - _startPos; if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown) @@ -1740,6 +1762,7 @@ namespace FlaxEditor.Viewport mouseDelta += _mouseDeltaLast * _mouseAccelerationScale; _mouseDeltaLast = currentDelta; } +#endif // Update moveDelta *= dt * (60.0f * 4.0f); @@ -1748,12 +1771,14 @@ namespace FlaxEditor.Viewport mouseDelta *= new Float2(1, -1); UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse); +#if !PLATFORM_SDL // Move mouse back to the root position if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown)) { var center = PointToWindow(_startPos); win.MousePosition = center; } +#endif // Change Ortho size on mouse scroll if (_isOrtho && !rmbWheel) @@ -1765,6 +1790,8 @@ namespace FlaxEditor.Viewport } else { +#if PLATFORM_SDL +#else if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown) { // Calculate smooth mouse delta not dependant on viewport size @@ -1779,6 +1806,7 @@ namespace FlaxEditor.Viewport _mouseDelta = Float2.Zero; } _mouseDeltaLast = Float2.Zero; +#endif if (ContainsFocus) { @@ -1828,6 +1856,12 @@ namespace FlaxEditor.Viewport _input.MouseWheelDelta = 0; } + /// + public void OnMouseMoveRelative(ref Float2 mouseMotion) + { + _mouseDelta += mouseMotion; + } + /// public override bool OnMouseDown(Float2 location, MouseButton button) { diff --git a/Source/Engine/Engine/Screen.cpp b/Source/Engine/Engine/Screen.cpp index 703f89c87..4131e11b4 100644 --- a/Source/Engine/Engine/Screen.cpp +++ b/Source/Engine/Engine/Screen.cpp @@ -106,13 +106,25 @@ void Screen::SetCursorVisible(const bool value) #else const auto win = Engine::MainWindow; #endif + bool focused = false; if (win && Engine::HasGameViewportFocus()) { win->SetCursor(value ? CursorType::Default : CursorType::Hidden); + focused = true; } else if (win) win->SetCursor(CursorType::Default); CursorVisible = value; + + // Just enable relative mode when cursor is constrained and not visible + if (CursorLock != CursorLockMode::None && !CursorVisible && focused) + { + Input::Mouse->SetRelativeMode(true); + } + else if (CursorLock == CursorLockMode::None || CursorVisible || !focused) + { + Input::Mouse->SetRelativeMode(false); + } } CursorLockMode Screen::GetCursorLock() @@ -141,6 +153,17 @@ void Screen::SetCursorLock(CursorLockMode mode) win->EndClippingCursor(); } CursorLock = mode; + + // Just enable relative mode when cursor is constrained and not visible + bool focused = win && Engine::HasGameViewportFocus(); + if (CursorLock != CursorLockMode::None && !CursorVisible && focused) + { + Input::Mouse->SetRelativeMode(true); + } + else if (CursorLock == CursorLockMode::None || CursorVisible || !focused) + { + Input::Mouse->SetRelativeMode(false); + } } GameWindowMode Screen::GetGameWindowMode() diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 7a4d0592c..3cd69e490 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -78,6 +78,7 @@ Delegate Input::MouseUp; Delegate Input::MouseDoubleClick; Delegate Input::MouseWheel; Delegate Input::MouseMove; +Delegate Input::MouseMoveRelative; Action Input::MouseLeave; Delegate Input::TouchDown; Delegate Input::TouchMove; @@ -208,6 +209,14 @@ void Mouse::OnMouseMove(const Float2& position, Window* target) e.MouseData.Position = position; } +void Mouse::OnMouseMoveRelative(const Float2& positionRelative, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseMoveRelative; + e.Target = target; + e.MouseMovementData.PositionRelative = positionRelative; +} + void Mouse::OnMouseLeave(Window* target) { Event& e = _queue.AddOne(); @@ -273,6 +282,11 @@ bool Mouse::Update(EventQueue& queue) _state.MousePosition = e.MouseData.Position; break; } + case EventType::MouseMoveRelative: + { + _state.MousePosition += e.MouseMovementData.PositionRelative; + break; + } case EventType::MouseLeave: { break; @@ -933,6 +947,9 @@ void InputService::Update() case InputDevice::EventType::MouseMove: window->OnMouseMove(window->ScreenToClient(e.MouseData.Position)); break; + case InputDevice::EventType::MouseMoveRelative: + window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative); + break; case InputDevice::EventType::MouseLeave: window->OnMouseLeave(); break; @@ -989,6 +1006,9 @@ void InputService::Update() case InputDevice::EventType::MouseMove: Input::MouseMove(e.MouseData.Position); break; + case InputDevice::EventType::MouseMoveRelative: + Input::MouseMoveRelative(e.MouseMovementData.PositionRelative); + break; case InputDevice::EventType::MouseLeave: Input::MouseLeave(); break; @@ -1202,6 +1222,7 @@ void InputService::Update() } } +#if !PLATFORM_SDL // Lock mouse if need to const auto lockMode = Screen::GetCursorLock(); if (lockMode == CursorLockMode::Locked) @@ -1210,6 +1231,7 @@ void InputService::Update() Screen::ScreenToGameViewport(Float2::Zero); Input::SetMousePosition(pos); } +#endif // Send events for the active actions and axes (send events only in play mode) if (!Time::GetGamePaused()) diff --git a/Source/Engine/Input/Input.h b/Source/Engine/Input/Input.h index 8cc1b2106..0021f99a5 100644 --- a/Source/Engine/Input/Input.h +++ b/Source/Engine/Input/Input.h @@ -108,6 +108,11 @@ public: /// API_EVENT() static Delegate MouseMove; + /// + /// Event fired when mouse moves while in relative mode. + /// + API_EVENT() static Delegate MouseMoveRelative; + /// /// Event fired when mouse leaves window. /// diff --git a/Source/Engine/Input/InputDevice.h b/Source/Engine/Input/InputDevice.h index 5d2a383be..80f98fbdd 100644 --- a/Source/Engine/Input/InputDevice.h +++ b/Source/Engine/Input/InputDevice.h @@ -25,6 +25,7 @@ public: MouseDoubleClick, MouseWheel, MouseMove, + MouseMoveRelative, MouseLeave, TouchDown, TouchMove, @@ -54,6 +55,11 @@ public: Float2 Position; } MouseData; + struct + { + Float2 PositionRelative; + } MouseMovementData; + struct { float WheelDelta; diff --git a/Source/Engine/Input/Mouse.h b/Source/Engine/Input/Mouse.h index e5e1b3639..44738f607 100644 --- a/Source/Engine/Input/Mouse.h +++ b/Source/Engine/Input/Mouse.h @@ -46,12 +46,14 @@ public: protected: State _state; State _prevState; + bool _relativeMode; explicit Mouse() : InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse")) { _state.Clear(); _prevState.Clear(); + _relativeMode = false; } public: @@ -114,6 +116,14 @@ public: return !_state.MouseButtons[static_cast(button)] && _prevState.MouseButtons[static_cast(button)]; } + /// + /// Gets the current state of mouse relative mode. + /// + API_FUNCTION() FORCE_INLINE bool IsRelative() const + { + return _relativeMode; + } + public: /// /// Sets the mouse position. @@ -121,6 +131,16 @@ public: /// The new position. virtual void SetMousePosition(const Float2& newPosition) = 0; + /// + /// Sets the mouse relative mode state. While enabled, the mouse movement tracking becomes more accurate. + /// The cursor will be hidden while in relative mode. + /// + /// The new relative mode state. + virtual void SetRelativeMode(bool relativeMode) + { + _relativeMode = relativeMode; + } + /// /// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically. /// @@ -158,6 +178,13 @@ public: /// The target window to receive this event, otherwise input system will pick the window automatically. void OnMouseMove(const Float2& position, Window* target = nullptr); + /// + /// Called when mouse moves in relative mode. + /// + /// The mouse position change. + /// The target window to receive this event, otherwise input system will pick the window automatically. + void OnMouseMoveRelative(const Float2& positionRelative, Window* target = nullptr); + /// /// Called when mouse leaves the input source area. /// diff --git a/Source/Engine/Platform/Base/WindowBase.cpp b/Source/Engine/Platform/Base/WindowBase.cpp index 4d7a925d7..befe86246 100644 --- a/Source/Engine/Platform/Base/WindowBase.cpp +++ b/Source/Engine/Platform/Base/WindowBase.cpp @@ -257,6 +257,13 @@ void WindowBase::OnMouseMove(const Float2& mousePosition) INVOKE_EVENT_PARAMS_1(OnMouseMove, (void*)&mousePosition); } +void WindowBase::OnMouseMoveRelative(const Float2& mousePositionRelative) +{ + PROFILE_CPU_NAMED("GUI.OnMouseMoveRelative"); + MouseMoveRelative(mousePositionRelative); + INVOKE_EVENT_PARAMS_1(OnMouseMoveRelative, (void*)&mousePositionRelative); +} + void WindowBase::OnMouseLeave() { PROFILE_CPU_NAMED("GUI.OnMouseLeave"); diff --git a/Source/Engine/Platform/Base/WindowBase.h b/Source/Engine/Platform/Base/WindowBase.h index f92502c18..db9a83839 100644 --- a/Source/Engine/Platform/Base/WindowBase.h +++ b/Source/Engine/Platform/Base/WindowBase.h @@ -592,6 +592,12 @@ public: MouseDelegate MouseMove; void OnMouseMove(const Float2& mousePosition); + /// + /// Event fired when mouse moves in relative mode. + /// + MouseDelegate MouseMoveRelative; + void OnMouseMoveRelative(const Float2& mousePositionRelative); + /// /// Event fired when mouse leaves window. /// diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index 5761685c8..f50fbce7f 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -616,7 +616,7 @@ void LinuxWindow::OnButtonPress(void* event) } // Handle double-click - if (buttonEvent->button == Button1) + if (buttonEvent->button == Button1 && !Input::Mouse->IsRelative()) { if ( buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) && diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp index ebccb5709..49a30ab42 100644 --- a/Source/Engine/Platform/Mac/MacWindow.cpp +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -507,7 +507,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) Float2 mousePos = GetMousePosition(Window, event); mousePos = Window->ClientToScreen(mousePos); MouseButton mouseButton = MouseButton::Left; - if ([event clickCount] == 2) + if ([event clickCount] == 2 && !Input::Mouse->IsRelative()) Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window); else Input::Mouse->OnMouseDown(mousePos, mouseButton, Window); @@ -544,7 +544,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) if (IsWindowInvalid(Window)) return; Float2 mousePos = GetMousePosition(Window, event); MouseButton mouseButton = MouseButton::Right; - if ([event clickCount] == 2) + if ([event clickCount] == 2 && !Input::Mouse->IsRelative()) Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window); else Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window); @@ -582,7 +582,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r) default: return; } - if ([event clickCount] == 2) + if ([event clickCount] == 2 && !Input::Mouse->IsRelative()) Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window); else Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window); diff --git a/Source/Engine/Platform/SDL/SDLInput.cpp b/Source/Engine/Platform/SDL/SDLInput.cpp index 720da2875..d73d28c47 100644 --- a/Source/Engine/Platform/SDL/SDLInput.cpp +++ b/Source/Engine/Platform/SDL/SDLInput.cpp @@ -474,8 +474,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event) { case SDL_EVENT_MOUSE_MOTION: { - const Float2 mousePos = window->ClientToScreen({ event.motion.x, event.motion.y }); - Input::Mouse->OnMouseMove(mousePos, window); + if (Input::Mouse->IsRelative()) + { + const Float2 mouseDelta(event.motion.xrel, event.motion.yrel); + Input::Mouse->OnMouseMoveRelative(mouseDelta, window); + } + else + { + const Float2 mousePos = window->ClientToScreen({ event.motion.x, event.motion.y }); + Input::Mouse->OnMouseMove(mousePos, window); + } return true; } case SDL_EVENT_WINDOW_MOUSE_LEAVE: @@ -486,7 +494,7 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event) case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { - const Float2 mousePos = window->ClientToScreen({ event.button.x, event.button.y }); + Float2 mousePos = window->ClientToScreen({ event.button.x, event.button.y }); MouseButton button = MouseButton::None; if (event.button.button == SDL_BUTTON_LEFT) button = MouseButton::Left; @@ -499,9 +507,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event) else if (event.button.button == SDL_BUTTON_X2) button = MouseButton::Extended2; + if (Input::Mouse->IsRelative()) + { + // Use the previous visible mouse position here, the event or global + // mouse position would cause input to trigger in other editor windows. + mousePos = SDLInputImpl::Mouse->GetMousePosition(); + } + if (event.button.state == SDL_RELEASED) Input::Mouse->OnMouseUp(mousePos, button, window); - // Prevent sending mouse down event when double-clicking + // Prevent sending multiple mouse down event when double-clicking UI elements else if (event.button.clicks % 2 == 1) Input::Mouse->OnMouseDown(mousePos, button, window); else @@ -511,9 +526,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event) } case SDL_EVENT_MOUSE_WHEEL: { - const Float2 mousePos = window->ClientToScreen({ event.wheel.mouse_x, event.wheel.mouse_y }); + Float2 mousePos = window->ClientToScreen({ event.wheel.mouse_x, event.wheel.mouse_y }); const float delta = event.wheel.y; + if (Input::Mouse->IsRelative()) + { + // Use the previous visible mouse position here, the event or global + // mouse position would cause input to trigger in other editor windows. + mousePos = SDLInputImpl::Mouse->GetMousePosition(); + } + Input::Mouse->OnMouseWheel(mousePos, delta, window); return true; } diff --git a/Source/Engine/Platform/SDL/SDLWindow.cpp b/Source/Engine/Platform/SDL/SDLWindow.cpp index ed540325e..600705398 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.cpp +++ b/Source/Engine/Platform/SDL/SDLWindow.cpp @@ -957,6 +957,10 @@ void SDLWindow::StartTrackingMouse(bool useMouseScreenOffset) { if (SDL_CaptureMouse(SDL_TRUE) != 0) LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError())); + + // For viewport camera mouse tracking we want to use relative mode for best precision + if (_cursor == CursorType::Hidden) + Input::Mouse->SetRelativeMode(true); } } @@ -973,6 +977,7 @@ void SDLWindow::EndTrackingMouse() LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError())); //SDL_SetWindowGrab(_window, SDL_FALSE); + Input::Mouse->SetRelativeMode(false); } void SDLWindow::StartClippingCursor(const Rectangle& bounds) @@ -1038,9 +1043,14 @@ void SDLWindow::UpdateCursor() const if (_cursor == CursorType::Hidden) { SDL_HideCursor(); + + if (_isTrackingMouse) + Input::Mouse->SetRelativeMode(true); return; } SDL_ShowCursor(); + //if (_isTrackingMouse) + // Input::Mouse->SetRelativeMode(false); int32 index = SDL_SYSTEM_CURSOR_DEFAULT; switch (_cursor) diff --git a/Source/Engine/Platform/Window.cs b/Source/Engine/Platform/Window.cs index 86b872513..06b02457a 100644 --- a/Source/Engine/Platform/Window.cs +++ b/Source/Engine/Platform/Window.cs @@ -17,31 +17,37 @@ namespace FlaxEngine /// /// Perform window hit test delegate. /// - /// The mouse position. The coordinate is relative to the upper-left corner of the screen. Use to convert position into client space coordinates. + /// The mouse position. The coordinate is relative to the upper-left corner of the screen. Use to convert position into client space coordinates. /// Hit result. - public delegate WindowHitCodes HitTestDelegate(ref Float2 mouse); + public delegate WindowHitCodes HitTestDelegate(ref Float2 mousePosition); /// /// Perform mouse buttons action. /// - /// The mouse position. + /// The mouse position. /// The mouse buttons state. /// The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false. - public delegate void MouseButtonDelegate(ref Float2 mouse, MouseButton button, ref bool handled); + public delegate void MouseButtonDelegate(ref Float2 mousePosition, MouseButton button, ref bool handled); /// /// Perform mouse move action. /// - /// The mouse position. - public delegate void MouseMoveDelegate(ref Float2 mouse); + /// The mouse position. + public delegate void MouseMoveDelegate(ref Float2 mousePosition); + + /// + /// Perform mouse move action in relative mode. + /// + /// The relative mouse motion. + public delegate void MouseMoveRelativeDelegate(ref Float2 mouseMotion); /// /// Perform mouse wheel action. /// - /// The mouse position. + /// The mouse position. /// The mouse wheel move delta (can be positive or negative; normalized to [-1;1] range). /// The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false. - public delegate void MouseWheelDelegate(ref Float2 mouse, float delta, ref bool handled); + public delegate void MouseWheelDelegate(ref Float2 mousePosition, float delta, ref bool handled); /// /// Perform touch action. @@ -99,9 +105,14 @@ namespace FlaxEngine public event MouseWheelDelegate MouseWheel; /// - /// Event fired when mouse moves + /// Event fired when mouse moves. /// public event MouseMoveDelegate MouseMove; + + /// + /// Event fired when mouse moves in relative mode. + /// + public event MouseMoveRelativeDelegate MouseMoveRelative; /// /// Event fired when mouse leaves window. @@ -273,6 +284,12 @@ namespace FlaxEngine MouseMove?.Invoke(ref pos); GUI.OnMouseMove(pos); } + + internal void Internal_OnMouseMoveRelative(ref Float2 mouseMotion) + { + MouseMoveRelative?.Invoke(ref mouseMotion); + GUI.OnMouseMoveRelative(mouseMotion); + } internal void Internal_OnMouseLeave() { diff --git a/Source/Engine/Platform/Windows/WindowsInput.cpp b/Source/Engine/Platform/Windows/WindowsInput.cpp index a772c4c33..64299b6a3 100644 --- a/Source/Engine/Platform/Windows/WindowsInput.cpp +++ b/Source/Engine/Platform/Windows/WindowsInput.cpp @@ -265,19 +265,38 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM } case WM_LBUTTONDBLCLK: { - OnMouseDoubleClick(mousePos, MouseButton::Left, window); + if (!Input::Mouse->IsRelative()) + OnMouseDoubleClick(mousePos, MouseButton::Left, window); + else + OnMouseDown(mousePos, MouseButton::Left, window); result = true; break; } case WM_RBUTTONDBLCLK: { - OnMouseDoubleClick(mousePos, MouseButton::Right, window); + if (!Input::Mouse->IsRelative()) + OnMouseDoubleClick(mousePos, MouseButton::Right, window); + else + OnMouseDown(mousePos, MouseButton::Right, window); result = true; break; } case WM_MBUTTONDBLCLK: { - OnMouseDoubleClick(mousePos, MouseButton::Middle, window); + if (!Input::Mouse->IsRelative()) + OnMouseDoubleClick(mousePos, MouseButton::Middle, window); + else + OnMouseDown(mousePos, MouseButton::Middle, window); + result = true; + break; + } + case WM_XBUTTONDBLCLK: + { + const auto button = (HIWORD(wParam) & XBUTTON1) ? MouseButton::Extended1 : MouseButton::Extended2; + if (!Input::Mouse->IsRelative()) + OnMouseDoubleClick(mousePos, button, window); + else + OnMouseDown(mousePos, button, window); result = true; break; } diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index c96ee0643..ef3ee5f91 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -769,6 +769,15 @@ namespace FlaxEngine.GUI Tooltip?.OnMouseLeaveControl(this); } } + + /// + /// When mouse moves over control's area while mouse is in relative mode + /// + /// Mouse relative motion + [NoAnimate] + public virtual void OnMouseMoveRelative(Float2 mouseMotion) + { + } /// /// When mouse leaves control's area diff --git a/Source/Engine/UI/GUI/WindowRootControl.cs b/Source/Engine/UI/GUI/WindowRootControl.cs index f39cf0346..20351e2df 100644 --- a/Source/Engine/UI/GUI/WindowRootControl.cs +++ b/Source/Engine/UI/GUI/WindowRootControl.cs @@ -338,5 +338,17 @@ namespace FlaxEngine.GUI base.OnMouseMove(location); } + + /// + public override void OnMouseMoveRelative(Float2 mouseMotion) + { + if (_trackingControl != null) + { + _trackingControl.OnMouseMoveRelative(mouseMotion); + return; + } + + base.OnMouseMoveRelative(mouseMotion); + } } }