mirror of
https://github.com/ppy/osu.git
synced 2024-12-17 21:32:55 +08:00
111 lines
4.6 KiB
C#
111 lines
4.6 KiB
C#
// 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.
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using osu.Framework.Bindables;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
|
|
namespace osu.Game.Skinning
|
|
{
|
|
/// <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="SkinnableContainer"/>, containing
|
|
/// the output of <see cref="ISerialisableDrawableContainer.CreateSerialisedInfo"/>.
|
|
/// </remarks>
|
|
public partial class SkinnableContainer : SkinReloadableDrawable, ISerialisableDrawableContainer
|
|
{
|
|
private Container? content;
|
|
|
|
/// <summary>
|
|
/// The lookup criteria which will be used to retrieve components from the active skin.
|
|
/// </summary>
|
|
public GlobalSkinnableContainerLookup Lookup { get; }
|
|
|
|
public IBindableList<ISerialisableDrawable> Components => components;
|
|
|
|
private readonly BindableList<ISerialisableDrawable> components = new BindableList<ISerialisableDrawable>();
|
|
|
|
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).
|
|
|
|
public bool ComponentsLoaded { get; private set; }
|
|
|
|
private CancellationTokenSource? cancellationSource;
|
|
|
|
public SkinnableContainer(GlobalSkinnableContainerLookup lookup)
|
|
{
|
|
Lookup = lookup;
|
|
}
|
|
|
|
public void Reload() => Reload((
|
|
CurrentSkin.GetDrawableComponent(new UserSkinComponentLookup(Lookup))
|
|
?? CurrentSkin.GetDrawableComponent(Lookup))
|
|
as Container);
|
|
|
|
public void Reload(Container? componentsContainer)
|
|
{
|
|
ClearInternal();
|
|
components.Clear();
|
|
ComponentsLoaded = false;
|
|
|
|
content = componentsContainer ?? new Container
|
|
{
|
|
RelativeSizeAxes = Axes.Both
|
|
};
|
|
|
|
cancellationSource?.Cancel();
|
|
cancellationSource = null;
|
|
|
|
LoadComponentAsync(content, wrapper =>
|
|
{
|
|
AddInternal(wrapper);
|
|
components.AddRange(wrapper.Children.OfType<ISerialisableDrawable>());
|
|
ComponentsLoaded = true;
|
|
}, (cancellationSource = new CancellationTokenSource()).Token);
|
|
}
|
|
|
|
/// <inheritdoc cref="ISerialisableDrawableContainer"/>
|
|
/// <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 Add(ISerialisableDrawable component)
|
|
{
|
|
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))
|
|
throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(component));
|
|
|
|
content.Add(drawable);
|
|
components.Add(component);
|
|
}
|
|
|
|
/// <inheritdoc cref="ISerialisableDrawableContainer"/>
|
|
/// <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(ISerialisableDrawable component, bool disposeImmediately)
|
|
{
|
|
if (content == null)
|
|
throw new NotSupportedException("Attempting to remove a new component from a target container which is not supported by the current skin.");
|
|
|
|
if (!(component is Drawable drawable))
|
|
throw new ArgumentException($"Provided argument must be of type {nameof(Drawable)}.", nameof(component));
|
|
|
|
content.Remove(drawable, disposeImmediately);
|
|
components.Remove(component);
|
|
}
|
|
|
|
protected override void SkinChanged(ISkinSource skin)
|
|
{
|
|
base.SkinChanged(skin);
|
|
|
|
Reload();
|
|
}
|
|
}
|
|
}
|