2019-07-19 17:38:24 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2019-01-24 16:43:03 +08:00
// See the LICENCE file in the repository root for full licence text.
2018-04-13 17:19:50 +08:00
using System ;
2019-07-18 16:52:50 +08:00
using osu.Framework.Caching ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Graphics ;
2020-07-13 19:12:50 +08:00
using osu.Framework.Graphics.Animations ;
2018-11-20 15:51:59 +08:00
using osuTK ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Skinning
{
2019-06-24 14:25:01 +08:00
/// <summary>
/// A drawable which can be skinned via an <see cref="ISkinSource"/>.
/// </summary>
2019-07-19 17:38:24 +08:00
public class SkinnableDrawable : SkinReloadableDrawable
2018-04-13 17:19:50 +08:00
{
2018-09-27 16:33:27 +08:00
/// <summary>
2019-07-19 17:38:24 +08:00
/// The displayed component.
2018-09-27 16:33:27 +08:00
/// </summary>
2019-08-28 18:57:17 +08:00
public Drawable Drawable { get ; private set ; }
2018-09-27 15:27:11 +08:00
2020-03-31 14:28:50 +08:00
public new Axes AutoSizeAxes
{
get = > base . AutoSizeAxes ;
set = > base . AutoSizeAxes = value ;
}
2019-08-30 13:39:02 +08:00
private readonly ISkinComponent component ;
2018-04-13 17:19:50 +08:00
2019-07-18 11:10:28 +08:00
private readonly ConfineMode confineMode ;
2018-04-13 17:19:50 +08:00
/// <summary>
2019-06-24 14:25:01 +08:00
/// Create a new skinnable drawable.
2018-04-13 17:19:50 +08:00
/// </summary>
2019-08-30 13:39:02 +08:00
/// <param name="component">The namespace-complete resource name for this skinnable element.</param>
2018-04-13 17:19:50 +08:00
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
2019-07-18 11:10:28 +08:00
/// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param>
2019-12-06 13:40:45 +08:00
public SkinnableDrawable ( ISkinComponent component , Func < ISkinComponent , Drawable > defaultImplementation , Func < ISkinSource , bool > allowFallback = null , ConfineMode confineMode = ConfineMode . NoScaling )
2019-08-30 13:39:02 +08:00
: this ( component , allowFallback , confineMode )
2019-06-24 13:39:20 +08:00
{
createDefault = defaultImplementation ;
}
2019-12-06 13:40:45 +08:00
protected SkinnableDrawable ( ISkinComponent component , Func < ISkinSource , bool > allowFallback = null , ConfineMode confineMode = ConfineMode . NoScaling )
2018-09-27 15:27:11 +08:00
: base ( allowFallback )
2018-04-13 17:19:50 +08:00
{
2019-08-30 13:39:02 +08:00
this . component = component ;
2019-07-18 11:10:28 +08:00
this . confineMode = confineMode ;
2018-04-13 17:19:50 +08:00
RelativeSizeAxes = Axes . Both ;
}
2020-07-13 19:12:50 +08:00
/// <summary>
/// Seeks to the 0-th frame if the content of this <see cref="SkinnableDrawable"/> is an <see cref="IFramedAnimation"/>.
/// </summary>
public void ResetAnimation ( ) = > ( Drawable as IFramedAnimation ) ? . GotoFrame ( 0 ) ;
2019-08-30 13:39:02 +08:00
private readonly Func < ISkinComponent , Drawable > createDefault ;
2019-06-24 14:27:46 +08:00
2019-07-18 16:52:50 +08:00
private readonly Cached scaling = new Cached ( ) ;
private bool isDefault ;
2019-08-30 13:39:02 +08:00
protected virtual Drawable CreateDefault ( ISkinComponent component ) = > createDefault ( component ) ;
2019-06-24 14:27:46 +08:00
2019-06-24 14:25:01 +08:00
/// <summary>
2019-07-18 11:10:28 +08:00
/// Whether to apply size restrictions (specified via <see cref="confineMode"/>) to the default implementation.
2019-06-24 14:25:01 +08:00
/// </summary>
protected virtual bool ApplySizeRestrictionsToDefault = > false ;
2019-06-24 13:39:20 +08:00
2018-04-13 17:19:50 +08:00
protected override void SkinChanged ( ISkinSource skin , bool allowFallback )
{
2019-08-30 13:39:02 +08:00
Drawable = skin . GetDrawableComponent ( component ) ;
2018-09-27 15:27:11 +08:00
2019-07-18 16:52:50 +08:00
isDefault = false ;
2019-06-24 13:39:20 +08:00
if ( Drawable = = null & & allowFallback )
{
2019-08-30 13:39:02 +08:00
Drawable = CreateDefault ( component ) ;
2019-06-24 13:39:20 +08:00
isDefault = true ;
}
2018-09-27 15:27:11 +08:00
if ( Drawable ! = null )
2018-04-13 17:19:50 +08:00
{
2019-07-18 16:52:50 +08:00
scaling . Invalidate ( ) ;
Drawable . Origin = Anchor . Centre ;
Drawable . Anchor = Anchor . Centre ;
InternalChild = Drawable ;
}
else
ClearInternal ( ) ;
}
protected override void Update ( )
{
base . Update ( ) ;
if ( ! scaling . IsValid )
{
2019-07-18 17:39:33 +08:00
try
2018-04-13 17:19:50 +08:00
{
2019-07-18 17:39:33 +08:00
if ( Drawable = = null | | ( isDefault & & ! ApplySizeRestrictionsToDefault ) ) return ;
2019-07-18 11:10:28 +08:00
2019-07-18 17:39:33 +08:00
switch ( confineMode )
2019-07-18 11:10:28 +08:00
{
2020-03-31 22:35:23 +08:00
case ConfineMode . ScaleToFit :
Drawable . RelativeSizeAxes = Axes . Both ;
Drawable . Size = Vector2 . One ;
Drawable . Scale = Vector2 . One ;
Drawable . FillMode = FillMode . Fit ;
2019-07-18 17:39:33 +08:00
break ;
2019-07-18 11:10:28 +08:00
}
2019-07-18 17:39:33 +08:00
}
finally
{
scaling . Validate ( ) ;
}
2018-04-13 17:19:50 +08:00
}
}
}
2019-07-18 11:10:28 +08:00
public enum ConfineMode
{
/// <summary>
/// Don't apply any scaling. This allows the user element to be of any size, exceeding specified bounds.
/// </summary>
NoScaling ,
ScaleToFit ,
}
2018-04-13 17:19:50 +08:00
}