1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 23:27:25 +08:00

Merge pull request #24954 from peppy/mod-icon-extended

Add the ability for mod icons to show extended information
This commit is contained in:
Bartłomiej Dach 2023-09-28 17:40:49 +02:00 committed by GitHub
commit c0114e6cb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 205 additions and 54 deletions

View File

@ -1,10 +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.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;
@ -25,6 +29,53 @@ namespace osu.Game.Tests.Visual.UserInterface
ChildrenEnumerable = Ruleset.Value.CreateInstance().CreateAllMods().Select(m => new ModIcon(m)),
};
});
AddStep("toggle selected", () =>
{
foreach (var icon in this.ChildrenOfType<ModIcon>())
icon.Selected.Toggle();
});
}
[Test]
public void TestShowRateAdjusts()
{
AddStep("create mod icons", () =>
{
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Full,
ChildrenEnumerable = Ruleset.Value.CreateInstance().CreateAllMods()
.OfType<ModRateAdjust>()
.SelectMany(m =>
{
List<ModIcon> icons = new List<ModIcon> { new ModIcon(m) };
for (double i = m.SpeedChange.MinValue; i < m.SpeedChange.MaxValue; i += m.SpeedChange.Precision * 10)
{
m = (ModRateAdjust)m.DeepClone();
m.SpeedChange.Value = i;
icons.Add(new ModIcon(m));
}
return icons;
}),
};
});
AddStep("adjust rates", () =>
{
foreach (var icon in this.ChildrenOfType<ModIcon>())
{
if (icon.Mod is ModRateAdjust rateAdjust)
{
rateAdjust.SpeedChange.Value = RNG.NextDouble() > 0.9
? rateAdjust.SpeedChange.Default
: RNG.NextDouble(rateAdjust.SpeedChange.MinValue, rateAdjust.SpeedChange.MaxValue);
}
}
});
}
[Test]

View File

@ -89,6 +89,8 @@ namespace osu.Game.Graphics
public static IconUsage ModSpunOut => Get(0xe046);
public static IconUsage ModSuddenDeath => Get(0xe047);
public static IconUsage ModTarget => Get(0xe048);
public static IconUsage ModBg => Get(0xe04a);
// Use "Icons/BeatmapDetails/mod-icon" instead
// public static IconUsage ModBg => Get(0xe04a);
}
}

View File

@ -210,7 +210,7 @@ namespace osu.Game.Online.Leaderboards
Spacing = new Vector2(2f, 0f),
Children = new Drawable[]
{
new ModIcon(mod, showTooltip: false).With(icon =>
new ModIcon(mod, showTooltip: false, showExtendedInformation: false).With(icon =>
{
icon.Origin = Anchor.CentreLeft;
icon.Anchor = Anchor.CentreLeft;

View File

@ -19,6 +19,13 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
string Name { get; }
/// <summary>
/// Short important information to display on the mod icon. For example, a rate adjust mod's rate
/// or similarly important setting.
/// Use <see cref="string.Empty"/> if the icon should not display any additional info.
/// </summary>
string ExtendedIconInformation { get; }
/// <summary>
/// The user readable description of this mod.
/// </summary>

View File

@ -27,6 +27,9 @@ namespace osu.Game.Rulesets.Mods
public abstract string Acronym { get; }
[JsonIgnore]
public virtual string ExtendedIconInformation => string.Empty;
[JsonIgnore]
public virtual IconUsage? Icon => null;

View File

@ -28,5 +28,7 @@ namespace osu.Game.Rulesets.Mods
public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp), typeof(ModAdaptiveSpeed), typeof(ModRateAdjust) };
public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x";
public override string ExtendedIconInformation => SettingDescription;
}
}

View File

@ -1,22 +1,22 @@
// 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.
#nullable disable
using System;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osuTK;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Localisation;
using osuTK.Graphics;
namespace osu.Game.Rulesets.UI
{
@ -27,22 +27,27 @@ namespace osu.Game.Rulesets.UI
{
public readonly BindableBool Selected = new BindableBool();
private readonly SpriteIcon modIcon;
private readonly SpriteText modAcronym;
private readonly SpriteIcon background;
private SpriteIcon modIcon = null!;
private SpriteText modAcronym = null!;
private Sprite background = null!;
private const float size = 80;
public static readonly Vector2 MOD_ICON_SIZE = new Vector2(80);
public virtual LocalisableString TooltipText => showTooltip ? ((mod as Mod)?.IconTooltip ?? mod.Name) : null;
public virtual LocalisableString TooltipText => showTooltip ? ((mod as Mod)?.IconTooltip ?? mod.Name) : string.Empty;
private IMod mod;
private readonly bool showTooltip;
private readonly bool showExtendedInformation;
public IMod Mod
{
get => mod;
set
{
if (mod == value)
return;
mod = value;
if (IsLoaded)
@ -51,49 +56,103 @@ namespace osu.Game.Rulesets.UI
}
[Resolved]
private OsuColour colours { get; set; }
private OsuColour colours { get; set; } = null!;
private Color4 backgroundColour;
private Sprite extendedBackground = null!;
private OsuSpriteText extendedText = null!;
private Container extendedContent = null!;
private ModSettingChangeTracker? modSettingsChangeTracker;
/// <summary>
/// Construct a new instance.
/// </summary>
/// <param name="mod">The mod to be displayed</param>
/// <param name="showTooltip">Whether a tooltip describing the mod should display on hover.</param>
public ModIcon(IMod mod, bool showTooltip = true)
/// <param name="showExtendedInformation">Whether to display a mod's extended information, if available.</param>
public ModIcon(IMod mod, bool showTooltip = true, bool showExtendedInformation = true)
{
// May expand due to expanded content, so autosize here.
AutoSizeAxes = Axes.X;
Height = MOD_ICON_SIZE.Y;
this.mod = mod ?? throw new ArgumentNullException(nameof(mod));
this.showTooltip = showTooltip;
this.showExtendedInformation = showExtendedInformation;
}
Size = new Vector2(size);
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Children = new Drawable[]
{
background = new SpriteIcon
extendedContent = new Container
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(size),
Icon = OsuIcon.ModBg,
Shadow = true,
Name = "extended content",
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(116, MOD_ICON_SIZE.Y),
X = MOD_ICON_SIZE.X - 22,
Children = new Drawable[]
{
extendedBackground = new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get("Icons/BeatmapDetails/mod-icon-extender"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
extendedText = new OsuSpriteText
{
Font = OsuFont.Default.With(size: 34f, weight: FontWeight.Bold),
UseFullGlyphHeight = false,
Text = mod.ExtendedIconInformation,
X = 6,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
}
},
modAcronym = new OsuSpriteText
new Container
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Colour = OsuColour.Gray(84),
Alpha = 0,
Font = OsuFont.Numeric.With(null, 22f),
UseFullGlyphHeight = false,
Text = mod.Acronym
},
modIcon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Colour = OsuColour.Gray(84),
Size = new Vector2(45),
Icon = FontAwesome.Solid.Question
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Name = "main content",
Size = MOD_ICON_SIZE,
Children = new Drawable[]
{
background = new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get("Icons/BeatmapDetails/mod-icon"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
modAcronym = new OsuSpriteText
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Colour = OsuColour.Gray(84),
Alpha = 0,
Font = OsuFont.Numeric.With(null, 22f),
UseFullGlyphHeight = false,
Text = mod.Acronym
},
modIcon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Colour = OsuColour.Gray(84),
Size = new Vector2(45),
Icon = FontAwesome.Solid.Question
},
}
},
};
}
@ -109,6 +168,14 @@ namespace osu.Game.Rulesets.UI
private void updateMod(IMod value)
{
modSettingsChangeTracker?.Dispose();
if (value is Mod actualMod)
{
modSettingsChangeTracker = new ModSettingChangeTracker(new[] { actualMod });
modSettingsChangeTracker.SettingChanged = _ => updateExtendedInformation();
}
modAcronym.Text = value.Acronym;
modIcon.Icon = value.Icon ?? FontAwesome.Solid.Question;
@ -125,11 +192,28 @@ namespace osu.Game.Rulesets.UI
backgroundColour = colours.ForModType(value.Type);
updateColour();
updateExtendedInformation();
}
private void updateExtendedInformation()
{
bool showExtended = showExtendedInformation && !string.IsNullOrEmpty(mod.ExtendedIconInformation);
extendedContent.Alpha = showExtended ? 1 : 0;
extendedText.Text = mod.ExtendedIconInformation;
}
private void updateColour()
{
background.Colour = Selected.Value ? backgroundColour.Lighten(0.2f) : backgroundColour;
extendedText.Colour = background.Colour = Selected.Value ? backgroundColour.Lighten(0.2f) : backgroundColour;
extendedBackground.Colour = Selected.Value ? backgroundColour.Darken(2.4f) : backgroundColour.Darken(2.8f);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
modSettingsChangeTracker?.Dispose();
}
}
}

View File

@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Overlays;
@ -23,8 +24,8 @@ namespace osu.Game.Rulesets.UI
private readonly IMod mod;
private readonly SpriteIcon background;
private readonly SpriteIcon? modIcon;
private Drawable background = null!;
private SpriteIcon? modIcon;
private Color4 activeForegroundColour;
private Color4 inactiveForegroundColour;
@ -36,19 +37,24 @@ namespace osu.Game.Rulesets.UI
{
this.mod = mod;
AutoSizeAxes = Axes.Both;
Size = new Vector2(DEFAULT_SIZE);
}
[BackgroundDependencyLoader]
private void load(TextureStore textures, OsuColour colours, OverlayColourProvider? colourProvider)
{
FillFlowContainer contentFlow;
ModSwitchTiny tinySwitch;
InternalChildren = new Drawable[]
InternalChildren = new[]
{
background = new SpriteIcon
background = new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get("Icons/BeatmapDetails/mod-icon"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(DEFAULT_SIZE),
Icon = OsuIcon.ModBg
},
contentFlow = new FillFlowContainer
{
@ -78,11 +84,7 @@ namespace osu.Game.Rulesets.UI
});
tinySwitch.Scale = new Vector2(0.3f);
}
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, OverlayColourProvider? colourProvider)
{
inactiveForegroundColour = colourProvider?.Background5 ?? colours.Gray3;
activeForegroundColour = colours.ForModType(mod.Type);

View File

@ -247,7 +247,7 @@ namespace osu.Game.Screens.Play
contentIn();
MetadataInfo.Delay(750).FadeIn(500);
MetadataInfo.Delay(750).FadeIn(500, Easing.OutQuint);
// after an initial delay, start the debounced load check.
// this will continue to execute even after resuming back on restart.
@ -420,7 +420,7 @@ namespace osu.Game.Screens.Play
{
MetadataInfo.Loading = true;
content.FadeInFromZero(400);
content.FadeInFromZero(500, Easing.OutQuint);
content.ScaleTo(1, 650, Easing.OutQuint).Then().Schedule(prepareNewPlayer);
settingsScroll.FadeInFromZero(500, Easing.Out)

View File

@ -37,7 +37,7 @@
</PackageReference>
<PackageReference Include="Realm" Version="11.5.0" />
<PackageReference Include="ppy.osu.Framework" Version="2023.922.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.914.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.928.0" />
<PackageReference Include="Sentry" Version="3.39.1" />
<PackageReference Include="SharpCompress" Version="0.33.0" />
<PackageReference Include="NUnit" Version="3.13.3" />