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, }