mirror of
https://github.com/ppy/osu
synced 2025-01-03 12:52:10 +00:00
Merge pull request #30993 from Tom94/skinnable-mod-display
Implement skinnable mod display
This commit is contained in:
commit
cf987bc027
BIN
osu.Game.Tests/Resources/Archives/modified-default-20241207.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-default-20241207.osk
Normal file
Binary file not shown.
@ -68,7 +68,9 @@ namespace osu.Game.Tests.Skins
|
||||
// Covers legacy rank display
|
||||
"Archives/modified-classic-20230809.osk",
|
||||
// Covers legacy key counter
|
||||
"Archives/modified-classic-20240724.osk"
|
||||
"Archives/modified-classic-20240724.osk",
|
||||
// Covers skinnable mod display
|
||||
"Archives/modified-default-20241207.osk",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -0,0 +1,49 @@
|
||||
// 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.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.SkinComponents
|
||||
{
|
||||
public static class SkinnableModDisplayStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.SkinnableModDisplay";
|
||||
|
||||
/// <summary>
|
||||
/// "Show extended information"
|
||||
/// </summary>
|
||||
public static LocalisableString ShowExtendedInformation => new TranslatableString(getKey(@"show_extended_information"), @"Show extended information");
|
||||
|
||||
/// <summary>
|
||||
/// "Whether to show extended information for each mod."
|
||||
/// </summary>
|
||||
public static LocalisableString ShowExtendedInformationDescription => new TranslatableString(getKey(@"whether_to_show_extended_information"), @"Whether to show extended information for each mod.");
|
||||
|
||||
/// <summary>
|
||||
/// "Expansion mode"
|
||||
/// </summary>
|
||||
public static LocalisableString ExpansionMode => new TranslatableString(getKey(@"expansion_mode"), @"Expansion mode");
|
||||
|
||||
/// <summary>
|
||||
/// "How the mod display expands when interacted with."
|
||||
/// </summary>
|
||||
public static LocalisableString ExpansionModeDescription => new TranslatableString(getKey(@"how_the_mod_display_expands"), @"How the mod display expands when interacted with.");
|
||||
|
||||
/// <summary>
|
||||
/// "Expand on hover"
|
||||
/// </summary>
|
||||
public static LocalisableString ExpandOnHover => new TranslatableString(getKey(@"expand_on_hover"), @"Expand on hover");
|
||||
|
||||
/// <summary>
|
||||
/// "Always contracted"
|
||||
/// </summary>
|
||||
public static LocalisableString AlwaysContracted => new TranslatableString(getKey(@"always_contracted"), @"Always contracted");
|
||||
|
||||
/// <summary>
|
||||
/// "Always expanded"
|
||||
/// </summary>
|
||||
public static LocalisableString AlwaysExpanded => new TranslatableString(getKey(@"always_expanded"), @"Always expanded");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -39,7 +39,18 @@ namespace osu.Game.Rulesets.UI
|
||||
private IMod mod;
|
||||
|
||||
private readonly bool showTooltip;
|
||||
private readonly bool showExtendedInformation;
|
||||
|
||||
private bool showExtendedInformation;
|
||||
|
||||
public bool ShowExtendedInformation
|
||||
{
|
||||
get => showExtendedInformation;
|
||||
set
|
||||
{
|
||||
showExtendedInformation = value;
|
||||
updateExtendedInformation();
|
||||
}
|
||||
}
|
||||
|
||||
public IMod Mod
|
||||
{
|
||||
|
@ -8,7 +8,9 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Localisation.SkinComponents;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
@ -20,9 +22,24 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// </summary>
|
||||
public partial class ModDisplay : CompositeDrawable, IHasCurrentValue<IReadOnlyList<Mod>>
|
||||
{
|
||||
private const int fade_duration = 1000;
|
||||
public const float MOD_ICON_SCALE = 0.6f;
|
||||
|
||||
public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover;
|
||||
private ExpansionMode expansionMode = ExpansionMode.ExpandOnHover;
|
||||
|
||||
public ExpansionMode ExpansionMode
|
||||
{
|
||||
get => expansionMode;
|
||||
set
|
||||
{
|
||||
if (expansionMode == value)
|
||||
return;
|
||||
|
||||
expansionMode = value;
|
||||
|
||||
if (IsLoaded)
|
||||
updateExpansionMode();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly BindableWithCurrent<IReadOnlyList<Mod>> current = new BindableWithCurrent<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
@ -37,7 +54,19 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}
|
||||
}
|
||||
|
||||
private readonly bool showExtendedInformation;
|
||||
private bool showExtendedInformation;
|
||||
|
||||
public bool ShowExtendedInformation
|
||||
{
|
||||
get => showExtendedInformation;
|
||||
set
|
||||
{
|
||||
showExtendedInformation = value;
|
||||
foreach (var icon in iconsContainer)
|
||||
icon.ShowExtendedInformation = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly FillFlowContainer<ModIcon> iconsContainer;
|
||||
|
||||
public ModDisplay(bool showExtendedInformation = true)
|
||||
@ -58,11 +87,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindValueChanged(updateDisplay, true);
|
||||
|
||||
iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
|
||||
|
||||
if (ExpansionMode == ExpansionMode.AlwaysExpanded || ExpansionMode == ExpansionMode.AlwaysContracted)
|
||||
FinishTransforms(true);
|
||||
updateExpansionMode(0);
|
||||
}
|
||||
|
||||
private void updateDisplay(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||
@ -70,29 +95,40 @@ namespace osu.Game.Screens.Play.HUD
|
||||
iconsContainer.Clear();
|
||||
|
||||
foreach (Mod mod in mods.NewValue.AsOrdered())
|
||||
iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(0.6f) });
|
||||
|
||||
appearTransform();
|
||||
iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(MOD_ICON_SCALE) });
|
||||
}
|
||||
|
||||
private void appearTransform()
|
||||
private void updateExpansionMode(double duration = 500)
|
||||
{
|
||||
expand();
|
||||
switch (expansionMode)
|
||||
{
|
||||
case ExpansionMode.AlwaysExpanded:
|
||||
expand(duration);
|
||||
break;
|
||||
|
||||
using (iconsContainer.BeginDelayedSequence(1200))
|
||||
contract();
|
||||
case ExpansionMode.AlwaysContracted:
|
||||
contract(duration);
|
||||
break;
|
||||
|
||||
case ExpansionMode.ExpandOnHover:
|
||||
if (IsHovered)
|
||||
expand(duration);
|
||||
else
|
||||
contract(duration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void expand()
|
||||
private void expand(double duration = 500)
|
||||
{
|
||||
if (ExpansionMode != ExpansionMode.AlwaysContracted)
|
||||
iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
|
||||
iconsContainer.TransformSpacingTo(new Vector2(5, 0), duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void contract()
|
||||
private void contract(double duration = 500)
|
||||
{
|
||||
if (ExpansionMode != ExpansionMode.AlwaysExpanded)
|
||||
iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
|
||||
iconsContainer.TransformSpacingTo(new Vector2(-25, 0), duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
@ -113,16 +149,19 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// <summary>
|
||||
/// The <see cref="ModDisplay"/> will expand only when hovered.
|
||||
/// </summary>
|
||||
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.ExpandOnHover))]
|
||||
ExpandOnHover,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ModDisplay"/> will always be expanded.
|
||||
/// </summary>
|
||||
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.AlwaysExpanded))]
|
||||
AlwaysExpanded,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ModDisplay"/> will always be contracted.
|
||||
/// </summary>
|
||||
AlwaysContracted
|
||||
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.AlwaysContracted))]
|
||||
AlwaysContracted,
|
||||
}
|
||||
}
|
||||
|
59
osu.Game/Screens/Play/HUD/SkinnableModDisplay.cs
Normal file
59
osu.Game/Screens/Play/HUD/SkinnableModDisplay.cs
Normal file
@ -0,0 +1,59 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Localisation.SkinComponents;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays a single-line horizontal auto-sized flow of mods. For cases where wrapping is required, use <see cref="ModFlowDisplay"/> instead.
|
||||
/// </summary>
|
||||
public partial class SkinnableModDisplay : CompositeDrawable, ISerialisableDrawable
|
||||
{
|
||||
private ModDisplay modDisplay = null!;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
||||
|
||||
[SettingSource(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.ShowExtendedInformation), nameof(SkinnableModDisplayStrings.ShowExtendedInformationDescription))]
|
||||
public Bindable<bool> ShowExtendedInformation { get; } = new Bindable<bool>(true);
|
||||
|
||||
[SettingSource(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.ExpansionMode), nameof(SkinnableModDisplayStrings.ExpansionModeDescription))]
|
||||
public Bindable<ExpansionMode> ExpansionModeSetting { get; } = new Bindable<ExpansionMode>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
// Provide a minimum autosize.
|
||||
new Container { Size = ModIcon.MOD_ICON_SIZE * ModDisplay.MOD_ICON_SCALE },
|
||||
modDisplay = new ModDisplay(),
|
||||
};
|
||||
|
||||
modDisplay.Current = mods;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ShowExtendedInformation.BindValueChanged(_ => modDisplay.ShowExtendedInformation = ShowExtendedInformation.Value, true);
|
||||
ExpansionModeSetting.BindValueChanged(_ => modDisplay.ExpansionMode = ExpansionModeSetting.Value, true);
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
}
|
||||
}
|
@ -85,7 +85,6 @@ namespace osu.Game.Screens.Play
|
||||
private readonly BindableBool replayLoaded = new BindableBool();
|
||||
|
||||
private static bool hasShownNotificationOnce;
|
||||
|
||||
private readonly FillFlowContainer bottomRightElements;
|
||||
|
||||
internal readonly FillFlowContainer TopRightElements;
|
||||
@ -238,7 +237,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
ModDisplay.FadeIn(200);
|
||||
ModDisplay.FadeIn(1000, FADE_EASING);
|
||||
InputCountController.Margin = new MarginPadding(10) { Bottom = 30 };
|
||||
}
|
||||
else
|
||||
@ -249,6 +248,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
updateVisibility();
|
||||
}, true);
|
||||
|
||||
ModDisplay.ExpansionMode = ExpansionMode.AlwaysExpanded;
|
||||
Scheduler.AddDelayed(() => ModDisplay.ExpansionMode = ExpansionMode.ExpandOnHover, 1200);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
Loading…
Reference in New Issue
Block a user