Use ISkinnableComponent wherever possible (and expose as BindableList)

This commit is contained in:
Dean Herbert 2021-05-11 17:48:08 +09:00
parent 61ea3f2e64
commit a88a8b7d8d
4 changed files with 42 additions and 28 deletions

View File

@ -1,8 +1,10 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -15,30 +17,46 @@ namespace osu.Game.Screens.Play.HUD
public SkinnableTarget Target { get; } public SkinnableTarget Target { get; }
public IBindableList<ISkinnableComponent> Components => components;
private readonly BindableList<ISkinnableComponent> components = new BindableList<ISkinnableComponent>();
public SkinnableElementTargetContainer(SkinnableTarget target) public SkinnableElementTargetContainer(SkinnableTarget target)
{ {
Target = target; Target = target;
} }
public IReadOnlyList<Drawable> Children => content?.Children;
public void Reload() public void Reload()
{ {
ClearInternal();
components.Clear();
content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper;
ClearInternal();
if (content != null) if (content != null)
LoadComponentAsync(content, AddInternal); {
LoadComponentAsync(content, wrapper =>
{
AddInternal(wrapper);
components.AddRange(wrapper.Children.OfType<ISkinnableComponent>());
});
}
} }
public void Add(Drawable drawable) public void Add(ISkinnableComponent 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(ISkinnableComponent)}.", nameof(drawable));
content.Add(drawable); content.Add(drawable);
components.Add(component);
} }
public IEnumerable<SkinnableInfo> CreateSerialisedChildren() => public IEnumerable<SkinnableInfo> CreateSerialisedChildren() =>
content.Select(d => d.CreateSerialisedInformation()); components.Select(d => ((Drawable)d).CreateSerialisedInformation());
protected override void SkinChanged(ISkinSource skin, bool allowFallback) protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.EnumExtensions;
@ -207,10 +208,8 @@ namespace osu.Game.Screens.Play
Vector2 lowestScreenSpace = Vector2.Zero; Vector2 lowestScreenSpace = Vector2.Zero;
// TODO: may be null during skin switching. not sure if there's a better way of exposing these children. // LINQ cast can be removed when IDrawable interface includes Anchor / RelativeSizeAxes.
if (mainComponents.Children != null) foreach (var element in mainComponents.Components.Cast<Drawable>())
{
foreach (var element in mainComponents.Children)
{ {
// for now align top-right components with the bottom-edge of the lowest top-anchored hud element. // for now align top-right components with the bottom-edge of the lowest top-anchored hud element.
if (!element.Anchor.HasFlagFast(Anchor.TopRight) && !element.RelativeSizeAxes.HasFlagFast(Axes.X)) if (!element.Anchor.HasFlagFast(Anchor.TopRight) && !element.RelativeSizeAxes.HasFlagFast(Axes.X))
@ -228,7 +227,6 @@ namespace osu.Game.Screens.Play
topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y; topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y;
bottomRightElements.Y = -Progress.Height; bottomRightElements.Y = -Progress.Height;
} }
}
private void updateVisibility() private void updateVisibility()
{ {

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
/// <summary> /// <summary>
@ -20,6 +18,6 @@ namespace osu.Game.Skinning
/// <summary> /// <summary>
/// Add the provided item to this target. /// Add the provided item to this target.
/// </summary> /// </summary>
public void Add(Drawable drawable); public void Add(ISkinnableComponent drawable);
} }
} }

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
/// <summary> /// <summary>
/// A container which is serialised and can encapsulate multiple skinnable elements into a single return type. /// A container which is serialised and can encapsulate multiple skinnable elements into a single return type (for consumption via <see cref="ISkin.GetDrawableComponent"/>.
/// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source.
/// </summary> /// </summary>
public class SkinnableTargetWrapper : Container, ISkinSerialisable public class SkinnableTargetWrapper : Container, ISkinSerialisable