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

Merge remote-tracking branch 'origin/master' into mask-separation

# Conflicts:
#	osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs
This commit is contained in:
smoogipoo 2018-10-30 16:46:13 +09:00
commit 5712a52817
17 changed files with 338 additions and 166 deletions

View File

@ -5,13 +5,11 @@ using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer<ManiaHitObject> public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap<ManiaHitObject>
{ {
public override string Name => "Dual Stages"; public override string Name => "Dual Stages";
public override string ShortenedName => "DS"; public override string ShortenedName => "DS";
@ -34,22 +32,21 @@ namespace osu.Game.Rulesets.Mania.Mods
mbc.TargetColumns *= 2; mbc.TargetColumns *= 2;
} }
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer) public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
{ {
var mrc = (ManiaRulesetContainer)rulesetContainer;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (isForCurrentRuleset) if (isForCurrentRuleset)
return; return;
var maniaBeatmap = (ManiaBeatmap)beatmap;
var newDefinitions = new List<StageDefinition>(); var newDefinitions = new List<StageDefinition>();
foreach (var existing in mrc.Beatmap.Stages) foreach (var existing in maniaBeatmap.Stages)
{ {
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
} }
mrc.Beatmap.Stages = newDefinitions; maniaBeatmap.Stages = newDefinitions;
} }
public PlayfieldType PlayfieldType => PlayfieldType.Dual; public PlayfieldType PlayfieldType => PlayfieldType.Dual;

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public readonly DrawableHitCircle HeadCircle; public readonly DrawableHitCircle HeadCircle;
public readonly DrawableSliderTail TailCircle; public readonly DrawableSliderTail TailCircle;
public readonly SliderBody Body; public readonly SnakingSliderBody Body;
public readonly SliderBall Ball; public readonly SliderBall Ball;
public DrawableSlider(Slider s) public DrawableSlider(Slider s)
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
Body = new SliderBody(s) Body = new SnakingSliderBody(s)
{ {
PathWidth = s.Scale * 64, PathWidth = s.Scale * 64,
}, },

View File

@ -0,0 +1,20 @@
// 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.Collections.Generic;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
/// <summary>
/// A <see cref="SliderBody"/> with the ability to set the drawn vertices manually.
/// </summary>
public class ManualSliderBody : SliderBody
{
public new void SetVertices(IReadOnlyList<Vector2> vertices)
{
base.SetVertices(vertices);
Size = Path.Size;
}
}
}

View File

@ -4,7 +4,6 @@
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -14,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class NumberPiece : Container public class NumberPiece : Container
{ {
private readonly SpriteText number; private readonly SkinnableSpriteText number;
public string Text public string Text
{ {
@ -41,15 +40,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}, },
Child = new Box() Child = new Box()
}, s => s.GetTexture("Play/osu/hitcircle") == null), }, s => s.GetTexture("Play/osu/hitcircle") == null),
number = new OsuSpriteText number = new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText
{ {
Text = @"1",
Font = @"Venera", Font = @"Venera",
UseFullGlyphHeight = false, UseFullGlyphHeight = false,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 40, TextSize = 40,
Alpha = 1 }, restrictSize: false)
{
Text = @"1"
} }
}; };
} }

View File

@ -1,24 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Lines;
using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Objects.Types;
using OpenTK; using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SliderBody : Container, ISliderProgress public abstract class SliderBody : CompositeDrawable
{ {
private readonly SliderPath path; private readonly SliderPath path;
protected Path Path => path;
private readonly BufferedContainer container; private readonly BufferedContainer container;
public float PathWidth public float PathWidth
@ -30,15 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
/// <summary> /// <summary>
/// Offset in absolute coordinates from the start of the curve. /// Offset in absolute coordinates from the start of the curve.
/// </summary> /// </summary>
public Vector2 PathOffset { get; private set; } public virtual Vector2 PathOffset => path.PositionInBoundingBox(path.Vertices[0]);
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
/// <summary> /// <summary>
/// Used to colour the path. /// Used to colour the path.
@ -74,28 +64,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
private Vector2 topLeftOffset; protected SliderBody()
private readonly Slider slider;
public SliderBody(Slider s)
{ {
slider = s; InternalChild = container = new BufferedContainer
Children = new Drawable[]
{ {
container = new BufferedContainer RelativeSizeAxes = Axes.Both,
{ CacheDrawnFrameBuffer = true,
RelativeSizeAxes = Axes.Both, Child = path = new SliderPath { Blending = BlendingMode.None }
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
{
path = new SliderPath
{
Blending = BlendingMode.None,
},
}
},
}; };
container.Attach(RenderbufferInternalFormat.DepthComponent16); container.Attach(RenderbufferInternalFormat.DepthComponent16);
@ -103,80 +78,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
public void SetRange(double p0, double p1) /// <summary>
/// Sets the vertices of the path which should be drawn by this <see cref="SliderBody"/>.
/// </summary>
/// <param name="vertices">The vertices</param>
protected void SetVertices(IReadOnlyList<Vector2> vertices)
{ {
if (p0 > p1) path.Vertices = vertices;
MathHelper.Swap(ref p0, ref p1); container.ForceRedraw();
if (updateSnaking(p0, p1))
{
// The path is generated such that its size encloses it. This change of size causes the path
// to move around while snaking, so we need to offset it to make sure it maintains the
// same position as when it is fully snaked.
var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
path.Position = topLeftOffset - newTopLeftOffset;
container.ForceRedraw();
}
}
[BackgroundDependencyLoader]
private void load()
{
computeSize();
}
private void computeSize()
{
// Generate the entire curve
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
foreach (Vector2 p in CurrentCurve)
path.AddVertex(p);
Size = path.Size;
topLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
PathOffset = path.PositionInBoundingBox(CurrentCurve[0]);
}
private bool updateSnaking(double p0, double p1)
{
if (SnakedStart == p0 && SnakedEnd == p1) return false;
SnakedStart = p0;
SnakedEnd = p1;
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
path.ClearVertices();
foreach (Vector2 p in CurrentCurve)
path.AddVertex(p);
return true;
}
public void UpdateProgress(double completionProgress)
{
var span = slider.SpanAt(completionProgress);
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
{
start = 0;
end = SnakingOut ? spanProgress : 1;
}
else
{
start = SnakingOut ? spanProgress : 0;
}
}
SetRange(start, end);
} }
private class SliderPath : SmoothPath private class SliderPath : SmoothPath

View File

@ -0,0 +1,105 @@
// 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 System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
/// <summary>
/// A <see cref="SliderBody"/> which changes its curve depending on the snaking progress.
/// </summary>
public class SnakingSliderBody : SliderBody, ISliderProgress
{
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
public override Vector2 PathOffset => snakedPathOffset;
/// <summary>
/// The top-left position of the path when fully snaked.
/// </summary>
private Vector2 snakedPosition;
/// <summary>
/// The offset of the path from <see cref="snakedPosition"/> when fully snaked.
/// </summary>
private Vector2 snakedPathOffset;
private readonly Slider slider;
public SnakingSliderBody(Slider slider)
{
this.slider = slider;
}
[BackgroundDependencyLoader]
private void load()
{
// Generate the entire curve
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
SetVertices(CurrentCurve);
// The body is sized to the full path size to avoid excessive autosize computations
Size = Path.Size;
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
}
public void UpdateProgress(double completionProgress)
{
var span = slider.SpanAt(completionProgress);
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
{
start = 0;
end = SnakingOut ? spanProgress : 1;
}
else
{
start = SnakingOut ? spanProgress : 0;
}
}
setRange(start, end);
}
private void setRange(double p0, double p1)
{
if (p0 > p1)
MathHelper.Swap(ref p0, ref p1);
if (SnakedStart == p0 && SnakedEnd == p1) return;
SnakedStart = p0;
SnakedEnd = p1;
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
SetVertices(CurrentCurve);
// The bounding box of the path expands as it snakes, which in turn shifts the position of the path.
// Depending on the direction of expansion, it may appear as if the path is expanding towards the position of the slider
// rather than expanding out from the position of the slider.
// To remove this effect, the path's position is shifted towards its final snaked position
Path.Position = snakedPosition - Path.PositionInBoundingBox(Vector2.Zero);
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="Beatmap"/>
/// after conversion and post-processing has completed.
/// </summary>
public interface IApplicableToBeatmap<TObject> : IApplicableMod
where TObject : HitObject
{
/// <summary>
/// Applies this <see cref="IApplicableToBeatmap{TObject}"/> to a <see cref="Beatmap{TObject}"/>.
/// </summary>
/// <param name="beatmap">The <see cref="Beatmap{TObject}"/> to apply to.</param>
void ApplyToBeatmap(Beatmap<TObject> beatmap);
}
}

View File

@ -238,6 +238,8 @@ namespace osu.Game.Rulesets.UI
KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both; KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
applyBeatmapMods(Mods);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -255,16 +257,29 @@ namespace osu.Game.Rulesets.UI
KeyBindingInputManager.Add(Cursor); KeyBindingInputManager.Add(Cursor);
// Apply mods // Apply mods
applyMods(Mods, config); applyRulesetMods(Mods, config);
loadObjects(); loadObjects();
} }
/// <summary>
/// Applies the active mods to the Beatmap.
/// </summary>
/// <param name="mods"></param>
private void applyBeatmapMods(IEnumerable<Mod> mods)
{
if (mods == null)
return;
foreach (var mod in mods.OfType<IApplicableToBeatmap<TObject>>())
mod.ApplyToBeatmap(Beatmap);
}
/// <summary> /// <summary>
/// Applies the active mods to this RulesetContainer. /// Applies the active mods to this RulesetContainer.
/// </summary> /// </summary>
/// <param name="mods"></param> /// <param name="mods"></param>
private void applyMods(IEnumerable<Mod> mods, OsuConfigManager config) private void applyRulesetMods(IEnumerable<Mod> mods, OsuConfigManager config)
{ {
if (mods == null) if (mods == null)
return; return;

View File

@ -307,10 +307,10 @@ namespace osu.Game.Screens.Select
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
Alpha = 0;
InternalChild = textContainer = new FillFlowContainer InternalChild = textContainer = new FillFlowContainer
{ {
Alpha = 0,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Spacing = new Vector2(spacing / 2), Spacing = new Vector2(spacing / 2),
@ -337,7 +337,7 @@ namespace osu.Game.Screens.Select
{ {
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
this.FadeOut(transition_duration); textContainer.FadeOut(transition_duration);
return; return;
} }
@ -345,8 +345,6 @@ namespace osu.Game.Screens.Select
} }
} }
public override bool IsPresent => base.IsPresent || textFlow == null; // Visibility is updated in the LoadComponentAsync callback
private void setTextAsync(string text) private void setTextAsync(string text)
{ {
LoadComponentAsync(new OsuTextFlowContainer(s => s.TextSize = 14) LoadComponentAsync(new OsuTextFlowContainer(s => s.TextSize = 14)
@ -361,7 +359,7 @@ namespace osu.Game.Screens.Select
textContainer.Add(textFlow = loaded); textContainer.Add(textFlow = loaded);
// fade in if we haven't yet. // fade in if we haven't yet.
this.FadeIn(transition_duration); textContainer.FadeIn(transition_duration);
}); });
} }
} }

View File

@ -16,7 +16,6 @@ using osu.Game.Graphics;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -68,7 +67,7 @@ namespace osu.Game.Screens.Select
BeatmapOptions.AddButton(@"Edit", @"beatmap", FontAwesome.fa_pencil, colours.Yellow, () => BeatmapOptions.AddButton(@"Edit", @"beatmap", FontAwesome.fa_pencil, colours.Yellow, () =>
{ {
ValidForResume = false; ValidForResume = false;
Push(new Editor()); Edit();
}, Key.Number3); }, Key.Number3);
if (dialogOverlay != null) if (dialogOverlay != null)

View File

@ -223,9 +223,9 @@ namespace osu.Game.Screens.Select
Carousel.LoadBeatmapSetsFromManager(this.beatmaps); Carousel.LoadBeatmapSetsFromManager(this.beatmaps);
} }
public void Edit(BeatmapInfo beatmap) public void Edit(BeatmapInfo beatmap = null)
{ {
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value); Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce);
Push(new Editor()); Push(new Editor());
} }

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics.Sprites;
using OpenTK; using OpenTK;
namespace osu.Game.Skinning namespace osu.Game.Skinning
@ -56,30 +57,39 @@ namespace osu.Game.Skinning
case "Play/Great": case "Play/Great":
componentName = "hit300"; componentName = "hit300";
break; break;
case "Play/osu/number-text":
return !hasFont(Configuration.HitCircleFont) ? null : new LegacySpriteText(Textures, Configuration.HitCircleFont) { Scale = new Vector2(0.96f) };
} }
float ratio = 0.72f; // brings sizing roughly in-line with stable var texture = GetTexture(componentName);
var texture = GetTexture($"{componentName}@2x");
if (texture == null) if (texture == null)
{ return null;
ratio *= 2;
texture = GetTexture(componentName);
}
if (texture == null) return null; return new Sprite { Texture = texture };
return new Sprite
{
Texture = texture,
Scale = new Vector2(ratio),
};
} }
public override Texture GetTexture(string componentName) => Textures.Get(componentName); public override Texture GetTexture(string componentName)
{
float ratio = 2;
var texture = Textures.Get($"{componentName}@2x");
if (texture == null)
{
ratio = 1;
texture = Textures.Get(componentName);
}
if (texture != null)
texture.ScaleAdjust = ratio / 0.72f; // brings sizing roughly in-line with stable
return texture;
}
public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName); public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName);
private bool hasFont(string fontName) => GetTexture($"{fontName}-0") != null;
protected class LegacySkinResourceStore<T> : IResourceStore<byte[]> protected class LegacySkinResourceStore<T> : IResourceStore<byte[]>
where T : INamedFileInfo where T : INamedFileInfo
{ {
@ -142,5 +152,40 @@ namespace osu.Game.Skinning
#endregion #endregion
} }
private class LegacySpriteText : OsuSpriteText
{
private readonly TextureStore textures;
private readonly string font;
public LegacySpriteText(TextureStore textures, string font)
{
this.textures = textures;
this.font = font;
Shadow = false;
UseFullGlyphHeight = false;
}
protected override Texture GetTextureForCharacter(char c)
{
string textureName = $"{font}-{c}";
// Approximate value that brings character sizing roughly in-line with stable
float ratio = 36;
var texture = textures.Get($"{textureName}@2x");
if (texture == null)
{
ratio = 18;
texture = textures.Get(textureName);
}
if (texture != null)
texture.ScaleAdjust = ratio;
return texture;
}
}
} }
} }

View File

@ -19,6 +19,7 @@ namespace osu.Game.Skinning
switch (section) switch (section)
{ {
case Section.General: case Section.General:
{
var pair = SplitKeyVal(line); var pair = SplitKeyVal(line);
switch (pair.Key) switch (pair.Key)
@ -32,6 +33,20 @@ namespace osu.Game.Skinning
} }
break; break;
}
case Section.Fonts:
{
var pair = SplitKeyVal(line);
switch (pair.Key)
{
case "HitCirclePrefix":
skin.HitCircleFont = pair.Value;
break;
}
break;
}
} }
base.ParseLine(skin, section, line); base.ParseLine(skin, section, line);

View File

@ -14,5 +14,7 @@ namespace osu.Game.Skinning
public List<Color4> ComboColours { get; set; } = new List<Color4>(); public List<Color4> ComboColours { get; set; } = new List<Color4>();
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; } = "default";
} }
} }

View File

@ -18,6 +18,11 @@ namespace osu.Game.Skinning
public class SkinnableDrawable<T> : SkinReloadableDrawable public class SkinnableDrawable<T> : SkinReloadableDrawable
where T : Drawable where T : Drawable
{ {
/// <summary>
/// The displayed component. May or may not be a type-<typeparamref name="T"/> member.
/// </summary>
protected Drawable Drawable { get; private set; }
private readonly Func<string, T> createDefault; private readonly Func<string, T> createDefault;
private readonly string componentName; private readonly string componentName;
@ -31,7 +36,8 @@ namespace osu.Game.Skinning
/// <param name="defaultImplementation">A function to create the default skin implementation of this 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="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
/// <param name="restrictSize">Whether a user-skin drawable should be limited to the size of our parent.</param> /// <param name="restrictSize">Whether a user-skin drawable should be limited to the size of our parent.</param>
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true) : base(allowFallback) public SkinnableDrawable(string name, Func<string, T> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
: base(allowFallback)
{ {
componentName = name; componentName = name;
createDefault = defaultImplementation; createDefault = defaultImplementation;
@ -42,26 +48,27 @@ namespace osu.Game.Skinning
protected override void SkinChanged(ISkinSource skin, bool allowFallback) protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{ {
var drawable = skin.GetDrawableComponent(componentName); Drawable = skin.GetDrawableComponent(componentName);
if (drawable != null)
if (Drawable != null)
{ {
if (restrictSize) if (restrictSize)
{ {
drawable.RelativeSizeAxes = Axes.Both; Drawable.RelativeSizeAxes = Axes.Both;
drawable.Size = Vector2.One; Drawable.Size = Vector2.One;
drawable.Scale = Vector2.One; Drawable.Scale = Vector2.One;
drawable.FillMode = FillMode.Fit; Drawable.FillMode = FillMode.Fit;
} }
} }
else if (allowFallback) else if (allowFallback)
drawable = createDefault(componentName); Drawable = createDefault(componentName);
if (drawable != null) if (Drawable != null)
{ {
drawable.Origin = Anchor.Centre; Drawable.Origin = Anchor.Centre;
drawable.Anchor = Anchor.Centre; Drawable.Anchor = Anchor.Centre;
InternalChild = drawable; InternalChild = Drawable;
} }
else else
ClearInternal(); ClearInternal();

View File

@ -0,0 +1,40 @@
// 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.Graphics.Sprites;
namespace osu.Game.Skinning
{
public class SkinnableSpriteText : SkinnableDrawable<SpriteText>, IHasText
{
public SkinnableSpriteText(string name, Func<string, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
: base(name, defaultImplementation, allowFallback, restrictSize)
{
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (Drawable is IHasText textDrawable)
textDrawable.Text = Text;
}
private string text;
public string Text
{
get => text;
set
{
if (text == value)
return;
text = value;
if (Drawable is IHasText textDrawable)
textDrawable.Text = value;
}
}
}
}

View File

@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.4" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="ppy.osu.Framework" Version="2018.1018.0" /> <PackageReference Include="ppy.osu.Framework" Version="2018.1030.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />