Files
sbox-public/engine/Sandbox.Engine/Systems/UI/Controls/Image.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

130 lines
3.2 KiB
C#

namespace Sandbox.UI
{
/// <summary>
/// A generic box that displays a given texture within itself.
/// </summary>
[Library( "image" ), Alias( "img" ), Expose]
public partial class Image : Panel
{
/// <summary>
/// The texture being displayed by this panel.
/// </summary>
public Texture Texture { get; set; }
public override bool HasContent => Texture != null;
public Image()
{
YogaNode.SetMeasureFunction( MeasureTexture );
}
/// <summary>
/// Set <see cref="Texture"/> from a file path. URLs supported.
/// </summary>
public async void SetTexture( string name )
{
if ( string.IsNullOrWhiteSpace( name ) ) return;
if ( !IsValid ) return;
Texture = await Texture.LoadAsync( name );
if ( !IsValid ) return;
YogaNode.MarkDirty(); // Update MeasureTexture
}
float oldScaleToScreen = 1.0f;
internal override void PreLayout( LayoutCascade cascade )
{
base.PreLayout( cascade );
if ( ScaleToScreen != oldScaleToScreen )
{
YogaNode.MarkDirty();
}
}
internal override void DrawContent( PanelRenderer renderer, ref RenderState state )
{
if ( Texture == null )
return;
if ( renderer is PanelRenderer pr )
{
var length = ComputedStyle.ObjectFit switch
{
ObjectFit.Contain => Length.Contain,
ObjectFit.Cover => Length.Cover,
ObjectFit.Fill => Length.Percent( 100 ).Value,
_ => Length.Auto,
};
pr.DrawBackgroundTexture( this, Texture, state, length );
}
}
public override void SetProperty( string name, string value )
{
base.SetProperty( name, value );
if ( name == "src" ) SetTexture( value );
}
internal Vector2 MeasureTexture( YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode )
{
if ( !Texture.IsValid() ) return default;
try
{
var (w, h) = (Texture.Width, Texture.Height);
var exact = YGMeasureMode.YGMeasureModeExactly;
var atMost = YGMeasureMode.YGMeasureModeAtMost;
oldScaleToScreen = ScaleToScreen;
var ideal = new Vector2( w * ScaleToScreen, h * ScaleToScreen );
if ( widthMode == exact ) return new Vector2( width, h * width / w );
if ( heightMode == exact ) return new Vector2( w * height / h, height );
if ( widthMode == atMost && heightMode == atMost && (width < ideal.x || height < ideal.y) )
{
float scale = Math.Min( width / w, height / h );
return new Vector2( w * scale, h * scale );
}
if ( widthMode == atMost && width < ideal.x ) return new Vector2( width, h * width / w );
if ( heightMode == atMost && height < ideal.y ) return new Vector2( w * height / h, height );
return ideal;
}
catch ( System.Exception e )
{
Log.Warning( e );
return default;
}
}
}
namespace Construct
{
public static class ImageConstructor
{
/// <summary>
/// Create an image with given texture and CSS classname.
/// </summary>
public static Image Image( this PanelCreator self, string image = null, string classname = null )
{
var control = self.panel.AddChild<Image>();
if ( image != null )
control.SetTexture( image );
if ( classname != null )
control.AddClass( classname );
return control;
}
}
}
}