Files
sbox-public/game/addons/base/code/UI/DropDown.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

211 lines
4.7 KiB
C#

using Sandbox.UI.Construct;
namespace Sandbox.UI
{
/// <summary>
/// A UI control which provides multiple options via a dropdown box.
/// </summary>
[Library( "select" ), Alias( "dropdown" )]
public class DropDown : PopupButton
{
/// <summary>
/// The icon of an arrow pointing down on the right of the element.
/// </summary>
protected IconPanel DropdownIndicator;
/// <summary>
/// Called when the value has been changed,
/// </summary>
[Parameter] public System.Action<string> ValueChanged { get; set; }
/// <summary>
/// Called just before opening, allows options to be dynamic
/// </summary>
[Parameter] public System.Func<List<Option>> BuildOptions { get; set; }
/// <summary>
/// The options to show on click. You can edit these directly via this property.
/// </summary>
[Parameter] public List<Option> Options { get; set; } = new();
Option selected;
object _value;
int _valueHash;
/// <summary>
/// The current string value. This is useful to have if Selected is null.
/// </summary>
[Parameter]
public override object Value
{
get => _value;
set
{
if ( _valueHash == HashCode.Combine( value ) )
return;
if ( $"{_value}" == $"{value}" )
return;
_valueHash = HashCode.Combine( value );
_value = value;
if ( BuildOptions != null )
{
Options = BuildOptions.Invoke();
}
if ( _value != null && Options.Count == 0 )
{
PopulateOptionsFromType( _value.GetType() );
}
Select( _value?.ToString(), false );
}
}
/// <summary>
/// The currently selected option.
/// </summary>
public Option Selected
{
get => selected;
set
{
if ( selected == value ) return;
selected = value;
if ( selected != null )
{
Value = $"{selected.Value}";
Icon = selected.Icon;
Text = selected.Title;
ValueChanged?.Invoke( Value.ToString() );
CreateEvent( "onchange" );
CreateValueEvent( "value", selected?.Value );
}
}
}
public DropDown()
{
AddClass( "dropdown" );
DropdownIndicator = Add.Icon( "expand_more", "dropdown_indicator" );
}
public DropDown( Panel parent ) : this()
{
Parent = parent;
}
public override void SetPropertyObject( string name, object value )
{
base.SetPropertyObject( name, value );
}
/// <summary>
/// Given the type, populate options. This is useful if you're an enum type.
/// </summary>
private void PopulateOptionsFromType( Type type )
{
if ( type == typeof( bool ) )
{
Options.Add( new Option( "True", true ) );
Options.Add( new Option( "False", false ) );
return;
}
if ( type.IsEnum )
{
var names = type.GetEnumNames();
var values = type.GetEnumValues();
for ( int i = 0; i < names.Length; i++ )
{
Options.Add( new Option( names[i], values.GetValue( i ) ) );
}
return;
}
//Log.Info( $"Dropdown Type: {type}" );
}
/// <summary>
/// Open the dropdown.
/// </summary>
public override void Open()
{
Popup = new Popup( this, Popup.PositionMode.BelowStretch, 0.0f );
Popup.CloseWhenParentIsHidden = true;
Popup.AddClass( "flat-top" );
if ( BuildOptions != null )
{
Options = BuildOptions.Invoke();
}
foreach ( var option in Options )
{
var o = Popup.AddOption( option.Title, option.Icon, () => Select( option ) );
if ( Selected != null && option.Value == Selected.Value )
{
o.AddClass( "active" );
}
}
}
/// <summary>
/// Select an option.
/// </summary>
protected virtual void Select( Option option, bool triggerChange = true )
{
if ( !triggerChange )
{
selected = option;
if ( option != null )
{
Value = option.Value;
Icon = option.Icon;
Text = option.Title;
}
}
else
{
Selected = option;
}
}
/// <summary>
/// Select an option by value string.
/// </summary>
protected virtual void Select( string value, bool triggerChange = true )
{
Select( Options.FirstOrDefault( x => string.Equals( x.Value.ToString(), value, StringComparison.OrdinalIgnoreCase ) ), triggerChange );
}
protected override void OnParametersSet()
{
// Only clear if we have some options to populate
if ( Children.Any( x => x.ElementName.Equals( "option", StringComparison.OrdinalIgnoreCase ) ) ) Options.Clear();
foreach ( var child in Children )
{
if ( child.ElementName.Equals( "option", StringComparison.OrdinalIgnoreCase ) )
{
var o = new Option();
o.Title = string.Join( "", child.Descendants.OfType<Label>().Select( x => x.Text ) );
o.Value = child.GetAttribute( "value", o.Title );
o.Icon = child.GetAttribute( "icon", null );
Options.Add( o );
}
}
}
}
}