Files
sbox-public/engine/Sandbox.Tools/GameMode.cs
2026-07-14 12:34:05 +01:00

101 lines
3.0 KiB
C#

using Sandbox.Engine;
namespace Editor;
/// <summary>
/// Registers a widget with the input system to use SDL and manages
/// inputs and focus as it relates to the editor's game widget.
/// </summary>
public static class GameMode
{
static Widget _inPlay;
/// <summary>
/// Is a render widget the active play widget
/// </summary>
internal static bool IsPlayWidget( SceneRenderingWidget widget ) => widget == _inPlay;
/// <summary>
/// Given a widget, register it for SDL input, and tell the engine this is the swapchain we have
/// </summary>
/// <param name="widget"></param>
public static void SetPlayWidget( SceneRenderingWidget widget )
{
if ( _inPlay == widget ) return;
// Blur before registering so SDL's fresh wrapper can't snapshot this widget as its
// keyboard focus window - relative mouse mode is driven from the main editor window
widget.Blur();
widget.Focused += WidgetFocused;
widget.Blurred += WidgetBlurred;
widget.MouseTracking = true;
widget.MouseMove += OnPlayWidgetMouseMove;
NativeEngine.InputSystem.RegisterWindowWithSDL( widget._widget.winId() );
g_pEngineServiceMgr.SetEngineState( widget._widget.winId(), widget.SwapChain );
// The play widget is where the game renders, so make it the main window: flip the existing
// m_bIsMainWindow flag so GetGPUFrameTimeMS reports the running game's GPU frame time.
g_pRenderDevice.SetSwapChainIsMainWindow( widget.SwapChain, true );
_inPlay = widget;
widget.Focus();
}
public static void ClearPlayMode()
{
if ( _inPlay is null )
return;
_inPlay.Blur();
_inPlay.Focused -= WidgetFocused;
_inPlay.Blurred -= WidgetBlurred;
_inPlay.MouseMove -= OnPlayWidgetMouseMove;
_inPlay.MouseTracking = false;
NativeEngine.InputSystem.UnregisterWindowFromSDL( _inPlay._widget.winId() );
if ( _inPlay is SceneRenderingWidget playWidget )
g_pRenderDevice.SetSwapChainIsMainWindow( playWidget.SwapChain, false );
_inPlay = null;
}
/// <summary>
/// When the editor gains focus of the game widget, tell the input system so it'll mouse capture (if it wants to)
/// </summary>
private static void WidgetFocused( FocusChangeReason reason )
{
if ( _inPlay is null )
return;
NativeEngine.InputSystem.OnEditorGameFocusChange( _inPlay._widget.winId(), true );
}
/// <summary>
/// When the editor loses focus of the game widget, tell the input system so it stops trying to do mouse capture.
/// </summary>
private static void WidgetBlurred( FocusChangeReason reason )
{
if ( _inPlay is null )
return;
NativeEngine.InputSystem.OnEditorGameFocusChange( _inPlay._widget.winId(), false );
}
private static void OnPlayWidgetMouseMove( Vector2 local )
{
// SDL handles position when the widget is focused; only fill in the gap when unfocused.
if ( _inPlay is null || _inPlay.IsFocused )
return;
var pos = new Vector2( (int)local.x, (int)local.y );
var delta = pos - InputRouter.MouseCursorPosition;
InputRouter.OnMousePositionChange( pos.x, pos.y, delta.x, delta.y );
}
}