mirror of
https://github.com/ppy/osu.git
synced 2025-02-14 22:43:05 +08:00
Merge branch 'master' into fix-op-non-current-onexiting
This commit is contained in:
commit
54b2fe9df3
@ -1,6 +1,6 @@
|
||||
clone_depth: 1
|
||||
version: '{build}'
|
||||
image: Visual Studio 2019
|
||||
image: Visual Studio 2022
|
||||
test: off
|
||||
skip_non_tags: true
|
||||
configuration: Release
|
||||
@ -83,4 +83,4 @@ artifacts:
|
||||
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: nuget
|
||||
name: nuget
|
||||
|
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1219.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1226.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
||||
</manifest>
|
@ -8,6 +8,9 @@
|
||||
<UseMauiEssentials>true</UseMauiEssentials>
|
||||
<!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 -->
|
||||
<EnableLLVM>false</EnableLLVM>
|
||||
<Version>0.0.0</Version>
|
||||
<ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>
|
||||
<ApplicationDisplayVersion Condition=" '$(ApplicationDisplayVersion)' == '' ">$(Version)</ApplicationDisplayVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
foreach (var state in Enum.GetValues(typeof(CatcherAnimationState)).Cast<CatcherAnimationState>())
|
||||
foreach (var state in Enum.GetValues<CatcherAnimationState>())
|
||||
{
|
||||
AddInternal(drawables[state] = getDrawableFor(state).With(d =>
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -160,9 +160,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
static bool assertSamples(HitObject hitObject) => hitObject.Samples.All(s => s.Name != HitSampleInfo.HIT_CLAP && s.Name != HitSampleInfo.HIT_WHISTLE);
|
||||
}
|
||||
|
||||
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||
private Drawable testSimpleBig(int repeats = 0) => createSlider(repeats: repeats);
|
||||
|
||||
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
|
||||
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(repeats: repeats, stackHeight: 10);
|
||||
|
||||
private Drawable testDistanceOverflow(int repeats = 0)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
HitPolicy = new StartTimeOrderedHitPolicy();
|
||||
|
||||
var hitWindows = new OsuHitWindows();
|
||||
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
|
||||
foreach (var result in Enum.GetValues<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
|
||||
poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded));
|
||||
|
||||
AddRangeInternal(poolDictionary.Values);
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||
{
|
||||
drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
||||
drawableTaikoRuleset.LockPlayfieldAspect.Value = false;
|
||||
drawableTaikoRuleset.LockPlayfieldMaxAspect.Value = false;
|
||||
|
||||
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
||||
playfield.ClassicHitTargetPosition.Value = true;
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
public new BindableDouble TimeRange => base.TimeRange;
|
||||
|
||||
public readonly BindableBool LockPlayfieldAspect = new BindableBool(true);
|
||||
public readonly BindableBool LockPlayfieldMaxAspect = new BindableBool(true);
|
||||
|
||||
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
||||
|
||||
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer
|
||||
{
|
||||
LockPlayfieldAspect = { BindTarget = LockPlayfieldAspect }
|
||||
LockPlayfieldMaxAspect = { BindTarget = LockPlayfieldMaxAspect }
|
||||
};
|
||||
|
||||
protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
||||
|
@ -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);
|
||||
|
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
var hitWindows = new TaikoHitWindows();
|
||||
|
||||
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => hitWindows.IsHitResultAllowed(r)))
|
||||
foreach (var result in Enum.GetValues<HitResult>().Where(r => hitWindows.IsHitResultAllowed(r)))
|
||||
{
|
||||
judgementPools.Add(result, new DrawablePool<DrawableTaikoJudgement>(15));
|
||||
explosionPools.Add(result, new HitExplosionPool(result));
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
|
||||
private const float default_aspect = 16f / 9f;
|
||||
|
||||
public readonly IBindable<bool> LockPlayfieldAspect = new BindableBool(true);
|
||||
public readonly IBindable<bool> LockPlayfieldMaxAspect = new BindableBool(true);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
@ -21,7 +21,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
float height = default_relative_height;
|
||||
|
||||
if (LockPlayfieldAspect.Value)
|
||||
// Players coming from stable expect to be able to change the aspect ratio regardless of the window size.
|
||||
// We originally wanted to limit this more, but there was considerable pushback from the community.
|
||||
//
|
||||
// As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit.
|
||||
// This is still a bit weird, because readability changes with window size, but it is what it is.
|
||||
if (LockPlayfieldMaxAspect.Value && Parent.ChildSize.X / Parent.ChildSize.Y > default_aspect)
|
||||
height *= Math.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect;
|
||||
|
||||
Height = height;
|
||||
|
@ -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; }
|
||||
|
@ -60,6 +60,6 @@ namespace osu.Game.Tests.Mods
|
||||
/// This local helper is used rather than <see cref="Ruleset.CreateAllMods"/>, because the aforementioned method flattens multi mods.
|
||||
/// </remarks>>
|
||||
private static IEnumerable<MultiMod> getMultiMods(Ruleset ruleset)
|
||||
=> Enum.GetValues(typeof(ModType)).Cast<ModType>().SelectMany(ruleset.GetModsFor).OfType<MultiMod>();
|
||||
=> Enum.GetValues<ModType>().SelectMany(ruleset.GetModsFor).OfType<MultiMod>();
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
35
osu.Game.Tests/Visual/Editing/TestScenePreviewTime.cs
Normal file
35
osu.Game.Tests/Visual/Editing/TestScenePreviewTime.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(false, createRuleset: () => new OsuRuleset
|
||||
createPlayerTest(createRuleset: () => new OsuRuleset
|
||||
{
|
||||
RulesetInfo =
|
||||
{
|
||||
|
@ -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>;
|
||||
|
@ -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));
|
||||
|
@ -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",
|
||||
|
@ -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]));
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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", () =>
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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" } },
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
117
osu.Game.Tests/Visual/Ranking/TestSceneOverallRanking.cs
Normal file
117
osu.Game.Tests/Visual/Ranking/TestSceneOverallRanking.cs
Normal file
@ -0,0 +1,117 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics.User;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public partial class TestSceneOverallRanking : OsuTestScene
|
||||
{
|
||||
private OverallRanking overallRanking = null!;
|
||||
|
||||
[Test]
|
||||
public void TestUpdatePending()
|
||||
{
|
||||
createDisplay();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAllIncreased()
|
||||
{
|
||||
createDisplay();
|
||||
displayUpdate(
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 12_345,
|
||||
Accuracy = 98.99,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = 5_072
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 1_234,
|
||||
Accuracy = 99.07,
|
||||
MaxCombo = 2_352,
|
||||
RankedScore = 23_124_231_435,
|
||||
TotalScore = 123_124_231_435,
|
||||
PP = 5_434
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAllDecreased()
|
||||
{
|
||||
createDisplay();
|
||||
displayUpdate(
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 1_234,
|
||||
Accuracy = 99.07,
|
||||
MaxCombo = 2_352,
|
||||
RankedScore = 23_124_231_435,
|
||||
TotalScore = 123_124_231_435,
|
||||
PP = 5_434
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 12_345,
|
||||
Accuracy = 98.99,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = 5_072
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoChanges()
|
||||
{
|
||||
var statistics = new UserStatistics
|
||||
{
|
||||
GlobalRank = 12_345,
|
||||
Accuracy = 98.99,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = 5_072
|
||||
};
|
||||
|
||||
createDisplay();
|
||||
displayUpdate(statistics, statistics);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNotRanked()
|
||||
{
|
||||
var statistics = new UserStatistics
|
||||
{
|
||||
GlobalRank = null,
|
||||
Accuracy = 98.99,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = null
|
||||
};
|
||||
|
||||
createDisplay();
|
||||
displayUpdate(statistics, statistics);
|
||||
}
|
||||
|
||||
private void createDisplay() => AddStep("create display", () => Child = overallRanking = new OverallRanking
|
||||
{
|
||||
Width = 400,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
});
|
||||
|
||||
private void displayUpdate(UserStatistics before, UserStatistics after) =>
|
||||
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new SoloStatisticsUpdate(new ScoreInfo(), before, after));
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -127,7 +127,7 @@ namespace osu.Game.Tournament.IPC
|
||||
using (var stream = IPCStorage.GetStream(file_ipc_state_filename))
|
||||
using (var sr = new StreamReader(stream))
|
||||
{
|
||||
State.Value = (TourneyState)Enum.Parse(typeof(TourneyState), sr.ReadLine().AsNonNull());
|
||||
State.Value = Enum.Parse<TourneyState>(sr.ReadLine().AsNonNull());
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
continue;
|
||||
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
string[] split = line.Split(':');
|
||||
string[] split = line.Split(':', StringSplitOptions.TrimEntries);
|
||||
|
||||
if (split.Length < 2)
|
||||
{
|
||||
@ -55,9 +55,9 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
|
||||
teams.Add(new TournamentTeam
|
||||
{
|
||||
FullName = { Value = split[1].Trim(), },
|
||||
Acronym = { Value = split.Length >= 3 ? split[2].Trim() : null, },
|
||||
FlagName = { Value = split[0].Trim() }
|
||||
FullName = { Value = split[1], },
|
||||
Acronym = { Value = split.Length >= 3 ? split[2] : null, },
|
||||
FlagName = { Value = split[0] }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
{
|
||||
var countries = new List<TournamentTeam>();
|
||||
|
||||
foreach (var country in Enum.GetValues(typeof(CountryCode)).Cast<CountryCode>().Skip(1))
|
||||
foreach (var country in Enum.GetValues<CountryCode>().Skip(1))
|
||||
{
|
||||
countries.Add(new TournamentTeam
|
||||
{
|
||||
|
22
osu.Game/Beatmaps/BeatmapSetOnlineNomination.cs
Normal file
22
osu.Game/Beatmaps/BeatmapSetOnlineNomination.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -5,16 +5,19 @@ using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
{
|
||||
public abstract partial class BeatmapCard : OsuClickableContainer
|
||||
public abstract partial class BeatmapCard : OsuClickableContainer, IHasContextMenu
|
||||
{
|
||||
public const float TRANSITION_DURATION = 400;
|
||||
public const float CORNER_RADIUS = 10;
|
||||
@ -96,5 +99,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
||||
throw new ArgumentOutOfRangeException(nameof(size), size, @"Unsupported card size");
|
||||
}
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem(ContextMenuStrings.ViewBeatmap, MenuItemType.Highlighted, Action),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -160,7 +160,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
break;
|
||||
|
||||
case @"SampleSet":
|
||||
defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value);
|
||||
defaultSampleBank = Enum.Parse<LegacySampleBank>(pair.Value);
|
||||
break;
|
||||
|
||||
case @"SampleVolume":
|
||||
@ -218,7 +218,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
break;
|
||||
|
||||
case @"Countdown":
|
||||
beatmap.BeatmapInfo.Countdown = (CountdownType)Enum.Parse(typeof(CountdownType), pair.Value);
|
||||
beatmap.BeatmapInfo.Countdown = Enum.Parse<CountdownType>(pair.Value);
|
||||
break;
|
||||
|
||||
case @"CountdownOffset":
|
||||
|
@ -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)},"));
|
||||
|
@ -132,13 +132,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator = ':', bool shouldTrim = true)
|
||||
{
|
||||
string[] split = line.Split(separator, 2);
|
||||
|
||||
if (shouldTrim)
|
||||
{
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
split[i] = split[i].Trim();
|
||||
}
|
||||
string[] split = line.Split(separator, 2, shouldTrim ? StringSplitOptions.TrimEntries : StringSplitOptions.None);
|
||||
|
||||
return new KeyValuePair<string, string>
|
||||
(
|
||||
|
@ -301,11 +301,11 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
private string parseLayer(string value) => Enum.Parse(typeof(LegacyStoryLayer), value).ToString();
|
||||
private string parseLayer(string value) => Enum.Parse<LegacyStoryLayer>(value).ToString();
|
||||
|
||||
private Anchor parseOrigin(string value)
|
||||
{
|
||||
var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value);
|
||||
var origin = Enum.Parse<LegacyOrigins>(value);
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
@ -343,8 +343,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private AnimationLoopType parseAnimationLoopType(string value)
|
||||
{
|
||||
var parsed = (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), value);
|
||||
return Enum.IsDefined(typeof(AnimationLoopType), parsed) ? parsed : AnimationLoopType.LoopForever;
|
||||
var parsed = Enum.Parse<AnimationLoopType>(value);
|
||||
return Enum.IsDefined(parsed) ? parsed : AnimationLoopType.LoopForever;
|
||||
}
|
||||
|
||||
private void handleVariables(string line)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -22,38 +22,8 @@ namespace osu.Game.Graphics
|
||||
public static Color4 Gray(byte amt) => new Color4(amt, amt, amt, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the colour for a <see cref="DifficultyRating"/>.
|
||||
/// Retrieves the colour for a given point in the star range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sourced from the @diff-{rating} variables in https://github.com/ppy/osu-web/blob/71fbab8936d79a7929d13854f5e854b4f383b236/resources/assets/less/variables.less.
|
||||
/// </remarks>
|
||||
public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterColour = false)
|
||||
{
|
||||
switch (difficulty)
|
||||
{
|
||||
case DifficultyRating.Easy:
|
||||
return Color4Extensions.FromHex("4ebfff");
|
||||
|
||||
case DifficultyRating.Normal:
|
||||
return Color4Extensions.FromHex("66ff91");
|
||||
|
||||
case DifficultyRating.Hard:
|
||||
return Color4Extensions.FromHex("f7e85d");
|
||||
|
||||
case DifficultyRating.Insane:
|
||||
return Color4Extensions.FromHex("ff7e68");
|
||||
|
||||
case DifficultyRating.Expert:
|
||||
return Color4Extensions.FromHex("fe3c71");
|
||||
|
||||
case DifficultyRating.ExpertPlus:
|
||||
return Color4Extensions.FromHex("6662dd");
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(difficulty));
|
||||
}
|
||||
}
|
||||
|
||||
public Color4 ForStarDifficulty(double starDifficulty) => ColourUtils.SampleFromLinearGradient(new[]
|
||||
{
|
||||
(0.1f, Color4Extensions.FromHex("aaaaaa")),
|
||||
|
@ -82,7 +82,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
if (Link != null)
|
||||
{
|
||||
items.Add(new OsuMenuItem("Open", MenuItemType.Standard, () => host.OpenUrlExternally(Link)));
|
||||
items.Add(new OsuMenuItem("Open", MenuItemType.Highlighted, () => host.OpenUrlExternally(Link)));
|
||||
items.Add(new OsuMenuItem("Copy URL", MenuItemType.Standard, copyUrl));
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public OsuEnumDropdown()
|
||||
{
|
||||
Items = (T[])Enum.GetValues(typeof(T));
|
||||
Items = Enum.GetValues<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
24
osu.Game/Localisation/ContextMenuStrings.cs
Normal file
24
osu.Game/Localisation/ContextMenuStrings.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation
|
||||
{
|
||||
public static class ContextMenuStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.ContextMenu";
|
||||
|
||||
/// <summary>
|
||||
/// "View profile"
|
||||
/// </summary>
|
||||
public static LocalisableString ViewProfile => new TranslatableString(getKey(@"view_profile"), @"View profile");
|
||||
|
||||
/// <summary>
|
||||
/// "View beatmap"
|
||||
/// </summary>
|
||||
public static LocalisableString ViewBeatmap => new TranslatableString(getKey(@"view_beatmap"), @"View beatmap");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -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))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")]
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty]
|
||||
private string type
|
||||
{
|
||||
set => Type = (RecentActivityType)Enum.Parse(typeof(RecentActivityType), value.ToPascalCase());
|
||||
set => Type = Enum.Parse<RecentActivityType>(value.ToPascalCase());
|
||||
}
|
||||
|
||||
public RecentActivityType Type;
|
||||
@ -29,7 +29,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty]
|
||||
private string scoreRank
|
||||
{
|
||||
set => ScoreRank = (ScoreRank)Enum.Parse(typeof(ScoreRank), value);
|
||||
set => ScoreRank = Enum.Parse<ScoreRank>(value);
|
||||
}
|
||||
|
||||
public ScoreRank ScoreRank;
|
||||
|
@ -185,7 +185,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty(@"playstyle")]
|
||||
private string[] playStyle
|
||||
{
|
||||
set => PlayStyles = value?.Select(str => Enum.Parse(typeof(APIPlayStyle), str, true)).Cast<APIPlayStyle>().ToArray();
|
||||
set => PlayStyles = value?.Select(str => Enum.Parse<APIPlayStyle>(str, true)).ToArray();
|
||||
}
|
||||
|
||||
public APIPlayStyle[] PlayStyles;
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -341,6 +341,8 @@ namespace osu.Game.Online.Chat
|
||||
OpenWiki,
|
||||
Custom,
|
||||
OpenChangelog,
|
||||
FilterBeatmapSetGenre,
|
||||
FilterBeatmapSetLanguage,
|
||||
}
|
||||
|
||||
public class Link : IComparable<Link>
|
||||
|
@ -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 };
|
||||
|
@ -9,7 +9,8 @@ namespace osu.Game.Online
|
||||
{
|
||||
public ProductionEndpointConfiguration()
|
||||
{
|
||||
WebsiteRootUrl = APIEndpointUrl = @"https://osu.ppy.sh";
|
||||
WebsiteRootUrl = @"https://osu.ppy.sh";
|
||||
APIEndpointUrl = @"https://lazer.ppy.sh";
|
||||
APIClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
|
||||
APIClientID = "5";
|
||||
SpectatorEndpointUrl = "https://spectator.ppy.sh/spectator";
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
@ -353,7 +354,18 @@ namespace osu.Game
|
||||
break;
|
||||
|
||||
case LinkAction.SearchBeatmapSet:
|
||||
SearchBeatmapSet(argString);
|
||||
if (link.Argument is RomanisableString romanisable)
|
||||
SearchBeatmapSet(romanisable.GetPreferred(Localisation.CurrentParameters.Value.PreferOriginalScript));
|
||||
else
|
||||
SearchBeatmapSet(argString);
|
||||
break;
|
||||
|
||||
case LinkAction.FilterBeatmapSetGenre:
|
||||
FilterBeatmapSetGenre((SearchGenre)link.Argument);
|
||||
break;
|
||||
|
||||
case LinkAction.FilterBeatmapSetLanguage:
|
||||
FilterBeatmapSetLanguage((SearchLanguage)link.Argument);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenEditorTimestamp:
|
||||
@ -460,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>
|
||||
@ -723,7 +739,7 @@ namespace osu.Game
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var languages = Enum.GetValues(typeof(Language)).OfType<Language>();
|
||||
var languages = Enum.GetValues<Language>();
|
||||
|
||||
var mappings = languages.Select(language =>
|
||||
{
|
||||
|
@ -46,6 +46,7 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Metadata;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Settings;
|
||||
@ -193,6 +194,7 @@ namespace osu.Game
|
||||
protected MultiplayerClient MultiplayerClient { get; private set; }
|
||||
|
||||
private MetadataClient metadataClient;
|
||||
private SoloStatisticsWatcher soloStatisticsWatcher;
|
||||
|
||||
private RealmAccess realm;
|
||||
|
||||
@ -301,6 +303,7 @@ namespace osu.Game
|
||||
dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints));
|
||||
dependencies.CacheAs(MultiplayerClient = new OnlineMultiplayerClient(endpoints));
|
||||
dependencies.CacheAs(metadataClient = new OnlineMetadataClient(endpoints));
|
||||
dependencies.CacheAs(soloStatisticsWatcher = new SoloStatisticsWatcher());
|
||||
|
||||
AddInternal(new BeatmapOnlineChangeIngest(beatmapUpdater, realm, metadataClient));
|
||||
|
||||
@ -346,6 +349,7 @@ namespace osu.Game
|
||||
AddInternal(spectatorClient);
|
||||
AddInternal(MultiplayerClient);
|
||||
AddInternal(metadataClient);
|
||||
AddInternal(soloStatisticsWatcher);
|
||||
|
||||
AddInternal(rulesetConfigCache);
|
||||
|
||||
@ -603,7 +607,7 @@ namespace osu.Game
|
||||
|
||||
try
|
||||
{
|
||||
foreach (ModType type in Enum.GetValues(typeof(ModType)))
|
||||
foreach (ModType type in Enum.GetValues<ModType>())
|
||||
{
|
||||
dict[type] = instance.GetModsFor(type)
|
||||
// Rulesets should never return null mods, but let's be defensive just in case.
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -3,26 +3,29 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Overlays.BeatmapSet.Buttons;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
@ -41,12 +44,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
private readonly UpdateableOnlineBeatmapSetCover cover;
|
||||
private readonly Box coverGradient;
|
||||
private readonly OsuSpriteText title, artist;
|
||||
private readonly LinkFlowContainer title, artist;
|
||||
private readonly AuthorInfo author;
|
||||
|
||||
private readonly ExplicitContentBeatmapBadge explicitContent;
|
||||
private readonly SpotlightBeatmapBadge spotlight;
|
||||
private readonly FeaturedArtistBeatmapBadge featuredArtist;
|
||||
private ExternalLinkButton externalLink;
|
||||
|
||||
private readonly FillFlowContainer downloadButtonsContainer;
|
||||
private readonly BeatmapAvailability beatmapAvailability;
|
||||
@ -65,8 +66,6 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
public BeatmapSetHeaderContent()
|
||||
{
|
||||
ExternalLinkButton externalLink;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = new Container
|
||||
@ -91,118 +90,74 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
},
|
||||
},
|
||||
},
|
||||
new OsuContextMenuContainer
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = new Container
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding
|
||||
Vertical = BeatmapSetOverlay.Y_PADDING,
|
||||
Left = BeatmapSetOverlay.X_PADDING,
|
||||
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
fadeContent = new FillFlowContainer
|
||||
{
|
||||
Vertical = BeatmapSetOverlay.Y_PADDING,
|
||||
Left = BeatmapSetOverlay.X_PADDING,
|
||||
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
fadeContent = new FillFlowContainer
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
new Container
|
||||
{
|
||||
new Container
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = Picker = new BeatmapPicker(),
|
||||
},
|
||||
title = new MetadataFlowContainer(s =>
|
||||
{
|
||||
s.Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true);
|
||||
})
|
||||
{
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
},
|
||||
artist = new MetadataFlowContainer(s =>
|
||||
{
|
||||
s.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true);
|
||||
})
|
||||
{
|
||||
Margin = new MarginPadding { Bottom = 20 },
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = author = new AuthorInfo(),
|
||||
},
|
||||
beatmapAvailability = new BeatmapAvailability(),
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = buttons_height,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = Picker = new BeatmapPicker(),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
Children = new Drawable[]
|
||||
favouriteButton = new FavouriteButton
|
||||
{
|
||||
title = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true)
|
||||
},
|
||||
externalLink = new ExternalLinkButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font
|
||||
},
|
||||
explicitContent = new ExplicitContentBeatmapBadge
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 10, Bottom = 4 },
|
||||
},
|
||||
spotlight = new SpotlightBeatmapBadge
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 10, Bottom = 4 },
|
||||
}
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Bottom = 20 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
artist = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true),
|
||||
},
|
||||
featuredArtist = new FeaturedArtistBeatmapBadge
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 10 }
|
||||
}
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = author = new AuthorInfo(),
|
||||
},
|
||||
beatmapAvailability = new BeatmapAvailability(),
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = buttons_height,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
favouriteButton = new FavouriteButton
|
||||
{
|
||||
BeatmapSet = { BindTarget = BeatmapSet }
|
||||
},
|
||||
downloadButtonsContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
|
||||
Spacing = new Vector2(buttons_spacing),
|
||||
},
|
||||
BeatmapSet = { BindTarget = BeatmapSet }
|
||||
},
|
||||
},
|
||||
downloadButtonsContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
|
||||
Spacing = new Vector2(buttons_spacing),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
loading = new LoadingSpinner
|
||||
{
|
||||
@ -237,12 +192,17 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
Picker.Beatmap.ValueChanged += b =>
|
||||
{
|
||||
Details.BeatmapInfo = b.NewValue;
|
||||
externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineID}";
|
||||
updateExternalLink();
|
||||
|
||||
onlineStatusPill.Status = b.NewValue?.Status ?? BeatmapOnlineStatus.None;
|
||||
};
|
||||
}
|
||||
|
||||
private void updateExternalLink()
|
||||
{
|
||||
if (externalLink != null) externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{Picker.Beatmap.Value?.Ruleset.ShortName}/{Picker.Beatmap.Value?.OnlineID}";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
@ -275,12 +235,38 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
loading.Hide();
|
||||
|
||||
title.Text = new RomanisableString(setInfo.NewValue.TitleUnicode, setInfo.NewValue.Title);
|
||||
artist.Text = new RomanisableString(setInfo.NewValue.ArtistUnicode, setInfo.NewValue.Artist);
|
||||
var titleText = new RomanisableString(setInfo.NewValue.TitleUnicode, setInfo.NewValue.Title);
|
||||
var artistText = new RomanisableString(setInfo.NewValue.ArtistUnicode, setInfo.NewValue.Artist);
|
||||
|
||||
explicitContent.Alpha = setInfo.NewValue.HasExplicitContent ? 1 : 0;
|
||||
spotlight.Alpha = setInfo.NewValue.FeaturedInSpotlight ? 1 : 0;
|
||||
featuredArtist.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0;
|
||||
title.Clear();
|
||||
artist.Clear();
|
||||
|
||||
title.AddLink(titleText, LinkAction.SearchBeatmapSet, titleText);
|
||||
|
||||
title.AddArbitraryDrawable(Empty().With(d => d.Width = 5));
|
||||
title.AddArbitraryDrawable(externalLink = new ExternalLinkButton());
|
||||
|
||||
if (setInfo.NewValue.HasExplicitContent)
|
||||
{
|
||||
title.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
title.AddArbitraryDrawable(new ExplicitContentBeatmapBadge());
|
||||
}
|
||||
|
||||
if (setInfo.NewValue.FeaturedInSpotlight)
|
||||
{
|
||||
title.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
title.AddArbitraryDrawable(new SpotlightBeatmapBadge());
|
||||
}
|
||||
|
||||
artist.AddLink(artistText, LinkAction.SearchBeatmapSet, artistText);
|
||||
|
||||
if (setInfo.NewValue.TrackId != null)
|
||||
{
|
||||
artist.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
artist.AddArbitraryDrawable(new FeaturedArtistBeatmapBadge());
|
||||
}
|
||||
|
||||
updateExternalLink();
|
||||
|
||||
onlineStatusPill.FadeIn(500, Easing.OutQuint);
|
||||
|
||||
@ -327,5 +313,32 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class MetadataFlowContainer : LinkFlowContainer
|
||||
{
|
||||
public MetadataFlowContainer(Action<SpriteText> defaultCreationParameters = null)
|
||||
: base(defaultCreationParameters)
|
||||
{
|
||||
TextAnchor = Anchor.CentreLeft;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
}
|
||||
|
||||
protected override DrawableLinkCompiler CreateLinkCompiler(ITextPart textPart) => new MetadataLinkCompiler(textPart);
|
||||
|
||||
public partial class MetadataLinkCompiler : DrawableLinkCompiler
|
||||
{
|
||||
public MetadataLinkCompiler(ITextPart part)
|
||||
: base(part)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
IdleColour = Color4.White;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
21
osu.Game/Overlays/BeatmapSet/MetadataSectionDescription.cs
Normal file
21
osu.Game/Overlays/BeatmapSet/MetadataSectionDescription.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
30
osu.Game/Overlays/BeatmapSet/MetadataSectionGenre.cs
Normal file
30
osu.Game/Overlays/BeatmapSet/MetadataSectionGenre.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
30
osu.Game/Overlays/BeatmapSet/MetadataSectionLanguage.cs
Normal file
30
osu.Game/Overlays/BeatmapSet/MetadataSectionLanguage.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
63
osu.Game/Overlays/BeatmapSet/MetadataSectionNominators.cs
Normal file
63
osu.Game/Overlays/BeatmapSet/MetadataSectionNominators.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
osu.Game/Overlays/BeatmapSet/MetadataSectionSource.cs
Normal file
25
osu.Game/Overlays/BeatmapSet/MetadataSectionSource.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
35
osu.Game/Overlays/BeatmapSet/MetadataSectionTags.cs
Normal file
35
osu.Game/Overlays/BeatmapSet/MetadataSectionTags.cs
Normal 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(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -95,8 +95,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersScore, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, minSize: 60, maxSize: 70)),
|
||||
new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 25)), // flag
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersPlayer, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 125)),
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersCombo, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 120))
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersPlayer, Anchor.CentreLeft, new Dimension(minSize: 125)),
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersCombo, Anchor.CentreLeft, new Dimension(minSize: 70, maxSize: 120))
|
||||
};
|
||||
|
||||
// All statistics across all scores, unordered.
|
||||
@ -116,7 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
|
||||
var displayName = ruleset.GetDisplayNameForHitResult(result);
|
||||
|
||||
columns.Add(new TableColumn(displayName, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60)));
|
||||
columns.Add(new TableColumn(displayName, Anchor.CentreLeft, new Dimension(minSize: 35, maxSize: 60)));
|
||||
statisticResultTypes.Add((result, displayName));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,6 @@ namespace osu.Game.Overlays.Chat
|
||||
bool showSearch = change.NewValue;
|
||||
|
||||
PlaceholderText = showSearch ? HomeStrings.SearchPlaceholder : ChatStrings.InputPlaceholder;
|
||||
Text = string.Empty;
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,11 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -148,11 +150,11 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
List<MenuItem> items = new List<MenuItem>
|
||||
{
|
||||
new OsuMenuItem("View Profile", MenuItemType.Highlighted, openUserProfile)
|
||||
new OsuMenuItem(ContextMenuStrings.ViewProfile, MenuItemType.Highlighted, openUserProfile)
|
||||
};
|
||||
|
||||
if (!user.Equals(api.LocalUser.Value))
|
||||
items.Add(new OsuMenuItem("Start Chat", MenuItemType.Standard, openUserChannel));
|
||||
items.Add(new OsuMenuItem(UsersStrings.CardSendMessage, MenuItemType.Standard, openUserChannel));
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.Settings.Sections.Maintenance;
|
||||
using osu.Game.Screens.Edit.Setup;
|
||||
@ -127,14 +128,17 @@ namespace osu.Game.Overlays.FirstRunSetup
|
||||
if (available)
|
||||
{
|
||||
copyInformation.Text =
|
||||
"Data migration will use \"hard links\". No extra disk space will be used, and you can delete either data folder at any point without affecting the other installation.";
|
||||
"Data migration will use \"hard links\". No extra disk space will be used, and you can delete either data folder at any point without affecting the other installation. ";
|
||||
|
||||
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()));
|
||||
|
@ -79,8 +79,7 @@ namespace osu.Game.Overlays.FirstRunSetup
|
||||
Direction = FillDirection.Full;
|
||||
Spacing = new Vector2(5);
|
||||
|
||||
ChildrenEnumerable = Enum.GetValues(typeof(Language))
|
||||
.Cast<Language>()
|
||||
ChildrenEnumerable = Enum.GetValues<Language>()
|
||||
.Select(l => new LanguageButton(l)
|
||||
{
|
||||
Action = () => frameworkLocale.Value = l.ToCultureCode()
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
|
||||
@ -38,20 +39,30 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new FillFlowContainer
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
Header.With(h => h.Depth = float.MinValue),
|
||||
content = new PopoverContainer
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header.With(h => h.Depth = float.MinValue),
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
Loading = new LoadingLayer(true)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)"-";
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user