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,27 +208,24 @@ 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.
{ if (!element.Anchor.HasFlagFast(Anchor.TopRight) && !element.RelativeSizeAxes.HasFlagFast(Axes.X))
// for now align top-right components with the bottom-edge of the lowest top-anchored hud element. continue;
if (!element.Anchor.HasFlagFast(Anchor.TopRight) && !element.RelativeSizeAxes.HasFlagFast(Axes.X))
continue;
// health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area.
if (element is LegacyHealthDisplay) if (element is LegacyHealthDisplay)
continue; continue;
var bottomRight = element.ScreenSpaceDrawQuad.BottomRight; var bottomRight = element.ScreenSpaceDrawQuad.BottomRight;
if (bottomRight.Y > lowestScreenSpace.Y) if (bottomRight.Y > lowestScreenSpace.Y)
lowestScreenSpace = bottomRight; lowestScreenSpace = bottomRight;
}
topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y;
bottomRightElements.Y = -Progress.Height;
} }
topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y;
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