From c2a44cf1185dd28897f5488829124d7533cfb337 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 23 Nov 2023 23:30:18 +0200 Subject: [PATCH] Made custom tooltip --- .../Mods/AdjustedAttributesTooltip.cs | 158 ++++++++++++++++++ .../Overlays/Mods/BeatmapAttributesDisplay.cs | 46 ++--- .../Screens/Select/Details/AdvancedStats.cs | 38 ++--- 3 files changed, 188 insertions(+), 54 deletions(-) create mode 100644 osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs new file mode 100644 index 0000000000..e6a554d1fb --- /dev/null +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -0,0 +1,158 @@ +// 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.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Framework.Utils; +using osuTK; + +namespace osu.Game.Overlays.Mods +{ + public partial class AdjustedAttributesTooltip : CompositeDrawable, ITooltip + { + private Dictionary> attributes = new Dictionary>(); + + private Container content; + + private FillFlowContainer attributesFillFlow; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + + public AdjustedAttributesTooltip() + { + // Need to be initialized in constructor to ensure accessability in AddAttribute function + InternalChild = content = new Container + { + AutoSizeAxes = Axes.Both + }; + attributesFillFlow = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both + }; + } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + Masking = true; + CornerRadius = 15; + + content.AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.8f + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding {Vertical = 10, Horizontal = 15}, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "One or more values are being adjusted by mods that change speed.", + }, + attributesFillFlow + } + } + }); + } + + private void checkAttributes() + { + foreach (var attribute in attributes) + { + if (!Precision.AlmostEquals(attribute.Value.Value.Old, attribute.Value.Value.New)) + { + content.Show(); + return; + } + } + content.Hide(); + } + + public void AddAttribute(string name) + { + Bindable newBindable = new Bindable(); + newBindable.BindValueChanged(_ => checkAttributes()); + attributes.Add(name, newBindable); + attributesFillFlow.Add(new AttributeDisplay(name, newBindable.GetBoundCopy())); + } + public void UpdateAttribute(string name, double oldValue, double newValue) + { + Bindable attribute = attributes[name]; + + OldNewPair attributeValue = attribute.Value; + attributeValue.Old = oldValue; + attributeValue.New = newValue; + + attribute.Value = attributeValue; + } + + protected override void Update() + { + } + public void SetContent(object content) + { + } + + public void Move(Vector2 pos) + { + Position = pos; + } + + private struct OldNewPair + { + public double Old, New; + } + + private partial class AttributeDisplay : CompositeDrawable + { + public Bindable AttributeValues = new Bindable(); + public string AttributeName; + + private OsuSpriteText text = new OsuSpriteText + { + Font = OsuFont.Default.With(weight: FontWeight.Bold) + }; + public AttributeDisplay(string name, Bindable boundCopy) + { + AutoSizeAxes = Axes.Both; + + AttributeName = name; + AttributeValues = boundCopy; + InternalChild = text; + AttributeValues.BindValueChanged(_ => update(), true); + } + + private void update() + { + if (Precision.AlmostEquals(AttributeValues.Value.Old, AttributeValues.Value.New)) + { + Hide(); + } + else + { + Show(); + text.Text = $"{AttributeName}: {(AttributeValues.Value.Old):0.0#} → {(AttributeValues.Value.New):0.0#}"; + } + } + } + } +} diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 6270b2bb49..fa8f9cb7a4 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Configuration; @@ -28,7 +27,7 @@ namespace osu.Game.Overlays.Mods /// On the mod select overlay, this provides a local updating view of BPM, star rating and other /// difficulty attributes so the user can have a better insight into what mods are changing. /// - public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay, IHasTooltip + public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay, IHasCustomTooltip { private StarRatingDisplay starRatingDisplay = null!; private BPMDisplay bpmDisplay = null!; @@ -58,10 +57,11 @@ namespace osu.Game.Overlays.Mods private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; - private BeatmapDifficulty? originalDifficulty; - private BeatmapDifficulty? adjustedDifficulty; + private AdjustedAttributesTooltip rateAdjustTooltip = null!; + + public ITooltip GetCustomTooltip() => rateAdjustTooltip; + public object TooltipContent => this; - private bool haveRateChangedValues; private const float transition_duration = 250; @@ -70,6 +70,8 @@ namespace osu.Game.Overlays.Mods { const float shear = ShearedOverlayContainer.SHEAR; + rateAdjustTooltip = new AdjustedAttributesTooltip(); + LeftContent.AddRange(new Drawable[] { starRatingDisplay = new StarRatingDisplay(default, animated: true) @@ -105,7 +107,6 @@ namespace osu.Game.Overlays.Mods mods.BindValueChanged(_ => { modSettingChangeTracker?.Dispose(); - modSettingChangeTracker = new ModSettingChangeTracker(mods.Value); modSettingChangeTracker.SettingChanged += _ => updateValues(); updateValues(); @@ -125,6 +126,9 @@ namespace osu.Game.Overlays.Mods BeatmapInfo.BindValueChanged(_ => updateValues(), true); + rateAdjustTooltip.AddAttribute("AR"); + rateAdjustTooltip.AddAttribute("OD"); + updateCollapsedState(); } @@ -173,15 +177,16 @@ namespace osu.Game.Overlays.Mods bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate; - originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); + BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(originalDifficulty); Ruleset ruleset = gameRuleset.Value.CreateInstance(); - adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); + BeatmapDifficulty adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); + rateAdjustTooltip.UpdateAttribute("AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); + rateAdjustTooltip.UpdateAttribute("OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); @@ -197,29 +202,6 @@ namespace osu.Game.Overlays.Mods RightContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint); } - public LocalisableString TooltipText - { - get - { - if (haveRateChangedValues) - { - return $"One or more values are being adjusted by mods that change speed." + - $" (AR {originalDifficulty?.ApproachRate ?? 0}→{(adjustedDifficulty?.ApproachRate ?? 0):0.0#}, " + - $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{(adjustedDifficulty?.OverallDifficulty ?? 0):0.0#})"; - } - - return string.Empty; - } - } - - private static bool hasRateAdjustedProperties(BeatmapDifficulty a, BeatmapDifficulty b) - { - if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate)) return true; - if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty)) return true; - - return false; - } - private partial class BPMDisplay : RollingCounter { protected override double RollingDuration => 500; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f617cb1d8e..d3a6a88c68 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -26,10 +26,11 @@ using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; +using osu.Game.Overlays.Mods; namespace osu.Game.Screens.Select.Details { - public partial class AdvancedStats : Container, IHasTooltip + public partial class AdvancedStats : Container, IHasCustomTooltip { [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -45,9 +46,9 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; - private BeatmapDifficulty originalDifficulty; - private BeatmapDifficulty adjustedDifficulty; - private bool haveRateChangedValues; + private AdjustedAttributesTooltip rateAdjustTooltip; + public ITooltip GetCustomTooltip() => rateAdjustTooltip; + public object TooltipContent => this; private IBeatmapInfo beatmapInfo; @@ -85,6 +86,7 @@ namespace osu.Game.Screens.Select.Details private void load(OsuColour colours) { starDifficulty.AccentColour = colours.Yellow; + rateAdjustTooltip = new AdjustedAttributesTooltip(); } protected override void LoadComplete() @@ -99,6 +101,9 @@ namespace osu.Game.Screens.Select.Details gameRuleset.BindValueChanged(_ => updateStatistics()); mods.BindValueChanged(modsChanged, true); + + rateAdjustTooltip.AddAttribute("AR"); + rateAdjustTooltip.AddAttribute("OD"); } private ModSettingChangeTracker modSettingChangeTracker; @@ -121,14 +126,17 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; + BeatmapDifficulty adjustedDifficulty = null; if (baseDifficulty != null) { - originalDifficulty = new BeatmapDifficulty(baseDifficulty); + BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(originalDifficulty); + adjustedDifficulty = originalDifficulty; + if (gameRuleset != null) { Ruleset ruleset = gameRuleset.Value.CreateInstance(); @@ -138,7 +146,9 @@ namespace osu.Game.Screens.Select.Details rate = mod.ApplyToRate(0, rate); adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); + + rateAdjustTooltip.UpdateAttribute("AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); + rateAdjustTooltip.UpdateAttribute("OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); } } @@ -204,22 +214,6 @@ namespace osu.Game.Screens.Select.Details starDifficultyCancellationSource?.Cancel(); } - public LocalisableString TooltipText - { - get - { - if (haveRateChangedValues) - { - // Rather than localising this, it should be displayed in a better way (a custom tooltip which isn't a single super-long line). - return "One or more values are being adjusted by mods that change speed." + - $" (AR {originalDifficulty?.ApproachRate ?? 0}→{(adjustedDifficulty?.ApproachRate ?? 0):0.0#}, " + - $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{(adjustedDifficulty?.OverallDifficulty ?? 0):0.0#})"; - } - - return string.Empty; - } - } - private static bool hasRateAdjustedProperties(BeatmapDifficulty a, BeatmapDifficulty b) { if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate)) return true;