mirror of
https://github.com/ppy/osu.git
synced 2025-01-30 23:03:20 +08: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
|
// Covers legacy rank display
|
||||||
"Archives/modified-classic-20230809.osk",
|
"Archives/modified-classic-20230809.osk",
|
||||||
// Covers legacy key counter
|
// Covers legacy key counter
|
||||||
"Archives/modified-classic-20240724.osk"
|
"Archives/modified-classic-20240724.osk",
|
||||||
|
// Covers skinnable mod display
|
||||||
|
"Archives/modified-default-20241207.osk",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <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 IMod mod;
|
||||||
|
|
||||||
private readonly bool showTooltip;
|
private readonly bool showTooltip;
|
||||||
private readonly bool showExtendedInformation;
|
|
||||||
|
private bool showExtendedInformation;
|
||||||
|
|
||||||
|
public bool ShowExtendedInformation
|
||||||
|
{
|
||||||
|
get => showExtendedInformation;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
showExtendedInformation = value;
|
||||||
|
updateExtendedInformation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IMod Mod
|
public IMod Mod
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,9 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Localisation.SkinComponents;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -20,9 +22,24 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ModDisplay : CompositeDrawable, IHasCurrentValue<IReadOnlyList<Mod>>
|
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>());
|
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;
|
private readonly FillFlowContainer<ModIcon> iconsContainer;
|
||||||
|
|
||||||
public ModDisplay(bool showExtendedInformation = true)
|
public ModDisplay(bool showExtendedInformation = true)
|
||||||
@ -58,11 +87,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Current.BindValueChanged(updateDisplay, true);
|
Current.BindValueChanged(updateDisplay, true);
|
||||||
|
updateExpansionMode(0);
|
||||||
iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
|
|
||||||
|
|
||||||
if (ExpansionMode == ExpansionMode.AlwaysExpanded || ExpansionMode == ExpansionMode.AlwaysContracted)
|
|
||||||
FinishTransforms(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDisplay(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
private void updateDisplay(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||||
@ -70,29 +95,40 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
iconsContainer.Clear();
|
iconsContainer.Clear();
|
||||||
|
|
||||||
foreach (Mod mod in mods.NewValue.AsOrdered())
|
foreach (Mod mod in mods.NewValue.AsOrdered())
|
||||||
iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(0.6f) });
|
iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(MOD_ICON_SCALE) });
|
||||||
|
|
||||||
appearTransform();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appearTransform()
|
private void updateExpansionMode(double duration = 500)
|
||||||
{
|
{
|
||||||
expand();
|
switch (expansionMode)
|
||||||
|
{
|
||||||
|
case ExpansionMode.AlwaysExpanded:
|
||||||
|
expand(duration);
|
||||||
|
break;
|
||||||
|
|
||||||
using (iconsContainer.BeginDelayedSequence(1200))
|
case ExpansionMode.AlwaysContracted:
|
||||||
contract();
|
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)
|
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)
|
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)
|
protected override bool OnHover(HoverEvent e)
|
||||||
@ -113,16 +149,19 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="ModDisplay"/> will expand only when hovered.
|
/// The <see cref="ModDisplay"/> will expand only when hovered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.ExpandOnHover))]
|
||||||
ExpandOnHover,
|
ExpandOnHover,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="ModDisplay"/> will always be expanded.
|
/// The <see cref="ModDisplay"/> will always be expanded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.AlwaysExpanded))]
|
||||||
AlwaysExpanded,
|
AlwaysExpanded,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="ModDisplay"/> will always be contracted.
|
/// The <see cref="ModDisplay"/> will always be contracted.
|
||||||
/// </summary>
|
/// </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 readonly BindableBool replayLoaded = new BindableBool();
|
||||||
|
|
||||||
private static bool hasShownNotificationOnce;
|
private static bool hasShownNotificationOnce;
|
||||||
|
|
||||||
private readonly FillFlowContainer bottomRightElements;
|
private readonly FillFlowContainer bottomRightElements;
|
||||||
|
|
||||||
internal readonly FillFlowContainer TopRightElements;
|
internal readonly FillFlowContainer TopRightElements;
|
||||||
@ -238,7 +237,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
if (e.NewValue)
|
if (e.NewValue)
|
||||||
{
|
{
|
||||||
ModDisplay.FadeIn(200);
|
ModDisplay.FadeIn(1000, FADE_EASING);
|
||||||
InputCountController.Margin = new MarginPadding(10) { Bottom = 30 };
|
InputCountController.Margin = new MarginPadding(10) { Bottom = 30 };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -249,6 +248,9 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
ModDisplay.ExpansionMode = ExpansionMode.AlwaysExpanded;
|
||||||
|
Scheduler.AddDelayed(() => ModDisplay.ExpansionMode = ExpansionMode.ExpandOnHover, 1200);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
Loading…
Reference in New Issue
Block a user