namespace Sandbox.UI { /// /// A generic box that displays a given texture within itself. /// [Library( "image" ), Alias( "img" ), Expose] public partial class Image : Panel { /// /// The texture being displayed by this panel. /// public Texture Texture { get; set; } public override bool HasContent => Texture != null; public Image() { YogaNode.SetMeasureFunction( MeasureTexture ); } /// /// Set from a file path. URLs supported. /// 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 { /// /// Create an image with given texture and CSS classname. /// public static Image Image( this PanelCreator self, string image = null, string classname = null ) { var control = self.panel.AddChild(); if ( image != null ) control.SetTexture( image ); if ( classname != null ) control.AddClass( classname ); return control; } } } }