mirror of
https://github.com/ppy/osu
synced 2024-12-14 19:06:07 +00:00
Merge remote-tracking branch 'origin/master' into slider-controlpoint-deletion
# Conflicts: # osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
This commit is contained in:
commit
08bd811326
@ -29,8 +29,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
private readonly Container marker;
|
||||
private readonly Drawable markerRing;
|
||||
|
||||
private bool isClicked;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
@ -101,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
||||
|
||||
Color4 colour = isSegmentSeparator ? colours.Red : colours.Yellow;
|
||||
if (IsHovered || isClicked || IsSelected.Value)
|
||||
if (IsHovered || IsSelected.Value)
|
||||
colour = Color4.White;
|
||||
marker.Colour = colour;
|
||||
}
|
||||
@ -127,21 +125,18 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
isClicked = true;
|
||||
return true;
|
||||
if (RequestSelection != null)
|
||||
{
|
||||
RequestSelection.Invoke(Index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
isClicked = false;
|
||||
return true;
|
||||
}
|
||||
protected override bool OnMouseUp(MouseUpEvent e) => RequestSelection != null;
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
RequestSelection?.Invoke(Index);
|
||||
return true;
|
||||
}
|
||||
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e) => true;
|
||||
|
||||
|
@ -21,15 +21,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
internal readonly Container<PathControlPointPiece> Pieces;
|
||||
private readonly Slider slider;
|
||||
private readonly bool allowSelection;
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IPlacementHandler placementHandler { get; set; }
|
||||
|
||||
public PathControlPointVisualiser(Slider slider)
|
||||
public PathControlPointVisualiser(Slider slider, bool allowSelection)
|
||||
{
|
||||
this.slider = slider;
|
||||
this.allowSelection = allowSelection;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -49,11 +51,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
|
||||
while (slider.Path.ControlPoints.Length > Pieces.Count)
|
||||
{
|
||||
Pieces.Add(new PathControlPointPiece(slider, Pieces.Count)
|
||||
var piece = new PathControlPointPiece(slider, Pieces.Count)
|
||||
{
|
||||
ControlPointsChanged = c => ControlPointsChanged?.Invoke(c),
|
||||
RequestSelection = selectPiece
|
||||
});
|
||||
};
|
||||
|
||||
if (allowSelection)
|
||||
piece.RequestSelection = selectPiece;
|
||||
|
||||
Pieces.Add(piece);
|
||||
}
|
||||
|
||||
while (slider.Path.ControlPoints.Length < Pieces.Count)
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
bodyPiece = new SliderBodyPiece(),
|
||||
headCirclePiece = new HitCirclePiece(),
|
||||
tailCirclePiece = new HitCirclePiece(),
|
||||
new PathControlPointVisualiser(HitObject) { ControlPointsChanged = _ => updateSlider() },
|
||||
new PathControlPointVisualiser(HitObject, false) { ControlPointsChanged = _ => updateSlider() },
|
||||
};
|
||||
|
||||
setState(PlacementState.Initial);
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
BodyPiece = new SliderBodyPiece(),
|
||||
HeadBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.Start),
|
||||
TailBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.End),
|
||||
ControlPointVisualiser = new PathControlPointVisualiser(sliderObject) { ControlPointsChanged = onNewControlPoints },
|
||||
ControlPointVisualiser = new PathControlPointVisualiser(sliderObject, true) { ControlPointsChanged = onNewControlPoints },
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,16 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Ranking.Pages;
|
||||
@ -27,7 +32,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
typeof(ScoreResultsPage),
|
||||
typeof(RetryButton),
|
||||
typeof(ReplayDownloadButton),
|
||||
typeof(LocalLeaderboardPage)
|
||||
typeof(LocalLeaderboardPage),
|
||||
typeof(TestPlayer)
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -43,26 +49,82 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
|
||||
if (beatmapInfo != null)
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
|
||||
}
|
||||
|
||||
LoadScreen(new SoloResults(new ScoreInfo
|
||||
private TestSoloResults createResultsScreen() => new TestSoloResults(new ScoreInfo
|
||||
{
|
||||
TotalScore = 2845370,
|
||||
Accuracy = 0.98,
|
||||
MaxCombo = 123,
|
||||
Rank = ScoreRank.A,
|
||||
Date = DateTimeOffset.Now,
|
||||
Statistics = new Dictionary<HitResult, int>
|
||||
{
|
||||
TotalScore = 2845370,
|
||||
Accuracy = 0.98,
|
||||
MaxCombo = 123,
|
||||
Rank = ScoreRank.A,
|
||||
Date = DateTimeOffset.Now,
|
||||
Statistics = new Dictionary<HitResult, int>
|
||||
{ HitResult.Great, 50 },
|
||||
{ HitResult.Good, 20 },
|
||||
{ HitResult.Meh, 50 },
|
||||
{ HitResult.Miss, 1 }
|
||||
},
|
||||
User = new User
|
||||
{
|
||||
Username = "peppy",
|
||||
}
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void ResultsWithoutPlayer()
|
||||
{
|
||||
TestSoloResults screen = null;
|
||||
|
||||
AddStep("load results", () => Child = new OsuScreenStack(screen = createResultsScreen())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
AddAssert("retry overlay not present", () => screen.RetryOverlay == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResultsWithPlayer()
|
||||
{
|
||||
TestSoloResults screen = null;
|
||||
|
||||
AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen()));
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
AddAssert("retry overlay present", () => screen.RetryOverlay != null);
|
||||
}
|
||||
|
||||
private class TestResultsContainer : Container
|
||||
{
|
||||
[Cached(typeof(Player))]
|
||||
private readonly Player player = new TestPlayer();
|
||||
|
||||
public TestResultsContainer(IScreen screen)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = new OsuScreenStack(screen)
|
||||
{
|
||||
{ HitResult.Great, 50 },
|
||||
{ HitResult.Good, 20 },
|
||||
{ HitResult.Meh, 50 },
|
||||
{ HitResult.Miss, 1 }
|
||||
},
|
||||
User = new User
|
||||
{
|
||||
Username = "peppy",
|
||||
}
|
||||
}));
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSoloResults : SoloResults
|
||||
{
|
||||
public HotkeyRetryOverlay RetryOverlay;
|
||||
|
||||
public TestSoloResults(ScoreInfo score)
|
||||
: base(score)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
RetryOverlay = InternalChildren.OfType<HotkeyRetryOverlay>().SingleOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
@ -23,21 +20,12 @@ namespace osu.Game.Graphics.Containers
|
||||
}
|
||||
|
||||
private OsuGame game;
|
||||
private ChannelManager channelManager;
|
||||
private Action showNotImplementedError;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame game, NotificationOverlay notifications, ChannelManager channelManager)
|
||||
private void load(OsuGame game)
|
||||
{
|
||||
// will be null in tests
|
||||
this.game = game;
|
||||
this.channelManager = channelManager;
|
||||
|
||||
showNotImplementedError = () => notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = @"This link type is not yet supported!",
|
||||
Icon = FontAwesome.Solid.LifeRing,
|
||||
});
|
||||
}
|
||||
|
||||
public void AddLinks(string text, List<Link> links)
|
||||
@ -56,85 +44,47 @@ namespace osu.Game.Graphics.Containers
|
||||
foreach (var link in links)
|
||||
{
|
||||
AddText(text.Substring(previousLinkEnd, link.Index - previousLinkEnd));
|
||||
AddLink(text.Substring(link.Index, link.Length), link.Url, link.Action, link.Argument);
|
||||
AddLink(text.Substring(link.Index, link.Length), link.Action, link.Argument ?? link.Url);
|
||||
previousLinkEnd = link.Index + link.Length;
|
||||
}
|
||||
|
||||
AddText(text.Substring(previousLinkEnd));
|
||||
}
|
||||
|
||||
public IEnumerable<Drawable> AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action<SpriteText> creationParameters = null)
|
||||
=> createLink(AddText(text, creationParameters), text, url, linkType, linkArgument, tooltipText);
|
||||
public void AddLink(string text, string url, Action<SpriteText> creationParameters = null) =>
|
||||
createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url);
|
||||
|
||||
public IEnumerable<Drawable> AddLink(string text, Action action, string tooltipText = null, Action<SpriteText> creationParameters = null)
|
||||
=> createLink(AddText(text, creationParameters), text, tooltipText: tooltipText, action: action);
|
||||
public void AddLink(string text, Action action, string tooltipText = null, Action<SpriteText> creationParameters = null)
|
||||
=> createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, null), tooltipText, action);
|
||||
|
||||
public IEnumerable<Drawable> AddLink(IEnumerable<SpriteText> text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null)
|
||||
public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action<SpriteText> creationParameters = null)
|
||||
=> createLink(AddText(text, creationParameters), new LinkDetails(action, argument), null);
|
||||
|
||||
public void AddLink(IEnumerable<SpriteText> text, LinkAction action = LinkAction.External, string linkArgument = null, string tooltipText = null)
|
||||
{
|
||||
foreach (var t in text)
|
||||
AddArbitraryDrawable(t);
|
||||
|
||||
return createLink(text, null, url, linkType, linkArgument, tooltipText);
|
||||
createLink(text, new LinkDetails(action, linkArgument), tooltipText);
|
||||
}
|
||||
|
||||
public IEnumerable<Drawable> AddUserLink(User user, Action<SpriteText> creationParameters = null)
|
||||
=> createLink(AddText(user.Username, creationParameters), user.Username, null, LinkAction.OpenUserProfile, user.Id.ToString(), "View profile");
|
||||
public void AddUserLink(User user, Action<SpriteText> creationParameters = null)
|
||||
=> createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "View Profile");
|
||||
|
||||
private IEnumerable<Drawable> createLink(IEnumerable<Drawable> drawables, string text, string url = null, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action action = null)
|
||||
private void createLink(IEnumerable<Drawable> drawables, LinkDetails link, string tooltipText, Action action = null)
|
||||
{
|
||||
AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
TooltipText = tooltipText ?? (url != text ? url : string.Empty),
|
||||
Action = action ?? (() =>
|
||||
TooltipText = tooltipText,
|
||||
Action = () =>
|
||||
{
|
||||
switch (linkType)
|
||||
{
|
||||
case LinkAction.OpenBeatmap:
|
||||
// TODO: proper query params handling
|
||||
if (linkArgument != null && int.TryParse(linkArgument.Contains('?') ? linkArgument.Split('?')[0] : linkArgument, out int beatmapId))
|
||||
game?.ShowBeatmap(beatmapId);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenBeatmapSet:
|
||||
if (int.TryParse(linkArgument, out int setId))
|
||||
game?.ShowBeatmapSet(setId);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenChannel:
|
||||
try
|
||||
{
|
||||
channelManager?.OpenChannel(linkArgument);
|
||||
}
|
||||
catch (ChannelNotFoundException)
|
||||
{
|
||||
Logger.Log($"The requested channel \"{linkArgument}\" does not exist");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LinkAction.OpenEditorTimestamp:
|
||||
case LinkAction.JoinMultiplayerMatch:
|
||||
case LinkAction.Spectate:
|
||||
showNotImplementedError?.Invoke();
|
||||
break;
|
||||
|
||||
case LinkAction.External:
|
||||
game?.OpenUrlExternally(url);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenUserProfile:
|
||||
if (long.TryParse(linkArgument, out long userId))
|
||||
game?.ShowUser(userId);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action.");
|
||||
}
|
||||
}),
|
||||
if (action != null)
|
||||
action();
|
||||
else
|
||||
game.HandleLink(link);
|
||||
},
|
||||
});
|
||||
|
||||
return drawables;
|
||||
}
|
||||
|
||||
// We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used.
|
||||
|
@ -81,7 +81,7 @@ namespace osu.Game.Online.Chat
|
||||
//since we just changed the line display text, offset any already processed links.
|
||||
result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0);
|
||||
|
||||
var details = getLinkDetails(linkText);
|
||||
var details = GetLinkDetails(linkText);
|
||||
result.Links.Add(new Link(linkText, index, displayText.Length, linkActionOverride ?? details.Action, details.Argument));
|
||||
|
||||
//adjust the offset for processing the current matches group.
|
||||
@ -98,7 +98,7 @@ namespace osu.Game.Online.Chat
|
||||
var linkText = m.Groups["link"].Value;
|
||||
var indexLength = linkText.Length;
|
||||
|
||||
var details = getLinkDetails(linkText);
|
||||
var details = GetLinkDetails(linkText);
|
||||
var link = new Link(linkText, index, indexLength, details.Action, details.Argument);
|
||||
|
||||
// sometimes an already-processed formatted link can reduce to a simple URL, too
|
||||
@ -109,7 +109,7 @@ namespace osu.Game.Online.Chat
|
||||
}
|
||||
}
|
||||
|
||||
private static LinkDetails getLinkDetails(string url)
|
||||
public static LinkDetails GetLinkDetails(string url)
|
||||
{
|
||||
var args = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
args[0] = args[0].TrimEnd(':');
|
||||
@ -255,17 +255,17 @@ namespace osu.Game.Online.Chat
|
||||
OriginalText = Text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LinkDetails
|
||||
public class LinkDetails
|
||||
{
|
||||
public LinkAction Action;
|
||||
public string Argument;
|
||||
|
||||
public LinkDetails(LinkAction action, string argument)
|
||||
{
|
||||
public LinkAction Action;
|
||||
public string Argument;
|
||||
|
||||
public LinkDetails(LinkAction action, string argument)
|
||||
{
|
||||
Action = action;
|
||||
Argument = argument;
|
||||
}
|
||||
Action = action;
|
||||
Argument = argument;
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,6 +279,7 @@ namespace osu.Game.Online.Chat
|
||||
JoinMultiplayerMatch,
|
||||
Spectate,
|
||||
OpenUserProfile,
|
||||
Custom
|
||||
}
|
||||
|
||||
public class Link : IComparable<Link>
|
||||
|
@ -21,6 +21,7 @@ using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using Humanizer;
|
||||
using osu.Game.Online.API;
|
||||
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
@ -37,6 +38,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
|
||||
private readonly ScoreInfo score;
|
||||
private readonly int rank;
|
||||
private readonly bool allowHighlight;
|
||||
|
||||
private Box background;
|
||||
private Container content;
|
||||
@ -49,17 +51,18 @@ namespace osu.Game.Online.Leaderboards
|
||||
|
||||
private List<ScoreComponentLabel> statisticsLabels;
|
||||
|
||||
public LeaderboardScore(ScoreInfo score, int rank)
|
||||
public LeaderboardScore(ScoreInfo score, int rank, bool allowHighlight = true)
|
||||
{
|
||||
this.score = score;
|
||||
this.rank = rank;
|
||||
this.allowHighlight = allowHighlight;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = HEIGHT;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(IAPIProvider api, OsuColour colour)
|
||||
{
|
||||
var user = score.User;
|
||||
|
||||
@ -100,7 +103,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Colour = user.Id == api.LocalUser.Value.Id && allowHighlight ? colour.Green : Color4.Black,
|
||||
Alpha = background_alpha,
|
||||
},
|
||||
},
|
||||
|
@ -215,31 +215,102 @@ namespace osu.Game
|
||||
|
||||
private ExternalLinkOpener externalLinkOpener;
|
||||
|
||||
public void OpenUrlExternally(string url)
|
||||
/// <summary>
|
||||
/// Handle an arbitrary URL. Displays via in-game overlays where possible.
|
||||
/// This can be called from a non-thread-safe non-game-loaded state.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL to load.</param>
|
||||
public void HandleLink(string url) => HandleLink(MessageFormatter.GetLinkDetails(url));
|
||||
|
||||
/// <summary>
|
||||
/// Handle a specific <see cref="LinkDetails"/>.
|
||||
/// This can be called from a non-thread-safe non-game-loaded state.
|
||||
/// </summary>
|
||||
/// <param name="link">The link to load.</param>
|
||||
public void HandleLink(LinkDetails link) => Schedule(() =>
|
||||
{
|
||||
switch (link.Action)
|
||||
{
|
||||
case LinkAction.OpenBeatmap:
|
||||
// TODO: proper query params handling
|
||||
if (link.Argument != null && int.TryParse(link.Argument.Contains('?') ? link.Argument.Split('?')[0] : link.Argument, out int beatmapId))
|
||||
ShowBeatmap(beatmapId);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenBeatmapSet:
|
||||
if (int.TryParse(link.Argument, out int setId))
|
||||
ShowBeatmapSet(setId);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenChannel:
|
||||
ShowChannel(link.Argument);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenEditorTimestamp:
|
||||
case LinkAction.JoinMultiplayerMatch:
|
||||
case LinkAction.Spectate:
|
||||
waitForReady(() => notifications, _ => notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = @"This link type is not yet supported!",
|
||||
Icon = FontAwesome.Solid.LifeRing,
|
||||
}));
|
||||
break;
|
||||
|
||||
case LinkAction.External:
|
||||
OpenUrlExternally(link.Argument);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenUserProfile:
|
||||
if (long.TryParse(link.Argument, out long userId))
|
||||
ShowUser(userId);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"This {nameof(LinkAction)} ({link.Action.ToString()}) is missing an associated action.");
|
||||
}
|
||||
});
|
||||
|
||||
public void OpenUrlExternally(string url) => waitForReady(() => externalLinkOpener, _ =>
|
||||
{
|
||||
if (url.StartsWith("/"))
|
||||
url = $"{API.Endpoint}{url}";
|
||||
|
||||
externalLinkOpener.OpenUrlExternally(url);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Open a specific channel in chat.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to display.</param>
|
||||
public void ShowChannel(string channel) => waitForReady(() => channelManager, _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
channelManager.OpenChannel(channel);
|
||||
}
|
||||
catch (ChannelNotFoundException)
|
||||
{
|
||||
Logger.Log($"The requested channel \"{channel}\" does not exist");
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Show a beatmap set as an overlay.
|
||||
/// </summary>
|
||||
/// <param name="setId">The set to display.</param>
|
||||
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
|
||||
public void ShowBeatmapSet(int setId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmapSet(setId));
|
||||
|
||||
/// <summary>
|
||||
/// Show a user's profile as an overlay.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user to display.</param>
|
||||
public void ShowUser(long userId) => userProfile.ShowUser(userId);
|
||||
public void ShowUser(long userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId));
|
||||
|
||||
/// <summary>
|
||||
/// Show a beatmap's set as an overlay, displaying the given beatmap.
|
||||
/// </summary>
|
||||
/// <param name="beatmapId">The beatmap to show.</param>
|
||||
public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
|
||||
public void ShowBeatmap(int beatmapId) => waitForReady(() => beatmapSetOverlay, _ => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId));
|
||||
|
||||
/// <summary>
|
||||
/// Present a beatmap at song select immediately.
|
||||
@ -397,6 +468,23 @@ namespace osu.Game
|
||||
performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for the game (and target component) to become loaded and then run an action.
|
||||
/// </summary>
|
||||
/// <param name="retrieveInstance">A function to retrieve a (potentially not-yet-constructed) target instance.</param>
|
||||
/// <param name="action">The action to perform on the instance when load is confirmed.</param>
|
||||
/// <typeparam name="T">The type of the target instance.</typeparam>
|
||||
private void waitForReady<T>(Func<T> retrieveInstance, Action<T> action)
|
||||
where T : Drawable
|
||||
{
|
||||
var instance = retrieveInstance();
|
||||
|
||||
if (ScreenStack == null || ScreenStack.CurrentScreen is StartupScreen || instance?.IsLoaded != true)
|
||||
Schedule(() => waitForReady(retrieveInstance, action));
|
||||
else
|
||||
action(instance);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
@ -110,7 +110,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
t.Font = fontLarge;
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, Online.Chat.LinkAction.External,
|
||||
title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl,
|
||||
creationParameters: t =>
|
||||
{
|
||||
t.Font = fontLarge;
|
||||
@ -140,7 +140,7 @@ namespace osu.Game.Overlays.Changelog
|
||||
t.Colour = entryColour;
|
||||
});
|
||||
else if (entry.GithubUser.GithubUrl != null)
|
||||
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t =>
|
||||
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t =>
|
||||
{
|
||||
t.Font = fontMedium;
|
||||
t.Colour = entryColour;
|
||||
|
@ -81,7 +81,7 @@ namespace osu.Game.Screens.Multi.Components
|
||||
Text = new LocalisedString((beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title)),
|
||||
Font = OsuFont.GetFont(size: TextSize),
|
||||
}
|
||||
}, null, LinkAction.OpenBeatmap, beatmap.OnlineBeatmapID.ToString(), "Open beatmap");
|
||||
}, LinkAction.OpenBeatmap, beatmap.OnlineBeatmapID.ToString(), "Open beatmap");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,146 +116,147 @@ namespace osu.Game.Screens.Ranking
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChild = new AspectContainer
|
||||
{
|
||||
new AspectContainer
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Height = overscan,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Height = overscan,
|
||||
Children = new Drawable[]
|
||||
circleOuterBackground = new CircularContainer
|
||||
{
|
||||
circleOuterBackground = new CircularContainer
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
new Box
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Alpha = 0.2f,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
}
|
||||
}
|
||||
},
|
||||
circleOuter = new CircularContainer
|
||||
{
|
||||
Size = new Vector2(circle_outer_scale),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.4f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 15,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
backgroundParallax = new ParallaxContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ParallaxAmount = 0.01f,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.2f,
|
||||
Texture = Beatmap.Value.Background,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill
|
||||
}
|
||||
}
|
||||
},
|
||||
modeChangeButtons = new ResultModeTabControl
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
Margin = new MarginPadding { Bottom = 110 },
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Text = $"{Score.MaxCombo}x",
|
||||
RelativePositionAxes = Axes.X,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 40),
|
||||
X = 0.1f,
|
||||
Colour = colours.BlueDarker,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "max combo",
|
||||
Font = OsuFont.GetFont(size: 20),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = 0.1f,
|
||||
Colour = colours.Gray6,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Text = $"{Score.Accuracy:P2}",
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 40),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = 0.9f,
|
||||
Colour = colours.BlueDarker,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "accuracy",
|
||||
Font = OsuFont.GetFont(size: 20),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = 0.9f,
|
||||
Colour = colours.Gray6,
|
||||
},
|
||||
}
|
||||
},
|
||||
circleInner = new CircularContainer
|
||||
{
|
||||
Size = new Vector2(0.6f),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.4f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 15,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
Alpha = 0.2f,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
}
|
||||
}
|
||||
},
|
||||
circleOuter = new CircularContainer
|
||||
{
|
||||
Size = new Vector2(circle_outer_scale),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.4f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 15,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
backgroundParallax = new ParallaxContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ParallaxAmount = 0.01f,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.2f,
|
||||
Texture = Beatmap.Value.Background,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill
|
||||
}
|
||||
}
|
||||
},
|
||||
modeChangeButtons = new ResultModeTabControl
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
Margin = new MarginPadding { Bottom = 110 },
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Text = $"{Score.MaxCombo}x",
|
||||
RelativePositionAxes = Axes.X,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 40),
|
||||
X = 0.1f,
|
||||
Colour = colours.BlueDarker,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "max combo",
|
||||
Font = OsuFont.GetFont(size: 20),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = 0.1f,
|
||||
Colour = colours.Gray6,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Text = $"{Score.Accuracy:P2}",
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 40),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = 0.9f,
|
||||
Colour = colours.BlueDarker,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "accuracy",
|
||||
Font = OsuFont.GetFont(size: 20),
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = 0.9f,
|
||||
Colour = colours.Gray6,
|
||||
},
|
||||
}
|
||||
},
|
||||
circleInner = new CircularContainer
|
||||
{
|
||||
Size = new Vector2(0.6f),
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.4f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 15,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
new HotkeyRetryOverlay
|
||||
}
|
||||
};
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
AddInternal(new HotkeyRetryOverlay
|
||||
{
|
||||
Action = () =>
|
||||
{
|
||||
@ -263,8 +264,8 @@ namespace osu.Game.Screens.Ranking
|
||||
|
||||
player?.Restart();
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
var pages = CreateResultPages();
|
||||
|
||||
|
@ -179,7 +179,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override LeaderboardScore CreateDrawableScore(ScoreInfo model, int index) => new LeaderboardScore(model, index)
|
||||
protected override LeaderboardScore CreateDrawableScore(ScoreInfo model, int index) => new LeaderboardScore(model, index, IsOnlineScope)
|
||||
{
|
||||
Action = () => ScoreSelected?.Invoke(model)
|
||||
};
|
||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
if (newScore == null)
|
||||
return;
|
||||
|
||||
LoadComponentAsync(new LeaderboardScore(newScore.Score, newScore.Position)
|
||||
LoadComponentAsync(new LeaderboardScore(newScore.Score, newScore.Position, false)
|
||||
{
|
||||
Action = () => ScoreSelected?.Invoke(newScore.Score)
|
||||
}, drawableScore =>
|
||||
|
Loading…
Reference in New Issue
Block a user