mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 12:33:01 +08:00
Merge branch 'master' into CompletionText-LocalisableString
This commit is contained in:
commit
15d209d17e
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
if (withModifiedSkin)
|
if (withModifiedSkin)
|
||||||
{
|
{
|
||||||
AddStep("change component scale", () => Player.ChildrenOfType<LegacyScoreCounter>().First().Scale = new Vector2(2f));
|
AddStep("change component scale", () => Player.ChildrenOfType<LegacyScoreCounter>().First().Scale = new Vector2(2f));
|
||||||
AddStep("update target", () => Player.ChildrenOfType<SkinnableTargetContainer>().ForEach(LegacySkin.UpdateDrawableTarget));
|
AddStep("update target", () => Player.ChildrenOfType<SkinComponentsContainer>().ForEach(LegacySkin.UpdateDrawableTarget));
|
||||||
AddStep("exit player", () => Player.Exit());
|
AddStep("exit player", () => Player.Exit());
|
||||||
CreateTest();
|
CreateTest();
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||||
{
|
{
|
||||||
if (lookup is GlobalSkinComponentLookup targetComponent)
|
if (lookup is SkinComponentsContainerLookup containerLookup)
|
||||||
{
|
{
|
||||||
switch (targetComponent.Lookup)
|
switch (containerLookup.Target)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||||
var components = base.GetDrawableComponent(lookup) as Container;
|
var components = base.GetDrawableComponent(lookup) as Container;
|
||||||
|
|
||||||
if (providesComboCounter && components != null)
|
if (providesComboCounter && components != null)
|
||||||
|
@ -74,7 +74,7 @@ namespace osu.Game.Tests.Skins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var editableTypes = SkinnableInfo.GetAllAvailableDrawables().Where(t => (Activator.CreateInstance(t) as ISkinnableDrawable)?.IsEditable == true);
|
var editableTypes = SerialisedDrawableInfo.GetAllAvailableDrawables().Where(t => (Activator.CreateInstance(t) as ISerialisableDrawable)?.IsEditable == true);
|
||||||
|
|
||||||
Assert.That(instantiatedTypes, Is.EquivalentTo(editableTypes));
|
Assert.That(instantiatedTypes, Is.EquivalentTo(editableTypes));
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ namespace osu.Game.Tests.Skins
|
|||||||
var skin = new TestSkin(new SkinInfo(), null, storage);
|
var skin = new TestSkin(new SkinInfo(), null, storage);
|
||||||
|
|
||||||
Assert.That(skin.DrawableComponentInfo, Has.Count.EqualTo(2));
|
Assert.That(skin.DrawableComponentInfo, Has.Count.EqualTo(2));
|
||||||
Assert.That(skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.MainHUDComponents], Has.Length.EqualTo(9));
|
Assert.That(skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.MainHUDComponents], Has.Length.EqualTo(9));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,10 +101,10 @@ namespace osu.Game.Tests.Skins
|
|||||||
var skin = new TestSkin(new SkinInfo(), null, storage);
|
var skin = new TestSkin(new SkinInfo(), null, storage);
|
||||||
|
|
||||||
Assert.That(skin.DrawableComponentInfo, Has.Count.EqualTo(2));
|
Assert.That(skin.DrawableComponentInfo, Has.Count.EqualTo(2));
|
||||||
Assert.That(skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.MainHUDComponents], Has.Length.EqualTo(6));
|
Assert.That(skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.MainHUDComponents], Has.Length.EqualTo(6));
|
||||||
Assert.That(skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.SongSelect], Has.Length.EqualTo(1));
|
Assert.That(skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.SongSelect], Has.Length.EqualTo(1));
|
||||||
|
|
||||||
var skinnableInfo = skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.SongSelect].First();
|
var skinnableInfo = skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.SongSelect].First();
|
||||||
|
|
||||||
Assert.That(skinnableInfo.Type, Is.EqualTo(typeof(SkinnableSprite)));
|
Assert.That(skinnableInfo.Type, Is.EqualTo(typeof(SkinnableSprite)));
|
||||||
Assert.That(skinnableInfo.Settings.First().Key, Is.EqualTo("sprite_name"));
|
Assert.That(skinnableInfo.Settings.First().Key, Is.EqualTo("sprite_name"));
|
||||||
@ -115,10 +115,10 @@ namespace osu.Game.Tests.Skins
|
|||||||
using (var storage = new ZipArchiveReader(stream))
|
using (var storage = new ZipArchiveReader(stream))
|
||||||
{
|
{
|
||||||
var skin = new TestSkin(new SkinInfo(), null, storage);
|
var skin = new TestSkin(new SkinInfo(), null, storage);
|
||||||
Assert.That(skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.MainHUDComponents], Has.Length.EqualTo(8));
|
Assert.That(skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.MainHUDComponents], Has.Length.EqualTo(8));
|
||||||
Assert.That(skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.MainHUDComponents].Select(i => i.Type), Contains.Item(typeof(UnstableRateCounter)));
|
Assert.That(skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].Select(i => i.Type), Contains.Item(typeof(UnstableRateCounter)));
|
||||||
Assert.That(skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.MainHUDComponents].Select(i => i.Type), Contains.Item(typeof(ColourHitErrorMeter)));
|
Assert.That(skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].Select(i => i.Type), Contains.Item(typeof(ColourHitErrorMeter)));
|
||||||
Assert.That(skin.DrawableComponentInfo[GlobalSkinComponentLookup.LookupType.MainHUDComponents].Select(i => i.Type), Contains.Item(typeof(LegacySongProgress)));
|
Assert.That(skin.DrawableComponentInfo[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].Select(i => i.Type), Contains.Item(typeof(LegacySongProgress)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,13 +13,11 @@ using osu.Framework.Timing;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Extensions;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
|
|
||||||
@ -38,8 +36,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestEmptyLegacyBeatmapSkinFallsBack()
|
public void TestEmptyLegacyBeatmapSkinFallsBack()
|
||||||
{
|
{
|
||||||
CreateSkinTest(TrianglesSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null));
|
CreateSkinTest(TrianglesSkin.CreateInfo(), () => new LegacyBeatmapSkin(new BeatmapInfo(), null));
|
||||||
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
|
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinComponentsContainer>().All(c => c.ComponentsLoaded));
|
||||||
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(GlobalSkinComponentLookup.LookupType.MainHUDComponents, skinManager.CurrentSkin.Value));
|
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinComponentsContainerLookup.TargetArea.MainHUDComponents, skinManager.CurrentSkin.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func<ISkin> getBeatmapSkin)
|
protected void CreateSkinTest(SkinInfo gameCurrentSkin, Func<ISkin> getBeatmapSkin)
|
||||||
@ -54,17 +52,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool AssertComponentsFromExpectedSource(GlobalSkinComponentLookup.LookupType target, ISkin expectedSource)
|
protected bool AssertComponentsFromExpectedSource(SkinComponentsContainerLookup.TargetArea target, ISkin expectedSource)
|
||||||
{
|
{
|
||||||
var targetContainer = Player.ChildrenOfType<SkinnableTargetContainer>().First(s => s.Target == target);
|
var targetContainer = Player.ChildrenOfType<SkinComponentsContainer>().First(s => s.Lookup.Target == target);
|
||||||
var actualComponentsContainer = targetContainer.ChildrenOfType<Container>().SingleOrDefault(c => c.Parent == targetContainer);
|
var actualComponentsContainer = targetContainer.ChildrenOfType<Container>().SingleOrDefault(c => c.Parent == targetContainer);
|
||||||
|
|
||||||
if (actualComponentsContainer == null)
|
if (actualComponentsContainer == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var actualInfo = actualComponentsContainer.CreateSkinnableInfo();
|
var actualInfo = actualComponentsContainer.CreateSerialisedInfo();
|
||||||
|
|
||||||
var expectedComponentsContainer = expectedSource.GetDrawableComponent(new GlobalSkinComponentLookup(target)) as Container;
|
var expectedComponentsContainer = expectedSource.GetDrawableComponent(new SkinComponentsContainerLookup(target)) as Container;
|
||||||
if (expectedComponentsContainer == null)
|
if (expectedComponentsContainer == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -85,21 +83,21 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
Add(expectedComponentsAdjustmentContainer);
|
Add(expectedComponentsAdjustmentContainer);
|
||||||
expectedComponentsAdjustmentContainer.UpdateSubTree();
|
expectedComponentsAdjustmentContainer.UpdateSubTree();
|
||||||
var expectedInfo = expectedComponentsContainer.CreateSkinnableInfo();
|
var expectedInfo = expectedComponentsContainer.CreateSerialisedInfo();
|
||||||
Remove(expectedComponentsAdjustmentContainer, true);
|
Remove(expectedComponentsAdjustmentContainer, true);
|
||||||
|
|
||||||
return almostEqual(actualInfo, expectedInfo);
|
return almostEqual(actualInfo, expectedInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool almostEqual(SkinnableInfo info, SkinnableInfo? other) =>
|
private static bool almostEqual(SerialisedDrawableInfo drawableInfo, SerialisedDrawableInfo? other) =>
|
||||||
other != null
|
other != null
|
||||||
&& info.Type == other.Type
|
&& drawableInfo.Type == other.Type
|
||||||
&& info.Anchor == other.Anchor
|
&& drawableInfo.Anchor == other.Anchor
|
||||||
&& info.Origin == other.Origin
|
&& drawableInfo.Origin == other.Origin
|
||||||
&& Precision.AlmostEquals(info.Position, other.Position, 1)
|
&& Precision.AlmostEquals(drawableInfo.Position, other.Position, 1)
|
||||||
&& Precision.AlmostEquals(info.Scale, other.Scale)
|
&& Precision.AlmostEquals(drawableInfo.Scale, other.Scale)
|
||||||
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
|
&& Precision.AlmostEquals(drawableInfo.Rotation, other.Rotation)
|
||||||
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
|
&& drawableInfo.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SerialisedDrawableInfo>(almostEqual));
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
||||||
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
|
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
|
||||||
|
@ -235,8 +235,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
createNew();
|
createNew();
|
||||||
|
|
||||||
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
|
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
|
||||||
AddUntilStep("wait for components to be hidden", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().Single().Alpha == 0);
|
AddUntilStep("wait for components to be hidden", () => hudOverlay.ChildrenOfType<SkinComponentsContainer>().Single().Alpha == 0);
|
||||||
AddUntilStep("wait for hud load", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
|
AddUntilStep("wait for hud load", () => hudOverlay.ChildrenOfType<SkinComponentsContainer>().All(c => c.ComponentsLoaded));
|
||||||
|
|
||||||
AddStep("bind on update", () =>
|
AddStep("bind on update", () =>
|
||||||
{
|
{
|
||||||
@ -254,10 +254,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
createNew();
|
createNew();
|
||||||
|
|
||||||
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
|
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
|
||||||
AddUntilStep("wait for components to be hidden", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().Single().Alpha == 0);
|
AddUntilStep("wait for components to be hidden", () => hudOverlay.ChildrenOfType<SkinComponentsContainer>().Single().Alpha == 0);
|
||||||
|
|
||||||
AddStep("reload components", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().Single().Reload());
|
AddStep("reload components", () => hudOverlay.ChildrenOfType<SkinComponentsContainer>().Single().Reload());
|
||||||
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().Single().ComponentsLoaded);
|
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType<SkinComponentsContainer>().Single().ComponentsLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNew(Action<HUDOverlay>? action = null)
|
private void createNew(Action<HUDOverlay>? action = null)
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
|
|
||||||
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
|
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinComponentsContainer>().All(c => c.ComponentsLoaded));
|
||||||
|
|
||||||
AddStep("reload skin editor", () =>
|
AddStep("reload skin editor", () =>
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
// 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.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Extensions
|
namespace osu.Game.Extensions
|
||||||
@ -48,42 +43,5 @@ namespace osu.Game.Extensions
|
|||||||
/// <returns>The delta vector in Parent's coordinates.</returns>
|
/// <returns>The delta vector in Parent's coordinates.</returns>
|
||||||
public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) =>
|
public static Vector2 ScreenSpaceDeltaToParentSpace(this Drawable drawable, Vector2 delta) =>
|
||||||
drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta);
|
drawable.Parent.ToLocalSpace(drawable.Parent.ToScreenSpace(Vector2.Zero) + delta);
|
||||||
|
|
||||||
public static SkinnableInfo CreateSkinnableInfo(this Drawable component) => new SkinnableInfo(component);
|
|
||||||
|
|
||||||
public static void ApplySkinnableInfo(this Drawable component, SkinnableInfo info)
|
|
||||||
{
|
|
||||||
// todo: can probably make this better via deserialisation directly using a common interface.
|
|
||||||
component.Position = info.Position;
|
|
||||||
component.Rotation = info.Rotation;
|
|
||||||
component.Scale = info.Scale;
|
|
||||||
component.Anchor = info.Anchor;
|
|
||||||
component.Origin = info.Origin;
|
|
||||||
|
|
||||||
if (component is ISkinnableDrawable skinnable)
|
|
||||||
{
|
|
||||||
skinnable.UsesFixedAnchor = info.UsesFixedAnchor;
|
|
||||||
|
|
||||||
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
|
||||||
{
|
|
||||||
var bindable = ((IBindable)property.GetValue(component)!);
|
|
||||||
|
|
||||||
if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
|
|
||||||
{
|
|
||||||
// TODO: We probably want to restore default if not included in serialisation information.
|
|
||||||
// This is not simple to do as SetDefault() is only found in the typed Bindable<T> interface right now.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
skinnable.CopyAdjustedSetting(bindable, settingValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component is Container container)
|
|
||||||
{
|
|
||||||
foreach (var child in info.Children)
|
|
||||||
container.Add(child.CreateInstance());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.SkinEditor
|
namespace osu.Game.Overlays.SkinEditor
|
||||||
{
|
{
|
||||||
public partial class SkinBlueprint : SelectionBlueprint<ISkinnableDrawable>
|
public partial class SkinBlueprint : SelectionBlueprint<ISerialisableDrawable>
|
||||||
{
|
{
|
||||||
private Container box = null!;
|
private Container box = null!;
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; } = null!;
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
public SkinBlueprint(ISkinnableDrawable component)
|
public SkinBlueprint(ISerialisableDrawable component)
|
||||||
: base(component)
|
: base(component)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,11 @@ using osuTK.Input;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.SkinEditor
|
namespace osu.Game.Overlays.SkinEditor
|
||||||
{
|
{
|
||||||
public partial class SkinBlueprintContainer : BlueprintContainer<ISkinnableDrawable>
|
public partial class SkinBlueprintContainer : BlueprintContainer<ISerialisableDrawable>
|
||||||
{
|
{
|
||||||
private readonly Drawable target;
|
private readonly Drawable target;
|
||||||
|
|
||||||
private readonly List<BindableList<ISkinnableDrawable>> targetComponents = new List<BindableList<ISkinnableDrawable>>();
|
private readonly List<BindableList<ISerialisableDrawable>> targetComponents = new List<BindableList<ISerialisableDrawable>>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinEditor editor { get; set; } = null!;
|
private SkinEditor editor { get; set; } = null!;
|
||||||
@ -45,7 +45,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
SelectedItems.BindTo(editor.SelectedComponents);
|
SelectedItems.BindTo(editor.SelectedComponents);
|
||||||
|
|
||||||
// track each target container on the current screen.
|
// track each target container on the current screen.
|
||||||
var targetContainers = target.ChildrenOfType<ISkinnableTarget>().ToArray();
|
var targetContainers = target.ChildrenOfType<ISerialisableDrawableContainer>().ToArray();
|
||||||
|
|
||||||
if (targetContainers.Length == 0)
|
if (targetContainers.Length == 0)
|
||||||
{
|
{
|
||||||
@ -55,7 +55,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
|
|
||||||
foreach (var targetContainer in targetContainers)
|
foreach (var targetContainer in targetContainers)
|
||||||
{
|
{
|
||||||
var bindableList = new BindableList<ISkinnableDrawable> { BindTarget = targetContainer.Components };
|
var bindableList = new BindableList<ISerialisableDrawable> { BindTarget = targetContainer.Components };
|
||||||
bindableList.BindCollectionChanged(componentsChanged, true);
|
bindableList.BindCollectionChanged(componentsChanged, true);
|
||||||
|
|
||||||
targetComponents.Add(bindableList);
|
targetComponents.Add(bindableList);
|
||||||
@ -69,7 +69,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
case NotifyCollectionChangedAction.Add:
|
case NotifyCollectionChangedAction.Add:
|
||||||
Debug.Assert(e.NewItems != null);
|
Debug.Assert(e.NewItems != null);
|
||||||
|
|
||||||
foreach (var item in e.NewItems.Cast<ISkinnableDrawable>())
|
foreach (var item in e.NewItems.Cast<ISerialisableDrawable>())
|
||||||
AddBlueprintFor(item);
|
AddBlueprintFor(item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
case NotifyCollectionChangedAction.Reset:
|
case NotifyCollectionChangedAction.Reset:
|
||||||
Debug.Assert(e.OldItems != null);
|
Debug.Assert(e.OldItems != null);
|
||||||
|
|
||||||
foreach (var item in e.OldItems.Cast<ISkinnableDrawable>())
|
foreach (var item in e.OldItems.Cast<ISerialisableDrawable>())
|
||||||
RemoveBlueprintFor(item);
|
RemoveBlueprintFor(item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -85,16 +85,16 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
Debug.Assert(e.NewItems != null);
|
Debug.Assert(e.NewItems != null);
|
||||||
Debug.Assert(e.OldItems != null);
|
Debug.Assert(e.OldItems != null);
|
||||||
|
|
||||||
foreach (var item in e.OldItems.Cast<ISkinnableDrawable>())
|
foreach (var item in e.OldItems.Cast<ISerialisableDrawable>())
|
||||||
RemoveBlueprintFor(item);
|
RemoveBlueprintFor(item);
|
||||||
|
|
||||||
foreach (var item in e.NewItems.Cast<ISkinnableDrawable>())
|
foreach (var item in e.NewItems.Cast<ISerialisableDrawable>())
|
||||||
AddBlueprintFor(item);
|
AddBlueprintFor(item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
protected override void AddBlueprintFor(ISkinnableDrawable item)
|
protected override void AddBlueprintFor(ISerialisableDrawable item)
|
||||||
{
|
{
|
||||||
if (!item.IsEditable)
|
if (!item.IsEditable)
|
||||||
return;
|
return;
|
||||||
@ -145,12 +145,12 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
// convert to game space coordinates
|
// convert to game space coordinates
|
||||||
delta = firstBlueprint.ToScreenSpace(delta) - firstBlueprint.ToScreenSpace(Vector2.Zero);
|
delta = firstBlueprint.ToScreenSpace(delta) - firstBlueprint.ToScreenSpace(Vector2.Zero);
|
||||||
|
|
||||||
SelectionHandler.HandleMovement(new MoveSelectionEvent<ISkinnableDrawable>(firstBlueprint, delta));
|
SelectionHandler.HandleMovement(new MoveSelectionEvent<ISerialisableDrawable>(firstBlueprint, delta));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override SelectionHandler<ISkinnableDrawable> CreateSelectionHandler() => new SkinSelectionHandler();
|
protected override SelectionHandler<ISerialisableDrawable> CreateSelectionHandler() => new SkinSelectionHandler();
|
||||||
|
|
||||||
protected override SelectionBlueprint<ISkinnableDrawable> CreateBlueprintFor(ISkinnableDrawable component)
|
protected override SelectionBlueprint<ISerialisableDrawable> CreateBlueprintFor(ISerialisableDrawable component)
|
||||||
=> new SkinBlueprint(component);
|
=> new SkinBlueprint(component);
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -2,17 +2,17 @@
|
|||||||
// 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;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Graphics;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Screens.Edit.Components;
|
using osu.Game.Screens.Edit.Components;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
fill.Clear();
|
fill.Clear();
|
||||||
|
|
||||||
var skinnableTypes = SkinnableInfo.GetAllAvailableDrawables();
|
var skinnableTypes = SerialisedDrawableInfo.GetAllAvailableDrawables();
|
||||||
foreach (var type in skinnableTypes)
|
foreach (var type in skinnableTypes)
|
||||||
attemptAddComponent(type);
|
attemptAddComponent(type);
|
||||||
}
|
}
|
||||||
@ -61,11 +61,12 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
Drawable instance = (Drawable)Activator.CreateInstance(type)!;
|
Drawable instance = (Drawable)Activator.CreateInstance(type)!;
|
||||||
|
|
||||||
if (!((ISkinnableDrawable)instance).IsEditable) return;
|
if (!((ISerialisableDrawable)instance).IsEditable) return;
|
||||||
|
|
||||||
fill.Add(new ToolboxComponentButton(instance, target)
|
fill.Add(new ToolboxComponentButton(instance, target)
|
||||||
{
|
{
|
||||||
RequestPlacement = t => RequestPlacement?.Invoke(t)
|
RequestPlacement = t => RequestPlacement?.Invoke(t),
|
||||||
|
Expanding = contractOtherButtons,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (DependencyNotRegisteredException)
|
catch (DependencyNotRegisteredException)
|
||||||
@ -79,15 +80,29 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void contractOtherButtons(ToolboxComponentButton obj)
|
||||||
|
{
|
||||||
|
foreach (var b in fill.OfType<ToolboxComponentButton>())
|
||||||
|
{
|
||||||
|
if (b == obj)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
b.Contract();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public partial class ToolboxComponentButton : OsuButton
|
public partial class ToolboxComponentButton : OsuButton
|
||||||
{
|
{
|
||||||
public Action<Type>? RequestPlacement;
|
public Action<Type>? RequestPlacement;
|
||||||
|
public Action<ToolboxComponentButton>? Expanding;
|
||||||
|
|
||||||
private readonly Drawable component;
|
private readonly Drawable component;
|
||||||
private readonly CompositeDrawable? dependencySource;
|
private readonly CompositeDrawable? dependencySource;
|
||||||
|
|
||||||
private Container innerContainer = null!;
|
private Container innerContainer = null!;
|
||||||
|
|
||||||
|
private ScheduledDelegate? expandContractAction;
|
||||||
|
|
||||||
private const float contracted_size = 60;
|
private const float contracted_size = 60;
|
||||||
private const float expanded_size = 120;
|
private const float expanded_size = 120;
|
||||||
|
|
||||||
@ -102,20 +117,45 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
Height = contracted_size;
|
Height = contracted_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const double animation_duration = 500;
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
this.Delay(300).ResizeHeightTo(expanded_size, 500, Easing.OutQuint);
|
expandContractAction?.Cancel();
|
||||||
|
expandContractAction = Scheduler.AddDelayed(() =>
|
||||||
|
{
|
||||||
|
this.ResizeHeightTo(expanded_size, animation_duration, Easing.OutQuint);
|
||||||
|
Expanding?.Invoke(this);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
return base.OnHover(e);
|
return base.OnHover(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
base.OnHoverLost(e);
|
base.OnHoverLost(e);
|
||||||
this.ResizeHeightTo(contracted_size, 500, Easing.OutQuint);
|
|
||||||
|
expandContractAction?.Cancel();
|
||||||
|
// If no other component is selected for too long, force a contract.
|
||||||
|
// Otherwise we will generally contract when Contract() is called from outside.
|
||||||
|
expandContractAction = Scheduler.AddDelayed(Contract, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Contract()
|
||||||
|
{
|
||||||
|
// Cheap debouncing to avoid stacking animations.
|
||||||
|
// The only place this is nulled is at the end of this method.
|
||||||
|
if (expandContractAction == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ResizeHeightTo(contracted_size, animation_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
expandContractAction?.Cancel();
|
||||||
|
expandContractAction = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider, OsuColour colours)
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
BackgroundColour = colourProvider.Background3;
|
BackgroundColour = colourProvider.Background3;
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
|
|
||||||
public const float MENU_HEIGHT = 40;
|
public const float MENU_HEIGHT = 40;
|
||||||
|
|
||||||
public readonly BindableList<ISkinnableDrawable> SelectedComponents = new BindableList<ISkinnableDrawable>();
|
public readonly BindableList<ISerialisableDrawable> SelectedComponents = new BindableList<ISerialisableDrawable>();
|
||||||
|
|
||||||
protected override bool StartHidden => true;
|
protected override bool StartHidden => true;
|
||||||
|
|
||||||
@ -302,13 +302,13 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
|
|
||||||
private void placeComponent(Type type)
|
private void placeComponent(Type type)
|
||||||
{
|
{
|
||||||
if (!(Activator.CreateInstance(type) is ISkinnableDrawable component))
|
if (!(Activator.CreateInstance(type) is ISerialisableDrawable component))
|
||||||
throw new InvalidOperationException($"Attempted to instantiate a component for placement which was not an {typeof(ISkinnableDrawable)}.");
|
throw new InvalidOperationException($"Attempted to instantiate a component for placement which was not an {typeof(ISerialisableDrawable)}.");
|
||||||
|
|
||||||
placeComponent(component);
|
placeComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void placeComponent(ISkinnableDrawable component, bool applyDefaults = true)
|
private void placeComponent(ISerialisableDrawable component, bool applyDefaults = true)
|
||||||
{
|
{
|
||||||
var targetContainer = getFirstTarget();
|
var targetContainer = getFirstTarget();
|
||||||
|
|
||||||
@ -339,25 +339,25 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
settingsSidebar.Add(new SkinSettingsToolbox(component));
|
settingsSidebar.Add(new SkinSettingsToolbox(component));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<ISkinnableTarget> availableTargets => targetScreen.ChildrenOfType<ISkinnableTarget>();
|
private IEnumerable<SkinComponentsContainer> availableTargets => targetScreen.ChildrenOfType<SkinComponentsContainer>();
|
||||||
|
|
||||||
private ISkinnableTarget? getFirstTarget() => availableTargets.FirstOrDefault();
|
private ISerialisableDrawableContainer? getFirstTarget() => availableTargets.FirstOrDefault();
|
||||||
|
|
||||||
private ISkinnableTarget? getTarget(GlobalSkinComponentLookup.LookupType target)
|
private ISerialisableDrawableContainer? getTarget(SkinComponentsContainerLookup.TargetArea target)
|
||||||
{
|
{
|
||||||
return availableTargets.FirstOrDefault(c => c.Target == target);
|
return availableTargets.FirstOrDefault(c => c.Lookup.Target == target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revert()
|
private void revert()
|
||||||
{
|
{
|
||||||
ISkinnableTarget[] targetContainers = availableTargets.ToArray();
|
SkinComponentsContainer[] targetContainers = availableTargets.ToArray();
|
||||||
|
|
||||||
foreach (var t in targetContainers)
|
foreach (var t in targetContainers)
|
||||||
{
|
{
|
||||||
currentSkin.Value.ResetDrawableTarget(t);
|
currentSkin.Value.ResetDrawableTarget(t);
|
||||||
|
|
||||||
// add back default components
|
// add back default components
|
||||||
getTarget(t.Target)?.Reload();
|
getTarget(t.Lookup.Target)?.Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +370,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
if (!hasBegunMutating)
|
if (!hasBegunMutating)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ISkinnableTarget[] targetContainers = availableTargets.ToArray();
|
SkinComponentsContainer[] targetContainers = availableTargets.ToArray();
|
||||||
|
|
||||||
foreach (var t in targetContainers)
|
foreach (var t in targetContainers)
|
||||||
currentSkin.Value.UpdateDrawableTarget(t);
|
currentSkin.Value.UpdateDrawableTarget(t);
|
||||||
@ -400,7 +400,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
this.FadeOut(TRANSITION_DURATION, Easing.OutQuint);
|
this.FadeOut(TRANSITION_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteItems(ISkinnableDrawable[] items)
|
public void DeleteItems(ISerialisableDrawable[] items)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item);
|
availableTargets.FirstOrDefault(t => t.Components.Contains(item))?.Remove(item);
|
||||||
|
@ -9,19 +9,17 @@ using Newtonsoft.Json;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Extensions;
|
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.SkinEditor
|
namespace osu.Game.Overlays.SkinEditor
|
||||||
{
|
{
|
||||||
public partial class SkinEditorChangeHandler : EditorChangeHandler
|
public partial class SkinEditorChangeHandler : EditorChangeHandler
|
||||||
{
|
{
|
||||||
private readonly ISkinnableTarget? firstTarget;
|
private readonly ISerialisableDrawableContainer? firstTarget;
|
||||||
|
|
||||||
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
||||||
private readonly BindableList<ISkinnableDrawable>? components;
|
private readonly BindableList<ISerialisableDrawable>? components;
|
||||||
|
|
||||||
public SkinEditorChangeHandler(Drawable targetScreen)
|
public SkinEditorChangeHandler(Drawable targetScreen)
|
||||||
{
|
{
|
||||||
@ -29,12 +27,12 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
// In the future we'll want this to cover all changes, even to skin's `InstantiationInfo`.
|
// In the future we'll want this to cover all changes, even to skin's `InstantiationInfo`.
|
||||||
// We'll also need to consider cases where multiple targets are on screen at the same time.
|
// We'll also need to consider cases where multiple targets are on screen at the same time.
|
||||||
|
|
||||||
firstTarget = targetScreen.ChildrenOfType<ISkinnableTarget>().FirstOrDefault();
|
firstTarget = targetScreen.ChildrenOfType<ISerialisableDrawableContainer>().FirstOrDefault();
|
||||||
|
|
||||||
if (firstTarget == null)
|
if (firstTarget == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
components = new BindableList<ISkinnableDrawable> { BindTarget = firstTarget.Components };
|
components = new BindableList<ISerialisableDrawable> { BindTarget = firstTarget.Components };
|
||||||
components.BindCollectionChanged((_, _) => SaveState());
|
components.BindCollectionChanged((_, _) => SaveState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
if (firstTarget == null)
|
if (firstTarget == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var skinnableInfos = firstTarget.CreateSkinnableInfo().ToArray();
|
var skinnableInfos = firstTarget.CreateSerialisedInfo().ToArray();
|
||||||
string json = JsonConvert.SerializeObject(skinnableInfos, new JsonSerializerSettings { Formatting = Formatting.Indented });
|
string json = JsonConvert.SerializeObject(skinnableInfos, new JsonSerializerSettings { Formatting = Formatting.Indented });
|
||||||
stream.Write(Encoding.UTF8.GetBytes(json));
|
stream.Write(Encoding.UTF8.GetBytes(json));
|
||||||
}
|
}
|
||||||
@ -53,12 +51,12 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
if (firstTarget == null)
|
if (firstTarget == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SkinnableInfo>>(Encoding.UTF8.GetString(newState));
|
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SerialisedDrawableInfo>>(Encoding.UTF8.GetString(newState));
|
||||||
|
|
||||||
if (deserializedContent == null)
|
if (deserializedContent == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SkinnableInfo[] skinnableInfo = deserializedContent.ToArray();
|
SerialisedDrawableInfo[] skinnableInfo = deserializedContent.ToArray();
|
||||||
Drawable[] targetComponents = firstTarget.Components.OfType<Drawable>().ToArray();
|
Drawable[] targetComponents = firstTarget.Components.OfType<Drawable>().ToArray();
|
||||||
|
|
||||||
if (!skinnableInfo.Select(s => s.Type).SequenceEqual(targetComponents.Select(d => d.GetType())))
|
if (!skinnableInfo.Select(s => s.Type).SequenceEqual(targetComponents.Select(d => d.GetType())))
|
||||||
@ -71,7 +69,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
foreach (var drawable in targetComponents)
|
foreach (var drawable in targetComponents)
|
||||||
drawable.ApplySkinnableInfo(skinnableInfo[i++]);
|
drawable.ApplySerialisedInfo(skinnableInfo[i++]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.SkinEditor
|
namespace osu.Game.Overlays.SkinEditor
|
||||||
{
|
{
|
||||||
public partial class SkinSelectionHandler : SelectionHandler<ISkinnableDrawable>
|
public partial class SkinSelectionHandler : SelectionHandler<ISerialisableDrawable>
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinEditor skinEditor { get; set; } = null!;
|
private SkinEditor skinEditor { get; set; } = null!;
|
||||||
@ -147,7 +147,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HandleMovement(MoveSelectionEvent<ISkinnableDrawable> moveEvent)
|
public override bool HandleMovement(MoveSelectionEvent<ISerialisableDrawable> moveEvent)
|
||||||
{
|
{
|
||||||
foreach (var c in SelectedBlueprints)
|
foreach (var c in SelectedBlueprints)
|
||||||
{
|
{
|
||||||
@ -178,10 +178,10 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
SelectionBox.CanReverse = false;
|
SelectionBox.CanReverse = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DeleteItems(IEnumerable<ISkinnableDrawable> items) =>
|
protected override void DeleteItems(IEnumerable<ISerialisableDrawable> items) =>
|
||||||
skinEditor.DeleteItems(items.ToArray());
|
skinEditor.DeleteItems(items.ToArray());
|
||||||
|
|
||||||
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint<ISkinnableDrawable>> selection)
|
protected override IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint<ISerialisableDrawable>> selection)
|
||||||
{
|
{
|
||||||
var closestItem = new TernaryStateRadioMenuItem("Closest", MenuItemType.Standard, _ => applyClosestAnchors())
|
var closestItem = new TernaryStateRadioMenuItem("Closest", MenuItemType.Standard, _ => applyClosestAnchors())
|
||||||
{
|
{
|
||||||
@ -209,7 +209,7 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
foreach (var item in base.GetContextMenuItemsForSelection(selection))
|
foreach (var item in base.GetContextMenuItemsForSelection(selection))
|
||||||
yield return item;
|
yield return item;
|
||||||
|
|
||||||
IEnumerable<TernaryStateMenuItem> createAnchorItems(Func<ISkinnableDrawable, Anchor, bool> checkFunction, Action<Anchor> applyFunction)
|
IEnumerable<TernaryStateMenuItem> createAnchorItems(Func<ISerialisableDrawable, Anchor, bool> checkFunction, Action<Anchor> applyFunction)
|
||||||
{
|
{
|
||||||
var displayableAnchors = new[]
|
var displayableAnchors = new[]
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public partial class BPMCounter : RollingCounter<double>, ISkinnableDrawable
|
public partial class BPMCounter : RollingCounter<double>, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
protected override double RollingDuration => 750;
|
protected override double RollingDuration => 750;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
|
namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
|
||||||
{
|
{
|
||||||
public partial class ClicksPerSecondCounter : RollingCounter<int>, ISkinnableDrawable
|
public partial class ClicksPerSecondCounter : RollingCounter<int>, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private ClicksPerSecondCalculator calculator { get; set; } = null!;
|
private ClicksPerSecondCalculator calculator { get; set; } = null!;
|
||||||
|
@ -7,7 +7,7 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public abstract partial class ComboCounter : RollingCounter<int>, ISkinnableDrawable
|
public abstract partial class ComboCounter : RollingCounter<int>, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public partial class DefaultAccuracyCounter : GameplayAccuracyCounter, ISkinnableDrawable
|
public partial class DefaultAccuracyCounter : GameplayAccuracyCounter, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public partial class DefaultHealthDisplay : HealthDisplay, IHasAccentColour, ISkinnableDrawable
|
public partial class DefaultHealthDisplay : HealthDisplay, IHasAccentColour, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base opacity of the glow.
|
/// The base opacity of the glow.
|
||||||
|
@ -10,7 +10,7 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public partial class DefaultScoreCounter : GameplayScoreCounter, ISkinnableDrawable
|
public partial class DefaultScoreCounter : GameplayScoreCounter, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public DefaultScoreCounter()
|
public DefaultScoreCounter()
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||||
{
|
{
|
||||||
public abstract partial class HitErrorMeter : CompositeDrawable, ISkinnableDrawable
|
public abstract partial class HitErrorMeter : CompositeDrawable, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
protected HitWindows HitWindows { get; private set; }
|
protected HitWindows HitWindows { get; private set; }
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD.JudgementCounter
|
namespace osu.Game.Screens.Play.HUD.JudgementCounter
|
||||||
{
|
{
|
||||||
public partial class JudgementCounterDisplay : CompositeDrawable, ISkinnableDrawable
|
public partial class JudgementCounterDisplay : CompositeDrawable, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public const int TRANSFORM_DURATION = 250;
|
public const int TRANSFORM_DURATION = 250;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public partial class PerformancePointsCounter : RollingCounter<int>, ISkinnableDrawable
|
public partial class PerformancePointsCounter : RollingCounter<int>, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public abstract partial class SongProgress : OverlayContainer, ISkinnableDrawable
|
public abstract partial class SongProgress : OverlayContainer, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
// Some implementations of this element allow seeking during gameplay playback.
|
// Some implementations of this element allow seeking during gameplay playback.
|
||||||
// Set a sane default of never handling input to override the behaviour provided by OverlayContainer.
|
// Set a sane default of never handling input to override the behaviour provided by OverlayContainer.
|
||||||
|
@ -20,7 +20,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public partial class UnstableRateCounter : RollingCounter<int>, ISkinnableDrawable
|
public partial class UnstableRateCounter : RollingCounter<int>, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly BindableBool holdingForHUD = new BindableBool();
|
private readonly BindableBool holdingForHUD = new BindableBool();
|
||||||
|
|
||||||
private readonly SkinnableTargetContainer mainComponents;
|
private readonly SkinComponentsContainer mainComponents;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A flow which sits at the left side of the screen to house leaderboard (and related) components.
|
/// A flow which sits at the left side of the screen to house leaderboard (and related) components.
|
||||||
@ -390,7 +390,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class MainComponentsContainer : SkinnableTargetContainer
|
private partial class MainComponentsContainer : SkinComponentsContainer
|
||||||
{
|
{
|
||||||
private Bindable<ScoringMode> scoringMode;
|
private Bindable<ScoringMode> scoringMode;
|
||||||
|
|
||||||
@ -398,7 +398,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private OsuConfigManager config { get; set; }
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
public MainComponentsContainer()
|
public MainComponentsContainer()
|
||||||
: base(GlobalSkinComponentLookup.LookupType.MainHUDComponents)
|
: base(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.MainHUDComponents))
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new SkinnableTargetContainer(GlobalSkinComponentLookup.LookupType.SongSelect)
|
new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.SongSelect))
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
|
@ -90,10 +90,10 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
switch (lookup)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup globalLookup:
|
case SkinComponentsContainerLookup containerLookup:
|
||||||
switch (globalLookup.Lookup)
|
switch (containerLookup.Target)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup.LookupType.SongSelect:
|
case SkinComponentsContainerLookup.TargetArea.SongSelect:
|
||||||
var songSelectComponents = new DefaultSkinComponentsContainer(_ =>
|
var songSelectComponents = new DefaultSkinComponentsContainer(_ =>
|
||||||
{
|
{
|
||||||
// do stuff when we need to.
|
// do stuff when we need to.
|
||||||
@ -101,7 +101,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
return songSelectComponents;
|
return songSelectComponents;
|
||||||
|
|
||||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||||
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
||||||
{
|
{
|
||||||
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Skinning.Components
|
|||||||
/// Intended to be a test bed for skinning. May be removed at some point in the future.
|
/// Intended to be a test bed for skinning. May be removed at some point in the future.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public partial class BigBlackBox : CompositeDrawable, ISkinnableDrawable
|
public partial class BigBlackBox : CompositeDrawable, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A skin component that contains text and allows the user to choose its font.
|
/// A skin component that contains text and allows the user to choose its font.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class FontAdjustableSkinComponent : Container, ISkinnableDrawable
|
public abstract partial class FontAdjustableSkinComponent : Container, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A lookup type intended for use for skinnable gameplay components (not HUD level components).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The most common usage of this class is for ruleset-specific skinning implementations, but it can also be used directly
|
||||||
|
/// (see <see cref="DrawableJudgement"/>'s usage for <see cref="HitResult"/>) where ruleset-agnostic elements are required.
|
||||||
|
/// </remarks>
|
||||||
|
/// <typeparam name="T">An enum lookup type.</typeparam>
|
||||||
public class GameplaySkinComponentLookup<T> : ISkinComponentLookup
|
public class GameplaySkinComponentLookup<T> : ISkinComponentLookup
|
||||||
where T : notnull
|
where T : Enum
|
||||||
{
|
{
|
||||||
public readonly T Component;
|
public readonly T Component;
|
||||||
|
|
||||||
@ -16,7 +27,7 @@ namespace osu.Game.Skinning
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string RulesetPrefix => string.Empty;
|
protected virtual string RulesetPrefix => string.Empty;
|
||||||
protected virtual string ComponentName => Component.ToString() ?? string.Empty;
|
protected virtual string ComponentName => Component.ToString();
|
||||||
|
|
||||||
public string LookupName =>
|
public string LookupName =>
|
||||||
string.Join('/', new[] { "Gameplay", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s)));
|
string.Join('/', new[] { "Gameplay", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s)));
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
|
||||||
{
|
|
||||||
public class GlobalSkinComponentLookup : ISkinComponentLookup
|
|
||||||
{
|
|
||||||
public readonly LookupType Lookup;
|
|
||||||
|
|
||||||
public GlobalSkinComponentLookup(LookupType lookup)
|
|
||||||
{
|
|
||||||
Lookup = lookup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum LookupType
|
|
||||||
{
|
|
||||||
MainHUDComponents,
|
|
||||||
SongSelect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,13 +10,16 @@ using osu.Game.Configuration;
|
|||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Denotes a drawable which, as a drawable, can be adjusted via skinning specifications.
|
/// A drawable which is intended to be serialised to <see cref="SerialisedDrawableInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Attaching this interface to any <see cref="IDrawable"/> will make it serialisable to skin settings.
|
/// This is currently used exclusively for serialisation to a skin, and leaned on heavily to allow placement and customisation in the skin layout editor.
|
||||||
/// Adding <see cref="SettingSourceAttribute"/> annotated bindables will also serialise these settings alongside each instance.
|
/// That said, it is intended to be flexible enough to potentially be used in other places we want to serialise drawables in the future.
|
||||||
|
///
|
||||||
|
/// Attaching this interface to any <see cref="IDrawable"/> will make it serialisable via <see cref="SerialisableDrawableExtensions.CreateSerialisedInfo"/>.
|
||||||
|
/// Adding <see cref="SettingSourceAttribute"/> annotated bindables will also allow serialising settings automatically.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public interface ISkinnableDrawable : IDrawable
|
public interface ISerialisableDrawable : IDrawable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this component should be editable by an end user.
|
/// Whether this component should be editable by an end user.
|
||||||
@ -24,8 +27,8 @@ namespace osu.Game.Skinning
|
|||||||
bool IsEditable => true;
|
bool IsEditable => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// In the context of the skin layout editor, whether this <see cref="ISkinnableDrawable"/> has a permanent anchor defined.
|
/// In the context of the skin layout editor, whether this <see cref="ISerialisableDrawable"/> has a permanent anchor defined.
|
||||||
/// If <see langword="false"/>, this <see cref="ISkinnableDrawable"/>'s <see cref="Drawable.Anchor"/> is automatically determined by proximity,
|
/// If <see langword="false"/>, this <see cref="ISerialisableDrawable"/>'s <see cref="Drawable.Anchor"/> is automatically determined by proximity,
|
||||||
/// If <see langword="true"/>, a fixed anchor point has been defined.
|
/// If <see langword="true"/>, a fixed anchor point has been defined.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool UsesFixedAnchor { get; set; }
|
bool UsesFixedAnchor { get; set; }
|
@ -5,31 +5,25 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Extensions;
|
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Denotes a container which can house <see cref="ISkinnableDrawable"/>s.
|
/// A container which can house <see cref="ISerialisableDrawable"/>s.
|
||||||
|
/// Contains functionality for new drawables to be added, removed, and reloaded from provided <see cref="SerialisedDrawableInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISkinnableTarget : IDrawable
|
public interface ISerialisableDrawableContainer : IDrawable
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The definition of this target.
|
|
||||||
/// </summary>
|
|
||||||
GlobalSkinComponentLookup.LookupType Target { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A bindable list of components which are being tracked by this skinnable target.
|
/// A bindable list of components which are being tracked by this skinnable target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IBindableList<ISkinnableDrawable> Components { get; }
|
IBindableList<ISerialisableDrawable> Components { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serialise all children as <see cref="SkinnableInfo"/>.
|
/// Serialise all children as <see cref="SerialisedDrawableInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The serialised content.</returns>
|
/// <returns>The serialised content.</returns>
|
||||||
IEnumerable<SkinnableInfo> CreateSkinnableInfo() => Components.Select(d => ((Drawable)d).CreateSkinnableInfo());
|
IEnumerable<SerialisedDrawableInfo> CreateSerialisedInfo() => Components.Select(d => ((Drawable)d).CreateSerialisedInfo());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reload this target from the current skin.
|
/// Reload this target from the current skin.
|
||||||
@ -39,18 +33,18 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reload this target from the provided skinnable information.
|
/// Reload this target from the provided skinnable information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Reload(SkinnableInfo[] skinnableInfo);
|
void Reload(SerialisedDrawableInfo[] skinnableInfo);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a new skinnable component to this target.
|
/// Add a new skinnable component to this target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="drawable">The component to add.</param>
|
/// <param name="drawable">The component to add.</param>
|
||||||
void Add(ISkinnableDrawable drawable);
|
void Add(ISerialisableDrawable drawable);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove an existing skinnable component from this target.
|
/// Remove an existing skinnable component from this target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="component">The component to remove.</param>
|
/// <param name="component">The component to remove.</param>
|
||||||
void Remove(ISkinnableDrawable component);
|
void Remove(ISerialisableDrawable component);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ using osu.Game.Audio;
|
|||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides access to skinnable elements.
|
/// Provides access to various elements contained by a skin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISkin
|
public interface ISkin
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A lookup type which can be used with <see cref="ISkin.GetDrawableComponent"/>.
|
/// The base lookup type to be used with <see cref="ISkin.GetDrawableComponent"/>.
|
||||||
|
/// Should be implemented as necessary to add further criteria to lookups, which are usually consumed by ruleset transformers or legacy lookup cases.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Implementations of <see cref="ISkin.GetDrawableComponent"/> should match on types implementing this interface
|
/// Implementations of <see cref="ISkin.GetDrawableComponent"/> should match on types implementing this interface
|
||||||
|
@ -7,8 +7,16 @@ using System.Collections.Generic;
|
|||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides access to skinnable elements.
|
/// An abstract skin implementation, whose primary purpose is to properly handle component fallback across multiple layers of skins (e.g.: beatmap skin, user skin, default skin).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Common usage is to do an initial lookup via <see cref="FindProvider"/>, and use the returned <see cref="ISkin"/>
|
||||||
|
/// to do further lookups for related components.
|
||||||
|
///
|
||||||
|
/// The initial lookup is used to lock consecutive lookups to the same underlying skin source (as to not get some elements
|
||||||
|
/// from one skin and others from another, which would be the case if using <see cref="ISkin"/> methods like
|
||||||
|
/// <see cref="ISkin.GetSample"/> directly).
|
||||||
|
/// </remarks>
|
||||||
public interface ISkinSource : ISkin
|
public interface ISkinSource : ISkin
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -8,7 +8,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public partial class LegacyAccuracyCounter : GameplayAccuracyCounter, ISkinnableDrawable
|
public partial class LegacyAccuracyCounter : GameplayAccuracyCounter, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
@ -45,11 +45,11 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||||
{
|
{
|
||||||
if (lookup is GlobalSkinComponentLookup targetComponent)
|
if (lookup is SkinComponentsContainerLookup containerLookup)
|
||||||
{
|
{
|
||||||
switch (targetComponent.Lookup)
|
switch (containerLookup.Target)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||||
// this should exist in LegacySkin instead, but there isn't a fallback skin for LegacySkins yet.
|
// this should exist in LegacySkin instead, but there isn't a fallback skin for LegacySkins yet.
|
||||||
// therefore keep the check here until fallback default legacy skin is supported.
|
// therefore keep the check here until fallback default legacy skin is supported.
|
||||||
if (!this.HasFont(LegacyFont.Score))
|
if (!this.HasFont(LegacyFont.Score))
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
@ -15,7 +14,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses the 'x' symbol and has a pop-out effect while rolling over.
|
/// Uses the 'x' symbol and has a pop-out effect while rolling over.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class LegacyComboCounter : CompositeDrawable, ISkinnableDrawable
|
public partial class LegacyComboCounter : CompositeDrawable, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
public Bindable<int> Current { get; } = new BindableInt { MinValue = 0 };
|
public Bindable<int> Current { get; } = new BindableInt { MinValue = 0 };
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ namespace osu.Game.Skinning
|
|||||||
private readonly Container counterContainer;
|
private readonly Container counterContainer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hides the combo counter internally without affecting its <see cref="SkinnableInfo"/>.
|
/// Hides the combo counter internally without affecting its <see cref="SerialisedDrawableInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is used for rulesets that provide their own combo counter and don't want this HUD one to be visible,
|
/// This is used for rulesets that provide their own combo counter and don't want this HUD one to be visible,
|
||||||
|
@ -19,7 +19,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public partial class LegacyHealthDisplay : HealthDisplay, ISkinnableDrawable
|
public partial class LegacyHealthDisplay : HealthDisplay, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
private const double epic_cutoff = 0.5;
|
private const double epic_cutoff = 0.5;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public partial class LegacyScoreCounter : GameplayScoreCounter, ISkinnableDrawable
|
public partial class LegacyScoreCounter : GameplayScoreCounter, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
protected override double RollingDuration => 1000;
|
protected override double RollingDuration => 1000;
|
||||||
protected override Easing RollingEasing => Easing.Out;
|
protected override Easing RollingEasing => Easing.Out;
|
||||||
|
@ -343,10 +343,10 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
switch (lookup)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup target:
|
case SkinComponentsContainerLookup containerLookup:
|
||||||
switch (target.Lookup)
|
switch (containerLookup.Target)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||||
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
||||||
{
|
{
|
||||||
var score = container.OfType<LegacyScoreCounter>().FirstOrDefault();
|
var score = container.OfType<LegacyScoreCounter>().FirstOrDefault();
|
||||||
|
51
osu.Game/Skinning/SerialisableDrawableExtensions.cs
Normal file
51
osu.Game/Skinning/SerialisableDrawableExtensions.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public static class SerialisableDrawableExtensions
|
||||||
|
{
|
||||||
|
public static SerialisedDrawableInfo CreateSerialisedInfo(this Drawable component) => new SerialisedDrawableInfo(component);
|
||||||
|
|
||||||
|
public static void ApplySerialisedInfo(this Drawable component, SerialisedDrawableInfo drawableInfo)
|
||||||
|
{
|
||||||
|
// todo: can probably make this better via deserialisation directly using a common interface.
|
||||||
|
component.Position = drawableInfo.Position;
|
||||||
|
component.Rotation = drawableInfo.Rotation;
|
||||||
|
component.Scale = drawableInfo.Scale;
|
||||||
|
component.Anchor = drawableInfo.Anchor;
|
||||||
|
component.Origin = drawableInfo.Origin;
|
||||||
|
|
||||||
|
if (component is ISerialisableDrawable serialisableDrawable)
|
||||||
|
{
|
||||||
|
serialisableDrawable.UsesFixedAnchor = drawableInfo.UsesFixedAnchor;
|
||||||
|
|
||||||
|
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
||||||
|
{
|
||||||
|
var bindable = ((IBindable)property.GetValue(component)!);
|
||||||
|
|
||||||
|
if (!drawableInfo.Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
|
||||||
|
{
|
||||||
|
// TODO: We probably want to restore default if not included in serialisation information.
|
||||||
|
// This is not simple to do as SetDefault() is only found in the typed Bindable<T> interface right now.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialisableDrawable.CopyAdjustedSetting(bindable, settingValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component is Container container)
|
||||||
|
{
|
||||||
|
foreach (var child in drawableInfo.Children)
|
||||||
|
container.Add(child.CreateInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -13,18 +11,22 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serialised information governing custom changes to an <see cref="ISkinnableDrawable"/>.
|
/// Serialised backing data for <see cref="ISerialisableDrawable"/>s.
|
||||||
|
/// Used for json serialisation in user skins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Can be created using <see cref="SerialisableDrawableExtensions.CreateSerialisedInfo"/>.
|
||||||
|
/// Can also be applied to an existing drawable using <see cref="SerialisableDrawableExtensions.ApplySerialisedInfo"/>.
|
||||||
|
/// </remarks>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class SkinnableInfo
|
public sealed class SerialisedDrawableInfo
|
||||||
{
|
{
|
||||||
public Type Type { get; set; }
|
public Type Type { get; set; } = null!;
|
||||||
|
|
||||||
public Vector2 Position { get; set; }
|
public Vector2 Position { get; set; }
|
||||||
|
|
||||||
@ -36,15 +38,15 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
public Anchor Origin { get; set; }
|
public Anchor Origin { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ISkinnableDrawable.UsesFixedAnchor"/>
|
/// <inheritdoc cref="ISerialisableDrawable.UsesFixedAnchor"/>
|
||||||
public bool UsesFixedAnchor { get; set; }
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
|
public Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
|
||||||
|
|
||||||
public List<SkinnableInfo> Children { get; } = new List<SkinnableInfo>();
|
public List<SerialisedDrawableInfo> Children { get; } = new List<SerialisedDrawableInfo>();
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public SkinnableInfo()
|
public SerialisedDrawableInfo()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
/// Construct a new instance populating all attributes from the provided drawable.
|
/// Construct a new instance populating all attributes from the provided drawable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="component">The drawable which attributes should be sourced from.</param>
|
/// <param name="component">The drawable which attributes should be sourced from.</param>
|
||||||
public SkinnableInfo(Drawable component)
|
public SerialisedDrawableInfo(Drawable component)
|
||||||
{
|
{
|
||||||
Type = component.GetType();
|
Type = component.GetType();
|
||||||
|
|
||||||
@ -62,8 +64,8 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
Anchor = component.Anchor;
|
Anchor = component.Anchor;
|
||||||
Origin = component.Origin;
|
Origin = component.Origin;
|
||||||
|
|
||||||
if (component is ISkinnableDrawable skinnable)
|
if (component is ISerialisableDrawable serialisableDrawable)
|
||||||
UsesFixedAnchor = skinnable.UsesFixedAnchor;
|
UsesFixedAnchor = serialisableDrawable.UsesFixedAnchor;
|
||||||
|
|
||||||
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
||||||
{
|
{
|
||||||
@ -74,8 +76,8 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
if (component is Container<Drawable> container)
|
if (component is Container<Drawable> container)
|
||||||
{
|
{
|
||||||
foreach (var child in container.OfType<ISkinnableDrawable>().OfType<Drawable>())
|
foreach (var child in container.OfType<ISerialisableDrawable>().OfType<Drawable>())
|
||||||
Children.Add(child.CreateSkinnableInfo());
|
Children.Add(child.CreateSerialisedInfo());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +90,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Drawable d = (Drawable)Activator.CreateInstance(Type)!;
|
Drawable d = (Drawable)Activator.CreateInstance(Type)!;
|
||||||
d.ApplySkinnableInfo(this);
|
d.ApplySerialisedInfo(this);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -102,7 +104,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
return typeof(OsuGame).Assembly.GetTypes()
|
return typeof(OsuGame).Assembly.GetTypes()
|
||||||
.Where(t => !t.IsInterface && !t.IsAbstract)
|
.Where(t => !t.IsInterface && !t.IsAbstract)
|
||||||
.Where(t => typeof(ISkinnableDrawable).IsAssignableFrom(t))
|
.Where(t => typeof(ISerialisableDrawable).IsAssignableFrom(t))
|
||||||
.OrderBy(t => t.Name)
|
.OrderBy(t => t.Name)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
@ -18,7 +18,6 @@ using osu.Framework.Logging;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
@ -38,9 +37,9 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public SkinConfiguration Configuration { get; set; }
|
public SkinConfiguration Configuration { get; set; }
|
||||||
|
|
||||||
public IDictionary<GlobalSkinComponentLookup.LookupType, SkinnableInfo[]> DrawableComponentInfo => drawableComponentInfo;
|
public IDictionary<SkinComponentsContainerLookup.TargetArea, SerialisedDrawableInfo[]> DrawableComponentInfo => drawableComponentInfo;
|
||||||
|
|
||||||
private readonly Dictionary<GlobalSkinComponentLookup.LookupType, SkinnableInfo[]> drawableComponentInfo = new Dictionary<GlobalSkinComponentLookup.LookupType, SkinnableInfo[]>();
|
private readonly Dictionary<SkinComponentsContainerLookup.TargetArea, SerialisedDrawableInfo[]> drawableComponentInfo = new Dictionary<SkinComponentsContainerLookup.TargetArea, SerialisedDrawableInfo[]>();
|
||||||
|
|
||||||
public abstract ISample? GetSample(ISampleInfo sampleInfo);
|
public abstract ISample? GetSample(ISampleInfo sampleInfo);
|
||||||
|
|
||||||
@ -101,7 +100,7 @@ namespace osu.Game.Skinning
|
|||||||
Configuration = new SkinConfiguration();
|
Configuration = new SkinConfiguration();
|
||||||
|
|
||||||
// skininfo files may be null for default skin.
|
// skininfo files may be null for default skin.
|
||||||
foreach (GlobalSkinComponentLookup.LookupType skinnableTarget in Enum.GetValues<GlobalSkinComponentLookup.LookupType>())
|
foreach (SkinComponentsContainerLookup.TargetArea skinnableTarget in Enum.GetValues<SkinComponentsContainerLookup.TargetArea>())
|
||||||
{
|
{
|
||||||
string filename = $"{skinnableTarget}.json";
|
string filename = $"{skinnableTarget}.json";
|
||||||
|
|
||||||
@ -120,7 +119,7 @@ namespace osu.Game.Skinning
|
|||||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.SongProgress", @"osu.Game.Screens.Play.HUD.DefaultSongProgress");
|
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.SongProgress", @"osu.Game.Screens.Play.HUD.DefaultSongProgress");
|
||||||
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.LegacyComboCounter", @"osu.Game.Skinning.LegacyComboCounter");
|
jsonContent = jsonContent.Replace(@"osu.Game.Screens.Play.HUD.LegacyComboCounter", @"osu.Game.Skinning.LegacyComboCounter");
|
||||||
|
|
||||||
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SkinnableInfo>>(jsonContent);
|
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SerialisedDrawableInfo>>(jsonContent);
|
||||||
|
|
||||||
if (deserializedContent == null)
|
if (deserializedContent == null)
|
||||||
continue;
|
continue;
|
||||||
@ -144,18 +143,18 @@ namespace osu.Game.Skinning
|
|||||||
/// Remove all stored customisations for the provided target.
|
/// Remove all stored customisations for the provided target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetContainer">The target container to reset.</param>
|
/// <param name="targetContainer">The target container to reset.</param>
|
||||||
public void ResetDrawableTarget(ISkinnableTarget targetContainer)
|
public void ResetDrawableTarget(SkinComponentsContainer targetContainer)
|
||||||
{
|
{
|
||||||
DrawableComponentInfo.Remove(targetContainer.Target);
|
DrawableComponentInfo.Remove(targetContainer.Lookup.Target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update serialised information for the provided target.
|
/// Update serialised information for the provided target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="targetContainer">The target container to serialise to this skin.</param>
|
/// <param name="targetContainer">The target container to serialise to this skin.</param>
|
||||||
public void UpdateDrawableTarget(ISkinnableTarget targetContainer)
|
public void UpdateDrawableTarget(SkinComponentsContainer targetContainer)
|
||||||
{
|
{
|
||||||
DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSkinnableInfo().ToArray();
|
DrawableComponentInfo[targetContainer.Lookup.Target] = ((ISerialisableDrawableContainer)targetContainer).CreateSerialisedInfo().ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
public virtual Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||||
@ -166,8 +165,8 @@ namespace osu.Game.Skinning
|
|||||||
case SkinnableSprite.SpriteComponentLookup sprite:
|
case SkinnableSprite.SpriteComponentLookup sprite:
|
||||||
return this.GetAnimation(sprite.LookupName, false, false);
|
return this.GetAnimation(sprite.LookupName, false, false);
|
||||||
|
|
||||||
case GlobalSkinComponentLookup target:
|
case SkinComponentsContainerLookup containerLookup:
|
||||||
if (!DrawableComponentInfo.TryGetValue(target.Lookup, out var skinnableInfo))
|
if (!DrawableComponentInfo.TryGetValue(containerLookup.Target, out var skinnableInfo))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var components = new List<Drawable>();
|
var components = new List<Drawable>();
|
||||||
|
@ -8,19 +8,30 @@ using System.Threading;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public partial class SkinnableTargetContainer : SkinReloadableDrawable, ISkinnableTarget
|
/// <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
|
||||||
{
|
{
|
||||||
private Container? content;
|
private Container? content;
|
||||||
|
|
||||||
public GlobalSkinComponentLookup.LookupType Target { get; }
|
/// <summary>
|
||||||
|
/// The lookup criteria which will be used to retrieve components from the active skin.
|
||||||
|
/// </summary>
|
||||||
|
public SkinComponentsContainerLookup Lookup { get; }
|
||||||
|
|
||||||
public IBindableList<ISkinnableDrawable> Components => components;
|
public IBindableList<ISerialisableDrawable> Components => components;
|
||||||
|
|
||||||
private readonly BindableList<ISkinnableDrawable> components = new BindableList<ISkinnableDrawable>();
|
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 override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; // ensure that components are loaded even if the target container is hidden (ie. due to user toggle).
|
||||||
|
|
||||||
@ -28,12 +39,12 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
private CancellationTokenSource? cancellationSource;
|
private CancellationTokenSource? cancellationSource;
|
||||||
|
|
||||||
public SkinnableTargetContainer(GlobalSkinComponentLookup.LookupType target)
|
public SkinComponentsContainer(SkinComponentsContainerLookup lookup)
|
||||||
{
|
{
|
||||||
Target = target;
|
Lookup = lookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reload(SkinnableInfo[] skinnableInfo)
|
public void Reload(SerialisedDrawableInfo[] skinnableInfo)
|
||||||
{
|
{
|
||||||
var drawables = new List<Drawable>();
|
var drawables = new List<Drawable>();
|
||||||
|
|
||||||
@ -47,7 +58,7 @@ namespace osu.Game.Skinning
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reload() => Reload(CurrentSkin.GetDrawableComponent(new GlobalSkinComponentLookup(Target)) as Container);
|
public void Reload() => Reload(CurrentSkin.GetDrawableComponent(Lookup) as Container);
|
||||||
|
|
||||||
public void Reload(Container? componentsContainer)
|
public void Reload(Container? componentsContainer)
|
||||||
{
|
{
|
||||||
@ -68,7 +79,7 @@ namespace osu.Game.Skinning
|
|||||||
LoadComponentAsync(content, wrapper =>
|
LoadComponentAsync(content, wrapper =>
|
||||||
{
|
{
|
||||||
AddInternal(wrapper);
|
AddInternal(wrapper);
|
||||||
components.AddRange(wrapper.Children.OfType<ISkinnableDrawable>());
|
components.AddRange(wrapper.Children.OfType<ISerialisableDrawable>());
|
||||||
ComponentsLoaded = true;
|
ComponentsLoaded = true;
|
||||||
}, (cancellationSource = new CancellationTokenSource()).Token);
|
}, (cancellationSource = new CancellationTokenSource()).Token);
|
||||||
}
|
}
|
||||||
@ -76,10 +87,10 @@ namespace osu.Game.Skinning
|
|||||||
ComponentsLoaded = true;
|
ComponentsLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ISkinnableTarget"/>
|
/// <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="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>
|
/// <exception cref="ArgumentException">Thrown if the provided instance is not a <see cref="Drawable"/>.</exception>
|
||||||
public void Add(ISkinnableDrawable component)
|
public void Add(ISerialisableDrawable component)
|
||||||
{
|
{
|
||||||
if (content == null)
|
if (content == null)
|
||||||
throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin.");
|
throw new NotSupportedException("Attempting to add a new component to a target container which is not supported by the current skin.");
|
||||||
@ -91,10 +102,10 @@ namespace osu.Game.Skinning
|
|||||||
components.Add(component);
|
components.Add(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ISkinnableTarget"/>
|
/// <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="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>
|
/// <exception cref="ArgumentException">Thrown if the provided instance is not a <see cref="Drawable"/>.</exception>
|
||||||
public void Remove(ISkinnableDrawable component)
|
public void Remove(ISerialisableDrawable component)
|
||||||
{
|
{
|
||||||
if (content == null)
|
if (content == null)
|
||||||
throw new NotSupportedException("Attempting to remove a new component from a target container which is not supported by the current skin.");
|
throw new NotSupportedException("Attempting to remove a new component from a target container which is not supported by the current skin.");
|
30
osu.Game/Skinning/SkinComponentsContainerLookup.cs
Normal file
30
osu.Game/Skinning/SkinComponentsContainerLookup.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a lookup of a collection of elements that make up a particular skinnable <see cref="TargetArea"/> of the game.
|
||||||
|
/// </summary>
|
||||||
|
public class SkinComponentsContainerLookup : ISkinComponentLookup
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The target area / layer of the game for which skin components will be returned.
|
||||||
|
/// </summary>
|
||||||
|
public readonly TargetArea Target;
|
||||||
|
|
||||||
|
public SkinComponentsContainerLookup(TargetArea target)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a particular area or part of a game screen whose layout can be customised using the skin editor.
|
||||||
|
/// </summary>
|
||||||
|
public enum TargetArea
|
||||||
|
{
|
||||||
|
MainHUDComponents,
|
||||||
|
SongSelect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,8 +15,13 @@ using osu.Game.Audio;
|
|||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container which adds a local <see cref="ISkinSource"/> to the hierarchy.
|
/// A container which adds a provided <see cref="ISkin"/> to the DI skin lookup hierarchy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This container will expose an <see cref="ISkinSource"/> to its children.
|
||||||
|
/// The source will first consider the skin provided via the constructor (if any), then fallback
|
||||||
|
/// to any <see cref="ISkinSource"/> providers in the parent DI hierarchy.
|
||||||
|
/// </remarks>
|
||||||
public partial class SkinProvidingContainer : Container, ISkinSource
|
public partial class SkinProvidingContainer : Container, ISkinSource
|
||||||
{
|
{
|
||||||
public event Action? SourceChanged;
|
public event Action? SourceChanged;
|
||||||
|
@ -9,7 +9,8 @@ using osu.Framework.Graphics.Pooling;
|
|||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A drawable which has a callback when the skin changes.
|
/// A poolable drawable implementation which has a pre-wired callback (see <see cref="SkinChanged"/>) that fires
|
||||||
|
/// once on load and again on any subsequent skin change.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class SkinReloadableDrawable : PoolableDrawable
|
public abstract partial class SkinReloadableDrawable : PoolableDrawable
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,13 @@ using osu.Game.Audio;
|
|||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A default skin transformer, which falls back to the provided skin by default.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Implementations of skin transformers should generally derive this class and override
|
||||||
|
/// individual lookup methods, modifying the lookup flow as required.
|
||||||
|
/// </remarks>
|
||||||
public abstract class SkinTransformer : ISkinTransformer
|
public abstract class SkinTransformer : ISkinTransformer
|
||||||
{
|
{
|
||||||
public ISkin Skin { get; }
|
public ISkin Skin { get; }
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A skinnable element which uses a single texture backing.
|
/// A skinnable element which uses a single texture backing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SkinnableSprite : SkinnableDrawable, ISkinnableDrawable
|
public partial class SkinnableSprite : SkinnableDrawable, ISerialisableDrawable
|
||||||
{
|
{
|
||||||
protected override bool ApplySizeRestrictionsToDefault => true;
|
protected override bool ApplySizeRestrictionsToDefault => true;
|
||||||
|
|
||||||
|
@ -68,10 +68,10 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
switch (lookup)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup target:
|
case SkinComponentsContainerLookup containerLookup:
|
||||||
switch (target.Lookup)
|
switch (containerLookup.Target)
|
||||||
{
|
{
|
||||||
case GlobalSkinComponentLookup.LookupType.SongSelect:
|
case SkinComponentsContainerLookup.TargetArea.SongSelect:
|
||||||
var songSelectComponents = new DefaultSkinComponentsContainer(_ =>
|
var songSelectComponents = new DefaultSkinComponentsContainer(_ =>
|
||||||
{
|
{
|
||||||
// do stuff when we need to.
|
// do stuff when we need to.
|
||||||
@ -79,7 +79,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
return songSelectComponents;
|
return songSelectComponents;
|
||||||
|
|
||||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||||
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
||||||
{
|
{
|
||||||
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
||||||
|
@ -45,13 +45,13 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
private void addResetTargetsStep()
|
private void addResetTargetsStep()
|
||||||
{
|
{
|
||||||
AddStep("reset targets", () => this.ChildrenOfType<SkinnableTargetContainer>().ForEach(t =>
|
AddStep("reset targets", () => this.ChildrenOfType<SkinComponentsContainer>().ForEach(t =>
|
||||||
{
|
{
|
||||||
LegacySkin.ResetDrawableTarget(t);
|
LegacySkin.ResetDrawableTarget(t);
|
||||||
t.Reload();
|
t.Reload();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddUntilStep("wait for components to load", () => this.ChildrenOfType<SkinnableTargetContainer>().All(t => t.ComponentsLoaded));
|
AddUntilStep("wait for components to load", () => this.ChildrenOfType<SkinComponentsContainer>().All(t => t.ComponentsLoaded));
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class SkinProvidingPlayer : TestPlayer
|
public partial class SkinProvidingPlayer : TestPlayer
|
||||||
|
Loading…
Reference in New Issue
Block a user