2019-01-24 16:43:03 +08:00
|
|
|
|
// 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.
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
2019-02-28 13:55:45 +08:00
|
|
|
|
using Humanizer;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using osu.Framework.Allocation;
|
2019-02-21 18:04:31 +08:00
|
|
|
|
using osu.Framework.Bindables;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using osu.Framework.Graphics;
|
|
|
|
|
using osu.Framework.Graphics.Containers;
|
|
|
|
|
using osu.Framework.Threading;
|
2018-05-26 04:51:05 +08:00
|
|
|
|
using osu.Game.Audio;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
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;
|
2018-11-20 15:51:59 +08:00
|
|
|
|
using osuTK;
|
|
|
|
|
using osuTK.Graphics;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
namespace osu.Game.Overlays
|
|
|
|
|
{
|
2018-04-18 13:16:58 +08:00
|
|
|
|
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, BeatmapSearchCategory>
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
private const float panel_padding = 10f;
|
|
|
|
|
|
2020-02-14 21:14:00 +08:00
|
|
|
|
[Resolved]
|
|
|
|
|
private RulesetStore rulesets { get; set; }
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
private readonly FillFlowContainer resultCountsContainer;
|
|
|
|
|
private readonly OsuSpriteText resultCountsText;
|
|
|
|
|
private FillFlowContainer<DirectPanel> 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<DirectTab> CreateHeader() => new Header();
|
2018-04-18 13:16:58 +08:00
|
|
|
|
protected override SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory> CreateFilterControl() => new FilterControl();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
private IEnumerable<BeatmapSetInfo> beatmapSets;
|
|
|
|
|
|
|
|
|
|
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
|
|
|
|
{
|
2019-02-28 12:58:19 +08:00
|
|
|
|
get => beatmapSets;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
set
|
|
|
|
|
{
|
2019-11-14 19:23:19 +08:00
|
|
|
|
if (ReferenceEquals(beatmapSets, value)) return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
beatmapSets = value?.ToList();
|
|
|
|
|
|
|
|
|
|
if (beatmapSets == null) return;
|
|
|
|
|
|
|
|
|
|
var artists = new List<string>();
|
|
|
|
|
var songs = new List<string>();
|
|
|
|
|
var tags = new List<string>();
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
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
|
|
|
|
|
{
|
2019-02-28 12:58:19 +08:00
|
|
|
|
get => resultAmounts;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value == ResultAmounts) return;
|
2019-02-28 12:31:40 +08:00
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
resultAmounts = value;
|
|
|
|
|
|
|
|
|
|
updateResultCounts();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public DirectOverlay()
|
2020-01-24 17:24:35 +08:00
|
|
|
|
: base(OverlayColourScheme.Blue)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
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 ",
|
2019-02-12 12:04:46 +08:00
|
|
|
|
Font = OsuFont.GetFont(size: 15)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
},
|
|
|
|
|
resultCountsText = new OsuSpriteText
|
|
|
|
|
{
|
2019-02-12 12:04:46 +08:00
|
|
|
|
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Filter.Search.Current.ValueChanged += text =>
|
|
|
|
|
{
|
2019-11-28 22:07:46 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(text.NewValue))
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
};
|
2019-03-12 00:34:31 +08:00
|
|
|
|
((FilterControl)Filter).Ruleset.ValueChanged += _ => queueUpdateSearch();
|
2019-02-22 16:51:39 +08:00
|
|
|
|
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue);
|
2019-03-12 00:34:31 +08:00
|
|
|
|
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdateSearch();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
Header.Tabs.Current.ValueChanged += tab =>
|
|
|
|
|
{
|
2019-02-22 16:51:39 +08:00
|
|
|
|
if (tab.NewValue != DirectTab.Search)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
currentQuery.Value = string.Empty;
|
|
|
|
|
Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value;
|
2019-03-12 00:34:31 +08:00
|
|
|
|
queueUpdateSearch();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-03-12 00:34:31 +08:00
|
|
|
|
currentQuery.ValueChanged += text => queueUpdateSearch(!string.IsNullOrEmpty(text.NewValue));
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
currentQuery.BindTo(Filter.Search.Current);
|
|
|
|
|
|
2019-02-22 16:51:39 +08:00
|
|
|
|
Filter.Tabs.Current.ValueChanged += tab =>
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2019-02-22 16:51:39 +08:00
|
|
|
|
if (Header.Tabs.Current.Value != DirectTab.Search && tab.NewValue != (DirectSortCriteria)Header.Tabs.Current.Value)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
Header.Tabs.Current.Value = DirectTab.Search;
|
|
|
|
|
|
2019-03-12 00:34:31 +08:00
|
|
|
|
queueUpdateSearch();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
updateResultCounts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
2020-02-14 21:14:00 +08:00
|
|
|
|
private void load(OsuColour colours)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
resultCountsContainer.Colour = colours.Yellow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateResultCounts()
|
|
|
|
|
{
|
|
|
|
|
resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint);
|
|
|
|
|
if (ResultAmounts == null) return;
|
|
|
|
|
|
2019-02-28 13:55:45 +08:00
|
|
|
|
resultCountsText.Text = "Artist".ToQuantity(ResultAmounts.Artists) + ", " +
|
|
|
|
|
"Song".ToQuantity(ResultAmounts.Songs) + ", " +
|
|
|
|
|
"Tag".ToQuantity(ResultAmounts.Tags);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
};
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
default:
|
|
|
|
|
return new DirectListPanel(b);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LoadComponentAsync(newPanels, p =>
|
|
|
|
|
{
|
|
|
|
|
if (panels != null) ScrollFlow.Remove(panels);
|
|
|
|
|
ScrollFlow.Add(panels = newPanels);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-29 14:56:24 +08:00
|
|
|
|
protected override void PopIn()
|
|
|
|
|
{
|
|
|
|
|
base.PopIn();
|
|
|
|
|
|
|
|
|
|
// Queries are allowed to be run only on the first pop-in
|
|
|
|
|
if (getSetsRequest == null)
|
2019-03-12 00:34:31 +08:00
|
|
|
|
queueUpdateSearch();
|
2018-08-29 14:56:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
private SearchBeatmapSetsRequest getSetsRequest;
|
|
|
|
|
|
2019-03-12 00:34:31 +08:00
|
|
|
|
private readonly Bindable<string> currentQuery = new Bindable<string>(string.Empty);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
private ScheduledDelegate queryChangedDebounce;
|
2020-02-14 21:30:27 +08:00
|
|
|
|
|
2020-02-14 21:14:00 +08:00
|
|
|
|
[Resolved]
|
|
|
|
|
private PreviewTrackManager previewTrackManager { get; set; }
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2019-03-12 00:34:31 +08:00
|
|
|
|
private void queueUpdateSearch(bool queryTextChanged = false)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2019-03-12 00:34:31 +08:00
|
|
|
|
BeatmapSets = null;
|
|
|
|
|
ResultAmounts = null;
|
|
|
|
|
|
|
|
|
|
getSetsRequest?.Cancel();
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
queryChangedDebounce?.Cancel();
|
2019-03-12 00:34:31 +08:00
|
|
|
|
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100);
|
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2019-03-12 00:34:31 +08:00
|
|
|
|
private void updateSearch()
|
|
|
|
|
{
|
2018-08-29 14:56:24 +08:00
|
|
|
|
if (!IsLoaded)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-06-11 13:28:52 +08:00
|
|
|
|
if (State.Value == Visibility.Hidden)
|
2018-08-29 14:56:24 +08:00
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2019-05-14 13:18:06 +08:00
|
|
|
|
if (API == null)
|
2018-08-29 14:56:24 +08:00
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-06-22 11:35:43 +08:00
|
|
|
|
previewTrackManager.StopAnyPlaying(this);
|
2018-06-07 19:40:41 +08:00
|
|
|
|
|
2019-03-12 00:34:31 +08:00
|
|
|
|
getSetsRequest = new SearchBeatmapSetsRequest(
|
|
|
|
|
currentQuery.Value,
|
2018-04-13 17:19:50 +08:00
|
|
|
|
((FilterControl)Filter).Ruleset.Value,
|
|
|
|
|
Filter.DisplayStyleControl.Dropdown.Current.Value,
|
|
|
|
|
Filter.Tabs.Current.Value); //todo: sort direction (?)
|
|
|
|
|
|
|
|
|
|
getSetsRequest.Success += response =>
|
|
|
|
|
{
|
|
|
|
|
Task.Run(() =>
|
|
|
|
|
{
|
2018-10-23 17:04:38 +08:00
|
|
|
|
var sets = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
// may not need scheduling; loads async internally.
|
|
|
|
|
Schedule(() =>
|
|
|
|
|
{
|
|
|
|
|
BeatmapSets = sets;
|
|
|
|
|
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2019-05-14 13:18:06 +08:00
|
|
|
|
API.Queue(getSetsRequest);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|