2021-05-06 06:16:16 +00:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2021-05-11 08:48:08 +00:00
using System ;
2021-05-10 13:36:20 +00:00
using System.Linq ;
2021-10-11 07:11:15 +00:00
using System.Threading ;
2021-05-11 08:48:08 +00:00
using osu.Framework.Bindables ;
2021-05-11 02:56:14 +00:00
using osu.Framework.Graphics ;
2021-05-06 06:16:16 +00:00
2021-05-12 06:59:33 +00:00
namespace osu.Game.Skinning
2021-05-06 06:16:16 +00:00
{
2021-05-13 08:07:38 +00:00
public partial class SkinnableTargetContainer : SkinReloadableDrawable , ISkinnableTarget
2021-05-06 06:16:16 +00:00
{
2022-11-09 04:44:59 +00:00
private SkinnableTargetComponentsContainer ? content ;
2021-05-10 13:36:20 +00:00
2022-11-09 07:04:56 +00:00
public GlobalSkinComponentLookup . LookupType Target { get ; }
2021-05-07 10:13:38 +00:00
2021-05-13 08:06:00 +00:00
public IBindableList < ISkinnableDrawable > Components = > components ;
2021-05-11 08:48:08 +00:00
2021-05-13 08:06:00 +00:00
private readonly BindableList < ISkinnableDrawable > components = new BindableList < ISkinnableDrawable > ( ) ;
2021-05-11 08:48:08 +00:00
2021-08-24 07:54:19 +00:00
public override bool IsPresent = > base . IsPresent | | Scheduler . HasPendingTasks ; // ensure that components are loaded even if the target container is hidden (ie. due to user toggle).
2021-08-24 06:18:27 +00:00
2021-06-16 10:52:58 +00:00
public bool ComponentsLoaded { get ; private set ; }
2022-11-09 04:44:59 +00:00
private CancellationTokenSource ? cancellationSource ;
2021-10-11 07:11:15 +00:00
2022-11-09 07:04:56 +00:00
public SkinnableTargetContainer ( GlobalSkinComponentLookup . LookupType target )
2021-05-07 10:13:38 +00:00
{
Target = target ;
}
2021-05-13 04:13:22 +00:00
/// <summary>
/// Reload all components in this container from the current skin.
/// </summary>
2021-05-11 02:56:14 +00:00
public void Reload ( )
{
ClearInternal ( ) ;
2021-05-11 08:48:08 +00:00
components . Clear ( ) ;
2021-06-16 10:52:58 +00:00
ComponentsLoaded = false ;
2021-05-11 08:48:08 +00:00
2022-11-09 07:04:56 +00:00
content = CurrentSkin . GetDrawableComponent ( new GlobalSkinComponentLookup ( Target ) ) as SkinnableTargetComponentsContainer ;
2021-05-11 02:56:14 +00:00
2021-10-11 07:11:15 +00:00
cancellationSource ? . Cancel ( ) ;
cancellationSource = null ;
2021-05-11 02:56:14 +00:00
if ( content ! = null )
2021-05-11 08:48:08 +00:00
{
LoadComponentAsync ( content , wrapper = >
{
AddInternal ( wrapper ) ;
2021-05-13 08:06:00 +00:00
components . AddRange ( wrapper . Children . OfType < ISkinnableDrawable > ( ) ) ;
2021-06-16 10:52:58 +00:00
ComponentsLoaded = true ;
2021-10-11 07:11:15 +00:00
} , ( cancellationSource = new CancellationTokenSource ( ) ) . Token ) ;
2021-05-11 08:48:08 +00:00
}
2021-06-16 10:52:58 +00:00
else
ComponentsLoaded = true ;
2021-05-11 02:56:14 +00:00
}
2021-05-14 07:03:22 +00:00
/// <inheritdoc cref="ISkinnableTarget"/>
2021-05-13 04:13:22 +00:00
/// <exception cref="NotSupportedException">Thrown when attempting to add an element to a target which is not supported by the current skin.</exception>
/// <exception cref="ArgumentException">Thrown if the provided instance is not a <see cref="Drawable"/>.</exception>
2021-05-13 08:06:00 +00:00
public void Add ( ISkinnableDrawable component )
2021-05-11 02:56:14 +00:00
{
2021-05-11 08:48:08 +00:00
if ( content = = null )
throw new NotSupportedException ( "Attempting to add a new component to a target container which is not supported by the current skin." ) ;
if ( ! ( component is Drawable drawable ) )
2021-05-14 20:16:37 +00:00
throw new ArgumentException ( $"Provided argument must be of type {nameof(Drawable)}." , nameof ( component ) ) ;
2021-05-11 08:48:08 +00:00
2021-05-11 02:56:14 +00:00
content . Add ( drawable ) ;
2021-05-11 08:48:08 +00:00
components . Add ( component ) ;
2021-05-11 02:56:14 +00:00
}
2021-05-14 07:03:22 +00:00
/// <inheritdoc cref="ISkinnableTarget"/>
/// <exception cref="NotSupportedException">Thrown when attempting to add an element to a target which is not supported by the current skin.</exception>
/// <exception cref="ArgumentException">Thrown if the provided instance is not a <see cref="Drawable"/>.</exception>
public void Remove ( ISkinnableDrawable component )
{
if ( content = = null )
2021-05-14 20:15:43 +00:00
throw new NotSupportedException ( "Attempting to remove a new component from a target container which is not supported by the current skin." ) ;
2021-05-14 07:03:22 +00:00
if ( ! ( component is Drawable drawable ) )
2021-05-14 20:16:37 +00:00
throw new ArgumentException ( $"Provided argument must be of type {nameof(Drawable)}." , nameof ( component ) ) ;
2021-05-14 07:03:22 +00:00
2022-08-26 06:19:05 +00:00
content . Remove ( drawable , true ) ;
2021-05-14 07:03:22 +00:00
components . Remove ( component ) ;
}
2021-05-27 05:50:42 +00:00
protected override void SkinChanged ( ISkinSource skin )
2021-05-07 09:18:29 +00:00
{
2021-05-27 05:50:42 +00:00
base . SkinChanged ( skin ) ;
2021-05-07 09:18:29 +00:00
2021-05-11 02:56:14 +00:00
Reload ( ) ;
2021-05-07 09:18:29 +00:00
}
2021-05-06 06:16:16 +00:00
}
}