1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 10:33:30 +08:00

Merge branch 'master' into samah-ios

This commit is contained in:
Shane Woolcock 2018-12-24 15:52:47 +10:30
commit 436fbc7ffe
59 changed files with 1450 additions and 432 deletions

View File

@ -21,7 +21,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected DrawableOsuHitObject(OsuHitObject hitObject) protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
base.AddInternal(shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both }); base.AddInternal(shakeContainer = new ShakeContainer
{
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both
});
Alpha = 0; Alpha = 0;
} }

View File

@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{ {
new KeyBinding(InputKey.A, OsuAction.LeftButton), new KeyBinding(InputKey.Z, OsuAction.LeftButton),
new KeyBinding(InputKey.S, OsuAction.RightButton), new KeyBinding(InputKey.X, OsuAction.RightButton),
new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton),
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
}; };

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.AccountCreation;
namespace osu.Game.Tests.Visual
{
public class TestCaseAccountCreationOverlay : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ErrorTextFlowContainer),
typeof(AccountCreationBackground),
typeof(ScreenEntry),
typeof(ScreenWarning),
typeof(ScreenWelcome),
typeof(AccountCreationScreen),
};
public TestCaseAccountCreationOverlay()
{
var accountCreation = new AccountCreationOverlay();
Child = accountCreation;
accountCreation.State = Visibility.Visible;
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseDrawableDate : OsuTestCase
{
public TestCaseDrawableDate()
{
Child = new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
{
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(60))),
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(55))),
new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(50))),
new PokeyDrawableDate(DateTimeOffset.Now),
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(60))),
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(65))),
new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(70))),
}
};
}
private class PokeyDrawableDate : CompositeDrawable
{
public PokeyDrawableDate(DateTimeOffset date)
{
const float box_size = 10;
DrawableDate drawableDate;
Box flash;
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
flash = new Box
{
Colour = Color4.Yellow,
Size = new Vector2(box_size),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Alpha = 0
},
drawableDate = new DrawableDate(date)
{
X = box_size + 2,
}
};
drawableDate.Current.ValueChanged += v => flash.FadeOutFromOne(500);
}
}
}
}

View File

@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using osuTK; using osuTK;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Scoring; using osu.Game.Scoring;
@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f), Size = new Vector2(550f, 450f),
Scope = LeaderboardScope.Global, Scope = BeatmapLeaderboardScope.Global,
}); });
AddStep(@"New Scores", newScores); AddStep(@"New Scores", newScores);
@ -275,7 +276,7 @@ namespace osu.Game.Tests.Visual
}; };
} }
private class FailableLeaderboard : Leaderboard private class FailableLeaderboard : BeatmapLeaderboard
{ {
public void SetRetrievalState(PlaceholderState state) public void SetRetrievalState(PlaceholderState state)
{ {

View File

@ -8,7 +8,9 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Pages;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -23,8 +25,8 @@ namespace osu.Game.Tests.Visual
typeof(ScoreInfo), typeof(ScoreInfo),
typeof(Results), typeof(Results),
typeof(ResultsPage), typeof(ResultsPage),
typeof(ResultsPageScore), typeof(ScoreResultsPage),
typeof(ResultsPageRanking) typeof(RankingResultsPage)
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -41,7 +43,7 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null) if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Add(new Results(new ScoreInfo Add(new SoloResults(new ScoreInfo
{ {
TotalScore = 2845370, TotalScore = 2845370,
Accuracy = 0.98, Accuracy = 0.98,

View File

@ -68,31 +68,33 @@ namespace osu.Game.Tests.Visual
chatDisplay.Channel.Value = testChannel; chatDisplay.Channel.Value = testChannel;
chatDisplay2.Channel.Value = testChannel; chatDisplay2.Channel.Value = testChannel;
AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage int sequence = 0;
AddStep("message from admin", () => testChannel.AddNewMessages(new Message(sequence++)
{ {
Sender = admin, Sender = admin,
Content = "I am a wang!" Content = "I am a wang!"
})); }));
AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage AddStep("message from team red", () => testChannel.AddNewMessages(new Message(sequence++)
{ {
Sender = redUser, Sender = redUser,
Content = "I am team red." Content = "I am team red."
})); }));
AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage AddStep("message from team red", () => testChannel.AddNewMessages(new Message(sequence++)
{ {
Sender = redUser, Sender = redUser,
Content = "I plan to win!" Content = "I plan to win!"
})); }));
AddStep("message from team blue", () => testChannel.AddLocalEcho(new LocalEchoMessage AddStep("message from team blue", () => testChannel.AddNewMessages(new Message(sequence++)
{ {
Sender = blueUser, Sender = blueUser,
Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand." Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand."
})); }));
AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage AddStep("message from admin", () => testChannel.AddNewMessages(new Message(sequence++)
{ {
Sender = admin, Sender = admin,
Content = "Okay okay, calm down guys. Let's do this!" Content = "Okay okay, calm down guys. Let's do this!"

View File

@ -26,7 +26,6 @@ namespace osu.Game.Graphics.Containers
private PreviewTrackManager previewTrackManager; private PreviewTrackManager previewTrackManager;
protected readonly Bindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All); protected readonly Bindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All);
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)

View File

@ -11,29 +11,43 @@ namespace osu.Game.Graphics.Containers
/// </summary> /// </summary>
public class ShakeContainer : Container public class ShakeContainer : Container
{ {
/// <summary>
/// The length of a single shake.
/// </summary>
public float ShakeDuration = 80;
/// <summary>
/// Total number of shakes. May be shortened if possible.
/// </summary>
public float TotalShakes = 4;
/// <summary>
/// Pixels of displacement per shake.
/// </summary>
public float ShakeMagnitude = 8;
/// <summary> /// <summary>
/// Shake the contents of this container. /// Shake the contents of this container.
/// </summary> /// </summary>
/// <param name="maximumLength">The maximum length the shake should last.</param> /// <param name="maximumLength">The maximum length the shake should last.</param>
public void Shake(double maximumLength) public void Shake(double? maximumLength = null)
{ {
const float shake_amount = 8; const float shake_amount = 8;
const float shake_duration = 30;
// if we don't have enough time, don't bother shaking. // if we don't have enough time, don't bother shaking.
if (maximumLength < shake_duration * 2) if (maximumLength < ShakeDuration * 2)
return; return;
var sequence = this.MoveToX(shake_amount, shake_duration / 2, Easing.OutSine).Then() var sequence = this.MoveToX(shake_amount, ShakeDuration / 2, Easing.OutSine).Then()
.MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then(); .MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
// if we don't have enough time for the second shake, skip it. // if we don't have enough time for the second shake, skip it.
if (maximumLength > shake_duration * 4) if (!maximumLength.HasValue || maximumLength >= ShakeDuration * 4)
sequence = sequence sequence = sequence
.MoveToX(shake_amount, shake_duration, Easing.InOutSine).Then() .MoveToX(shake_amount, ShakeDuration, Easing.InOutSine).Then()
.MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then(); .MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
sequence.MoveToX(0, shake_duration / 2, Easing.InSine); sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine);
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using Humanizer; using Humanizer;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -11,13 +12,27 @@ namespace osu.Game.Graphics
{ {
public class DrawableDate : OsuSpriteText, IHasTooltip public class DrawableDate : OsuSpriteText, IHasTooltip
{ {
protected readonly DateTimeOffset Date; private DateTimeOffset date;
public DateTimeOffset Date
{
get => date;
set
{
if (date == value)
return;
date = value.ToLocalTime();
if (LoadState >= LoadState.Ready)
updateTime();
}
}
public DrawableDate(DateTimeOffset date) public DrawableDate(DateTimeOffset date)
{ {
Font = "Exo2.0-RegularItalic"; Font = "Exo2.0-RegularItalic";
Date = date.ToLocalTime(); Date = date;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -39,14 +54,14 @@ namespace osu.Game.Graphics
var diffToNow = DateTimeOffset.Now.Subtract(Date); var diffToNow = DateTimeOffset.Now.Subtract(Date);
double timeUntilNextUpdate = 1000; double timeUntilNextUpdate = 1000;
if (diffToNow.TotalSeconds > 60) if (Math.Abs(diffToNow.TotalSeconds) > 120)
{ {
timeUntilNextUpdate *= 60; timeUntilNextUpdate *= 60;
if (diffToNow.TotalMinutes > 60) if (Math.Abs(diffToNow.TotalMinutes) > 120)
{ {
timeUntilNextUpdate *= 60; timeUntilNextUpdate *= 60;
if (diffToNow.TotalHours > 24) if (Math.Abs(diffToNow.TotalHours) > 48)
timeUntilNextUpdate *= 24; timeUntilNextUpdate *= 24;
} }
} }

View File

@ -8,6 +8,9 @@ using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
/// <summary>
/// A loading spinner.
/// </summary>
public class LoadingAnimation : VisibilityContainer public class LoadingAnimation : VisibilityContainer
{ {
private readonly SpriteIcon spinner; private readonly SpriteIcon spinner;

View File

@ -0,0 +1,56 @@
// Copyright (c) 2007-2018 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.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// An overlay that will consume all available space and block input when required.
/// Useful for disabling all elements in a form and showing we are waiting on a response, for instance.
/// </summary>
public class ProcessingOverlay : VisibilityContainer
{
private const float transition_duration = 200;
public ProcessingOverlay()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
Alpha = 0.9f,
},
new LoadingAnimation { State = Visibility.Visible }
};
}
protected override bool Handle(UIEvent e)
{
return true;
}
protected override void PopIn()
{
this.FadeIn(transition_duration * 2, Easing.OutQuint);
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.OutQuint);
}
}
}

View File

@ -5,7 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Http;
using System.Threading; using System.Threading;
using Newtonsoft.Json.Linq;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Logging; using osu.Framework.Logging;
@ -159,7 +161,7 @@ namespace osu.Game.Online.API
//hard bail if we can't get a valid access token. //hard bail if we can't get a valid access token.
if (authentication.RequestAccessToken() == null) if (authentication.RequestAccessToken() == null)
{ {
Logout(false); Logout();
continue; continue;
} }
@ -188,6 +190,39 @@ namespace osu.Game.Online.API
this.password = password; this.password = password;
} }
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password)
{
Debug.Assert(State == APIState.Offline);
var req = new RegistrationRequest
{
Url = $@"{Endpoint}/users",
Method = HttpMethod.Post,
Username = username,
Email = email,
Password = password
};
try
{
req.Perform();
}
catch (Exception e)
{
try
{
return JObject.Parse(req.ResponseString).SelectToken("form_error", true).ToObject<RegistrationRequest.RegistrationRequestErrors>();
}
catch
{
// if we couldn't deserialize the error message let's throw the original exception outwards.
throw e;
}
}
return null;
}
/// <summary> /// <summary>
/// Handle a single API request. /// Handle a single API request.
/// Ensures all exceptions are caught and dealt with correctly. /// Ensures all exceptions are caught and dealt with correctly.
@ -258,7 +293,7 @@ namespace osu.Game.Online.API
switch (statusCode) switch (statusCode)
{ {
case HttpStatusCode.Unauthorized: case HttpStatusCode.Unauthorized:
Logout(false); Logout();
return true; return true;
case HttpStatusCode.RequestTimeout: case HttpStatusCode.RequestTimeout:
failureCount++; failureCount++;
@ -307,10 +342,9 @@ namespace osu.Game.Online.API
} }
} }
public void Logout(bool clearUsername = true) public void Logout()
{ {
flushQueue(); flushQueue();
if (clearUsername) ProvidedUsername = null;
password = null; password = null;
authentication.Clear(); authentication.Clear();
LocalUser.Value = createGuestUser(); LocalUser.Value = createGuestUser();

View File

@ -0,0 +1,41 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API
{
public class RegistrationRequest : WebRequest
{
internal string Username;
internal string Email;
internal string Password;
protected override void PrePerform()
{
AddParameter("user[username]", Username);
AddParameter("user[user_email]", Email);
AddParameter("user[password]", Password);
base.PrePerform();
}
public class RegistrationRequestErrors
{
public UserErrors User;
public class UserErrors
{
[JsonProperty("username")]
public string[] Username;
[JsonProperty("user_email")]
public string[] Email;
[JsonProperty("password")]
public string[] Password;
}
}
}
}

View File

@ -13,15 +13,15 @@ namespace osu.Game.Online.API.Requests
public class GetScoresRequest : APIRequest<APIScores> public class GetScoresRequest : APIRequest<APIScores>
{ {
private readonly BeatmapInfo beatmap; private readonly BeatmapInfo beatmap;
private readonly LeaderboardScope scope; private readonly BeatmapLeaderboardScope scope;
private readonly RulesetInfo ruleset; private readonly RulesetInfo ruleset;
public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global) public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.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) if (scope == BeatmapLeaderboardScope.Local)
throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard"); throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard");
this.beatmap = beatmap; this.beatmap = beatmap;

View File

@ -1,19 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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 System.Collections.Generic; using System;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK; using osu.Game.Overlays.Chat;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Online.Chat namespace osu.Game.Online.Chat
@ -23,22 +19,27 @@ namespace osu.Game.Online.Chat
/// </summary> /// </summary>
public class StandAloneChatDisplay : CompositeDrawable public class StandAloneChatDisplay : CompositeDrawable
{ {
private readonly bool postingTextbox;
public readonly Bindable<Channel> Channel = new Bindable<Channel>(); public readonly Bindable<Channel> Channel = new Bindable<Channel>();
private readonly FillFlowContainer messagesFlow;
private Channel lastChannel;
private readonly FocusedTextBox textbox; private readonly FocusedTextBox textbox;
protected ChannelManager ChannelManager; protected ChannelManager ChannelManager;
private ScrollContainer scroll;
private DrawableChannel drawableChannel;
private const float textbox_height = 30;
/// <summary> /// <summary>
/// Construct a new instance. /// Construct a new instance.
/// </summary> /// </summary>
/// <param name="postingTextbox">Whether a textbox for posting new messages should be displayed.</param> /// <param name="postingTextbox">Whether a textbox for posting new messages should be displayed.</param>
public StandAloneChatDisplay(bool postingTextbox = false) public StandAloneChatDisplay(bool postingTextbox = false)
{ {
this.postingTextbox = postingTextbox;
CornerRadius = 10; CornerRadius = 10;
Masking = true; Masking = true;
@ -50,23 +51,10 @@ namespace osu.Game.Online.Chat
Alpha = 0.8f, Alpha = 0.8f,
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },
messagesFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
LayoutEasing = Easing.Out,
LayoutDuration = 500,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Direction = FillDirection.Vertical
}
}; };
const float textbox_height = 30;
if (postingTextbox) if (postingTextbox)
{ {
messagesFlow.Y -= textbox_height;
AddInternal(textbox = new FocusedTextBox AddInternal(textbox = new FocusedTextBox
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -117,112 +105,43 @@ namespace osu.Game.Online.Chat
this.MoveToY(100, 500, Easing.In); this.MoveToY(100, 500, Easing.In);
} }
protected virtual Drawable CreateMessage(Message message) protected virtual ChatLine CreateMessage(Message message) => new StandAloneMessage(message);
{
return new StandAloneMessage(message);
}
private void channelChanged(Channel channel) private void channelChanged(Channel channel)
{ {
if (lastChannel != null) drawableChannel?.Expire();
lastChannel.NewMessagesArrived -= newMessages;
lastChannel = channel;
messagesFlow.Clear();
if (channel == null) return; if (channel == null) return;
channel.NewMessagesArrived += newMessages; AddInternal(drawableChannel = new StandAloneDrawableChannel(channel)
{
newMessages(channel.Messages); CreateChatLineAction = CreateMessage,
Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 }
});
} }
private void newMessages(IEnumerable<Message> messages) protected class StandAloneDrawableChannel : DrawableChannel
{ {
var excessChildren = messagesFlow.Children.Count - 10; public Func<Message,ChatLine> CreateChatLineAction;
if (excessChildren > 0)
foreach (var c in messagesFlow.Children.Take(excessChildren))
c.Expire();
foreach (var message in messages) protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
public StandAloneDrawableChannel(Channel channel)
: base(channel)
{ {
var formatted = MessageFormatter.FormatMessage(message); ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 };
var drawable = CreateMessage(formatted);
drawable.Y = messagesFlow.Height;
messagesFlow.Add(drawable);
} }
} }
protected class StandAloneMessage : CompositeDrawable protected class StandAloneMessage : ChatLine
{ {
protected readonly Message Message; protected override float TextSize => 15;
protected OsuSpriteText SenderText;
protected Circle ColourBox;
public StandAloneMessage(Message message) protected override float HorizontalPadding => 10;
protected override float MessagePadding => 120;
public StandAloneMessage(Message message) : base(message)
{ {
Message = message;
}
[BackgroundDependencyLoader]
private void load()
{
Margin = new MarginPadding(3);
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Width = 0.2f,
Children = new Drawable[]
{
SenderText = new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = Message.Sender.ToString()
}
}
},
new Container
{
Size = new Vector2(8, OsuSpriteText.FONT_SIZE),
Margin = new MarginPadding { Horizontal = 3 },
Children = new Drawable[]
{
ColourBox = new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(8)
}
}
},
new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.5f,
Text = Message.DisplayContent
}
}
}
};
if (!string.IsNullOrEmpty(Message.Sender.Colour))
SenderText.Colour = ColourBox.Colour = OsuColour.FromHex(Message.Sender.Colour);
} }
} }
} }

View File

@ -2,14 +2,14 @@
// 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.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Extensions;
using osu.Game.Scoring; using osu.Game.Scoring;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class DrawableRank : Container public class DrawableRank : Container
{ {

View File

@ -3,27 +3,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osuTK; using System.Linq;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osuTK;
using System.Linq; using osuTK.Graphics;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class Leaderboard : Container public abstract class Leaderboard<TScope, ScoreInfo> : Container
{ {
private const double fade_duration = 300; private const double fade_duration = 300;
@ -32,10 +27,6 @@ namespace osu.Game.Screens.Select.Leaderboards
private FillFlowContainer<LeaderboardScore> scrollFlow; private FillFlowContainer<LeaderboardScore> scrollFlow;
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
public Action<ScoreInfo> ScoreSelected;
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
private ScheduledDelegate showScoresDelegate; private ScheduledDelegate showScoresDelegate;
@ -70,7 +61,7 @@ namespace osu.Game.Screens.Select.Leaderboards
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f), Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 }, Padding = new MarginPadding { Top = 10, Bottom = 5 },
ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) }) ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1))
}; };
// schedule because we may not be loaded yet (LoadComponentAsync complains). // schedule because we may not be loaded yet (LoadComponentAsync complains).
@ -96,18 +87,18 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
private LeaderboardScope scope; private TScope scope;
public LeaderboardScope Scope public TScope Scope
{ {
get { return scope; } get { return scope; }
set set
{ {
if (value == scope) if (value.Equals(scope))
return; return;
scope = value; scope = value;
updateScores(); UpdateScores();
} }
} }
@ -137,7 +128,7 @@ namespace osu.Game.Screens.Select.Leaderboards
case PlaceholderState.NetworkFailure: case PlaceholderState.NetworkFailure:
replacePlaceholder(new RetrievalFailurePlaceholder replacePlaceholder(new RetrievalFailurePlaceholder
{ {
OnRetry = updateScores, OnRetry = UpdateScores,
}); });
break; break;
case PlaceholderState.Unavailable: case PlaceholderState.Unavailable:
@ -159,7 +150,7 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
public Leaderboard() protected Leaderboard()
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
@ -177,36 +168,14 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
private APIAccess api; private APIAccess api;
private BeatmapInfo beatmap;
[Resolved]
private ScoreManager scoreManager { get; set; }
private ScheduledDelegate pendingUpdateScores; private ScheduledDelegate pendingUpdateScores;
public BeatmapInfo Beatmap [BackgroundDependencyLoader(true)]
{ private void load(APIAccess api)
get { return beatmap; }
set
{
if (beatmap == value)
return;
beatmap = value;
Scores = null;
updateScores();
}
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, IBindable<RulesetInfo> parentRuleset)
{ {
this.api = api; this.api = api;
ruleset.BindTo(parentRuleset);
ruleset.ValueChanged += _ => updateScores();
if (api != null) if (api != null)
api.OnStateChange += handleApiStateChange; api.OnStateChange += handleApiStateChange;
} }
@ -219,21 +188,17 @@ namespace osu.Game.Screens.Select.Leaderboards
api.OnStateChange -= handleApiStateChange; api.OnStateChange -= handleApiStateChange;
} }
public void RefreshScores() => updateScores(); public void RefreshScores() => UpdateScores();
private GetScoresRequest getScoresRequest; private APIRequest getScoresRequest;
private void handleApiStateChange(APIState oldState, APIState newState) 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) if (newState == APIState.Online)
updateScores(); UpdateScores();
} }
private void updateScores() protected void UpdateScores()
{ {
// don't display any scores or placeholder until the first Scores_Set has been called. // 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. // this avoids scope changes flickering a "no scores" placeholder before initialisation of song select is finished.
@ -245,40 +210,23 @@ namespace osu.Game.Screens.Select.Leaderboards
pendingUpdateScores?.Cancel(); pendingUpdateScores?.Cancel();
pendingUpdateScores = Schedule(() => pendingUpdateScores = Schedule(() =>
{ {
if (Scope == LeaderboardScope.Local)
{
Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray();
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
return;
}
if (Beatmap?.OnlineBeatmapID == null)
{
PlaceholderState = PlaceholderState.Unavailable;
return;
}
if (api?.IsLoggedIn != true) if (api?.IsLoggedIn != true)
{ {
PlaceholderState = PlaceholderState.NotLoggedIn; PlaceholderState = PlaceholderState.NotLoggedIn;
return; return;
} }
if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
{
PlaceholderState = PlaceholderState.NotSupporter;
return;
}
PlaceholderState = PlaceholderState.Retrieving; PlaceholderState = PlaceholderState.Retrieving;
loading.Show(); loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest = FetchScores(scores => Schedule(() =>
getScoresRequest.Success += r => Schedule(() =>
{ {
Scores = r.Scores; Scores = scores;
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
}); }));
if (getScoresRequest == null)
return;
getScoresRequest.Failure += e => Schedule(() => getScoresRequest.Failure += e => Schedule(() =>
{ {
@ -292,6 +240,8 @@ namespace osu.Game.Screens.Select.Leaderboards
}); });
} }
protected abstract APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback);
private Placeholder currentPlaceholder; private Placeholder currentPlaceholder;
private void replacePlaceholder(Placeholder placeholder) private void replacePlaceholder(Placeholder placeholder)
@ -344,5 +294,7 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
} }
protected abstract LeaderboardScore CreateDrawableScore(ScoreInfo model, int index);
} }
} }

View File

@ -1,9 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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 System.Collections.Generic;
using System.Linq; using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -17,35 +16,40 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Users; using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class LeaderboardScore : OsuClickableContainer public class LeaderboardScore : OsuClickableContainer
{ {
public static readonly float HEIGHT = 60;
public readonly int RankPosition; public readonly int RankPosition;
public readonly ScoreInfo Score;
public const float HEIGHT = 60;
private const float corner_radius = 5; private const float corner_radius = 5;
private const float edge_margin = 5; private const float edge_margin = 5;
private const float background_alpha = 0.25f; private const float background_alpha = 0.25f;
private const float rank_width = 30; private const float rank_width = 30;
protected Container RankContainer { get; private set; }
private readonly ScoreInfo score;
private Box background; private Box background;
private Container content; private Container content;
private Drawable avatar; private Drawable avatar;
private DrawableRank scoreRank; private Drawable scoreRank;
private OsuSpriteText nameLabel; private OsuSpriteText nameLabel;
private GlowingSpriteText scoreLabel; private GlowingSpriteText scoreLabel;
private ScoreComponentLabel maxCombo;
private ScoreComponentLabel accuracy;
private Container flagBadgeContainer; private Container flagBadgeContainer;
private FillFlowContainer<ModIcon> modsContainer; private FillFlowContainer<ModIcon> modsContainer;
private List<ScoreComponentLabel> statisticsLabels;
public LeaderboardScore(ScoreInfo score, int rank) public LeaderboardScore(ScoreInfo score, int rank)
{ {
Score = score; this.score = score;
RankPosition = rank; RankPosition = rank;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -55,6 +59,10 @@ namespace osu.Game.Screens.Select.Leaderboards
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
var user = score.User;
statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList();
Children = new Drawable[] Children = new Drawable[]
{ {
new Container new Container
@ -102,7 +110,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Children = new[] Children = new[]
{ {
avatar = new DelayedLoadWrapper( avatar = new DelayedLoadWrapper(
new Avatar(Score.User) new Avatar(user)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
CornerRadius = corner_radius, CornerRadius = corner_radius,
@ -128,7 +136,7 @@ namespace osu.Game.Screens.Select.Leaderboards
{ {
nameLabel = new OsuSpriteText nameLabel = new OsuSpriteText
{ {
Text = Score.User.Username, Text = user.Username,
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
TextSize = 23, TextSize = 23,
}, },
@ -149,7 +157,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Masking = true, Masking = true,
Children = new Drawable[] Children = new Drawable[]
{ {
new DrawableFlag(Score.User?.Country) new DrawableFlag(user.Country)
{ {
Width = 30, Width = 30,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
@ -164,11 +172,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f), Spacing = new Vector2(10f, 0f),
Margin = new MarginPadding { Left = edge_margin }, Margin = new MarginPadding { Left = edge_margin },
Children = new Drawable[] Children = statisticsLabels
{
maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString(), "Max Combo"),
accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy), "Accuracy"),
},
}, },
}, },
}, },
@ -183,17 +187,17 @@ namespace osu.Game.Screens.Select.Leaderboards
Spacing = new Vector2(5f, 0f), Spacing = new Vector2(5f, 0f),
Children = new Drawable[] Children = new Drawable[]
{ {
scoreLabel = new GlowingSpriteText(Score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")), scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
new Container RankContainer = new Container
{ {
Size = new Vector2(40f, 20f), Size = new Vector2(40f, 20f),
Children = new[] Children = new[]
{ {
scoreRank = new DrawableRank(Score.Rank) scoreRank = new DrawableRank(score.Rank)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(40f), Size = new Vector2(40f)
}, },
}, },
}, },
@ -205,7 +209,7 @@ namespace osu.Game.Screens.Select.Leaderboards
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
}, },
}, },
}, },
@ -216,7 +220,7 @@ namespace osu.Game.Screens.Select.Leaderboards
public override void Show() public override void Show()
{ {
foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer }) foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, modsContainer }.Concat(statisticsLabels))
d.FadeOut(); d.FadeOut();
Alpha = 0; Alpha = 0;
@ -243,7 +247,7 @@ namespace osu.Game.Screens.Select.Leaderboards
using (BeginDelayedSequence(50, true)) using (BeginDelayedSequence(50, true))
{ {
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, }; var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray();
for (int i = 0; i < drawables.Length; i++) for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50); drawables[i].FadeIn(100 + i * 50);
} }
@ -251,6 +255,12 @@ namespace osu.Game.Screens.Select.Leaderboards
} }
} }
protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
{
new LeaderboardScoreStatistic(FontAwesome.fa_link, "Max Combo", model.MaxCombo.ToString()),
new LeaderboardScoreStatistic(FontAwesome.fa_crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy))
};
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
background.FadeTo(0.5f, 300, Easing.OutQuint); background.FadeTo(0.5f, 300, Easing.OutQuint);
@ -321,11 +331,10 @@ namespace osu.Game.Screens.Select.Leaderboards
public string TooltipText => name; public string TooltipText => name;
public ScoreComponentLabel(FontAwesome icon, string value, string name) public ScoreComponentLabel(LeaderboardScoreStatistic statistic)
{ {
this.name = name; name = statistic.Name;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Both;
Width = 60;
Child = content = new FillFlowContainer Child = content = new FillFlowContainer
{ {
@ -356,11 +365,11 @@ namespace osu.Game.Screens.Select.Leaderboards
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(icon_size - 6), Size = new Vector2(icon_size - 6),
Colour = OsuColour.FromHex(@"a4edff"), Colour = OsuColour.FromHex(@"a4edff"),
Icon = icon, Icon = statistic.Icon,
}, },
}, },
}, },
new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa")) new GlowingSpriteText(statistic.Value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
@ -369,5 +378,19 @@ namespace osu.Game.Screens.Select.Leaderboards
}; };
} }
} }
public class LeaderboardScoreStatistic
{
public FontAwesome Icon;
public string Value;
public string Name;
public LeaderboardScoreStatistic(FontAwesome icon, string name, string value)
{
Icon = icon;
Name = name;
Value = value;
}
}
} }
} }

View File

@ -4,7 +4,7 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class MessagePlaceholder : Placeholder public class MessagePlaceholder : Placeholder
{ {

View File

@ -5,7 +5,7 @@ using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public abstract class Placeholder : OsuTextFlowContainer, IEquatable<Placeholder> public abstract class Placeholder : OsuTextFlowContainer, IEquatable<Placeholder>
{ {

View File

@ -1,7 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public enum PlaceholderState public enum PlaceholderState
{ {

View File

@ -8,7 +8,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osuTK; using osuTK;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Online.Leaderboards
{ {
public class RetrievalFailurePlaceholder : Placeholder public class RetrievalFailurePlaceholder : Placeholder
{ {

View File

@ -61,6 +61,8 @@ namespace osu.Game
private DialogOverlay dialogOverlay; private DialogOverlay dialogOverlay;
private AccountCreationOverlay accountCreation;
private DirectOverlay direct; private DirectOverlay direct;
private SocialOverlay social; private SocialOverlay social;
@ -185,7 +187,13 @@ namespace osu.Game
} }
private ExternalLinkOpener externalLinkOpener; private ExternalLinkOpener externalLinkOpener;
public void OpenUrlExternally(string url) => externalLinkOpener.OpenUrlExternally(url); public void OpenUrlExternally(string url)
{
if (url.StartsWith("/"))
url = $"{API.Endpoint}{url}";
externalLinkOpener.OpenUrlExternally(url);
}
private ScheduledDelegate scoreLoad; private ScheduledDelegate scoreLoad;
@ -400,11 +408,21 @@ namespace osu.Game
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
}, overlayContent.Add); }, overlayContent.Add);
loadComponentSingleFile(dialogOverlay = new DialogOverlay loadComponentSingleFile(accountCreation = new AccountCreationOverlay
{ {
Depth = -6, Depth = -6,
}, overlayContent.Add); }, overlayContent.Add);
loadComponentSingleFile(dialogOverlay = new DialogOverlay
{
Depth = -7,
}, overlayContent.Add);
loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener
{
Depth = -8,
}, overlayContent.Add);
dependencies.Cache(idleTracker); dependencies.Cache(idleTracker);
dependencies.Cache(settings); dependencies.Cache(settings);
dependencies.Cache(onscreenDisplay); dependencies.Cache(onscreenDisplay);
@ -417,6 +435,7 @@ namespace osu.Game
dependencies.Cache(beatmapSetOverlay); dependencies.Cache(beatmapSetOverlay);
dependencies.Cache(notifications); dependencies.Cache(notifications);
dependencies.Cache(dialogOverlay); dependencies.Cache(dialogOverlay);
dependencies.Cache(accountCreation);
chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible; chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible;

View File

@ -60,6 +60,8 @@ namespace osu.Game
protected RulesetConfigCache RulesetConfigCache; protected RulesetConfigCache RulesetConfigCache;
protected APIAccess API;
protected MenuCursorContainer MenuCursorContainer; protected MenuCursorContainer MenuCursorContainer;
private Container content; private Container content;
@ -146,14 +148,14 @@ namespace osu.Game
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
dependencies.CacheAs<ISkinSource>(SkinManager); dependencies.CacheAs<ISkinSource>(SkinManager);
var api = new APIAccess(LocalConfig); API = new APIAccess(LocalConfig);
dependencies.Cache(api); dependencies.Cache(API);
dependencies.CacheAs<IAPIProvider>(api); dependencies.CacheAs<IAPIProvider>(API);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, API, Audio, Host));
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, BeatmapManager, Host.Storage, contextFactory, Host)); dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, BeatmapManager, Host.Storage, contextFactory, Host));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
@ -177,7 +179,7 @@ namespace osu.Game
FileStore.Cleanup(); FileStore.Cleanup();
AddInternal(api); AddInternal(API);
GlobalActionContainer globalBinding; GlobalActionContainer globalBinding;

View File

@ -0,0 +1,48 @@
// Copyright (c) 2007-2018 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.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Overlays.AccountCreation
{
public class AccountCreationBackground : Sprite
{
public AccountCreationBackground()
{
FillMode = FillMode.Fill;
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.CentreRight;
Origin = Anchor.CentreRight;
}
[BackgroundDependencyLoader]
private void load(LargeTextureStore textures)
{
Texture = textures.Get("Backgrounds/registration");
}
protected override void LoadComplete()
{
const float x_movement = 80;
const float initial_move_time = 5000;
const float loop_move_time = 10000;
base.LoadComplete();
this.FadeInFromZero(initial_move_time / 4, Easing.OutQuint);
this.MoveToX(x_movement / 2).MoveToX(0, initial_move_time, Easing.OutQuint);
using (BeginDelayedSequence(initial_move_time))
{
this
.MoveToX(x_movement, loop_move_time, Easing.InOutSine)
.Then().MoveToX(0, loop_move_time, Easing.InOutSine)
.Loop();
}
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Screens;
namespace osu.Game.Overlays.AccountCreation
{
public abstract class AccountCreationScreen : Screen
{
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
Content.FadeOut().Delay(200).FadeIn(200);
}
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
Content.FadeIn(200);
}
protected override void OnSuspending(Screen next)
{
base.OnSuspending(next);
Content.FadeOut(200);
}
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osuTK.Graphics;
namespace osu.Game.Overlays.AccountCreation
{
public class ErrorTextFlowContainer : OsuTextFlowContainer
{
private readonly List<Drawable> errorDrawables = new List<Drawable>();
public ErrorTextFlowContainer()
: base(cp => { cp.TextSize = 12; })
{
}
public void ClearErrors()
{
errorDrawables.ForEach(d => d.Expire());
}
public void AddErrors(string[] errors)
{
ClearErrors();
if (errors == null) return;
foreach (var error in errors)
errorDrawables.AddRange(AddParagraph(error, cp => cp.Colour = Color4.Red));
}
}
}

View File

@ -0,0 +1,220 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays.Settings;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.AccountCreation
{
public class ScreenEntry : AccountCreationScreen
{
private ErrorTextFlowContainer usernameDescription;
private ErrorTextFlowContainer emailAddressDescription;
private ErrorTextFlowContainer passwordDescription;
private OsuTextBox usernameTextBox;
private OsuTextBox emailTextBox;
private OsuPasswordTextBox passwordTextBox;
private APIAccess api;
private ShakeContainer registerShake;
private IEnumerable<Drawable> characterCheckText;
private OsuTextBox[] textboxes;
private ProcessingOverlay processingOverlay;
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api)
{
this.api = api;
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Padding = new MarginPadding(20),
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new OsuSpriteText
{
TextSize = 20,
Margin = new MarginPadding { Vertical = 10 },
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Let's create an account!",
},
usernameTextBox = new OsuTextBox
{
PlaceholderText = "username",
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this
},
usernameDescription = new ErrorTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
},
emailTextBox = new OsuTextBox
{
PlaceholderText = "email address",
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this
},
emailAddressDescription = new ErrorTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
},
passwordTextBox = new OsuPasswordTextBox
{
PlaceholderText = "password",
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this,
},
passwordDescription = new ErrorTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
registerShake = new ShakeContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = new SettingsButton
{
Text = "Register",
Margin = new MarginPadding { Vertical = 20 },
Action = performRegistration
}
}
}
},
},
},
processingOverlay = new ProcessingOverlay { Alpha = 0 }
};
textboxes = new[] { usernameTextBox, emailTextBox, passwordTextBox };
usernameDescription.AddText("This will be your public presence. No profanity, no impersonation. Avoid exposing your own personal details, too!");
emailAddressDescription.AddText("Will be used for notifications, account verification and in the case you forget your password. No spam, ever.");
emailAddressDescription.AddText(" Make sure to get it right!", cp => cp.Font = "Exo2.0-Bold");
passwordDescription.AddText("At least ");
characterCheckText = passwordDescription.AddText("8 characters long");
passwordDescription.AddText(". Choose something long but also something you will remember, like a line from your favourite song.");
passwordTextBox.Current.ValueChanged += text => { characterCheckText.ForEach(s => s.Colour = text.Length == 0 ? Color4.White : Interpolation.ValueAt(text.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); };
}
protected override void Update()
{
base.Update();
if (!textboxes.Any(t => t.HasFocus))
focusNextTextbox();
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
processingOverlay.Hide();
}
private void performRegistration()
{
if (focusNextTextbox())
{
registerShake.Shake();
return;
}
usernameDescription.ClearErrors();
emailAddressDescription.ClearErrors();
passwordDescription.ClearErrors();
processingOverlay.Show();
Task.Run(() =>
{
bool success;
RegistrationRequest.RegistrationRequestErrors errors = null;
try
{
errors = api.CreateAccount(emailTextBox.Text, usernameTextBox.Text, passwordTextBox.Text);
success = errors == null;
}
catch (Exception)
{
success = false;
}
Schedule(() =>
{
if (!success)
{
if (errors != null)
{
usernameDescription.AddErrors(errors.User.Username);
emailAddressDescription.AddErrors(errors.User.Email);
passwordDescription.AddErrors(errors.User.Password);
}
else
{
passwordDescription.AddErrors(new[] { "Something happened... but we're not sure what." });
}
registerShake.Shake();
processingOverlay.Hide();
return;
}
api.Login(emailTextBox.Text, passwordTextBox.Text);
});
});
}
private bool focusNextTextbox()
{
var nextTextbox = nextUnfilledTextbox();
if (nextTextbox != null)
{
Schedule(() => GetContainingInputManager().ChangeFocus(nextTextbox));
return true;
}
return false;
}
private OsuTextBox nextUnfilledTextbox() => textboxes.FirstOrDefault(t => string.IsNullOrEmpty(t.Text));
}
}

View File

@ -0,0 +1,133 @@
// Copyright (c) 2007-2018 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.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Overlays.Settings;
using osu.Game.Screens.Menu;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.AccountCreation
{
public class ScreenWarning : AccountCreationScreen
{
private OsuTextFlowContainer multiAccountExplanationText;
private LinkFlowContainer furtherAssistance;
private APIAccess api;
private const string help_centre_url = "/help/wiki/Help_Centre#login";
protected override void OnEntering(Screen last)
{
if (string.IsNullOrEmpty(api.ProvidedUsername))
{
Content.FadeOut();
Push(new ScreenEntry());
return;
}
base.OnEntering(last);
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, APIAccess api, OsuGame game, TextureStore textures)
{
this.api = api;
if (string.IsNullOrEmpty(api.ProvidedUsername))
return;
Children = new Drawable[]
{
new Sprite
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
},
new Sprite
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Texture = textures.Get(@"Menu/dev-build-footer"),
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Padding = new MarginPadding(20),
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = 150,
Child = new OsuLogo
{
Scale = new Vector2(0.1f),
Anchor = Anchor.Centre,
Triangles = false,
},
},
new OsuSpriteText
{
TextSize = 28,
Font = "Exo2.0-Light",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = Color4.Red,
Text = "Warning! 注意!",
},
multiAccountExplanationText = new OsuTextFlowContainer(cp => { cp.TextSize = 12; })
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
},
new SettingsButton
{
Text = "Help, I can't access my account!",
Margin = new MarginPadding { Top = 50 },
Action = () => game?.OpenUrlExternally(help_centre_url)
},
new DangerousSettingsButton
{
Text = "I understand. This account isn't for me.",
Action = () => Push(new ScreenEntry())
},
furtherAssistance = new LinkFlowContainer(cp => { cp.TextSize = 12; })
{
Margin = new MarginPadding { Top = 20 },
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both
},
}
}
};
multiAccountExplanationText.AddText("Are you ");
multiAccountExplanationText.AddText(api.ProvidedUsername, cp => cp.Colour = colours.BlueLight);
multiAccountExplanationText.AddText("? osu! has a policy of ");
multiAccountExplanationText.AddText("one account per person!", cp => cp.Colour = colours.Yellow);
multiAccountExplanationText.AddText(" Please be aware that creating more than one account per person may result in ");
multiAccountExplanationText.AddText("permanent deactivation of accounts", cp => cp.Colour = colours.Yellow);
multiAccountExplanationText.AddText(".");
furtherAssistance.AddText("Need further assistance? Contact us via our ");
furtherAssistance.AddLink("support system", help_centre_url);
furtherAssistance.AddText(".");
}
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2007-2018 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.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using osu.Game.Screens.Menu;
using osuTK;
namespace osu.Game.Overlays.AccountCreation
{
public class ScreenWelcome : AccountCreationScreen
{
[BackgroundDependencyLoader]
private void load()
{
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Padding = new MarginPadding(20),
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = 150,
Child = new OsuLogo
{
Scale = new Vector2(0.1f),
Anchor = Anchor.Centre,
Triangles = false,
},
},
new OsuSpriteText
{
TextSize = 24,
Font = "Exo2.0-Light",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "New Player Registration",
},
new OsuSpriteText
{
TextSize = 12,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "let's get you started",
},
new SettingsButton
{
Text = "Let's create an account!",
Margin = new MarginPadding { Vertical = 120 },
Action = () => Push(new ScreenWarning())
}
}
};
}
}
}

View File

@ -0,0 +1,110 @@
// Copyright (c) 2007-2018 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.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Overlays.AccountCreation;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class AccountCreationOverlay : OsuFocusedOverlayContainer, IOnlineComponent
{
private const float transition_time = 400;
private ScreenWelcome welcomeScreen;
public AccountCreationOverlay()
{
Size = new Vector2(620, 450);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api)
{
api.Register(this);
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Radius = 5,
Colour = Color4.Black.Opacity(0.2f),
},
Masking = true,
CornerRadius = 10,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
new DelayedLoadWrapper(new AccountCreationBackground(), 0),
new Container
{
RelativeSizeAxes = Axes.Both,
Width = 0.6f,
AutoSizeDuration = transition_time,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.9f,
},
welcomeScreen = new ScreenWelcome(),
}
}
}
}
};
}
protected override void PopIn()
{
base.PopIn();
this.FadeIn(transition_time, Easing.OutQuint);
if (welcomeScreen.ChildScreen != null)
welcomeScreen.MakeCurrent();
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(100);
}
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
case APIState.Offline:
case APIState.Failing:
break;
case APIState.Connecting:
break;
case APIState.Online:
State = Visibility.Hidden;
break;
}
}
}
}

View File

@ -10,11 +10,11 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores

View File

@ -12,12 +12,12 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores

View File

@ -1,72 +1,38 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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 System; using System;
using System.Linq; using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; 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.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
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.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Users; using osu.Game.Users;
using osu.Framework.Graphics.Cursor; using osuTK;
using osu.Framework.Graphics.UserInterface; using osuTK.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Chat namespace osu.Game.Overlays.Chat
{ {
public class ChatLine : Container public class ChatLine : CompositeDrawable
{ {
private static readonly Color4[] username_colours = public const float LEFT_PADDING = default_message_padding + default_horizontal_padding * 2;
{
OsuColour.FromHex("588c7e"),
OsuColour.FromHex("b2a367"),
OsuColour.FromHex("c98f65"),
OsuColour.FromHex("bc5151"),
OsuColour.FromHex("5c8bd6"),
OsuColour.FromHex("7f6ab7"),
OsuColour.FromHex("a368ad"),
OsuColour.FromHex("aa6880"),
OsuColour.FromHex("6fad9b"), private const float default_message_padding = 200;
OsuColour.FromHex("f2e394"),
OsuColour.FromHex("f2ae72"),
OsuColour.FromHex("f98f8a"),
OsuColour.FromHex("7daef4"),
OsuColour.FromHex("a691f2"),
OsuColour.FromHex("c894d3"),
OsuColour.FromHex("d895b0"),
OsuColour.FromHex("53c4a1"), protected virtual float MessagePadding => default_message_padding;
OsuColour.FromHex("eace5c"),
OsuColour.FromHex("ea8c47"),
OsuColour.FromHex("fc4f4f"),
OsuColour.FromHex("3d94ea"),
OsuColour.FromHex("7760ea"),
OsuColour.FromHex("af52c6"),
OsuColour.FromHex("e25696"),
OsuColour.FromHex("677c66"), private const float default_horizontal_padding = 15;
OsuColour.FromHex("9b8732"),
OsuColour.FromHex("8c5129"),
OsuColour.FromHex("8c3030"),
OsuColour.FromHex("1f5d91"),
OsuColour.FromHex("4335a5"),
OsuColour.FromHex("812a96"),
OsuColour.FromHex("992861"),
};
public const float LEFT_PADDING = message_padding + padding * 2; protected virtual float HorizontalPadding => default_horizontal_padding;
private const float padding = 15; protected virtual float TextSize => 20;
private const float message_padding = 200;
private const float action_padding = 3;
private const float text_size = 20;
private Color4 customUsernameColour; private Color4 customUsernameColour;
@ -75,14 +41,13 @@ namespace osu.Game.Overlays.Chat
public ChatLine(Message message) public ChatLine(Message message)
{ {
Message = message; Message = message;
Padding = new MarginPadding { Left = HorizontalPadding, Right = HorizontalPadding };
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Left = padding, Right = padding };
} }
private ChannelManager chatManager; [Resolved(CanBeNull = true)]
private ChannelManager chatManager { get; set; }
private Message message; private Message message;
private OsuSpriteText username; private OsuSpriteText username;
@ -106,10 +71,9 @@ namespace osu.Game.Overlays.Chat
} }
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader]
private void load(OsuColour colours, ChannelManager chatManager) private void load(OsuColour colours)
{ {
this.chatManager = chatManager;
customUsernameColour = colours.ChatBlue; customUsernameColour = colours.ChatBlue;
} }
@ -125,7 +89,7 @@ namespace osu.Game.Overlays.Chat
{ {
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
TextSize = text_size, TextSize = TextSize,
}; };
if (hasBackground) if (hasBackground)
@ -163,11 +127,11 @@ namespace osu.Game.Overlays.Chat
}; };
} }
Children = new Drawable[] InternalChildren = new Drawable[]
{ {
new Container new Container
{ {
Size = new Vector2(message_padding, text_size), Size = new Vector2(MessagePadding, TextSize),
Children = new Drawable[] Children = new Drawable[]
{ {
timestamp = new OsuSpriteText timestamp = new OsuSpriteText
@ -176,7 +140,7 @@ namespace osu.Game.Overlays.Chat
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBold", Font = @"Exo2.0-SemiBold",
FixedWidth = true, FixedWidth = true,
TextSize = text_size * 0.75f, TextSize = TextSize * 0.75f,
}, },
new MessageSender(message.Sender) new MessageSender(message.Sender)
{ {
@ -191,7 +155,7 @@ namespace osu.Game.Overlays.Chat
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = message_padding + padding }, Padding = new MarginPadding { Left = MessagePadding + HorizontalPadding },
Children = new Drawable[] Children = new Drawable[]
{ {
contentFlow = new LinkFlowContainer(t => contentFlow = new LinkFlowContainer(t =>
@ -204,7 +168,7 @@ namespace osu.Game.Overlays.Chat
t.Colour = OsuColour.FromHex(message.Sender.Colour); t.Colour = OsuColour.FromHex(message.Sender.Colour);
} }
t.TextSize = text_size; t.TextSize = TextSize;
}) })
{ {
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
@ -257,5 +221,44 @@ namespace osu.Game.Overlays.Chat
new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction), new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction),
}; };
} }
private static readonly Color4[] username_colours =
{
OsuColour.FromHex("588c7e"),
OsuColour.FromHex("b2a367"),
OsuColour.FromHex("c98f65"),
OsuColour.FromHex("bc5151"),
OsuColour.FromHex("5c8bd6"),
OsuColour.FromHex("7f6ab7"),
OsuColour.FromHex("a368ad"),
OsuColour.FromHex("aa6880"),
OsuColour.FromHex("6fad9b"),
OsuColour.FromHex("f2e394"),
OsuColour.FromHex("f2ae72"),
OsuColour.FromHex("f98f8a"),
OsuColour.FromHex("7daef4"),
OsuColour.FromHex("a691f2"),
OsuColour.FromHex("c894d3"),
OsuColour.FromHex("d895b0"),
OsuColour.FromHex("53c4a1"),
OsuColour.FromHex("eace5c"),
OsuColour.FromHex("ea8c47"),
OsuColour.FromHex("fc4f4f"),
OsuColour.FromHex("3d94ea"),
OsuColour.FromHex("7760ea"),
OsuColour.FromHex("af52c6"),
OsuColour.FromHex("e25696"),
OsuColour.FromHex("677c66"),
OsuColour.FromHex("9b8732"),
OsuColour.FromHex("8c5129"),
OsuColour.FromHex("8c3030"),
OsuColour.FromHex("1f5d91"),
OsuColour.FromHex("4335a5"),
OsuColour.FromHex("812a96"),
OsuColour.FromHex("992861"),
};
} }
} }

View File

@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Chat
public class DrawableChannel : Container public class DrawableChannel : Container
{ {
public readonly Channel Channel; public readonly Channel Channel;
private readonly ChatLineContainer flow; protected readonly ChatLineContainer ChatLineFlow;
private readonly ScrollContainer scroll; private readonly ScrollContainer scroll;
public DrawableChannel(Channel channel) public DrawableChannel(Channel channel)
@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Chat
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Child = flow = new ChatLineContainer Child = ChatLineFlow = new ChatLineContainer
{ {
Padding = new MarginPadding { Left = 20, Right = 20 }, Padding = new MarginPadding { Left = 20, Right = 20 },
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -72,17 +72,19 @@ namespace osu.Game.Overlays.Chat
Channel.PendingMessageResolved -= pendingMessageResolved; Channel.PendingMessageResolved -= pendingMessageResolved;
} }
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
private void newMessagesArrived(IEnumerable<Message> newMessages) private void newMessagesArrived(IEnumerable<Message> newMessages)
{ {
// Add up to last Channel.MAX_HISTORY messages // Add up to last Channel.MAX_HISTORY messages
var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MaxHistory)); var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MaxHistory));
flow.AddRange(displayMessages.Select(m => new ChatLine(m))); ChatLineFlow.AddRange(displayMessages.Select(CreateChatLine));
if (scroll.IsScrolledToEnd(10) || !flow.Children.Any() || newMessages.Any(m => m is LocalMessage)) if (scroll.IsScrolledToEnd(10) || !ChatLineFlow.Children.Any() || newMessages.Any(m => m is LocalMessage))
scrollToEnd(); scrollToEnd();
var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); var staleMessages = ChatLineFlow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
int count = staleMessages.Length - Channel.MaxHistory; int count = staleMessages.Length - Channel.MaxHistory;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -96,25 +98,25 @@ namespace osu.Game.Overlays.Chat
private void pendingMessageResolved(Message existing, Message updated) private void pendingMessageResolved(Message existing, Message updated)
{ {
var found = flow.Children.LastOrDefault(c => c.Message == existing); var found = ChatLineFlow.Children.LastOrDefault(c => c.Message == existing);
if (found != null) if (found != null)
{ {
Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID."); Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID.");
flow.Remove(found); ChatLineFlow.Remove(found);
found.Message = updated; found.Message = updated;
flow.Add(found); ChatLineFlow.Add(found);
} }
} }
private void messageRemoved(Message removed) private void messageRemoved(Message removed)
{ {
flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); ChatLineFlow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire();
} }
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
private class ChatLineContainer : FillFlowContainer<ChatLine> protected class ChatLineContainer : FillFlowContainer<ChatLine>
{ {
protected override int Compare(Drawable x, Drawable y) protected override int Compare(Drawable x, Drawable y)
{ {

View File

@ -52,6 +52,7 @@ namespace osu.Game.Overlays.Chat.Tabs
Child = new DelayedLoadWrapper(new Avatar(value.Users.First()) Child = new DelayedLoadWrapper(new Avatar(value.Users.First())
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
OpenOnClick = { Value = false },
OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
}) })
{ {

View File

@ -6,8 +6,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;

View File

@ -10,7 +10,7 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Online.Leaderboards;
namespace osu.Game.Overlays.Profile.Sections.Recent namespace osu.Game.Overlays.Profile.Sections.Recent
{ {

View File

@ -79,7 +79,10 @@ namespace osu.Game.Overlays.Settings.Sections.General
Margin = new MarginPadding { Bottom = 5 }, Margin = new MarginPadding { Bottom = 5 },
Font = @"Exo2.0-Black", Font = @"Exo2.0-Black",
}, },
form = new LoginForm() form = new LoginForm
{
RequestHide = RequestHide
}
}; };
break; break;
case APIState.Failing: case APIState.Failing:
@ -189,6 +192,8 @@ namespace osu.Game.Overlays.Settings.Sections.General
private TextBox password; private TextBox password;
private APIAccess api; private APIAccess api;
public Action RequestHide;
private void performLogin() private void performLogin()
{ {
if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text)) if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text))
@ -196,7 +201,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
} }
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, OsuConfigManager config) private void load(APIAccess api, OsuConfigManager config, AccountCreationOverlay accountCreation)
{ {
this.api = api; this.api = api;
Direction = FillDirection.Vertical; Direction = FillDirection.Vertical;
@ -207,14 +212,14 @@ namespace osu.Game.Overlays.Settings.Sections.General
{ {
username = new OsuTextBox username = new OsuTextBox
{ {
PlaceholderText = "Email address", PlaceholderText = "email address",
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Text = api?.ProvidedUsername ?? string.Empty, Text = api?.ProvidedUsername ?? string.Empty,
TabbableContentContainer = this TabbableContentContainer = this
}, },
password = new OsuPasswordTextBox password = new OsuPasswordTextBox
{ {
PlaceholderText = "Password", PlaceholderText = "password",
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
TabbableContentContainer = this, TabbableContentContainer = this,
OnCommit = (sender, newText) => performLogin() OnCommit = (sender, newText) => performLogin()
@ -237,7 +242,11 @@ namespace osu.Game.Overlays.Settings.Sections.General
new SettingsButton new SettingsButton
{ {
Text = "Register", Text = "Register",
//Action = registerLink Action = () =>
{
RequestHide();
accountCreation.Show();
}
} }
}; };
} }
@ -322,12 +331,10 @@ namespace osu.Game.Overlays.Settings.Sections.General
public const float LABEL_LEFT_MARGIN = 20; public const float LABEL_LEFT_MARGIN = 20;
private readonly SpriteIcon statusIcon; private readonly SpriteIcon statusIcon;
public Color4 StatusColour public Color4 StatusColour
{ {
set set { statusIcon.FadeColour(value, 500, Easing.OutQuint); }
{
statusIcon.FadeColour(value, 500, Easing.OutQuint);
}
} }
public UserDropdownHeader() public UserDropdownHeader()
@ -368,10 +375,13 @@ namespace osu.Game.Overlays.Settings.Sections.General
private enum UserAction private enum UserAction
{ {
Online, Online,
[Description(@"Do not disturb")] [Description(@"Do not disturb")]
DoNotDisturb, DoNotDisturb,
[Description(@"Appear offline")] [Description(@"Appear offline")]
AppearOffline, AppearOffline,
[Description(@"Sign out")] [Description(@"Sign out")]
SignOut, SignOut,
} }

View File

@ -79,8 +79,6 @@ namespace osu.Game.Screens.Menu
private readonly Container impactContainer; private readonly Container impactContainer;
private const float default_size = 480;
private const double early_activation = 60; private const double early_activation = 60;
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
@ -89,8 +87,6 @@ namespace osu.Game.Screens.Menu
{ {
EarlyActivationMilliseconds = early_activation; EarlyActivationMilliseconds = early_activation;
Size = new Vector2(default_size);
Origin = Anchor.Centre; Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;

View File

@ -28,7 +28,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Storyboards.Drawables; using osu.Game.Storyboards.Drawables;
@ -288,7 +287,7 @@ namespace osu.Game.Screens.Play
if (RulesetContainer.Replay == null) if (RulesetContainer.Replay == null)
scoreManager.Import(score, true); scoreManager.Import(score, true);
Push(new Results(score)); Push(new SoloResults(score));
onCompletionEvent = null; onCompletionEvent = null;
}); });

View File

@ -0,0 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Types;
namespace osu.Game.Screens.Play
{
public class SoloResults : Results
{
public SoloResults(ScoreInfo score)
: base(score)
{
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new IResultPageInfo[]
{
new ScoreOverviewPageInfo(Score, Beatmap),
new BeatmapLeaderboardPageInfo(Score, Beatmap)
};
}
}

View File

@ -1,12 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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.Game.Graphics;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public enum ResultMode public interface IResultPageInfo
{ {
Summary, FontAwesome Icon { get; }
Ranking,
Share string Name { get; }
ResultsPage CreatePage();
} }
} }

View File

@ -3,18 +3,19 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Leaderboards;
using osuTK; using osuTK;
using osu.Framework.Graphics.Shapes;
using osu.Game.Scoring;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking.Pages
{ {
public class ResultsPageRanking : ResultsPage public class RankingResultsPage : ResultsPage
{ {
public ResultsPageRanking(ScoreInfo score, WorkingBeatmap beatmap = null) : base(score, beatmap) public RankingResultsPage(ScoreInfo score, WorkingBeatmap beatmap = null)
: base(score, beatmap)
{ {
} }
@ -28,7 +29,7 @@ namespace osu.Game.Screens.Ranking
Colour = colours.GrayE, Colour = colours.GrayE,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
new Leaderboard new BeatmapLeaderboard
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -4,36 +4,39 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
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.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions;
using osu.Framework.Localisation;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking.Pages
{ {
public class ResultsPageScore : ResultsPage public class ScoreResultsPage : ResultsPage
{ {
private Container scoreContainer; private Container scoreContainer;
private ScoreCounter scoreCounter; private ScoreCounter scoreCounter;
public ResultsPageScore(ScoreInfo score, WorkingBeatmap beatmap) : base(score, beatmap) { } public ScoreResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
private FillFlowContainer<DrawableScoreStatistic> statisticsContainer; private FillFlowContainer<DrawableScoreStatistic> statisticsContainer;

View File

@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; 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.Cursor;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
using osuTK; using osuTK;
@ -13,27 +14,18 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class ResultModeButton : TabItem<ResultMode> public class ResultModeButton : TabItem<IResultPageInfo>, IHasTooltip
{ {
private readonly FontAwesome icon; private readonly FontAwesome icon;
private Color4 activeColour; private Color4 activeColour;
private Color4 inactiveColour; private Color4 inactiveColour;
private CircularContainer colouredPart; private CircularContainer colouredPart;
public ResultModeButton(ResultMode mode) : base(mode) public ResultModeButton(IResultPageInfo mode)
: base(mode)
{ {
switch (mode) icon = mode.Icon;
{ TooltipText = mode.Name;
case ResultMode.Summary:
icon = FontAwesome.fa_asterisk;
break;
case ResultMode.Ranking:
icon = FontAwesome.fa_list;
break;
case ResultMode.Share:
icon = FontAwesome.fa_camera;
break;
}
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -95,5 +87,7 @@ namespace osu.Game.Screens.Ranking
protected override void OnActivated() => colouredPart.FadeColour(activeColour, 200, Easing.OutQuint); protected override void OnActivated() => colouredPart.FadeColour(activeColour, 200, Easing.OutQuint);
protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint); protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint);
public string TooltipText { get; private set; }
} }
} }

View File

@ -7,7 +7,7 @@ using osuTK;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class ResultModeTabControl : TabControl<ResultMode> public class ResultModeTabControl : TabControl<IResultPageInfo>
{ {
public ResultModeTabControl() public ResultModeTabControl()
{ {
@ -19,9 +19,9 @@ namespace osu.Game.Screens.Ranking
TabContainer.Padding = new MarginPadding(5); TabContainer.Padding = new MarginPadding(5);
} }
protected override Dropdown<ResultMode> CreateDropdown() => null; protected override Dropdown<IResultPageInfo> CreateDropdown() => null;
protected override TabItem<ResultMode> CreateTabItem(ResultMode value) => new ResultModeButton(value) protected override TabItem<IResultPageInfo> CreateTabItem(IResultPageInfo value) => new ResultModeButton(value)
{ {
Anchor = TabContainer.Anchor, Anchor = TabContainer.Anchor,
Origin = TabContainer.Origin Origin = TabContainer.Origin

View File

@ -2,6 +2,7 @@
// 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 System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
@ -21,9 +22,8 @@ using osu.Game.Scoring;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class Results : OsuScreen public abstract class Results : OsuScreen
{ {
private readonly ScoreInfo score;
private Container circleOuterBackground; private Container circleOuterBackground;
private Container circleOuter; private Container circleOuter;
private Container circleInner; private Container circleInner;
@ -34,6 +34,8 @@ namespace osu.Game.Screens.Ranking
public override bool AllowBeatmapRulesetChange => false; public override bool AllowBeatmapRulesetChange => false;
protected readonly ScoreInfo Score;
private Container currentPage; private Container currentPage;
private static readonly Vector2 background_blur = new Vector2(20); private static readonly Vector2 background_blur = new Vector2(20);
@ -44,9 +46,9 @@ namespace osu.Game.Screens.Ranking
private const float circle_outer_scale = 0.96f; private const float circle_outer_scale = 0.96f;
public Results(ScoreInfo score) protected Results(ScoreInfo score)
{ {
this.score = score; Score = score;
} }
private const float transition_time = 800; private const float transition_time = 800;
@ -67,7 +69,7 @@ namespace osu.Game.Screens.Ranking
backgroundParallax.FadeOut(); backgroundParallax.FadeOut();
modeChangeButtons.FadeOut(); modeChangeButtons.FadeOut();
currentPage.FadeOut(); currentPage?.FadeOut();
circleOuterBackground circleOuterBackground
.FadeIn(transition_time, Easing.OutQuint) .FadeIn(transition_time, Easing.OutQuint)
@ -90,7 +92,7 @@ namespace osu.Game.Screens.Ranking
using (BeginDelayedSequence(transition_time * 0.4f, true)) using (BeginDelayedSequence(transition_time * 0.4f, true))
{ {
modeChangeButtons.FadeIn(transition_time, Easing.OutQuint); modeChangeButtons.FadeIn(transition_time, Easing.OutQuint);
currentPage.FadeIn(transition_time, Easing.OutQuint); currentPage?.FadeIn(transition_time, Easing.OutQuint);
} }
} }
} }
@ -188,7 +190,7 @@ namespace osu.Game.Screens.Ranking
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = $"{score.MaxCombo}x", Text = $"{Score.MaxCombo}x",
TextSize = 40, TextSize = 40,
RelativePositionAxes = Axes.X, RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
@ -209,7 +211,7 @@ namespace osu.Game.Screens.Ranking
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = $"{score.Accuracy:P2}", Text = $"{Score.Accuracy:P2}",
TextSize = 40, TextSize = 40,
RelativePositionAxes = Axes.X, RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
@ -262,30 +264,22 @@ namespace osu.Game.Screens.Ranking
}, },
}; };
modeChangeButtons.AddItem(ResultMode.Summary); foreach (var t in CreateResultPages())
modeChangeButtons.AddItem(ResultMode.Ranking); modeChangeButtons.AddItem(t);
//modeChangeButtons.AddItem(ResultMode.Share); modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault();
modeChangeButtons.Current.ValueChanged += mode => modeChangeButtons.Current.BindValueChanged(m =>
{ {
currentPage?.FadeOut(); currentPage?.FadeOut();
currentPage?.Expire(); currentPage?.Expire();
switch (mode) currentPage = m?.CreatePage();
{
case ResultMode.Summary:
currentPage = new ResultsPageScore(score, Beatmap.Value);
break;
case ResultMode.Ranking:
currentPage = new ResultsPageRanking(score, Beatmap.Value);
break;
}
if (currentPage != null) if (currentPage != null)
circleInner.Add(currentPage); circleInner.Add(currentPage);
}; }, true);
modeChangeButtons.Current.TriggerChange();
} }
protected abstract IEnumerable<IResultPageInfo> CreateResultPages();
} }
} }

View File

@ -14,7 +14,7 @@ using osuTK.Graphics;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
public class ResultsPage : Container public abstract class ResultsPage : Container
{ {
protected readonly ScoreInfo Score; protected readonly ScoreInfo Score;
protected readonly WorkingBeatmap Beatmap; protected readonly WorkingBeatmap Beatmap;
@ -23,7 +23,7 @@ namespace osu.Game.Screens.Ranking
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
public ResultsPage(ScoreInfo score, WorkingBeatmap beatmap) protected ResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
{ {
Score = score; Score = score;
Beatmap = beatmap; Beatmap = beatmap;

View File

@ -0,0 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Pages;
namespace osu.Game.Screens.Ranking.Types
{
public class BeatmapLeaderboardPageInfo : IResultPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public BeatmapLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public FontAwesome Icon => FontAwesome.fa_list;
public string Name => @"Beatmap Leaderboard";
public ResultsPage CreatePage() => new RankingResultsPage(score, beatmap);
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Pages;
namespace osu.Game.Screens.Ranking.Types
{
public class ScoreOverviewPageInfo : IResultPageInfo
{
public FontAwesome Icon => FontAwesome.fa_asterisk;
public string Name => "Overview";
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public ScoreOverviewPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public ResultsPage CreatePage()
{
return new ScoreResultsPage(score, beatmap);
}
}
}

View File

@ -17,7 +17,7 @@ namespace osu.Game.Screens.Select
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
public readonly BeatmapDetails Details; public readonly BeatmapDetails Details;
public readonly Leaderboard Leaderboard; public readonly BeatmapLeaderboard Leaderboard;
private WorkingBeatmap beatmap; private WorkingBeatmap beatmap;
public WorkingBeatmap Beatmap public WorkingBeatmap Beatmap
@ -52,7 +52,7 @@ namespace osu.Game.Screens.Select
default: default:
Details.Hide(); Details.Hide();
Leaderboard.Scope = (LeaderboardScope)tab - 1; Leaderboard.Scope = (BeatmapLeaderboardScope)tab - 1;
Leaderboard.Show(); Leaderboard.Show();
break; break;
} }
@ -73,7 +73,7 @@ namespace osu.Game.Screens.Select
Alpha = 0, Alpha = 0,
Margin = new MarginPadding { Top = details_padding }, Margin = new MarginPadding { Top = details_padding },
}, },
Leaderboard = new Leaderboard Leaderboard = new BeatmapLeaderboard
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
} }

View File

@ -24,12 +24,12 @@ namespace osu.Game.Screens.Select.Carousel
{ {
base.Filter(criteria); base.Filter(criteria);
bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps; bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || (Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps);
if (!string.IsNullOrEmpty(criteria.SearchText)) foreach (var criteriaTerm in criteria.SearchTerms)
match &= match &=
Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteria.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0) || Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) ||
Beatmap.Version.IndexOf(criteria.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0; Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0;
Filtered.Value = !match; Filtered.Value = !match;
} }

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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 System;
using System.Linq;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.Select.Filter; using osu.Game.Screens.Select.Filter;
@ -10,8 +12,22 @@ namespace osu.Game.Screens.Select
{ {
public GroupMode Group; public GroupMode Group;
public SortMode Sort; public SortMode Sort;
public string SearchText;
public string[] SearchTerms = Array.Empty<string>();
public RulesetInfo Ruleset; public RulesetInfo Ruleset;
public bool AllowConvertedBeatmaps; public bool AllowConvertedBeatmaps;
private string searchText;
public string SearchText
{
get { return searchText; }
set
{
searchText = value;
SearchTerms = searchText.Split(',', ' ', '!').Where(s => !string.IsNullOrEmpty(s)).ToArray();
}
}
} }
} }

View File

@ -0,0 +1,87 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Screens.Select.Leaderboards
{
public class BeatmapLeaderboard : Leaderboard<BeatmapLeaderboardScope, ScoreInfo>
{
public Action<ScoreInfo> ScoreSelected;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (beatmap == value)
return;
beatmap = value;
Scores = null;
UpdateScores();
}
}
[Resolved]
private ScoreManager scoreManager { get; set; }
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
[Resolved]
private APIAccess api { get; set; }
[BackgroundDependencyLoader]
private void load()
{
ruleset.ValueChanged += _ => UpdateScores();
}
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
{
if (Scope == BeatmapLeaderboardScope.Local)
{
Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray();
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
return null;
}
if (Beatmap?.OnlineBeatmapID == null)
{
PlaceholderState = PlaceholderState.Unavailable;
return null;
}
if (Scope != BeatmapLeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
{
PlaceholderState = PlaceholderState.NotSupporter;
return null;
}
var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope);
req.Success += r => scoresCallback?.Invoke(r.Scores);
return req;
}
protected override LeaderboardScore CreateDrawableScore(ScoreInfo model, int index) => new LeaderboardScore(model, index)
{
Action = () => ScoreSelected?.Invoke(model)
};
}
}

View File

@ -3,7 +3,7 @@
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Screens.Select.Leaderboards
{ {
public enum LeaderboardScope public enum BeatmapLeaderboardScope
{ {
Local, Local,
Country, Country,

View File

@ -28,7 +28,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Play;
using osu.Game.Screens.Select.Options; using osu.Game.Screens.Select.Options;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -210,7 +210,7 @@ namespace osu.Game.Screens.Select
}); });
} }
BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s)); BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new SoloResults(s));
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]

View File

@ -8,7 +8,8 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">SOLUTION</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">SOLUTION</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeAccessorOwnerBody/@EntryIndexedValue">HINT</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeAccessorOwnerBody/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeModifiersOrder/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeModifiersOrder/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeRedundantParentheses/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeRedundantParentheses/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeRedundantParentheses/@EntryIndexRemoved">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeMemberModifiers/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeMemberModifiers/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeModifiers/@EntryIndexedValue">WARNING</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeTypeModifiers/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AssignedValueIsNeverUsed/@EntryIndexedValue">HINT</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AssignedValueIsNeverUsed/@EntryIndexedValue">HINT</s:String>