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;
}
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)
{
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)
{
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)
{
resultAsRef = Float2.Zero;
resultAsRef = new Float2(1280, 720);
var gameWin = Windows.GameWin;
if (gameWin != null)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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