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;
|
|
|
|
|
using System.Collections.Generic;
|
2018-12-14 18:51:27 +08:00
|
|
|
|
using System.Linq;
|
2019-01-07 18:28:46 +08:00
|
|
|
|
using System.Threading;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using osu.Framework.Allocation;
|
2020-10-22 13:19:12 +08:00
|
|
|
|
using osu.Framework.Bindables;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using osu.Framework.Extensions.Color4Extensions;
|
|
|
|
|
using osu.Framework.Graphics;
|
|
|
|
|
using osu.Framework.Graphics.Colour;
|
|
|
|
|
using osu.Framework.Graphics.Containers;
|
2020-03-07 05:12:02 +08:00
|
|
|
|
using osu.Framework.Graphics.Sprites;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using osu.Framework.Threading;
|
|
|
|
|
using osu.Game.Graphics.Containers;
|
2019-12-17 11:25:28 +08:00
|
|
|
|
using osu.Game.Graphics.Cursor;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using osu.Game.Graphics.UserInterface;
|
|
|
|
|
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;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
namespace osu.Game.Online.Leaderboards
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2020-10-22 13:19:12 +08:00
|
|
|
|
public abstract class Leaderboard<TScope, TScoreInfo> : Container
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
private const double fade_duration = 300;
|
|
|
|
|
|
2019-06-14 14:55:32 +08:00
|
|
|
|
private readonly OsuScrollContainer scrollContainer;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
private readonly Container placeholderContainer;
|
2020-08-31 18:54:22 +08:00
|
|
|
|
private readonly UserTopScoreContainer<TScoreInfo> topScoreContainer;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-22 14:20:35 +08:00
|
|
|
|
private FillFlowContainer<LeaderboardScore> scrollFlow;
|
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
|
|
|
|
|
2018-04-16 16:39:03 +08:00
|
|
|
|
private ScheduledDelegate showScoresDelegate;
|
2019-01-07 18:28:46 +08:00
|
|
|
|
private CancellationTokenSource showScoresCancellationSource;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-07-01 17:17:02 +08:00
|
|
|
|
private bool scoresLoadedOnce;
|
|
|
|
|
|
2019-09-19 13:52:31 +08:00
|
|
|
|
private readonly Container content;
|
|
|
|
|
|
|
|
|
|
protected override Container<Drawable> Content => content;
|
|
|
|
|
|
2021-06-14 13:20:23 +08:00
|
|
|
|
private ICollection<TScoreInfo> scores;
|
2018-06-26 17:24:34 +08:00
|
|
|
|
|
2021-06-14 13:20:23 +08:00
|
|
|
|
public ICollection<TScoreInfo> Scores
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2019-02-28 12:58:19 +08:00
|
|
|
|
get => scores;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
scores = value;
|
|
|
|
|
|
2018-07-01 17:17:02 +08:00
|
|
|
|
scoresLoadedOnce = true;
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
scrollFlow?.FadeOut(fade_duration, Easing.OutQuint).Expire();
|
|
|
|
|
scrollFlow = null;
|
|
|
|
|
|
2019-01-07 18:31:05 +08:00
|
|
|
|
showScoresDelegate?.Cancel();
|
|
|
|
|
showScoresCancellationSource?.Cancel();
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
if (scores == null || !scores.Any())
|
2020-09-09 19:19:07 +08:00
|
|
|
|
{
|
|
|
|
|
loading.Hide();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
return;
|
2020-09-09 19:19:07 +08:00
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
// ensure placeholder is hidden when displaying scores
|
|
|
|
|
PlaceholderState = PlaceholderState.Successful;
|
|
|
|
|
|
2019-09-19 13:52:31 +08:00
|
|
|
|
var scoreFlow = CreateScoreFlow();
|
|
|
|
|
scoreFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1));
|
2018-04-16 16:39:03 +08:00
|
|
|
|
|
2019-07-17 16:39:04 +08:00
|
|
|
|
// schedule because we may not be loaded yet (LoadComponentAsync complains).
|
2019-09-19 13:52:31 +08:00
|
|
|
|
showScoresDelegate = Schedule(() => LoadComponentAsync(scoreFlow, _ =>
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2019-09-19 13:52:31 +08:00
|
|
|
|
scrollContainer.Add(scrollFlow = scoreFlow);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-04-16 16:39:03 +08:00
|
|
|
|
int i = 0;
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2018-12-27 14:30:02 +08:00
|
|
|
|
foreach (var s in scrollFlow.Children)
|
2019-11-11 19:53:22 +08:00
|
|
|
|
{
|
2018-04-16 16:39:03 +08:00
|
|
|
|
using (s.BeginDelayedSequence(i++ * 50, true))
|
|
|
|
|
s.Show();
|
2019-11-11 19:53:22 +08:00
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-04-16 16:39:03 +08:00
|
|
|
|
scrollContainer.ScrollTo(0f, false);
|
2020-09-09 19:19:07 +08:00
|
|
|
|
loading.Hide();
|
2019-07-17 16:39:04 +08:00
|
|
|
|
}, (showScoresCancellationSource = new CancellationTokenSource()).Token));
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-31 18:54:22 +08:00
|
|
|
|
public TScoreInfo TopScore
|
|
|
|
|
{
|
|
|
|
|
get => topScoreContainer.Score.Value;
|
|
|
|
|
set
|
|
|
|
|
{
|
2020-09-02 13:29:46 +08:00
|
|
|
|
topScoreContainer.Score.Value = value;
|
|
|
|
|
|
2020-08-31 18:54:22 +08:00
|
|
|
|
if (value == null)
|
|
|
|
|
topScoreContainer.Hide();
|
|
|
|
|
else
|
|
|
|
|
topScoreContainer.Show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-27 14:30:02 +08:00
|
|
|
|
protected virtual FillFlowContainer<LeaderboardScore> CreateScoreFlow()
|
|
|
|
|
=> new FillFlowContainer<LeaderboardScore>
|
|
|
|
|
{
|
|
|
|
|
RelativeSizeAxes = Axes.X,
|
|
|
|
|
AutoSizeAxes = Axes.Y,
|
|
|
|
|
Spacing = new Vector2(0f, 5f),
|
|
|
|
|
Padding = new MarginPadding { Top = 10, Bottom = 5 },
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
private TScope scope;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
public TScope Scope
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2019-02-28 12:58:19 +08:00
|
|
|
|
get => scope;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
set
|
|
|
|
|
{
|
2019-11-13 22:35:50 +08:00
|
|
|
|
if (EqualityComparer<TScope>.Default.Equals(value, scope))
|
2018-04-13 17:19:50 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
scope = value;
|
2021-06-14 13:26:40 +08:00
|
|
|
|
RefreshScores();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private PlaceholderState placeholderState;
|
|
|
|
|
|
2018-04-18 10:57:06 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Update the placeholder visibility.
|
|
|
|
|
/// Setting this to anything other than PlaceholderState.Successful will cancel all existing retrieval requests and hide scores.
|
|
|
|
|
/// </summary>
|
2018-04-13 17:19:50 +08:00
|
|
|
|
protected PlaceholderState PlaceholderState
|
|
|
|
|
{
|
2019-02-28 12:58:19 +08:00
|
|
|
|
get => placeholderState;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value != PlaceholderState.Successful)
|
|
|
|
|
{
|
2019-09-19 14:23:33 +08:00
|
|
|
|
Reset();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value == placeholderState)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (placeholderState = value)
|
|
|
|
|
{
|
|
|
|
|
case PlaceholderState.NetworkFailure:
|
2020-03-07 05:12:02 +08:00
|
|
|
|
replacePlaceholder(new ClickablePlaceholder(@"Couldn't fetch scores!", FontAwesome.Solid.Sync)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2021-06-14 13:26:40 +08:00
|
|
|
|
Action = RefreshScores
|
2018-04-13 17:19:50 +08:00
|
|
|
|
});
|
|
|
|
|
break;
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2019-09-05 10:56:21 +08:00
|
|
|
|
case PlaceholderState.NoneSelected:
|
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"Please select a beatmap!"));
|
|
|
|
|
break;
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
case PlaceholderState.Unavailable:
|
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!"));
|
|
|
|
|
break;
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
case PlaceholderState.NoScores:
|
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"No records yet!"));
|
|
|
|
|
break;
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
case PlaceholderState.NotLoggedIn:
|
2020-01-12 22:50:35 +08:00
|
|
|
|
replacePlaceholder(new LoginPlaceholder(@"Please sign in to view online leaderboards!"));
|
2018-04-13 17:19:50 +08:00
|
|
|
|
break;
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
case PlaceholderState.NotSupporter:
|
2018-06-28 08:57:55 +08:00
|
|
|
|
replacePlaceholder(new MessagePlaceholder(@"Please invest in an osu!supporter tag to view this leaderboard!"));
|
2018-04-13 17:19:50 +08:00
|
|
|
|
break;
|
2019-04-01 11:16:05 +08:00
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
default:
|
|
|
|
|
replacePlaceholder(null);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
protected Leaderboard()
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2019-09-19 14:23:33 +08:00
|
|
|
|
InternalChildren = new Drawable[]
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2020-07-12 07:22:01 +08:00
|
|
|
|
new OsuContextMenuContainer
|
2018-04-13 17:19:50 +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
|
|
|
|
{
|
2020-07-12 07:22:01 +08:00
|
|
|
|
content = new Container
|
|
|
|
|
{
|
|
|
|
|
AutoSizeAxes = Axes.Y,
|
|
|
|
|
RelativeSizeAxes = Axes.X,
|
2020-08-31 18:54:22 +08:00
|
|
|
|
Child = topScoreContainer = new UserTopScoreContainer<TScoreInfo>(CreateDrawableTopScore)
|
2020-07-12 07:22:01 +08:00
|
|
|
|
},
|
2019-09-19 13:52:31 +08:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2018-04-13 17:19:50 +08:00
|
|
|
|
},
|
2020-02-21 14:33:31 +08:00
|
|
|
|
loading = new LoadingSpinner(),
|
2018-04-13 17:19:50 +08:00
|
|
|
|
placeholderContainer = new Container
|
|
|
|
|
{
|
|
|
|
|
RelativeSizeAxes = Axes.Both
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-19 14:23:33 +08:00
|
|
|
|
protected virtual void Reset()
|
|
|
|
|
{
|
|
|
|
|
getScoresRequest?.Cancel();
|
|
|
|
|
getScoresRequest = null;
|
|
|
|
|
Scores = null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-14 21:14:00 +08:00
|
|
|
|
[Resolved(CanBeNull = true)]
|
|
|
|
|
private IAPIProvider api { get; set; }
|
2018-11-29 17:30:43 +08:00
|
|
|
|
|
2018-06-26 15:33:22 +08:00
|
|
|
|
private ScheduledDelegate pendingUpdateScores;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2020-10-22 13:19:12 +08:00
|
|
|
|
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
|
|
|
|
|
2020-02-14 21:14:00 +08:00
|
|
|
|
[BackgroundDependencyLoader]
|
|
|
|
|
private void load()
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2020-11-14 21:48:48 +08:00
|
|
|
|
if (api != null)
|
|
|
|
|
apiState.BindTo(api.State);
|
|
|
|
|
|
2020-10-22 13:19:12 +08:00
|
|
|
|
apiState.BindValueChanged(onlineStateChanged, true);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
private APIRequest getScoresRequest;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2019-07-21 09:42:40 +08:00
|
|
|
|
protected abstract bool IsOnlineScope { get; }
|
2019-07-21 08:07:27 +08:00
|
|
|
|
|
2020-10-22 13:19:12 +08:00
|
|
|
|
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2020-10-22 13:19:12 +08:00
|
|
|
|
switch (state.NewValue)
|
2019-07-09 22:59:38 +08:00
|
|
|
|
{
|
|
|
|
|
case APIState.Online:
|
|
|
|
|
case APIState.Offline:
|
2019-07-21 09:42:40 +08:00
|
|
|
|
if (IsOnlineScope)
|
2021-06-14 13:26:40 +08:00
|
|
|
|
RefreshScores();
|
2019-07-21 08:07:27 +08:00
|
|
|
|
|
2019-07-09 22:59:38 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2020-10-22 13:19:12 +08:00
|
|
|
|
});
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2021-06-14 13:26:40 +08:00
|
|
|
|
public void RefreshScores() => Scheduler.AddOnce(UpdateScores);
|
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
protected void UpdateScores()
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2018-07-01 17:17:02 +08:00
|
|
|
|
// don't display any scores or placeholder until the first Scores_Set has been called.
|
|
|
|
|
// this avoids scope changes flickering a "no scores" placeholder before initialisation of song select is finished.
|
|
|
|
|
if (!scoresLoadedOnce) return;
|
|
|
|
|
|
2018-06-26 15:33:22 +08:00
|
|
|
|
getScoresRequest?.Cancel();
|
|
|
|
|
getScoresRequest = null;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-06-26 15:33:22 +08:00
|
|
|
|
pendingUpdateScores?.Cancel();
|
|
|
|
|
pendingUpdateScores = Schedule(() =>
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2018-06-26 15:33:22 +08:00
|
|
|
|
PlaceholderState = PlaceholderState.Retrieving;
|
|
|
|
|
loading.Show();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-14 18:51:27 +08:00
|
|
|
|
getScoresRequest = FetchScores(scores => Schedule(() =>
|
2018-06-26 15:33:22 +08:00
|
|
|
|
{
|
2021-06-14 13:20:23 +08:00
|
|
|
|
Scores = scores.ToArray();
|
2018-06-26 15:33:22 +08:00
|
|
|
|
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
2018-12-14 18:51:27 +08:00
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if (getScoresRequest == null)
|
|
|
|
|
return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-06-26 15:33:22 +08:00
|
|
|
|
getScoresRequest.Failure += e => Schedule(() =>
|
|
|
|
|
{
|
|
|
|
|
if (e is OperationCanceledException)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PlaceholderState = PlaceholderState.NetworkFailure;
|
|
|
|
|
});
|
2018-04-16 16:39:03 +08:00
|
|
|
|
|
2020-11-14 21:48:48 +08:00
|
|
|
|
api?.Queue(getScoresRequest);
|
2018-06-26 15:33:22 +08:00
|
|
|
|
});
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-05 17:48:59 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Performs a fetch/refresh of scores to be displayed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="scoresCallback">A callback which should be called when fetching is completed. Scheduling is not required.</param>
|
|
|
|
|
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
|
2019-12-10 21:04:26 +08:00
|
|
|
|
protected abstract APIRequest FetchScores(Action<IEnumerable<TScoreInfo>> scoresCallback);
|
2018-12-14 18:51:27 +08:00
|
|
|
|
|
2018-04-16 16:39:55 +08:00
|
|
|
|
private Placeholder currentPlaceholder;
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
private void replacePlaceholder(Placeholder placeholder)
|
|
|
|
|
{
|
2018-04-16 16:39:55 +08:00
|
|
|
|
if (placeholder != null && placeholder.Equals(currentPlaceholder))
|
2018-04-13 17:19:50 +08:00
|
|
|
|
return;
|
|
|
|
|
|
2018-04-16 16:39:55 +08:00
|
|
|
|
currentPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
if (placeholder == null)
|
2018-04-16 16:39:55 +08:00
|
|
|
|
{
|
|
|
|
|
currentPlaceholder = null;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
return;
|
2018-04-16 16:39:55 +08:00
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
|
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
|
|
|
|
|
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
|
2018-04-16 16:39:55 +08:00
|
|
|
|
|
|
|
|
|
currentPlaceholder = placeholder;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-27 14:30:02 +08:00
|
|
|
|
protected virtual bool FadeBottom => true;
|
2018-12-27 16:29:55 +08:00
|
|
|
|
protected virtual bool FadeTop => false;
|
2018-12-27 14:30:02 +08:00
|
|
|
|
|
2018-11-22 18:08:46 +08:00
|
|
|
|
protected override void UpdateAfterChildren()
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
2018-11-22 18:08:46 +08:00
|
|
|
|
base.UpdateAfterChildren();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2018-12-27 14:30:02 +08:00
|
|
|
|
var fadeBottom = scrollContainer.Current + scrollContainer.DrawHeight;
|
|
|
|
|
var fadeTop = scrollContainer.Current + LeaderboardScore.HEIGHT;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
if (!scrollContainer.IsScrolledToEnd())
|
2018-12-27 14:30:02 +08:00
|
|
|
|
fadeBottom -= LeaderboardScore.HEIGHT;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
if (scrollFlow == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (var c in scrollFlow.Children)
|
|
|
|
|
{
|
|
|
|
|
var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y;
|
|
|
|
|
var bottomY = topY + LeaderboardScore.HEIGHT;
|
|
|
|
|
|
2018-12-27 14:30:02 +08:00
|
|
|
|
bool requireTopFade = FadeTop && topY <= fadeTop;
|
|
|
|
|
bool requireBottomFade = FadeBottom && bottomY >= fadeBottom;
|
|
|
|
|
|
|
|
|
|
if (!requireTopFade && !requireBottomFade)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
c.Colour = Color4.White;
|
2018-12-27 14:30:02 +08:00
|
|
|
|
else if (topY > fadeBottom + LeaderboardScore.HEIGHT || bottomY < fadeTop - LeaderboardScore.HEIGHT)
|
2018-04-13 17:19:50 +08:00
|
|
|
|
c.Colour = Color4.Transparent;
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-12-27 14:30:02 +08:00
|
|
|
|
if (bottomY - fadeBottom > 0 && FadeBottom)
|
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
|
|
|
|
}
|
2018-12-27 14:30:02 +08:00
|
|
|
|
else if (FadeTop)
|
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
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-14 18:51:27 +08:00
|
|
|
|
|
2019-12-10 21:04:26 +08:00
|
|
|
|
protected abstract LeaderboardScore CreateDrawableScore(TScoreInfo model, int index);
|
2020-08-31 18:54:22 +08:00
|
|
|
|
|
|
|
|
|
protected abstract LeaderboardScore CreateDrawableTopScore(TScoreInfo model);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|