mirror of
https://github.com/osukey/osukey.git
synced 2025-08-06 16:13:57 +09:00
Implement BeatmapListingSearchHandler component
This commit is contained in:
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Overlays.BeatmapListing;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
@ -13,6 +14,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(BeatmapListingOverlay),
|
typeof(BeatmapListingOverlay),
|
||||||
|
typeof(BeatmapListingSearchHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override bool UseOnlineAPI => true;
|
protected override bool UseOnlineAPI => true;
|
||||||
|
163
osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs
Normal file
163
osu.Game/Overlays/BeatmapListing/BeatmapListingSearchHandler.cs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
// 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Overlays.Direct;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.BeatmapListing
|
||||||
|
{
|
||||||
|
public class BeatmapListingSearchHandler : CompositeDrawable
|
||||||
|
{
|
||||||
|
public Action<List<BeatmapSetInfo>> SearchFinished;
|
||||||
|
public Action SearchStarted;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
|
private readonly BeatmapListingSearchSection searchSection;
|
||||||
|
private readonly BeatmapListingSortTabControl sortControl;
|
||||||
|
private readonly Box sortControlBackground;
|
||||||
|
|
||||||
|
private SearchBeatmapSetsRequest getSetsRequest;
|
||||||
|
|
||||||
|
public BeatmapListingSearchHandler()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 10),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Masking = true,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Radius = 3,
|
||||||
|
Offset = new Vector2(0f, 1f),
|
||||||
|
},
|
||||||
|
Child = searchSection = new BeatmapListingSearchSection(),
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 40,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
sortControlBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
sortControl = new BeatmapListingSortTabControl
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Margin = new MarginPadding { Left = 20 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
sortControlBackground.Colour = colourProvider.Background5;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
var sortCriteria = sortControl.Current;
|
||||||
|
var sortDirection = sortControl.SortDirection;
|
||||||
|
|
||||||
|
searchSection.Query.BindValueChanged(query =>
|
||||||
|
{
|
||||||
|
sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance;
|
||||||
|
sortDirection.Value = SortDirection.Descending;
|
||||||
|
queueUpdateSearch(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch());
|
||||||
|
searchSection.Category.BindValueChanged(_ => queueUpdateSearch());
|
||||||
|
searchSection.Genre.BindValueChanged(_ => queueUpdateSearch());
|
||||||
|
searchSection.Language.BindValueChanged(_ => queueUpdateSearch());
|
||||||
|
|
||||||
|
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
|
||||||
|
sortDirection.BindValueChanged(_ => queueUpdateSearch());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate queryChangedDebounce;
|
||||||
|
|
||||||
|
private void queueUpdateSearch(bool queryTextChanged = false)
|
||||||
|
{
|
||||||
|
SearchStarted?.Invoke();
|
||||||
|
|
||||||
|
getSetsRequest?.Cancel();
|
||||||
|
|
||||||
|
queryChangedDebounce?.Cancel();
|
||||||
|
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSearch()
|
||||||
|
{
|
||||||
|
getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value)
|
||||||
|
{
|
||||||
|
SearchCategory = searchSection.Category.Value,
|
||||||
|
SortCriteria = sortControl.Current.Value,
|
||||||
|
SortDirection = sortControl.SortDirection.Value,
|
||||||
|
Genre = searchSection.Genre.Value,
|
||||||
|
Language = searchSection.Language.Value
|
||||||
|
};
|
||||||
|
|
||||||
|
getSetsRequest.Success += response => Schedule(() => onSearchFinished(response));
|
||||||
|
|
||||||
|
api.Queue(getSetsRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSearchFinished(SearchBeatmapSetsResponse response)
|
||||||
|
{
|
||||||
|
var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
||||||
|
|
||||||
|
searchSection.BeatmapSet = response.Total == 0 ? null : beatmaps.First();
|
||||||
|
|
||||||
|
SearchFinished?.Invoke(beatmaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
getSetsRequest?.Cancel();
|
||||||
|
queryChangedDebounce?.Cancel();
|
||||||
|
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,23 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API.Requests;
|
|
||||||
using osu.Game.Overlays.BeatmapListing;
|
using osu.Game.Overlays.BeatmapListing;
|
||||||
using osu.Game.Overlays.Direct;
|
using osu.Game.Overlays.Direct;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
@ -30,14 +26,9 @@ namespace osu.Game.Overlays
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private PreviewTrackManager previewTrackManager { get; set; }
|
private PreviewTrackManager previewTrackManager { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private RulesetStore rulesets { get; set; }
|
|
||||||
|
|
||||||
private SearchBeatmapSetsRequest getSetsRequest;
|
|
||||||
|
|
||||||
private Drawable currentContent;
|
private Drawable currentContent;
|
||||||
private BeatmapListingSearchSection searchSection;
|
private LoadingLayer loadingLayer;
|
||||||
private BeatmapListingSortTabControl sortControl;
|
private Container panelTarget;
|
||||||
|
|
||||||
public BeatmapListingOverlay()
|
public BeatmapListingOverlay()
|
||||||
: base(OverlayColourScheme.Blue)
|
: base(OverlayColourScheme.Blue)
|
||||||
@ -63,27 +54,13 @@ namespace osu.Game.Overlays
|
|||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 10),
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new BeatmapListingHeader(),
|
||||||
|
new BeatmapListingSearchHandler
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y,
|
SearchStarted = onSearchStarted,
|
||||||
RelativeSizeAxes = Axes.X,
|
SearchFinished = onSearchFinished,
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Masking = true,
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Colour = Color4.Black.Opacity(0.25f),
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Radius = 3,
|
|
||||||
Offset = new Vector2(0f, 1f),
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new BeatmapListingHeader(),
|
|
||||||
searchSection = new BeatmapListingSearchSection(),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
@ -96,132 +73,41 @@ namespace osu.Game.Overlays
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = ColourProvider.Background4,
|
Colour = ColourProvider.Background4,
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
panelTarget = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Children = new Drawable[]
|
RelativeSizeAxes = Axes.X,
|
||||||
{
|
Padding = new MarginPadding { Horizontal = 20 }
|
||||||
new Container
|
},
|
||||||
{
|
loadingLayer = new LoadingLayer(panelTarget)
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 40,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourProvider.Background5
|
|
||||||
},
|
|
||||||
sortControl = new BeatmapListingSortTabControl
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Margin = new MarginPadding { Left = 20 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Padding = new MarginPadding { Horizontal = 20 },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
panelTarget = new Container
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
},
|
|
||||||
loadingLayer = new LoadingLayer(panelTarget),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
private CancellationTokenSource cancellationToken;
|
||||||
|
|
||||||
|
private void onSearchStarted()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
cancellationToken?.Cancel();
|
||||||
|
|
||||||
var sortCriteria = sortControl.Current;
|
|
||||||
var sortDirection = sortControl.SortDirection;
|
|
||||||
|
|
||||||
searchSection.Query.BindValueChanged(query =>
|
|
||||||
{
|
|
||||||
sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance;
|
|
||||||
sortDirection.Value = SortDirection.Descending;
|
|
||||||
queueUpdateSearch(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch());
|
|
||||||
searchSection.Category.BindValueChanged(_ => queueUpdateSearch());
|
|
||||||
searchSection.Genre.BindValueChanged(_ => queueUpdateSearch());
|
|
||||||
searchSection.Language.BindValueChanged(_ => queueUpdateSearch());
|
|
||||||
|
|
||||||
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
|
|
||||||
sortDirection.BindValueChanged(_ => queueUpdateSearch());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScheduledDelegate queryChangedDebounce;
|
|
||||||
|
|
||||||
private LoadingLayer loadingLayer;
|
|
||||||
private Container panelTarget;
|
|
||||||
|
|
||||||
private void queueUpdateSearch(bool queryTextChanged = false)
|
|
||||||
{
|
|
||||||
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);
|
previewTrackManager.StopAnyPlaying(this);
|
||||||
|
|
||||||
loadingLayer.Show();
|
if (panelTarget.Any())
|
||||||
|
loadingLayer.Show();
|
||||||
getSetsRequest = new SearchBeatmapSetsRequest(searchSection.Query.Value, searchSection.Ruleset.Value)
|
|
||||||
{
|
|
||||||
SearchCategory = searchSection.Category.Value,
|
|
||||||
SortCriteria = sortControl.Current.Value,
|
|
||||||
SortDirection = sortControl.SortDirection.Value,
|
|
||||||
Genre = searchSection.Genre.Value,
|
|
||||||
Language = searchSection.Language.Value
|
|
||||||
};
|
|
||||||
|
|
||||||
getSetsRequest.Success += response => Schedule(() => recreatePanels(response));
|
|
||||||
|
|
||||||
API.Queue(getSetsRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recreatePanels(SearchBeatmapSetsResponse response)
|
private void onSearchFinished(List<BeatmapSetInfo> beatmaps)
|
||||||
{
|
{
|
||||||
if (response.Total == 0)
|
if (!beatmaps.Any())
|
||||||
{
|
{
|
||||||
searchSection.BeatmapSet = null;
|
LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||||
LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
|
||||||
|
|
||||||
var newPanels = new FillFlowContainer<DirectPanel>
|
var newPanels = new FillFlowContainer<DirectPanel>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
@ -236,18 +122,14 @@ namespace osu.Game.Overlays
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadComponentAsync(newPanels, loaded =>
|
LoadComponentAsync(newPanels, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||||
{
|
|
||||||
addContentToPlaceholder(loaded);
|
|
||||||
searchSection.BeatmapSet = beatmaps.First();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addContentToPlaceholder(Drawable content)
|
private void addContentToPlaceholder(Drawable content)
|
||||||
{
|
{
|
||||||
loadingLayer.Hide();
|
loadingLayer.Hide();
|
||||||
|
|
||||||
Drawable lastContent = currentContent;
|
var lastContent = currentContent;
|
||||||
|
|
||||||
if (lastContent != null)
|
if (lastContent != null)
|
||||||
{
|
{
|
||||||
@ -266,9 +148,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
getSetsRequest?.Cancel();
|
cancellationToken?.Cancel();
|
||||||
queryChangedDebounce?.Cancel();
|
|
||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user