2021-05-06 15:16:16 +09: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 17:48:08 +09:00
using System ;
2021-05-10 22:36:20 +09:00
using System.Linq ;
2021-10-11 16:11:15 +09:00
using System.Threading ;
2021-05-11 17:48:08 +09:00
using osu.Framework.Bindables ;
2021-05-11 11:56:14 +09:00
using osu.Framework.Graphics ;
2023-02-15 17:24:34 +09:00
using osu.Framework.Graphics.Containers ;
2021-05-06 15:16:16 +09:00
2021-05-12 15:59:33 +09:00
namespace osu.Game.Skinning
2021-05-06 15:16:16 +09:00
{
2023-02-15 18:31:55 +09:00
/// <summary>
/// A container which holds many skinnable components, with functionality to add, remove and reload layouts.
/// Used to allow user customisation of skin layouts.
/// </summary>
/// <remarks>
/// This is currently used as a means of serialising skin layouts to files.
/// Currently, one json file in a skin will represent one <see cref="SkinComponentsContainer"/>, containing
/// the output of <see cref="ISerialisableDrawableContainer.CreateSerialisedInfo"/>.
/// </remarks>
public partial class SkinComponentsContainer : SkinReloadableDrawable , ISerialisableDrawableContainer
2021-05-06 15:16:16 +09:00
{
2023-02-15 17:24:34 +09:00
private Container ? content ;
2021-05-10 22:36:20 +09:00
2023-02-17 14:18:05 +09:00
/// <summary>
/// The lookup criteria which will be used to retrieve components from the active skin.
/// </summary>
2023-02-15 18:31:55 +09:00
public SkinComponentsContainerLookup Lookup { get ; }
2021-05-07 19:13:38 +09:00
2023-02-15 16:01:26 +09:00
public IBindableList < ISerialisableDrawable > Components = > components ;
2021-05-11 17:48:08 +09:00
2023-02-15 16:01:26 +09:00
private readonly BindableList < ISerialisableDrawable > components = new BindableList < ISerialisableDrawable > ( ) ;
2021-05-11 17:48:08 +09:00
2021-08-24 16:54:19 +09: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 15:18:27 +09:00
2021-06-16 19:52:58 +09:00
public bool ComponentsLoaded { get ; private set ; }
2022-11-09 13:44:59 +09:00
private CancellationTokenSource ? cancellationSource ;
2021-10-11 16:11:15 +09:00
2023-02-15 18:31:55 +09:00
public SkinComponentsContainer ( SkinComponentsContainerLookup lookup )
2021-05-07 19:13:38 +09:00
{
2023-02-15 18:31:55 +09:00
Lookup = lookup ;
2021-05-07 19:13:38 +09:00
}
2023-02-15 18:31:55 +09:00
public void Reload ( ) = > Reload ( CurrentSkin . GetDrawableComponent ( Lookup ) as Container ) ;
2023-02-03 18:53:09 +09:00
2023-02-15 17:24:34 +09:00
public void Reload ( Container ? componentsContainer )
2021-05-11 11:56:14 +09:00
{
ClearInternal ( ) ;
2021-05-11 17:48:08 +09:00
components . Clear ( ) ;
2021-06-16 19:52:58 +09:00
ComponentsLoaded = false ;
2021-05-11 17:48:08 +09:00
2023-02-16 19:25:55 +09:00
content = componentsContainer ? ? new Container
{
RelativeSizeAxes = Axes . Both
} ;
2021-05-11 11:56:14 +09:00
2021-10-11 16:11:15 +09:00
cancellationSource ? . Cancel ( ) ;
cancellationSource = null ;
2023-02-20 19:53:04 +09:00
LoadComponentAsync ( content , wrapper = >
2021-05-11 17:48:08 +09:00
{
2023-02-20 19:53:04 +09:00
AddInternal ( wrapper ) ;
components . AddRange ( wrapper . Children . OfType < ISerialisableDrawable > ( ) ) ;
2021-06-16 19:52:58 +09:00
ComponentsLoaded = true ;
2023-02-20 19:53:04 +09:00
} , ( cancellationSource = new CancellationTokenSource ( ) ) . Token ) ;
2021-05-11 11:56:14 +09:00
}
2023-02-15 16:28:42 +09:00
/// <inheritdoc cref="ISerialisableDrawableContainer"/>
2021-05-13 13:13:22 +09: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>
2023-02-15 16:01:26 +09:00
public void Add ( ISerialisableDrawable component )
2021-05-11 11:56:14 +09:00
{
2021-05-11 17:48:08 +09: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 22:16:37 +02:00
throw new ArgumentException ( $"Provided argument must be of type {nameof(Drawable)}." , nameof ( component ) ) ;
2021-05-11 17:48:08 +09:00
2021-05-11 11:56:14 +09:00
content . Add ( drawable ) ;
2021-05-11 17:48:08 +09:00
components . Add ( component ) ;
2021-05-11 11:56:14 +09:00
}
2023-02-15 16:28:42 +09:00
/// <inheritdoc cref="ISerialisableDrawableContainer"/>
2021-05-14 16:03:22 +09: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>
2023-02-22 17:45:38 +09:00
public void Remove ( ISerialisableDrawable component , bool disposeImmediately )
2021-05-14 16:03:22 +09:00
{
if ( content = = null )
2021-05-14 22:15:43 +02: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 16:03:22 +09:00
if ( ! ( component is Drawable drawable ) )
2021-05-14 22:16:37 +02:00
throw new ArgumentException ( $"Provided argument must be of type {nameof(Drawable)}." , nameof ( component ) ) ;
2021-05-14 16:03:22 +09:00
2023-02-22 17:45:38 +09:00
content . Remove ( drawable , disposeImmediately ) ;
2021-05-14 16:03:22 +09:00
components . Remove ( component ) ;
}
2021-05-27 14:50:42 +09:00
protected override void SkinChanged ( ISkinSource skin )
2021-05-07 18:18:29 +09:00
{
2021-05-27 14:50:42 +09:00
base . SkinChanged ( skin ) ;
2021-05-07 18:18:29 +09:00
2021-05-11 11:56:14 +09:00
Reload ( ) ;
2021-05-07 18:18:29 +09:00
}
2021-05-06 15:16:16 +09:00
}
}