1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 03:13:21 +08:00

Add skin source fallback chain

This commit is contained in:
Dean Herbert 2018-03-20 16:26:36 +09:00
parent fd0391daf7
commit 9ad4e9284a
12 changed files with 142 additions and 23 deletions

View File

@ -105,6 +105,7 @@ namespace osu.Game
runMigrations();
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
dependencies.CacheAs<ISkinSource>(SkinManager);
var api = new APIAccess(LocalConfig);

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Audio;
using osu.Game.Graphics;
@ -19,7 +18,7 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Objects.Drawables
{
public abstract class DrawableHitObject : CompositeDrawable, IHasAccentColour
public abstract class DrawableHitObject : SkinReloadableDrawable, IHasAccentColour
{
public readonly HitObject HitObject;
@ -103,6 +102,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboIndex combo)
AccentColour = skin.GetComboColour(combo) ?? Color4.White;
}
protected override void LoadComplete()
{
base.LoadComplete();

View File

@ -26,6 +26,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osu.Game.Storyboards.Drawables;
namespace osu.Game.Screens.Play
@ -163,7 +164,11 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
RulesetContainer,
new LocalSkinOverrideContainer(working.Skin)
{
RelativeSizeAxes = Axes.Both,
Child = RulesetContainer
},
new SkipOverlay(firstObjectTime)
{
Clock = Clock, // skip button doesn't want to use the audio clock directly

View File

@ -3,6 +3,7 @@
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using OpenTK.Graphics;
namespace osu.Game.Skinning
{
@ -11,17 +12,20 @@ namespace osu.Game.Skinning
public DefaultSkin()
: base(SkinInfo.Default)
{
Configuration = new SkinConfiguration();
Configuration = new SkinConfiguration
{
ComboColours =
{
new Color4(17, 136, 170, 255),
new Color4(102, 136, 0, 255),
new Color4(204, 102, 0, 255),
new Color4(121, 9, 13, 255)
}
};
}
public override Drawable GetDrawableComponent(string componentName)
{
return null;
}
public override Drawable GetDrawableComponent(string componentName) => null;
public override SampleChannel GetSample(string sampleName)
{
return null;
}
public override SampleChannel GetSample(string sampleName) => null;
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Skinning
{
/// <summary>
/// Provides access to skinnable elements.
/// </summary>
public interface ISkinSource
{
event Action SourceChanged;
Drawable GetDrawableComponent(string componentName);
SampleChannel GetSample(string sampleName);
Color4? GetComboColour(IHasComboIndex comboObject);
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Skinning
{
public class LocalSkinOverrideContainer : Container, ISkinSource
{
public event Action SourceChanged;
public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName);
public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName);
public Color4? GetComboColour(IHasComboIndex comboObject) => source.GetComboColour(comboObject) ?? fallbackSource?.GetComboColour(comboObject);
private readonly ISkinSource source;
private ISkinSource fallbackSource;
public LocalSkinOverrideContainer(ISkinSource source)
{
this.source = source;
}
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
fallbackSource = dependencies.Get<ISkinSource>();
if (fallbackSource != null)
fallbackSource.SourceChanged += () => SourceChanged?.Invoke();
dependencies.CacheAs<ISkinSource>(this);
return dependencies;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (fallbackSource != null)
fallbackSource.SourceChanged -= SourceChanged;
}
}
}

View File

@ -4,19 +4,26 @@
using System;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Skinning
{
public abstract class Skin : IDisposable
public abstract class Skin : IDisposable, ISkinSource
{
public readonly SkinInfo SkinInfo;
public virtual SkinConfiguration Configuration { get; protected set; }
public event Action SourceChanged;
public abstract Drawable GetDrawableComponent(string componentName);
public abstract SampleChannel GetSample(string sampleName);
public virtual Color4? GetComboColour(IHasComboIndex comboObject) =>
Configuration.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[comboObject.ComboIndex % Configuration.ComboColours.Count];
protected Skin(SkinInfo skin)
{
SkinInfo = skin;

View File

@ -7,14 +7,18 @@ using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IO.Archives;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Skinning
{
public class SkinManager : ArchiveModelManager<SkinInfo, SkinFileInfo>
public class SkinManager : ArchiveModelManager<SkinInfo, SkinFileInfo>, ISkinSource
{
private readonly AudioManager audio;
@ -89,6 +93,8 @@ namespace osu.Game.Skinning
{
if (skin.SkinInfo != CurrentSkinInfo.Value)
throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead.");
SourceChanged?.Invoke();
};
// migrate older imports which didn't have access to skin.ini
@ -108,5 +114,13 @@ namespace osu.Game.Skinning
/// <param name="query">The query.</param>
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public SkinInfo Query(Expression<Func<SkinInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query);
public event Action SourceChanged;
public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName);
public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName);
public Color4? GetComboColour(IHasComboIndex comboObject) => CurrentSkin.Value.GetComboColour(comboObject);
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Skinning
@ -12,7 +11,7 @@ namespace osu.Game.Skinning
/// </summary>
public abstract class SkinReloadableDrawable : CompositeDrawable
{
private Bindable<Skin> skin;
private ISkinSource skin;
/// <summary>
/// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
@ -29,16 +28,18 @@ namespace osu.Game.Skinning
}
[BackgroundDependencyLoader]
private void load(SkinManager skinManager)
private void load(ISkinSource source)
{
skin = skinManager.CurrentSkin.GetBoundCopy();
skin.ValueChanged += skin => SkinChanged(skin, allowDefaultFallback || skin.SkinInfo == SkinInfo.Default);
skin = source;
skin.SourceChanged += onChange;
}
private void onChange() => SkinChanged(skin, allowDefaultFallback);
protected override void LoadAsyncComplete()
{
base.LoadAsyncComplete();
skin.TriggerChange();
onChange();
}
/// <summary>
@ -46,7 +47,7 @@ namespace osu.Game.Skinning
/// </summary>
/// <param name="skin">The new skin.</param>
/// <param name="allowFallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
protected virtual void SkinChanged(Skin skin, bool allowFallback)
protected virtual void SkinChanged(ISkinSource skin, bool allowFallback)
{
}
}

View File

@ -40,7 +40,7 @@ namespace osu.Game.Skinning
RelativeSizeAxes = Axes.Both;
}
protected override void SkinChanged(Skin skin, bool allowFallback)
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
var drawable = skin.GetDrawableComponent(componentName);
if (drawable != null)

View File

@ -31,7 +31,7 @@ namespace osu.Game.Skinning
public void Play() => channels?.ForEach(c => c.Play());
protected override void SkinChanged(Skin skin, bool allowFallback)
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
channels = samples.Select(s =>
{

View File

@ -874,8 +874,10 @@
<Compile Include="Screens\Tournament\Teams\StorageBackedTeamList.cs" />
<Compile Include="Skinning\BeatmapSkin.cs" />
<Compile Include="Skinning\DefaultSkin.cs" />
<Compile Include="Skinning\ISkinSource.cs" />
<Compile Include="Skinning\LegacySkin.cs" />
<Compile Include="Skinning\LegacySkinDecoder.cs" />
<Compile Include="Skinning\LocalSkinOverrideContainer.cs" />
<Compile Include="Skinning\Skin.cs" />
<Compile Include="Skinning\SkinConfiguration.cs" />
<Compile Include="Skinning\SkinFileInfo.cs" />