diff --git a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs
index e8f40a7e07..399cabda99 100644
--- a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs
@@ -10,6 +10,28 @@ namespace osu.Game.Beatmaps
///
public class BeatmapOnlineInfo
{
+ ///
+ /// The length in milliseconds of this beatmap's song.
+ ///
+ public double Length { get; set; }
+
+ ///
+ /// Whether or not this beatmap has a background video.
+ ///
+ public bool HasVideo { get; set; }
+
+ ///
+ /// The amount of circles in this beatmap.
+ ///
+ [JsonProperty(@"count_circles")]
+ public int CircleCount { get; set; }
+
+ ///
+ /// The amount of sliders in this beatmap.
+ ///
+ [JsonProperty(@"count_sliders")]
+ public int SliderCount { get; set; }
+
///
/// The amount of plays this beatmap has.
///
diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
index e5a1984f50..6b59f0f298 100644
--- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
@@ -1,7 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using Newtonsoft.Json;
+using osu.Game.Users;
namespace osu.Game.Beatmaps
{
@@ -10,6 +12,26 @@ namespace osu.Game.Beatmaps
///
public class BeatmapSetOnlineInfo
{
+ ///
+ /// The author of the beatmaps in this set.
+ ///
+ public User Author;
+
+ ///
+ /// The date this beatmap set was submitted to the online listing.
+ ///
+ public DateTimeOffset Submitted { get; set; }
+
+ ///
+ /// The date this beatmap set was ranked.
+ ///
+ public DateTimeOffset? Ranked { get; set; }
+
+ ///
+ /// The date this beatmap set was last updated.
+ ///
+ public DateTimeOffset? LastUpdated { get; set; }
+
///
/// The different sizes of cover art for this beatmap set.
///
@@ -22,6 +44,11 @@ namespace osu.Game.Beatmaps
[JsonProperty(@"previewUrl")]
public string Preview { get; set; }
+ ///
+ /// The beats per minute of this beatmap set's song.
+ ///
+ public double BPM { get; set; }
+
///
/// The amount of plays this beatmap set has.
///
diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs
index e4763f73ee..470e13ea7b 100644
--- a/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs
+++ b/osu.Game/Online/API/Requests/GetBeatmapSetsRequest.cs
@@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
+using osu.Game.Users;
namespace osu.Game.Online.API.Requests
{
@@ -49,6 +50,12 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"id")]
private int onlineId { get; set; }
+ [JsonProperty(@"creator")]
+ private string creatorUsername;
+
+ [JsonProperty(@"user_id")]
+ private long creatorId = 1;
+
[JsonProperty(@"beatmaps")]
private IEnumerable beatmaps { get; set; }
@@ -60,6 +67,11 @@ namespace osu.Game.Online.API.Requests
Metadata = this,
OnlineInfo = new BeatmapSetOnlineInfo
{
+ Author = new User
+ {
+ Id = creatorId,
+ Username = creatorUsername,
+ },
Covers = covers,
Preview = preview,
PlayCount = playCount,
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index b4fbdfb252..c137b8f6f5 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -47,6 +47,8 @@ namespace osu.Game
private UserProfileOverlay userProfile;
+ private BeatmapSetOverlay beatmapSetOverlay;
+
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
@@ -187,6 +189,7 @@ namespace osu.Game
Depth = -1
}, overlayContent.Add);
LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add);
+ LoadComponentAsync(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -2 }, mainContent.Add);
LoadComponentAsync(musicController = new MusicController
{
Depth = -3,
@@ -223,6 +226,7 @@ namespace osu.Game
dependencies.Cache(chat);
dependencies.Cache(userProfile);
dependencies.Cache(musicController);
+ dependencies.Cache(beatmapSetOverlay);
dependencies.Cache(notificationOverlay);
dependencies.Cache(dialogOverlay);
diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
new file mode 100644
index 0000000000..b0e4e49e31
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
@@ -0,0 +1,107 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class AuthorInfo : Container
+ {
+ private const float height = 50;
+
+ private readonly UpdateableAvatar avatar;
+ private readonly FillFlowContainer fields;
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ var i = BeatmapSet.OnlineInfo;
+
+ avatar.User = i.Author;
+ fields.Children = new Drawable[]
+ {
+ new Field("made by", i.Author.Username, @"Exo2.0-RegularItalic"),
+ new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
+ {
+ Margin = new MarginPadding { Top = 5 },
+ },
+ };
+
+ if (i.Ranked.HasValue)
+ {
+ fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
+ }
+ else if (i.LastUpdated.HasValue)
+ {
+ fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
+ }
+ }
+ }
+
+ public AuthorInfo()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = height;
+
+ Children = new Drawable[]
+ {
+ avatar = new UpdateableAvatar
+ {
+ Size = new Vector2(height),
+ CornerRadius = 3,
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.Black.Opacity(0.25f),
+ Type = EdgeEffectType.Shadow,
+ Radius = 3,
+ Offset = new Vector2(0f, 1f),
+ },
+ },
+ fields = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Padding = new MarginPadding { Left = height + 5 },
+ },
+ };
+ }
+
+ private class Field : FillFlowContainer
+ {
+ public Field(string first, string second, string secondFont)
+ {
+ AutoSizeAxes = Axes.Both;
+ Direction = FillDirection.Horizontal;
+
+ Children = new[]
+ {
+ new OsuSpriteText
+ {
+ Text = $"{first} ",
+ TextSize = 13,
+ },
+ new OsuSpriteText
+ {
+ Text = second,
+ TextSize = 13,
+ Font = secondFont,
+ },
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs
new file mode 100644
index 0000000000..885f9cc219
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs
@@ -0,0 +1,130 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class BasicStats : Container
+ {
+ private readonly Statistic length, bpm, circleCount, sliderCount;
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##");
+ }
+ }
+
+ private BeatmapInfo beatmap;
+ public BeatmapInfo Beatmap
+ {
+ get { return beatmap; }
+ set
+ {
+ if (value == beatmap) return;
+ beatmap = value;
+
+ length.Value = TimeSpan.FromMilliseconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
+ circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString("N0");
+ sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString("N0");
+ }
+ }
+
+ public BasicStats()
+ {
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Children = new[]
+ {
+ length = new Statistic(FontAwesome.fa_clock_o, "Length") { Width = 0.25f },
+ bpm = new Statistic(FontAwesome.fa_circle, "BPM") { Width = 0.25f },
+ circleCount = new Statistic(FontAwesome.fa_circle_o, "Circle Count") { Width = 0.25f },
+ sliderCount = new Statistic(FontAwesome.fa_circle, "Slider Count") { Width = 0.25f },
+ },
+ };
+ }
+
+ private class Statistic : Container, IHasTooltip
+ {
+ private readonly string name;
+ private readonly OsuSpriteText value;
+
+ public string TooltipText => name;
+ public string Value
+ {
+ get { return value.Text; }
+ set { this.value.Text = value; }
+ }
+
+ public Statistic(FontAwesome icon, string name)
+ {
+ this.name = name;
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.Centre,
+ Icon = FontAwesome.fa_square,
+ Size = new Vector2(13),
+ Rotation = 45,
+ Colour = OsuColour.FromHex(@"441288"),
+ },
+ new SpriteIcon
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.Centre,
+ Icon = icon,
+ Size = new Vector2(13),
+ Colour = OsuColour.FromHex(@"f7dd55"),
+ Scale = new Vector2(0.8f),
+ },
+ value = new OsuSpriteText
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ TextSize = 13,
+ Font = @"Exo2.0-Bold",
+ Margin = new MarginPadding { Left = 10 },
+ },
+ },
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colour)
+ {
+ value.Colour = colour.Yellow;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
new file mode 100644
index 0000000000..2317e8562a
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
@@ -0,0 +1,312 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Linq;
+using osu.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class BeatmapPicker : Container
+ {
+ private const float tile_icon_padding = 7;
+ private const float tile_spacing = 2;
+
+ private readonly DifficultiesContainer difficulties;
+ private readonly OsuSpriteText version, starRating;
+ private readonly Statistic plays, favourites;
+
+ public readonly Bindable Beatmap = new Bindable();
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ Beatmap.Value = BeatmapSet.Beatmaps.First();
+ plays.Value = BeatmapSet.OnlineInfo.PlayCount;
+ favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount;
+ difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Select(b => new DifficultySelectorButton(b)
+ {
+ State = DifficultySelectorState.NotSelected,
+ OnHovered = beatmap =>
+ {
+ showBeatmap(beatmap);
+ starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
+ starRating.FadeIn(100);
+ },
+ OnClicked = beatmap =>
+ {
+ Beatmap.Value = beatmap;
+ },
+ });
+
+ updateDifficultyButtons();
+ }
+ }
+
+ public BeatmapPicker()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ difficulties = new DifficultiesContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) },
+ OnLostHover = () =>
+ {
+ showBeatmap(Beatmap.Value);
+ starRating.FadeOut(100);
+ },
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Top = 10 },
+ Spacing = new Vector2(5f),
+ Children = new[]
+ {
+ version = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ TextSize = 20,
+ Font = @"Exo2.0-Bold",
+ },
+ starRating = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ TextSize = 13,
+ Font = @"Exo2.0-Bold",
+ Text = "Star Difficulty",
+ Alpha = 0,
+ Margin = new MarginPadding { Bottom = 1 },
+ },
+ },
+ },
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(10f),
+ Margin = new MarginPadding { Top = 5 },
+ Children = new[]
+ {
+ plays = new Statistic(FontAwesome.fa_play_circle),
+ favourites = new Statistic(FontAwesome.fa_heart),
+ },
+ },
+ },
+ },
+ };
+
+ Beatmap.ValueChanged += b =>
+ {
+ showBeatmap(b);
+ updateDifficultyButtons();
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ starRating.Colour = colours.Yellow;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // done here so everything can bind in intialization and get the first trigger
+ Beatmap.TriggerChange();
+ }
+
+ private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version;
+
+ private void updateDifficultyButtons()
+ {
+ difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected);
+ }
+
+ private class DifficultiesContainer : FillFlowContainer
+ {
+ public Action OnLostHover;
+
+ protected override void OnHoverLost(InputState state)
+ {
+ base.OnHoverLost(state);
+ OnLostHover?.Invoke();
+ }
+ }
+
+ private class DifficultySelectorButton : OsuClickableContainer, IStateful
+ {
+ private const float transition_duration = 100;
+ private const float size = 52;
+
+ private readonly Container bg;
+ private readonly DifficultyIcon icon;
+
+ public readonly BeatmapInfo Beatmap;
+
+ public Action OnHovered;
+ public Action OnClicked;
+ public event Action StateChanged;
+
+ private DifficultySelectorState state;
+ public DifficultySelectorState State
+ {
+ get { return state; }
+ set
+ {
+ if (value == state) return;
+ state = value;
+
+ StateChanged?.Invoke(State);
+ if (value == DifficultySelectorState.Selected)
+ fadeIn();
+ else
+ fadeOut();
+ }
+ }
+
+ public DifficultySelectorButton(BeatmapInfo beatmap)
+ {
+ Beatmap = beatmap;
+ Size = new Vector2(size);
+ Margin = new MarginPadding { Horizontal = tile_spacing / 2 };
+
+ Children = new Drawable[]
+ {
+ bg = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ CornerRadius = 4,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(0.5f),
+ },
+ },
+ icon = new DifficultyIcon(beatmap)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(size - tile_icon_padding * 2),
+ Margin = new MarginPadding { Bottom = 1 },
+ },
+ };
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ fadeIn();
+ OnHovered?.Invoke(Beatmap);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ if (State == DifficultySelectorState.NotSelected)
+ fadeOut();
+ base.OnHoverLost(state);
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ OnClicked?.Invoke(Beatmap);
+ return base.OnClick(state);
+ }
+
+ private void fadeIn()
+ {
+ bg.FadeIn(transition_duration);
+ icon.FadeIn(transition_duration);
+ }
+
+ private void fadeOut()
+ {
+ bg.FadeOut();
+ icon.FadeTo(0.7f, transition_duration);
+ }
+ }
+
+ private class Statistic : FillFlowContainer
+ {
+ private readonly OsuSpriteText text;
+
+ private int value;
+ public int Value
+ {
+ get { return value; }
+ set
+ {
+ this.value = value;
+ text.Text = Value.ToString(@"N0");
+ }
+ }
+
+ public Statistic(FontAwesome icon)
+ {
+ AutoSizeAxes = Axes.Both;
+ Direction = FillDirection.Horizontal;
+ Spacing = new Vector2(2f);
+
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Icon = icon,
+ Shadow = true,
+ Size = new Vector2(13),
+ },
+ text = new OsuSpriteText
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Font = @"Exo2.0-SemiBoldItalic",
+ TextSize = 14,
+ },
+ };
+ }
+ }
+
+ private enum DifficultySelectorState
+ {
+ Selected,
+ NotSelected,
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs
new file mode 100644
index 0000000000..2fd0a55d9e
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/Details.cs
@@ -0,0 +1,118 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps;
+using osu.Game.Screens.Select.Details;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class Details : FillFlowContainer
+ {
+ private readonly PreviewButton preview;
+ private readonly BasicStats basic;
+ private readonly AdvancedStats advanced;
+ private readonly UserRatings ratings;
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
+ }
+ }
+
+ private BeatmapInfo beatmap;
+ public BeatmapInfo Beatmap
+ {
+ get { return beatmap; }
+ set
+ {
+ if (value == beatmap) return;
+ beatmap = value;
+
+ basic.Beatmap = advanced.Beatmap = Beatmap;
+ ratings.Metrics = Beatmap.Metrics;
+ }
+ }
+
+ public Details()
+ {
+ Width = BeatmapSetOverlay.RIGHT_WIDTH;
+ AutoSizeAxes = Axes.Y;
+ Spacing = new Vector2(1f);
+
+ Children = new Drawable[]
+ {
+ preview = new PreviewButton
+ {
+ RelativeSizeAxes = Axes.X,
+ },
+ new DetailBox
+ {
+ Child = basic = new BasicStats
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Vertical = 10 },
+ },
+ },
+ new DetailBox
+ {
+ Child = advanced = new AdvancedStats
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Vertical = 7.5f },
+ },
+ },
+ new DetailBox
+ {
+ Child = ratings = new UserRatings
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 95,
+ Margin = new MarginPadding { Top = 10 },
+ },
+ },
+ };
+ }
+
+ private class DetailBox : Container
+ {
+ private readonly Container content;
+ protected override Container Content => content;
+
+ public DetailBox()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(0.5f),
+ },
+ content = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Horizontal = 15 },
+ },
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs
new file mode 100644
index 0000000000..18a0cfd968
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs
@@ -0,0 +1,59 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class DownloadButton : HeaderButton
+ {
+ public DownloadButton(string title, string subtitle)
+ {
+ Width = 120;
+ RelativeSizeAxes = Axes.Y;
+
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Horizontal = 10 },
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new[]
+ {
+ new OsuSpriteText
+ {
+ Text = title,
+ TextSize = 13,
+ Font = @"Exo2.0-Bold",
+ },
+ new OsuSpriteText
+ {
+ Text = subtitle,
+ TextSize = 11,
+ Font = @"Exo2.0-Bold",
+ },
+ },
+ },
+ new SpriteIcon
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Icon = FontAwesome.fa_download,
+ Size = new Vector2(16),
+ Margin = new MarginPadding { Right = 5 },
+ },
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
new file mode 100644
index 0000000000..9fd4ac177c
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
@@ -0,0 +1,80 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Backgrounds;
+using OpenTK;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class FavouriteButton : HeaderButton
+ {
+ public readonly Bindable Favourited = new Bindable();
+
+ public FavouriteButton()
+ {
+ RelativeSizeAxes = Axes.Y;
+
+ Container pink;
+ SpriteIcon icon;
+ Children = new Drawable[]
+ {
+ pink = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0f,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.FromHex(@"9f015f"),
+ },
+ new Triangles
+ {
+ RelativeSizeAxes = Axes.Both,
+ ColourLight = OsuColour.FromHex(@"cb2187"),
+ ColourDark = OsuColour.FromHex(@"9f015f"),
+ TriangleScale = 1.5f,
+ },
+ },
+ },
+ icon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Icon = FontAwesome.fa_heart_o,
+ Size = new Vector2(18),
+ Shadow = false,
+ },
+ };
+
+ Favourited.ValueChanged += value =>
+ {
+ if (value)
+ {
+ pink.FadeIn(200);
+ icon.Icon = FontAwesome.fa_heart;
+ }
+ else
+ {
+ pink.FadeOut(200);
+ icon.Icon = FontAwesome.fa_heart_o;
+ }
+ };
+
+ Action = () => Favourited.Value = !Favourited.Value;
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ Width = DrawHeight;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs
new file mode 100644
index 0000000000..a93ccbf704
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/Header.cs
@@ -0,0 +1,228 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Drawables;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class Header : Container
+ {
+ private const float transition_duration = 250;
+ private const float tabs_height = 50;
+ private const float buttons_height = 45;
+ private const float buttons_spacing = 5;
+
+ private readonly Box tabsBg;
+ private readonly Container coverContainer;
+ private readonly OsuSpriteText title, artist;
+ private readonly AuthorInfo author;
+ private readonly Details details;
+
+ private DelayedLoadWrapper cover;
+
+ public readonly BeatmapPicker Picker;
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ Picker.BeatmapSet = author.BeatmapSet = details.BeatmapSet = BeatmapSet;
+ title.Text = BeatmapSet.Metadata.Title;
+ artist.Text = BeatmapSet.Metadata.Artist;
+
+ cover?.FadeOut(400, Easing.Out);
+ coverContainer.Add(cover = new DelayedLoadWrapper(new BeatmapSetCover(BeatmapSet)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fill,
+ OnLoadComplete = d =>
+ {
+ d.FadeInFromZero(400, Easing.Out);
+ },
+ })
+ {
+ RelativeSizeAxes = Axes.Both,
+ TimeBeforeLoad = 300
+ });
+ }
+ }
+
+ public Header()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = 400;
+ Masking = true;
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.Black.Opacity(0.25f),
+ Type = EdgeEffectType.Shadow,
+ Radius = 3,
+ Offset = new Vector2(0f, 1f),
+ };
+
+ Container noVideoButtons;
+ FillFlowContainer videoButtons;
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = tabs_height,
+ Children = new[]
+ {
+ tabsBg = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ },
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = tabs_height },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ coverContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)),
+ },
+ },
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING },
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 113,
+ Child = Picker = new BeatmapPicker(),
+ },
+ title = new OsuSpriteText
+ {
+ Font = @"Exo2.0-BoldItalic",
+ TextSize = 37,
+ },
+ artist = new OsuSpriteText
+ {
+ Font = @"Exo2.0-SemiBoldItalic",
+ TextSize = 25,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Top = 20 },
+ Child = author = new AuthorInfo(),
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = buttons_height,
+ Margin = new MarginPadding { Top = 10 },
+ Children = new Drawable[]
+ {
+ new FavouriteButton(),
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
+ Children = new Drawable[]
+ {
+ noVideoButtons = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0f,
+ Child = new DownloadButton("Download", @""),
+ },
+ videoButtons = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Spacing = new Vector2(buttons_spacing),
+ Alpha = 0f,
+ Children = new[]
+ {
+ new DownloadButton("Download", "with Video"),
+ new DownloadButton("Download", "without Video"),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ details = new Details
+ {
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.BottomRight,
+ Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING },
+ },
+ },
+ },
+ };
+
+ Picker.Beatmap.ValueChanged += b =>
+ {
+ details.Beatmap = b;
+
+ if (b.OnlineInfo.HasVideo)
+ {
+ noVideoButtons.FadeOut(transition_duration);
+ videoButtons.FadeIn(transition_duration);
+ }
+ else
+ {
+ noVideoButtons.FadeIn(transition_duration);
+ videoButtons.FadeOut(transition_duration);
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ tabsBg.Colour = colours.Gray3;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs
new file mode 100644
index 0000000000..3075020fe6
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs
@@ -0,0 +1,45 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Backgrounds;
+using osu.Game.Graphics.Containers;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class HeaderButton : OsuClickableContainer
+ {
+ private readonly Container content;
+
+ protected override Container Content => content;
+
+ public HeaderButton()
+ {
+ CornerRadius = 3;
+ Masking = true;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.FromHex(@"094c5f"),
+ },
+ new Triangles
+ {
+ RelativeSizeAxes = Axes.Both,
+ ColourLight = OsuColour.FromHex(@"0f7c9b"),
+ ColourDark = OsuColour.FromHex(@"094c5f"),
+ TriangleScale = 1.5f,
+ },
+ content = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs
new file mode 100644
index 0000000000..4a59591a72
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/Info.cs
@@ -0,0 +1,196 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class Info : Container
+ {
+ private const float transition_duration = 250;
+ private const float metadata_width = 225;
+ private const float spacing = 20;
+
+ private readonly MetadataSection description, source, tags;
+ private readonly Box successRateBackground;
+ private readonly SuccessRate successRate;
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ source.Text = BeatmapSet.Metadata.Source;
+ tags.Text = BeatmapSet.Metadata.Tags;
+ }
+ }
+
+ public BeatmapInfo Beatmap
+ {
+ get { return successRate.Beatmap; }
+ set { successRate.Beatmap = value; }
+ }
+
+ public Info()
+ {
+ RelativeSizeAxes = Axes.X;
+ Height = 220;
+ Masking = true;
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.Black.Opacity(0.25f),
+ Type = EdgeEffectType.Shadow,
+ Radius = 3,
+ Offset = new Vector2(0f, 1f),
+ };
+
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.White,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Right = metadata_width + BeatmapSetOverlay.RIGHT_WIDTH + spacing * 2 },
+ Child = new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ ScrollbarVisible = false,
+ Child = description = new MetadataSection("Description"),
+ },
+ },
+ new ScrollContainer
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = metadata_width,
+ ScrollbarVisible = false,
+ Padding = new MarginPadding { Horizontal = 10 },
+ Margin = new MarginPadding { Right = BeatmapSetOverlay.RIGHT_WIDTH + spacing },
+ Child = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ LayoutDuration = transition_duration,
+ Children = new[]
+ {
+ source = new MetadataSection("Source"),
+ tags = new MetadataSection("Tags"),
+ },
+ },
+ },
+ new Container
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = BeatmapSetOverlay.RIGHT_WIDTH,
+ Children = new Drawable[]
+ {
+ successRateBackground = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ successRate = new SuccessRate
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Top = 20, Horizontal = 15 },
+ },
+ },
+ },
+ },
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ successRateBackground.Colour = colours.GrayE;
+ source.TextColour = description.TextColour = colours.Gray5;
+ tags.TextColour = colours.BlueDark;
+ }
+
+ private class MetadataSection : FillFlowContainer
+ {
+ private readonly OsuSpriteText header;
+ private readonly TextFlowContainer textFlow;
+
+ public string Text
+ {
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ this.FadeOut(transition_duration);
+ return;
+ }
+
+ this.FadeIn(transition_duration);
+ textFlow.Clear();
+ textFlow.AddText(value, s => s.TextSize = 14);
+ }
+ }
+
+ public Color4 TextColour
+ {
+ get { return textFlow.Colour; }
+ set { textFlow.Colour = value; }
+ }
+
+ public MetadataSection(string title)
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ Spacing = new Vector2(5f);
+
+ InternalChildren = new Drawable[]
+ {
+ header = new OsuSpriteText
+ {
+ Text = title,
+ Font = @"Exo2.0-Bold",
+ TextSize = 14,
+ Shadow = false,
+ Margin = new MarginPadding { Top = 20 },
+ },
+ textFlow = new TextFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ header.Colour = colours.Gray5;
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs
new file mode 100644
index 0000000000..bdb06106f0
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs
@@ -0,0 +1,218 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
+using osu.Framework.Audio.Track;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class PreviewButton : OsuClickableContainer
+ {
+ private const float transition_duration = 500;
+
+ private readonly Container audioWrapper;
+ private readonly Box bg, progress;
+ private readonly SpriteIcon icon;
+ private readonly LoadingAnimation loadingAnimation;
+
+ private Track preview;
+
+ private bool loading
+ {
+ set
+ {
+ if (value)
+ {
+ loadingAnimation.Show();
+ icon.FadeOut(transition_duration * 5, Easing.OutQuint);
+ }
+ else
+ {
+ loadingAnimation.Hide();
+ icon.FadeIn(transition_duration, Easing.OutQuint);
+ }
+ }
+ }
+
+ private BeatmapSetInfo beatmapSet;
+ public BeatmapSetInfo BeatmapSet
+ {
+ get { return beatmapSet; }
+ set
+ {
+ if (value == beatmapSet) return;
+ beatmapSet = value;
+
+ Playing = false;
+ preview = null;
+ }
+ }
+
+ private bool playing;
+ public bool Playing
+ {
+ get { return playing; }
+ set
+ {
+ if (value == playing) return;
+ playing = value;
+
+ if (preview == null)
+ {
+ loading = true;
+ audioWrapper.Child = new AsyncLoadWrapper(new AudioLoadWrapper(BeatmapSet)
+ {
+ OnLoadComplete = d =>
+ {
+ loading = false;
+
+ preview = (d as AudioLoadWrapper)?.Preview;
+ Playing = Playing;
+ updatePlayingState();
+ },
+ });
+
+ return;
+ }
+
+ updatePlayingState();
+ }
+ }
+
+ public PreviewButton()
+ {
+ Height = 42;
+
+ Children = new Drawable[]
+ {
+ audioWrapper = new Container(),
+ bg = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black.Opacity(0.25f),
+ },
+ new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.X,
+ Height = 3,
+ Child = progress = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Width = 0f,
+ Alpha = 0f,
+ },
+ },
+ icon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Icon = FontAwesome.fa_play,
+ Size = new Vector2(18),
+ Shadow = false,
+ },
+ loadingAnimation = new LoadingAnimation
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ };
+
+ Action = () => Playing = !Playing;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ progress.Colour = colours.Yellow;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ if (Playing && preview != null)
+ {
+ progress.Width = (float)(preview.CurrentTime / preview.Length);
+ if (preview.HasCompleted)
+ {
+ Playing = false;
+ preview = null;
+ }
+ }
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ Playing = false;
+ base.Dispose(isDisposing);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ bg.FadeColour(Color4.Black.Opacity(0.5f), 100);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ bg.FadeColour(Color4.Black.Opacity(0.25f), 100);
+ base.OnHoverLost(state);
+ }
+
+ private void updatePlayingState()
+ {
+ if (preview == null) return;
+
+ if (Playing)
+ {
+ icon.Icon = FontAwesome.fa_stop;
+ progress.FadeIn(100);
+
+ preview.Seek(0);
+ preview.Start();
+ }
+ else
+ {
+ icon.Icon = FontAwesome.fa_play;
+ progress.FadeOut(100);
+ preview.Stop();
+ }
+ }
+
+ private class AudioLoadWrapper : Drawable
+ {
+ private readonly string preview;
+
+ public Track Preview;
+
+ public AudioLoadWrapper(BeatmapSetInfo set)
+ {
+ preview = set.OnlineInfo.Preview;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(AudioManager audio)
+ {
+ if (!string.IsNullOrEmpty(preview))
+ {
+ Preview = audio.Track.Get(preview);
+ Preview.Volume.Value = 0.5;
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
new file mode 100644
index 0000000000..26335aac9b
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
@@ -0,0 +1,113 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Screens.Select.Details;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class SuccessRate : Container
+ {
+ private readonly FillFlowContainer header;
+ private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
+ private readonly Bar successRate;
+ private readonly Container percentContainer;
+ private readonly FailRetryGraph graph;
+
+ private BeatmapInfo beatmap;
+ public BeatmapInfo Beatmap
+ {
+ get { return beatmap; }
+ set
+ {
+ if (value == beatmap) return;
+ beatmap = value;
+
+ var rate = (float)beatmap.OnlineInfo.PassCount / beatmap.OnlineInfo.PlayCount;
+ successPercent.Text = $"{Math.Round(rate * 100)}%";
+ successRate.Length = rate;
+ percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
+
+ graph.Metrics = Beatmap.Metrics;
+ }
+ }
+
+ public SuccessRate()
+ {
+ Children = new Drawable[]
+ {
+ header = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ successRateLabel = new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Success Rate",
+ TextSize = 13,
+ },
+ successRate = new Bar
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 5,
+ Margin = new MarginPadding { Top = 5 },
+ },
+ percentContainer = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Width = 0f,
+ Child = successPercent = new OsuSpriteText
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopCentre,
+ Text = @"0%",
+ TextSize = 13,
+ },
+ },
+ graphLabel = new OsuSpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Points of Failure",
+ TextSize = 13,
+ Margin = new MarginPadding { Vertical = 20 },
+ },
+ },
+ },
+ graph = new FailRetryGraph
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
+ successRate.AccentColour = colours.Green;
+ successRate.BackgroundColour = colours.GrayD;
+ }
+
+ protected override void UpdateAfterChildren()
+ {
+ base.UpdateAfterChildren();
+
+ graph.Padding = new MarginPadding { Top = header.DrawHeight };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
new file mode 100644
index 0000000000..8e28ad33c5
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -0,0 +1,99 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input;
+using osu.Game.Beatmaps;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Overlays.BeatmapSet;
+
+namespace osu.Game.Overlays
+{
+ public class BeatmapSetOverlay : WaveOverlayContainer
+ {
+ public const float X_PADDING = 40;
+ public const float RIGHT_WIDTH = 275;
+
+ private readonly Header header;
+ private readonly Info info;
+
+ public BeatmapSetOverlay()
+ {
+ FirstWaveColour = OsuColour.Gray(0.4f);
+ SecondWaveColour = OsuColour.Gray(0.3f);
+ ThirdWaveColour = OsuColour.Gray(0.2f);
+ FourthWaveColour = OsuColour.Gray(0.1f);
+
+ Anchor = Anchor.TopCentre;
+ Origin = Anchor.TopCentre;
+ RelativeSizeAxes = Axes.Both;
+ Width = 0.85f;
+
+ Masking = true;
+ EdgeEffect = new EdgeEffectParameters
+ {
+ Colour = Color4.Black.Opacity(0),
+ Type = EdgeEffectType.Shadow,
+ Radius = 3,
+ Offset = new Vector2(0f, 1f),
+ };
+
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = OsuColour.Gray(0.2f)
+ },
+ new ScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ ScrollbarVisible = false,
+ Child = new ReverseChildIDFillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ header = new Header(),
+ info = new Info(),
+ },
+ },
+ },
+ };
+
+ header.Picker.Beatmap.ValueChanged += b => info.Beatmap = b;
+ }
+
+ protected override void PopIn()
+ {
+ base.PopIn();
+ FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In);
+ }
+
+ protected override void PopOut()
+ {
+ base.PopOut();
+ FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ State = Visibility.Hidden;
+ return true;
+ }
+
+ public void ShowBeatmapSet(BeatmapSetInfo set)
+ {
+ header.BeatmapSet = info.BeatmapSet = set;
+ Show();
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs
index 3a9e75bd38..1675a2f663 100644
--- a/osu.Game/Overlays/Direct/DirectGridPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs
@@ -12,7 +12,6 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
-using osu.Framework.Input;
namespace osu.Game.Overlays.Direct
{
@@ -150,6 +149,15 @@ namespace osu.Game.Overlays.Direct
},
},
},
+ new DownloadButton
+ {
+ Size = new Vector2(30),
+ Margin = new MarginPadding(horizontal_padding),
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Colour = colours.Gray5,
+ Action = StartDownload
+ },
},
},
},
@@ -172,11 +180,5 @@ namespace osu.Game.Overlays.Direct
},
});
}
-
- protected override bool OnClick(InputState state)
- {
- StartDownload();
- return true;
- }
}
}
diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs
index b3502b0827..6702b7394c 100644
--- a/osu.Game/Overlays/Direct/DirectListPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectListPanel.cs
@@ -11,10 +11,8 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
-using osu.Framework.Input;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
-using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Direct
{
@@ -130,47 +128,5 @@ namespace osu.Game.Overlays.Direct
},
});
}
-
- private class DownloadButton : OsuClickableContainer
- {
- private readonly SpriteIcon icon;
-
- public DownloadButton()
- {
- Children = new Drawable[]
- {
- icon = new SpriteIcon
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(30),
- Icon = FontAwesome.fa_osu_chevron_down_o,
- },
- };
- }
-
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
- {
- icon.ScaleTo(0.9f, 1000, Easing.Out);
- return base.OnMouseDown(state, args);
- }
-
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
- {
- icon.ScaleTo(1f, 500, Easing.OutElastic);
- return base.OnMouseUp(state, args);
- }
-
- protected override bool OnHover(InputState state)
- {
- icon.ScaleTo(1.1f, 500, Easing.OutElastic);
- return base.OnHover(state);
- }
-
- protected override void OnHoverLost(InputState state)
- {
- icon.ScaleTo(1f, 500, Easing.OutElastic);
- }
- }
}
}
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index 6f1f581d0b..4f7f1bb39e 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -37,6 +37,7 @@ namespace osu.Game.Overlays.Direct
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private NotificationOverlay notifications;
+ private BeatmapSetOverlay beatmapSetOverlay;
protected override Container Content => content;
@@ -63,11 +64,12 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(permitNulls: true)]
- private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
+ private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications, BeatmapSetOverlay beatmapSetOverlay)
{
this.api = api;
this.beatmaps = beatmaps;
this.notifications = notifications;
+ this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container
{
@@ -118,6 +120,14 @@ namespace osu.Game.Overlays.Direct
base.OnHoverLost(state);
}
+ protected override bool OnClick(InputState state)
+ {
+ ShowInformation();
+ return true;
+ }
+
+ protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
+
protected void StartDownload()
{
if (!api.LocalUser.Value.IsSupporter)
diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs
new file mode 100644
index 0000000000..28f5344eae
--- /dev/null
+++ b/osu.Game/Overlays/Direct/DownloadButton.cs
@@ -0,0 +1,53 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Input;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using OpenTK;
+
+namespace osu.Game.Overlays.Direct
+{
+ public class DownloadButton : OsuClickableContainer
+ {
+ private readonly SpriteIcon icon;
+
+ public DownloadButton()
+ {
+ Children = new Drawable[]
+ {
+ icon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(30),
+ Icon = FontAwesome.fa_osu_chevron_down_o,
+ },
+ };
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ icon.ScaleTo(0.9f, 1000, Easing.Out);
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ icon.ScaleTo(1f, 500, Easing.OutElastic);
+ return base.OnMouseUp(state, args);
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ icon.ScaleTo(1.1f, 500, Easing.OutElastic);
+ return base.OnHover(state);
+ }
+
+ protected override void OnHoverLost(InputState state)
+ {
+ icon.ScaleTo(1f, 500, Easing.OutElastic);
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs
index d92c8ed509..f1215ab33d 100644
--- a/osu.Game/Screens/Select/Details/AdvancedStats.cs
+++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs
@@ -40,9 +40,9 @@ namespace osu.Game.Screens.Select.Details
firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
}
- hpDrain.Value = beatmap.Difficulty.DrainRate;
- accuracy.Value = beatmap.Difficulty.OverallDifficulty;
- approachRate.Value = beatmap.Difficulty.ApproachRate;
+ hpDrain.Value = beatmap.Difficulty?.DrainRate ?? 0;
+ accuracy.Value = beatmap.Difficulty?.OverallDifficulty ?? 0;
+ approachRate.Value = beatmap.Difficulty?.ApproachRate ?? 0;
starDifficulty.Value = (float)beatmap.StarDifficulty;
}
}
diff --git a/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs
new file mode 100644
index 0000000000..76ed9979ca
--- /dev/null
+++ b/osu.Game/Tests/Visual/TestCaseBeatmapSetOverlay.cs
@@ -0,0 +1,385 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Overlays;
+using osu.Game.Rulesets;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual
+{
+ internal class TestCaseBeatmapSetOverlay : OsuTestCase
+ {
+ public override string Description => @"view online beatmap sets";
+
+ private readonly BeatmapSetOverlay overlay;
+
+ public TestCaseBeatmapSetOverlay()
+ {
+ Add(overlay = new BeatmapSetOverlay());
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetStore rulesets)
+ {
+ var mania = rulesets.GetRuleset(3);
+ var taiko = rulesets.GetRuleset(1);
+
+ AddStep(@"show first", () =>
+ {
+ overlay.ShowBeatmapSet(new BeatmapSetInfo
+ {
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Lachryma ",
+ Artist = @"Kaneko Chiharu",
+ Source = @"SOUND VOLTEX III GRAVITY WARS",
+ Tags = @"sdvx grace the 5th kac original song contest konami bemani",
+ },
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Preview = @"https://b.ppy.sh/preview/415886.mp3",
+ PlayCount = 681380,
+ FavouriteCount = 356,
+ Submitted = new DateTime(2016, 2, 10),
+ Ranked = new DateTime(2016, 6, 19),
+ BPM = 236,
+ Author = new User
+ {
+ Username = @"Fresh Chicken",
+ Id = 3984370,
+ },
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
+ },
+ },
+ Beatmaps = new List
+ {
+ new BeatmapInfo
+ {
+ StarDifficulty = 1.36,
+ Version = @"BASIC",
+ Ruleset = mania,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 4,
+ DrainRate = 6.5f,
+ OverallDifficulty = 6.5f,
+ ApproachRate = 5,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 115000,
+ HasVideo = false,
+ CircleCount = 265,
+ SliderCount = 71,
+ PlayCount = 47906,
+ PassCount = 19899,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 2.22,
+ Version = @"NOVICE",
+ Ruleset = mania,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 4,
+ DrainRate = 7,
+ OverallDifficulty = 7,
+ ApproachRate = 5,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 118000,
+ HasVideo = true,
+ CircleCount = 592,
+ SliderCount = 62,
+ PlayCount = 162021,
+ PassCount = 72116,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 3.49,
+ Version = @"ADVANCED",
+ Ruleset = mania,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 4,
+ DrainRate = 7.5f,
+ OverallDifficulty = 7.5f,
+ ApproachRate = 5,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 118000,
+ HasVideo = false,
+ CircleCount = 1042,
+ SliderCount = 79,
+ PlayCount = 225178,
+ PassCount = 73001,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 4.24,
+ Version = @"EXHAUST",
+ Ruleset = mania,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 4,
+ DrainRate = 8,
+ OverallDifficulty = 8,
+ ApproachRate = 5,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 118000,
+ HasVideo = false,
+ CircleCount = 1352,
+ SliderCount = 69,
+ PlayCount = 131545,
+ PassCount = 42703,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 5.26,
+ Version = @"GRAVITY",
+ Ruleset = mania,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 4,
+ DrainRate = 8.5f,
+ OverallDifficulty = 8.5f,
+ ApproachRate = 5,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 118000,
+ HasVideo = false,
+ CircleCount = 1730,
+ SliderCount = 115,
+ PlayCount = 117673,
+ PassCount = 24241,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ },
+ });
+ });
+
+ AddStep(@"show second", () =>
+ {
+ overlay.ShowBeatmapSet(new BeatmapSetInfo
+ {
+ Metadata = new BeatmapMetadata
+ {
+ Title = @"Soumatou Labyrinth",
+ Artist = @"Yunomi with Momobako&miko",
+ Tags = @"mmbk.com yuzu__rinrin charlotte",
+ },
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Preview = @"https://b.ppy.sh/preview/625493.mp3",
+ PlayCount = 22996,
+ FavouriteCount = 58,
+ Submitted = new DateTime(2016, 6, 11),
+ Ranked = new DateTime(2016, 7, 12),
+ BPM = 160,
+ Author = new User
+ {
+ Username = @"komasy",
+ Id = 1980256,
+ },
+ Covers = new BeatmapSetOnlineCovers
+ {
+ Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
+ },
+ },
+ Beatmaps = new List
+ {
+ new BeatmapInfo
+ {
+ StarDifficulty = 1.40,
+ Version = @"yzrin's Kantan",
+ Ruleset = taiko,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 2,
+ DrainRate = 7,
+ OverallDifficulty = 3,
+ ApproachRate = 10,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 193000,
+ HasVideo = false,
+ CircleCount = 262,
+ SliderCount = 0,
+ PlayCount = 3952,
+ PassCount = 1373,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 2.23,
+ Version = @"Futsuu",
+ Ruleset = taiko,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 2,
+ DrainRate = 6,
+ OverallDifficulty = 4,
+ ApproachRate = 10,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 193000,
+ HasVideo = false,
+ CircleCount = 464,
+ SliderCount = 0,
+ PlayCount = 4833,
+ PassCount = 920,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 3.19,
+ Version = @"Muzukashii",
+ Ruleset = taiko,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 2,
+ DrainRate = 6,
+ OverallDifficulty = 5,
+ ApproachRate = 10,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 193000,
+ HasVideo = false,
+ CircleCount = 712,
+ SliderCount = 0,
+ PlayCount = 4405,
+ PassCount = 854,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 3.97,
+ Version = @"Charlotte's Oni",
+ Ruleset = taiko,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 5,
+ DrainRate = 6,
+ OverallDifficulty = 5.5f,
+ ApproachRate = 10,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 193000,
+ HasVideo = false,
+ CircleCount = 943,
+ SliderCount = 0,
+ PlayCount = 3950,
+ PassCount = 693,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ new BeatmapInfo
+ {
+ StarDifficulty = 5.08,
+ Version = @"Labyrinth Oni",
+ Ruleset = taiko,
+ Difficulty = new BeatmapDifficulty
+ {
+ CircleSize = 5,
+ DrainRate = 5,
+ OverallDifficulty = 6,
+ ApproachRate = 10,
+ },
+ OnlineInfo = new BeatmapOnlineInfo
+ {
+ Length = 193000,
+ HasVideo = false,
+ CircleCount = 1068,
+ SliderCount = 0,
+ PlayCount = 5856,
+ PassCount = 1207,
+ },
+ Metrics = new BeatmapMetrics
+ {
+ Ratings = Enumerable.Range(0, 10),
+ Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
+ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
+ },
+ },
+ },
+ });
+ });
+
+ AddStep(@"hide", overlay.Hide);
+ AddStep(@"show without reload", overlay.Show);
+ }
+ }
+}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 1679c53ff9..cd4447c734 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -398,6 +398,7 @@
+
@@ -774,6 +775,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+