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
|
|
|
|
|
2017-08-13 23:41:13 +08:00
|
|
|
|
using System;
|
2017-03-13 20:33:25 +08:00
|
|
|
|
using System.Collections.Generic;
|
2022-01-29 11:18:34 +08:00
|
|
|
|
using System.Diagnostics;
|
2018-12-14 18:51:27 +08:00
|
|
|
|
using System.Linq;
|
2019-01-07 18:28:46 +08:00
|
|
|
|
using System.Threading;
|
2017-08-13 23:41:13 +08:00
|
|
|
|
using osu.Framework.Allocation;
|
2020-10-22 13:19:12 +08:00
|
|
|
|
using osu.Framework.Bindables;
|
2022-01-29 11:18:34 +08:00
|
|
|
|
using osu.Framework.Development;
|
2017-03-16 00:57:41 +08:00
|
|
|
|
using osu.Framework.Extensions.Color4Extensions;
|
2017-03-04 15:37:34 +08:00
|
|
|
|
using osu.Framework.Graphics;
|
2017-03-16 00:57:41 +08:00
|
|
|
|
using osu.Framework.Graphics.Colour;
|
2017-03-04 15:37:34 +08:00
|
|
|
|
using osu.Framework.Graphics.Containers;
|
2020-03-07 05:12:02 +08:00
|
|
|
|
using osu.Framework.Graphics.Sprites;
|
2017-07-12 17:57:44 +08:00
|
|
|
|
using osu.Game.Graphics.Containers;
|
2019-12-17 11:25:28 +08:00
|
|
|
|
using osu.Game.Graphics.Cursor;
|
2017-06-12 15:40:53 +08:00
|
|
|
|
using osu.Game.Graphics.UserInterface;
|
2017-04-11 12:48:43 +08:00
|
|
|
|
using osu.Game.Online.API;
|
2020-01-05 01:26:31 +08:00
|
|
|
|
using osu.Game.Online.Placeholders;
|
2018-12-14 18:51:27 +08:00
|
|
|
|
using osuTK;
|
|
|
|
|
using osuTK.Graphics;
|
2022-03-02 13:14:44 +08:00
|
|
|
|
using osu.Game.Localisation;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
namespace osu.Game.Online.Leaderboards
|
2017-03-04 15:37:34 +08:00
|
|
|
|
{
|
2022-01-28 20:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// A leaderboard which displays a scrolling list of top scores, along with a single "user best"
|
|
|
|
|
/// for the local user.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TScope">The scope of the leaderboard (ie. global or local).</typeparam>
|
|
|
|
|
/// <typeparam name="TScoreInfo">The score model class.</typeparam>
|
2022-01-28 20:38:23 +08:00
|
|
|
|
public abstract partial class Leaderboard<TScope, TScoreInfo> : CompositeDrawable
|
2017-03-04 15:37:34 +08:00
|
|
|
|
{
|
2022-01-30 15:16:00 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The currently displayed scores.
|
|
|
|
|
/// </summary>
|
2022-09-13 16:49:53 +08:00
|
|
|
|
public IBindableList<TScoreInfo> Scores => scores;
|
|
|
|
|
|
|
|
|
|
private readonly BindableList<TScoreInfo> scores = new BindableList<TScoreInfo>();
|
2022-01-30 15:16:00 +08:00
|
|
|
|
|
2022-01-28 22:17:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Whether the current scope should refetch in response to changes in API connectivity state.
|
|
|
|
|
/// </summary>
|
2022-01-28 21:28:13 +08:00
|
|
|
|
protected abstract bool IsOnlineScope { get; }
|
|
|
|
|
|
2017-12-22 21:41:18 +08:00
|
|
|
|
private const double fade_duration = 300;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2019-06-14 14:55:32 +08:00
|
|
|
|
private readonly OsuScrollContainer scrollContainer;
|
2017-11-26 17:33:49 +08:00
|
|
|
|
private readonly Container placeholderContainer;
|
2022-01-30 15:16:00 +08:00
|
|
|
|
private readonly UserTopScoreContainer<TScoreInfo> userScoreContainer;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-09-26 15:12:47 +08:00
|
|
|
|
private FillFlowContainer<LeaderboardScore>? scoreFlowContainer;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2020-02-21 14:33:31 +08:00
|
|
|
|
private readonly LoadingSpinner loading;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-09-26 15:12:47 +08:00
|
|
|
|
private CancellationTokenSource? currentFetchCancellationSource;
|
|
|
|
|
private CancellationTokenSource? currentScoresAsyncLoadCancellationSource;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-09-26 15:12:47 +08:00
|
|
|
|
private APIRequest? fetchScoresRequest;
|
2022-01-28 20:33:22 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
private LeaderboardState state;
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
2022-01-28 20:33:22 +08:00
|
|
|
|
[Resolved(CanBeNull = true)]
|
2022-09-26 15:12:47 +08:00
|
|
|
|
private IAPIProvider? api { get; set; }
|
2022-01-28 20:33:22 +08:00
|
|
|
|
|
|
|
|
|
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
|
|
|
|
|
2022-09-26 15:12:47 +08:00
|
|
|
|
private TScope scope = default!;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
public TScope Scope
|
2017-11-20 13:06:26 +08:00
|
|
|
|
{
|
2019-02-28 12:58:19 +08:00
|
|
|
|
get => scope;
|
2017-11-20 13:06:26 +08:00
|
|
|
|
set
|
|
|
|
|
{
|
2019-11-13 22:35:50 +08:00
|
|
|
|
if (EqualityComparer<TScope>.Default.Equals(value, scope))
|
2018-01-06 02:13:54 +08:00
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2017-11-20 13:06:26 +08:00
|
|
|
|
scope = value;
|
2022-01-28 20:22:09 +08:00
|
|
|
|
RefetchScores();
|
2017-12-21 17:47:37 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
protected Leaderboard()
|
2017-03-04 15:37:34 +08:00
|
|
|
|
{
|
2019-09-19 14:23:33 +08:00
|
|
|
|
InternalChildren = new Drawable[]
|
2017-03-04 15:37:34 +08:00
|
|
|
|
{
|
2020-07-12 07:22:01 +08:00
|
|
|
|
new OsuContextMenuContainer
|
2017-03-04 15:37:34 +08:00
|
|
|
|
{
|
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2020-07-13 13:04:00 +08:00
|
|
|
|
Masking = true,
|
2020-07-12 07:22:01 +08:00
|
|
|
|
Child = new GridContainer
|
2019-09-19 13:52:31 +08:00
|
|
|
|
{
|
2020-07-12 07:22:01 +08:00
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
|
RowDimensions = new[]
|
2019-09-19 13:52:31 +08:00
|
|
|
|
{
|
2020-07-12 07:22:01 +08:00
|
|
|
|
new Dimension(),
|
|
|
|
|
new Dimension(GridSizeMode.AutoSize),
|
|
|
|
|
},
|
|
|
|
|
Content = new[]
|
|
|
|
|
{
|
|
|
|
|
new Drawable[]
|
2019-09-19 13:52:31 +08:00
|
|
|
|
{
|
2020-07-12 07:22:01 +08:00
|
|
|
|
scrollContainer = new OsuScrollContainer
|
2019-12-17 11:25:28 +08:00
|
|
|
|
{
|
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
|
ScrollbarVisible = false,
|
|
|
|
|
}
|
2020-07-12 07:22:01 +08:00
|
|
|
|
},
|
|
|
|
|
new Drawable[]
|
2019-09-19 13:52:31 +08:00
|
|
|
|
{
|
2022-01-31 12:45:49 +08:00
|
|
|
|
userScoreContainer = new UserTopScoreContainer<TScoreInfo>(CreateDrawableTopScore)
|
2019-09-19 13:52:31 +08:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2017-03-04 15:37:34 +08:00
|
|
|
|
},
|
2020-02-21 14:33:31 +08:00
|
|
|
|
loading = new LoadingSpinner(),
|
2017-11-26 15:20:20 +08:00
|
|
|
|
placeholderContainer = new Container
|
2017-11-20 19:37:41 +08:00
|
|
|
|
{
|
2017-12-22 21:41:18 +08:00
|
|
|
|
RelativeSizeAxes = Axes.Both
|
2017-11-24 21:10:52 +08:00
|
|
|
|
},
|
2017-03-04 15:37:34 +08:00
|
|
|
|
};
|
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-28 20:33:22 +08:00
|
|
|
|
protected override void LoadComplete()
|
2017-04-11 12:48:43 +08:00
|
|
|
|
{
|
2022-01-28 20:33:22 +08:00
|
|
|
|
base.LoadComplete();
|
2019-07-21 08:07:27 +08:00
|
|
|
|
|
2022-01-28 20:33:22 +08:00
|
|
|
|
if (api != null)
|
2019-07-09 22:59:38 +08:00
|
|
|
|
{
|
2022-01-28 20:33:22 +08:00
|
|
|
|
apiState.BindTo(api.State);
|
|
|
|
|
apiState.BindValueChanged(state =>
|
|
|
|
|
{
|
|
|
|
|
switch (state.NewValue)
|
|
|
|
|
{
|
|
|
|
|
case APIState.Online:
|
|
|
|
|
case APIState.Offline:
|
|
|
|
|
if (IsOnlineScope)
|
|
|
|
|
RefetchScores();
|
2019-07-21 08:07:27 +08:00
|
|
|
|
|
2022-01-28 20:33:22 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-07-09 22:59:38 +08:00
|
|
|
|
}
|
2022-01-28 20:33:22 +08:00
|
|
|
|
|
|
|
|
|
RefetchScores();
|
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-30 02:37:57 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Perform a full refetch of scores using current criteria.
|
|
|
|
|
/// </summary>
|
2022-01-28 20:22:09 +08:00
|
|
|
|
public void RefetchScores() => Scheduler.AddOnce(refetchScores);
|
2021-06-14 13:26:40 +08:00
|
|
|
|
|
2024-01-06 19:44:07 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Clear all scores from the display.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ClearScores()
|
|
|
|
|
{
|
|
|
|
|
cancelPendingWork();
|
|
|
|
|
SetScores(null);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-30 02:37:57 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Call when a retrieval or display failure happened to show a relevant message to the user.
|
|
|
|
|
/// </summary>
|
2022-01-31 00:12:03 +08:00
|
|
|
|
/// <param name="state">The state to display.</param>
|
|
|
|
|
protected void SetErrorState(LeaderboardState state)
|
2022-01-30 02:37:57 +08:00
|
|
|
|
{
|
2022-01-31 00:12:03 +08:00
|
|
|
|
switch (state)
|
2022-01-30 02:37:57 +08:00
|
|
|
|
{
|
2022-01-31 00:12:03 +08:00
|
|
|
|
case LeaderboardState.NoScores:
|
|
|
|
|
case LeaderboardState.Retrieving:
|
|
|
|
|
case LeaderboardState.Success:
|
|
|
|
|
throw new InvalidOperationException($"State {state} cannot be set by a leaderboard implementation.");
|
2022-01-30 02:37:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-22 19:35:26 +08:00
|
|
|
|
Debug.Assert(!scores.Any());
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
setState(state);
|
2022-01-30 02:37:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-30 15:16:00 +08:00
|
|
|
|
/// <summary>
|
2022-01-30 22:18:40 +08:00
|
|
|
|
/// Call when retrieved scores are ready to be displayed.
|
2022-01-30 15:16:00 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="scores">The scores to display.</param>
|
|
|
|
|
/// <param name="userScore">The user top score, if any.</param>
|
2022-09-26 15:12:47 +08:00
|
|
|
|
protected void SetScores(IEnumerable<TScoreInfo>? scores, TScoreInfo? userScore = default)
|
2022-01-30 15:16:00 +08:00
|
|
|
|
{
|
2022-09-13 16:49:53 +08:00
|
|
|
|
this.scores.Clear();
|
|
|
|
|
if (scores != null)
|
|
|
|
|
this.scores.AddRange(scores);
|
|
|
|
|
|
2022-09-26 15:02:33 +08:00
|
|
|
|
// Non-delayed schedule may potentially run inline (due to IsMainThread check passing) after leaderboard is disposed.
|
|
|
|
|
// This is guarded against in BeatmapLeaderboard via web request cancellation, but let's be extra safe.
|
|
|
|
|
if (!IsDisposed)
|
|
|
|
|
{
|
|
|
|
|
// Schedule needs to be non-delayed here for the weird logic in refetchScores to work.
|
|
|
|
|
// If it is removed, the placeholder will be incorrectly updated to "no scores" rather than "retrieving".
|
|
|
|
|
// This whole flow should be refactored in the future.
|
|
|
|
|
Scheduler.Add(applyNewScores, false);
|
|
|
|
|
}
|
2022-01-30 15:16:00 +08:00
|
|
|
|
|
2022-09-20 16:01:44 +08:00
|
|
|
|
void applyNewScores()
|
|
|
|
|
{
|
|
|
|
|
userScoreContainer.Score.Value = userScore;
|
|
|
|
|
|
|
|
|
|
if (userScore == null)
|
|
|
|
|
userScoreContainer.Hide();
|
|
|
|
|
else
|
|
|
|
|
userScoreContainer.Show();
|
2022-01-30 15:16:00 +08:00
|
|
|
|
|
2022-09-20 16:01:44 +08:00
|
|
|
|
updateScoresDrawables();
|
|
|
|
|
}
|
2022-01-30 15:16:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-28 20:53:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Performs a fetch/refresh of scores to be displayed.
|
|
|
|
|
/// </summary>
|
2022-01-28 22:14:26 +08:00
|
|
|
|
/// <param name="cancellationToken"></param>
|
2022-01-28 20:53:12 +08:00
|
|
|
|
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
|
2022-09-26 15:12:47 +08:00
|
|
|
|
protected abstract APIRequest? FetchScores(CancellationToken cancellationToken);
|
2022-01-28 20:53:12 +08:00
|
|
|
|
|
|
|
|
|
protected abstract LeaderboardScore CreateDrawableScore(TScoreInfo model, int index);
|
|
|
|
|
|
|
|
|
|
protected abstract LeaderboardScore CreateDrawableTopScore(TScoreInfo model);
|
|
|
|
|
|
2022-01-28 20:22:09 +08:00
|
|
|
|
private void refetchScores()
|
2017-04-11 12:48:43 +08:00
|
|
|
|
{
|
2022-01-29 11:18:34 +08:00
|
|
|
|
Debug.Assert(ThreadSafety.IsUpdateThread);
|
|
|
|
|
|
2024-01-06 19:44:07 +08:00
|
|
|
|
ClearScores();
|
2022-01-31 00:12:03 +08:00
|
|
|
|
setState(LeaderboardState.Retrieving);
|
2022-01-28 20:47:28 +08:00
|
|
|
|
|
2022-01-28 22:14:26 +08:00
|
|
|
|
currentFetchCancellationSource = new CancellationTokenSource();
|
|
|
|
|
|
|
|
|
|
fetchScoresRequest = FetchScores(currentFetchCancellationSource.Token);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-28 22:14:26 +08:00
|
|
|
|
if (fetchScoresRequest == null)
|
2022-01-28 20:47:28 +08:00
|
|
|
|
return;
|
2018-12-14 18:51:27 +08:00
|
|
|
|
|
2022-01-28 22:14:26 +08:00
|
|
|
|
fetchScoresRequest.Failure += e => Schedule(() =>
|
2022-01-28 20:47:28 +08:00
|
|
|
|
{
|
2022-01-28 22:14:26 +08:00
|
|
|
|
if (e is OperationCanceledException || currentFetchCancellationSource.IsCancellationRequested)
|
2018-12-14 18:51:27 +08:00
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
SetErrorState(LeaderboardState.NetworkFailure);
|
2018-06-26 15:33:22 +08:00
|
|
|
|
});
|
2022-01-28 20:47:28 +08:00
|
|
|
|
|
2022-01-28 22:14:26 +08:00
|
|
|
|
api?.Queue(fetchScoresRequest);
|
2017-04-11 12:48:43 +08:00
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-28 20:33:22 +08:00
|
|
|
|
private void cancelPendingWork()
|
|
|
|
|
{
|
2022-01-28 22:14:26 +08:00
|
|
|
|
currentFetchCancellationSource?.Cancel();
|
2022-01-29 11:18:34 +08:00
|
|
|
|
currentScoresAsyncLoadCancellationSource?.Cancel();
|
2022-01-28 22:14:26 +08:00
|
|
|
|
fetchScoresRequest?.Cancel();
|
2022-01-28 20:33:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 01:12:36 +08:00
|
|
|
|
private void updateScoresDrawables()
|
2022-01-28 21:28:13 +08:00
|
|
|
|
{
|
2022-01-29 01:12:36 +08:00
|
|
|
|
currentScoresAsyncLoadCancellationSource?.Cancel();
|
|
|
|
|
|
|
|
|
|
scoreFlowContainer?
|
|
|
|
|
.FadeOut(fade_duration, Easing.OutQuint)
|
|
|
|
|
.Expire();
|
|
|
|
|
scoreFlowContainer = null;
|
|
|
|
|
|
2022-09-22 19:35:26 +08:00
|
|
|
|
if (!scores.Any())
|
2022-01-28 21:28:13 +08:00
|
|
|
|
{
|
2022-01-31 00:12:03 +08:00
|
|
|
|
setState(LeaderboardState.NoScores);
|
2022-01-28 21:28:13 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-29 01:12:36 +08:00
|
|
|
|
LoadComponentAsync(new FillFlowContainer<LeaderboardScore>
|
2022-01-28 21:28:13 +08:00
|
|
|
|
{
|
|
|
|
|
RelativeSizeAxes = Axes.X,
|
|
|
|
|
AutoSizeAxes = Axes.Y,
|
|
|
|
|
Spacing = new Vector2(0f, 5f),
|
|
|
|
|
Padding = new MarginPadding { Top = 10, Bottom = 5 },
|
|
|
|
|
ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1))
|
2022-01-29 01:12:36 +08:00
|
|
|
|
}, newFlow =>
|
2022-01-28 21:28:13 +08:00
|
|
|
|
{
|
2022-01-31 00:12:03 +08:00
|
|
|
|
setState(LeaderboardState.Success);
|
|
|
|
|
|
2022-01-29 01:12:36 +08:00
|
|
|
|
scrollContainer.Add(scoreFlowContainer = newFlow);
|
2022-01-28 21:28:13 +08:00
|
|
|
|
|
2022-01-30 01:36:50 +08:00
|
|
|
|
double delay = 0;
|
2022-01-28 21:28:13 +08:00
|
|
|
|
|
2024-01-23 04:26:16 +08:00
|
|
|
|
foreach (var s in scoreFlowContainer)
|
2022-01-28 21:28:13 +08:00
|
|
|
|
{
|
2022-01-30 01:36:50 +08:00
|
|
|
|
using (s.BeginDelayedSequence(delay))
|
2022-01-28 21:28:13 +08:00
|
|
|
|
s.Show();
|
2022-01-30 01:36:50 +08:00
|
|
|
|
|
|
|
|
|
delay += 50;
|
2022-01-28 21:28:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scrollContainer.ScrollToStart(false);
|
2022-01-29 11:18:34 +08:00
|
|
|
|
}, (currentScoresAsyncLoadCancellationSource = new CancellationTokenSource()).Token);
|
2022-01-29 01:12:36 +08:00
|
|
|
|
}
|
2022-01-28 21:28:13 +08:00
|
|
|
|
|
2022-01-30 02:37:57 +08:00
|
|
|
|
#region Placeholder handling
|
|
|
|
|
|
2022-09-26 15:12:47 +08:00
|
|
|
|
private Placeholder? placeholder;
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
private void setState(LeaderboardState state)
|
2017-12-20 22:30:52 +08:00
|
|
|
|
{
|
2022-01-31 00:12:03 +08:00
|
|
|
|
if (state == this.state)
|
2017-12-21 17:47:37 +08:00
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
if (state == LeaderboardState.Retrieving)
|
|
|
|
|
loading.Show();
|
|
|
|
|
else
|
|
|
|
|
loading.Hide();
|
|
|
|
|
|
|
|
|
|
this.state = state;
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
|
|
|
|
placeholder?.FadeOut(150, Easing.OutQuint).Expire();
|
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
placeholder = getPlaceholderFor(state);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2017-12-22 21:41:18 +08:00
|
|
|
|
if (placeholder == null)
|
2017-12-21 17:47:37 +08:00
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-04-16 16:39:55 +08:00
|
|
|
|
placeholderContainer.Child = placeholder;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2017-12-22 21:41:18 +08:00
|
|
|
|
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
|
|
|
|
|
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
|
2022-01-30 02:37:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 15:12:47 +08:00
|
|
|
|
private Placeholder? getPlaceholderFor(LeaderboardState state)
|
2022-01-30 02:37:57 +08:00
|
|
|
|
{
|
2022-01-31 00:12:03 +08:00
|
|
|
|
switch (state)
|
2022-01-30 02:37:57 +08:00
|
|
|
|
{
|
2022-01-31 00:12:03 +08:00
|
|
|
|
case LeaderboardState.NetworkFailure:
|
2022-03-02 13:14:44 +08:00
|
|
|
|
return new ClickablePlaceholder(LeaderboardStrings.CouldntFetchScores, FontAwesome.Solid.Sync)
|
2022-01-30 02:37:57 +08:00
|
|
|
|
{
|
|
|
|
|
Action = RefetchScores
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
case LeaderboardState.NoneSelected:
|
2022-03-02 13:14:44 +08:00
|
|
|
|
return new MessagePlaceholder(LeaderboardStrings.PleaseSelectABeatmap);
|
2018-04-16 16:39:55 +08:00
|
|
|
|
|
2022-03-02 13:10:59 +08:00
|
|
|
|
case LeaderboardState.RulesetUnavailable:
|
2022-03-02 13:14:44 +08:00
|
|
|
|
return new MessagePlaceholder(LeaderboardStrings.LeaderboardsAreNotAvailableForThisRuleset);
|
2022-03-02 13:10:59 +08:00
|
|
|
|
|
|
|
|
|
case LeaderboardState.BeatmapUnavailable:
|
2022-03-02 13:14:44 +08:00
|
|
|
|
return new MessagePlaceholder(LeaderboardStrings.LeaderboardsAreNotAvailableForThisBeatmap);
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
case LeaderboardState.NoScores:
|
2022-03-02 13:14:44 +08:00
|
|
|
|
return new MessagePlaceholder(LeaderboardStrings.NoRecordsYet);
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
case LeaderboardState.NotLoggedIn:
|
2022-03-02 13:14:44 +08:00
|
|
|
|
return new LoginPlaceholder(LeaderboardStrings.PleaseSignInToViewOnlineLeaderboards);
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
case LeaderboardState.NotSupporter:
|
2022-03-02 13:14:44 +08:00
|
|
|
|
return new MessagePlaceholder(LeaderboardStrings.PleaseInvestInAnOsuSupporterTagToViewThisLeaderboard);
|
2022-01-30 02:37:57 +08:00
|
|
|
|
|
2022-01-31 00:12:03 +08:00
|
|
|
|
case LeaderboardState.Retrieving:
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
case LeaderboardState.Success:
|
2022-01-30 02:37:57 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
2017-12-20 22:30:52 +08:00
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-28 20:53:12 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Fade handling
|
|
|
|
|
|
2018-11-22 18:08:46 +08:00
|
|
|
|
protected override void UpdateAfterChildren()
|
2017-03-16 12:15:06 +08:00
|
|
|
|
{
|
2018-11-22 18:08:46 +08:00
|
|
|
|
base.UpdateAfterChildren();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2021-10-27 12:04:41 +08:00
|
|
|
|
float fadeBottom = scrollContainer.Current + scrollContainer.DrawHeight;
|
|
|
|
|
float fadeTop = scrollContainer.Current + LeaderboardScore.HEIGHT;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2017-03-19 20:49:29 +08:00
|
|
|
|
if (!scrollContainer.IsScrolledToEnd())
|
2018-12-27 14:30:02 +08:00
|
|
|
|
fadeBottom -= LeaderboardScore.HEIGHT;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-29 01:12:36 +08:00
|
|
|
|
if (scoreFlowContainer == null)
|
2018-01-06 02:13:54 +08:00
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2024-01-23 04:26:16 +08:00
|
|
|
|
foreach (var c in scoreFlowContainer)
|
2017-03-16 12:15:06 +08:00
|
|
|
|
{
|
2022-01-29 01:12:36 +08:00
|
|
|
|
float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scoreFlowContainer).Y;
|
2021-10-27 12:04:41 +08:00
|
|
|
|
float bottomY = topY + LeaderboardScore.HEIGHT;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-01-28 20:49:52 +08:00
|
|
|
|
bool requireBottomFade = bottomY >= fadeBottom;
|
2018-12-27 14:30:02 +08:00
|
|
|
|
|
2022-01-28 20:49:52 +08:00
|
|
|
|
if (!requireBottomFade)
|
2017-03-19 20:49:29 +08:00
|
|
|
|
c.Colour = Color4.White;
|
2018-12-27 14:30:02 +08:00
|
|
|
|
else if (topY > fadeBottom + LeaderboardScore.HEIGHT || bottomY < fadeTop - LeaderboardScore.HEIGHT)
|
2017-03-19 20:49:29 +08:00
|
|
|
|
c.Colour = Color4.Transparent;
|
2017-03-18 12:44:05 +08:00
|
|
|
|
else
|
|
|
|
|
{
|
2022-01-28 20:49:52 +08:00
|
|
|
|
if (bottomY - fadeBottom > 0)
|
2019-11-11 19:53:22 +08:00
|
|
|
|
{
|
2018-12-27 14:30:02 +08:00
|
|
|
|
c.Colour = ColourInfo.GradientVertical(
|
|
|
|
|
Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / LeaderboardScore.HEIGHT, 1)),
|
|
|
|
|
Color4.White.Opacity(Math.Min(1 - (bottomY - fadeBottom) / LeaderboardScore.HEIGHT, 1)));
|
2019-11-11 19:53:22 +08:00
|
|
|
|
}
|
2022-01-28 20:49:52 +08:00
|
|
|
|
else
|
2019-11-11 19:53:22 +08:00
|
|
|
|
{
|
2018-12-27 14:30:02 +08:00
|
|
|
|
c.Colour = ColourInfo.GradientVertical(
|
|
|
|
|
Color4.White.Opacity(Math.Min(1 - (fadeTop - topY) / LeaderboardScore.HEIGHT, 1)),
|
|
|
|
|
Color4.White.Opacity(Math.Min(1 - (fadeTop - bottomY) / LeaderboardScore.HEIGHT, 1)));
|
2019-11-11 19:53:22 +08:00
|
|
|
|
}
|
2017-03-18 12:44:05 +08:00
|
|
|
|
}
|
2017-03-16 12:15:06 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-14 18:51:27 +08:00
|
|
|
|
|
2022-01-28 20:53:12 +08:00
|
|
|
|
#endregion
|
2017-03-04 15:37:34 +08:00
|
|
|
|
}
|
|
|
|
|
}
|