1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 10:12:54 +08:00

Merge branch 'master' into comment-editor-1

This commit is contained in:
Dean Herbert 2023-01-09 15:52:17 +09:00
commit d2f4ffcc5b
139 changed files with 1339 additions and 584 deletions

View File

@ -18,6 +18,36 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
{
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
[Test]
public void TestAlwaysHidden()
{
CreateModTest(new ModTestData
{
Mod = new CatchModNoScope
{
HiddenComboCount = { Value = 0 },
},
Autoplay = true,
PassCondition = () => Player.ScoreProcessor.Combo.Value == 2,
Beatmap = new Beatmap
{
HitObjects = new List<HitObject>
{
new Fruit
{
X = CatchPlayfield.CENTER_X * 0.5f,
StartTime = 1000,
},
new Fruit
{
X = CatchPlayfield.CENTER_X * 1.5f,
StartTime = 2000,
}
}
}
});
}
[Test]
public void TestVisibleDuringBreak()
{

View File

@ -26,6 +26,9 @@ namespace osu.Game.Rulesets.Catch.Mods
var catchPlayfield = (CatchPlayfield)playfield;
bool shouldAlwaysShowCatcher = IsBreakTime.Value;
float targetAlpha = shouldAlwaysShowCatcher ? 1 : ComboBasedAlpha;
// AlwaysPresent required for catcher to still act on input when fully hidden.
catchPlayfield.CatcherArea.AlwaysPresent = true;
catchPlayfield.CatcherArea.Alpha = (float)Interpolation.Lerp(catchPlayfield.CatcherArea.Alpha, targetAlpha, Math.Clamp(catchPlayfield.Time.Elapsed / TRANSITION_DURATION, 0, 1));
}
}

View File

@ -11,7 +11,6 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Catch.UI
{
@ -106,41 +105,17 @@ namespace osu.Game.Rulesets.Catch.UI
return false;
}
protected override bool OnMouseDown(MouseDownEvent e)
{
return updateAction(e.Button, getTouchCatchActionFromInput(e.ScreenSpaceMousePosition));
}
protected override bool OnTouchDown(TouchDownEvent e)
{
return updateAction(e.Touch.Source, getTouchCatchActionFromInput(e.ScreenSpaceTouch.Position));
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
Show();
TouchCatchAction? action = getTouchCatchActionFromInput(e.ScreenSpaceMousePosition);
// multiple mouse buttons may be pressed and handling the same action.
foreach (MouseButton button in e.PressedButtons)
updateAction(button, action);
return false;
}
protected override void OnTouchMove(TouchMoveEvent e)
{
updateAction(e.Touch.Source, getTouchCatchActionFromInput(e.ScreenSpaceTouch.Position));
base.OnTouchMove(e);
}
protected override void OnMouseUp(MouseUpEvent e)
{
updateAction(e.Button, null);
base.OnMouseUp(e);
}
protected override void OnTouchUp(TouchUpEvent e)
{
updateAction(e.Touch.Source, null);

View File

@ -209,18 +209,6 @@ namespace osu.Game.Rulesets.Mania.UI
keyBindingContainer = maniaInputManager?.KeyBindingContainer;
}
protected override bool OnMouseDown(MouseDownEvent e)
{
keyBindingContainer?.TriggerPressed(column.Action.Value);
return base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseUpEvent e)
{
keyBindingContainer?.TriggerReleased(column.Action.Value);
base.OnMouseUp(e);
}
protected override bool OnTouchDown(TouchDownEvent e)
{
keyBindingContainer?.TriggerPressed(column.Action.Value);

View File

@ -1,35 +1,64 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning.Argon
{
public partial class ArgonFollowCircle : FollowCircle
{
private readonly CircularContainer circleContainer;
private readonly Box circleFill;
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
[Resolved(canBeNull: true)]
private DrawableHitObject? parentObject { get; set; }
public ArgonFollowCircle()
{
InternalChild = new CircularContainer
InternalChild = circleContainer = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 4,
BorderColour = ColourInfo.GradientVertical(Colour4.FromHex("FC618F"), Colour4.FromHex("BB1A41")),
Blending = BlendingParameters.Additive,
Child = new Box
Child = circleFill = new Box
{
Colour = ColourInfo.GradientVertical(Colour4.FromHex("FC618F"), Colour4.FromHex("BB1A41")),
RelativeSizeAxes = Axes.Both,
Alpha = 0.3f,
}
};
}
[BackgroundDependencyLoader]
private void load()
{
if (parentObject != null)
accentColour.BindTo(parentObject.AccentColour);
}
protected override void LoadComplete()
{
base.LoadComplete();
accentColour.BindValueChanged(colour =>
{
circleContainer.BorderColour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.5f));
circleFill.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.5f));
}, true);
}
protected override void OnSliderPress()
{
const float duration = 300f;

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@ -21,6 +23,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
private readonly Vector2 defaultIconScale = new Vector2(0.6f, 0.8f);
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
[Resolved(canBeNull: true)]
private DrawableHitObject? parentObject { get; set; }
@ -37,7 +41,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
{
fill = new Box
{
Colour = ColourInfo.GradientVertical(Colour4.FromHex("FC618F"), Colour4.FromHex("BB1A41")),
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -53,10 +56,22 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
};
}
[BackgroundDependencyLoader]
private void load()
{
if (parentObject != null)
accentColour.BindTo(parentObject.AccentColour);
}
protected override void LoadComplete()
{
base.LoadComplete();
accentColour.BindValueChanged(colour =>
{
fill.Colour = ColourInfo.GradientVertical(colour.NewValue, colour.NewValue.Darken(0.5f));
}, true);
if (parentObject != null)
{
parentObject.ApplyCustomUpdateState += updateStateTransforms;

View File

@ -107,24 +107,6 @@ namespace osu.Game.Rulesets.Taiko.UI
return false;
}
protected override bool OnMouseDown(MouseDownEvent e)
{
if (!validMouse(e))
return false;
handleDown(e.Button, e.ScreenSpaceMousePosition);
return true;
}
protected override void OnMouseUp(MouseUpEvent e)
{
if (!validMouse(e))
return;
handleUp(e.Button);
base.OnMouseUp(e);
}
protected override bool OnTouchDown(TouchDownEvent e)
{
handleDown(e.Touch.Source, e.ScreenSpaceTouchDownPosition);

View File

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Online.Spectator;
using osu.Game.Rulesets.Judgements;
@ -139,6 +140,29 @@ namespace osu.Game.Tests.Gameplay
Assert.That(score.MaximumStatistics[HitResult.LargeBonus], Is.EqualTo(1));
}
[Test]
public void TestAccuracyModes()
{
var beatmap = new Beatmap<HitObject>
{
HitObjects = Enumerable.Range(0, 4).Select(_ => new TestHitObject(HitResult.Great)).ToList<HitObject>()
};
var scoreProcessor = new ScoreProcessor(new OsuRuleset());
scoreProcessor.ApplyBeatmap(beatmap);
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(1));
Assert.That(scoreProcessor.MinimumAccuracy.Value, Is.EqualTo(0));
Assert.That(scoreProcessor.MaximumAccuracy.Value, Is.EqualTo(1));
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], beatmap.HitObjects[0].CreateJudgement()) { Type = HitResult.Ok });
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], beatmap.HitObjects[1].CreateJudgement()) { Type = HitResult.Great });
Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo((double)(100 + 300) / (2 * 300)).Within(Precision.DOUBLE_EPSILON));
Assert.That(scoreProcessor.MinimumAccuracy.Value, Is.EqualTo((double)(100 + 300) / (4 * 300)).Within(Precision.DOUBLE_EPSILON));
Assert.That(scoreProcessor.MaximumAccuracy.Value, Is.EqualTo((double)(100 + 3 * 300) / (4 * 300)).Within(Precision.DOUBLE_EPSILON));
}
private class TestJudgement : Judgement
{
public override HitResult MaxResult { get; }

View File

@ -6,6 +6,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
@ -21,7 +22,13 @@ namespace osu.Game.Tests.Visual.Editing
public TestSceneEditorSummaryTimeline()
{
editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
var beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo);
beatmap.ControlPointInfo.Add(100000, new TimingControlPoint { BeatLength = 100 });
beatmap.ControlPointInfo.Add(50000, new DifficultyControlPoint { SliderVelocity = 2 });
beatmap.BeatmapInfo.Bookmarks = new[] { 75000, 125000 };
editorBeatmap = new EditorBeatmap(beatmap);
}
protected override void LoadComplete()

View File

@ -0,0 +1,35 @@
// 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 NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
namespace osu.Game.Tests.Visual.Editing
{
public partial class TestScenePreviewTime : EditorTestScene
{
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
[Test]
public void TestSceneSetPreviewTimingPoint()
{
AddStep("seek to 1000", () => EditorClock.Seek(1000));
AddAssert("time is 1000", () => EditorClock.CurrentTime == 1000);
AddStep("set current time as preview point", () => Editor.SetPreviewPointToCurrentTime());
AddAssert("preview time is 1000", () => EditorBeatmap.PreviewTime.Value == 1000);
}
[Test]
public void TestScenePreviewTimeline()
{
AddStep("set preview time to -1", () => EditorBeatmap.PreviewTime.Value = -1);
AddAssert("preview time line should not show", () => !Editor.ChildrenOfType<PreviewTimePart>().Single().Children.Any());
AddStep("set preview time to 1000", () => EditorBeatmap.PreviewTime.Value = 1000);
AddAssert("preview time line should show", () => Editor.ChildrenOfType<PreviewTimePart>().Single().Children.Single().Alpha == 1);
}
}
}

View File

@ -201,6 +201,23 @@ namespace osu.Game.Tests.Visual.Menus
AddAssert("volume not changed", () => Audio.Volume.Value == 0.5);
}
[Test]
public void TestRulesetSelectorOverflow()
{
AddStep("set toolbar width", () =>
{
toolbar.RelativeSizeAxes = Axes.None;
toolbar.Width = 400;
});
AddStep("move mouse over news toggle button", () =>
{
var button = toolbar.ChildrenOfType<ToolbarNewsButton>().Single();
InputManager.MoveMouseTo(button);
});
AddAssert("no ruleset toggle buttons hovered", () => !toolbar.ChildrenOfType<ToolbarRulesetTabButton>().Any(button => button.IsHovered));
AddUntilStep("toolbar gradient visible", () => toolbar.ChildrenOfType<Toolbar.ToolbarBackground>().Single().Children.All(d => d.Alpha > 0));
}
public partial class TestToolbar : Toolbar
{
public new Bindable<OverlayActivation> OverlayActivationMode => base.OverlayActivationMode as Bindable<OverlayActivation>;

View File

@ -3,6 +3,7 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
@ -46,10 +47,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("item removed", () => !playlist.Items.Contains(selectedItem));
}
[Test]
public void TestNextItemSelectedAfterDeletion()
[TestCase(true)]
[TestCase(false)]
public void TestNextItemSelectedAfterDeletion(bool allowSelection)
{
createPlaylist();
createPlaylist(p =>
{
p.AllowSelection = allowSelection;
});
moveToItem(0);
AddStep("click", () => InputManager.Click(MouseButton.Left));
@ -57,7 +62,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
moveToDeleteButton(0);
AddStep("click delete button", () => InputManager.Click(MouseButton.Left));
AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]);
AddAssert("item 0 is " + (allowSelection ? "selected" : "not selected"), () => playlist.SelectedItem.Value == (allowSelection ? playlist.Items[0] : null));
}
[Test]
@ -117,7 +122,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
InputManager.MoveMouseTo(item.ChildrenOfType<DrawableRoomPlaylistItem.PlaylistRemoveButton>().ElementAt(0), offset);
});
private void createPlaylist()
private void createPlaylist(Action<TestPlaylist> setupPlaylist = null)
{
AddStep("create playlist", () =>
{
@ -154,6 +159,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
});
}
setupPlaylist?.Invoke(playlist);
});
AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded));

View File

@ -54,6 +54,8 @@ namespace osu.Game.Tests.Visual.Online
{
overlay.ShowBeatmapSet(new APIBeatmapSet
{
Genre = new BeatmapSetOnlineGenre { Id = 15, Name = "Future genre" },
Language = new BeatmapSetOnlineLanguage { Id = 15, Name = "Future language" },
OnlineID = 1235,
Title = @"an awesome beatmap",
Artist = @"naru narusegawa",

View File

@ -530,6 +530,52 @@ namespace osu.Game.Tests.Visual.Online
});
}
[Test]
public void TestTextBoxSavePerChannel()
{
var testPMChannel = new Channel(testUser);
AddStep("show overlay", () => chatOverlay.Show());
joinTestChannel(0);
joinChannel(testPMChannel);
AddAssert("listing is visible", () => listingIsVisible);
AddStep("search for 'number 2'", () => chatOverlayTextBox.Text = "number 2");
AddAssert("'number 2' saved to selector", () => channelManager.CurrentChannel.Value.TextBoxMessage.Value == "number 2");
AddStep("select normal channel", () => clickDrawable(getChannelListItem(testChannel1)));
AddAssert("text box cleared on normal channel", () => chatOverlayTextBox.Text == string.Empty);
AddAssert("nothing saved on normal channel", () => channelManager.CurrentChannel.Value.TextBoxMessage.Value == string.Empty);
AddStep("type '727'", () => chatOverlayTextBox.Text = "727");
AddAssert("'727' saved to normal channel", () => channelManager.CurrentChannel.Value.TextBoxMessage.Value == "727");
AddStep("select PM channel", () => clickDrawable(getChannelListItem(testPMChannel)));
AddAssert("text box cleared on PM channel", () => chatOverlayTextBox.Text == string.Empty);
AddAssert("nothing saved on PM channel", () => channelManager.CurrentChannel.Value.TextBoxMessage.Value == string.Empty);
AddStep("type 'hello'", () => chatOverlayTextBox.Text = "hello");
AddAssert("'hello' saved to PM channel", () => channelManager.CurrentChannel.Value.TextBoxMessage.Value == "hello");
AddStep("select normal channel", () => clickDrawable(getChannelListItem(testChannel1)));
AddAssert("text box contains '727'", () => chatOverlayTextBox.Text == "727");
AddStep("select PM channel", () => clickDrawable(getChannelListItem(testPMChannel)));
AddAssert("text box contains 'hello'", () => chatOverlayTextBox.Text == "hello");
AddStep("click close button", () =>
{
ChannelListItemCloseButton closeButton = getChannelListItem(testPMChannel).ChildrenOfType<ChannelListItemCloseButton>().Single();
clickDrawable(closeButton);
});
AddAssert("listing is visible", () => listingIsVisible);
AddAssert("text box contains 'channel 2'", () => chatOverlayTextBox.Text == "number 2");
AddUntilStep("only channel 2 visible", () =>
{
IEnumerable<ChannelListingItem> listingItems = chatOverlay.ChildrenOfType<ChannelListingItem>()
.Where(item => item.IsPresent);
return listingItems.Count() == 1 && listingItems.Single().Channel == testChannel2;
});
}
private void joinTestChannel(int i)
{
AddStep($"Join test channel {i}", () => channelManager.JoinChannel(testChannels[i]));

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Game.Overlays.Profile.Sections.Kudosu;
using System.Collections.Generic;
using System;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Graphics;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Rulesets.Catch;
@ -24,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online
public TestSceneProfileRulesetSelector()
{
ProfileRulesetSelector selector;
var user = new Bindable<APIUser>();
var user = new Bindable<APIUser?>();
Child = selector = new ProfileRulesetSelector
{

View File

@ -35,6 +35,8 @@ namespace osu.Game.Tests.Visual.Online
private Action<GetUsersRequest>? handleGetUsersRequest;
private Action<GetUserRequest>? handleGetUserRequest;
private IDisposable? subscription;
private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
[SetUpSteps]
@ -246,6 +248,26 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("values after are correct", () => update!.After.TotalScore, () => Is.EqualTo(6_000_000));
}
[Test]
public void TestStatisticsUpdateNotFiredAfterSubscriptionDisposal()
{
int userId = getUserId();
setUpUser(userId);
long scoreId = getScoreId();
var ruleset = new OsuRuleset().RulesetInfo;
SoloStatisticsUpdate? update = null;
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
AddStep("unsubscribe", () => subscription!.Dispose());
feignScoreProcessing(userId, ruleset, 5_000_000);
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
AddWaitStep("wait a bit", 5);
AddAssert("update not received", () => update == null);
}
private int nextUserId = 2000;
private long nextScoreId = 50000;
@ -266,7 +288,7 @@ namespace osu.Game.Tests.Visual.Online
}
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<SoloStatisticsUpdate> onUpdateReady) =>
AddStep("register for updates", () => watcher.RegisterForStatisticsUpdateAfter(
AddStep("register for updates", () => subscription = watcher.RegisterForStatisticsUpdateAfter(
new ScoreInfo(Beatmap.Value.BeatmapInfo, new OsuRuleset().RulesetInfo, new RealmUser())
{
Ruleset = rulesetInfo,

View File

@ -50,6 +50,8 @@ namespace osu.Game.Tests.Visual.Online
private ChannelManager channelManager;
private TestStandAloneChatDisplay chatDisplay;
private TestStandAloneChatDisplay chatWithTextBox;
private TestStandAloneChatDisplay chatWithTextBox2;
private int messageIdSequence;
private Channel testChannel;
@ -78,7 +80,7 @@ namespace osu.Game.Tests.Visual.Online
private void reinitialiseDrawableDisplay()
{
Children = new[]
Children = new Drawable[]
{
chatDisplay = new TestStandAloneChatDisplay
{
@ -88,13 +90,28 @@ namespace osu.Game.Tests.Visual.Online
Size = new Vector2(400, 80),
Channel = { Value = testChannel },
},
new TestStandAloneChatDisplay(true)
new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding(20),
Size = new Vector2(400, 150),
Channel = { Value = testChannel },
Children = new[]
{
chatWithTextBox = new TestStandAloneChatDisplay(true)
{
Margin = new MarginPadding(20),
Size = new Vector2(400, 150),
Channel = { Value = testChannel },
},
chatWithTextBox2 = new TestStandAloneChatDisplay(true)
{
Margin = new MarginPadding(20),
Size = new Vector2(400, 150),
Channel = { Value = testChannel },
},
}
}
};
}
@ -351,6 +368,13 @@ namespace osu.Game.Tests.Visual.Online
checkScrolledToBottom();
}
[Test]
public void TestTextBoxSync()
{
AddStep("type 'hello' to text box 1", () => chatWithTextBox.ChildrenOfType<StandAloneChatDisplay.ChatTextBox>().Single().Text = "hello");
AddAssert("text box 2 contains 'hello'", () => chatWithTextBox2.ChildrenOfType<StandAloneChatDisplay.ChatTextBox>().Single().Text == "hello");
}
private void fillChat(int count = 10)
{
AddStep("fill chat", () =>

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Linq;
using NUnit.Framework;
@ -20,7 +18,7 @@ namespace osu.Game.Tests.Visual.Online
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
private ProfileHeader header;
private ProfileHeader header = null!;
[SetUpSteps]
public void SetUpSteps()

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Linq;
using NUnit.Framework;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using NUnit.Framework;
using osu.Framework.Graphics;
@ -14,7 +12,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public partial class TestSceneUserProfilePreviousUsernames : OsuTestScene
{
private PreviousUsernames container;
private PreviousUsernames container = null!;
[SetUp]
public void SetUp() => Schedule(() =>
@ -50,7 +48,7 @@ namespace osu.Game.Tests.Visual.Online
AddUntilStep("Is hidden", () => container.Alpha == 0);
}
private static readonly APIUser[] users =
private static readonly APIUser?[] users =
{
new APIUser { Id = 1, PreviousUsernames = new[] { "username1" } },
new APIUser { Id = 2, PreviousUsernames = new[] { "longusername", "longerusername" } },

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using NUnit.Framework;
using osu.Game.Overlays.Profile.Sections;
using osu.Framework.Testing;
@ -18,7 +16,7 @@ namespace osu.Game.Tests.Visual.UserInterface
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
private ProfileSubsectionHeader header;
private ProfileSubsectionHeader header = null!;
[Test]
public void TestHiddenCounter()

View File

@ -55,6 +55,16 @@ namespace osu.Game.Tests.Visual.UserInterface
};
});
[Test]
public void TestDisplay()
{
AddRepeatStep("toggle expanded state", () =>
{
InputManager.MoveMouseTo(group.ChildrenOfType<IconButton>().Single());
InputManager.Click(MouseButton.Left);
}, 5);
}
[Test]
public void TestClickExpandButtonMultipleTimes()
{

View File

@ -0,0 +1,22 @@
// 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 Newtonsoft.Json;
namespace osu.Game.Beatmaps
{
public struct BeatmapSetOnlineNomination
{
[JsonProperty(@"beatmapset_id")]
public int BeatmapsetId { get; set; }
[JsonProperty(@"reset")]
public bool Reset { get; set; }
[JsonProperty(@"rulesets")]
public string[]? Rulesets { get; set; }
[JsonProperty(@"user_id")]
public int UserId { get; set; }
}
}

View File

@ -138,9 +138,18 @@ namespace osu.Game.Beatmaps.Drawables.Cards
// This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left.
this.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint);
background.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
dropdownContent.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
borderContainer.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
if (Expanded.Value)
{
background.FadeIn(BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
dropdownContent.FadeIn(BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
borderContainer.FadeIn(BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
}
else
{
background.FadeOut(BeatmapCard.TRANSITION_DURATION / 3f, Easing.OutQuint);
dropdownContent.FadeOut(BeatmapCard.TRANSITION_DURATION / 3f, Easing.OutQuint);
borderContainer.FadeOut(BeatmapCard.TRANSITION_DURATION / 3f, Easing.OutQuint);
}
content.TweenEdgeEffectTo(new EdgeEffectParameters
{

View File

@ -300,7 +300,7 @@ namespace osu.Game.Beatmaps.Formats
{
var comboColour = colours[i];
writer.Write(FormattableString.Invariant($"Combo{i}: "));
writer.Write(FormattableString.Invariant($"Combo{1 + i}: "));
writer.Write(FormattableString.Invariant($"{(byte)(comboColour.R * byte.MaxValue)},"));
writer.Write(FormattableString.Invariant($"{(byte)(comboColour.G * byte.MaxValue)},"));
writer.Write(FormattableString.Invariant($"{(byte)(comboColour.B * byte.MaxValue)},"));

View File

@ -34,8 +34,6 @@ namespace osu.Game.Beatmaps
// TODO: remove once the fallback lookup is not required (and access via `working.BeatmapInfo.Metadata` directly).
public BeatmapMetadata Metadata => BeatmapInfo.Metadata;
public Waveform Waveform => waveform.Value;
public Storyboard Storyboard => storyboard.Value;
public Texture Background => GetBackground(); // Texture uses ref counting, so we want to return a new instance every usage.
@ -48,10 +46,11 @@ namespace osu.Game.Beatmaps
private readonly object beatmapFetchLock = new object();
private readonly Lazy<Waveform> waveform;
private readonly Lazy<Storyboard> storyboard;
private readonly Lazy<ISkin> skin;
private Track track; // track is not Lazy as we allow transferring and loading multiple times.
private Waveform waveform; // waveform is also not Lazy as the track may change.
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
{
@ -60,7 +59,6 @@ namespace osu.Game.Beatmaps
BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet ?? new BeatmapSetInfo();
waveform = new Lazy<Waveform>(GetWaveform);
storyboard = new Lazy<Storyboard>(GetStoryboard);
skin = new Lazy<ISkin>(GetSkin);
}
@ -108,7 +106,16 @@ namespace osu.Game.Beatmaps
public virtual bool TrackLoaded => track != null;
public Track LoadTrack() => track = GetBeatmapTrack() ?? GetVirtualTrack(1000);
public Track LoadTrack()
{
track = GetBeatmapTrack() ?? GetVirtualTrack(1000);
// the track may have changed, recycle the current waveform.
waveform?.Dispose();
waveform = null;
return track;
}
public void PrepareTrackForPreview(bool looping, double offsetFromPreviewPoint = 0)
{
@ -171,6 +178,12 @@ namespace osu.Game.Beatmaps
#endregion
#region Waveform
public Waveform Waveform => waveform ??= GetWaveform();
#endregion
#region Beatmap
public virtual bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false;

View File

@ -4,7 +4,6 @@
using System;
using System.IO;
using System.Linq;
using osu.Framework;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
@ -63,10 +62,10 @@ namespace osu.Game.Database
private void copyToStore(RealmFile file, Stream data, bool preferHardLinks)
{
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows && data is FileStream fs && preferHardLinks)
if (data is FileStream fs && preferHardLinks)
{
// attempt to do a fast hard link rather than copy.
if (HardLinkHelper.CreateHardLink(Storage.GetFullPath(file.GetStoragePath(), true), fs.Name, IntPtr.Zero))
if (HardLinkHelper.TryCreateHardLink(Storage.GetFullPath(file.GetStoragePath(), true), fs.Name))
return;
}

View File

@ -45,6 +45,9 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.ControlPressed || e.AltPressed || e.SuperPressed || e.ShiftPressed)
return false;
switch (e.Key)
{
case Key.Up:

View File

@ -14,9 +14,8 @@ namespace osu.Game.IO
{
public static bool CheckAvailability(string testDestinationPath, string testSourcePath)
{
// We can support other operating systems quite easily in the future.
// Let's handle the most common one for now, though.
if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
// For simplicity, only support desktop operating systems for now.
if (!RuntimeInfo.IsDesktop)
return false;
const string test_filename = "_hard_link_test";
@ -31,7 +30,7 @@ namespace osu.Game.IO
File.WriteAllText(testSourcePath, string.Empty);
// Test availability by creating an arbitrary hard link between the source and destination paths.
return CreateHardLink(testDestinationPath, testSourcePath, IntPtr.Zero);
return TryCreateHardLink(testDestinationPath, testSourcePath);
}
catch
{
@ -55,21 +54,60 @@ namespace osu.Game.IO
}
}
/// <summary>
/// Attempts to create a hard link from <paramref name="sourcePath"/> to <paramref name="destinationPath"/>,
/// using platform-specific native methods.
/// </summary>
/// <remarks>
/// Hard links are only available on desktop platforms.
/// </remarks>
/// <returns>Whether the hard link was successfully created.</returns>
public static bool TryCreateHardLink(string destinationPath, string sourcePath)
{
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
return CreateHardLink(destinationPath, sourcePath, IntPtr.Zero);
case RuntimeInfo.Platform.Linux:
case RuntimeInfo.Platform.macOS:
return link(sourcePath, destinationPath) == 0;
default:
return false;
}
}
// For future use (to detect if a file is a hard link with other references existing on disk).
public static int GetFileLinkCount(string filePath)
{
int result = 0;
SafeFileHandle handle = CreateFile(filePath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
ByHandleFileInformation fileInfo;
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
SafeFileHandle handle = CreateFile(filePath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
if (GetFileInformationByHandle(handle, out fileInfo))
result = (int)fileInfo.NumberOfLinks;
CloseHandle(handle);
ByHandleFileInformation fileInfo;
if (GetFileInformationByHandle(handle, out fileInfo))
result = (int)fileInfo.NumberOfLinks;
CloseHandle(handle);
break;
case RuntimeInfo.Platform.Linux:
case RuntimeInfo.Platform.macOS:
if (stat(filePath, out var statbuf) == 0)
result = (int)statbuf.st_nlink;
break;
}
return result;
}
#region Windows native methods
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
@ -104,5 +142,49 @@ namespace osu.Game.IO
public readonly uint FileIndexHigh;
public readonly uint FileIndexLow;
}
#endregion
#region Linux native methods
#pragma warning disable IDE1006 // Naming rule violation
[DllImport("libc", SetLastError = true)]
public static extern int link(string oldpath, string newpath);
[DllImport("libc", SetLastError = true)]
private static extern int stat(string pathname, out struct_stat statbuf);
// ReSharper disable once InconsistentNaming
// Struct layout is likely non-portable across unices. Tread with caution.
[StructLayout(LayoutKind.Sequential)]
private struct struct_stat
{
public readonly long st_dev;
public readonly long st_ino;
public readonly long st_nlink;
public readonly int st_mode;
public readonly int st_uid;
public readonly int st_gid;
public readonly long st_rdev;
public readonly long st_size;
public readonly long st_blksize;
public readonly long st_blocks;
public readonly timespec st_atim;
public readonly timespec st_mtim;
public readonly timespec st_ctim;
}
// ReSharper disable once InconsistentNaming
[StructLayout(LayoutKind.Sequential)]
private struct timespec
{
public readonly long tv_sec;
public readonly long tv_nsec;
}
#pragma warning restore IDE1006
#endregion
}
}

View File

@ -259,7 +259,11 @@ namespace osu.Game.Online.API
var friendsReq = new GetFriendsRequest();
friendsReq.Failure += _ => state.Value = APIState.Failing;
friendsReq.Success += res => friends.AddRange(res);
friendsReq.Success += res =>
{
friends.Clear();
friends.AddRange(res);
};
if (!handleRequest(friendsReq))
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
@ -11,10 +9,11 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using System.Text;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Online.API.Requests
{
public class GetScoresRequest : APIRequest<APIScoresCollection>
public class GetScoresRequest : APIRequest<APIScoresCollection>, IEquatable<GetScoresRequest>
{
public const int MAX_SCORES_PER_REQUEST = 50;
@ -23,7 +22,7 @@ namespace osu.Game.Online.API.Requests
private readonly IRulesetInfo ruleset;
private readonly IEnumerable<IMod> mods;
public GetScoresRequest(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable<IMod> mods = null)
public GetScoresRequest(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable<IMod>? mods = null)
{
if (beatmapInfo.OnlineID <= 0)
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(IBeatmapInfo.OnlineID)}.");
@ -51,5 +50,16 @@ namespace osu.Game.Online.API.Requests
return query.ToString();
}
public bool Equals(GetScoresRequest? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return beatmapInfo.Equals(other.beatmapInfo)
&& scope == other.scope
&& ruleset.Equals(other.ruleset)
&& mods.SequenceEqual(other.mods);
}
}
}

View File

@ -111,6 +111,12 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"language")]
public BeatmapSetOnlineLanguage Language { get; set; }
[JsonProperty(@"current_nominations")]
public BeatmapSetOnlineNomination[]? CurrentNominations { get; set; }
[JsonProperty(@"related_users")]
public APIUser[]? RelatedUsers { get; set; }
public string Source { get; set; } = string.Empty;
[JsonProperty(@"tags")]

View File

@ -98,6 +98,11 @@ namespace osu.Game.Online.Chat
/// </summary>
public Bindable<Message> HighlightedMessage = new Bindable<Message>();
/// <summary>
/// The current text box message while in this <see cref="Channel"/>.
/// </summary>
public Bindable<string> TextBoxMessage = new Bindable<string>(string.Empty);
[JsonConstructor]
public Channel()
{

View File

@ -341,6 +341,8 @@ namespace osu.Game.Online.Chat
OpenWiki,
Custom,
OpenChangelog,
FilterBeatmapSetGenre,
FilterBeatmapSetLanguage,
}
public class Link : IComparable<Link>

View File

@ -111,8 +111,13 @@ namespace osu.Game.Online.Chat
{
drawableChannel?.Expire();
if (e.OldValue != null)
TextBox?.Current.UnbindFrom(e.OldValue.TextBoxMessage);
if (e.NewValue == null) return;
TextBox?.Current.BindTo(e.NewValue.TextBoxMessage);
drawableChannel = CreateDrawableChannel(e.NewValue);
drawableChannel.CreateChatLineAction = CreateMessage;
drawableChannel.Padding = new MarginPadding { Bottom = postingTextBox ? text_box_height : 0 };

View File

@ -46,24 +46,30 @@ namespace osu.Game.Online.Solo
/// </summary>
/// <param name="score">The score to listen for the statistics update for.</param>
/// <param name="onUpdateReady">The callback to be invoked once the statistics update has been prepared.</param>
public void RegisterForStatisticsUpdateAfter(ScoreInfo score, Action<SoloStatisticsUpdate> onUpdateReady) => Schedule(() =>
/// <returns>An <see cref="IDisposable"/> representing the subscription. Disposing it is equivalent to unsubscribing from future notifications.</returns>
public IDisposable RegisterForStatisticsUpdateAfter(ScoreInfo score, Action<SoloStatisticsUpdate> onUpdateReady)
{
if (!api.IsLoggedIn)
return;
if (!score.Ruleset.IsLegacyRuleset())
return;
var callback = new StatisticsUpdateCallback(score, onUpdateReady);
if (lastProcessedScoreId == score.OnlineID)
Schedule(() =>
{
requestStatisticsUpdate(api.LocalUser.Value.Id, callback);
return;
}
if (!api.IsLoggedIn)
return;
callbacks.Add(score.OnlineID, callback);
});
if (!score.Ruleset.IsLegacyRuleset() || score.OnlineID <= 0)
return;
var callback = new StatisticsUpdateCallback(score, onUpdateReady);
if (lastProcessedScoreId == score.OnlineID)
{
requestStatisticsUpdate(api.LocalUser.Value.Id, callback);
return;
}
callbacks.Add(score.OnlineID, callback);
});
return new InvokeOnDisposal(() => Schedule(() => callbacks.Remove(score.OnlineID)));
}
private void onUserChanged(APIUser? localUser) => Schedule(() =>
{
@ -75,15 +81,27 @@ namespace osu.Game.Online.Solo
return;
var userRequest = new GetUsersRequest(new[] { localUser.OnlineID });
userRequest.Success += response => Schedule(() =>
{
latestStatistics = new Dictionary<string, UserStatistics>();
foreach (var rulesetStats in response.Users.Single().RulesetsStatistics)
latestStatistics.Add(rulesetStats.Key, rulesetStats.Value);
});
userRequest.Success += initialiseUserStatistics;
api.Queue(userRequest);
});
private void initialiseUserStatistics(GetUsersResponse response) => Schedule(() =>
{
var user = response.Users.SingleOrDefault();
// possible if the user is restricted or similar.
if (user == null)
return;
latestStatistics = new Dictionary<string, UserStatistics>();
if (user.RulesetsStatistics != null)
{
foreach (var rulesetStats in user.RulesetsStatistics)
latestStatistics.Add(rulesetStats.Key, rulesetStats.Value);
}
});
private void userScoreProcessed(int userId, long scoreId)
{
if (userId != api.LocalUser.Value?.OnlineID)

View File

@ -46,6 +46,7 @@ using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing;
using osu.Game.Overlays.Music;
using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.Toolbar;
@ -359,6 +360,14 @@ namespace osu.Game
SearchBeatmapSet(argString);
break;
case LinkAction.FilterBeatmapSetGenre:
FilterBeatmapSetGenre((SearchGenre)link.Argument);
break;
case LinkAction.FilterBeatmapSetLanguage:
FilterBeatmapSetLanguage((SearchLanguage)link.Argument);
break;
case LinkAction.OpenEditorTimestamp:
case LinkAction.JoinMultiplayerMatch:
case LinkAction.Spectate:
@ -463,6 +472,10 @@ namespace osu.Game
/// <param name="query">The query to search for.</param>
public void SearchBeatmapSet(string query) => waitForReady(() => beatmapListing, _ => beatmapListing.ShowWithSearch(query));
public void FilterBeatmapSetGenre(SearchGenre genre) => waitForReady(() => beatmapListing, _ => beatmapListing.ShowWithGenreFilter(genre));
public void FilterBeatmapSetLanguage(SearchLanguage language) => waitForReady(() => beatmapListing, _ => beatmapListing.ShowWithLanguageFilter(language));
/// <summary>
/// Show a wiki's page as an overlay
/// </summary>

View File

@ -145,6 +145,12 @@ namespace osu.Game.Overlays.BeatmapListing
public void Search(string query)
=> Schedule(() => searchControl.Query.Value = query);
public void FilterGenre(SearchGenre genre)
=> Schedule(() => searchControl.Genre.Value = genre);
public void FilterLanguage(SearchLanguage language)
=> Schedule(() => searchControl.Language.Value = language);
protected override void LoadComplete()
{
base.LoadComplete();

View File

@ -12,6 +12,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osuTK;
@ -100,9 +101,30 @@ namespace osu.Game.Overlays.BeatmapListing
protected partial class MultipleSelectionFilterTabItem : FilterTabItem<T>
{
private readonly Box selectedUnderline;
protected override bool HighlightOnHoverWhenActive => true;
public MultipleSelectionFilterTabItem(T value)
: base(value)
{
// This doesn't match any actual design, but should make it easier for the user to understand
// that filters are applied until we settle on a final design.
AddInternal(selectedUnderline = new Box
{
Depth = float.MaxValue,
RelativeSizeAxes = Axes.X,
Height = 1.5f,
Anchor = Anchor.BottomLeft,
Origin = Anchor.CentreLeft,
});
}
protected override void UpdateState()
{
base.UpdateState();
selectedUnderline.FadeTo(Active.Value ? 1 : 0, 200, Easing.OutQuint);
selectedUnderline.FadeColour(IsHovered ? ColourProvider.Content2 : GetStateColour(), 200, Easing.OutQuint);
}
protected override bool OnClick(ClickEvent e)

View File

@ -20,7 +20,7 @@ namespace osu.Game.Overlays.BeatmapListing
public partial class FilterTabItem<T> : TabItem<T>
{
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
protected OverlayColourProvider ColourProvider { get; private set; }
private OsuSpriteText text;
@ -52,38 +52,42 @@ namespace osu.Game.Overlays.BeatmapListing
{
base.LoadComplete();
updateState();
UpdateState();
FinishTransforms(true);
}
protected override bool OnHover(HoverEvent e)
{
base.OnHover(e);
updateState();
UpdateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateState();
UpdateState();
}
protected override void OnActivated() => updateState();
protected override void OnActivated() => UpdateState();
protected override void OnDeactivated() => updateState();
protected override void OnDeactivated() => UpdateState();
/// <summary>
/// Returns the label text to be used for the supplied <paramref name="value"/>.
/// </summary>
protected virtual LocalisableString LabelFor(T value) => (value as Enum)?.GetLocalisableDescription() ?? value.ToString();
private void updateState()
protected virtual bool HighlightOnHoverWhenActive => false;
protected virtual void UpdateState()
{
text.FadeColour(IsHovered ? colourProvider.Light1 : GetStateColour(), 200, Easing.OutQuint);
text.Font = text.Font.With(weight: Active.Value ? FontWeight.SemiBold : FontWeight.Regular);
bool highlightHover = IsHovered && (!Active.Value || HighlightOnHoverWhenActive);
text.FadeColour(highlightHover ? ColourProvider.Content2 : GetStateColour(), 200, Easing.OutQuint);
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Regular);
}
protected virtual Color4 GetStateColour() => Active.Value ? colourProvider.Content1 : colourProvider.Light2;
protected virtual Color4 GetStateColour() => Active.Value ? ColourProvider.Content1 : ColourProvider.Light2;
}
}

View File

@ -110,6 +110,18 @@ namespace osu.Game.Overlays
ScrollFlow.ScrollToStart();
}
public void ShowWithGenreFilter(SearchGenre genre)
{
ShowWithSearch(string.Empty);
filterControl.FilterGenre(genre);
}
public void ShowWithLanguageFilter(SearchLanguage language)
{
ShowWithSearch(string.Empty);
filterControl.FilterLanguage(language);
}
protected override BeatmapListingHeader CreateHeader() => new BeatmapListingHeader();
protected override Color4 BackgroundColour => ColourProvider.Background6;

View File

@ -3,14 +3,17 @@
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapListing;
namespace osu.Game.Overlays.BeatmapSet
{
@ -34,7 +37,10 @@ namespace osu.Game.Overlays.BeatmapSet
public Info()
{
MetadataSection source, tags, genre, language;
MetadataSectionNominators nominators;
MetadataSection source, tags;
MetadataSectionGenre genre;
MetadataSectionLanguage language;
OsuSpriteText notRankedPlaceholder;
RelativeSizeAxes = Axes.X;
@ -59,7 +65,7 @@ namespace osu.Game.Overlays.BeatmapSet
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Child = new MetadataSection(MetadataType.Description),
Child = new MetadataSectionDescription(),
},
},
new Container
@ -76,12 +82,13 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Children = new[]
Children = new Drawable[]
{
source = new MetadataSection(MetadataType.Source),
genre = new MetadataSection(MetadataType.Genre) { Width = 0.5f },
language = new MetadataSection(MetadataType.Language) { Width = 0.5f },
tags = new MetadataSection(MetadataType.Tags),
nominators = new MetadataSectionNominators(),
source = new MetadataSectionSource(),
genre = new MetadataSectionGenre { Width = 0.5f },
language = new MetadataSectionLanguage { Width = 0.5f },
tags = new MetadataSectionTags(),
},
},
},
@ -118,10 +125,11 @@ namespace osu.Game.Overlays.BeatmapSet
BeatmapSet.ValueChanged += b =>
{
source.Text = b.NewValue?.Source ?? string.Empty;
tags.Text = b.NewValue?.Tags ?? string.Empty;
genre.Text = b.NewValue?.Genre.Name ?? string.Empty;
language.Text = b.NewValue?.Language.Name ?? string.Empty;
nominators.Metadata = (b.NewValue?.CurrentNominations ?? Array.Empty<BeatmapSetOnlineNomination>(), b.NewValue?.RelatedUsers ?? Array.Empty<APIUser>());
source.Metadata = b.NewValue?.Source ?? string.Empty;
tags.Metadata = b.NewValue?.Tags ?? string.Empty;
genre.Metadata = b.NewValue?.Genre ?? new BeatmapSetOnlineGenre { Id = (int)SearchGenre.Unspecified };
language.Metadata = b.NewValue?.Language ?? new BeatmapSetOnlineLanguage { Id = (int)SearchLanguage.Unspecified };
bool setHasLeaderboard = b.NewValue?.Status > 0;
successRate.Alpha = setHasLeaderboard ? 1 : 0;
notRankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
@ -11,26 +9,45 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSection : Container
public abstract partial class MetadataSection : MetadataSection<string>
{
public override string Metadata
{
set
{
if (string.IsNullOrEmpty(value))
{
this.FadeOut(TRANSITION_DURATION);
return;
}
base.Metadata = value;
}
}
protected MetadataSection(MetadataType type, Action<string>? searchAction = null)
: base(type, searchAction)
{
}
}
public abstract partial class MetadataSection<T> : Container
{
private readonly FillFlowContainer textContainer;
private readonly MetadataType type;
private TextFlowContainer textFlow;
private TextFlowContainer? textFlow;
private readonly Action<string> searchAction;
protected readonly Action<T>? SearchAction;
private const float transition_duration = 250;
protected const float TRANSITION_DURATION = 250;
public MetadataSection(MetadataType type, Action<string> searchAction = null)
protected MetadataSection(MetadataType type, Action<T>? searchAction = null)
{
this.type = type;
this.searchAction = searchAction;
SearchAction = searchAction;
Alpha = 0;
@ -53,7 +70,7 @@ namespace osu.Game.Overlays.BeatmapSet
AutoSizeAxes = Axes.Y,
Child = new OsuSpriteText
{
Text = this.type.GetLocalisableDescription(),
Text = type.GetLocalisableDescription(),
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 14),
},
},
@ -61,23 +78,23 @@ namespace osu.Game.Overlays.BeatmapSet
};
}
public string Text
public virtual T Metadata
{
set
{
if (string.IsNullOrEmpty(value))
if (value == null)
{
this.FadeOut(transition_duration);
this.FadeOut(TRANSITION_DURATION);
return;
}
this.FadeIn(transition_duration);
this.FadeIn(TRANSITION_DURATION);
setTextAsync(value);
setTextFlowAsync(value);
}
}
private void setTextAsync(string text)
private void setTextFlowAsync(T metadata)
{
LoadComponentAsync(new LinkFlowContainer(s => s.Font = s.Font.With(size: 14))
{
@ -88,44 +105,15 @@ namespace osu.Game.Overlays.BeatmapSet
{
textFlow?.Expire();
switch (type)
{
case MetadataType.Tags:
string[] tags = text.Split(" ");
for (int i = 0; i <= tags.Length - 1; i++)
{
string tag = tags[i];
if (searchAction != null)
loaded.AddLink(tag, () => searchAction(tag));
else
loaded.AddLink(tag, LinkAction.SearchBeatmapSet, tag);
if (i != tags.Length - 1)
loaded.AddText(" ");
}
break;
case MetadataType.Source:
if (searchAction != null)
loaded.AddLink(text, () => searchAction(text));
else
loaded.AddLink(text, LinkAction.SearchBeatmapSet, text);
break;
default:
loaded.AddText(text);
break;
}
AddMetadata(metadata, loaded);
textContainer.Add(textFlow = loaded);
// fade in if we haven't yet.
textContainer.FadeIn(transition_duration);
textContainer.FadeIn(TRANSITION_DURATION);
});
}
protected abstract void AddMetadata(T metadata, LinkFlowContainer loaded);
}
}

View File

@ -0,0 +1,21 @@
// 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 osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionDescription : MetadataSection
{
public MetadataSectionDescription(Action<string>? searchAction = null)
: base(MetadataType.Description, searchAction)
{
}
protected override void AddMetadata(string metadata, LinkFlowContainer loaded)
{
loaded.AddText(metadata);
}
}
}

View File

@ -0,0 +1,30 @@
// 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 osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
using osu.Game.Overlays.BeatmapListing;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionGenre : MetadataSection<BeatmapSetOnlineGenre>
{
public MetadataSectionGenre(Action<BeatmapSetOnlineGenre>? searchAction = null)
: base(MetadataType.Genre, searchAction)
{
}
protected override void AddMetadata(BeatmapSetOnlineGenre metadata, LinkFlowContainer loaded)
{
var genre = (SearchGenre)metadata.Id;
if (Enum.IsDefined(genre))
loaded.AddLink(genre.GetLocalisableDescription(), LinkAction.FilterBeatmapSetGenre, genre);
else
loaded.AddText(metadata.Name);
}
}
}

View File

@ -0,0 +1,30 @@
// 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 osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
using osu.Game.Overlays.BeatmapListing;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionLanguage : MetadataSection<BeatmapSetOnlineLanguage>
{
public MetadataSectionLanguage(Action<BeatmapSetOnlineLanguage>? searchAction = null)
: base(MetadataType.Language, searchAction)
{
}
protected override void AddMetadata(BeatmapSetOnlineLanguage metadata, LinkFlowContainer loaded)
{
var language = (SearchLanguage)metadata.Id;
if (Enum.IsDefined(language))
loaded.AddLink(language.GetLocalisableDescription(), LinkAction.FilterBeatmapSetLanguage, language);
else
loaded.AddText(metadata.Name);
}
}
}

View File

@ -0,0 +1,63 @@
// 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 osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionNominators : MetadataSection<(BeatmapSetOnlineNomination[] CurrentNominations, APIUser[] RelatedUsers)>
{
public override (BeatmapSetOnlineNomination[] CurrentNominations, APIUser[] RelatedUsers) Metadata
{
set
{
if (value.CurrentNominations.Length == 0)
{
this.FadeOut(TRANSITION_DURATION);
return;
}
base.Metadata = value;
}
}
public MetadataSectionNominators(Action<(BeatmapSetOnlineNomination[] CurrentNominations, APIUser[] RelatedUsers)>? searchAction = null)
: base(MetadataType.Nominators, searchAction)
{
}
protected override void AddMetadata((BeatmapSetOnlineNomination[] CurrentNominations, APIUser[] RelatedUsers) metadata, LinkFlowContainer loaded)
{
int[] nominatorIds = metadata.CurrentNominations.Select(n => n.UserId).ToArray();
int nominatorsFound = 0;
foreach (int nominatorId in nominatorIds)
{
foreach (var user in metadata.RelatedUsers)
{
if (nominatorId != user.OnlineID) continue;
nominatorsFound++;
loaded.AddUserLink(new APIUser
{
Username = user.Username,
Id = nominatorId,
});
if (nominatorsFound < nominatorIds.Length)
loaded.AddText(CommonStrings.ArrayAndWordsConnector);
break;
}
}
}
}
}

View File

@ -0,0 +1,25 @@
// 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 osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionSource : MetadataSection
{
public MetadataSectionSource(Action<string>? searchAction = null)
: base(MetadataType.Source, searchAction)
{
}
protected override void AddMetadata(string metadata, LinkFlowContainer loaded)
{
if (SearchAction != null)
loaded.AddLink(metadata, () => SearchAction(metadata));
else
loaded.AddLink(metadata, LinkAction.SearchBeatmapSet, metadata);
}
}
}

View File

@ -0,0 +1,35 @@
// 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 osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionTags : MetadataSection
{
public MetadataSectionTags(Action<string>? searchAction = null)
: base(MetadataType.Tags, searchAction)
{
}
protected override void AddMetadata(string metadata, LinkFlowContainer loaded)
{
string[] tags = metadata.Split(" ");
for (int i = 0; i <= tags.Length - 1; i++)
{
string tag = tags[i];
if (SearchAction != null)
loaded.AddLink(tag, () => SearchAction(tag));
else
loaded.AddLink(tag, LinkAction.SearchBeatmapSet, tag);
if (i != tags.Length - 1)
loaded.AddText(" ");
}
}
}
}

View File

@ -23,6 +23,9 @@ namespace osu.Game.Overlays.BeatmapSet
Genre,
[LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoLanguage))]
Language
Language,
[LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoNominators))]
Nominators,
}
}

View File

@ -128,9 +128,8 @@ namespace osu.Game.Overlays.Chat
chattingTextContainer.FadeTo(showSearch ? 0 : 1);
searchIconContainer.FadeTo(showSearch ? 1 : 0);
// Clear search terms if any exist when switching back to chat mode
if (!showSearch)
OnSearchTermsChanged?.Invoke(string.Empty);
if (showSearch)
OnSearchTermsChanged?.Invoke(chatTextBox.Current.Value);
}, true);
currentChannel.BindValueChanged(change =>
@ -151,6 +150,12 @@ namespace osu.Game.Overlays.Chat
chattingText.Text = ChatStrings.TalkingIn(newChannel.Name);
break;
}
if (change.OldValue != null)
chatTextBox.Current.UnbindFrom(change.OldValue.TextBoxMessage);
if (newChannel != null)
chatTextBox.Current.BindTo(newChannel.TextBoxMessage);
}, true);
}

View File

@ -24,7 +24,6 @@ namespace osu.Game.Overlays.Chat
bool showSearch = change.NewValue;
PlaceholderText = showSearch ? HomeStrings.SearchPlaceholder : ChatStrings.InputPlaceholder;
Text = string.Empty;
}, true);
}

View File

@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Chat.Listing
private Box hoverBox = null!;
private SpriteIcon checkbox = null!;
private OsuSpriteText channelText = null!;
private OsuSpriteText topicText = null!;
private OsuTextFlowContainer topicText = null!;
private IBindable<bool> channelJoined = null!;
[Resolved]
@ -65,8 +65,8 @@ namespace osu.Game.Overlays.Chat.Listing
Masking = true;
CornerRadius = 5;
RelativeSizeAxes = Axes.X;
Height = 20 + (vertical_margin * 2);
RelativeSizeAxes = Content.RelativeSizeAxes = Axes.X;
AutoSizeAxes = Content.AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
@ -79,14 +79,19 @@ namespace osu.Game.Overlays.Chat.Listing
},
new GridContainer
{
RelativeSizeAxes = Axes.Both,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, 40),
new Dimension(GridSizeMode.Absolute, 200),
new Dimension(GridSizeMode.Absolute, 400),
new Dimension(maxSize: 400),
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
new Dimension(GridSizeMode.Absolute, 50), // enough for any 5 digit user count
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize, minSize: 20 + (vertical_margin * 2)),
},
Content = new[]
{
@ -108,12 +113,13 @@ namespace osu.Game.Overlays.Chat.Listing
Font = OsuFont.Torus.With(size: text_size, weight: FontWeight.SemiBold),
Margin = new MarginPadding { Bottom = 2 },
},
topicText = new OsuSpriteText
topicText = new OsuTextFlowContainer(t => t.Font = OsuFont.Torus.With(size: text_size))
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = Channel.Topic,
Font = OsuFont.Torus.With(size: text_size),
Margin = new MarginPadding { Bottom = 2 },
},
new SpriteIcon

View File

@ -132,12 +132,13 @@ namespace osu.Game.Overlays.FirstRunSetup
copyInformation.AddLink("Learn more about how \"hard links\" work", LinkAction.OpenWiki, @"Client/Release_stream/Lazer/File_storage#via-hard-links");
}
else if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
else if (!RuntimeInfo.IsDesktop)
copyInformation.Text = "Lightweight linking of files is not supported on your operating system yet, so a copy of all files will be made during import.";
else
{
copyInformation.Text =
"A second copy of all files will be made during import. To avoid this, please make sure the lazer data folder is on the same drive as your previous osu! install (and the file system is NTFS). ";
copyInformation.Text = RuntimeInfo.OS == RuntimeInfo.Platform.Windows
? "A second copy of all files will be made during import. To avoid this, please make sure the lazer data folder is on the same drive as your previous osu! install (and the file system is NTFS). "
: "A second copy of all files will be made during import. To avoid this, please make sure the lazer data folder is on the same drive as your previous osu! install (and the file system supports hard links). ";
copyInformation.AddLink(GeneralSettingsStrings.ChangeFolderLocation, () =>
{
game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen()));

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using Humanizer;
using osu.Framework.Allocation;
@ -25,15 +23,15 @@ namespace osu.Game.Overlays.Profile.Header
{
public partial class BottomHeaderContainer : CompositeDrawable
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
private LinkFlowContainer topLinkContainer;
private LinkFlowContainer bottomLinkContainer;
private LinkFlowContainer topLinkContainer = null!;
private LinkFlowContainer bottomLinkContainer = null!;
private Color4 iconColour;
[Resolved]
private IAPIProvider api { get; set; }
private IAPIProvider api { get; set; } = null!;
public BottomHeaderContainer()
{
@ -78,7 +76,7 @@ namespace osu.Game.Overlays.Profile.Header
User.BindValueChanged(user => updateDisplay(user.NewValue));
}
private void updateDisplay(APIUser user)
private void updateDisplay(APIUser? user)
{
topLinkContainer.Clear();
bottomLinkContainer.Clear();
@ -164,7 +162,7 @@ namespace osu.Game.Overlays.Profile.Header
private void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 });
private bool tryAddInfo(IconUsage icon, string content, string link = null)
private bool tryAddInfo(IconUsage icon, string content, string? link = null)
{
if (string.IsNullOrEmpty(content)) return false;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
@ -20,10 +18,10 @@ namespace osu.Game.Overlays.Profile.Header
public partial class CentreHeaderContainer : CompositeDrawable
{
public readonly BindableBool DetailsVisible = new BindableBool(true);
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
private OverlinedInfoContainer hiddenDetailGlobal;
private OverlinedInfoContainer hiddenDetailCountry;
private OverlinedInfoContainer hiddenDetailGlobal = null!;
private OverlinedInfoContainer hiddenDetailCountry = null!;
public CentreHeaderContainer()
{
@ -146,7 +144,7 @@ namespace osu.Game.Overlays.Profile.Header
User.BindValueChanged(user => updateDisplay(user.NewValue));
}
private void updateDisplay(APIUser user)
private void updateDisplay(APIUser? user)
{
hiddenDetailGlobal.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";
hiddenDetailCountry.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-";

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@ -23,9 +21,9 @@ namespace osu.Game.Overlays.Profile.Header.Components
public override LocalisableString TooltipText => DetailsVisible.Value ? CommonStrings.ButtonsCollapse : CommonStrings.ButtonsExpand;
private SpriteIcon icon;
private Sample sampleOpen;
private Sample sampleClose;
private SpriteIcon icon = null!;
private Sample? sampleOpen;
private Sample? sampleClose;
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds();

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
@ -14,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class FollowersButton : ProfileHeaderStatisticsButton
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
public override LocalisableString TooltipText => FriendsStrings.ButtonsDisabled;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -20,11 +18,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class LevelBadge : CompositeDrawable, IHasTooltip
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
public LocalisableString TooltipText { get; private set; }
private OsuSpriteText levelText;
private OsuSpriteText levelText = null!;
public LevelBadge()
{
@ -53,10 +51,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
User.BindValueChanged(user => updateLevel(user.NewValue));
}
private void updateLevel(APIUser user)
private void updateLevel(APIUser? user)
{
levelText.Text = user?.Statistics?.Level.Current.ToString() ?? "0";
TooltipText = UsersStrings.ShowStatsLevel(user?.Statistics?.Level.Current.ToString());
string level = user?.Statistics?.Level.Current.ToString() ?? "0";
levelText.Text = level;
TooltipText = UsersStrings.ShowStatsLevel(level);
}
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
@ -21,12 +19,12 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class LevelProgressBar : CompositeDrawable, IHasTooltip
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
public LocalisableString TooltipText { get; }
private Bar levelProgressBar;
private OsuSpriteText levelProgressText;
private Bar levelProgressBar = null!;
private OsuSpriteText levelProgressText = null!;
public LevelProgressBar()
{
@ -61,7 +59,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
User.BindValueChanged(user => updateProgress(user.NewValue));
}
private void updateProgress(APIUser user)
private void updateProgress(APIUser? user)
{
levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0;
levelProgressText.Text = user?.Statistics?.Level.Progress.ToLocalisableString("0'%'") ?? default;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
@ -14,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class MappingSubscribersButton : ProfileHeaderStatisticsButton
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
public override LocalisableString TooltipText => FollowsStrings.MappingFollowers;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -18,21 +16,21 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class MessageUserButton : ProfileHeaderButton
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
public override LocalisableString TooltipText => UsersStrings.CardSendMessage;
[Resolved(CanBeNull = true)]
private ChannelManager channelManager { get; set; }
[Resolved(CanBeNull = true)]
private UserProfileOverlay userOverlay { get; set; }
[Resolved(CanBeNull = true)]
private ChatOverlay chatOverlay { get; set; }
[Resolved]
private ChannelManager? channelManager { get; set; }
[Resolved]
private IAPIProvider apiProvider { get; set; }
private UserProfileOverlay? userOverlay { get; set; }
[Resolved]
private ChatOverlay? chatOverlay { get; set; }
[Resolved]
private IAPIProvider apiProvider { get; set; } = null!;
public MessageUserButton()
{
@ -56,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
chatOverlay?.Show();
};
User.ValueChanged += e => Content.Alpha = !e.NewValue.PMFriendsOnly && apiProvider.LocalUser.Value.Id != e.NewValue.Id ? 1 : 0;
User.ValueChanged += e => Content.Alpha = e.NewValue != null && !e.NewValue.PMFriendsOnly && apiProvider.LocalUser.Value.Id != e.NewValue.Id ? 1 : 0;
}
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -16,11 +14,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class OverlinedTotalPlayTime : CompositeDrawable, IHasTooltip
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
public LocalisableString TooltipText { get; set; }
private OverlinedInfoContainer info;
private OverlinedInfoContainer info = null!;
public OverlinedTotalPlayTime()
{
@ -41,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
User.BindValueChanged(updateTime, true);
}
private void updateTime(ValueChangedEvent<APIUser> user)
private void updateTime(ValueChangedEvent<APIUser?> user)
{
TooltipText = (user.NewValue?.Statistics?.PlayTime ?? 0) / 3600 + " hours";
info.Content = formatTime(user.NewValue?.Statistics?.PlayTime);

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Linq;
using osu.Framework.Allocation;
@ -27,7 +25,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
private const int width = 310;
private const int move_offset = 15;
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
private readonly TextFlowContainer text;
private readonly Box background;
@ -109,11 +107,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
User.BindValueChanged(onUserChanged, true);
}
private void onUserChanged(ValueChangedEvent<APIUser> user)
private void onUserChanged(ValueChangedEvent<APIUser?> user)
{
text.Text = string.Empty;
string[] usernames = user.NewValue?.PreviousUsernames;
string[]? usernames = user.NewValue?.PreviousUsernames;
if (usernames?.Any() ?? false)
{
@ -149,7 +147,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
private partial class HoverIconContainer : Container
{
public Action ActivateHover;
public Action? ActivateHover;
public HoverIconContainer()
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;

View File

@ -1,9 +1,8 @@
// 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.
#nullable disable
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
@ -12,12 +11,12 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
public partial class ProfileRulesetSelector : OverlayRulesetSelector
{
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
protected override void LoadComplete()
{
base.LoadComplete();
User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu")), true);
User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu").AsNonNull()), true);
}
public void SetDefaultRuleset(RulesetInfo ruleset)

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@ -21,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
private const int ranked_days = 88;
public readonly Bindable<UserStatistics> Statistics = new Bindable<UserStatistics>();
public readonly Bindable<UserStatistics?> Statistics = new Bindable<UserStatistics?>();
private readonly OsuSpriteText placeholder;
@ -42,11 +40,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
Statistics.BindValueChanged(statistics => updateStatistics(statistics.NewValue), true);
}
private void updateStatistics(UserStatistics statistics)
private void updateStatistics(UserStatistics? statistics)
{
// checking both IsRanked and RankHistory is required.
// see https://github.com/ppy/osu-web/blob/154ceafba0f35a1dd935df53ec98ae2ea5615f9f/resources/assets/lib/profile-page/rank-chart.tsx#L46
int[] userRanks = statistics?.IsRanked == true ? statistics.RankHistory?.Data : null;
int[]? userRanks = statistics?.IsRanked == true ? statistics.RankHistory?.Data : null;
Data = userRanks?.Select((x, index) => new KeyValuePair<int, int>(index, x)).Where(x => x.Value != 0).ToArray();
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -82,10 +80,10 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
[Resolved]
private OsuColour colours { get; set; }
private OsuColour colours { get; set; } = null!;
[BackgroundDependencyLoader(true)]
private void load(OsuGame game)
[BackgroundDependencyLoader]
private void load(OsuGame? game)
{
background.Colour = colours.Pink;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -25,14 +23,14 @@ namespace osu.Game.Overlays.Profile.Header
public partial class DetailHeaderContainer : CompositeDrawable
{
private readonly Dictionary<ScoreRank, ScoreRankInfo> scoreRankInfos = new Dictionary<ScoreRank, ScoreRankInfo>();
private OverlinedInfoContainer medalInfo;
private OverlinedInfoContainer ppInfo;
private OverlinedInfoContainer detailGlobalRank;
private OverlinedInfoContainer detailCountryRank;
private FillFlowContainer fillFlow;
private RankGraph rankGraph;
private OverlinedInfoContainer medalInfo = null!;
private OverlinedInfoContainer ppInfo = null!;
private OverlinedInfoContainer detailGlobalRank = null!;
private OverlinedInfoContainer detailCountryRank = null!;
private FillFlowContainer? fillFlow;
private RankGraph rankGraph = null!;
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
private bool expanded = true;
@ -173,7 +171,7 @@ namespace osu.Game.Overlays.Profile.Header
};
}
private void updateDisplay(APIUser user)
private void updateDisplay(APIUser? user)
{
medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0";
ppInfo.Content = user?.Statistics?.PP?.ToLocalisableString("#,##0") ?? (LocalisableString)"0";

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -20,9 +18,9 @@ namespace osu.Game.Overlays.Profile.Header
{
public partial class MedalHeaderContainer : CompositeDrawable
{
private FillFlowContainer badgeFlowContainer;
private FillFlowContainer badgeFlowContainer = null!;
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
@ -66,16 +64,16 @@ namespace osu.Game.Overlays.Profile.Header
};
}
private CancellationTokenSource cancellationTokenSource;
private CancellationTokenSource? cancellationTokenSource;
private void updateDisplay(APIUser user)
private void updateDisplay(APIUser? user)
{
cancellationTokenSource?.Cancel();
cancellationTokenSource = new CancellationTokenSource();
badgeFlowContainer.Clear();
var badges = user.Badges;
var badges = user?.Badges;
if (badges?.Length > 0)
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
@ -29,19 +27,19 @@ namespace osu.Game.Overlays.Profile.Header
{
private const float avatar_size = 110;
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
[Resolved]
private IAPIProvider api { get; set; }
private IAPIProvider api { get; set; } = null!;
private SupporterIcon supporterTag;
private UpdateableAvatar avatar;
private OsuSpriteText usernameText;
private ExternalLinkButton openUserExternally;
private OsuSpriteText titleText;
private UpdateableFlag userFlag;
private OsuSpriteText userCountryText;
private FillFlowContainer userStats;
private SupporterIcon supporterTag = null!;
private UpdateableAvatar avatar = null!;
private OsuSpriteText usernameText = null!;
private ExternalLinkButton openUserExternally = null!;
private OsuSpriteText titleText = null!;
private UpdateableFlag userFlag = null!;
private OsuSpriteText userCountryText = null!;
private FillFlowContainer userStats = null!;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
@ -176,7 +174,7 @@ namespace osu.Game.Overlays.Profile.Header
User.BindValueChanged(user => updateUser(user.NewValue));
}
private void updateUser(APIUser user)
private void updateUser(APIUser? user)
{
avatar.User = user;
usernameText.Text = user?.Username ?? string.Empty;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Diagnostics;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
@ -20,9 +18,9 @@ namespace osu.Game.Overlays.Profile
{
public partial class ProfileHeader : TabControlOverlayHeader<LocalisableString>
{
private UserCoverBackground coverContainer;
private UserCoverBackground coverContainer = null!;
public Bindable<APIUser> User = new Bindable<APIUser>();
public Bindable<APIUser?> User = new Bindable<APIUser?>();
private CentreHeaderContainer centreHeaderContainer;
private DetailHeaderContainer detailHeaderContainer;
@ -102,7 +100,7 @@ namespace osu.Game.Overlays.Profile
protected override OverlayTitle CreateTitle() => new ProfileHeaderTitle();
private void updateDisplay(APIUser user) => coverContainer.User = user;
private void updateDisplay(APIUser? user) => coverContainer.User = user;
private partial class ProfileHeaderTitle : OverlayTitle
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
@ -31,7 +29,7 @@ namespace osu.Game.Overlays.Profile
protected override Container<Drawable> Content => content;
public readonly Bindable<APIUser> User = new Bindable<APIUser>();
public readonly Bindable<APIUser?> User = new Bindable<APIUser?>();
protected ProfileSection()
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -25,8 +23,8 @@ namespace osu.Game.Overlays.Profile.Sections
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader(true)]
private void load(BeatmapSetOverlay beatmapSetOverlay)
[BackgroundDependencyLoader]
private void load(BeatmapSetOverlay? beatmapSetOverlay)
{
Action = () =>
{

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -24,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
protected override int InitialItemsCount => type == BeatmapSetType.Graveyard ? 2 : 6;
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<APIUser> user, LocalisableString headerText)
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<APIUser?> user, LocalisableString headerText)
: base(user, headerText)
{
this.type = type;
@ -66,10 +64,10 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
}
}
protected override APIRequest<List<APIBeatmapSet>> CreateRequest(PaginationParameters pagination) =>
new GetUserBeatmapsRequest(User.Value.Id, type, pagination);
protected override APIRequest<List<APIBeatmapSet>> CreateRequest(APIUser user, PaginationParameters pagination) =>
new GetUserBeatmapsRequest(user.Id, type, pagination);
protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0
protected override Drawable? CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0
? new BeatmapCardNormal(model)
{
Anchor = Anchor.TopCentre,

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Localisation;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Beatmaps;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
@ -18,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections
{
public readonly BindableInt Current = new BindableInt();
private OsuSpriteText counter;
private OsuSpriteText counter = null!;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
@ -15,14 +13,14 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{
public abstract partial class ChartProfileSubsection : ProfileSubsection
{
private ProfileLineChart chart;
private ProfileLineChart chart = null!;
/// <summary>
/// Text describing the value being plotted on the graph, which will be displayed as a prefix to the value in the history graph tooltip.
/// </summary>
protected abstract LocalisableString GraphCounterName { get; }
protected ChartProfileSubsection(Bindable<APIUser> user, LocalisableString headerText)
protected ChartProfileSubsection(Bindable<APIUser?> user, LocalisableString headerText)
: base(user, headerText)
{
}
@ -46,7 +44,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
User.BindValueChanged(onUserChanged, true);
}
private void onUserChanged(ValueChangedEvent<APIUser> e)
private void onUserChanged(ValueChangedEvent<APIUser?> e)
{
var values = GetValues(e.NewValue);
@ -86,6 +84,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
return filledHistoryEntries.ToArray();
}
protected abstract APIUserHistoryCount[] GetValues(APIUser user);
protected abstract APIUserHistoryCount[]? GetValues(APIUser? user);
}
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -18,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{
public partial class PaginatedMostPlayedBeatmapContainer : PaginatedProfileSubsection<APIUserMostPlayedBeatmap>
{
public PaginatedMostPlayedBeatmapContainer(Bindable<APIUser> user)
public PaginatedMostPlayedBeatmapContainer(Bindable<APIUser?> user)
: base(user, UsersStrings.ShowExtraHistoricalMostPlayedTitle)
{
}
@ -31,8 +29,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount;
protected override APIRequest<List<APIUserMostPlayedBeatmap>> CreateRequest(PaginationParameters pagination) =>
new GetUserMostPlayedBeatmapsRequest(User.Value.Id, pagination);
protected override APIRequest<List<APIUserMostPlayedBeatmap>> CreateRequest(APIUser user, PaginationParameters pagination) =>
new GetUserMostPlayedBeatmapsRequest(user.Id, pagination);
protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) =>
new DrawableMostPlayedBeatmap(mostPlayed);

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Game.Online.API.Requests.Responses;
@ -14,11 +12,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{
protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalMonthlyPlaycountsCountLabel;
public PlayHistorySubsection(Bindable<APIUser> user)
public PlayHistorySubsection(Bindable<APIUser?> user)
: base(user, UsersStrings.ShowExtraHistoricalMonthlyPlaycountsTitle)
{
}
protected override APIUserHistoryCount[] GetValues(APIUser user) => user?.MonthlyPlayCounts;
protected override APIUserHistoryCount[]? GetValues(APIUser? user) => user?.MonthlyPlayCounts;
}
}

View File

@ -1,11 +1,8 @@
// 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.
#nullable disable
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using JetBrains.Annotations;
using System;
using System.Linq;
using osu.Game.Graphics.Sprites;
@ -22,9 +19,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{
public partial class ProfileLineChart : CompositeDrawable
{
private APIUserHistoryCount[] values;
private APIUserHistoryCount[] values = Array.Empty<APIUserHistoryCount>();
[NotNull]
public APIUserHistoryCount[] Values
{
get => values;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Game.Online.API.Requests.Responses;
@ -14,11 +12,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{
protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalReplaysWatchedCountsCountLabel;
public ReplaysSubsection(Bindable<APIUser> user)
public ReplaysSubsection(Bindable<APIUser?> user)
: base(user, UsersStrings.ShowExtraHistoricalReplaysWatchedCountsTitle)
{
}
protected override APIUserHistoryCount[] GetValues(APIUser user) => user?.ReplaysWatchedCounts;
protected override APIUserHistoryCount[]? GetValues(APIUser? user) => user?.ReplaysWatchedCounts;
}
}

View File

@ -1,12 +1,9 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Localisation;
using osu.Game.Online.API.Requests.Responses;
@ -17,8 +14,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
{
private readonly LocalisableString tooltipCounterName;
[CanBeNull]
public APIUserHistoryCount[] Values
public APIUserHistoryCount[]? Values
{
set => Data = value?.Select(v => new KeyValuePair<DateTime, long>(v.Date, v.Count)).ToArray();
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Online.API.Requests;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -20,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
private const int height = 25;
[Resolved]
private OsuColour colours { get; set; }
private OsuColour colours { get; set; } = null!;
private readonly APIKudosuHistory historyItem;
private readonly LinkFlowContainer linkFlowContainer;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Bindables;
using osuTK;
using osu.Framework.Graphics;
@ -22,9 +20,9 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
{
public partial class KudosuInfo : Container
{
private readonly Bindable<APIUser> user = new Bindable<APIUser>();
private readonly Bindable<APIUser?> user = new Bindable<APIUser?>();
public KudosuInfo(Bindable<APIUser> user)
public KudosuInfo(Bindable<APIUser?> user)
{
this.user.BindTo(user);
CountSection total;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Framework.Bindables;
@ -16,13 +14,13 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
{
public partial class PaginatedKudosuHistoryContainer : PaginatedProfileSubsection<APIKudosuHistory>
{
public PaginatedKudosuHistoryContainer(Bindable<APIUser> user)
public PaginatedKudosuHistoryContainer(Bindable<APIUser?> user)
: base(user, missingText: UsersStrings.ShowExtraKudosuEntryEmpty)
{
}
protected override APIRequest<List<APIKudosuHistory>> CreateRequest(PaginationParameters pagination)
=> new GetUserKudosuHistoryRequest(User.Value.Id, pagination);
protected override APIRequest<List<APIKudosuHistory>> CreateRequest(APIUser user, PaginationParameters pagination)
=> new GetUserKudosuHistoryRequest(user.Id, pagination);
protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item);
}

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Graphics;
using osu.Game.Overlays.Profile.Sections.Kudosu;
using osu.Framework.Localisation;

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;

Some files were not shown because too many files have changed in this diff Show More