mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 08:52:55 +08:00
Implement transient stats display on user toolbar button
This commit is contained in:
parent
21b9fb95e2
commit
14052ae1cc
@ -2,12 +2,17 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Overlays.Toolbar;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -87,5 +92,91 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
AddStep($"Change state to {state}", () => dummyAPI.SetState(state));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTransientUserStatisticsDisplay()
|
||||
{
|
||||
AddStep("Log in", () => dummyAPI.Login("wang", "jang"));
|
||||
AddStep("Gain", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 123_456,
|
||||
PP = 1234
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 111_111,
|
||||
PP = 1357
|
||||
});
|
||||
});
|
||||
AddStep("Loss", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 111_111,
|
||||
PP = 1357
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 123_456,
|
||||
PP = 1234
|
||||
});
|
||||
});
|
||||
AddStep("No change", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 111_111,
|
||||
PP = 1357
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 111_111,
|
||||
PP = 1357
|
||||
});
|
||||
});
|
||||
AddStep("Was null", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = null,
|
||||
PP = null
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 111_111,
|
||||
PP = 1357
|
||||
});
|
||||
});
|
||||
AddStep("Became null", () =>
|
||||
{
|
||||
var transientUpdateDisplay = this.ChildrenOfType<TransientUserStatisticsUpdateDisplay>().Single();
|
||||
transientUpdateDisplay.LatestUpdate.Value = new SoloStatisticsUpdate(
|
||||
new ScoreInfo(),
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 111_111,
|
||||
PP = 1357
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = null,
|
||||
PP = null
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,13 @@ namespace osu.Game.Overlays.Toolbar
|
||||
}
|
||||
});
|
||||
|
||||
Flow.Add(new TransientUserStatisticsUpdateDisplay
|
||||
{
|
||||
Alpha = 0
|
||||
});
|
||||
Flow.AutoSizeEasing = Easing.OutQuint;
|
||||
Flow.AutoSizeDuration = 250;
|
||||
|
||||
apiState = api.State.GetBoundCopy();
|
||||
apiState.BindValueChanged(onlineStateChanged, true);
|
||||
|
||||
|
@ -1,10 +1,221 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
public class TransientUserStatisticsUpdateDisplay
|
||||
public partial class TransientUserStatisticsUpdateDisplay : CompositeDrawable
|
||||
{
|
||||
|
||||
public Bindable<SoloStatisticsUpdate?> LatestUpdate { get; } = new Bindable<SoloStatisticsUpdate?>();
|
||||
|
||||
private Statistic<int> globalRank = null!;
|
||||
private Statistic<decimal> pp = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AutoSizeAxes = Axes.X;
|
||||
Alpha = 0;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding { Horizontal = 10 },
|
||||
Spacing = new Vector2(10),
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
globalRank = new Statistic<int>(UsersStrings.ShowRankGlobalSimple, @"#", Comparer<int>.Create((before, after) => before - after)),
|
||||
pp = new Statistic<decimal>(RankingsStrings.StatPerformance, string.Empty, Comparer<decimal>.Create((before, after) => Math.Sign(after - before))),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
LatestUpdate.BindValueChanged(val =>
|
||||
{
|
||||
if (val.NewValue == null)
|
||||
return;
|
||||
|
||||
var update = val.NewValue;
|
||||
|
||||
// null handling here is best effort because it is annoying.
|
||||
|
||||
globalRank.Alpha = update.After.GlobalRank == null ? 0 : 1;
|
||||
pp.Alpha = update.After.PP == null ? 0 : 1;
|
||||
|
||||
if (globalRank.Alpha == 0 && pp.Alpha == 0)
|
||||
return;
|
||||
|
||||
FinishTransforms(true);
|
||||
|
||||
this.FadeIn(500, Easing.OutQuint);
|
||||
|
||||
if (update.After.GlobalRank != null)
|
||||
{
|
||||
globalRank.Display(
|
||||
update.Before.GlobalRank ?? update.After.GlobalRank.Value,
|
||||
Math.Abs((update.After.GlobalRank.Value - update.Before.GlobalRank) ?? 0),
|
||||
update.After.GlobalRank.Value);
|
||||
}
|
||||
|
||||
if (update.After.PP != null)
|
||||
pp.Display(update.Before.PP ?? update.After.PP.Value, Math.Abs((update.After.PP - update.Before.PP) ?? 0M), update.After.PP.Value);
|
||||
|
||||
this.Delay(4000).FadeOut(500, Easing.OutQuint);
|
||||
});
|
||||
}
|
||||
|
||||
private partial class Statistic<T> : CompositeDrawable
|
||||
where T : struct, IEquatable<T>, IFormattable
|
||||
{
|
||||
private readonly LocalisableString title;
|
||||
private readonly string mainValuePrefix;
|
||||
private readonly IComparer<T> valueComparer;
|
||||
|
||||
private Counter<T> mainValue = null!;
|
||||
private Counter<T> deltaValue = null!;
|
||||
private OsuSpriteText titleText = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public Statistic(LocalisableString title, string mainValuePrefix, IComparer<T> valueComparer)
|
||||
{
|
||||
this.title = title;
|
||||
this.mainValuePrefix = mainValuePrefix;
|
||||
this.valueComparer = valueComparer;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
AutoSizeAxes = Axes.X;
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
mainValue = new Counter<T>
|
||||
{
|
||||
ValuePrefix = mainValuePrefix,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
deltaValue = new Counter<T>
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(size: 12, fixedWidth: false, weight: FontWeight.SemiBold),
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
titleText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.Default.With(size: 12, weight: FontWeight.SemiBold),
|
||||
Text = title,
|
||||
AlwaysPresent = true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Display(T before, T delta, T after)
|
||||
{
|
||||
int comparison = valueComparer.Compare(before, after);
|
||||
|
||||
if (comparison > 0)
|
||||
{
|
||||
deltaValue.Colour = colours.Lime1;
|
||||
deltaValue.ValuePrefix = "+";
|
||||
}
|
||||
else if (comparison < 0)
|
||||
{
|
||||
deltaValue.Colour = colours.Red1;
|
||||
deltaValue.ValuePrefix = "-";
|
||||
}
|
||||
else
|
||||
{
|
||||
deltaValue.Colour = Colour4.White;
|
||||
deltaValue.ValuePrefix = string.Empty;
|
||||
}
|
||||
|
||||
mainValue.SetCountWithoutRolling(before);
|
||||
deltaValue.SetCountWithoutRolling(delta);
|
||||
|
||||
titleText.Alpha = 1;
|
||||
deltaValue.Alpha = 0;
|
||||
|
||||
using (BeginDelayedSequence(1000))
|
||||
{
|
||||
titleText.FadeOut(250, Easing.OutQuint);
|
||||
deltaValue.FadeIn(250, Easing.OutQuint)
|
||||
.Then().Delay(1000)
|
||||
.Then().OnComplete(_ =>
|
||||
{
|
||||
mainValue.Current.Value = after;
|
||||
deltaValue.Current.SetDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private partial class Counter<T> : RollingCounter<T>
|
||||
where T : struct, IEquatable<T>, IFormattable
|
||||
{
|
||||
public const double ROLLING_DURATION = 500;
|
||||
|
||||
public FontUsage Font { get; init; } = OsuFont.Default;
|
||||
|
||||
public string ValuePrefix
|
||||
{
|
||||
get => valuePrefix;
|
||||
set
|
||||
{
|
||||
valuePrefix = value;
|
||||
UpdateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private string valuePrefix = string.Empty;
|
||||
|
||||
protected override LocalisableString FormatCount(T count) => LocalisableString.Format(@"{0}{1:N0}", ValuePrefix, count);
|
||||
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = Font);
|
||||
protected override double RollingDuration => ROLLING_DURATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user