mirror of
https://github.com/osukey/osukey.git
synced 2025-08-03 06:36:31 +09:00
Merge branch 'master' into skin-editor-closest-anchor
This commit is contained in:
@ -22,6 +22,8 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public class DefaultSkin : Skin
|
||||
{
|
||||
private readonly IStorageResourceProvider resources;
|
||||
|
||||
public DefaultSkin(IStorageResourceProvider resources)
|
||||
: this(SkinInfo.Default, resources)
|
||||
{
|
||||
@ -31,12 +33,23 @@ namespace osu.Game.Skinning
|
||||
public DefaultSkin(SkinInfo skin, IStorageResourceProvider resources)
|
||||
: base(skin, resources)
|
||||
{
|
||||
this.resources = resources;
|
||||
Configuration = new DefaultSkinConfiguration();
|
||||
}
|
||||
|
||||
public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||
|
||||
public override ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||
public override ISample GetSample(ISampleInfo sampleInfo)
|
||||
{
|
||||
foreach (var lookup in sampleInfo.LookupNames)
|
||||
{
|
||||
var sample = resources.AudioManager.Samples.Get(lookup);
|
||||
if (sample != null)
|
||||
return sample;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -11,5 +12,13 @@ namespace osu.Game.Skinning
|
||||
public interface ISkinSource : ISkin
|
||||
{
|
||||
event Action SourceChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Find the first (if any) skin that can fulfill the lookup.
|
||||
/// This should be used for cases where subsequent lookups (for related components) need to occur on the same skin.
|
||||
/// </summary>
|
||||
/// <returns>The skin to be used for subsequent lookups, or <c>null</c> if none is available.</returns>
|
||||
[CanBeNull]
|
||||
ISkin FindProvider(Func<ISkin, bool> lookupFunction);
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,6 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
private const double epic_cutoff = 0.5;
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource skin { get; set; }
|
||||
|
||||
private LegacyHealthPiece fill;
|
||||
private LegacyHealthPiece marker;
|
||||
|
||||
@ -33,10 +30,13 @@ namespace osu.Game.Skinning
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
var skin = source.FindProvider(s => getTexture(s, "bg") != null);
|
||||
|
||||
// the marker lookup to decide which display style must be performed on the source of the bg, which is the most common element.
|
||||
isNewStyle = getTexture(skin, "marker") != null;
|
||||
|
||||
// background implementation is the same for both versions.
|
||||
@ -78,7 +78,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
protected override void Flash(JudgementResult result) => marker.Flash(result);
|
||||
|
||||
private static Texture getTexture(ISkinSource skin, string name) => skin.GetTexture($"scorebar-{name}");
|
||||
private static Texture getTexture(ISkin skin, string name) => skin?.GetTexture($"scorebar-{name}");
|
||||
|
||||
private static Color4 getFillColour(double hp)
|
||||
{
|
||||
@ -97,7 +97,7 @@ namespace osu.Game.Skinning
|
||||
private readonly Texture dangerTexture;
|
||||
private readonly Texture superDangerTexture;
|
||||
|
||||
public LegacyOldStyleMarker(ISkinSource skin)
|
||||
public LegacyOldStyleMarker(ISkin skin)
|
||||
{
|
||||
normalTexture = getTexture(skin, "ki");
|
||||
dangerTexture = getTexture(skin, "kidanger");
|
||||
@ -128,9 +128,9 @@ namespace osu.Game.Skinning
|
||||
|
||||
public class LegacyNewStyleMarker : LegacyMarker
|
||||
{
|
||||
private readonly ISkinSource skin;
|
||||
private readonly ISkin skin;
|
||||
|
||||
public LegacyNewStyleMarker(ISkinSource skin)
|
||||
public LegacyNewStyleMarker(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
@ -150,9 +150,9 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
internal class LegacyOldStyleFill : LegacyHealthPiece
|
||||
internal abstract class LegacyFill : LegacyHealthPiece
|
||||
{
|
||||
public LegacyOldStyleFill(ISkinSource skin)
|
||||
protected LegacyFill(ISkin skin)
|
||||
{
|
||||
// required for sizing correctly..
|
||||
var firstFrame = getTexture(skin, "colour-0");
|
||||
@ -164,27 +164,29 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
else
|
||||
{
|
||||
InternalChild = skin.GetAnimation("scorebar-colour", true, true, startAtCurrentTime: false, applyConfigFrameRate: true) ?? Drawable.Empty();
|
||||
InternalChild = skin.GetAnimation("scorebar-colour", true, true, startAtCurrentTime: false, applyConfigFrameRate: true) ?? Empty();
|
||||
Size = new Vector2(firstFrame.DisplayWidth, firstFrame.DisplayHeight);
|
||||
}
|
||||
|
||||
Position = new Vector2(3, 10) * 1.6f;
|
||||
Masking = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class LegacyNewStyleFill : LegacyHealthPiece
|
||||
internal class LegacyOldStyleFill : LegacyFill
|
||||
{
|
||||
public LegacyNewStyleFill(ISkinSource skin)
|
||||
public LegacyOldStyleFill(ISkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
InternalChild = new Sprite
|
||||
{
|
||||
Texture = getTexture(skin, "colour"),
|
||||
};
|
||||
Position = new Vector2(3, 10) * 1.6f;
|
||||
}
|
||||
}
|
||||
|
||||
Size = InternalChild.Size;
|
||||
internal class LegacyNewStyleFill : LegacyFill
|
||||
{
|
||||
public LegacyNewStyleFill(ISkin skin)
|
||||
: base(skin)
|
||||
{
|
||||
Position = new Vector2(7.5f, 7.8f) * 1.6f;
|
||||
Masking = true;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -60,10 +60,17 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
}
|
||||
|
||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, string filename)
|
||||
/// <summary>
|
||||
/// Construct a new legacy skin instance.
|
||||
/// </summary>
|
||||
/// <param name="skin">The model for this skin.</param>
|
||||
/// <param name="storage">A storage for looking up files within this skin using user-facing filenames.</param>
|
||||
/// <param name="resources">Access to raw game resources.</param>
|
||||
/// <param name="configurationFilename">The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file.</param>
|
||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore<byte[]> storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename)
|
||||
: base(skin, resources)
|
||||
{
|
||||
using (var stream = storage?.GetStream(filename))
|
||||
using (var stream = storage?.GetStream(configurationFilename))
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
@ -127,7 +134,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
case LegacyManiaSkinConfigurationLookup maniaLookup:
|
||||
if (!AllowManiaSkin)
|
||||
return null;
|
||||
break;
|
||||
|
||||
var result = lookupForMania<TValue>(maniaLookup);
|
||||
if (result != null)
|
||||
@ -390,6 +397,7 @@ namespace osu.Game.Skinning
|
||||
return null;
|
||||
|
||||
case GameplaySkinComponent<HitResult> resultComponent:
|
||||
// TODO: this should be inside the judgement pieces.
|
||||
Func<Drawable> createDrawable = () => getJudgementAnimation(resultComponent.Component);
|
||||
|
||||
// kind of wasteful that we throw this away, but should do for now.
|
||||
@ -485,7 +493,9 @@ namespace osu.Game.Skinning
|
||||
var sample = Samples?.Get(lookup);
|
||||
|
||||
if (sample != null)
|
||||
{
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -518,8 +528,7 @@ namespace osu.Game.Skinning
|
||||
yield return componentName;
|
||||
|
||||
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
|
||||
string lastPiece = componentName.Split('/').Last();
|
||||
yield return componentName.StartsWith("Gameplay/taiko/", StringComparison.Ordinal) ? "taiko-" + lastPiece : lastPiece;
|
||||
yield return componentName.Split('/').Last();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -27,6 +27,18 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
Texture texture;
|
||||
|
||||
// find the first source which provides either the animated or non-animated version.
|
||||
ISkin skin = (source as ISkinSource)?.FindProvider(s =>
|
||||
{
|
||||
if (animatable && s.GetTexture(getFrameName(0)) != null)
|
||||
return true;
|
||||
|
||||
return s.GetTexture(componentName, wrapModeS, wrapModeT) != null;
|
||||
}) ?? source;
|
||||
|
||||
if (skin == null)
|
||||
return null;
|
||||
|
||||
if (animatable)
|
||||
{
|
||||
var textures = getTextures().ToArray();
|
||||
@ -35,7 +47,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
var animation = new SkinnableTextureAnimation(startAtCurrentTime)
|
||||
{
|
||||
DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures),
|
||||
DefaultFrameLength = frameLength ?? getFrameLength(skin, applyConfigFrameRate, textures),
|
||||
Loop = looping,
|
||||
};
|
||||
|
||||
@ -47,7 +59,7 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
|
||||
// if an animation was not allowed or not found, fall back to a sprite retrieval.
|
||||
if ((texture = source.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
||||
if ((texture = skin.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
||||
return new Sprite { Texture = texture };
|
||||
|
||||
return null;
|
||||
@ -56,12 +68,14 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
for (int i = 0; true; i++)
|
||||
{
|
||||
if ((texture = source.GetTexture($"{componentName}{animationSeparator}{i}", wrapModeS, wrapModeT)) == null)
|
||||
if ((texture = skin.GetTexture(getFrameName(i), wrapModeS, wrapModeT)) == null)
|
||||
break;
|
||||
|
||||
yield return texture;
|
||||
}
|
||||
}
|
||||
|
||||
string getFrameName(int frameIndex) => $"{componentName}{animationSeparator}{frameIndex}";
|
||||
}
|
||||
|
||||
public static bool HasFont(this ISkin source, LegacyFont font)
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 System;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -15,7 +16,7 @@ namespace osu.Game.Skinning
|
||||
/// <summary>
|
||||
/// Transformer used to handle support of legacy features for individual rulesets.
|
||||
/// </summary>
|
||||
public abstract class LegacySkinTransformer : ISkin
|
||||
public abstract class LegacySkinTransformer : ISkinSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Source of the <see cref="ISkin"/> which is being transformed.
|
||||
@ -47,5 +48,13 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
|
||||
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => Source.FindProvider(lookupFunction);
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add => Source.SourceChanged += value;
|
||||
remove => Source.SourceChanged -= value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@ -70,33 +71,50 @@ namespace osu.Game.Skinning
|
||||
updateSample();
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.LoadComplete();
|
||||
|
||||
CurrentSkin.SourceChanged += skinChangedImmediate;
|
||||
}
|
||||
|
||||
private void skinChangedImmediate()
|
||||
{
|
||||
// Clean up the previous sample immediately on a source change.
|
||||
// This avoids a potential call to Play() of an already disposed sample (samples are disposed along with the skin, but SkinChanged is scheduled).
|
||||
clearPreviousSamples();
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin);
|
||||
updateSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this sample was playing before a skin source change.
|
||||
/// </summary>
|
||||
private bool wasPlaying;
|
||||
|
||||
private void clearPreviousSamples()
|
||||
{
|
||||
// only run if the samples aren't already cleared.
|
||||
// this ensures the "wasPlaying" state is stored correctly even if multiple clear calls are executed.
|
||||
if (!sampleContainer.Any()) return;
|
||||
|
||||
wasPlaying = Playing;
|
||||
|
||||
sampleContainer.Clear();
|
||||
Sample = null;
|
||||
}
|
||||
|
||||
private void updateSample()
|
||||
{
|
||||
if (sampleInfo == null)
|
||||
return;
|
||||
|
||||
bool wasPlaying = Playing;
|
||||
|
||||
sampleContainer.Clear();
|
||||
Sample = null;
|
||||
|
||||
var sample = CurrentSkin.GetSample(sampleInfo);
|
||||
|
||||
if (sample == null && AllowDefaultFallback)
|
||||
{
|
||||
foreach (var lookup in sampleInfo.LookupNames)
|
||||
{
|
||||
if ((sample = sampleStore.Get(lookup)) != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sample == null)
|
||||
return;
|
||||
|
||||
@ -155,6 +173,14 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (CurrentSkin != null)
|
||||
CurrentSkin.SourceChanged -= skinChangedImmediate;
|
||||
}
|
||||
|
||||
#region Re-expose AudioContainer
|
||||
|
||||
public BindableNumber<double> Volume => sampleContainer.Volume;
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
private readonly IResourceStore<byte[]> resources;
|
||||
|
||||
public readonly Bindable<Skin> CurrentSkin = new Bindable<Skin>(new DefaultSkin(null));
|
||||
public readonly Bindable<Skin> CurrentSkin = new Bindable<Skin>();
|
||||
public readonly Bindable<SkinInfo> CurrentSkinInfo = new Bindable<SkinInfo>(SkinInfo.Default) { Default = SkinInfo.Default };
|
||||
|
||||
public override IEnumerable<string> HandledExtensions => new[] { ".osk" };
|
||||
@ -48,6 +48,10 @@ namespace osu.Game.Skinning
|
||||
|
||||
protected override string ImportFromStablePath => "Skins";
|
||||
|
||||
private readonly Skin defaultLegacySkin;
|
||||
|
||||
private readonly Skin defaultSkin;
|
||||
|
||||
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore<byte[]> resources, AudioManager audio)
|
||||
: base(storage, contextFactory, new SkinStore(contextFactory, storage), host)
|
||||
{
|
||||
@ -55,7 +59,12 @@ namespace osu.Game.Skinning
|
||||
this.host = host;
|
||||
this.resources = resources;
|
||||
|
||||
defaultLegacySkin = new DefaultLegacySkin(this);
|
||||
defaultSkin = new DefaultSkin(this);
|
||||
|
||||
CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue);
|
||||
|
||||
CurrentSkin.Value = defaultSkin;
|
||||
CurrentSkin.ValueChanged += skin =>
|
||||
{
|
||||
if (skin.NewValue.SkinInfo != CurrentSkinInfo.Value)
|
||||
@ -204,13 +213,43 @@ namespace osu.Game.Skinning
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => CurrentSkin.Value.GetDrawableComponent(component);
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => lookupWithFallback(s => s.GetDrawableComponent(component));
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => CurrentSkin.Value.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => lookupWithFallback(s => s.GetTexture(componentName, wrapModeS, wrapModeT));
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo);
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => lookupWithFallback(s => s.GetSample(sampleInfo));
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => CurrentSkin.Value.GetConfig<TLookup, TValue>(lookup);
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => lookupWithFallback(s => s.GetConfig<TLookup, TValue>(lookup));
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
|
||||
{
|
||||
if (lookupFunction(CurrentSkin.Value))
|
||||
return CurrentSkin.Value;
|
||||
|
||||
if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin))
|
||||
return defaultLegacySkin;
|
||||
|
||||
if (lookupFunction(defaultSkin))
|
||||
return defaultSkin;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private T lookupWithFallback<T>(Func<ISkin, T> lookupFunction)
|
||||
where T : class
|
||||
{
|
||||
if (lookupFunction(CurrentSkin.Value) is T skinSourced)
|
||||
return skinSourced;
|
||||
|
||||
// TODO: we also want to return a DefaultLegacySkin here if the current *beatmap* is providing any skinned elements.
|
||||
// When attempting to address this, we may want to move the full DefaultLegacySkin fallback logic to within Player itself (to better allow
|
||||
// for beatmap skin visibility).
|
||||
if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin) is T legacySourced)
|
||||
return legacySourced;
|
||||
|
||||
// Finally fall back to the (non-legacy) default.
|
||||
return lookupFunction(defaultSkin);
|
||||
}
|
||||
|
||||
#region IResourceStorageProvider
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
@ -20,10 +21,14 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public event Action SourceChanged;
|
||||
|
||||
[CanBeNull]
|
||||
private readonly ISkin skin;
|
||||
|
||||
[CanBeNull]
|
||||
private ISkinSource fallbackSource;
|
||||
|
||||
private readonly NoFallbackProxy noFallbackLookupProxy;
|
||||
|
||||
protected virtual bool AllowDrawableLookup(ISkinComponent component) => true;
|
||||
|
||||
protected virtual bool AllowTextureLookup(string componentName) => true;
|
||||
@ -39,57 +44,106 @@ namespace osu.Game.Skinning
|
||||
this.skin = skin;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
noFallbackLookupProxy = new NoFallbackProxy(this);
|
||||
|
||||
if (skin is ISkinSource source)
|
||||
source.SourceChanged += TriggerSourceChanged;
|
||||
}
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
|
||||
{
|
||||
if (skin is ISkinSource source)
|
||||
{
|
||||
if (source.FindProvider(lookupFunction) is ISkin found)
|
||||
return found;
|
||||
}
|
||||
else if (skin != null)
|
||||
{
|
||||
// a proxy must be used here to correctly pass through the "Allow" checks without implicitly falling back to the fallbackSource.
|
||||
if (lookupFunction(noFallbackLookupProxy))
|
||||
return skin;
|
||||
}
|
||||
|
||||
return fallbackSource?.FindProvider(lookupFunction);
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||
=> GetDrawableComponent(component, true);
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component, bool fallback)
|
||||
{
|
||||
Drawable sourceDrawable;
|
||||
if (AllowDrawableLookup(component) && (sourceDrawable = skin?.GetDrawableComponent(component)) != null)
|
||||
return sourceDrawable;
|
||||
|
||||
if (!fallback)
|
||||
return null;
|
||||
|
||||
return fallbackSource?.GetDrawableComponent(component);
|
||||
}
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
||||
=> GetTexture(componentName, wrapModeS, wrapModeT, true);
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT, bool fallback)
|
||||
{
|
||||
Texture sourceTexture;
|
||||
if (AllowTextureLookup(componentName) && (sourceTexture = skin?.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
||||
return sourceTexture;
|
||||
|
||||
if (!fallback)
|
||||
return null;
|
||||
|
||||
return fallbackSource?.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
}
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo)
|
||||
=> GetSample(sampleInfo, true);
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo, bool fallback)
|
||||
{
|
||||
ISample sourceChannel;
|
||||
if (AllowSampleLookup(sampleInfo) && (sourceChannel = skin?.GetSample(sampleInfo)) != null)
|
||||
return sourceChannel;
|
||||
|
||||
if (!fallback)
|
||||
return null;
|
||||
|
||||
return fallbackSource?.GetSample(sampleInfo);
|
||||
}
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
=> GetConfig<TLookup, TValue>(lookup, true);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup, bool fallback)
|
||||
{
|
||||
if (skin != null)
|
||||
{
|
||||
if (lookup is GlobalSkinColours || lookup is SkinCustomColourLookup)
|
||||
return lookupWithFallback<TLookup, TValue>(lookup, AllowColourLookup);
|
||||
return lookupWithFallback<TLookup, TValue>(lookup, AllowColourLookup, fallback);
|
||||
|
||||
return lookupWithFallback<TLookup, TValue>(lookup, AllowConfigurationLookup);
|
||||
return lookupWithFallback<TLookup, TValue>(lookup, AllowConfigurationLookup, fallback);
|
||||
}
|
||||
|
||||
if (!fallback)
|
||||
return null;
|
||||
|
||||
return fallbackSource?.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
||||
private IBindable<TValue> lookupWithFallback<TLookup, TValue>(TLookup lookup, bool canUseSkinLookup)
|
||||
private IBindable<TValue> lookupWithFallback<TLookup, TValue>(TLookup lookup, bool canUseSkinLookup, bool canUseFallback)
|
||||
{
|
||||
if (canUseSkinLookup)
|
||||
{
|
||||
var bindable = skin.GetConfig<TLookup, TValue>(lookup);
|
||||
var bindable = skin?.GetConfig<TLookup, TValue>(lookup);
|
||||
if (bindable != null)
|
||||
return bindable;
|
||||
}
|
||||
|
||||
if (!canUseFallback)
|
||||
return null;
|
||||
|
||||
return fallbackSource?.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
||||
@ -117,6 +171,40 @@ namespace osu.Game.Skinning
|
||||
|
||||
if (fallbackSource != null)
|
||||
fallbackSource.SourceChanged -= TriggerSourceChanged;
|
||||
|
||||
if (skin is ISkinSource source)
|
||||
source.SourceChanged -= TriggerSourceChanged;
|
||||
}
|
||||
|
||||
private class NoFallbackProxy : ISkinSource
|
||||
{
|
||||
private readonly SkinProvidingContainer provider;
|
||||
|
||||
public NoFallbackProxy(SkinProvidingContainer provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||
=> provider.GetDrawableComponent(component, false);
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
||||
=> provider.GetTexture(componentName, wrapModeS, wrapModeT, false);
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo)
|
||||
=> provider.GetSample(sampleInfo, false);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
=> provider.GetConfig<TLookup, TValue>(lookup, false);
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add => provider.SourceChanged += value;
|
||||
remove => provider.SourceChanged -= value;
|
||||
}
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) =>
|
||||
provider.FindProvider(lookupFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,22 +22,6 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
protected ISkinSource CurrentSkin { get; private set; }
|
||||
|
||||
private readonly Func<ISkinSource, bool> allowFallback;
|
||||
|
||||
/// <summary>
|
||||
/// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
|
||||
/// </summary>
|
||||
protected bool AllowDefaultFallback => allowFallback == null || allowFallback.Invoke(CurrentSkin);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SkinReloadableDrawable"/>
|
||||
/// </summary>
|
||||
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
|
||||
protected SkinReloadableDrawable(Func<ISkinSource, bool> allowFallback = null)
|
||||
{
|
||||
this.allowFallback = allowFallback;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
@ -58,7 +42,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
private void skinChanged()
|
||||
{
|
||||
SkinChanged(CurrentSkin, AllowDefaultFallback);
|
||||
SkinChanged(CurrentSkin);
|
||||
OnSkinChanged?.Invoke();
|
||||
}
|
||||
|
||||
@ -66,8 +50,7 @@ namespace osu.Game.Skinning
|
||||
/// Called when a change is made to the skin.
|
||||
/// </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(ISkinSource skin, bool allowFallback)
|
||||
protected virtual void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -40,17 +40,14 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
/// <param name="component">The namespace-complete resource name for this skinnable element.</param>
|
||||
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
|
||||
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
|
||||
/// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param>
|
||||
public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation = null, Func<ISkinSource, bool> allowFallback = null,
|
||||
ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: this(component, allowFallback, confineMode)
|
||||
public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: this(component, confineMode)
|
||||
{
|
||||
createDefault = defaultImplementation;
|
||||
}
|
||||
|
||||
protected SkinnableDrawable(ISkinComponent component, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(allowFallback)
|
||||
protected SkinnableDrawable(ISkinComponent component, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
{
|
||||
this.component = component;
|
||||
this.confineMode = confineMode;
|
||||
@ -76,13 +73,13 @@ namespace osu.Game.Skinning
|
||||
/// </summary>
|
||||
protected virtual bool ApplySizeRestrictionsToDefault => false;
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
Drawable = skin.GetDrawableComponent(component);
|
||||
|
||||
isDefault = false;
|
||||
|
||||
if (Drawable == null && allowFallback)
|
||||
if (Drawable == null)
|
||||
{
|
||||
Drawable = CreateDefault(component);
|
||||
isDefault = true;
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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 System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -19,8 +18,8 @@ namespace osu.Game.Skinning
|
||||
[Resolved]
|
||||
private TextureStore textures { get; set; }
|
||||
|
||||
public SkinnableSprite(string textureName, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(new SpriteComponent(textureName), allowFallback, confineMode)
|
||||
public SkinnableSprite(string textureName, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(new SpriteComponent(textureName), confineMode)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -9,14 +9,14 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public class SkinnableSpriteText : SkinnableDrawable, IHasText
|
||||
{
|
||||
public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(component, defaultImplementation, allowFallback, confineMode)
|
||||
public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(component, defaultImplementation, confineMode)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.SkinChanged(skin);
|
||||
|
||||
if (Drawable is IHasText textDrawable)
|
||||
textDrawable.Text = Text;
|
||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Skinning
|
||||
|
||||
private readonly BindableList<ISkinnableDrawable> components = new BindableList<ISkinnableDrawable>();
|
||||
|
||||
public bool ComponentsLoaded { get; private set; }
|
||||
|
||||
public SkinnableTargetContainer(SkinnableTarget target)
|
||||
{
|
||||
Target = target;
|
||||
@ -30,6 +32,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
ClearInternal();
|
||||
components.Clear();
|
||||
ComponentsLoaded = false;
|
||||
|
||||
content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetComponentsContainer;
|
||||
|
||||
@ -39,8 +42,11 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
AddInternal(wrapper);
|
||||
components.AddRange(wrapper.Children.OfType<ISkinnableDrawable>());
|
||||
ComponentsLoaded = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
ComponentsLoaded = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ISkinnableTarget"/>
|
||||
@ -73,9 +79,9 @@ namespace osu.Game.Skinning
|
||||
components.Remove(component);
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.SkinChanged(skin);
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
Reference in New Issue
Block a user