From 5da21d81e449040db47f02efe3e1fa0f7b4ac673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 7 Dec 2024 11:11:43 +0900 Subject: [PATCH] Implement skinnable mod display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also makes the mod display initialization sequence (start expanded, then unexpand) controlled by HUDOverlay rather than mod display itself. This enabled different treatment depending on whether the mod display is viewed in the skin editor or in the player. Co-authored-by: Bartłomiej Dach --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 6 ++ osu.Game/Rulesets/UI/ModIcon.cs | 13 ++- osu.Game/Screens/Play/HUD/ModDisplay.cs | 80 ++++++++++++++----- .../Screens/Play/HUD/SkinnableModDisplay.cs | 55 +++++++++++++ osu.Game/Screens/Play/HUDOverlay.cs | 11 ++- 5 files changed, 141 insertions(+), 24 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/SkinnableModDisplay.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 91188f5bacd8..49a8a65cd00e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -20,6 +20,7 @@ using osu.Game.Overlays.SkinEditor; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Edit; using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD.HitErrorMeters; @@ -53,6 +54,11 @@ public override void SetUpSteps() { base.SetUpSteps(); + AddStep("Add DT and HD", () => + { + LoadPlayer([new OsuModDoubleTime { SpeedChange = { Value = 1.337 } }, new OsuModHidden()]); + }); + AddStep("reset skin", () => skins.CurrentSkinInfo.SetDefault()); AddUntilStep("wait for hud load", () => targetContainer.ComponentsLoaded); diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 5237425075ac..6abc7355d521 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -39,7 +39,18 @@ public partial class ModIcon : Container, IHasTooltip 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 { diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index b37d41e7a20a..4e3dcd3c641e 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -20,9 +20,30 @@ namespace osu.Game.Screens.Play.HUD /// public partial class ModDisplay : CompositeDrawable, IHasCurrentValue> { - private const int fade_duration = 1000; + private ExpansionMode expansionMode = ExpansionMode.ExpandOnHover; + public ExpansionMode ExpansionMode + { + get + { + return expansionMode; + } - public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; + set + { + if (expansionMode == value) + return; + + expansionMode = value; + + if (IsLoaded) + { + if (expansionMode == ExpansionMode.AlwaysExpanded || (expansionMode == ExpansionMode.ExpandOnHover && IsHovered)) + expand(); + else if (expansionMode == ExpansionMode.AlwaysContracted || (expansionMode == ExpansionMode.ExpandOnHover && !IsHovered)) + contract(); + } + } + } private readonly BindableWithCurrent> current = new BindableWithCurrent>(Array.Empty()); @@ -37,7 +58,19 @@ public Bindable> Current } } - 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 iconsContainer; public ModDisplay(bool showExtendedInformation = true) @@ -59,10 +92,23 @@ protected override void LoadComplete() Current.BindValueChanged(updateDisplay, true); - iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint); - - if (ExpansionMode == ExpansionMode.AlwaysExpanded || ExpansionMode == ExpansionMode.AlwaysContracted) - FinishTransforms(true); + switch (expansionMode) + { + case ExpansionMode.AlwaysExpanded: + expand(0); + break; + + case ExpansionMode.AlwaysContracted: + contract(0); + break; + + case ExpansionMode.ExpandOnHover: + if (IsHovered) + expand(0); + else + contract(0); + break; + } } private void updateDisplay(ValueChangedEvent> mods) @@ -71,28 +117,18 @@ private void updateDisplay(ValueChangedEvent> mods) foreach (Mod mod in mods.NewValue.AsOrdered()) iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(0.6f) }); - - appearTransform(); - } - - private void appearTransform() - { - expand(); - - using (iconsContainer.BeginDelayedSequence(1200)) - contract(); } - 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) @@ -123,6 +159,6 @@ public enum ExpansionMode /// /// The will always be contracted. /// - AlwaysContracted + AlwaysContracted, } } diff --git a/osu.Game/Screens/Play/HUD/SkinnableModDisplay.cs b/osu.Game/Screens/Play/HUD/SkinnableModDisplay.cs new file mode 100644 index 000000000000..dc5da5480a7c --- /dev/null +++ b/osu.Game/Screens/Play/HUD/SkinnableModDisplay.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . 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.Rulesets.Mods; +using osu.Game.Skinning; + +namespace osu.Game.Screens.Play.HUD +{ + /// + /// Displays a single-line horizontal auto-sized flow of mods. For cases where wrapping is required, use instead. + /// + public partial class SkinnableModDisplay : CompositeDrawable, ISerialisableDrawable + { + private ModDisplay modDisplay = null!; + + [Resolved] + private Bindable> mods { get; set; } = null!; + + [SettingSource("Show extended info", "Whether to show extended information for each mod.")] + public Bindable ShowExtendedInformation { get; } = new Bindable(true); + + [SettingSource("Expansion mode", "How the mod display expands when interacted with.")] + public Bindable ExpansionModeSetting { get; } = new Bindable(ExpansionMode.ExpandOnHover); + + public SkinnableModDisplay() + { + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = 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; } + } +} diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index fca871e42f83..5d92fee84108 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -35,6 +35,8 @@ public partial class HUDOverlay : Container, IKeyBindingHandler { public const float FADE_DURATION = 300; + private const float mods_fade_duration = 1000; + public const Easing FADE_EASING = Easing.OutQuint; /// @@ -85,7 +87,6 @@ protected override bool ShouldBeConsideredForInput(Drawable child) private readonly BindableBool replayLoaded = new BindableBool(); private static bool hasShownNotificationOnce; - private readonly FillFlowContainer bottomRightElements; private readonly FillFlowContainer topRightElements; @@ -248,6 +249,14 @@ protected override void LoadComplete() updateVisibility(); }, true); + + ModDisplay.ExpansionMode = ExpansionMode.AlwaysExpanded; + Scheduler.AddDelayed(() => + { + ModDisplay.ExpansionMode = ExpansionMode.ExpandOnHover; + }, 1200); + + ModDisplay.FadeInFromZero(mods_fade_duration, FADE_EASING); } protected override void Update()