Refactor skin configuration to be infinitely extensible

This commit is contained in:
Dean Herbert 2019-09-03 17:57:34 +09:00
parent 976f5020c0
commit bebc3309ce
28 changed files with 214 additions and 107 deletions

View File

@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Audio; using osu.Game.Audio;
@ -99,8 +100,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public Texture GetTexture(string componentName) => public Texture GetTexture(string componentName) =>
throw new NotImplementedException(); throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
throw new NotImplementedException();
} }
} }
} }

View File

@ -7,6 +7,7 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
@ -135,6 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public SampleChannel GetSample(ISampleInfo sampleInfo) => null; public SampleChannel GetSample(ISampleInfo sampleInfo) => null;
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default; public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
public event Action SourceChanged; public event Action SourceChanged;

View File

@ -12,6 +12,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -166,12 +167,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
base.SkinChanged(skin, allowFallback); base.SkinChanged(skin, allowFallback);
Body.BorderSize = skin.GetValue<SkinConfiguration, float?>(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE; Body.BorderSize = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE;
sliderPathRadius = skin.GetValue<SkinConfiguration, float?>(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; sliderPathRadius = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
updatePathRadius(); updatePathRadius();
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value; Body.AccentColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value;
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; Body.BorderColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBorder)?.Value ?? Color4.White;
} }
private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius; private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius;

View File

@ -11,6 +11,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Skinning;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
@ -218,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
float radius = skin.GetValue<SkinConfiguration, float?>(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS; float radius = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
InternalChild = new CircularContainer InternalChild = new CircularContainer
{ {

View File

@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this); public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkin(source); public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
public override int? LegacyID => 0; public override int? LegacyID => 0;

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin, DrawableHitObject drawableObject) private void load(ISkinSource skin, DrawableHitObject drawableObject)
{ {
animationContent.Colour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Color4.White; animationContent.Colour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
InternalChildren = new[] InternalChildren = new[]
{ {

View File

@ -3,21 +3,19 @@
using System; using System;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning namespace osu.Game.Rulesets.Osu.Skinning
{ {
public class OsuLegacySkin : ISkin public class OsuLegacySkinTransformer : ISkin
{ {
private readonly ISkin source; private readonly ISkin source;
private Lazy<SkinConfiguration> configuration;
private Lazy<bool> hasHitCircle; private Lazy<bool> hasHitCircle;
/// <summary> /// <summary>
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
/// </summary> /// </summary>
private const float legacy_circle_radius = 64 - 5; private const float legacy_circle_radius = 64 - 5;
public OsuLegacySkin(ISkinSource source) public OsuLegacySkinTransformer(ISkinSource source)
{ {
this.source = source; this.source = source;
@ -37,21 +35,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
private void sourceChanged() private void sourceChanged()
{ {
// these need to be lazy in order to ensure they aren't called before the dependencies have been loaded into our source.
configuration = new Lazy<SkinConfiguration>(() =>
{
var config = new SkinConfiguration();
if (hasHitCircle.Value)
config.SliderPathRadius = legacy_circle_radius;
// defaults should only be applied for non-beatmap skins (which are parsed via this constructor).
config.CustomColours["SliderBall"] =
source.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.TryGetValue("SliderBall", out var val) ? val : (Color4?)null)
?? new Color4(2, 170, 255, 255);
return config;
});
hasHitCircle = new Lazy<bool>(() => source.GetTexture("hitcircle") != null); hasHitCircle = new Lazy<bool>(() => source.GetTexture("hitcircle") != null);
} }
@ -96,8 +79,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
return null; return null;
case OsuSkinComponents.HitCircleText: case OsuSkinComponents.HitCircleText:
string font = GetValue<SkinConfiguration, string>(config => config.HitCircleFont); var font = GetConfig<OsuSkinConfiguration, string>(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default";
var overlap = GetValue<SkinConfiguration, float>(config => config.HitCircleOverlap); var overlap = GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0;
return !hasFont(font) return !hasFont(font)
? null ? null
@ -116,13 +99,27 @@ namespace osu.Game.Rulesets.Osu.Skinning
public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample); public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{ {
TValue val; switch (lookup)
if (configuration.Value is TConfiguration conf && (val = query.Invoke(conf)) != null) {
return val; case OsuSkinColour colour:
return source.GetConfig<SkinCustomColourLookup, TValue>(new SkinCustomColourLookup(colour));
return source.GetValue(query); case OsuSkinConfiguration osuLookup:
switch (osuLookup)
{
case OsuSkinConfiguration.SliderPathRadius:
if (hasHitCircle.Value)
return new BindableFloat(legacy_circle_radius) as Bindable<TValue>;
break;
}
break;
}
return source.GetConfig<TLookup, TValue>(lookup);
} }
private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null; private bool hasFont(string fontName) => source.GetTexture($"{fontName}-0") != null;

View File

@ -0,0 +1,12 @@
// 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.Rulesets.Osu.Skinning
{
public enum OsuSkinColour
{
SliderTrackOverride,
SliderBorder,
SliderBall
}
}

View File

@ -0,0 +1,14 @@
// 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.Rulesets.Osu.Skinning
{
public enum OsuSkinConfiguration
{
HitCircleFont,
HitCircleOverlap,
SliderBorderSize,
SliderPathRadius,
CursorExpand,
}
}

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void SkinChanged(ISkinSource skin, bool allowFallback) protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{ {
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true); cursorExpand = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -6,6 +6,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -137,7 +138,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public new Drawable Drawable => base.Drawable; public new Drawable Drawable => base.Drawable;
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null,
ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode) : base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
{ {
} }
@ -256,7 +258,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
} }
private class SecondarySource : ISkin private class SecondarySource : ISkin
@ -267,7 +269,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
} }
private class SkinSourceContainer : Container, ISkin private class SkinSourceContainer : Container, ISkin
@ -278,7 +280,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
} }
private class TestSkinComponent : ISkinComponent private class TestSkinComponent : ISkinComponent

View File

@ -241,7 +241,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
base.SkinChanged(skin, allowFallback); base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo) if (HitObject is IHasComboInformation combo)
AccentColour.Value = skin.GetValue<SkinConfiguration, Color4?>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; {
var comboColours = skin.GetConfig<GlobalSkinConfiguration, List<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
}
} }
/// <summary> /// <summary>

View File

@ -122,7 +122,7 @@ namespace osu.Game.Screens.Menu
Color4 defaultColour = Color4.White.Opacity(0.2f); Color4 defaultColour = Color4.White.Opacity(0.2f);
if (user.Value?.IsSupporter ?? false) if (user.Value?.IsSupporter ?? false)
AccentColour = skin.Value.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? defaultColour; AccentColour = skin.Value.GetConfig<GlobalSkinColour, Color4>(GlobalSkinColour.MenuGlow)?.Value ?? defaultColour;
else else
AccentColour = defaultColour; AccentColour = defaultColour;
} }

View File

@ -112,7 +112,7 @@ namespace osu.Game.Screens.Menu
Color4 baseColour = colours.Blue; Color4 baseColour = colours.Blue;
if (user.Value?.IsSupporter ?? false) if (user.Value?.IsSupporter ?? false)
baseColour = skin.Value.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("MenuGlow") ? s.CustomColours["MenuGlow"] : (Color4?)null) ?? baseColour; baseColour = skin.Value.GetConfig<GlobalSkinColour, Color4>(GlobalSkinColour.MenuGlow)?.Value ?? baseColour;
// linear colour looks better in this case, so let's use it for now. // linear colour looks better in this case, so let's use it for now.
Color4 gradientDark = baseColour.Opacity(0).ToLinear(); Color4 gradientDark = baseColour.Opacity(0).ToLinear();

View File

@ -1,7 +1,8 @@
// 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.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Audio; using osu.Game.Audio;
@ -21,5 +22,7 @@ namespace osu.Game.Skinning
public override Texture GetTexture(string componentName) => null; public override Texture GetTexture(string componentName) => null;
public override SampleChannel GetSample(ISampleInfo sampleInfo) => null; public override SampleChannel GetSample(ISampleInfo sampleInfo) => null;
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
} }
} }

View File

@ -12,8 +12,6 @@ namespace osu.Game.Skinning
{ {
public DefaultSkinConfiguration() public DefaultSkinConfiguration()
{ {
HitCircleFont = "default";
ComboColours.AddRange(new[] ComboColours.AddRange(new[]
{ {
new Color4(17, 136, 170, 255), new Color4(17, 136, 170, 255),
@ -21,8 +19,6 @@ namespace osu.Game.Skinning
new Color4(204, 102, 0, 255), new Color4(204, 102, 0, 255),
new Color4(121, 9, 13, 255) new Color4(121, 9, 13, 255)
}); });
CursorExpand = true;
} }
} }
} }

View File

@ -5,7 +5,7 @@ using System.Linq;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
public class GameplaySkinComponent<T> : ISkinComponent where T : struct public class GameplaySkinComponent<T> : ISkinComponent
{ {
public readonly T Component; public readonly T Component;

View File

@ -0,0 +1,10 @@
// 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 enum GlobalSkinColour
{
MenuGlow
}
}

View File

@ -0,0 +1,10 @@
// 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 enum GlobalSkinConfiguration
{
ComboColours
}
}

View File

@ -1,8 +1,8 @@
// 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 osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Audio; using osu.Game.Audio;
@ -20,6 +20,6 @@ namespace osu.Game.Skinning
SampleChannel GetSample(ISampleInfo sampleInfo); SampleChannel GetSample(ISampleInfo sampleInfo);
TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration; IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
} }
} }

View File

@ -1,10 +1,12 @@
// 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.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
@ -48,6 +50,47 @@ namespace osu.Game.Skinning
Samples?.Dispose(); Samples?.Dispose();
} }
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{
switch (lookup)
{
case GlobalSkinConfiguration global:
switch (global)
{
case GlobalSkinConfiguration.ComboColours:
return new Bindable<List<Color4>>(Configuration.ComboColours) as IBindable<TValue>;
}
break;
case GlobalSkinColour colour:
return getCustomColour(colour.ToString()) as IBindable<TValue>;
case SkinCustomColourLookup customColour:
return getCustomColour(customColour.Lookup.ToString()) as IBindable<TValue>;
default:
try
{
if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val))
{
var bindable = new Bindable<TValue>();
bindable.Parse(val);
return bindable;
}
}
catch
{
}
break;
}
return null;
}
private IBindable<Color4> getCustomColour(string lookup) => Configuration.CustomColours.TryGetValue(lookup, out var col) ? new Bindable<Color4>(col) : null;
public override Drawable GetDrawableComponent(ISkinComponent component) public override Drawable GetDrawableComponent(ISkinComponent component)
{ {
switch (component) switch (component)

View File

@ -14,47 +14,31 @@ namespace osu.Game.Skinning
protected override void ParseLine(DefaultSkinConfiguration skin, Section section, string line) protected override void ParseLine(DefaultSkinConfiguration skin, Section section, string line)
{ {
line = StripComments(line); if (section != Section.Colours)
var pair = SplitKeyVal(line);
switch (section)
{ {
case Section.General: line = StripComments(line);
switch (pair.Key)
{
case @"Name":
skin.SkinInfo.Name = pair.Value;
break;
case @"Author": var pair = SplitKeyVal(line);
skin.SkinInfo.Creator = pair.Value;
break;
case @"CursorExpand": switch (section)
skin.CursorExpand = pair.Value != "0"; {
break; case Section.General:
switch (pair.Key)
{
case @"Name":
skin.SkinInfo.Name = pair.Value;
return;
case @"SliderBorderSize": case @"Author":
skin.SliderBorderSize = Parsing.ParseFloat(pair.Value); skin.SkinInfo.Creator = pair.Value;
break; return;
} }
break; break;
}
case Section.Fonts: if (!string.IsNullOrEmpty(pair.Key))
switch (pair.Key) skin.ConfigDictionary[$"{section}/{pair.Key}"] = pair.Value;
{
case "HitCirclePrefix":
skin.HitCircleFont = pair.Value;
break;
case "HitCircleOverlap":
skin.HitCircleOverlap = int.Parse(pair.Value);
break;
}
break;
} }
base.ParseLine(skin, section, line); base.ParseLine(skin, section, line);

View File

@ -1,8 +1,9 @@
// 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;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Audio; using osu.Game.Audio;
@ -13,7 +14,7 @@ namespace osu.Game.Skinning
{ {
public readonly SkinInfo SkinInfo; public readonly SkinInfo SkinInfo;
public virtual SkinConfiguration Configuration { get; protected set; } public SkinConfiguration Configuration { get; protected set; }
public abstract Drawable GetDrawableComponent(ISkinComponent componentName); public abstract Drawable GetDrawableComponent(ISkinComponent componentName);
@ -21,8 +22,7 @@ namespace osu.Game.Skinning
public abstract Texture GetTexture(string componentName); public abstract Texture GetTexture(string componentName);
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
=> Configuration is TConfiguration conf ? query.Invoke(conf) : default;
protected Skin(SkinInfo skin) protected Skin(SkinInfo skin)
{ {

View File

@ -0,0 +1,16 @@
// 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.Configuration;
namespace osu.Game.Skinning
{
public class SkinConfigManager<T> : ConfigManager<T> where T : struct
{
protected override void PerformLoad()
{
}
protected override bool PerformSave() => false;
}
}

View File

@ -18,14 +18,6 @@ namespace osu.Game.Skinning
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>(); public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
public string HitCircleFont { get; set; } public readonly Dictionary<string, string> ConfigDictionary = new Dictionary<string, string>();
public int HitCircleOverlap { get; set; }
public float? SliderBorderSize { get; set; }
public float? SliderPathRadius { get; set; }
public bool? CursorExpand { get; set; }
} }
} }

View File

@ -0,0 +1,15 @@
// 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 SkinCustomColourLookup
{
public readonly object Lookup;
public SkinCustomColourLookup(object lookup)
{
Lookup = lookup;
}
}
}

View File

@ -1,4 +1,4 @@
// 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;
@ -131,6 +131,6 @@ namespace osu.Game.Skinning
public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo); public SampleChannel GetSample(ISampleInfo sampleInfo) => CurrentSkin.Value.GetSample(sampleInfo);
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query); public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => CurrentSkin.Value.GetConfig<TLookup, TValue>(lookup);
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
@ -64,13 +65,16 @@ namespace osu.Game.Skinning
return fallbackSource?.GetSample(sampleInfo); return fallbackSource?.GetSample(sampleInfo);
} }
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{ {
TValue val; if (AllowConfigurationLookup && skin != null)
if (AllowConfigurationLookup && skin != null && (val = skin.GetValue(query)) != null) {
return val; var bindable = skin.GetConfig<TLookup, TValue>(lookup);
if (bindable != null)
return bindable;
}
return fallbackSource == null ? default : fallbackSource.GetValue(query); return fallbackSource?.GetConfig<TLookup, TValue>(lookup);
} }
protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke(); protected virtual void TriggerSourceChanged() => SourceChanged?.Invoke();