namespace Sandbox;
public static partial class Input
{
[ConVar( "ui_input_force_vendor", ConVarFlags.Protected )]
private static bool ForceDefaultVendor { get; set; } = false;
static Texture LoadGlyphTexture( string vendor, string key, bool outline = false, InputGlyphSize size = InputGlyphSize.Small )
{
key = key.ToLowerInvariant();
var px = size.ToPixels();
if ( outline )
{
var outlinePath = $"/ui/glyphs/{vendor}/outline/{key}.svg?w={px}&h={px}";
var tx = Texture.Load( outlinePath, false );
if ( tx is not null ) return tx;
}
var path = $"/ui/glyphs/{vendor}/{key}.svg?w={px}&h={px}";
return Texture.Load( path, false );
}
///
/// Tries to load a glyph texture, will seek the current vendor controller (Xbox, PlayStation, Nintendo) and fall back to Xbox if not found.
///
///
///
///
///
///
static Texture LoadGlyphTexture( string file, InputGlyphSize size = InputGlyphSize.Small, bool outline = false, bool noController = false )
{
if ( UsingController && !noController )
{
var vendor = CurrentController?.GlyphVendor ?? "xbox";
if ( ForceDefaultVendor ) vendor = "xbox";
// Did we find our vendor's texture?
if ( LoadGlyphTexture( vendor, file.ToLowerInvariant(), outline, size ) is Texture vendorTex ) return vendorTex;
// Try using xbox
if ( LoadGlyphTexture( "xbox", file.ToLowerInvariant(), outline, size ) is Texture xboxTex ) return xboxTex;
}
return LoadGlyphTexture( "default", file.ToLowerInvariant(), outline, size );
}
///
/// Some keys can't be parsed by files because they're symbols, so we change them into something readable
///
///
///
static string GetButtonName( string key )
{
return key.ToLowerInvariant() switch
{
"/" => "slash",
"\\" => "backslash",
"." => "period",
"," => "comma",
"-" => "minus",
"=" => "equals",
"'" => "apostrophe",
"`" => "backquote",
"[" => "leftbracket",
"]" => "rightbracket",
"rwin" => "windows",
"lwin" => "windows",
_ => key
};
}
///
/// Get a glyph texture from the controller bound to the action.
/// If no binding is found will return an 'UNBOUND' glyph.
///
/// You should update your UI with this every frame, it's very cheap to call and context can change.
public static Texture GetGlyph( string name, InputGlyphSize size = InputGlyphSize.Small, bool outline = false )
{
var action = InputActions?
.FirstOrDefault( x => string.Equals( x.Name, name, StringComparison.OrdinalIgnoreCase ) );
if ( action is null )
{
return LoadGlyphTexture( "unknown", size, outline );
}
var key = GetButtonOrigin( action ).ToLowerInvariant();
key = GetButtonName( key );
if ( string.IsNullOrEmpty( key ) ) key = "UNBOUND";
if ( UsingController )
{
key = $"{action.GamepadCode.ToString().ToLowerInvariant()}";
}
// Find an existing texture
var tx = LoadGlyphTexture( key, size, outline );
if ( tx is not null ) return tx;
// Fall back to an empty glyph
return LoadGlyphTexture( "unknown", size, outline );
}
///
public static Texture GetGlyph( string name, InputGlyphSize size = InputGlyphSize.Small, GlyphStyle style = default )
{
return GetGlyph( name, size, false );
}
///
/// Get a glyph texture from an analog input on a controller.
///
public static Texture GetGlyph( InputAnalog analog, InputGlyphSize size = InputGlyphSize.Small, bool outline = false )
{
return LoadGlyphTexture( analog.ToString(), size, outline );
}
///
/// Returns the name of the analog axis bound to this .
/// For example:
/// Input.GetButtonOrigin( InputAnalog.Move )
/// could return Left Joystick
///
///
public static string GetButtonOrigin( InputAnalog analog )
{
// TODO: better naming
return analog.ToString();
}
///
/// Keyboard related glyph methods.
///
public static partial class Keyboard
{
///
/// Get a glyph texture from a specific key name.
///
///
///
///
///
public static Texture GetGlyph( string key, InputGlyphSize size = InputGlyphSize.Small, bool outline = false )
{
if ( string.IsNullOrEmpty( key ) ) key = "UNBOUND";
key = GetButtonName( key );
return LoadGlyphTexture( key, size, outline, noController: true );
}
}
}
public enum InputGlyphSize
{
///
/// Small 32x32 ( Keyboard glyphs can be wider for long key names )
///
Small,
///
/// Medium 128x128 ( Keyboard glyphs can be wider for long key names )
///
Medium,
///
/// Large 256x256 ( Keyboard glyphs can be wider for long key names )
///
Large
}
public static partial class SandboxGameExtensions
{
///
/// Translates this enum to pixel size.
///
public static int ToPixels( this InputGlyphSize size ) => size switch
{
InputGlyphSize.Small => 32,
InputGlyphSize.Medium => 128,
InputGlyphSize.Large => 256,
_ => 32,
};
}
public struct GlyphStyle
{
///
/// Face buttons will have colored labels/outlines on a knocked out background
/// Rest of inputs will have white detail/borders on a knocked out background
///
public static readonly GlyphStyle Knockout = new();
///
/// Black detail/borders on a white background
///
public static readonly GlyphStyle Light = new();
///
/// White detail/borders on a black background
///
public static readonly GlyphStyle Dark = new();
//
// Modifiers
//
// Default ABXY/PS equivalent glyphs have a solid fill w/ color matching the physical buttons on the device
///
/// ABXY Buttons will match the base style color instead of their normal associated color
///
public readonly GlyphStyle WithNeutralColorABXY() => new();
///
/// ABXY Buttons will have a solid fill
///
public readonly GlyphStyle WithSolidABXY() => new();
}
///
/// Internal bit flags for glyph styles, matches Steam Input ones.
///
[Flags]
internal enum GlyphStyleMask
{
//
// Base-styles - cannot mix
//
Knockout = 0x00,
Light = 0x01,
Dark = 0x02,
//
// Modifiers
//
NeutralColorABXY = 0x10,
SolidABXY = 0x20,
}