diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs b/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs new file mode 100644 index 0000000000..eb7df96355 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseSocial.cs @@ -0,0 +1,85 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Testing; +using osu.Game.Overlays; +using osu.Game.Users; + +namespace osu.Desktop.VisualTests.Tests +{ + public class TestCaseSocial : TestCase + { + public override string Description => @"social browser overlay"; + + public override void Reset() + { + base.Reset(); + + SocialOverlay s = new SocialOverlay + { + Users = new[] + { + new User + { + Username = @"flyte", + Id = 3103765, + Country = new Country { FlagName = @"JP" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + }, + new User + { + Username = @"Cookiezi", + Id = 124493, + Country = new Country { FlagName = @"KR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg", + }, + new User + { + Username = @"Angelsim", + Id = 1777162, + Country = new Country { FlagName = @"KR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + new User + { + Username = @"Rafis", + Id = 2558286, + Country = new Country { FlagName = @"PL" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg", + }, + new User + { + Username = @"hvick225", + Id = 50265, + Country = new Country { FlagName = @"TW" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c5.jpg", + }, + new User + { + Username = @"peppy", + Id = 2, + Country = new Country { FlagName = @"AU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" + }, + new User + { + Username = @"filsdelama", + Id = 2831793, + Country = new Country { FlagName = @"FR" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c7.jpg" + }, + new User + { + Username = @"_index", + Id = 652457, + Country = new Country { FlagName = @"RU" }, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c8.jpg" + }, + }, + }; + Add(s); + + AddStep(@"toggle", s.ToggleVisibility); + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index f72b08adde..d1d0cc1c1a 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -224,6 +224,7 @@ + diff --git a/osu.Game/Overlays/Direct/SortTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs similarity index 80% rename from osu.Game/Overlays/Direct/SortTabControl.cs rename to osu.Game/Graphics/UserInterface/PageTabControl.cs index 4d4e02d875..8bf455b099 100644 --- a/osu.Game/Overlays/Direct/SortTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -10,22 +10,20 @@ 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 +namespace osu.Game.Graphics.UserInterface { - public class SortTabControl : OsuTabControl + public class PageTabControl : OsuTabControl { - protected override TabItem CreateTabItem(SortCriteria value) => new SortTabItem(value); + protected override TabItem CreateTabItem(T value) => new PageTabItem(value); - public SortTabControl() + public PageTabControl() { Height = 30; } - private class SortTabItem : TabItem + private class PageTabItem : TabItem { private const float transition_duration = 100; @@ -46,7 +44,7 @@ namespace osu.Game.Overlays.Direct } } - public SortTabItem(SortCriteria value) : base(value) + public PageTabItem(T value) : base(value) { AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; @@ -104,14 +102,4 @@ namespace osu.Game.Overlays.Direct } } } - - public enum SortCriteria - { - Title, - Artist, - Creator, - Difficulty, - Ranked, - Rating, - } } diff --git a/osu.Game/Online/API/Requests/GetUsersRequest.cs b/osu.Game/Online/API/Requests/GetUsersRequest.cs new file mode 100644 index 0000000000..5fb8606e1e --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUsersRequest.cs @@ -0,0 +1,20 @@ +// 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 Newtonsoft.Json; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class GetUsersRequest : APIRequest> + { + protected override string Target => @"rankings/osu/performance"; + } + + public class RankingEntry + { + [JsonProperty] + public User User; + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a8bee97eb4..6bcd89b878 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -43,6 +43,8 @@ namespace osu.Game private DirectOverlay direct; + private SocialOverlay social; + private Intro intro { get @@ -165,6 +167,7 @@ namespace osu.Game //overlay elements LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); + LoadComponentAsync(social = new SocialOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add); LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add); LoadComponentAsync(musicController = new MusicController @@ -198,11 +201,16 @@ namespace osu.Game }; Dependencies.Cache(settings); + Dependencies.Cache(social); Dependencies.Cache(chat); Dependencies.Cache(musicController); Dependencies.Cache(notificationManager); Dependencies.Cache(dialogOverlay); + // ensure both overlays aren't presented at the same time + chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State; + social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State; + LoadComponentAsync(Toolbar = new Toolbar { Depth = -3, @@ -234,6 +242,9 @@ namespace osu.Game case Key.F8: chat.ToggleVisibility(); return true; + case Key.F9: + social.ToggleVisibility(); + return true; case Key.PageUp: case Key.PageDown: var swClock = (Clock as ThrottledFrameClock)?.Source as StopwatchClock; @@ -292,6 +303,7 @@ namespace osu.Game musicController.State = Visibility.Hidden; chat.State = Visibility.Hidden; direct.State = Visibility.Hidden; + social.State = Visibility.Hidden; } else { diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 735e14b8c1..455d0ab77b 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -5,117 +5,35 @@ 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; +using osu.Game.Overlays.SearchableList; namespace osu.Game.Overlays.Direct { - public class FilterControl : Container + public class FilterControl : SearchableListFilterControl { - public static readonly float HEIGHT = 35 + 32 + 30 + padding * 2; // search + mode toggle buttons + sort tabs + padding + private FillFlowContainer modeButtons; - 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() + protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); + protected override DirectSortCritera DefaultTab => DirectSortCritera.Title; + protected override Drawable CreateSupplementaryControls() { - RelativeSizeAxes = Axes.X; - Height = HEIGHT; - DisplayStyle.Value = DirectOverlay.PanelDisplayStyle.Grid; - - Children = new Drawable[] + modeButtons = new FillFlowContainer { - 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, - }, - }, - }, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10f, 0f), }; - RankStatusDropdown.Current.Value = RankStatus.RankedApproved; - SortTabs.Current.Value = SortCriteria.Title; - SortTabs.Current.TriggerChange(); + return modeButtons; } [BackgroundDependencyLoader(true)] private void load(OsuGame game, RulesetDatabase rulesets, OsuColour colours) { - tabStrip.Colour = colours.Yellow; - RankStatusDropdown.AccentColour = colours.BlueDark; + DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; var b = new Bindable(); //backup bindable incase the game is null foreach (var r in rulesets.AllRulesets) @@ -124,20 +42,6 @@ namespace osu.Game.Overlays.Direct } } - 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; @@ -188,46 +92,15 @@ namespace osu.Game.Overlays.Direct 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; - } - } + public enum DirectSortCritera + { + Title, + Artist, + Creator, + Difficulty, + Ranked, + Rating, } } diff --git a/osu.Game/Overlays/Direct/Header.cs b/osu.Game/Overlays/Direct/Header.cs index 8e4ede48d5..000ef473cc 100644 --- a/osu.Game/Overlays/Direct/Header.cs +++ b/osu.Game/Overlays/Direct/Header.cs @@ -2,113 +2,28 @@ // 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; +using osu.Game.Overlays.SearchableList; namespace osu.Game.Overlays.Direct { - public class Header : Container + public class Header : SearchableListHeader { - public static readonly float HEIGHT = 90; + protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a"); + protected override float TabStripWidth => 298; - private readonly Box tabStrip; - - public readonly OsuTabControl Tabs; + protected override DirectTab DefaultTab => DirectTab.Search; + protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 }; + protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o; 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 diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index b7f6572bcc..93c440384b 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -7,28 +7,30 @@ 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; +using osu.Game.Overlays.SearchableList; +using OpenTK.Graphics; namespace osu.Game.Overlays { - public class DirectOverlay : WaveOverlayContainer + public class DirectOverlay : SearchableListOverlay { - 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; + protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74"); + protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71"); + protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265"); + + protected override SearchableListHeader CreateHeader() => new Header(); + protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); + private IEnumerable beatmapSets; public IEnumerable BeatmapSets { @@ -38,7 +40,7 @@ namespace osu.Game.Overlays if (beatmapSets?.Equals(value) ?? false) return; beatmapSets = value; - recreatePanels(filter.DisplayStyle.Value); + recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); } } @@ -66,96 +68,39 @@ namespace osu.Game.Overlays ThirdWaveColour = OsuColour.FromHex(@"005774"); FourthWaveColour = OsuColour.FromHex(@"003a4e"); - Header header; - Children = new Drawable[] + ScrollFlow.Children = new Drawable[] { - new Box + resultCountsContainer = new FillFlowContainer { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"485e74"), - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new[] + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Top = 5 }, + Children = new Drawable[] { - new Triangles + new OsuSpriteText { - RelativeSizeAxes = Axes.Both, - TriangleScale = 5, - ColourLight = OsuColour.FromHex(@"465b71"), - ColourDark = OsuColour.FromHex(@"3f5265"), + Text = "Found ", + TextSize = 15, }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = Header.HEIGHT + FilterControl.HEIGHT }, - Children = new[] - { - new ScrollContainer + resultCountsText = new OsuSpriteText { - RelativeSizeAxes = Axes.Both, - ScrollbarVisible = 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), - }, - }, - }, - }, + TextSize = 15, + Font = @"Exo2.0-Bold", }, - }, + } }, - filter = new FilterControl - { - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Top = Header.HEIGHT }, - }, - header = new Header + panels = new FillFlowContainer { RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(panel_padding), + Margin = new MarginPadding { Top = 10 }, }, }; - 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; + Header.Tabs.Current.ValueChanged += tab => { if (tab != DirectTab.Search) Filter.Search.Text = string.Empty; }; + Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; }; + Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; updateResultCounts(); } @@ -187,30 +132,6 @@ namespace osu.Game.Overlays panels.Children = BeatmapSets.Select(b => displayStyle == PanelDisplayStyle.Grid ? (DirectPanel)new DirectGridPanel(b) { Width = 400 } : new DirectListPanel(b)); } - public override bool AcceptsFocus => true; - - protected override bool OnClick(InputState state) => true; - - protected override void OnFocus(InputState state) - { - InputManager.ChangeFocus(filter.Search); - base.OnFocus(state); - } - - 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; @@ -224,11 +145,5 @@ namespace osu.Game.Overlays Tags = tags; } } - - public enum PanelDisplayStyle - { - Grid, - List, - } } } diff --git a/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs new file mode 100644 index 0000000000..b37b0db139 --- /dev/null +++ b/osu.Game/Overlays/SearchableList/DisplayStyleControl.cs @@ -0,0 +1,102 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.SearchableList +{ + public class DisplayStyleControl : Container + { + public readonly SlimEnumDropdown Dropdown; + public readonly Bindable DisplayStyle = new Bindable(); + + public DisplayStyleControl() + { + AutoSizeAxes = Axes.Both; + + Children = new[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Spacing = new Vector2(10f, 0f), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5f, 0f), + Direction = FillDirection.Horizontal, + Children = new[] + { + new DisplayStyleToggleButton(FontAwesome.fa_th_large, PanelDisplayStyle.Grid, DisplayStyle), + new DisplayStyleToggleButton(FontAwesome.fa_list_ul, PanelDisplayStyle.List, DisplayStyle), + }, + }, + Dropdown = new SlimEnumDropdown + { + RelativeSizeAxes = Axes.None, + Width = 160f, + }, + }, + }, + }; + + DisplayStyle.Value = PanelDisplayStyle.Grid; + } + + private class DisplayStyleToggleButton : ClickableContainer + { + private readonly TextAwesome icon; + private readonly PanelDisplayStyle style; + private readonly Bindable bindable; + + public DisplayStyleToggleButton(FontAwesome icon, PanelDisplayStyle style, Bindable bindable) + { + this.bindable = bindable; + this.style = style; + Size = new Vector2(25f); + + 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(PanelDisplayStyle style) + { + icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100); + } + + protected override void Dispose(bool isDisposing) + { + bindable.ValueChanged -= Bindable_ValueChanged; + } + } + } + + public enum PanelDisplayStyle + { + Grid, + List, + } +} diff --git a/osu.Game/Overlays/SearchableList/HeaderTabControl.cs b/osu.Game/Overlays/SearchableList/HeaderTabControl.cs new file mode 100644 index 0000000000..56569fcca5 --- /dev/null +++ b/osu.Game/Overlays/SearchableList/HeaderTabControl.cs @@ -0,0 +1,28 @@ +// 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.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.SearchableList +{ + public class HeaderTabControl : OsuTabControl + { + protected override TabItem CreateTabItem(T value) => new HeaderTabItem(value); + + public HeaderTabControl() + { + Height = 26; + AccentColour = Color4.White; + } + + private class HeaderTabItem : OsuTabItem + { + public HeaderTabItem(T value) : base(value) + { + Text.TextSize = 16; + } + } + } +} diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs new file mode 100644 index 0000000000..dfc2dbe49a --- /dev/null +++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs @@ -0,0 +1,136 @@ +// 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.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.SearchableList +{ + public abstract class SearchableListFilterControl : Container + { + private const float padding = 10; + + private readonly Container filterContainer; + private readonly Box tabStrip; + + public readonly SearchTextBox Search; + public readonly PageTabControl Tabs; + public readonly DisplayStyleControl DisplayStyleControl; + + protected abstract Color4 BackgroundColour { get; } + protected abstract T DefaultTab { get; } + protected virtual Drawable CreateSupplementaryControls() => null; + + protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || DisplayStyleControl.Dropdown.Contains(screenSpacePos); + + protected SearchableListFilterControl() + { + if (!typeof(T).IsEnum) + throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument"); + + RelativeSizeAxes = Axes.X; + + var controls = CreateSupplementaryControls(); + Container controlsContainer; + Children = new Drawable[] + { + filterContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = BackgroundColour, + Alpha = 0.9f, + }, + tabStrip = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 1, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Top = padding, Horizontal = SearchableListOverlay.WIDTH_PADDING }, + Children = new Drawable[] + { + Search = new FilterSearchTextBox + { + RelativeSizeAxes = Axes.X, + }, + controlsContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = controls != null ? padding : 0 }, + }, + Tabs = new PageTabControl + { + RelativeSizeAxes = Axes.X, + }, + new Box //keep the tab strip part of autosize, but don't put it in the flow container + { + RelativeSizeAxes = Axes.X, + Height = 1, + Colour = Color4.White.Opacity(0), + }, + }, + }, + }, + }, + DisplayStyleControl = new DisplayStyleControl + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + }; + + if (controls != null) controlsContainer.Children = new[] { controls }; + + Tabs.Current.Value = DefaultTab; + Tabs.Current.TriggerChange(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + tabStrip.Colour = colours.Yellow; + } + + protected override void Update() + { + base.Update(); + + Height = filterContainer.Height; + DisplayStyleControl.Margin = new MarginPadding { Top = filterContainer.Height - 35, Right = SearchableListOverlay.WIDTH_PADDING }; + } + + private class FilterSearchTextBox : 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); + } + } + } +} diff --git a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs new file mode 100644 index 0000000000..26dc9b03c8 --- /dev/null +++ b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs @@ -0,0 +1,93 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.SearchableList +{ + public abstract class SearchableListHeader : Container + { + private readonly Box tabStrip; + + public readonly HeaderTabControl Tabs; + + protected abstract Color4 BackgroundColour { get; } + protected abstract float TabStripWidth { get; } //can be removed once (if?) TabControl support auto sizing + protected abstract T DefaultTab { get; } + protected abstract Drawable CreateHeaderText(); + protected abstract FontAwesome Icon { get; } + + protected SearchableListHeader() + { + if (!typeof(T).IsEnum) + throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument"); + + RelativeSizeAxes = Axes.X; + Height = 90; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = BackgroundColour, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING, Right = SearchableListOverlay.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[] + { + new TextAwesome + { + TextSize = 25, + Icon = Icon, + }, + CreateHeaderText(), + }, + }, + tabStrip = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Width = TabStripWidth, + Height = 1, + }, + Tabs = new HeaderTabControl + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + }, + }, + }, + }; + + Tabs.Current.Value = DefaultTab; + Tabs.Current.TriggerChange(); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + tabStrip.Colour = colours.Green; + } + } +} diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs new file mode 100644 index 0000000000..093750bcc0 --- /dev/null +++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs @@ -0,0 +1,123 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics.Backgrounds; + +namespace osu.Game.Overlays.SearchableList +{ + public abstract class SearchableListOverlay : WaveOverlayContainer + { + public static readonly float WIDTH_PADDING = 80; + } + + public abstract class SearchableListOverlay : SearchableListOverlay + { + private readonly Container scrollContainer; + + protected readonly SearchableListHeader Header; + protected readonly SearchableListFilterControl Filter; + protected readonly FillFlowContainer ScrollFlow; + + protected abstract Color4 BackgroundColour { get; } + protected abstract Color4 TrianglesColourLight { get; } + protected abstract Color4 TrianglesColourDark { get; } + protected abstract SearchableListHeader CreateHeader(); + protected abstract SearchableListFilterControl CreateFilterControl(); + + protected SearchableListOverlay() + { + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = BackgroundColour, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new[] + { + new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 5, + ColourLight = TrianglesColourLight, + ColourDark = TrianglesColourDark, + }, + }, + }, + scrollContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Children = new[] + { + ScrollFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 }, + Direction = FillDirection.Vertical, + }, + }, + }, + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + AlwaysReceiveInput = true, + Children = new Drawable[] + { + Header = CreateHeader(), + Filter = CreateFilterControl(), + }, + }, + }; + + Filter.Search.Exit = Hide; + } + + protected override void Update() + { + base.Update(); + + scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height }; + } + + protected override void OnFocus(InputState state) + { + InputManager.ChangeFocus(Filter.Search); + } + + protected override void PopIn() + { + base.PopIn(); + + Filter.Search.HoldFocus = true; + } + + protected override void PopOut() + { + base.PopOut(); + + Filter.Search.HoldFocus = false; + } + } +} diff --git a/osu.Game/Overlays/Direct/SlimEnumDropdown.cs b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs similarity index 89% rename from osu.Game/Overlays/Direct/SlimEnumDropdown.cs rename to osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs index 1d12b8477b..6c0887b5df 100644 --- a/osu.Game/Overlays/Direct/SlimEnumDropdown.cs +++ b/osu.Game/Overlays/SearchableList/SlimEnumDropdown.cs @@ -7,12 +7,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; -namespace osu.Game.Overlays.Direct +namespace osu.Game.Overlays.SearchableList { public class SlimEnumDropdown : OsuEnumDropdown { - public const float HEIGHT = 25; - protected override DropdownHeader CreateHeader() => new SlimDropdownHeader { AccentColour = AccentColour }; protected override Menu CreateMenu() => new SlimMenu(); @@ -20,7 +18,7 @@ namespace osu.Game.Overlays.Direct { public SlimDropdownHeader() { - Height = HEIGHT; + Height = 25; Icon.TextSize = 16; Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 }; } diff --git a/osu.Game/Overlays/Social/FilterControl.cs b/osu.Game/Overlays/Social/FilterControl.cs new file mode 100644 index 0000000000..cf4097643e --- /dev/null +++ b/osu.Game/Overlays/Social/FilterControl.cs @@ -0,0 +1,31 @@ +// 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.Graphics; +using osu.Game.Graphics; +using osu.Game.Overlays.SearchableList; + +namespace osu.Game.Overlays.Social +{ + public class FilterControl : SearchableListFilterControl + { + protected override Color4 BackgroundColour => OsuColour.FromHex(@"47253a"); + protected override SocialSortCriteria DefaultTab => SocialSortCriteria.Rank; + + public FilterControl() + { + Tabs.Margin = new MarginPadding { Top = 10 }; + } + } + + public enum SocialSortCriteria + { + Rank, + //Location, + //[Description("Time Zone")] + //TimeZone, + //[Description("World Map")] + //WorldMap, + } +} diff --git a/osu.Game/Overlays/Social/Header.cs b/osu.Game/Overlays/Social/Header.cs new file mode 100644 index 0000000000..2674854327 --- /dev/null +++ b/osu.Game/Overlays/Social/Header.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Overlays.SearchableList; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Framework.Allocation; +using System.ComponentModel; + +namespace osu.Game.Overlays.Social +{ + public class Header : SearchableListHeader + { + private OsuSpriteText browser; + + protected override Color4 BackgroundColour => OsuColour.FromHex(@"38202e"); + protected override float TabStripWidth => 438; + protected override SocialTab DefaultTab => SocialTab.OnlinePlayers; + protected override FontAwesome Icon => FontAwesome.fa_users; + + protected override Drawable CreateHeaderText() + { + return new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = "social ", + TextSize = 25, + }, + browser = new OsuSpriteText + { + Text = "browser", + TextSize = 25, + Font = @"Exo2.0-Light", + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + browser.Colour = colours.Pink; + } + } + + public enum SocialTab + { + [Description("Online Players")] + OnlinePlayers, + //[Description("Online Friends")] + //OnlineFriends, + //[Description("Online Team Members")] + //OnlineTeamMembers, + //[Description("Chat Channels")] + //ChatChannels, + } +} diff --git a/osu.Game/Overlays/SocialOverlay.cs b/osu.Game/Overlays/SocialOverlay.cs new file mode 100644 index 0000000000..97c27a9ea9 --- /dev/null +++ b/osu.Game/Overlays/SocialOverlay.cs @@ -0,0 +1,109 @@ +// 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.Allocation; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.SearchableList; +using osu.Game.Overlays.Social; +using osu.Game.Users; + +namespace osu.Game.Overlays +{ + public class SocialOverlay : SearchableListOverlay, IOnlineComponent + { + private readonly FillFlowContainer panelFlow; + + protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b"); + protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"672b51"); + protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"5c2648"); + + protected override SearchableListHeader CreateHeader() => new Header(); + protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); + + private IEnumerable users; + public IEnumerable Users + { + get { return users; } + set + { + if (users?.Equals(value) ?? false) return; + users = value; + + if (users == null) + panelFlow.Clear(); + else + { + panelFlow.Children = users.Select(u => + { + var p = new UserPanel(u) { Width = 300 }; + p.Status.BindTo(u.Status); + return p; + }); + } + } + } + + public SocialOverlay() + { + FirstWaveColour = OsuColour.FromHex(@"cb5fa0"); + SecondWaveColour = OsuColour.FromHex(@"b04384"); + ThirdWaveColour = OsuColour.FromHex(@"9b2b6e"); + FourthWaveColour = OsuColour.FromHex(@"6d214d"); + + ScrollFlow.Children = new[] + { + panelFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 20 }, + Spacing = new Vector2(10f), + }, + }; + } + + [BackgroundDependencyLoader] + private void load(APIAccess api) + { + if (Users == null) + reloadUsers(api); + } + + private void reloadUsers(APIAccess api) + { + Users = null; + + // no this is not the correct data source, but it's something. + var request = new GetUsersRequest(); + request.Success += res => Users = res.Select(e => e.User); + api.Queue(request); + } + + public void APIStateChanged(APIAccess api, APIState state) + { + switch (state) + { + case APIState.Online: + reloadUsers(api); + break; + default: + Users = null; + break; + } + } + } + + public enum SortDirection + { + Descending, + Ascending, + } +} diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 43c3cd32f2..158992fef8 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -63,6 +63,7 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { + new ToolbarSocialButton(), new ToolbarChatButton(), new ToolbarMusicButton(), new ToolbarButton diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs new file mode 100644 index 0000000000..ed36fd8f9e --- /dev/null +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -0,0 +1,22 @@ +// 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.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + internal class ToolbarSocialButton : ToolbarOverlayToggleButton + { + public ToolbarSocialButton() + { + Icon = FontAwesome.fa_users; + } + + [BackgroundDependencyLoader] + private void load(SocialOverlay chat) + { + StateContainer = chat; + } + } +} \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 759bd2d6f0..a83945d5db 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -43,7 +43,7 @@ $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll - $(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll + $(SolutionDir)\packages\sharpcompress.0.15.2\lib\net45\SharpCompress.dll $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll @@ -76,6 +76,7 @@ + @@ -88,6 +89,7 @@ + @@ -453,16 +455,24 @@ - - + + + + + + + + + +