osu/osu.Game/Overlays/DirectOverlay.cs

299 lines
10 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Humanizer;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, BeatmapSearchCategory>
{
private const float panel_padding = 10f;
[Resolved]
private RulesetStore rulesets { get; set; }
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
private FillFlowContainer<DirectPanel> panels;
protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"485e74");
protected override Color4 TrianglesColourLight => Color4Extensions.FromHex(@"465b71");
protected override Color4 TrianglesColourDark => Color4Extensions.FromHex(@"3f5265");
protected override SearchableListHeader<DirectTab> CreateHeader() => new Header();
protected override SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory> CreateFilterControl() => new FilterControl();
private IEnumerable<BeatmapSetInfo> beatmapSets;
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get => beatmapSets;
set
{
if (ReferenceEquals(beatmapSets, value)) return;
beatmapSets = value?.ToList();
if (beatmapSets == null) return;
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in beatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
}
}
private ResultCounts resultAmounts;
public ResultCounts ResultAmounts
{
get => resultAmounts;
set
{
if (value == ResultAmounts) return;
resultAmounts = value;
updateResultCounts();
}
}
public DirectOverlay()
: base(OverlayColourScheme.Blue)
{
ScrollFlow.Children = new Drawable[]
{
resultCountsContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Top = 5 },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Found ",
Font = OsuFont.GetFont(size: 15)
},
resultCountsText = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
},
}
},
};
Filter.Search.Current.ValueChanged += text =>
{
if (!string.IsNullOrEmpty(text.NewValue))
{
Header.Tabs.Current.Value = DirectTab.Search;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked)
Filter.Tabs.Current.Value = DirectSortCriteria.Relevance;
}
else
{
Header.Tabs.Current.Value = DirectTab.NewestMaps;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance)
Filter.Tabs.Current.Value = DirectSortCriteria.Ranked;
}
};
((FilterControl)Filter).Ruleset.ValueChanged += _ => queueUpdateSearch();
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue);
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdateSearch();
Header.Tabs.Current.ValueChanged += tab =>
{
if (tab.NewValue != DirectTab.Search)
{
currentQuery.Value = string.Empty;
Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value;
queueUpdateSearch();
}
};
currentQuery.ValueChanged += text => queueUpdateSearch(!string.IsNullOrEmpty(text.NewValue));
currentQuery.BindTo(Filter.Search.Current);
Filter.Tabs.Current.ValueChanged += tab =>
{
if (Header.Tabs.Current.Value != DirectTab.Search && tab.NewValue != (DirectSortCriteria)Header.Tabs.Current.Value)
Header.Tabs.Current.Value = DirectTab.Search;
queueUpdateSearch();
};
updateResultCounts();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
resultCountsContainer.Colour = colours.Yellow;
}
private void updateResultCounts()
{
resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint);
if (ResultAmounts == null) return;
resultCountsText.Text = "Artist".ToQuantity(ResultAmounts.Artists) + ", " +
"Song".ToQuantity(ResultAmounts.Songs) + ", " +
"Tag".ToQuantity(ResultAmounts.Tags);
}
private void recreatePanels(PanelDisplayStyle displayStyle)
{
if (panels != null)
{
panels.FadeOut(200);
panels.Expire();
panels = null;
}
if (BeatmapSets == null) return;
var newPanels = new FillFlowContainer<DirectPanel>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(panel_padding),
Margin = new MarginPadding { Top = 10 },
ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
{
switch (displayStyle)
{
case PanelDisplayStyle.Grid:
return new DirectGridPanel(b)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
};
default:
return new DirectListPanel(b);
}
})
};
LoadComponentAsync(newPanels, p =>
{
if (panels != null) ScrollFlow.Remove(panels);
ScrollFlow.Add(panels = newPanels);
});
}
protected override void PopIn()
{
base.PopIn();
// Queries are allowed to be run only on the first pop-in
if (getSetsRequest == null)
queueUpdateSearch();
}
private SearchBeatmapSetsRequest getSetsRequest;
private readonly Bindable<string> currentQuery = new Bindable<string>(string.Empty);
private ScheduledDelegate queryChangedDebounce;
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
private void queueUpdateSearch(bool queryTextChanged = false)
{
BeatmapSets = null;
ResultAmounts = null;
getSetsRequest?.Cancel();
queryChangedDebounce?.Cancel();
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100);
}
private void updateSearch()
{
if (!IsLoaded)
return;
if (State.Value == Visibility.Hidden)
return;
if (API == null)
return;
previewTrackManager.StopAnyPlaying(this);
getSetsRequest = new SearchBeatmapSetsRequest(
currentQuery.Value,
((FilterControl)Filter).Ruleset.Value,
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)
getSetsRequest.Success += response =>
{
Task.Run(() =>
{
var sets = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
// may not need scheduling; loads async internally.
Schedule(() =>
{
BeatmapSets = sets;
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
});
});
};
API.Queue(getSetsRequest);
}
private int distinctCount(List<string> list) => list.Distinct().ToArray().Length;
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;
}
}
}
}