Implement relative mouse mode (raw input) for SDL platform

This commit is contained in:
2024-07-25 21:29:38 +03:00
parent ba248a61bd
commit 4a7f6c34f3
18 changed files with 276 additions and 23 deletions

View File

@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
return base.OnMouseDown(location, button); return base.OnMouseDown(location, button);
} }
#if !PLATFORM_SDL
/// <inheritdoc /> /// <inheritdoc />
public override void OnMouseMove(Float2 location) public override void OnMouseMove(Float2 location)
{ {
@@ -292,6 +293,36 @@ namespace FlaxEditor.GUI.Input
base.OnMouseMove(location); base.OnMouseMove(location);
} }
#else
/// <inheritdoc />
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
/// <inheritdoc /> /// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button) public override bool OnMouseUp(Float2 location, MouseButton button)
{ {

View File

@@ -435,6 +435,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
case InputDevice::EventType::MouseMove: case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position)); window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break; break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave: case InputDevice::EventType::MouseLeave:
window->OnMouseLeave(); window->OnMouseLeave();
break; break;

View File

@@ -158,18 +158,22 @@ namespace FlaxEditor.Viewport
private float _movementSpeed; private float _movementSpeed;
private float _minMovementSpeed; private float _minMovementSpeed;
private float _maxMovementSpeed; private float _maxMovementSpeed;
#if !PLATFORM_SDL
private float _mouseAccelerationScale; private float _mouseAccelerationScale;
private bool _useMouseFiltering; private bool _useMouseFiltering;
private bool _useMouseAcceleration; private bool _useMouseAcceleration;
#endif
// Input // Input
internal bool _disableInputUpdate; internal bool _disableInputUpdate;
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown; private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
private int _deltaFilteringStep;
private Float2 _startPos; private Float2 _startPos;
#if !PLATFORM_SDL
private Float2 _mouseDeltaLast; private Float2 _mouseDeltaLast;
private int _deltaFilteringStep;
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames]; private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
#endif
/// <summary> /// <summary>
/// The previous input (from the previous update). /// The previous input (from the previous update).
@@ -525,10 +529,11 @@ namespace FlaxEditor.Viewport
: base(task) : base(task)
{ {
_editor = Editor.Instance; _editor = Editor.Instance;
#if !PLATFORM_SDL
_mouseAccelerationScale = 0.1f; _mouseAccelerationScale = 0.1f;
_useMouseFiltering = false; _useMouseFiltering = false;
_useMouseAcceleration = false; _useMouseAcceleration = false;
#endif
_camera = camera; _camera = camera;
if (_camera != null) if (_camera != null)
_camera.Viewport = this; _camera.Viewport = this;
@@ -1456,7 +1461,9 @@ namespace FlaxEditor.Viewport
// Hide cursor and start tracking mouse movement // Hide cursor and start tracking mouse movement
win.StartTrackingMouse(false); win.StartTrackingMouse(false);
win.Cursor = CursorType.Hidden; win.Cursor = CursorType.Hidden;
win.MouseMoveRelative += OnMouseMoveRelative;
#if !PLATFORM_SDL
// Center mouse position if it's too close to the edge // Center mouse position if it's too close to the edge
var size = Size; var size = Size;
var center = Float2.Round(size * 0.5f); var center = Float2.Round(size * 0.5f);
@@ -1465,6 +1472,7 @@ namespace FlaxEditor.Viewport
_viewMousePos = center; _viewMousePos = center;
win.MousePosition = PointToWindow(_viewMousePos); win.MousePosition = PointToWindow(_viewMousePos);
} }
#endif
} }
/// <summary> /// <summary>
@@ -1476,6 +1484,7 @@ namespace FlaxEditor.Viewport
// Restore cursor and stop tracking mouse movement // Restore cursor and stop tracking mouse movement
win.Cursor = CursorType.Default; win.Cursor = CursorType.Default;
win.EndTrackingMouse(); win.EndTrackingMouse();
win.MouseMoveRelative -= OnMouseMoveRelative;
} }
/// <summary> /// <summary>
@@ -1580,6 +1589,14 @@ namespace FlaxEditor.Viewport
else else
EndMouseCapture(); 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)); bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
_prevInput = _input; _prevInput = _input;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot)); var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
@@ -1587,6 +1604,7 @@ namespace FlaxEditor.Viewport
_input.Gather(win.Window, useMouse, ref _prevInput); _input.Gather(win.Window, useMouse, ref _prevInput);
else else
_input.Clear(); _input.Clear();
#endif
// Track controlling mouse state change // Track controlling mouse state change
bool wasControllingMouse = _prevInput.IsControllingMouse; bool wasControllingMouse = _prevInput.IsControllingMouse;
@@ -1695,6 +1713,10 @@ namespace FlaxEditor.Viewport
if (_input.IsControlDown) if (_input.IsControlDown)
moveDelta *= 0.3f; moveDelta *= 0.3f;
#if PLATFORM_SDL
var mouseDelta = _mouseDelta;
_mouseDelta = Float2.Zero;
#else
// Calculate smooth mouse delta not dependant on viewport size // Calculate smooth mouse delta not dependant on viewport size
var offset = _viewMousePos - _startPos; var offset = _viewMousePos - _startPos;
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown) if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
@@ -1736,6 +1758,7 @@ namespace FlaxEditor.Viewport
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale; mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
_mouseDeltaLast = currentDelta; _mouseDeltaLast = currentDelta;
} }
#endif
// Update // Update
moveDelta *= dt * (60.0f * 4.0f); moveDelta *= dt * (60.0f * 4.0f);
@@ -1744,12 +1767,14 @@ namespace FlaxEditor.Viewport
mouseDelta *= new Float2(1, -1); mouseDelta *= new Float2(1, -1);
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse); UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
#if !PLATFORM_SDL
// Move mouse back to the root position // Move mouse back to the root position
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown)) if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
{ {
var center = PointToWindow(_startPos); var center = PointToWindow(_startPos);
win.MousePosition = center; win.MousePosition = center;
} }
#endif
// Change Ortho size on mouse scroll // Change Ortho size on mouse scroll
if (_isOrtho && !rmbWheel) if (_isOrtho && !rmbWheel)
@@ -1761,6 +1786,8 @@ namespace FlaxEditor.Viewport
} }
else else
{ {
#if PLATFORM_SDL
#else
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown) if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
{ {
// Calculate smooth mouse delta not dependant on viewport size // Calculate smooth mouse delta not dependant on viewport size
@@ -1775,6 +1802,7 @@ namespace FlaxEditor.Viewport
_mouseDelta = Float2.Zero; _mouseDelta = Float2.Zero;
} }
_mouseDeltaLast = Float2.Zero; _mouseDeltaLast = Float2.Zero;
#endif
if (ContainsFocus) if (ContainsFocus)
{ {
@@ -1824,6 +1852,12 @@ namespace FlaxEditor.Viewport
_input.MouseWheelDelta = 0; _input.MouseWheelDelta = 0;
} }
/// <inheritdoc />
public void OnMouseMoveRelative(ref Float2 mouseMotion)
{
_mouseDelta += mouseMotion;
}
/// <inheritdoc /> /// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button) public override bool OnMouseDown(Float2 location, MouseButton button)
{ {

View File

@@ -106,13 +106,25 @@ void Screen::SetCursorVisible(const bool value)
#else #else
const auto win = Engine::MainWindow; const auto win = Engine::MainWindow;
#endif #endif
bool focused = false;
if (win && Engine::HasGameViewportFocus()) if (win && Engine::HasGameViewportFocus())
{ {
win->SetCursor(value ? CursorType::Default : CursorType::Hidden); win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
focused = true;
} }
else if (win) else if (win)
win->SetCursor(CursorType::Default); win->SetCursor(CursorType::Default);
CursorVisible = value; 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() CursorLockMode Screen::GetCursorLock()
@@ -141,6 +153,17 @@ void Screen::SetCursorLock(CursorLockMode mode)
win->EndClippingCursor(); win->EndClippingCursor();
} }
CursorLock = mode; 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() GameWindowMode Screen::GetGameWindowMode()

View File

@@ -79,6 +79,7 @@ Delegate<const Float2&, MouseButton> Input::MouseUp;
Delegate<const Float2&, MouseButton> Input::MouseDoubleClick; Delegate<const Float2&, MouseButton> Input::MouseDoubleClick;
Delegate<const Float2&, float> Input::MouseWheel; Delegate<const Float2&, float> Input::MouseWheel;
Delegate<const Float2&> Input::MouseMove; Delegate<const Float2&> Input::MouseMove;
Delegate<const Float2&> Input::MouseMoveRelative;
Action Input::MouseLeave; Action Input::MouseLeave;
Delegate<const Float2&, int32> Input::TouchDown; Delegate<const Float2&, int32> Input::TouchDown;
Delegate<const Float2&, int32> Input::TouchMove; Delegate<const Float2&, int32> Input::TouchMove;
@@ -209,6 +210,14 @@ void Mouse::OnMouseMove(const Float2& position, Window* target)
e.MouseData.Position = position; 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) void Mouse::OnMouseLeave(Window* target)
{ {
Event& e = _queue.AddOne(); Event& e = _queue.AddOne();
@@ -274,6 +283,11 @@ bool Mouse::Update(EventQueue& queue)
_state.MousePosition = e.MouseData.Position; _state.MousePosition = e.MouseData.Position;
break; break;
} }
case EventType::MouseMoveRelative:
{
_state.MousePosition += e.MouseMovementData.PositionRelative;
break;
}
case EventType::MouseLeave: case EventType::MouseLeave:
{ {
break; break;
@@ -731,6 +745,9 @@ void InputService::Update()
case InputDevice::EventType::MouseMove: case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position)); window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break; break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave: case InputDevice::EventType::MouseLeave:
window->OnMouseLeave(); window->OnMouseLeave();
break; break;
@@ -787,6 +804,9 @@ void InputService::Update()
case InputDevice::EventType::MouseMove: case InputDevice::EventType::MouseMove:
Input::MouseMove(e.MouseData.Position); Input::MouseMove(e.MouseData.Position);
break; break;
case InputDevice::EventType::MouseMoveRelative:
Input::MouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave: case InputDevice::EventType::MouseLeave:
Input::MouseLeave(); Input::MouseLeave();
break; break;
@@ -1000,12 +1020,14 @@ void InputService::Update()
} }
} }
#if !PLATFORM_SDL
// Lock mouse if need to // Lock mouse if need to
const auto lockMode = Screen::GetCursorLock(); const auto lockMode = Screen::GetCursorLock();
if (lockMode == CursorLockMode::Locked) if (lockMode == CursorLockMode::Locked)
{ {
Input::SetMousePosition(Screen::GetSize() * 0.5f); Input::SetMousePosition(Screen::GetSize() * 0.5f);
} }
#endif
// Send events for the active actions and axes (send events only in play mode) // Send events for the active actions and axes (send events only in play mode)
if (!Time::GetGamePaused()) if (!Time::GetGamePaused())

View File

@@ -107,6 +107,11 @@ public:
/// </summary> /// </summary>
API_EVENT() static Delegate<const Float2&> MouseMove; API_EVENT() static Delegate<const Float2&> MouseMove;
/// <summary>
/// Event fired when mouse moves while in relative mode.
/// </summary>
API_EVENT() static Delegate<const Float2&> MouseMoveRelative;
/// <summary> /// <summary>
/// Event fired when mouse leaves window. /// Event fired when mouse leaves window.
/// </summary> /// </summary>

View File

@@ -25,6 +25,7 @@ public:
MouseDoubleClick, MouseDoubleClick,
MouseWheel, MouseWheel,
MouseMove, MouseMove,
MouseMoveRelative,
MouseLeave, MouseLeave,
TouchDown, TouchDown,
TouchMove, TouchMove,
@@ -54,6 +55,11 @@ public:
Float2 Position; Float2 Position;
} MouseData; } MouseData;
struct
{
Float2 PositionRelative;
} MouseMovementData;
struct struct
{ {
float WheelDelta; float WheelDelta;

View File

@@ -46,12 +46,14 @@ public:
protected: protected:
State _state; State _state;
State _prevState; State _prevState;
bool _relativeMode;
explicit Mouse() explicit Mouse()
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse")) : InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse"))
{ {
_state.Clear(); _state.Clear();
_prevState.Clear(); _prevState.Clear();
_relativeMode = false;
} }
public: public:
@@ -114,6 +116,14 @@ public:
return !_state.MouseButtons[static_cast<int32>(button)] && _prevState.MouseButtons[static_cast<int32>(button)]; return !_state.MouseButtons[static_cast<int32>(button)] && _prevState.MouseButtons[static_cast<int32>(button)];
} }
/// <summary>
/// Gets the current state of mouse relative mode.
/// </summary>
API_FUNCTION() FORCE_INLINE bool IsRelative() const
{
return _relativeMode;
}
public: public:
/// <summary> /// <summary>
/// Sets the mouse position. /// Sets the mouse position.
@@ -121,6 +131,16 @@ public:
/// <param name="newPosition">The new position.</param> /// <param name="newPosition">The new position.</param>
virtual void SetMousePosition(const Float2& newPosition) = 0; virtual void SetMousePosition(const Float2& newPosition) = 0;
/// <summary>
/// Sets the mouse relative mode state. While enabled, the mouse movement tracking becomes more accurate.
/// The cursor will be hidden while in relative mode.
/// </summary>
/// <param name="relativeMode">The new relative mode state.</param>
virtual void SetRelativeMode(bool relativeMode)
{
_relativeMode = relativeMode;
}
/// <summary> /// <summary>
/// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically. /// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically.
/// </summary> /// </summary>
@@ -158,6 +178,13 @@ public:
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param> /// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseMove(const Float2& position, Window* target = nullptr); void OnMouseMove(const Float2& position, Window* target = nullptr);
/// <summary>
/// Called when mouse moves in relative mode.
/// </summary>
/// <param name="positionRelative">The mouse position change.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseMoveRelative(const Float2& positionRelative, Window* target = nullptr);
/// <summary> /// <summary>
/// Called when mouse leaves the input source area. /// Called when mouse leaves the input source area.
/// </summary> /// </summary>

View File

@@ -257,6 +257,13 @@ void WindowBase::OnMouseMove(const Float2& mousePosition)
INVOKE_EVENT_PARAMS_1(OnMouseMove, (void*)&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() void WindowBase::OnMouseLeave()
{ {
PROFILE_CPU_NAMED("GUI.OnMouseLeave"); PROFILE_CPU_NAMED("GUI.OnMouseLeave");

View File

@@ -592,6 +592,12 @@ public:
MouseDelegate MouseMove; MouseDelegate MouseMove;
void OnMouseMove(const Float2& mousePosition); void OnMouseMove(const Float2& mousePosition);
/// <summary>
/// Event fired when mouse moves in relative mode.
/// </summary>
MouseDelegate MouseMoveRelative;
void OnMouseMoveRelative(const Float2& mousePositionRelative);
/// <summary> /// <summary>
/// Event fired when mouse leaves window. /// Event fired when mouse leaves window.
/// </summary> /// </summary>

View File

@@ -617,7 +617,7 @@ void LinuxWindow::OnButtonPress(void* event)
} }
// Handle double-click // Handle double-click
if (buttonEvent->button == Button1) if (buttonEvent->button == Button1 && !Input::Mouse->IsRelative())
{ {
if ( if (
buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) && buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) &&

View File

@@ -507,7 +507,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
Float2 mousePos = GetMousePosition(Window, event); Float2 mousePos = GetMousePosition(Window, event);
mousePos = Window->ClientToScreen(mousePos); mousePos = Window->ClientToScreen(mousePos);
MouseButton mouseButton = MouseButton::Left; MouseButton mouseButton = MouseButton::Left;
if ([event clickCount] == 2) if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window); Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window);
else else
Input::Mouse->OnMouseDown(mousePos, mouseButton, Window); Input::Mouse->OnMouseDown(mousePos, mouseButton, Window);
@@ -544,7 +544,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
if (IsWindowInvalid(Window)) return; if (IsWindowInvalid(Window)) return;
Float2 mousePos = GetMousePosition(Window, event); Float2 mousePos = GetMousePosition(Window, event);
MouseButton mouseButton = MouseButton::Right; MouseButton mouseButton = MouseButton::Right;
if ([event clickCount] == 2) if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window); Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
else else
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window); Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);
@@ -582,7 +582,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
default: default:
return; return;
} }
if ([event clickCount] == 2) if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window); Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
else else
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window); Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);

View File

@@ -474,8 +474,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
{ {
case SDL_EVENT_MOUSE_MOTION: case SDL_EVENT_MOUSE_MOTION:
{ {
const Float2 mousePos = window->ClientToScreen({ event.motion.x, event.motion.y }); if (Input::Mouse->IsRelative())
Input::Mouse->OnMouseMove(mousePos, window); {
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; return true;
} }
case SDL_EVENT_WINDOW_MOUSE_LEAVE: 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_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: 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; MouseButton button = MouseButton::None;
if (event.button.button == SDL_BUTTON_LEFT) if (event.button.button == SDL_BUTTON_LEFT)
button = MouseButton::Left; button = MouseButton::Left;
@@ -499,9 +507,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
else if (event.button.button == SDL_BUTTON_X2) else if (event.button.button == SDL_BUTTON_X2)
button = MouseButton::Extended2; 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) if (event.button.state == SDL_RELEASED)
Input::Mouse->OnMouseUp(mousePos, button, window); 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) else if (event.button.clicks % 2 == 1)
Input::Mouse->OnMouseDown(mousePos, button, window); Input::Mouse->OnMouseDown(mousePos, button, window);
else else
@@ -511,9 +526,16 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
} }
case SDL_EVENT_MOUSE_WHEEL: 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; 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); Input::Mouse->OnMouseWheel(mousePos, delta, window);
return true; return true;
} }

View File

@@ -957,6 +957,10 @@ void SDLWindow::StartTrackingMouse(bool useMouseScreenOffset)
{ {
if (SDL_CaptureMouse(SDL_TRUE) != 0) if (SDL_CaptureMouse(SDL_TRUE) != 0)
LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError())); 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())); LOG(Warning, "SDL_CaptureMouse: {0}", String(SDL_GetError()));
//SDL_SetWindowGrab(_window, SDL_FALSE); //SDL_SetWindowGrab(_window, SDL_FALSE);
Input::Mouse->SetRelativeMode(false);
} }
void SDLWindow::StartClippingCursor(const Rectangle& bounds) void SDLWindow::StartClippingCursor(const Rectangle& bounds)
@@ -1038,9 +1043,14 @@ void SDLWindow::UpdateCursor() const
if (_cursor == CursorType::Hidden) if (_cursor == CursorType::Hidden)
{ {
SDL_HideCursor(); SDL_HideCursor();
if (_isTrackingMouse)
Input::Mouse->SetRelativeMode(true);
return; return;
} }
SDL_ShowCursor(); SDL_ShowCursor();
//if (_isTrackingMouse)
// Input::Mouse->SetRelativeMode(false);
int32 index = SDL_SYSTEM_CURSOR_DEFAULT; int32 index = SDL_SYSTEM_CURSOR_DEFAULT;
switch (_cursor) switch (_cursor)

View File

@@ -17,31 +17,37 @@ namespace FlaxEngine
/// <summary> /// <summary>
/// Perform window hit test delegate. /// Perform window hit test delegate.
/// </summary> /// </summary>
/// <param name="mouse">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param> /// <param name="mousePosition">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param>
/// <returns>Hit result.</returns> /// <returns>Hit result.</returns>
public delegate WindowHitCodes HitTestDelegate(ref Float2 mouse); public delegate WindowHitCodes HitTestDelegate(ref Float2 mousePosition);
/// <summary> /// <summary>
/// Perform mouse buttons action. /// Perform mouse buttons action.
/// </summary> /// </summary>
/// <param name="mouse">The mouse position.</param> /// <param name="mousePosition">The mouse position.</param>
/// <param name="button">The mouse buttons state.</param> /// <param name="button">The mouse buttons state.</param>
/// <param name="handled">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.</param> /// <param name="handled">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.</param>
public delegate void MouseButtonDelegate(ref Float2 mouse, MouseButton button, ref bool handled); public delegate void MouseButtonDelegate(ref Float2 mousePosition, MouseButton button, ref bool handled);
/// <summary> /// <summary>
/// Perform mouse move action. /// Perform mouse move action.
/// </summary> /// </summary>
/// <param name="mouse">The mouse position.</param> /// <param name="mousePosition">The mouse position.</param>
public delegate void MouseMoveDelegate(ref Float2 mouse); public delegate void MouseMoveDelegate(ref Float2 mousePosition);
/// <summary>
/// Perform mouse move action in relative mode.
/// </summary>
/// <param name="mouseMotion">The relative mouse motion.</param>
public delegate void MouseMoveRelativeDelegate(ref Float2 mouseMotion);
/// <summary> /// <summary>
/// Perform mouse wheel action. /// Perform mouse wheel action.
/// </summary> /// </summary>
/// <param name="mouse">The mouse position.</param> /// <param name="mousePosition">The mouse position.</param>
/// <param name="delta">The mouse wheel move delta (can be positive or negative; normalized to [-1;1] range).</param> /// <param name="delta">The mouse wheel move delta (can be positive or negative; normalized to [-1;1] range).</param>
/// <param name="handled">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.</param> /// <param name="handled">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.</param>
public delegate void MouseWheelDelegate(ref Float2 mouse, float delta, ref bool handled); public delegate void MouseWheelDelegate(ref Float2 mousePosition, float delta, ref bool handled);
/// <summary> /// <summary>
/// Perform touch action. /// Perform touch action.
@@ -99,9 +105,14 @@ namespace FlaxEngine
public event MouseWheelDelegate MouseWheel; public event MouseWheelDelegate MouseWheel;
/// <summary> /// <summary>
/// Event fired when mouse moves /// Event fired when mouse moves.
/// </summary> /// </summary>
public event MouseMoveDelegate MouseMove; public event MouseMoveDelegate MouseMove;
/// <summary>
/// Event fired when mouse moves in relative mode.
/// </summary>
public event MouseMoveRelativeDelegate MouseMoveRelative;
/// <summary> /// <summary>
/// Event fired when mouse leaves window. /// Event fired when mouse leaves window.
@@ -273,6 +284,12 @@ namespace FlaxEngine
MouseMove?.Invoke(ref pos); MouseMove?.Invoke(ref pos);
GUI.OnMouseMove(pos); GUI.OnMouseMove(pos);
} }
internal void Internal_OnMouseMoveRelative(ref Float2 mouseMotion)
{
MouseMoveRelative?.Invoke(ref mouseMotion);
GUI.OnMouseMoveRelative(mouseMotion);
}
internal void Internal_OnMouseLeave() internal void Internal_OnMouseLeave()
{ {

View File

@@ -265,19 +265,38 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
} }
case WM_LBUTTONDBLCLK: 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; result = true;
break; break;
} }
case WM_RBUTTONDBLCLK: 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; result = true;
break; break;
} }
case WM_MBUTTONDBLCLK: 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; result = true;
break; break;
} }

View File

@@ -771,6 +771,15 @@ namespace FlaxEngine.GUI
Tooltip.OnMouseLeaveControl(this); Tooltip.OnMouseLeaveControl(this);
} }
} }
/// <summary>
/// When mouse moves over control's area while mouse is in relative mode
/// </summary>
/// <param name="mouseMotion">Mouse relative motion</param>
[NoAnimate]
public virtual void OnMouseMoveRelative(Float2 mouseMotion)
{
}
/// <summary> /// <summary>
/// When mouse leaves control's area /// When mouse leaves control's area

View File

@@ -338,5 +338,17 @@ namespace FlaxEngine.GUI
base.OnMouseMove(location); base.OnMouseMove(location);
} }
/// <inheritdoc />
public override void OnMouseMoveRelative(Float2 mouseMotion)
{
if (_trackingControl != null)
{
_trackingControl.OnMouseMoveRelative(mouseMotion);
return;
}
base.OnMouseMoveRelative(mouseMotion);
}
} }
} }