1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-16 18:32:54 +08:00
osu-lazer/osu.Game/Screens/Play/HUD/ModDisplay.cs
Dean Herbert 7d74d84e6c Display mod icons using stable ordering
In discussion with nanaya, we likely want this now that we're adding the
ability for some icons to be extended. Historically mod icons have been
displayed in a stable but arbitrary order (based on their position in
the `Mods` enum).

This change aims to make them stable across lazer scores (where they are
stored based on .. the order the user selected them).
2023-09-28 16:48:09 +09:00

123 lines
3.5 KiB
C#

// 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;
using System.Collections.Generic;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osuTK;
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 ModDisplay : CompositeDrawable, IHasCurrentValue<IReadOnlyList<Mod>>
{
private const int fade_duration = 1000;
public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover;
private readonly BindableWithCurrent<IReadOnlyList<Mod>> current = new BindableWithCurrent<IReadOnlyList<Mod>>(Array.Empty<Mod>());
public Bindable<IReadOnlyList<Mod>> Current
{
get => current.Current;
set
{
ArgumentNullException.ThrowIfNull(value);
current.Current = value;
}
}
private readonly FillFlowContainer<ModIcon> iconsContainer;
public ModDisplay()
{
AutoSizeAxes = Axes.Both;
InternalChild = iconsContainer = new ReverseChildIDFillFlowContainer<ModIcon>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(updateDisplay, true);
iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
}
private void updateDisplay(ValueChangedEvent<IReadOnlyList<Mod>> mods)
{
iconsContainer.Clear();
foreach (Mod mod in mods.NewValue.AsOrdered())
iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) });
appearTransform();
}
private void appearTransform()
{
expand();
using (iconsContainer.BeginDelayedSequence(1200))
contract();
}
private void expand()
{
if (ExpansionMode != ExpansionMode.AlwaysContracted)
iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
}
private void contract()
{
if (ExpansionMode != ExpansionMode.AlwaysExpanded)
iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
}
protected override bool OnHover(HoverEvent e)
{
expand();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
contract();
base.OnHoverLost(e);
}
}
public enum ExpansionMode
{
/// <summary>
/// The <see cref="ModDisplay"/> will expand only when hovered.
/// </summary>
ExpandOnHover,
/// <summary>
/// The <see cref="ModDisplay"/> will always be expanded.
/// </summary>
AlwaysExpanded,
/// <summary>
/// The <see cref="ModDisplay"/> will always be contracted.
/// </summary>
AlwaysContracted
}
}