Merge branch 'master' into fix-skin-layout-editor-crash

This commit is contained in:
Bartłomiej Dach 2021-10-24 12:58:56 +02:00
commit d24fbc6ade
No known key found for this signature in database
GPG Key ID: BCECCD4FA41F6497
16 changed files with 393 additions and 255 deletions

View File

@ -0,0 +1,167 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Lounge;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneDrawableLoungeRoom : OsuManualInputManagerTestScene
{
private readonly Room room = new Room
{
HasPassword = { Value = true }
};
[Cached]
protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
private DrawableLoungeRoom drawableRoom;
private SearchTextBox searchTextBox;
private readonly ManualResetEventSlim allowResponseCallback = new ManualResetEventSlim();
[BackgroundDependencyLoader]
private void load()
{
var mockLounge = new Mock<LoungeSubScreen>();
mockLounge
.Setup(l => l.Join(It.IsAny<Room>(), It.IsAny<string>(), It.IsAny<Action<Room>>(), It.IsAny<Action<string>>()))
.Callback<Room, string, Action<Room>, Action<string>>((a, b, c, d) =>
{
Task.Run(() =>
{
allowResponseCallback.Wait();
allowResponseCallback.Reset();
Schedule(() => d?.Invoke("Incorrect password"));
});
});
Dependencies.CacheAs(mockLounge.Object);
}
[SetUpSteps]
public void SetUpSteps()
{
AddStep("create drawable", () =>
{
Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
searchTextBox = new SearchTextBox
{
HoldFocus = true,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding(50),
Width = 500,
Depth = float.MaxValue
},
drawableRoom = new DrawableLoungeRoom(room)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
};
});
}
[Test]
public void TestFocusViaKeyboardCommit()
{
DrawableLoungeRoom.PasswordEntryPopover popover = null;
AddAssert("search textbox has focus", () => checkFocus(searchTextBox));
AddStep("click room twice", () =>
{
InputManager.MoveMouseTo(drawableRoom);
InputManager.Click(MouseButton.Left);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for popover", () => (popover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().SingleOrDefault()) != null);
AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType<OsuPasswordTextBox>().Single()));
AddStep("enter password", () => popover.ChildrenOfType<OsuPasswordTextBox>().Single().Text = "password");
AddStep("commit via enter", () => InputManager.Key(Key.Enter));
AddAssert("popover has focus", () => checkFocus(popover));
AddStep("attempt another enter", () => InputManager.Key(Key.Enter));
AddAssert("popover still has focus", () => checkFocus(popover));
AddStep("unblock response", () => allowResponseCallback.Set());
AddUntilStep("wait for textbox refocus", () => checkFocus(popover.ChildrenOfType<OsuPasswordTextBox>().Single()));
AddStep("press escape", () => InputManager.Key(Key.Escape));
AddStep("press escape", () => InputManager.Key(Key.Escape));
AddUntilStep("search textbox has focus", () => checkFocus(searchTextBox));
}
[Test]
public void TestFocusViaMouseCommit()
{
DrawableLoungeRoom.PasswordEntryPopover popover = null;
AddAssert("search textbox has focus", () => checkFocus(searchTextBox));
AddStep("click room twice", () =>
{
InputManager.MoveMouseTo(drawableRoom);
InputManager.Click(MouseButton.Left);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for popover", () => (popover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().SingleOrDefault()) != null);
AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType<OsuPasswordTextBox>().Single()));
AddStep("enter password", () => popover.ChildrenOfType<OsuPasswordTextBox>().Single().Text = "password");
AddStep("commit via click button", () =>
{
var button = popover.ChildrenOfType<OsuButton>().Single();
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
AddAssert("popover has focus", () => checkFocus(popover));
AddStep("attempt another click", () => InputManager.Click(MouseButton.Left));
AddAssert("popover still has focus", () => checkFocus(popover));
AddStep("unblock response", () => allowResponseCallback.Set());
AddUntilStep("wait for textbox refocus", () => checkFocus(popover.ChildrenOfType<OsuPasswordTextBox>().Single()));
AddStep("click away", () =>
{
InputManager.MoveMouseTo(searchTextBox);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("search textbox has focus", () => checkFocus(searchTextBox));
}
private bool checkFocus(Drawable expected) =>
InputManager.FocusedDrawable == expected;
}
}

View File

@ -5,6 +5,7 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
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.Testing; using osu.Framework.Testing;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch;
@ -25,12 +26,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
[SetUp] [SetUp]
public new void Setup() => Schedule(() => public new void Setup() => Schedule(() =>
{ {
Child = container = new RoomsContainer Child = new PopoverContainer
{ {
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Width = 0.5f, Width = 0.5f,
SelectedRoom = { BindTarget = SelectedRoom }
Child = container = new RoomsContainer
{
SelectedRoom = { BindTarget = SelectedRoom }
}
}; };
}); });

View File

@ -107,27 +107,15 @@ namespace osu.Game.Beatmaps
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public DateTimeOffset Submitted public DateTimeOffset Submitted => OnlineInfo.Submitted;
{
get => OnlineInfo.Submitted;
set => OnlineInfo.Submitted = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public DateTimeOffset? Ranked public DateTimeOffset? Ranked => OnlineInfo.Ranked;
{
get => OnlineInfo.Ranked;
set => OnlineInfo.Ranked = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public DateTimeOffset? LastUpdated public DateTimeOffset? LastUpdated => OnlineInfo.LastUpdated;
{
get => OnlineInfo.LastUpdated;
set => OnlineInfo.LastUpdated = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
@ -135,107 +123,55 @@ namespace osu.Game.Beatmaps
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public bool HasExplicitContent public bool HasExplicitContent => OnlineInfo.HasExplicitContent;
{
get => OnlineInfo.HasExplicitContent;
set => OnlineInfo.HasExplicitContent = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public bool HasVideo public bool HasVideo => OnlineInfo.HasVideo;
{
get => OnlineInfo.HasVideo;
set => OnlineInfo.HasVideo = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public bool HasStoryboard public bool HasStoryboard => OnlineInfo.HasStoryboard;
{
get => OnlineInfo.HasStoryboard;
set => OnlineInfo.HasStoryboard = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public BeatmapSetOnlineCovers Covers public BeatmapSetOnlineCovers Covers => OnlineInfo.Covers;
{
get => OnlineInfo.Covers;
set => OnlineInfo.Covers = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public string Preview public string Preview => OnlineInfo.Preview;
{
get => OnlineInfo.Preview;
set => OnlineInfo.Preview = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public double BPM public double BPM => OnlineInfo.BPM;
{
get => OnlineInfo.BPM;
set => OnlineInfo.BPM = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public int PlayCount public int PlayCount => OnlineInfo.PlayCount;
{
get => OnlineInfo.PlayCount;
set => OnlineInfo.PlayCount = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public int FavouriteCount public int FavouriteCount => OnlineInfo.FavouriteCount;
{
get => OnlineInfo.FavouriteCount;
set => OnlineInfo.FavouriteCount = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public bool HasFavourited public bool HasFavourited => OnlineInfo.HasFavourited;
{
get => OnlineInfo.HasFavourited;
set => OnlineInfo.HasFavourited = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public BeatmapSetOnlineAvailability Availability public BeatmapSetOnlineAvailability Availability => OnlineInfo.Availability;
{
get => OnlineInfo.Availability;
set => OnlineInfo.Availability = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public BeatmapSetOnlineGenre Genre public BeatmapSetOnlineGenre Genre => OnlineInfo.Genre;
{
get => OnlineInfo.Genre;
set => OnlineInfo.Genre = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public BeatmapSetOnlineLanguage Language public BeatmapSetOnlineLanguage Language => OnlineInfo.Language;
{
get => OnlineInfo.Language;
set => OnlineInfo.Language = value;
}
[NotMapped] [NotMapped]
[JsonIgnore] [JsonIgnore]
public int? TrackId public int? TrackId => OnlineInfo?.TrackId;
{
get => OnlineInfo.TrackId;
set => OnlineInfo.TrackId = value;
}
#endregion #endregion
} }

View File

@ -15,87 +15,87 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// The date this beatmap set was submitted to the online listing. /// The date this beatmap set was submitted to the online listing.
/// </summary> /// </summary>
DateTimeOffset Submitted { get; set; } DateTimeOffset Submitted { get; }
/// <summary> /// <summary>
/// The date this beatmap set was ranked. /// The date this beatmap set was ranked.
/// </summary> /// </summary>
DateTimeOffset? Ranked { get; set; } DateTimeOffset? Ranked { get; }
/// <summary> /// <summary>
/// The date this beatmap set was last updated. /// The date this beatmap set was last updated.
/// </summary> /// </summary>
DateTimeOffset? LastUpdated { get; set; } DateTimeOffset? LastUpdated { get; }
/// <summary> /// <summary>
/// The status of this beatmap set. /// The status of this beatmap set.
/// </summary> /// </summary>
BeatmapSetOnlineStatus Status { get; set; } BeatmapSetOnlineStatus Status { get; }
/// <summary> /// <summary>
/// Whether or not this beatmap set has explicit content. /// Whether or not this beatmap set has explicit content.
/// </summary> /// </summary>
bool HasExplicitContent { get; set; } bool HasExplicitContent { get; }
/// <summary> /// <summary>
/// Whether or not this beatmap set has a background video. /// Whether or not this beatmap set has a background video.
/// </summary> /// </summary>
bool HasVideo { get; set; } bool HasVideo { get; }
/// <summary> /// <summary>
/// Whether or not this beatmap set has a storyboard. /// Whether or not this beatmap set has a storyboard.
/// </summary> /// </summary>
bool HasStoryboard { get; set; } bool HasStoryboard { get; }
/// <summary> /// <summary>
/// The different sizes of cover art for this beatmap set. /// The different sizes of cover art for this beatmap set.
/// </summary> /// </summary>
BeatmapSetOnlineCovers Covers { get; set; } BeatmapSetOnlineCovers Covers { get; }
/// <summary> /// <summary>
/// A small sample clip of this beatmap set's song. /// A small sample clip of this beatmap set's song.
/// </summary> /// </summary>
string Preview { get; set; } string Preview { get; }
/// <summary> /// <summary>
/// The beats per minute of this beatmap set's song. /// The beats per minute of this beatmap set's song.
/// </summary> /// </summary>
double BPM { get; set; } double BPM { get; }
/// <summary> /// <summary>
/// The amount of plays this beatmap set has. /// The amount of plays this beatmap set has.
/// </summary> /// </summary>
int PlayCount { get; set; } int PlayCount { get; }
/// <summary> /// <summary>
/// The amount of people who have favourited this beatmap set. /// The amount of people who have favourited this beatmap set.
/// </summary> /// </summary>
int FavouriteCount { get; set; } int FavouriteCount { get; }
/// <summary> /// <summary>
/// Whether this beatmap set has been favourited by the current user. /// Whether this beatmap set has been favourited by the current user.
/// </summary> /// </summary>
bool HasFavourited { get; set; } bool HasFavourited { get; }
/// <summary> /// <summary>
/// The availability of this beatmap set. /// The availability of this beatmap set.
/// </summary> /// </summary>
BeatmapSetOnlineAvailability Availability { get; set; } BeatmapSetOnlineAvailability Availability { get; }
/// <summary> /// <summary>
/// The song genre of this beatmap set. /// The song genre of this beatmap set.
/// </summary> /// </summary>
BeatmapSetOnlineGenre Genre { get; set; } BeatmapSetOnlineGenre Genre { get; }
/// <summary> /// <summary>
/// The song language of this beatmap set. /// The song language of this beatmap set.
/// </summary> /// </summary>
BeatmapSetOnlineLanguage Language { get; set; } BeatmapSetOnlineLanguage Language { get; }
/// <summary> /// <summary>
/// The track ID of this beatmap set. /// The track ID of this beatmap set.
/// Non-null only if the track is linked to a featured artist track entry. /// Non-null only if the track is linked to a featured artist track entry.
/// </summary> /// </summary>
int? TrackId { get; set; } int? TrackId { get; }
} }
} }

View File

@ -218,7 +218,9 @@ namespace osu.Game.Configuration
rawValue: skinName, rawValue: skinName,
name: SkinSettingsStrings.SkinSectionHeader, name: SkinSettingsStrings.SkinSectionHeader,
value: skinName, value: skinName,
shortcut: $"{GlobalActionKeyBindingStrings.RandomSkin}: {LookupKeyBindings(GlobalAction.RandomSkin)}" shortcut: new TranslatableString(@"_", @"{0}: {1}",
GlobalActionKeyBindingStrings.RandomSkin,
LookupKeyBindings(GlobalAction.RandomSkin))
); );
}), }),
new TrackedSetting<float>(OsuSetting.UIScale, scale => new SettingDescription( new TrackedSetting<float>(OsuSetting.UIScale, scale => new SettingDescription(

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests.Responses namespace osu.Game.Online.API.Requests.Responses
{ {
public class APIBeatmap : BeatmapMetadata, IBeatmapInfo public class APIBeatmap : IBeatmapInfo
{ {
[JsonProperty(@"id")] [JsonProperty(@"id")]
public int OnlineID { get; set; } public int OnlineID { get; set; }
@ -24,6 +24,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty("checksum")] [JsonProperty("checksum")]
public string Checksum { get; set; } = string.Empty; public string Checksum { get; set; } = string.Empty;
[JsonProperty(@"user_id")]
public int AuthorID { get; set; }
[JsonProperty(@"beatmapset")] [JsonProperty(@"beatmapset")]
public APIBeatmapSet? BeatmapSet { get; set; } public APIBeatmapSet? BeatmapSet { get; set; }
@ -51,8 +54,10 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"accuracy")] [JsonProperty(@"accuracy")]
private float overallDifficulty { get; set; } private float overallDifficulty { get; set; }
public double Length => lengthInSeconds * 1000;
[JsonProperty(@"total_length")] [JsonProperty(@"total_length")]
public double Length { get; set; } private double lengthInSeconds { get; set; }
[JsonProperty(@"count_circles")] [JsonProperty(@"count_circles")]
private int circleCount { get; set; } private int circleCount { get; set; }
@ -75,7 +80,7 @@ namespace osu.Game.Online.API.Requests.Responses
return new BeatmapInfo return new BeatmapInfo
{ {
Metadata = set?.Metadata ?? this, Metadata = set?.Metadata ?? new BeatmapMetadata(),
Ruleset = rulesets.GetRuleset(RulesetID), Ruleset = rulesets.GetRuleset(RulesetID),
StarDifficulty = StarRating, StarDifficulty = StarRating,
OnlineBeatmapID = OnlineID, OnlineBeatmapID = OnlineID,
@ -106,7 +111,7 @@ namespace osu.Game.Online.API.Requests.Responses
#region Implementation of IBeatmapInfo #region Implementation of IBeatmapInfo
public IBeatmapMetadataInfo Metadata => this; public IBeatmapMetadataInfo Metadata => (BeatmapSet as IBeatmapSetInfo)?.Metadata ?? new BeatmapMetadata();
public IBeatmapDifficultyInfo Difficulty => new BeatmapDifficulty public IBeatmapDifficultyInfo Difficulty => new BeatmapDifficulty
{ {

View File

@ -8,12 +8,13 @@ using Newtonsoft.Json;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Users;
#nullable enable #nullable enable
namespace osu.Game.Online.API.Requests.Responses namespace osu.Game.Online.API.Requests.Responses
{ {
public class APIBeatmapSet : BeatmapMetadata, IBeatmapSetOnlineInfo, IBeatmapSetInfo public class APIBeatmapSet : IBeatmapSetOnlineInfo, IBeatmapSetInfo
{ {
[JsonProperty(@"covers")] [JsonProperty(@"covers")]
public BeatmapSetOnlineCovers Covers { get; set; } public BeatmapSetOnlineCovers Covers { get; set; }
@ -63,10 +64,44 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"track_id")] [JsonProperty(@"track_id")]
public int? TrackId { get; set; } public int? TrackId { get; set; }
public string Title { get; set; } = string.Empty;
[JsonProperty("title_unicode")]
public string TitleUnicode { get; set; } = string.Empty;
public string Artist { get; set; } = string.Empty;
[JsonProperty("artist_unicode")]
public string ArtistUnicode { get; set; } = string.Empty;
public User? Author = new User();
/// <summary>
/// Helper property to deserialize a username to <see cref="User"/>.
/// </summary>
[JsonProperty(@"user_id")] [JsonProperty(@"user_id")]
private int creatorId public int AuthorID
{ {
set => Author.Id = value; get => Author?.Id ?? 1;
set
{
Author ??= new User();
Author.Id = value;
}
}
/// <summary>
/// Helper property to deserialize a username to <see cref="User"/>.
/// </summary>
[JsonProperty(@"creator")]
public string AuthorString
{
get => Author?.Username ?? string.Empty;
set
{
Author ??= new User();
Author.Username = value;
}
} }
[JsonProperty(@"availability")] [JsonProperty(@"availability")]
@ -78,6 +113,11 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"language")] [JsonProperty(@"language")]
public BeatmapSetOnlineLanguage Language { get; set; } public BeatmapSetOnlineLanguage Language { get; set; }
public string Source { get; set; } = string.Empty;
[JsonProperty(@"tags")]
public string Tags { get; set; } = string.Empty;
[JsonProperty(@"beatmaps")] [JsonProperty(@"beatmaps")]
private IEnumerable<APIBeatmap> beatmaps { get; set; } = Array.Empty<APIBeatmap>(); private IEnumerable<APIBeatmap> beatmaps { get; set; } = Array.Empty<APIBeatmap>();
@ -86,7 +126,7 @@ namespace osu.Game.Online.API.Requests.Responses
var beatmapSet = new BeatmapSetInfo var beatmapSet = new BeatmapSetInfo
{ {
OnlineBeatmapSetID = OnlineID, OnlineBeatmapSetID = OnlineID,
Metadata = this, Metadata = metadata,
Status = Status, Status = Status,
Metrics = new BeatmapSetMetrics { Ratings = ratings }, Metrics = new BeatmapSetMetrics { Ratings = ratings },
OnlineInfo = this OnlineInfo = this
@ -103,11 +143,23 @@ namespace osu.Game.Online.API.Requests.Responses
return beatmapSet; return beatmapSet;
} }
private BeatmapMetadata metadata => new BeatmapMetadata
{
Title = Title,
TitleUnicode = TitleUnicode,
Artist = Artist,
ArtistUnicode = ArtistUnicode,
AuthorID = AuthorID,
Author = Author,
Source = Source,
Tags = Tags,
};
#region Implementation of IBeatmapSetInfo #region Implementation of IBeatmapSetInfo
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => beatmaps; IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => beatmaps;
IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => this; IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => metadata;
DateTimeOffset IBeatmapSetInfo.DateAdded => throw new NotImplementedException(); DateTimeOffset IBeatmapSetInfo.DateAdded => throw new NotImplementedException();
IEnumerable<INamedFileUsage> IBeatmapSetInfo.Files => throw new NotImplementedException(); IEnumerable<INamedFileUsage> IBeatmapSetInfo.Files => throw new NotImplementedException();

View File

@ -2,8 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests.Responses namespace osu.Game.Online.API.Requests.Responses
{ {
@ -16,17 +14,19 @@ namespace osu.Game.Online.API.Requests.Responses
public int PlayCount { get; set; } public int PlayCount { get; set; }
[JsonProperty("beatmap")] [JsonProperty("beatmap")]
private BeatmapInfo beatmapInfo { get; set; } private APIBeatmap beatmap { get; set; }
[JsonProperty] public APIBeatmap BeatmapInfo
private APIBeatmapSet beatmapSet { get; set; }
public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets)
{ {
BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); get
beatmapInfo.BeatmapSet = setInfo; {
beatmapInfo.Metadata = setInfo.Metadata; // old osu-web code doesn't nest set.
return beatmapInfo; beatmap.BeatmapSet = BeatmapSet;
return beatmap;
}
} }
[JsonProperty("beatmapset")]
public APIBeatmapSet BeatmapSet { get; set; }
} }
} }

View File

@ -15,9 +15,9 @@ namespace osu.Game.Overlays.Profile.Sections
/// </summary> /// </summary>
public abstract class BeatmapMetadataContainer : OsuHoverContainer public abstract class BeatmapMetadataContainer : OsuHoverContainer
{ {
private readonly BeatmapInfo beatmapInfo; private readonly IBeatmapInfo beatmapInfo;
protected BeatmapMetadataContainer(BeatmapInfo beatmapInfo) protected BeatmapMetadataContainer(IBeatmapInfo beatmapInfo)
: base(HoverSampleSet.Submit) : base(HoverSampleSet.Submit)
{ {
this.beatmapInfo = beatmapInfo; this.beatmapInfo = beatmapInfo;
@ -30,10 +30,7 @@ namespace osu.Game.Overlays.Profile.Sections
{ {
Action = () => Action = () =>
{ {
if (beatmapInfo.OnlineBeatmapID != null) beatmapSetOverlay?.FetchAndShowBeatmap(beatmapInfo.OnlineID);
beatmapSetOverlay?.FetchAndShowBeatmap(beatmapInfo.OnlineBeatmapID.Value);
else if (beatmapInfo.BeatmapSet?.OnlineBeatmapSetID != null)
beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapInfo.BeatmapSet.OnlineBeatmapSetID.Value);
}; };
Child = new FillFlowContainer Child = new FillFlowContainer
@ -43,6 +40,6 @@ namespace osu.Game.Overlays.Profile.Sections
}; };
} }
protected abstract Drawable[] CreateText(BeatmapInfo beatmapInfo); protected abstract Drawable[] CreateText(IBeatmapInfo beatmapInfo);
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -13,6 +14,7 @@ using osu.Game.Graphics.Sprites;
using osuTK; using osuTK;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Resources.Localisation.Web; using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.Profile.Sections.Historical namespace osu.Game.Overlays.Profile.Sections.Historical
@ -22,13 +24,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
private const int cover_width = 100; private const int cover_width = 100;
private const int corner_radius = 6; private const int corner_radius = 6;
private readonly BeatmapInfo beatmapInfo; private readonly APIUserMostPlayedBeatmap mostPlayed;
private readonly int playCount;
public DrawableMostPlayedBeatmap(BeatmapInfo beatmapInfo, int playCount) public DrawableMostPlayedBeatmap(APIUserMostPlayedBeatmap mostPlayed)
{ {
this.beatmapInfo = beatmapInfo; this.mostPlayed = mostPlayed;
this.playCount = playCount;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 50; Height = 50;
@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{ {
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Width = cover_width, Width = cover_width,
BeatmapSet = beatmapInfo.BeatmapSet, BeatmapSet = mostPlayed.BeatmapSet,
}, },
new Container new Container
{ {
@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{ {
new MostPlayedBeatmapMetadataContainer(beatmapInfo), new MostPlayedBeatmapMetadataContainer(mostPlayed.BeatmapInfo),
new LinkFlowContainer(t => new LinkFlowContainer(t =>
{ {
t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular); t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular);
@ -89,11 +89,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
}.With(d => }.With(d =>
{ {
d.AddText("mapped by "); d.AddText("mapped by ");
d.AddUserLink(beatmapInfo.Metadata.Author); d.AddUserLink(mostPlayed.BeatmapSet.Author);
}), }),
} }
}, },
new PlayCountText(playCount) new PlayCountText(mostPlayed.PlayCount)
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight Origin = Anchor.CentreRight
@ -120,26 +120,41 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer
{ {
public MostPlayedBeatmapMetadataContainer(BeatmapInfo beatmapInfo) public MostPlayedBeatmapMetadataContainer(IBeatmapInfo beatmapInfo)
: base(beatmapInfo) : base(beatmapInfo)
{ {
} }
protected override Drawable[] CreateText(BeatmapInfo beatmapInfo) => new Drawable[] protected override Drawable[] CreateText(IBeatmapInfo beatmapInfo)
{ {
new OsuSpriteText var metadata = beatmapInfo.Metadata;
Debug.Assert(metadata != null);
return new Drawable[]
{ {
Text = new RomanisableString( new OsuSpriteText
$"{beatmapInfo.Metadata.TitleUnicode ?? beatmapInfo.Metadata.Title} [{beatmapInfo.Version}] ", {
$"{beatmapInfo.Metadata.Title ?? beatmapInfo.Metadata.TitleUnicode} [{beatmapInfo.Version}] "), Text = new RomanisableString(metadata.TitleUnicode, metadata.Title),
Font = OsuFont.GetFont(weight: FontWeight.Bold) Font = OsuFont.GetFont(weight: FontWeight.Bold)
}, },
new OsuSpriteText new OsuSpriteText
{ {
Text = "by " + new RomanisableString(beatmapInfo.Metadata.ArtistUnicode, beatmapInfo.Metadata.Artist), Text = $" [{beatmapInfo.DifficultyName}]",
Font = OsuFont.GetFont(weight: FontWeight.Regular) Font = OsuFont.GetFont(weight: FontWeight.Bold)
}, },
}; new OsuSpriteText
{
Text = " by ",
Font = OsuFont.GetFont(weight: FontWeight.Regular)
},
new OsuSpriteText
{
Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist),
Font = OsuFont.GetFont(weight: FontWeight.Regular)
},
};
}
} }
private class PlayCountText : CompositeDrawable, IHasTooltip private class PlayCountText : CompositeDrawable, IHasTooltip

View File

@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
protected override APIRequest<List<APIUserMostPlayedBeatmap>> CreateRequest() => protected override APIRequest<List<APIUserMostPlayedBeatmap>> CreateRequest() =>
new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage);
protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap model) => protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) =>
new DrawableMostPlayedBeatmap(model.GetBeatmapInfo(Rulesets), model.PlayCount); new DrawableMostPlayedBeatmap(mostPlayed);
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -245,30 +246,42 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
private class ScoreBeatmapMetadataContainer : BeatmapMetadataContainer private class ScoreBeatmapMetadataContainer : BeatmapMetadataContainer
{ {
public ScoreBeatmapMetadataContainer(BeatmapInfo beatmapInfo) public ScoreBeatmapMetadataContainer(IBeatmapInfo beatmapInfo)
: base(beatmapInfo) : base(beatmapInfo)
{ {
} }
protected override Drawable[] CreateText(BeatmapInfo beatmapInfo) => new Drawable[] protected override Drawable[] CreateText(IBeatmapInfo beatmapInfo)
{ {
new OsuSpriteText var metadata = beatmapInfo.Metadata;
Debug.Assert(metadata != null);
return new Drawable[]
{ {
Anchor = Anchor.BottomLeft, new OsuSpriteText
Origin = Anchor.BottomLeft, {
Text = new RomanisableString( Anchor = Anchor.BottomLeft,
$"{beatmapInfo.Metadata.TitleUnicode ?? beatmapInfo.Metadata.Title} ", Origin = Anchor.BottomLeft,
$"{beatmapInfo.Metadata.Title ?? beatmapInfo.Metadata.TitleUnicode} "), Text = new RomanisableString(metadata.TitleUnicode, metadata.Title),
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true) Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true)
}, },
new OsuSpriteText new OsuSpriteText
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Text = "by " + new RomanisableString(beatmapInfo.Metadata.ArtistUnicode, beatmapInfo.Metadata.Artist), Text = " by ",
Font = OsuFont.GetFont(size: 12, italics: true) Font = OsuFont.GetFont(size: 12, italics: true)
}, },
}; new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist),
Font = OsuFont.GetFont(size: 12, italics: true)
},
};
}
} }
} }
} }

View File

@ -1,72 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components
{
public class BeatmapTypeInfo : OnlinePlayComposite
{
private LinkFlowContainer beatmapAuthor;
public BeatmapTypeInfo()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
LayoutDuration = 100,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new ModeTypeInfo(),
new Container
{
AutoSizeAxes = Axes.X,
Height = 30,
Margin = new MarginPadding { Left = 5 },
Children = new Drawable[]
{
new BeatmapTitle(),
beatmapAuthor = new LinkFlowContainer(s => s.Font = s.Font.With(size: 14))
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both
},
},
},
}
};
Playlist.CollectionChanged += (_, __) => updateInfo();
updateInfo();
}
private void updateInfo()
{
beatmapAuthor.Clear();
var beatmap = Playlist.FirstOrDefault()?.Beatmap;
if (beatmap != null)
{
beatmapAuthor.AddText("mapped by ", s => s.Colour = OsuColour.Gray(0.8f));
beatmapAuthor.AddUserLink(beatmap.Value.Metadata.Author);
}
}
}
}

View File

@ -103,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
public IEnumerable<string> FilterTerms => new[] { Room.Name.Value }; public IEnumerable<string> FilterTerms => new[] { Room.Name.Value };
private bool matchingFilter; private bool matchingFilter = true;
public bool MatchingFilter public bool MatchingFilter
{ {
@ -181,6 +181,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private LoungeSubScreen lounge { get; set; } private LoungeSubScreen lounge { get; set; }
public override bool HandleNonPositionalInput => true;
protected override bool BlockNonPositionalInput => true;
public PasswordEntryPopover(Room room) public PasswordEntryPopover(Room room)
{ {
this.room = room; this.room = room;
@ -200,6 +204,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
Spacing = new Vector2(5), Spacing = new Vector2(5),
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
LayoutDuration = 500,
LayoutEasing = Easing.OutQuint,
Children = new Drawable[] Children = new Drawable[]
{ {
new FillFlowContainer new FillFlowContainer
@ -230,10 +236,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
sampleJoinFail = audio.Samples.Get(@"UI/password-fail"); sampleJoinFail = audio.Samples.Get(@"UI/password-fail");
joinButton.Action = () => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); joinButton.Action = performJoin;
} }
private void joinFailed(string error) protected override void LoadComplete()
{
base.LoadComplete();
Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox));
passwordTextbox.OnCommit += (_, __) => performJoin();
}
private void performJoin()
{
lounge?.Join(room, passwordTextbox.Text, null, joinFailed);
GetContainingInputManager().TriggerFocusContention(passwordTextbox);
}
private void joinFailed(string error) => Schedule(() =>
{ {
passwordTextbox.Text = string.Empty; passwordTextbox.Text = string.Empty;
@ -249,15 +269,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
Body.Shake(); Body.Shake();
sampleJoinFail?.Play(); sampleJoinFail?.Play();
} });
protected override void LoadComplete()
{
base.LoadComplete();
Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox));
passwordTextbox.OnCommit += (_, __) => lounge?.Join(room, passwordTextbox.Text, null, joinFailed);
}
} }
} }
} }

View File

@ -289,7 +289,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
popoverContainer.HidePopover(); popoverContainer.HidePopover();
} }
public void Join(Room room, string password, Action<Room> onSuccess = null, Action<string> onFailure = null) => Schedule(() => public virtual void Join(Room room, string password, Action<Room> onSuccess = null, Action<string> onFailure = null) => Schedule(() =>
{ {
if (joiningRoomOperation != null) if (joiningRoomOperation != null)
return; return;

View File

@ -106,6 +106,7 @@ namespace osu.Game.Screens.Play
this.TransformBindableTo(trackFreq, 0, duration).OnComplete(_ => this.TransformBindableTo(trackFreq, 0, duration).OnComplete(_ =>
{ {
RemoveFilters();
OnComplete?.Invoke(); OnComplete?.Invoke();
}); });
@ -137,6 +138,9 @@ namespace osu.Game.Screens.Play
public void RemoveFilters() public void RemoveFilters()
{ {
if (filters.Parent == null)
return;
RemoveInternal(filters); RemoveInternal(filters);
filters.Dispose(); filters.Dispose();