diff --git a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs index 051ab9ad3c..b2f6d32927 100644 --- a/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs +++ b/osu.Game/Screens/Play/HUD/SkinnableElementTargetContainer.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Extensions; using osu.Game.Skinning; @@ -15,30 +17,46 @@ namespace osu.Game.Screens.Play.HUD public SkinnableTarget Target { get; } + public IBindableList Components => components; + + private readonly BindableList components = new BindableList(); + public SkinnableElementTargetContainer(SkinnableTarget target) { Target = target; } - public IReadOnlyList Children => content?.Children; - public void Reload() { + ClearInternal(); + components.Clear(); + content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetWrapper; - ClearInternal(); - if (content != null) - LoadComponentAsync(content, AddInternal); + { + LoadComponentAsync(content, wrapper => + { + AddInternal(wrapper); + components.AddRange(wrapper.Children.OfType()); + }); + } } - 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); + components.Add(component); } public IEnumerable CreateSerialisedChildren() => - content.Select(d => d.CreateSerialisedInformation()); + components.Select(d => ((Drawable)d).CreateSerialisedInformation()); protected override void SkinChanged(ISkinSource skin, bool allowFallback) { diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index c4a8860bb6..887346b5df 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.EnumExtensions; @@ -207,27 +208,24 @@ namespace osu.Game.Screens.Play Vector2 lowestScreenSpace = Vector2.Zero; - // TODO: may be null during skin switching. not sure if there's a better way of exposing these children. - if (mainComponents.Children != null) + // LINQ cast can be removed when IDrawable interface includes Anchor / RelativeSizeAxes. + foreach (var element in mainComponents.Components.Cast()) { - 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)) - continue; + // 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)) + continue; - // 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) - continue; + // 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) + continue; - var bottomRight = element.ScreenSpaceDrawQuad.BottomRight; - if (bottomRight.Y > lowestScreenSpace.Y) - lowestScreenSpace = bottomRight; - } - - topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y; - bottomRightElements.Y = -Progress.Height; + var bottomRight = element.ScreenSpaceDrawQuad.BottomRight; + if (bottomRight.Y > lowestScreenSpace.Y) + lowestScreenSpace = bottomRight; } + + topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(lowestScreenSpace).Y; + bottomRightElements.Y = -Progress.Height; } private void updateVisibility() diff --git a/osu.Game/Skinning/ISkinnableTarget.cs b/osu.Game/Skinning/ISkinnableTarget.cs index 2379fd4247..4d97b42e8e 100644 --- a/osu.Game/Skinning/ISkinnableTarget.cs +++ b/osu.Game/Skinning/ISkinnableTarget.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; - namespace osu.Game.Skinning { /// @@ -20,6 +18,6 @@ namespace osu.Game.Skinning /// /// Add the provided item to this target. /// - public void Add(Drawable drawable); + public void Add(ISkinnableComponent drawable); } } diff --git a/osu.Game/Skinning/SkinnableTargetWrapper.cs b/osu.Game/Skinning/SkinnableTargetWrapper.cs index a0df610488..0d16775f51 100644 --- a/osu.Game/Skinning/SkinnableTargetWrapper.cs +++ b/osu.Game/Skinning/SkinnableTargetWrapper.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; namespace osu.Game.Skinning { /// - /// 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 . /// Will also optionally apply default cross-element layout dependencies when initialised from a non-deserialised source. /// public class SkinnableTargetWrapper : Container, ISkinSerialisable