Merge branch 'master' into beatmap-serialization

This commit is contained in:
Dan Balasescu 2017-12-22 07:38:05 +09:00 committed by GitHub
commit a3d947301b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 526 additions and 47 deletions

View File

@ -93,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
float approxFollowCircleRadius = (float)(slider.Radius * 3); float approxFollowCircleRadius = (float)(slider.Radius * 3);
var computeVertex = new Action<double>(t => var computeVertex = new Action<double>(t =>
{ {
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value; var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value;
float dist = diff.Length; float dist = diff.Length;

View File

@ -6,14 +6,44 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
using osu.Framework.Allocation;
using OpenTK; using OpenTK;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[Description("PlaySongSelect leaderboard")] [Description("PlaySongSelect leaderboard")]
public class TestCaseLeaderboard : OsuTestCase public class TestCaseLeaderboard : OsuTestCase
{ {
private readonly Leaderboard leaderboard; private RulesetStore rulesets;
private readonly FailableLeaderboard leaderboard;
public TestCaseLeaderboard()
{
Add(leaderboard = new FailableLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = LeaderboardScope.Global,
});
AddStep(@"New Scores", newScores);
AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores));
AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure));
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
AddStep(@"Real beatmap", realBeatmap);
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
private void newScores() private void newScores()
{ {
@ -204,17 +234,44 @@ namespace osu.Game.Tests.Visual
leaderboard.Scores = scores; leaderboard.Scores = scores;
} }
public TestCaseLeaderboard() private void realBeatmap()
{ {
Add(leaderboard = new Leaderboard leaderboard.Beatmap = new BeatmapInfo
{ {
Origin = Anchor.Centre, StarDifficulty = 1.36,
Anchor = Anchor.Centre, Version = @"BASIC",
Size = new Vector2(550f, 450f), OnlineBeatmapID = 1113057,
}); Ruleset = rulesets.GetRuleset(0),
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 6.5f,
OverallDifficulty = 6.5f,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 115000,
CircleCount = 265,
SliderCount = 71,
PlayCount = 47906,
PassCount = 19899,
},
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 11),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
};
}
AddStep(@"New Scores", newScores); private class FailableLeaderboard : Leaderboard
newScores(); {
public void SetRetrievalState(PlaceholderState state)
{
PlaceholderState = state;
}
} }
} }
} }

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays; using osu.Game.Overlays;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -8,16 +10,30 @@ namespace osu.Game.Tests.Visual
public class TestCaseSettings : OsuTestCase public class TestCaseSettings : OsuTestCase
{ {
private readonly SettingsOverlay settings; private readonly SettingsOverlay settings;
private readonly DialogOverlay dialogOverlay;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
public TestCaseSettings() public TestCaseSettings()
{ {
Children = new[] { settings = new MainSettings() }; settings = new MainSettings
{
State = Visibility.Visible
};
Add(dialogOverlay = new DialogOverlay
{
Depth = -1
});
} }
protected override void LoadComplete() [BackgroundDependencyLoader]
private void load()
{ {
base.LoadComplete(); dependencies.Cache(dialogOverlay);
settings.ToggleVisibility();
Add(settings);
} }
} }
} }

View File

@ -341,6 +341,61 @@ namespace osu.Game.Beatmaps
} }
} }
public void UndeleteAll()
{
var deleteMaps = QueryBeatmapSets(bs => bs.DeletePending).ToList();
if (!deleteMaps.Any()) return;
var notification = new ProgressNotification
{
CompletionText = "Restored all deleted beatmaps!",
Progress = 0,
State = ProgressNotificationState.Active,
};
PostNotification?.Invoke(notification);
int i = 0;
foreach (var bs in deleteMaps)
{
if (notification.State == ProgressNotificationState.Cancelled)
// user requested abort
return;
notification.Text = $"Restoring ({i} of {deleteMaps.Count})";
notification.Progress = (float)++i / deleteMaps.Count;
Undelete(bs);
}
notification.State = ProgressNotificationState.Completed;
}
public void Undelete(BeatmapSetInfo beatmapSet)
{
if (beatmapSet.Protected)
return;
lock (importContext)
{
var context = importContext.Value;
using (var transaction = context.BeginTransaction())
{
context.ChangeTracker.AutoDetectChangesEnabled = false;
var iFiles = new FileStore(() => context, storage);
var iBeatmaps = createBeatmapStore(() => context);
undelete(iBeatmaps, iFiles, beatmapSet);
context.ChangeTracker.AutoDetectChangesEnabled = true;
context.SaveChanges(transaction);
}
}
}
/// <summary> /// <summary>
/// Delete a beatmap difficulty. /// Delete a beatmap difficulty.
/// </summary> /// </summary>

View File

@ -10,19 +10,28 @@ using osu.Game.Rulesets;
using osu.Game.Users; using osu.Game.Users;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetScoresRequest : APIRequest<GetScoresResponse> public class GetScoresRequest : APIRequest<GetScoresResponse>
{ {
private readonly BeatmapInfo beatmap; private readonly BeatmapInfo beatmap;
private readonly LeaderboardScope scope;
private readonly RulesetInfo ruleset;
public GetScoresRequest(BeatmapInfo beatmap) public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global)
{ {
if (!beatmap.OnlineBeatmapID.HasValue) if (!beatmap.OnlineBeatmapID.HasValue)
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}.");
if (scope == LeaderboardScope.Local)
throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard");
this.beatmap = beatmap; this.beatmap = beatmap;
this.scope = scope;
this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset));
Success += onSuccess; Success += onSuccess;
} }
@ -33,6 +42,17 @@ namespace osu.Game.Online.API.Requests
score.ApplyBeatmap(beatmap); score.ApplyBeatmap(beatmap);
} }
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.Timeout = 30000;
req.AddParameter(@"type", scope.ToString().ToLowerInvariant());
req.AddParameter(@"mode", ruleset.ShortName);
return req;
}
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores"; protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores";
} }

View File

@ -104,7 +104,7 @@ namespace osu.Game.Overlays
scores.IsLoading = true; scores.IsLoading = true;
getScoresRequest = new GetScoresRequest(beatmap); getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r => getScoresRequest.Success += r =>
{ {
scores.Scores = r.Scores; scores.Scores = r.Scores;

View File

@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Mods
protected readonly OsuSpriteText MultiplierLabel; protected readonly OsuSpriteText MultiplierLabel;
private readonly FillFlowContainer footerContainer; private readonly FillFlowContainer footerContainer;
protected override bool BlockPassThroughKeyboard => false;
protected readonly FillFlowContainer<ModSection> ModSectionsContainer; protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>(); public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();

View File

@ -13,7 +13,6 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using System.Threading;
namespace osu.Game.Overlays.Music namespace osu.Game.Overlays.Music
{ {
@ -153,11 +152,6 @@ namespace osu.Game.Overlays.Music
var track = beatmapBacking.Value.Track; var track = beatmapBacking.Value.Track;
track.Restart(); track.Restart();
// this is temporary until we have blocking (async.Wait()) audio component methods.
// then we can call RestartAsync().Wait() or the blocking version above.
while (!track.IsRunning)
Thread.Sleep(1);
} }
} }

View File

@ -0,0 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Settings
{
/// <summary>
/// A <see cref="SettingsButton"/> with pink colours to mark dangerous/destructive actions.
/// </summary>
public class DangerousSettingsButton : SettingsButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Pink;
Triangles.ColourDark = colours.PinkDark;
Triangles.ColourLight = colours.PinkLight;
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Graphics;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Overlays.Settings.Sections.Maintenance
{
public class DeleteAllBeatmapsDialog : PopupDialog
{
public DeleteAllBeatmapsDialog(Action deleteAction)
{
BodyText = "Everything?";
Icon = FontAwesome.fa_trash_o;
HeaderText = @"Confirm deletion of";
Buttons = new PopupDialogButton[]
{
new PopupDialogOkButton
{
Text = @"Yes. Go for it.",
Action = deleteAction
},
new PopupDialogCancelButton
{
Text = @"No! Abort mission!",
},
};
}
}
}

View File

@ -15,12 +15,12 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
private TriangleButton importButton; private TriangleButton importButton;
private TriangleButton deleteButton; private TriangleButton deleteButton;
private TriangleButton restoreButton; private TriangleButton restoreButton;
private TriangleButton migrateButton; private TriangleButton undeleteButton;
protected override string Header => "General"; protected override string Header => "General";
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps) private void load(BeatmapManager beatmaps, DialogOverlay dialogOverlay)
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
@ -34,13 +34,16 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
.ContinueWith(t => Schedule(() => importButton.Enabled.Value = true), TaskContinuationOptions.LongRunning); .ContinueWith(t => Schedule(() => importButton.Enabled.Value = true), TaskContinuationOptions.LongRunning);
} }
}, },
deleteButton = new SettingsButton deleteButton = new DangerousSettingsButton
{ {
Text = "Delete ALL beatmaps", Text = "Delete ALL beatmaps",
Action = () => Action = () =>
{ {
deleteButton.Enabled.Value = false; dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); {
deleteButton.Enabled.Value = false;
Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
}));
} }
}, },
restoreButton = new SettingsButton restoreButton = new SettingsButton
@ -55,7 +58,16 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
beatmaps.Restore(b); beatmaps.Restore(b);
}).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true)); }).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true));
} }
} },
undeleteButton = new SettingsButton
{
Text = "Restore all recently deleted beatmaps",
Action = () =>
{
undeleteButton.Enabled.Value = false;
Task.Run(() => beatmaps.UndeleteAll()).ContinueWith(t => Schedule(() => undeleteButton.Enabled.Value = true));
}
},
}; };
} }
} }

View File

@ -28,6 +28,8 @@ namespace osu.Game.Overlays
private readonly Container contentContainer; private readonly Container contentContainer;
protected override bool BlockPassThroughKeyboard => true;
protected override Container<Drawable> Content => contentContainer; protected override Container<Drawable> Content => contentContainer;
protected Color4 FirstWaveColour protected Color4 FirstWaveColour

View File

@ -17,7 +17,6 @@ using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -327,11 +326,6 @@ namespace osu.Game.Screens.Play
{ {
adjustableSourceClock.Reset(); adjustableSourceClock.Reset();
// this is temporary until we have blocking (async.Wait()) audio component methods.
// then we can call ResetAsync().Wait() or the blocking version above.
while (adjustableSourceClock.IsRunning)
Thread.Sleep(1);
Schedule(() => Schedule(() =>
{ {
decoupledClock.ChangeSource(adjustableSourceClock); decoupledClock.ChangeSource(adjustableSourceClock);

View File

@ -52,6 +52,7 @@ namespace osu.Game.Screens.Select
default: default:
Details.Hide(); Details.Hide();
Leaderboard.Scope = (LeaderboardScope)tab - 1;
Leaderboard.Show(); Leaderboard.Show();
break; break;
} }

View File

@ -18,14 +18,26 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using System.Linq; using System.Linq;
using osu.Framework.Configuration;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Framework.Logging;
using osu.Game.Rulesets;
using osu.Framework.Input;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Screens.Select.Leaderboards
{ {
public class Leaderboard : Container public class Leaderboard : Container
{ {
private const double fade_duration = 200;
private readonly ScrollContainer scrollContainer; private readonly ScrollContainer scrollContainer;
private readonly Container placeholderContainer;
private FillFlowContainer<LeaderboardScore> scrollFlow; private FillFlowContainer<LeaderboardScore> scrollFlow;
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
public Action<Score> ScoreSelected; public Action<Score> ScoreSelected;
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
@ -38,15 +50,18 @@ namespace osu.Game.Screens.Select.Leaderboards
set set
{ {
scores = value; scores = value;
getScoresRequest?.Cancel();
scrollFlow?.FadeOut(200); scrollFlow?.FadeOut(fade_duration).Expire();
scrollFlow?.Expire();
scrollFlow = null; scrollFlow = null;
if (scores == null) loading.Hide();
if (scores == null || !scores.Any())
return; return;
// ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful;
// schedule because we may not be loaded yet (LoadComponentAsync complains). // schedule because we may not be loaded yet (LoadComponentAsync complains).
Schedule(() => Schedule(() =>
{ {
@ -74,6 +89,55 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
private LeaderboardScope scope;
public LeaderboardScope Scope
{
get { return scope; }
set
{
if (value == scope) return;
scope = value;
updateScores();
}
}
private PlaceholderState placeholderState;
protected PlaceholderState PlaceholderState
{
get { return placeholderState; }
set
{
if (value == placeholderState) return;
switch (placeholderState = value)
{
case PlaceholderState.NetworkFailure:
replacePlaceholder(new RetrievalFailurePlaceholder
{
OnRetry = updateScores,
});
break;
case PlaceholderState.NoScores:
replacePlaceholder(new MessagePlaceholder(@"No records yet!"));
break;
case PlaceholderState.NotLoggedIn:
replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!"));
break;
case PlaceholderState.NotSupporter:
replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!"));
break;
default:
replacePlaceholder(null);
break;
}
}
}
public Leaderboard() public Leaderboard()
{ {
Children = new Drawable[] Children = new Drawable[]
@ -83,7 +147,14 @@ namespace osu.Game.Screens.Select.Leaderboards
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false, ScrollbarVisible = false,
}, },
loading = new LoadingAnimation() loading = new LoadingAnimation(),
placeholderContainer = new Container
{
Alpha = 0,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
}; };
} }
@ -91,6 +162,8 @@ namespace osu.Game.Screens.Select.Leaderboards
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
private OsuGame osuGame;
private ScheduledDelegate pendingBeatmapSwitch; private ScheduledDelegate pendingBeatmapSwitch;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
@ -109,33 +182,115 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api) private void load(APIAccess api, OsuGame osuGame)
{ {
this.api = api; this.api = api;
this.osuGame = osuGame;
if (osuGame != null)
ruleset.BindTo(osuGame.Ruleset);
ruleset.ValueChanged += r => updateScores();
if (api != null)
api.OnStateChange += handleApiStateChange;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (api != null)
api.OnStateChange -= handleApiStateChange;
} }
private GetScoresRequest getScoresRequest; private GetScoresRequest getScoresRequest;
private void handleApiStateChange(APIState oldState, APIState newState)
{
if (Scope == LeaderboardScope.Local)
// No need to respond to API state change while current scope is local
return;
if (newState == APIState.Online)
updateScores();
}
private void updateScores() private void updateScores()
{ {
if (!IsLoaded) return;
Scores = null;
getScoresRequest?.Cancel(); getScoresRequest?.Cancel();
getScoresRequest = null;
Scores = null;
if (api == null || Beatmap?.OnlineBeatmapID == null) return; if (Scope == LeaderboardScope.Local)
{
// TODO: get local scores from wherever here.
PlaceholderState = PlaceholderState.NoScores;
return;
}
if (api?.IsLoggedIn != true)
{
PlaceholderState = PlaceholderState.NotLoggedIn;
return;
}
if (Beatmap?.OnlineBeatmapID == null)
{
PlaceholderState = PlaceholderState.NetworkFailure;
return;
}
PlaceholderState = PlaceholderState.Retrieving;
loading.Show(); loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap); if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
{
loading.Hide();
PlaceholderState = PlaceholderState.NotSupporter;
return;
}
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
getScoresRequest.Success += r => getScoresRequest.Success += r =>
{ {
Scores = r.Scores; Scores = r.Scores;
loading.Hide(); PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
}; };
getScoresRequest.Failure += onUpdateFailed;
api.Queue(getScoresRequest); api.Queue(getScoresRequest);
} }
private void onUpdateFailed(Exception e)
{
if (e is OperationCanceledException) return;
PlaceholderState = PlaceholderState.NetworkFailure;
Logger.Error(e, @"Couldn't fetch beatmap scores!");
}
private void replacePlaceholder(Placeholder placeholder)
{
if (placeholder == null)
{
placeholderContainer.FadeOutFromOne(fade_duration, Easing.OutQuint);
placeholderContainer.Clear(true);
return;
}
var existingPlaceholder = placeholderContainer.Children.FirstOrDefault() as Placeholder;
if (placeholder.Equals(existingPlaceholder))
return;
Scores = null;
placeholderContainer.Clear(true);
placeholderContainer.Child = placeholder;
placeholderContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
@ -164,5 +319,118 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
} }
private abstract class Placeholder : FillFlowContainer, IEquatable<Placeholder>
{
public virtual bool Equals(Placeholder other) => GetType() == other?.GetType();
}
private class MessagePlaceholder : Placeholder
{
private readonly string message;
public MessagePlaceholder(string message)
{
Direction = FillDirection.Horizontal;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.fa_exclamation_circle,
Size = new Vector2(26),
Margin = new MarginPadding { Right = 10 },
},
new OsuSpriteText
{
Text = this.message = message,
TextSize = 22,
},
};
}
public override bool Equals(Placeholder other) => (other as MessagePlaceholder)?.message == message;
}
private class RetrievalFailurePlaceholder : Placeholder
{
public Action OnRetry;
public RetrievalFailurePlaceholder()
{
Direction = FillDirection.Horizontal;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new RetryButton
{
Action = () => OnRetry?.Invoke(),
Margin = new MarginPadding { Right = 10 },
},
new OsuSpriteText
{
Anchor = Anchor.TopLeft,
Text = @"Couldn't retrieve scores!",
TextSize = 22,
},
};
}
private class RetryButton : OsuHoverContainer
{
private readonly SpriteIcon icon;
public Action Action;
public RetryButton()
{
Height = 26;
Width = 26;
Child = new OsuClickableContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = () => Action?.Invoke(),
Child = icon = new SpriteIcon
{
Icon = FontAwesome.fa_refresh,
Size = new Vector2(26),
Shadow = true,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.8f, 4000, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1, 1000, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
}
}
}
public enum LeaderboardScope
{
Local,
Country,
Global,
Friend,
}
public enum PlaceholderState
{
Successful,
Retrieving,
NetworkFailure,
NoScores,
NotLoggedIn,
NotSupporter,
} }
} }

View File

@ -275,7 +275,7 @@ namespace osu.Game.Screens.Select
if (beatmap == Beatmap.Value.BeatmapInfo) if (beatmap == Beatmap.Value.BeatmapInfo)
performLoad(); performLoad();
else else
selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 100); selectionChangedDebounce = Scheduler.AddDelayed(performLoad, 200);
} }
} }

View File

@ -244,7 +244,7 @@ namespace osu.Game.Tests.Visual
if (!api.IsLoggedIn) if (!api.IsLoggedIn)
return; return;
lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo); lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo, newBeatmap.BeatmapInfo.Ruleset);
lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap))); lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap)));
api.Queue(lastRequest); api.Queue(lastRequest);
} }

View File

@ -268,6 +268,7 @@
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" /> <Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
<Compile Include="Database\DatabaseContextFactory.cs" /> <Compile Include="Database\DatabaseContextFactory.cs" />
<Compile Include="Database\IHasPrimaryKey.cs" /> <Compile Include="Database\IHasPrimaryKey.cs" />
<Compile Include="Overlays\Settings\DangerousSettingsButton.cs" />
<Compile Include="Graphics\UserInterface\HoverClickSounds.cs" /> <Compile Include="Graphics\UserInterface\HoverClickSounds.cs" />
<Compile Include="Graphics\UserInterface\HoverSounds.cs" /> <Compile Include="Graphics\UserInterface\HoverSounds.cs" />
<Compile Include="Graphics\UserInterface\OsuButton.cs" /> <Compile Include="Graphics\UserInterface\OsuButton.cs" />
@ -308,6 +309,7 @@
<Compile Include="Overlays\Profile\Sections\Ranks\PaginatedScoreContainer.cs" /> <Compile Include="Overlays\Profile\Sections\Ranks\PaginatedScoreContainer.cs" />
<Compile Include="Overlays\Profile\Sections\Ranks\DrawableTotalScore.cs" /> <Compile Include="Overlays\Profile\Sections\Ranks\DrawableTotalScore.cs" />
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" /> <Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
<Compile Include="Overlays\Settings\SettingsButton.cs" /> <Compile Include="Overlays\Settings\SettingsButton.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\OriginHandle.cs" /> <Compile Include="Rulesets\Edit\Layers\Selection\OriginHandle.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectSelectionBox.cs" /> <Compile Include="Rulesets\Edit\Layers\Selection\HitObjectSelectionBox.cs" />