2019-08-27 21:44:02 +02:00
using System ;
2019-08-09 22:23:16 +02:00
using Microsoft.Xna.Framework ;
using Microsoft.Xna.Framework.Graphics ;
2022-04-25 15:25:58 +02:00
using MLEM.Graphics ;
2019-11-02 14:53:59 +01:00
using MLEM.Misc ;
2019-08-09 22:23:16 +02:00
using MLEM.Textures ;
2019-10-14 21:28:12 +02:00
using MLEM.Ui.Style ;
2022-08-20 11:39:28 +02:00
#if FNA
using MLEM.Extensions ;
#endif
2019-08-09 22:23:16 +02:00
namespace MLEM.Ui.Elements {
2020-05-22 17:02:24 +02:00
/// <summary>
/// An image element to be used inside of a <see cref="UiSystem"/>.
/// An image is simply an element that displays a supplied <see cref="TextureRegion"/> and optionally allows for the texture region to remain at its original aspect ratio, regardless of the element's size.
/// </summary>
2019-08-09 22:23:16 +02:00
public class Image : Element {
2020-05-22 17:02:24 +02:00
/// <summary>
/// The color to render the image at
/// </summary>
2019-10-14 21:28:12 +02:00
public StyleProp < Color > Color ;
2020-05-22 17:02:24 +02:00
/// <summary>
/// A callback to retrieve the <see cref="TextureRegion"/> that this image should render.
/// This can be used if the image changes frequently.
/// </summary>
2019-09-26 17:39:38 +02:00
public TextureCallback GetTextureCallback ;
2020-05-22 17:02:24 +02:00
/// <summary>
/// The texture that this <see cref="TextureRegion"/> should render
/// </summary>
2019-09-11 21:01:08 +02:00
public TextureRegion Texture {
2020-05-17 00:59:15 +02:00
get {
2022-12-21 21:47:49 +01:00
var ret = this . GetTextureCallback ? . Invoke ( this ) ? ? this . texture ;
this . CheckTextureChange ( ret ) ;
return ret ;
2020-05-17 00:59:15 +02:00
}
2019-09-11 21:01:08 +02:00
set {
2022-12-21 21:47:49 +01:00
this . texture = value ;
this . CheckTextureChange ( value ) ;
2019-09-11 21:01:08 +02:00
}
}
2020-05-22 17:02:24 +02:00
/// <summary>
/// Whether this image element's <see cref="Element.Size"/> should be based on the size of the <see cref="TextureRegion"/> given.
2021-10-29 17:11:45 +02:00
/// Note that, when scaling to the image's size, the <see cref="Element.Scale"/> is also taken into account.
2020-05-22 17:02:24 +02:00
/// </summary>
2019-09-11 21:01:08 +02:00
public bool ScaleToImage {
get = > this . scaleToImage ;
set {
if ( this . scaleToImage ! = value ) {
this . scaleToImage = value ;
this . SetAreaDirty ( ) ;
}
}
}
2020-05-22 17:02:24 +02:00
/// <summary>
/// Whether to cause the <see cref="TextureRegion"/> to be rendered at its proper aspect ratio.
/// If this is false, the image will be stretched according to this component's size.
/// </summary>
2019-08-27 21:44:02 +02:00
public bool MaintainImageAspect = true ;
2020-05-22 17:02:24 +02:00
/// <summary>
/// The <see cref="SpriteEffects"/> that the texture should be rendered with
/// </summary>
2019-09-12 18:44:24 +02:00
public SpriteEffects ImageEffects = SpriteEffects . None ;
2020-05-22 17:02:24 +02:00
/// <summary>
/// The scale that the image should be rendered with
/// </summary>
2019-09-12 18:44:24 +02:00
public Vector2 ImageScale = Vector2 . One ;
2020-05-22 17:02:24 +02:00
/// <summary>
/// The rotation that the image should be rendered with.
/// Note that increased rotation does not increase this component's size, even if the rotated texture would go out of bounds of this component.
/// </summary>
2019-09-12 18:44:24 +02:00
public float ImageRotation ;
2019-08-09 22:23:16 +02:00
2022-12-21 21:47:49 +01:00
/// <inheritdoc />
public override bool IsHidden = > base . IsHidden | | this . Texture = = null ;
2021-10-30 15:01:04 +02:00
private bool scaleToImage ;
private TextureRegion texture ;
2022-12-21 21:47:49 +01:00
private TextureRegion lastTexture ;
2021-10-30 15:01:04 +02:00
2020-05-22 17:02:24 +02:00
/// <summary>
/// Creates a new image with the given settings
/// </summary>
/// <param name="anchor">The image's anchor</param>
/// <param name="size">The image's size</param>
/// <param name="texture">The texture the image should render</param>
/// <param name="scaleToImage">Whether this image's size should be based on the texture's size</param>
2019-08-09 22:23:16 +02:00
public Image ( Anchor anchor , Vector2 size , TextureRegion texture , bool scaleToImage = false ) : base ( anchor , size ) {
2019-09-26 17:39:38 +02:00
this . Texture = texture ;
this . scaleToImage = scaleToImage ;
this . CanBeSelected = false ;
this . CanBeMoused = false ;
}
2020-05-22 17:02:24 +02:00
/// <inheritdoc cref="Image(Anchor,Vector2,TextureRegion,bool)"/>
2019-09-26 17:39:38 +02:00
public Image ( Anchor anchor , Vector2 size , TextureCallback getTextureCallback , bool scaleToImage = false ) : base ( anchor , size ) {
this . GetTextureCallback = getTextureCallback ;
this . Texture = getTextureCallback ( this ) ;
2019-09-11 21:01:08 +02:00
this . scaleToImage = scaleToImage ;
2019-08-28 18:27:17 +02:00
this . CanBeSelected = false ;
2019-09-11 20:10:28 +02:00
this . CanBeMoused = false ;
2019-08-09 22:23:16 +02:00
}
2020-05-22 17:02:24 +02:00
/// <inheritdoc />
2019-11-02 14:53:59 +01:00
protected override Vector2 CalcActualSize ( RectangleF parentArea ) {
2021-10-29 17:11:45 +02:00
return this . Texture ! = null & & this . scaleToImage ? this . Texture . Size . ToVector2 ( ) * this . Scale : base . CalcActualSize ( parentArea ) ;
2019-09-26 17:39:38 +02:00
}
2020-05-22 17:02:24 +02:00
/// <inheritdoc />
2022-04-25 15:25:58 +02:00
public override void Draw ( GameTime time , SpriteBatch batch , float alpha , SpriteBatchContext context ) {
2020-05-17 00:59:15 +02:00
if ( this . Texture = = null )
2019-09-26 17:39:38 +02:00
return ;
2020-05-17 00:59:15 +02:00
var center = new Vector2 ( this . Texture . Width / 2F , this . Texture . Height / 2F ) ;
2019-11-05 13:28:41 +01:00
var color = this . Color . OrDefault ( Microsoft . Xna . Framework . Color . White ) * alpha ;
2019-08-27 21:44:02 +02:00
if ( this . MaintainImageAspect ) {
2020-05-17 00:59:15 +02:00
var scale = Math . Min ( this . DisplayArea . Width / this . Texture . Width , this . DisplayArea . Height / this . Texture . Height ) ;
var imageOffset = new Vector2 ( this . DisplayArea . Width / 2F - this . Texture . Width * scale / 2 , this . DisplayArea . Height / 2F - this . Texture . Height * scale / 2 ) ;
batch . Draw ( this . Texture , this . DisplayArea . Location + center * scale + imageOffset , color , this . ImageRotation , center , scale * this . ImageScale , this . ImageEffects , 0 ) ;
2019-08-27 21:44:02 +02:00
} else {
2020-05-17 00:59:15 +02:00
var scale = new Vector2 ( 1F / this . Texture . Width , 1F / this . Texture . Height ) * this . DisplayArea . Size ;
batch . Draw ( this . Texture , this . DisplayArea . Location + center * scale , color , this . ImageRotation , center , scale * this . ImageScale , this . ImageEffects , 0 ) ;
2019-08-27 21:44:02 +02:00
}
2022-04-25 15:25:58 +02:00
base . Draw ( time , batch , alpha , context ) ;
2019-08-09 22:23:16 +02:00
}
2022-12-21 21:47:49 +01:00
private void CheckTextureChange ( TextureRegion newTexture ) {
if ( this . lastTexture = = newTexture )
return ;
var nullChanged = this . lastTexture = = null ! = ( newTexture = = null ) ;
this . lastTexture = newTexture ;
if ( nullChanged | | this . scaleToImage )
this . SetAreaDirty ( ) ;
}
2020-05-22 17:02:24 +02:00
/// <summary>
/// A delegate method used for <see cref="Image.GetTextureCallback"/>
/// </summary>
/// <param name="image">The current image element</param>
2019-09-26 17:39:38 +02:00
public delegate TextureRegion TextureCallback ( Image image ) ;
2019-08-09 22:23:16 +02:00
}
2022-06-17 18:23:47 +02:00
}