Improve cursor clipping logic (fix after alt+tab and confine to game window in Editor)

#691
This commit is contained in:
Wojtek Figat
2022-07-17 00:09:59 +02:00
parent fe0e24357a
commit 7f3a32a69b
7 changed files with 65 additions and 69 deletions

View File

@@ -1292,43 +1292,29 @@ namespace FlaxEditor
return false; return false;
} }
internal void Internal_ScreenToGameViewport(ref Float2 pos) internal void Internal_ScreenToGameViewport(out Float2 pos)
{ {
if (Windows.GameWin != null && Windows.GameWin.ContainsFocus) pos = Float2.Zero;
//if (Windows.GameWin != null && Windows.GameWin.ContainsFocus)
{ {
var win = Windows.GameWin.Root; var win = Windows.GameWin?.Root;
if (win?.RootWindow is WindowRootControl root && root.Window && root.Window.IsFocused) if (win?.RootWindow is WindowRootControl root && root.Window && root.Window.IsFocused)
{ {
pos = Float2.Round(Windows.GameWin.Viewport.PointFromScreen(pos) * root.DpiScale); pos = Float2.Round(Windows.GameWin.Viewport.PointFromScreen(pos) * root.DpiScale);
} }
else
{
pos = Float2.Minimum;
}
}
else
{
pos = Float2.Minimum;
} }
} }
internal void Internal_GameViewportToScreen(ref Float2 pos) internal void Internal_GameViewportToScreen(out Float2 pos)
{ {
if (Windows.GameWin != null && Windows.GameWin.ContainsFocus) pos = Float2.Zero;
//if (Windows.GameWin != null && Windows.GameWin.ContainsFocus)
{ {
var win = Windows.GameWin.Root; var win = Windows.GameWin?.Root;
if (win?.RootWindow is WindowRootControl root && root.Window && root.Window.IsFocused) if (win?.RootWindow is WindowRootControl root && root.Window && root.Window.IsFocused)
{ {
pos = Float2.Round(Windows.GameWin.Viewport.PointToScreen(pos / root.DpiScale)); pos = Float2.Round(Windows.GameWin.Viewport.PointToScreen(pos / root.DpiScale));
} }
else
{
pos = Float2.Minimum;
}
}
else
{
pos = Float2.Minimum;
} }
} }
@@ -1345,7 +1331,7 @@ namespace FlaxEditor
internal void Internal_GetGameWindowSize(out Float2 resultAsRef) internal void Internal_GetGameWindowSize(out Float2 resultAsRef)
{ {
resultAsRef = Float2.Zero; resultAsRef = new Float2(1280, 720);
var gameWin = Windows.GameWin; var gameWin = Windows.GameWin;
if (gameWin != null) if (gameWin != null)
{ {

View File

@@ -25,6 +25,7 @@ namespace FlaxEditor.Windows
private float _gameStartTime; private float _gameStartTime;
private GUI.Docking.DockState _maximizeRestoreDockState; private GUI.Docking.DockState _maximizeRestoreDockState;
private GUI.Docking.DockPanel _maximizeRestoreDockTo; private GUI.Docking.DockPanel _maximizeRestoreDockTo;
private CursorLockMode _cursorLockMode = CursorLockMode.None;
/// <summary> /// <summary>
/// Gets the viewport. /// Gets the viewport.
@@ -464,6 +465,8 @@ namespace FlaxEditor.Windows
if (Editor.Windows.PropertiesWin.IsDocked) if (Editor.Windows.PropertiesWin.IsDocked)
Editor.Windows.PropertiesWin.Focus(); Editor.Windows.PropertiesWin.Focus();
Screen.CursorVisible = true; Screen.CursorVisible = true;
if (Screen.CursorLock == CursorLockMode.Clipped)
Screen.CursorLock = CursorLockMode.None;
} }
} }
@@ -522,11 +525,18 @@ namespace FlaxEditor.Windows
{ {
base.OnStartContainsFocus(); base.OnStartContainsFocus();
// Center mouse in play mode if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
if (CenterMouseOnFocus && Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
{ {
var center = PointToWindow(Size * 0.5f); // Center mouse in play mode
Root.MousePosition = center; if (CenterMouseOnFocus)
{
var center = PointToWindow(Size * 0.5f);
Root.MousePosition = center;
}
// Restore lock mode
if (_cursorLockMode != CursorLockMode.None)
Screen.CursorLock = _cursorLockMode;
} }
} }
@@ -537,6 +547,9 @@ namespace FlaxEditor.Windows
// Restore cursor visibility (could be hidden by the game) // Restore cursor visibility (could be hidden by the game)
Screen.CursorVisible = true; Screen.CursorVisible = true;
// Cache lock mode
_cursorLockMode = Screen.CursorLock;
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -19,7 +19,6 @@ static CursorLockMode CursorLock = CursorLockMode::None;
class ScreenService : public EngineService class ScreenService : public EngineService
{ {
public: public:
ScreenService() ScreenService()
: EngineService(TEXT("Screen"), 120) : EngineService(TEXT("Screen"), 120)
{ {
@@ -89,7 +88,7 @@ Float2 Screen::GameViewportToScreen(const Float2& viewportPos)
bool Screen::GetCursorVisible() bool Screen::GetCursorVisible()
{ {
#if USE_EDITOR #if USE_EDITOR
const auto win = Editor::Managed->GetGameWindow(); const auto win = Editor::Managed->GetGameWindow(true);
#else #else
const auto win = Engine::MainWindow; const auto win = Engine::MainWindow;
#endif #endif
@@ -99,7 +98,7 @@ bool Screen::GetCursorVisible()
void Screen::SetCursorVisible(const bool value) void Screen::SetCursorVisible(const bool value)
{ {
#if USE_EDITOR #if USE_EDITOR
const auto win = Editor::Managed->GetGameWindow(); const auto win = Editor::Managed->GetGameWindow(true);
#else #else
const auto win = Engine::MainWindow; const auto win = Engine::MainWindow;
#endif #endif
@@ -117,13 +116,18 @@ CursorLockMode Screen::GetCursorLock()
void Screen::SetCursorLock(CursorLockMode mode) void Screen::SetCursorLock(CursorLockMode mode)
{ {
#if USE_EDITOR #if USE_EDITOR
const auto win = Editor::Managed->GetGameWindow(); const auto win = Editor::Managed->GetGameWindow(true);
#else #else
const auto win = Engine::MainWindow; const auto win = Engine::MainWindow;
#endif #endif
if (win && mode == CursorLockMode::Clipped) if (win && mode == CursorLockMode::Clipped)
{ {
win->StartClippingCursor(win->GetClientBounds()); #if USE_EDITOR
Rectangle bounds(Editor::Managed->GameViewportToScreen(Float2::Zero), Editor::Managed->GetGameWindowSize());
#else
Rectangle bounds = win->GetClientBounds();
#endif
win->StartClippingCursor(bounds);
} }
else if (win && CursorLock == CursorLockMode::Clipped) else if (win && CursorLock == CursorLockMode::Clipped)
{ {

View File

@@ -537,6 +537,8 @@ void WindowBase::Hide()
{ {
if (!_visible) if (!_visible)
return; return;
EndClippingCursor();
EndTrackingMouse();
_visible = false; _visible = false;
_showAfterFirstPaint = _settings.ShowAfterFirstPaint; _showAfterFirstPaint = _settings.ShowAfterFirstPaint;
Hidden(); Hidden();
@@ -560,7 +562,6 @@ void WindowBase::Close(ClosingReason reason)
} }
// Close // Close
EndTrackingMouse();
Hide(); Hide();
OnClosed(); OnClosed();
} }

View File

@@ -271,10 +271,9 @@ API_INJECT_CODE(cpp, "#include \"Engine/Platform/Window.h\"");
API_CLASS(NoSpawn, NoConstructor, Sealed, Name="Window") API_CLASS(NoSpawn, NoConstructor, Sealed, Name="Window")
class FLAXENGINE_API WindowBase : public ScriptingObject class FLAXENGINE_API WindowBase : public ScriptingObject
{ {
DECLARE_SCRIPTING_TYPE_NO_SPAWN(WindowBase); DECLARE_SCRIPTING_TYPE_NO_SPAWN(WindowBase);
friend GPUSwapChain; friend GPUSwapChain;
protected: protected:
bool _visible, _minimized, _maximized, _isClosing, _showAfterFirstPaint, _focused; bool _visible, _minimized, _maximized, _isClosing, _showAfterFirstPaint, _focused;
GPUSwapChain* _swapChain; GPUSwapChain* _swapChain;
CreateWindowSettings _settings; CreateWindowSettings _settings;
@@ -294,7 +293,6 @@ protected:
virtual ~WindowBase(); virtual ~WindowBase();
public: public:
/// <summary> /// <summary>
/// The rendering task for that window. /// The rendering task for that window.
/// </summary> /// </summary>
@@ -336,7 +334,6 @@ public:
Action Draw; Action Draw;
public: public:
// Returns true if that window is the main Engine window (works in both editor and game mode) // Returns true if that window is the main Engine window (works in both editor and game mode)
bool IsMain() const; bool IsMain() const;
@@ -405,7 +402,6 @@ public:
API_PROPERTY() virtual void* GetNativePtr() const = 0; API_PROPERTY() virtual void* GetNativePtr() const = 0;
public: public:
/// <summary> /// <summary>
/// Performs the UI update. /// Performs the UI update.
/// </summary> /// </summary>
@@ -471,11 +467,9 @@ public:
/// <summary> /// <summary>
/// Checks if window is foreground (the window with which the user is currently working). /// Checks if window is foreground (the window with which the user is currently working).
/// </summary> /// </summary>
/// <returns>True if window is foreground, otherwise false.</returns>
API_PROPERTY() virtual bool IsForegroundWindow() const; API_PROPERTY() virtual bool IsForegroundWindow() const;
public: public:
/// <summary> /// <summary>
/// Gets the client bounds of the window (client area not including border). /// Gets the client bounds of the window (client area not including border).
/// </summary> /// </summary>
@@ -585,8 +579,8 @@ public:
{ {
return Platform::CustomDpiScale * _dpiScale; return Platform::CustomDpiScale * _dpiScale;
} }
public:
public:
/// <summary> /// <summary>
/// Gets the window title. /// Gets the window title.
/// </summary> /// </summary>
@@ -652,7 +646,6 @@ public:
} }
public: public:
/// <summary> /// <summary>
/// Starts drag and drop operation /// Starts drag and drop operation
/// </summary> /// </summary>
@@ -674,7 +667,6 @@ public:
/// <summary> /// <summary>
/// Gets the mouse tracking offset. /// Gets the mouse tracking offset.
/// </summary> /// </summary>
/// <returns>The mouse screen offset.</returns>
API_PROPERTY() Float2 GetTrackingMouseOffset() const API_PROPERTY() Float2 GetTrackingMouseOffset() const
{ {
return _trackingMouseOffset; return _trackingMouseOffset;
@@ -698,7 +690,7 @@ public:
/// <summary> /// <summary>
/// Starts the cursor clipping. /// Starts the cursor clipping.
/// </summary> /// </summary>
/// <param name="bounds">The bounds that the cursor will be confined to.</param> /// <param name="bounds">The screen-space bounds that the cursor will be confined to.</param>
API_FUNCTION() virtual void StartClippingCursor(const Rectangle& bounds) API_FUNCTION() virtual void StartClippingCursor(const Rectangle& bounds)
{ {
} }
@@ -721,7 +713,6 @@ public:
/// <summary> /// <summary>
/// Gets the mouse cursor. /// Gets the mouse cursor.
/// </summary> /// </summary>
/// <returns>The cursor type</returns>
API_PROPERTY() FORCE_INLINE CursorType GetCursor() const API_PROPERTY() FORCE_INLINE CursorType GetCursor() const
{ {
return _cursor; return _cursor;
@@ -755,7 +746,6 @@ public:
API_PROPERTY() void SetRenderingEnabled(bool value); API_PROPERTY() void SetRenderingEnabled(bool value);
public: public:
typedef Delegate<Char> CharDelegate; typedef Delegate<Char> CharDelegate;
typedef Delegate<KeyboardKeys> KeyboardDelegate; typedef Delegate<KeyboardKeys> KeyboardDelegate;
typedef Delegate<const Float2&> MouseDelegate; typedef Delegate<const Float2&> MouseDelegate;
@@ -882,7 +872,6 @@ public:
void OnClosing(ClosingReason reason, bool& cancel); void OnClosing(ClosingReason reason, bool& cancel);
public: public:
/// <summary> /// <summary>
/// Gets the text entered during the current frame (Unicode). /// Gets the text entered during the current frame (Unicode).
/// </summary> /// </summary>
@@ -911,11 +900,9 @@ public:
API_FUNCTION() bool GetKeyUp(KeyboardKeys key) const; API_FUNCTION() bool GetKeyUp(KeyboardKeys key) const;
public: public:
/// <summary> /// <summary>
/// Gets the mouse position in window coordinates. /// Gets the mouse position in window coordinates.
/// </summary> /// </summary>
/// <returns>Mouse cursor coordinates</returns>
API_PROPERTY() Float2 GetMousePosition() const; API_PROPERTY() Float2 GetMousePosition() const;
/// <summary> /// <summary>
@@ -933,7 +920,6 @@ public:
/// <summary> /// <summary>
/// Gets the mouse wheel change during the last frame. /// Gets the mouse wheel change during the last frame.
/// </summary> /// </summary>
/// <returns>Mouse wheel value delta</returns>
API_PROPERTY() float GetMouseScrollDelta() const; API_PROPERTY() float GetMouseScrollDelta() const;
/// <summary> /// <summary>
@@ -958,7 +944,6 @@ public:
API_FUNCTION() bool GetMouseButtonUp(MouseButton button) const; API_FUNCTION() bool GetMouseButtonUp(MouseButton button) const;
public: public:
void OnShow(); void OnShow();
void OnResize(int32 width, int32 height); void OnResize(int32 width, int32 height);
void OnClosed(); void OnClosed();
@@ -966,14 +951,12 @@ public:
void OnLostFocus(); void OnLostFocus();
private: private:
void OnMainRenderTaskDelete(class ScriptingObject* obj) void OnMainRenderTaskDelete(class ScriptingObject* obj)
{ {
RenderTask = nullptr; RenderTask = nullptr;
} }
public: public:
// [ScriptingObject] // [ScriptingObject]
String ToString() const override; String ToString() const override;
void OnDeleteObject() override; void OnDeleteObject() override;

View File

@@ -519,7 +519,6 @@ void WindowsWindow::SetOpacity(const float opacity)
void WindowsWindow::Focus() void WindowsWindow::Focus()
{ {
ASSERT(HasHWND()); ASSERT(HasHWND());
if (GetFocus() != _handle) if (GetFocus() != _handle)
{ {
SetFocus(_handle); SetFocus(_handle);
@@ -566,20 +565,18 @@ void WindowsWindow::EndTrackingMouse()
void WindowsWindow::StartClippingCursor(const Rectangle& bounds) void WindowsWindow::StartClippingCursor(const Rectangle& bounds)
{ {
ASSERT(HasHWND()); _isClippingCursor = true;
*(RECT*)_clipCursorRect = {
if (!_isClippingCursor) (LONG)bounds.GetUpperLeft().X,
{ (LONG)bounds.GetUpperLeft().Y,
_isClippingCursor = true; (LONG)bounds.GetBottomRight().X,
} (LONG)bounds.GetBottomRight().Y
const RECT lpRect = {
bounds.GetUpperLeft().X,
bounds.GetUpperLeft().Y,
bounds.GetBottomRight().X,
bounds.GetBottomRight().Y
}; };
ClipCursor(&lpRect); if (IsFocused())
{
_clipCursorSet = true;
ClipCursor((RECT*)_clipCursorRect);
}
} }
void WindowsWindow::EndClippingCursor() void WindowsWindow::EndClippingCursor()
@@ -587,8 +584,8 @@ void WindowsWindow::EndClippingCursor()
if (_isClippingCursor) if (_isClippingCursor)
{ {
_isClippingCursor = false; _isClippingCursor = false;
_clipCursorSet = false;
ClipCursor(NULL); ClipCursor(nullptr);
} }
} }
@@ -1094,8 +1091,18 @@ LRESULT WindowsWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
break; break;
case WM_SETFOCUS: case WM_SETFOCUS:
OnGotFocus(); OnGotFocus();
if (_isClippingCursor && !_clipCursorSet)
{
_clipCursorSet = true;
ClipCursor((RECT*)_clipCursorRect);
}
break; break;
case WM_KILLFOCUS: case WM_KILLFOCUS:
if (_clipCursorSet)
{
_clipCursorSet = false;
ClipCursor(nullptr);
}
OnLostFocus(); OnLostFocus();
break; break;
case WM_ACTIVATEAPP: case WM_ACTIVATEAPP:

View File

@@ -27,9 +27,11 @@ private:
bool _isResizing = false; bool _isResizing = false;
bool _isSwitchingFullScreen = false; bool _isSwitchingFullScreen = false;
bool _trackingMouse = false; bool _trackingMouse = false;
bool _clipCursorSet = false;
bool _isDuringMaximize = false; bool _isDuringMaximize = false;
Windows::HANDLE _monitor = nullptr; Windows::HANDLE _monitor = nullptr;
Float2 _clientSize; Float2 _clientSize;
Windows::LONG _clipCursorRect[4];
int32 _regionWidth = 0, _regionHeight = 0; int32 _regionWidth = 0, _regionHeight = 0;
public: public: