diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs b/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs new file mode 100644 index 0000000000..4cda14559f --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseDirect.cs @@ -0,0 +1,199 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Database; +using osu.Game.Overlays; + +namespace osu.Desktop.VisualTests.Tests +{ + public class TestCaseDirect : TestCase + { + public override string Description => @"osu!direct overlay"; + + private DirectOverlay direct; + private RulesetDatabase rulesets; + + public override void Reset() + { + base.Reset(); + + Add(direct = new DirectOverlay()); + newBeatmaps(); + + AddStep(@"toggle", direct.ToggleVisibility); + AddStep(@"result counts", () => direct.ResultAmounts = new DirectOverlay.ResultCounts(1, 4, 13)); + } + + [BackgroundDependencyLoader] + private void load(RulesetDatabase rulesets) + { + this.rulesets = rulesets; + } + + private void newBeatmaps() + { + var ruleset = rulesets.GetRuleset(0); + + direct.BeatmapSets = new[] + { + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"OrVid", + Artist = @"An", + Author = @"RLC", + Source = @"", + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.35f, + Metadata = new BeatmapMetadata(), + OnlineInfo = new BeatmapOnlineInfo + { + Covers = new[] { @"https://assets.ppy.sh//beatmaps/578332/covers/cover.jpg?1494591390" }, + Preview = @"https://b.ppy.sh/preview/578332.mp3", + PlayCount = 97, + FavouriteCount = 72, + }, + }, + }, + }, + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"tiny lamp", + Artist = @"fhana", + Author = @"Sotarks", + Source = @"ぎんぎつね", + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.81f, + Metadata = new BeatmapMetadata(), + OnlineInfo = new BeatmapOnlineInfo + { + Covers = new[] { @"https://assets.ppy.sh//beatmaps/599627/covers/cover.jpg?1494539318" }, + Preview = @"https//b.ppy.sh/preview/599627.mp3", + PlayCount = 3082, + FavouriteCount = 14, + }, + }, + }, + }, + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"At Gwanghwamun", + Artist = @"KYUHYUN", + Author = @"Cerulean Veyron", + Source = @"", + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 0.9f, + Metadata = new BeatmapMetadata(), + OnlineInfo = new BeatmapOnlineInfo + { + Covers = new[] { @"https://assets.ppy.sh//beatmaps/513268/covers/cover.jpg?1494502863" }, + Preview = @"https//b.ppy.sh/preview/513268.mp3", + PlayCount = 2762, + FavouriteCount = 15, + }, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 1.1f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 2.02f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 3.49f, + }, + }, + }, + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = @"RHAPSODY OF BLUE SKY", + Artist = @"fhana", + Author = @"[Kamiya]", + Source = @"小林さんちのメイドラゴン", + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 1.26f, + Metadata = new BeatmapMetadata(), + OnlineInfo = new BeatmapOnlineInfo + { + Covers = new[] { @"https://assets.ppy.sh//beatmaps/586841/covers/cover.jpg?1494052741" }, + Preview = @"https//b.ppy.sh/preview/586841.mp3", + PlayCount = 62317, + FavouriteCount = 161, + }, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 2.01f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 2.87f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 3.76f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 3.93f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 4.37f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.13f, + }, + new BeatmapInfo + { + Ruleset = ruleset, + StarDifficulty = 5.42f, + }, + }, + }, + }; + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index dbb1750b72..8ec0fc83db 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -220,6 +220,7 @@ + diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index 7c0aa49d2a..e91b52565a 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -9,7 +9,7 @@ using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - internal class DifficultyColouredContainer : Container, IHasAccentColour + public class DifficultyColouredContainer : Container, IHasAccentColour { public Color4 AccentColour { get; set; } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index a8b63c2502..9df1f0f284 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -11,7 +11,7 @@ using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - internal class DifficultyIcon : DifficultyColouredContainer + public class DifficultyIcon : DifficultyColouredContainer { private readonly BeatmapInfo beatmap; diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index c2e35d64a8..9f253f6055 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -43,6 +43,9 @@ namespace osu.Game.Database [Ignore] public BeatmapMetrics Metrics { get; set; } + [Ignore] + public BeatmapOnlineInfo OnlineInfo { get; set; } + public string Path { get; set; } [JsonProperty("file_md5")] diff --git a/osu.Game/Database/BeatmapOnlineInfo.cs b/osu.Game/Database/BeatmapOnlineInfo.cs new file mode 100644 index 0000000000..bcb2ef16ac --- /dev/null +++ b/osu.Game/Database/BeatmapOnlineInfo.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace osu.Game.Database +{ + /// + /// Beatmap info retrieved for previewing locally without having the beatmap downloaded. + /// + public class BeatmapOnlineInfo + { + /// + /// The different sizes of cover art for this beatmap: cover, cover@2x, card, card@2x, list, list@2x. + /// + [JsonProperty(@"covers")] + public IEnumerable Covers { get; set; } + + /// + /// A small sample clip of this beatmap's song. + /// + [JsonProperty(@"previewUrl")] + public string Preview { get; set; } + + /// + /// The amount of plays this beatmap has. + /// + [JsonProperty(@"play_count")] + public int PlayCount { get; set; } + + /// + /// The amount of people who have favourited this map. + /// + [JsonProperty(@"favourite_count")] + public int FavouriteCount { get; set; } + } +} diff --git a/osu.Game/Database/OnlineWorkingBeatmap.cs b/osu.Game/Database/OnlineWorkingBeatmap.cs new file mode 100644 index 0000000000..1465c59434 --- /dev/null +++ b/osu.Game/Database/OnlineWorkingBeatmap.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; + +namespace osu.Game.Database +{ + internal class OnlineWorkingBeatmap : WorkingBeatmap + { + private readonly TextureStore textures; + private readonly TrackManager tracks; + + public OnlineWorkingBeatmap(BeatmapInfo beatmapInfo, TextureStore textures, TrackManager tracks) : base(beatmapInfo) + { + this.textures = textures; + this.tracks = tracks; + } + + protected override Beatmap GetBeatmap() + { + return new Beatmap(); + } + + protected override Texture GetBackground() + { + return textures.Get(BeatmapInfo.OnlineInfo.Covers.FirstOrDefault()); + } + + protected override Track GetTrack() + { + return tracks.Get(BeatmapInfo.OnlineInfo.Preview); + } + } +} diff --git a/osu.Game/Database/RankStatus.cs b/osu.Game/Database/RankStatus.cs new file mode 100644 index 0000000000..f2a7d67a40 --- /dev/null +++ b/osu.Game/Database/RankStatus.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; + +namespace osu.Game.Database +{ + public enum RankStatus + { + Any = 7, + [Description("Ranked & Approved")] + RankedApproved = 0, + Approved = 1, + Loved = 8, + Favourites = 2, + [Description("Mod Requests")] + ModRequests = 3, + Pending = 4, + Graveyard = 5, + [Description("My Maps")] + MyMaps = 6, + } +} diff --git a/osu.Game/Graphics/Containers/ReverseDepthFillFlowContainer.cs b/osu.Game/Graphics/Containers/ReverseDepthFillFlowContainer.cs new file mode 100644 index 0000000000..2b52b06abc --- /dev/null +++ b/osu.Game/Graphics/Containers/ReverseDepthFillFlowContainer.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Graphics.Containers +{ + public class ReverseDepthFillFlowContainer : FillFlowContainer where T : Drawable + { + protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); + protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 4f286ba7b5..9c1799c04c 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -113,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface } } - protected class OsuDropdownHeader : DropdownHeader + public class OsuDropdownHeader : DropdownHeader { private readonly SpriteText label; protected override string Label diff --git a/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs new file mode 100644 index 0000000000..5de6507bb3 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuEnumDropdown.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.ComponentModel; +using System.Reflection; +using System.Collections.Generic; + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuEnumDropdown : OsuDropdown + { + public OsuEnumDropdown() + { + if (!typeof(T).IsEnum) + throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument"); + + List> items = new List>(); + foreach(var val in (T[])Enum.GetValues(typeof(T))) + { + var field = typeof(T).GetField(Enum.GetName(typeof(T), val)); + items.Add( + new KeyValuePair( + field.GetCustomAttribute()?.Description ?? Enum.GetName(typeof(T), val), + val + ) + ); + } + Items = items; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index a9fad0c739..4bbae4efd1 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -57,9 +57,9 @@ namespace osu.Game.Graphics.UserInterface } } - private class OsuTabItem : TabItem + public class OsuTabItem : TabItem { - private readonly SpriteText text; + protected readonly SpriteText Text; private readonly Box box; private Color4? accentColour; @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.UserInterface { accentColour = value; if (!Active) - text.Colour = value; + Text.Colour = value; } } @@ -94,13 +94,13 @@ namespace osu.Game.Graphics.UserInterface private void fadeActive() { box.FadeIn(transition_length, EasingTypes.OutQuint); - text.FadeColour(Color4.White, transition_length, EasingTypes.OutQuint); + Text.FadeColour(Color4.White, transition_length, EasingTypes.OutQuint); } private void fadeInactive() { box.FadeOut(transition_length, EasingTypes.OutQuint); - text.FadeColour(AccentColour, transition_length, EasingTypes.OutQuint); + Text.FadeColour(AccentColour, transition_length, EasingTypes.OutQuint); } protected override bool OnHover(InputState state) @@ -130,7 +130,7 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - text = new OsuSpriteText + Text = new OsuSpriteText { Margin = new MarginPadding { Top = 5, Bottom = 5 }, Origin = Anchor.BottomLeft, diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f13043a745..886ff4f8d1 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -41,6 +41,8 @@ namespace osu.Game private DialogOverlay dialogOverlay; + private DirectOverlay direct; + private Intro intro { get @@ -70,6 +72,8 @@ namespace osu.Game public void ToggleSettings() => settings.ToggleVisibility(); + public void ToggleDirect() => direct.ToggleVisibility(); + [BackgroundDependencyLoader] private void load() { @@ -160,6 +164,7 @@ namespace osu.Game }); //overlay elements + LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add); LoadComponentAsync(musicController = new MusicController @@ -249,6 +254,9 @@ namespace osu.Game case Key.O: settings.ToggleVisibility(); return true; + case Key.D: + direct.ToggleVisibility(); + return true; } } @@ -280,6 +288,7 @@ namespace osu.Game Toolbar.State = Visibility.Hidden; musicController.State = Visibility.Hidden; chat.State = Visibility.Hidden; + direct.State = Visibility.Hidden; } else { diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs new file mode 100644 index 0000000000..4be68157d7 --- /dev/null +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -0,0 +1,193 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Direct +{ + public class DirectGridPanel : DirectPanel + { + private const float horizontal_padding = 10; + private const float vertical_padding = 5; + + private FillFlowContainer bottomPanel; + + public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap) + { + Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image) + CornerRadius = 4; + Masking = true; + + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 3f, + Colour = Color4.Black.Opacity(0.25f), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + FadeInFromZero(200, EasingTypes.Out); + bottomPanel.LayoutDuration = 200; + bottomPanel.LayoutEasing = EasingTypes.Out; + bottomPanel.Origin = Anchor.BottomLeft; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, LocalisationEngine localisation, TextureStore textures) + { + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + GetBackground(textures), + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f), + }, + bottomPanel = new FillFlowContainer + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + Direction = FillDirection.Vertical, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, vertical_padding), + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding }, + Direction = FillDirection.Vertical, + Children = new[] + { + new OsuSpriteText + { + Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + TextSize = 18, + Font = @"Exo2.0-BoldItalic", + }, + new OsuSpriteText + { + Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = @"Exo2.0-BoldItalic", + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding + { + Top = vertical_padding, + Bottom = vertical_padding, + Left = horizontal_padding, + Right = horizontal_padding, + }, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = "mapped by ", + TextSize = 14, + Shadow = false, + Colour = colours.Gray5, + }, + new OsuSpriteText + { + Text = SetInfo.Metadata.Author, + TextSize = 14, + Font = @"Exo2.0-SemiBoldItalic", + Shadow = false, + Colour = colours.BlueDark, + }, + }, + }, + new Container + { + AutoSizeAxes = Axes.X, + Height = 14, + Children = new[] + { + new OsuSpriteText + { + Text = $"from {SetInfo.Metadata.Source}", + TextSize = 14, + Shadow = false, + Colour = colours.Gray5, + Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + }, + }, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = 20, + Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Children = GetDifficultyIcons(), + }, + }, + }, + }, + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding }, + Children = new[] + { + new Statistic(FontAwesome.fa_play_circle, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.PlayCount ?? 0) + { + Margin = new MarginPadding { Right = 1 }, + }, + new Statistic(FontAwesome.fa_heart, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.FavouriteCount ?? 0), + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs new file mode 100644 index 0000000000..48636a5228 --- /dev/null +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -0,0 +1,197 @@ +// 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.Sprites; +using osu.Framework.Graphics.Colour; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Database; +using osu.Framework.Allocation; +using osu.Framework.Localisation; +using osu.Framework.Graphics.Textures; +using System.Linq; +using osu.Framework.Input; + +namespace osu.Game.Overlays.Direct +{ + public class DirectListPanel : DirectPanel + { + private const float horizontal_padding = 10; + private const float vertical_padding = 5; + private const float height = 70; + + public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap) + { + RelativeSizeAxes = Axes.X; + Height = height; + CornerRadius = 5; + Masking = true; + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 3f, + Colour = Color4.Black.Opacity(0.25f), + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + FadeInFromZero(200, EasingTypes.Out); + } + + [BackgroundDependencyLoader] + private void load(LocalisationEngine localisation, TextureStore textures) + { + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + GetBackground(textures), + new Box + { + RelativeSizeAxes = Axes.Both, + ColourInfo = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.25f), Color4.Black.Opacity(0.75f)), + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding, Left = horizontal_padding, Right = vertical_padding }, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + TextSize = 18, + Font = @"Exo2.0-BoldItalic", + }, + new OsuSpriteText + { + Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = @"Exo2.0-BoldItalic", + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = 20, + Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Children = GetDifficultyIcons(), + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Right = height - vertical_padding * 2 + vertical_padding }, + Children = new Drawable[] + { + new Statistic(FontAwesome.fa_play_circle, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.PlayCount ?? 0) + { + Margin = new MarginPadding { Right = 1 }, + }, + new Statistic(FontAwesome.fa_heart, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.FavouriteCount ?? 0), + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = "mapped by ", + TextSize = 14, + }, + new OsuSpriteText + { + Text = SetInfo.Metadata.Author, + TextSize = 14, + Font = @"Exo2.0-SemiBoldItalic", + }, + }, + }, + new OsuSpriteText + { + Text = $"from {SetInfo.Metadata.Source}", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + TextSize = 14, + Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + }, + }, + }, + new DownloadButton + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Size = new Vector2(height - vertical_padding * 2), + }, + }, + }, + }; + } + + private class DownloadButton : ClickableContainer + { + private readonly TextAwesome icon; + + public DownloadButton() + { + Children = new Drawable[] + { + icon = new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + UseFullGlyphHeight = false, + TextSize = 30, + Icon = FontAwesome.fa_osu_chevron_down_o, + }, + }; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + icon.ScaleTo(0.9f, 1000, EasingTypes.Out); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + icon.ScaleTo(1f, 500, EasingTypes.OutElastic); + return base.OnMouseUp(state, args); + } + + protected override bool OnHover(InputState state) + { + icon.ScaleTo(1.1f, 500, EasingTypes.OutElastic); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + icon.ScaleTo(1f, 500, EasingTypes.OutElastic); + } + } + } +} diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs new file mode 100644 index 0000000000..8a56cf392e --- /dev/null +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -0,0 +1,88 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Direct +{ + public abstract class DirectPanel : Container + { + protected readonly BeatmapSetInfo SetInfo; + + protected DirectPanel(BeatmapSetInfo setInfo) + { + SetInfo = setInfo; + } + + protected IEnumerable GetDifficultyIcons() + { + var icons = new List(); + + foreach (var b in SetInfo.Beatmaps) + icons.Add(new DifficultyIcon(b)); + + return icons; + } + + protected Drawable GetBackground(TextureStore textures) + { + return new AsyncLoadWrapper(new BeatmapBackgroundSprite(new OnlineWorkingBeatmap(SetInfo.Beatmaps.FirstOrDefault(), textures, null)) + { + FillMode = FillMode.Fill, + OnLoadComplete = d => d.FadeInFromZero(400, EasingTypes.Out), + }) { RelativeSizeAxes = Axes.Both }; + } + + public class Statistic : FillFlowContainer + { + private readonly SpriteText text; + + private int value; + public int Value + { + get { return value; } + set + { + this.value = value; + text.Text = Value.ToString(@"N0"); + } + } + + public Statistic(FontAwesome icon, int value = 0) + { + Anchor = Anchor.TopRight; + Origin = Anchor.TopRight; + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Horizontal; + Spacing = new Vector2(5f, 0f); + + Children = new Drawable[] + { + text = new OsuSpriteText + { + Font = @"Exo2.0-SemiBoldItalic", + }, + new TextAwesome + { + Icon = icon, + Shadow = true, + TextSize = 14, + Margin = new MarginPadding { Top = 1 }, + }, + }; + + Value = value; + } + } + } +} diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs new file mode 100644 index 0000000000..735e14b8c1 --- /dev/null +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -0,0 +1,233 @@ +// 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.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Direct +{ + public class FilterControl : Container + { + public static readonly float HEIGHT = 35 + 32 + 30 + padding * 2; // search + mode toggle buttons + sort tabs + padding + + private const float padding = 10; + + private readonly Box tabStrip; + private readonly FillFlowContainer modeButtons; + + public readonly SearchTextBox Search; + public readonly SortTabControl SortTabs; + public readonly OsuEnumDropdown RankStatusDropdown; + public readonly Bindable DisplayStyle = new Bindable(); + + protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || RankStatusDropdown.Contains(screenSpacePos); + + public FilterControl() + { + RelativeSizeAxes = Axes.X; + Height = HEIGHT; + DisplayStyle.Value = DirectOverlay.PanelDisplayStyle.Grid; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"384552"), + Alpha = 0.9f, + }, + tabStrip = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + RelativeSizeAxes = Axes.X, + Height = 1, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING }, + Children = new Drawable[] + { + Search = new DirectSearchTextBox + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = padding }, + }, + modeButtons = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(padding, 0f), + Margin = new MarginPadding { Top = padding }, + }, + SortTabs = new SortTabControl + { + RelativeSizeAxes = Axes.X, + }, + }, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Spacing = new Vector2(10f, 0f), + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Top = HEIGHT - SlimEnumDropdown.HEIGHT - padding, Right = DirectOverlay.WIDTH_PADDING }, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5f, 0f), + Direction = FillDirection.Horizontal, + Children = new[] + { + new DisplayStyleToggleButton(FontAwesome.fa_th_large, DirectOverlay.PanelDisplayStyle.Grid, DisplayStyle), + new DisplayStyleToggleButton(FontAwesome.fa_list_ul, DirectOverlay.PanelDisplayStyle.List, DisplayStyle), + }, + }, + RankStatusDropdown = new SlimEnumDropdown + { + RelativeSizeAxes = Axes.None, + Width = 160f, + }, + }, + }, + }; + + RankStatusDropdown.Current.Value = RankStatus.RankedApproved; + SortTabs.Current.Value = SortCriteria.Title; + SortTabs.Current.TriggerChange(); + } + + [BackgroundDependencyLoader(true)] + private void load(OsuGame game, RulesetDatabase rulesets, OsuColour colours) + { + tabStrip.Colour = colours.Yellow; + RankStatusDropdown.AccentColour = colours.BlueDark; + + var b = new Bindable(); //backup bindable incase the game is null + foreach (var r in rulesets.AllRulesets) + { + modeButtons.Add(new RulesetToggleButton(game?.Ruleset ?? b, r)); + } + } + + private class DirectSearchTextBox : SearchTextBox + { + protected override Color4 BackgroundUnfocused => backgroundColour; + protected override Color4 BackgroundFocused => backgroundColour; + + private Color4 backgroundColour; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + backgroundColour = colours.Gray2.Opacity(0.9f); + } + } + + private class RulesetToggleButton : ClickableContainer + { + private readonly TextAwesome icon; + + private RulesetInfo ruleset; + public RulesetInfo Ruleset + { + get { return ruleset; } + set + { + ruleset = value; + icon.Icon = Ruleset.CreateInstance().Icon; + } + } + + private readonly Bindable bindable; + + private void Bindable_ValueChanged(RulesetInfo obj) + { + icon.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); + } + + public RulesetToggleButton(Bindable bindable, RulesetInfo ruleset) + { + this.bindable = bindable; + AutoSizeAxes = Axes.Both; + + Children = new[] + { + icon = new TextAwesome + { + Origin = Anchor.TopLeft, + Anchor = Anchor.TopLeft, + TextSize = 32, + } + }; + + Ruleset = ruleset; + bindable.ValueChanged += Bindable_ValueChanged; + Bindable_ValueChanged(bindable.Value); + Action = () => bindable.Value = Ruleset; + } + + protected override void Dispose(bool isDisposing) + { + if (bindable != null) + bindable.ValueChanged -= Bindable_ValueChanged; + base.Dispose(isDisposing); + } + } + + private class DisplayStyleToggleButton : ClickableContainer + { + private readonly TextAwesome icon; + private readonly DirectOverlay.PanelDisplayStyle style; + private readonly Bindable bindable; + + public DisplayStyleToggleButton(FontAwesome icon, DirectOverlay.PanelDisplayStyle style, Bindable bindable) + { + this.bindable = bindable; + this.style = style; + Size = new Vector2(SlimEnumDropdown.HEIGHT); + + Children = new Drawable[] + { + this.icon = new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = icon, + TextSize = 18, + UseFullGlyphHeight = false, + Alpha = 0.5f, + }, + }; + + bindable.ValueChanged += Bindable_ValueChanged; + Bindable_ValueChanged(bindable.Value); + Action = () => bindable.Value = this.style; + } + + private void Bindable_ValueChanged(DirectOverlay.PanelDisplayStyle style) + { + icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100); + } + + protected override void Dispose(bool isDisposing) + { + bindable.ValueChanged -= Bindable_ValueChanged; + } + } + } +} diff --git a/osu.Game/Overlays/Direct/Header.cs b/osu.Game/Overlays/Direct/Header.cs new file mode 100644 index 0000000000..8e4ede48d5 --- /dev/null +++ b/osu.Game/Overlays/Direct/Header.cs @@ -0,0 +1,124 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +using Container = osu.Framework.Graphics.Containers.Container; + +namespace osu.Game.Overlays.Direct +{ + public class Header : Container + { + public static readonly float HEIGHT = 90; + + private readonly Box tabStrip; + + public readonly OsuTabControl Tabs; + + public Header() + { + Height = HEIGHT; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"252f3a"), + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING }, + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft, + Position = new Vector2(-35f, 5f), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10f, 0f), + Children = new Drawable[] + { + new TextAwesome + { + TextSize = 25, + Icon = FontAwesome.fa_osu_chevron_down_o, + }, + new OsuSpriteText + { + TextSize = 25, + Text = @"osu!direct", + }, + }, + }, + tabStrip = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Width = 282, //todo: make this actually match the tab control's width instead of hardcoding + Height = 1, + }, + Tabs = new DirectTabControl + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + }, + }, + }, + }; + + Tabs.Current.Value = DirectTab.Search; + Tabs.Current.TriggerChange(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + tabStrip.Colour = colours.Green; + } + + private class DirectTabControl : OsuTabControl + { + protected override TabItem CreateTabItem(DirectTab value) => new DirectTabItem(value); + + public DirectTabControl() + { + Height = 25; + AccentColour = Color4.White; + } + + private class DirectTabItem : OsuTabItem + { + public DirectTabItem(DirectTab value) : base(value) + { + Text.TextSize = 15; + } + } + } + } + + public enum DirectTab + { + Search, + [Description("Newest Maps")] + NewestMaps, + [Description("Top Rated")] + TopRated, + [Description("Most Played")] + MostPlayed + } +} diff --git a/osu.Game/Overlays/Direct/SlimEnumDropdown.cs b/osu.Game/Overlays/Direct/SlimEnumDropdown.cs new file mode 100644 index 0000000000..1d12b8477b --- /dev/null +++ b/osu.Game/Overlays/Direct/SlimEnumDropdown.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Direct +{ + public class SlimEnumDropdown : OsuEnumDropdown + { + public const float HEIGHT = 25; + + protected override DropdownHeader CreateHeader() => new SlimDropdownHeader { AccentColour = AccentColour }; + protected override Menu CreateMenu() => new SlimMenu(); + + private class SlimDropdownHeader : OsuDropdownHeader + { + public SlimDropdownHeader() + { + Height = HEIGHT; + Icon.TextSize = 16; + Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + BackgroundColour = Color4.Black.Opacity(0.25f); + } + } + + private class SlimMenu : OsuMenu + { + public SlimMenu() + { + Background.Colour = Color4.Black.Opacity(0.25f); + } + } + } +} diff --git a/osu.Game/Overlays/Direct/SortTabControl.cs b/osu.Game/Overlays/Direct/SortTabControl.cs new file mode 100644 index 0000000000..4d4e02d875 --- /dev/null +++ b/osu.Game/Overlays/Direct/SortTabControl.cs @@ -0,0 +1,117 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Direct +{ + public class SortTabControl : OsuTabControl + { + protected override TabItem CreateTabItem(SortCriteria value) => new SortTabItem(value); + + public SortTabControl() + { + Height = 30; + } + + private class SortTabItem : TabItem + { + private const float transition_duration = 100; + + private readonly Box box; + + public override bool Active + { + get { return base.Active; } + set + { + if (Active == value) return; + + if (value) + slideActive(); + else + slideInactive(); + base.Active = value; + } + } + + public SortTabItem(SortCriteria value) : base(value) + { + AutoSizeAxes = Axes.X; + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new OsuSpriteText + { + Margin = new MarginPadding { Top = 8, Bottom = 8 }, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Text = (value as Enum).GetDescription() ?? value.ToString(), + TextSize = 14, + Font = @"Exo2.0-Bold", + }, + box = new Box + { + RelativeSizeAxes = Axes.X, + Height = 5, + Scale = new Vector2(1f, 0f), + Colour = Color4.White, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + box.Colour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + if (!Active) + slideActive(); + return true; + } + + protected override void OnHoverLost(InputState state) + { + if (!Active) + slideInactive(); + } + + private void slideActive() + { + box.ScaleTo(new Vector2(1f), transition_duration); + } + + private void slideInactive() + { + box.ScaleTo(new Vector2(1f, 0f), transition_duration); + } + } + } + + public enum SortCriteria + { + Title, + Artist, + Creator, + Difficulty, + Ranked, + Rating, + } +} diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs new file mode 100644 index 0000000000..0930c825b6 --- /dev/null +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -0,0 +1,230 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Direct; + +using Container = osu.Framework.Graphics.Containers.Container; + +namespace osu.Game.Overlays +{ + public class DirectOverlay : WaveOverlayContainer + { + public static readonly int WIDTH_PADDING = 80; + private const float panel_padding = 10f; + + private readonly FilterControl filter; + private readonly FillFlowContainer resultCountsContainer; + private readonly OsuSpriteText resultCountsText; + private readonly FillFlowContainer panels; + + private IEnumerable beatmapSets; + public IEnumerable BeatmapSets + { + get { return beatmapSets; } + set + { + if (beatmapSets?.Equals(value) ?? false) return; + beatmapSets = value; + + recreatePanels(filter.DisplayStyle.Value); + } + } + + private ResultCounts resultAmounts; + public ResultCounts ResultAmounts + { + get { return resultAmounts; } + set + { + if (value == ResultAmounts) return; + resultAmounts = value; + + updateResultCounts(); + } + } + + public DirectOverlay() + { + RelativeSizeAxes = Axes.Both; + + // osu!direct colours are not part of the standard palette + + FirstWaveColour = OsuColour.FromHex(@"19b0e2"); + SecondWaveColour = OsuColour.FromHex(@"2280a2"); + ThirdWaveColour = OsuColour.FromHex(@"005774"); + FourthWaveColour = OsuColour.FromHex(@"003a4e"); + + Header header; + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"485e74"), + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] + { + new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 5, + ColourLight = OsuColour.FromHex(@"465b71"), + ColourDark = OsuColour.FromHex(@"3f5265"), + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = Header.HEIGHT + FilterControl.HEIGHT }, + Children = new[] + { + new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollDraggerVisible = false, + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + resultCountsContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = WIDTH_PADDING, Top = 6 }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Found ", + TextSize = 15, + }, + resultCountsText = new OsuSpriteText + { + TextSize = 15, + Font = @"Exo2.0-Bold", + }, + } + }, + panels = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Top = panel_padding, Bottom = panel_padding, Left = WIDTH_PADDING, Right = WIDTH_PADDING }, + Spacing = new Vector2(panel_padding), + }, + }, + }, + }, + }, + }, + }, + filter = new FilterControl + { + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Top = Header.HEIGHT }, + }, + header = new Header + { + RelativeSizeAxes = Axes.X, + }, + }; + + header.Tabs.Current.ValueChanged += tab => { if (tab != DirectTab.Search) filter.Search.Current.Value = string.Empty; }; + + filter.Search.Exit = Hide; + filter.Search.Current.ValueChanged += text => { if (text != string.Empty) header.Tabs.Current.Value = DirectTab.Search; }; + filter.DisplayStyle.ValueChanged += recreatePanels; + + updateResultCounts(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + resultCountsContainer.Colour = colours.Yellow; + } + + private void updateResultCounts() + { + resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, EasingTypes.Out); + if (ResultAmounts == null) return; + + resultCountsText.Text = pluralize("Artist", ResultAmounts.Artists) + ", " + + pluralize("Song", ResultAmounts.Songs) + ", " + + pluralize("Tag", ResultAmounts.Tags); + } + + private string pluralize(string prefix, int value) + { + return $@"{value} {prefix}" + (value == 1 ? string.Empty : @"s"); + } + + private void recreatePanels(PanelDisplayStyle displayStyle) + { + if (BeatmapSets == null) return; + panels.Children = BeatmapSets.Select(b => displayStyle == PanelDisplayStyle.Grid ? (DirectPanel)new DirectGridPanel(b) { Width = 400 } : new DirectListPanel(b)); + } + + protected override bool OnFocus(InputState state) + { + filter.Search.TriggerFocus(); + return false; + } + + protected override void PopIn() + { + base.PopIn(); + + filter.Search.HoldFocus = true; + } + + protected override void PopOut() + { + base.PopOut(); + + filter.Search.HoldFocus = false; + } + + public class ResultCounts + { + public readonly int Artists; + public readonly int Songs; + public readonly int Tags; + + public ResultCounts(int artists, int songs, int tags) + { + Artists = artists; + Songs = songs; + Tags = tags; + } + } + + public enum PanelDisplayStyle + { + Grid, + List, + } + } +} diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs index a9f0848403..5725ee8439 100644 --- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs @@ -1,32 +1,17 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.Reflection; -using System.ComponentModel; -using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { public class SettingsEnumDropdown : SettingsDropdown { - public SettingsEnumDropdown() + protected override Drawable CreateControl() => new OsuEnumDropdown { - if (!typeof(T).IsEnum) - throw new InvalidOperationException("SettingsDropdown only supports enums as the generic type argument"); - - List> items = new List>(); - foreach(var val in (T[])Enum.GetValues(typeof(T))) - { - var field = typeof(T).GetField(Enum.GetName(typeof(T), val)); - items.Add( - new KeyValuePair( - field.GetCustomAttribute()?.Description ?? Enum.GetName(typeof(T), val), - val - ) - ); - } - Items = items; - } + Margin = new MarginPadding { Top = 5 }, + RelativeSizeAxes = Axes.X, + }; } } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 42acd9b090..1a7d2d4e37 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -61,6 +61,7 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(background); buttons.OnSettings = game.ToggleSettings; + buttons.OnDirect = game.ToggleDirect; preloadSongSelect(); } diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 92c40f6351..4a10438cdb 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,6 +11,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using OpenTK; using osu.Framework.Input; +using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Play.HUD { @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD { Children = new Drawable[] { - iconsContainer = new IconFlow + iconsContainer = new ReverseDepthFillFlowContainer { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -93,12 +93,5 @@ namespace osu.Game.Screens.Play.HUD contract(); base.OnHoverLost(state); } - - private class IconFlow : FillFlowContainer - { - // just reverses the depth of flow contents. - protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); - protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); - } } } diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index c064a0272e..be2f5196ef 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -2,8 +2,6 @@ // 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.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -12,6 +10,7 @@ using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; +using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Select.Options { @@ -71,7 +70,7 @@ namespace osu.Game.Screens.Select.Options Scale = new Vector2(1, 0), Colour = Color4.Black.Opacity(0.5f), }, - buttonsContainer = new ButtonFlow + buttonsContainer = new ReverseDepthFillFlowContainer { Height = height, RelativePositionAxes = Axes.X, @@ -109,16 +108,5 @@ namespace osu.Game.Screens.Select.Options HotKey = hotkey }); } - - private class ButtonFlow : FillFlowContainer - { - protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); - protected override IEnumerable FlowingChildren => base.FlowingChildren.Reverse(); - - public ButtonFlow() - { - Direction = FillDirection.Horizontal; - } - } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 643270d289..84ccec8763 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -432,6 +432,19 @@ + + + + + + + + + + + + +