mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-11 07:48:36 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
159 lines
3.3 KiB
C#
159 lines
3.3 KiB
C#
using Sandbox.Engine;
|
|
|
|
namespace Sandbox.UI;
|
|
|
|
/// <summary>
|
|
/// Handles input focus for <see cref="Panel"/>s.
|
|
/// </summary>
|
|
public class InputFocus
|
|
{
|
|
/// <summary>
|
|
/// The panel that currently has input focus.
|
|
/// </summary>
|
|
public static Panel Current
|
|
{
|
|
get => GlobalContext.Current.UISystem.CurrentFocus;
|
|
internal set => GlobalContext.Current.UISystem.CurrentFocus = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The panel that will have the input focus next.
|
|
/// </summary>
|
|
public static Panel Next
|
|
{
|
|
get => GlobalContext.Current.UISystem.NextFocus;
|
|
internal set => GlobalContext.Current.UISystem.NextFocus = value;
|
|
}
|
|
|
|
static bool PendingChange
|
|
{
|
|
get => GlobalContext.Current.UISystem.FocusPendingChange;
|
|
set => GlobalContext.Current.UISystem.FocusPendingChange = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the focus to this panel (or its nearest ancestor with AcceptsFocus).
|
|
/// Note that <see cref="Current"/> won't change until the next frame.
|
|
/// </summary>
|
|
public static bool Set( Panel panel )
|
|
{
|
|
return TrySetOrParent( panel );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear focus away from this panel.
|
|
/// </summary>
|
|
public static bool Clear( Panel panel )
|
|
{
|
|
Next = null;
|
|
PendingChange = true;
|
|
|
|
Set( panel?.Parent );
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear keyboard focus
|
|
/// </summary>
|
|
public static bool Clear()
|
|
{
|
|
if ( Current == null )
|
|
return false;
|
|
|
|
Next = null;
|
|
PendingChange = true;
|
|
return true;
|
|
}
|
|
|
|
static bool TrySetOrParent( Panel panel )
|
|
{
|
|
if ( panel == null ) return false;
|
|
if ( Next == panel ) return true;
|
|
|
|
//
|
|
// Note that we're not judging eligibility based on styles here
|
|
// That happens in the Tick because those styles might not have been
|
|
// calculated yet.
|
|
//
|
|
|
|
if ( panel.AcceptsFocus )
|
|
{
|
|
Next = panel;
|
|
PendingChange = true;
|
|
return true;
|
|
}
|
|
|
|
return TrySetOrParent( panel.Parent );
|
|
}
|
|
|
|
internal static void Tick()
|
|
{
|
|
// TODO - maintain - make sure Current/Next can still be the focus
|
|
// move the focus up if they can't
|
|
|
|
// TODO - make sure current selected is a child of one of the root panels
|
|
// if the panel was removed then it's no good to us
|
|
|
|
//
|
|
// If our focus became uneligable then defocus
|
|
//
|
|
if ( Current != null )
|
|
{
|
|
if ( !IsPanelEligibleForFocus( Current ) )
|
|
{
|
|
if ( !PendingChange || Next == Current )
|
|
{
|
|
// TODO - should we shift focus to a parent
|
|
// or should that logic be in panels?
|
|
Next = null;
|
|
PendingChange = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Don't swap to an uneligable panel
|
|
//
|
|
if ( PendingChange && Next != null )
|
|
{
|
|
if ( !Next.AcceptsFocus )
|
|
{
|
|
Next = null;
|
|
PendingChange = false;
|
|
}
|
|
}
|
|
|
|
|
|
if ( PendingChange )
|
|
{
|
|
PendingChange = false;
|
|
|
|
if ( Current != Next )
|
|
{
|
|
if ( Current != null )
|
|
{
|
|
Panel.Switch( PseudoClass.Focus, false, Current, Next );
|
|
Current.CreateEvent( new PanelEvent( "onblur", Current ) );
|
|
}
|
|
|
|
Current = Next;
|
|
|
|
Panel.Switch( PseudoClass.Focus, true, Current, null );
|
|
Current?.CreateEvent( new PanelEvent( "onfocus", Current ) );
|
|
}
|
|
}
|
|
|
|
//Log.Info( $"InputRouter.NeedsKeyboardInput = {InputRouter.NeedsKeyboardInput} ({Current})" );
|
|
Next = null;
|
|
}
|
|
|
|
static bool IsPanelEligibleForFocus( Panel panel )
|
|
{
|
|
if ( !panel.IsVisible ) return false;
|
|
if ( !panel.AcceptsFocus ) return false;
|
|
|
|
return true;
|
|
}
|
|
}
|