mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 23:05:37 +08:00
Merge branch 'master' into classic-mode-flags
This commit is contained in:
commit
4710a12774
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.128.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.128.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public class CatchModDifficultyAdjust : ModDifficultyAdjust
|
public class CatchModDifficultyAdjust : ModDifficultyAdjust
|
||||||
{
|
{
|
||||||
[SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
|
[SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
|
||||||
public BindableNumber<float> CircleSize { get; } = new BindableFloat
|
public BindableNumber<float> CircleSize { get; } = new BindableFloatWithLimitExtension
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 1,
|
MinValue = 1,
|
||||||
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
};
|
};
|
||||||
|
|
||||||
[SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
|
[SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
|
||||||
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
|
public BindableNumber<float> ApproachRate { get; } = new BindableFloatWithLimitExtension
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 1,
|
MinValue = 1,
|
||||||
@ -31,6 +31,14 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
Value = 5,
|
Value = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override void ApplyLimits(bool extended)
|
||||||
|
{
|
||||||
|
base.ApplyLimits(extended);
|
||||||
|
|
||||||
|
CircleSize.MaxValue = extended ? 11 : 10;
|
||||||
|
ApproachRate.MaxValue = extended ? 11 : 10;
|
||||||
|
}
|
||||||
|
|
||||||
public override string SettingDescription
|
public override string SettingDescription
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public class OsuModDifficultyAdjust : ModDifficultyAdjust
|
public class OsuModDifficultyAdjust : ModDifficultyAdjust
|
||||||
{
|
{
|
||||||
[SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
|
[SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
|
||||||
public BindableNumber<float> CircleSize { get; } = new BindableFloat
|
public BindableNumber<float> CircleSize { get; } = new BindableFloatWithLimitExtension
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
};
|
};
|
||||||
|
|
||||||
[SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
|
[SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
|
||||||
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
|
public BindableNumber<float> ApproachRate { get; } = new BindableFloatWithLimitExtension
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
@ -31,6 +31,14 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
Value = 5,
|
Value = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override void ApplyLimits(bool extended)
|
||||||
|
{
|
||||||
|
base.ApplyLimits(extended);
|
||||||
|
|
||||||
|
CircleSize.MaxValue = extended ? 11 : 10;
|
||||||
|
ApproachRate.MaxValue = extended ? 11 : 10;
|
||||||
|
}
|
||||||
|
|
||||||
public override string SettingDescription
|
public override string SettingDescription
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -110,8 +110,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
double startTime = start.GetEndTime();
|
double startTime = start.GetEndTime();
|
||||||
double duration = end.StartTime - startTime;
|
double duration = end.StartTime - startTime;
|
||||||
|
|
||||||
|
// Preempt time can go below 800ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||||
|
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear preempt function (see: OsuHitObject).
|
||||||
|
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
|
||||||
|
double preempt = PREEMPT * Math.Min(1, start.TimePreempt / OsuHitObject.PREEMPT_MIN);
|
||||||
|
|
||||||
fadeOutTime = startTime + fraction * duration;
|
fadeOutTime = startTime + fraction * duration;
|
||||||
fadeInTime = fadeOutTime - PREEMPT;
|
fadeInTime = fadeOutTime - preempt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -25,6 +26,11 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal const float BASE_SCORING_DISTANCE = 100;
|
internal const float BASE_SCORING_DISTANCE = 100;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum preempt time at AR=10.
|
||||||
|
/// </summary>
|
||||||
|
public const double PREEMPT_MIN = 450;
|
||||||
|
|
||||||
public double TimePreempt = 600;
|
public double TimePreempt = 600;
|
||||||
public double TimeFadeIn = 400;
|
public double TimeFadeIn = 400;
|
||||||
|
|
||||||
@ -112,8 +118,13 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
|
||||||
TimeFadeIn = 400; // as per osu-stable
|
|
||||||
|
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||||
|
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
|
||||||
|
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
|
||||||
|
// This adjustment is necessary for AR>10, otherwise TimePreempt can become smaller leading to hitcircles not fully fading in.
|
||||||
|
TimeFadeIn = 400 * Math.Min(1, TimePreempt / PREEMPT_MIN);
|
||||||
|
|
||||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
protected override bool Handle(UIEvent e)
|
protected override bool Handle(UIEvent e)
|
||||||
{
|
{
|
||||||
if (e is MouseMoveEvent && !AllowUserCursorMovement) return false;
|
if ((e is MouseMoveEvent || e is TouchMoveEvent) && !AllowUserCursorMovement) return false;
|
||||||
|
|
||||||
return base.Handle(e);
|
return base.Handle(e);
|
||||||
}
|
}
|
||||||
|
@ -68,12 +68,29 @@ namespace osu.Game.Tests.Online
|
|||||||
Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25));
|
Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeserialiseDifficultyAdjustModWithExtendedLimits()
|
||||||
|
{
|
||||||
|
var apiMod = new APIMod(new TestModDifficultyAdjust
|
||||||
|
{
|
||||||
|
OverallDifficulty = { Value = 11 },
|
||||||
|
ExtendedLimits = { Value = true }
|
||||||
|
});
|
||||||
|
|
||||||
|
var deserialised = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||||
|
var converted = (TestModDifficultyAdjust)deserialised.ToMod(new TestRuleset());
|
||||||
|
|
||||||
|
Assert.That(converted.ExtendedLimits.Value, Is.True);
|
||||||
|
Assert.That(converted.OverallDifficulty.Value, Is.EqualTo(11));
|
||||||
|
}
|
||||||
|
|
||||||
private class TestRuleset : Ruleset
|
private class TestRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
||||||
{
|
{
|
||||||
new TestMod(),
|
new TestMod(),
|
||||||
new TestModTimeRamp(),
|
new TestModTimeRamp(),
|
||||||
|
new TestModDifficultyAdjust()
|
||||||
};
|
};
|
||||||
|
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
|
||||||
@ -135,5 +152,9 @@ namespace osu.Game.Tests.Online
|
|||||||
Value = true
|
Value = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestModDifficultyAdjust : ModDifficultyAdjust
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,16 @@ namespace osu.Game.Tests.Online
|
|||||||
Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25));
|
Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeserialiseEnumMod()
|
||||||
|
{
|
||||||
|
var apiMod = new APIMod(new TestModEnum { TestSetting = { Value = TestEnum.Value2 } });
|
||||||
|
|
||||||
|
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||||
|
|
||||||
|
Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(1));
|
||||||
|
}
|
||||||
|
|
||||||
private class TestRuleset : Ruleset
|
private class TestRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
||||||
@ -135,5 +145,22 @@ namespace osu.Game.Tests.Online
|
|||||||
Value = true
|
Value = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestModEnum : Mod
|
||||||
|
{
|
||||||
|
public override string Name => "Test Mod";
|
||||||
|
public override string Acronym => "TM";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
[SettingSource("Test")]
|
||||||
|
public Bindable<TestEnum> TestSetting { get; } = new Bindable<TestEnum>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TestEnum
|
||||||
|
{
|
||||||
|
Value1 = 0,
|
||||||
|
Value2 = 1,
|
||||||
|
Value3 = 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
||||||
@ -19,16 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
public class TestSceneMultiplayerParticipantsList : MultiplayerTestScene
|
public class TestSceneMultiplayerParticipantsList : MultiplayerTestScene
|
||||||
{
|
{
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public new void Setup() => Schedule(() =>
|
public new void Setup() => Schedule(createNewParticipantsList);
|
||||||
{
|
|
||||||
Child = new ParticipantsList
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Size = new Vector2(380, 0.7f)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAddUser()
|
public void TestAddUser()
|
||||||
@ -75,6 +69,50 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddAssert("single panel is for second user", () => this.ChildrenOfType<ParticipantPanel>().Single().User.User == secondUser);
|
AddAssert("single panel is for second user", () => this.ChildrenOfType<ParticipantPanel>().Single().User.User == secondUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestGameStateHasPriorityOverDownloadState()
|
||||||
|
{
|
||||||
|
AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
|
||||||
|
checkProgressBarVisibility(true);
|
||||||
|
|
||||||
|
AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Results));
|
||||||
|
checkProgressBarVisibility(false);
|
||||||
|
AddUntilStep("ready mark visible", () => this.ChildrenOfType<StateDisplay>().Single().IsPresent);
|
||||||
|
|
||||||
|
AddStep("make user ready", () => Client.ChangeState(MultiplayerUserState.Idle));
|
||||||
|
checkProgressBarVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCorrectInitialState()
|
||||||
|
{
|
||||||
|
AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
|
||||||
|
AddStep("recreate list", createNewParticipantsList);
|
||||||
|
checkProgressBarVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapDownloadingStates()
|
||||||
|
{
|
||||||
|
AddStep("set to no map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.NotDownloaded()));
|
||||||
|
AddStep("set to downloading map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(0)));
|
||||||
|
|
||||||
|
checkProgressBarVisibility(true);
|
||||||
|
|
||||||
|
AddRepeatStep("increment progress", () =>
|
||||||
|
{
|
||||||
|
var progress = this.ChildrenOfType<ParticipantPanel>().Single().User.BeatmapAvailability.DownloadProgress ?? 0;
|
||||||
|
Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(progress + RNG.NextSingle(0.1f)));
|
||||||
|
}, 25);
|
||||||
|
|
||||||
|
AddAssert("progress bar increased", () => this.ChildrenOfType<ProgressBar>().Single().Current.Value > 0);
|
||||||
|
|
||||||
|
AddStep("set to importing map", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.Importing()));
|
||||||
|
checkProgressBarVisibility(false);
|
||||||
|
|
||||||
|
AddStep("set to available", () => Client.ChangeBeatmapAvailability(BeatmapAvailability.LocallyAvailable()));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestToggleReadyState()
|
public void TestToggleReadyState()
|
||||||
{
|
{
|
||||||
@ -122,6 +160,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
});
|
});
|
||||||
|
|
||||||
Client.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results + 1));
|
Client.ChangeUserState(i, (MultiplayerUserState)RNG.Next(0, (int)MultiplayerUserState.Results + 1));
|
||||||
|
|
||||||
|
if (RNG.NextBool())
|
||||||
|
{
|
||||||
|
var beatmapState = (DownloadState)RNG.Next(0, (int)DownloadState.LocallyAvailable + 1);
|
||||||
|
|
||||||
|
switch (beatmapState)
|
||||||
|
{
|
||||||
|
case DownloadState.NotDownloaded:
|
||||||
|
Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.NotDownloaded());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DownloadState.Downloading:
|
||||||
|
Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Downloading(RNG.NextSingle()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DownloadState.Importing:
|
||||||
|
Client.ChangeUserBeatmapAvailability(i, BeatmapAvailability.Importing());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -152,5 +210,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddStep($"set state: {state}", () => Client.ChangeUserState(0, state));
|
AddStep($"set state: {state}", () => Client.ChangeUserState(0, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createNewParticipantsList()
|
||||||
|
{
|
||||||
|
Child = new ParticipantsList { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, Size = new Vector2(380, 0.7f) };
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkProgressBarVisibility(bool visible) =>
|
||||||
|
AddUntilStep($"progress bar {(visible ? "is" : "is not")}visible", () =>
|
||||||
|
this.ChildrenOfType<ProgressBar>().Single().IsPresent == visible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
||||||
Beatmap.SetDefault();
|
Beatmap.SetDefault();
|
||||||
|
SelectedMods.Value = Array.Empty<Mod>();
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect()));
|
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect()));
|
||||||
@ -143,7 +144,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
/// Tests that the same <see cref="Mod"/> instances are not shared between two playlist items.
|
/// Tests that the same <see cref="Mod"/> instances are not shared between two playlist items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
[Ignore("Temporarily disabled due to a non-trivial test failure")]
|
|
||||||
public void TestNewItemHasNewModInstances()
|
public void TestNewItemHasNewModInstances()
|
||||||
{
|
{
|
||||||
AddStep("set dt mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() });
|
AddStep("set dt mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() });
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
private class TestFullscreenOverlay : FullscreenOverlay<OverlayHeader>
|
private class TestFullscreenOverlay : FullscreenOverlay<OverlayHeader>
|
||||||
{
|
{
|
||||||
public TestFullscreenOverlay()
|
public TestFullscreenOverlay()
|
||||||
: base(OverlayColourScheme.Pink, null)
|
: base(OverlayColourScheme.Pink)
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -52,6 +52,17 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override OverlayHeader CreateHeader() => new TestHeader();
|
||||||
|
|
||||||
|
internal class TestHeader : OverlayHeader
|
||||||
|
{
|
||||||
|
protected override OverlayTitle CreateTitle() => new TestTitle();
|
||||||
|
|
||||||
|
internal class TestTitle : OverlayTitle
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Add(rankingsOverlay = new TestRankingsOverlay
|
Add(rankingsOverlay = new TestRankingsOverlay
|
||||||
{
|
{
|
||||||
Country = { BindTarget = countryBindable },
|
Country = { BindTarget = countryBindable },
|
||||||
Scope = { BindTarget = scope },
|
Header = { Current = { BindTarget = scope } },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,8 +65,6 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
private class TestRankingsOverlay : RankingsOverlay
|
private class TestRankingsOverlay : RankingsOverlay
|
||||||
{
|
{
|
||||||
public new Bindable<Country> Country => base.Country;
|
public new Bindable<Country> Country => base.Country;
|
||||||
|
|
||||||
public new Bindable<RankingsScope> Scope => base.Scope;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() => createDisplay(() => new TestModSelectOverlay()));
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = Array.Empty<Mod>();
|
||||||
|
createDisplay(() => new TestModSelectOverlay());
|
||||||
|
});
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
@ -47,6 +51,24 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("show", () => modSelect.Show());
|
AddStep("show", () => modSelect.Show());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSettingsResetOnDeselection()
|
||||||
|
{
|
||||||
|
var osuModDoubleTime = new OsuModDoubleTime { SpeedChange = { Value = 1.2 } };
|
||||||
|
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddStep("set dt mod with custom rate", () => { SelectedMods.Value = new[] { osuModDoubleTime }; });
|
||||||
|
|
||||||
|
AddAssert("selected mod matches", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.Value == 1.2);
|
||||||
|
|
||||||
|
AddStep("deselect", () => modSelect.DeselectAllButton.Click());
|
||||||
|
AddAssert("selected mods empty", () => SelectedMods.Value.Count == 0);
|
||||||
|
|
||||||
|
AddStep("reselect", () => modSelect.GetModButton(osuModDoubleTime).Click());
|
||||||
|
AddAssert("selected mod has default value", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.IsDefault == true);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAnimationFlushOnClose()
|
public void TestAnimationFlushOnClose()
|
||||||
{
|
{
|
||||||
@ -152,6 +174,45 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSettingsAreRetainedOnReload()
|
||||||
|
{
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } });
|
||||||
|
|
||||||
|
AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01);
|
||||||
|
|
||||||
|
AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay()));
|
||||||
|
|
||||||
|
AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExternallySetModIsReplacedByOverlayInstance()
|
||||||
|
{
|
||||||
|
Mod external = new OsuModDoubleTime();
|
||||||
|
Mod overlayButtonMod = null;
|
||||||
|
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddStep("set mod externally", () => { SelectedMods.Value = new[] { external }; });
|
||||||
|
|
||||||
|
AddAssert("ensure button is selected", () =>
|
||||||
|
{
|
||||||
|
var button = modSelect.GetModButton(SelectedMods.Value.Single());
|
||||||
|
overlayButtonMod = button.SelectedMod;
|
||||||
|
return overlayButtonMod.GetType() == external.GetType();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Right now, when an external change occurs, the ModSelectOverlay will replace the global instance with its own
|
||||||
|
AddAssert("mod instance doesn't match", () => external != overlayButtonMod);
|
||||||
|
|
||||||
|
AddAssert("one mod present in global selected", () => SelectedMods.Value.Count == 1);
|
||||||
|
AddAssert("globally selected matches button's mod instance", () => SelectedMods.Value.Contains(overlayButtonMod));
|
||||||
|
AddAssert("globally selected doesn't contain original external change", () => !SelectedMods.Value.Contains(external));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestNonStacked()
|
public void TestNonStacked()
|
||||||
{
|
{
|
||||||
@ -313,7 +374,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private void createDisplay(Func<TestModSelectOverlay> createOverlayFunc)
|
private void createDisplay(Func<TestModSelectOverlay> createOverlayFunc)
|
||||||
{
|
{
|
||||||
SelectedMods.Value = Array.Empty<Mod>();
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
modSelect = createOverlayFunc().With(d =>
|
modSelect = createOverlayFunc().With(d =>
|
||||||
|
@ -105,6 +105,15 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
checkDisplayedCount(3);
|
checkDisplayedCount(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestError()
|
||||||
|
{
|
||||||
|
setState(Visibility.Visible);
|
||||||
|
AddStep(@"error #1", sendErrorNotification);
|
||||||
|
AddAssert("Is visible", () => notificationOverlay.State.Value == Visibility.Visible);
|
||||||
|
checkDisplayedCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSpam()
|
public void TestSpam()
|
||||||
{
|
{
|
||||||
@ -179,7 +188,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private void sendBarrage()
|
private void sendBarrage()
|
||||||
{
|
{
|
||||||
switch (RNG.Next(0, 4))
|
switch (RNG.Next(0, 5))
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
sendHelloNotification();
|
sendHelloNotification();
|
||||||
@ -196,6 +205,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
case 3:
|
case 3:
|
||||||
sendDownloadProgress();
|
sendDownloadProgress();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
sendErrorNotification();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +227,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
notificationOverlay.Post(new BackgroundNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
|
notificationOverlay.Post(new BackgroundNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendErrorNotification()
|
||||||
|
{
|
||||||
|
notificationOverlay.Post(new SimpleErrorNotification { Text = @"Rut roh!. Something went wrong!" });
|
||||||
|
}
|
||||||
|
|
||||||
private void sendManyNotifications()
|
private void sendManyNotifications()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
|
52
osu.Game/Configuration/ModSettingChangeTracker.cs
Normal file
52
osu.Game/Configuration/ModSettingChangeTracker.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A helper class for tracking changes to the settings of a set of <see cref="Mod"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Ensure to dispose when usage is finished.
|
||||||
|
/// </remarks>
|
||||||
|
public class ModSettingChangeTracker : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Notifies that the setting of a <see cref="Mod"/> has changed.
|
||||||
|
/// </summary>
|
||||||
|
public Action<Mod> SettingChanged;
|
||||||
|
|
||||||
|
private readonly List<ISettingsItem> settings = new List<ISettingsItem>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ModSettingChangeTracker"/> for a set of <see cref="Mod"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mods">The set of <see cref="Mod"/>s whose settings need to be tracked.</param>
|
||||||
|
public ModSettingChangeTracker(IEnumerable<Mod> mods)
|
||||||
|
{
|
||||||
|
foreach (var mod in mods)
|
||||||
|
{
|
||||||
|
foreach (var setting in mod.CreateSettingsControls().OfType<ISettingsItem>())
|
||||||
|
{
|
||||||
|
setting.SettingChanged += () => SettingChanged?.Invoke(mod);
|
||||||
|
settings.Add(setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
SettingChanged = null;
|
||||||
|
|
||||||
|
foreach (var r in settings)
|
||||||
|
r.Dispose();
|
||||||
|
settings.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
{
|
{
|
||||||
private SampleChannel samplePopIn;
|
private SampleChannel samplePopIn;
|
||||||
private SampleChannel samplePopOut;
|
private SampleChannel samplePopOut;
|
||||||
|
protected virtual string PopInSampleName => "UI/overlay-pop-in";
|
||||||
|
protected virtual string PopOutSampleName => "UI/overlay-pop-out";
|
||||||
|
|
||||||
protected override bool BlockNonPositionalInput => true;
|
protected override bool BlockNonPositionalInput => true;
|
||||||
|
|
||||||
@ -40,8 +42,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
|
samplePopIn = audio.Samples.Get(PopInSampleName);
|
||||||
samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
|
samplePopOut = audio.Samples.Get(PopOutSampleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
@ -47,15 +48,20 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
|
if (sampleHover == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime;
|
bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= HoverDebounceTime;
|
||||||
|
|
||||||
if (enoughTimePassedSinceLastPlayback)
|
if (enoughTimePassedSinceLastPlayback)
|
||||||
{
|
{
|
||||||
sampleHover?.Play();
|
sampleHover.Frequency.Value = 0.96 + RNG.NextDouble(0.08);
|
||||||
|
sampleHover.Play();
|
||||||
|
|
||||||
lastPlaybackTime.Value = Time.Current;
|
lastPlaybackTime.Value = Time.Current;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnHover(e);
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +74,9 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Normal,
|
Normal,
|
||||||
|
|
||||||
[Description("-softer")]
|
[Description("-softer")]
|
||||||
Soft
|
Soft,
|
||||||
|
|
||||||
|
[Description("-toolbar")]
|
||||||
|
Toolbar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
Nub = new Nub(),
|
Nub = new Nub(),
|
||||||
new HoverClickSounds()
|
new HoverSounds()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nubOnRight)
|
if (nubOnRight)
|
||||||
|
@ -40,8 +40,19 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
set => CurrentNumber.Value = value;
|
set => CurrentNumber.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgressBar()
|
private readonly bool allowSeek;
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => allowSeek;
|
||||||
|
public override bool HandleNonPositionalInput => allowSeek;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a new progress bar.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="allowSeek">Whether the user should be allowed to click/drag to adjust the value.</param>
|
||||||
|
public ProgressBar(bool allowSeek)
|
||||||
{
|
{
|
||||||
|
this.allowSeek = allowSeek;
|
||||||
|
|
||||||
CurrentNumber.MinValue = 0;
|
CurrentNumber.MinValue = 0;
|
||||||
CurrentNumber.MaxValue = 1;
|
CurrentNumber.MaxValue = 1;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
using MessagePack.Formatters;
|
using MessagePack.Formatters;
|
||||||
@ -41,6 +42,13 @@ namespace osu.Game.Online.API
|
|||||||
primitiveFormatter.Serialize(ref writer, b.Value, options);
|
primitiveFormatter.Serialize(ref writer, b.Value, options);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IBindable u:
|
||||||
|
// A mod with unknown (e.g. enum) generic type.
|
||||||
|
var valueMethod = u.GetType().GetProperty(nameof(IBindable<int>.Value));
|
||||||
|
Debug.Assert(valueMethod != null);
|
||||||
|
primitiveFormatter.Serialize(ref writer, valueMethod.GetValue(u), options);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// fall back for non-bindable cases.
|
// fall back for non-bindable cases.
|
||||||
primitiveFormatter.Serialize(ref writer, kvp.Value, options);
|
primitiveFormatter.Serialize(ref writer, kvp.Value, options);
|
||||||
|
@ -239,6 +239,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
newConnection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted);
|
newConnection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted);
|
||||||
newConnection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
newConnection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
||||||
newConnection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
newConnection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
||||||
|
newConnection.On<int, BeatmapAvailability>(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged);
|
||||||
|
|
||||||
newConnection.Closed += ex =>
|
newConnection.Closed += ex =>
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Online.Rooms
|
namespace osu.Game.Online.Rooms
|
||||||
@ -24,19 +25,33 @@ namespace osu.Game.Online.Rooms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IBindable<BeatmapAvailability> Availability => availability;
|
public IBindable<BeatmapAvailability> Availability => availability;
|
||||||
|
|
||||||
private readonly Bindable<BeatmapAvailability> availability = new Bindable<BeatmapAvailability>();
|
private readonly Bindable<BeatmapAvailability> availability = new Bindable<BeatmapAvailability>(BeatmapAvailability.LocallyAvailable());
|
||||||
|
|
||||||
public OnlinePlayBeatmapAvailablilityTracker()
|
private ScheduledDelegate progressUpdate;
|
||||||
{
|
|
||||||
State.BindValueChanged(_ => updateAvailability());
|
|
||||||
Progress.BindValueChanged(_ => updateAvailability(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
SelectedItem.BindValueChanged(item => Model.Value = item.NewValue?.Beatmap.Value.BeatmapSet, true);
|
SelectedItem.BindValueChanged(item =>
|
||||||
|
{
|
||||||
|
// the underlying playlist is regularly cleared for maintenance purposes (things which probably need to be fixed eventually).
|
||||||
|
// to avoid exposing a state change when there may actually be none, ignore all nulls for now.
|
||||||
|
if (item.NewValue == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Model.Value = item.NewValue.Beatmap.Value.BeatmapSet;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
Progress.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
// incoming progress changes are going to be at a very high rate.
|
||||||
|
// we don't want to flood the network with this, so rate limit how often we send progress updates.
|
||||||
|
if (progressUpdate?.Completed != false)
|
||||||
|
progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
State.BindValueChanged(_ => updateAvailability(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool VerifyDatabasedModel(BeatmapSetInfo databasedSet)
|
protected override bool VerifyDatabasedModel(BeatmapSetInfo databasedSet)
|
||||||
|
@ -778,7 +778,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
if (recentLogCount < short_term_display_limit)
|
if (recentLogCount < short_term_display_limit)
|
||||||
{
|
{
|
||||||
Schedule(() => notifications.Post(new SimpleNotification
|
Schedule(() => notifications.Post(new SimpleErrorNotification
|
||||||
{
|
{
|
||||||
Icon = entry.Level == LogLevel.Important ? FontAwesome.Solid.ExclamationCircle : FontAwesome.Solid.Bomb,
|
Icon = entry.Level == LogLevel.Important ? FontAwesome.Solid.ExclamationCircle : FontAwesome.Solid.Bomb,
|
||||||
Text = entry.Message.Truncate(256) + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
|
Text = entry.Message.Truncate(256) + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
|
||||||
|
@ -98,7 +98,14 @@ namespace osu.Game
|
|||||||
[Cached(typeof(IBindable<RulesetInfo>))]
|
[Cached(typeof(IBindable<RulesetInfo>))]
|
||||||
protected readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
protected readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
// todo: move this to SongSelect once Screen has the ability to unsuspend.
|
/// <summary>
|
||||||
|
/// The current mod selection for the local user.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a mod select overlay is present, mod instances set to this value are not guaranteed to remain as the provided instance and will be overwritten by a copy.
|
||||||
|
/// In such a case, changes to settings of a mod will *not* propagate after a mod is added to this collection.
|
||||||
|
/// As such, all settings should be finalised before adding a mod to this collection.
|
||||||
|
/// </remarks>
|
||||||
[Cached]
|
[Cached]
|
||||||
[Cached(typeof(IBindable<IReadOnlyList<Mod>>))]
|
[Cached(typeof(IBindable<IReadOnlyList<Mod>>))]
|
||||||
protected readonly Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
protected readonly Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
|||||||
public DownloadProgressBar(BeatmapSetInfo beatmapSet)
|
public DownloadProgressBar(BeatmapSetInfo beatmapSet)
|
||||||
: base(beatmapSet)
|
: base(beatmapSet)
|
||||||
{
|
{
|
||||||
AddInternal(progressBar = new InteractionDisabledProgressBar
|
AddInternal(progressBar = new ProgressBar(false)
|
||||||
{
|
{
|
||||||
Height = 0,
|
Height = 0,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
@ -64,11 +64,5 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
|||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InteractionDisabledProgressBar : ProgressBar
|
|
||||||
{
|
|
||||||
public override bool HandlePositionalInput => false;
|
|
||||||
public override bool HandleNonPositionalInput => false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,98 +15,82 @@ using osu.Framework.Graphics.Textures;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Overlays.BeatmapListing;
|
using osu.Game.Overlays.BeatmapListing;
|
||||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class BeatmapListingOverlay : FullscreenOverlay<BeatmapListingHeader>
|
public class BeatmapListingOverlay : OnlineOverlay<BeatmapListingHeader>
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private PreviewTrackManager previewTrackManager { get; set; }
|
private PreviewTrackManager previewTrackManager { get; set; }
|
||||||
|
|
||||||
private Drawable currentContent;
|
private Drawable currentContent;
|
||||||
private LoadingLayer loadingLayer;
|
|
||||||
private Container panelTarget;
|
private Container panelTarget;
|
||||||
private FillFlowContainer<BeatmapPanel> foundContent;
|
private FillFlowContainer<BeatmapPanel> foundContent;
|
||||||
private NotFoundDrawable notFoundContent;
|
private NotFoundDrawable notFoundContent;
|
||||||
|
private BeatmapListingFilterControl filterControl;
|
||||||
private OverlayScrollContainer resultScrollContainer;
|
|
||||||
|
|
||||||
public BeatmapListingOverlay()
|
public BeatmapListingOverlay()
|
||||||
: base(OverlayColourScheme.Blue, new BeatmapListingHeader())
|
: base(OverlayColourScheme.Blue)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeatmapListingFilterControl filterControl;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
new Box
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
filterControl = new BeatmapListingFilterControl
|
||||||
Colour = ColourProvider.Background6
|
{
|
||||||
},
|
TypingStarted = onTypingStarted,
|
||||||
resultScrollContainer = new OverlayScrollContainer
|
SearchStarted = onSearchStarted,
|
||||||
{
|
SearchFinished = onSearchFinished,
|
||||||
RelativeSizeAxes = Axes.Both,
|
},
|
||||||
ScrollbarVisible = false,
|
new Container
|
||||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Header,
|
new Box
|
||||||
filterControl = new BeatmapListingFilterControl
|
|
||||||
{
|
{
|
||||||
TypingStarted = onTypingStarted,
|
RelativeSizeAxes = Axes.Both,
|
||||||
SearchStarted = onSearchStarted,
|
Colour = ColourProvider.Background4,
|
||||||
SearchFinished = onSearchFinished,
|
|
||||||
},
|
},
|
||||||
new Container
|
panelTarget = new Container
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Padding = new MarginPadding { Horizontal = 20 },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
foundContent = new FillFlowContainer<BeatmapPanel>(),
|
||||||
{
|
notFoundContent = new NotFoundDrawable(),
|
||||||
RelativeSizeAxes = Axes.Both,
|
}
|
||||||
Colour = ColourProvider.Background4,
|
}
|
||||||
},
|
},
|
||||||
panelTarget = new Container
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Padding = new MarginPadding { Horizontal = 20 },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
foundContent = new FillFlowContainer<BeatmapPanel>(),
|
|
||||||
notFoundContent = new NotFoundDrawable(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
loadingLayer = new LoadingLayer(true)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override BeatmapListingHeader CreateHeader() => new BeatmapListingHeader();
|
||||||
|
|
||||||
|
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
||||||
|
|
||||||
private void onTypingStarted()
|
private void onTypingStarted()
|
||||||
{
|
{
|
||||||
// temporary until the textbox/header is updated to always stay on screen.
|
// temporary until the textbox/header is updated to always stay on screen.
|
||||||
resultScrollContainer.ScrollToStart();
|
ScrollFlow.ScrollToStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFocus(FocusEvent e)
|
protected override void OnFocus(FocusEvent e)
|
||||||
@ -125,7 +109,7 @@ namespace osu.Game.Overlays
|
|||||||
previewTrackManager.StopAnyPlaying(this);
|
previewTrackManager.StopAnyPlaying(this);
|
||||||
|
|
||||||
if (panelTarget.Any())
|
if (panelTarget.Any())
|
||||||
loadingLayer.Show();
|
Loading.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task panelLoadDelegate;
|
private Task panelLoadDelegate;
|
||||||
@ -173,7 +157,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private void addContentToPlaceholder(Drawable content)
|
private void addContentToPlaceholder(Drawable content)
|
||||||
{
|
{
|
||||||
loadingLayer.Hide();
|
Loading.Hide();
|
||||||
lastFetchDisplayedTime = Time.Current;
|
lastFetchDisplayedTime = Time.Current;
|
||||||
|
|
||||||
if (content == currentContent)
|
if (content == currentContent)
|
||||||
@ -267,7 +251,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
bool shouldShowMore = panelLoadDelegate?.IsCompleted != false
|
bool shouldShowMore = panelLoadDelegate?.IsCompleted != false
|
||||||
&& Time.Current - lastFetchDisplayedTime > time_between_fetches
|
&& Time.Current - lastFetchDisplayedTime > time_between_fetches
|
||||||
&& (resultScrollContainer.ScrollableExtent > 0 && resultScrollContainer.IsScrolledToEnd(pagination_scroll_distance));
|
&& (ScrollFlow.ScrollableExtent > 0 && ScrollFlow.IsScrolledToEnd(pagination_scroll_distance));
|
||||||
|
|
||||||
if (shouldShowMore)
|
if (shouldShowMore)
|
||||||
filterControl.FetchNextPage();
|
filterControl.FetchNextPage();
|
||||||
|
@ -6,20 +6,19 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Overlays.BeatmapSet;
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||||
using osu.Game.Overlays.Comments;
|
using osu.Game.Overlays.Comments;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class BeatmapSetOverlay : FullscreenOverlay<BeatmapSetHeader>
|
public class BeatmapSetOverlay : OnlineOverlay<BeatmapSetHeader>
|
||||||
{
|
{
|
||||||
public const float X_PADDING = 40;
|
public const float X_PADDING = 40;
|
||||||
public const float Y_PADDING = 25;
|
public const float Y_PADDING = 25;
|
||||||
@ -33,55 +32,27 @@ namespace osu.Game.Overlays
|
|||||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
private readonly Box background;
|
|
||||||
|
|
||||||
public BeatmapSetOverlay()
|
public BeatmapSetOverlay()
|
||||||
: base(OverlayColourScheme.Blue, new BeatmapSetHeader())
|
: base(OverlayColourScheme.Blue)
|
||||||
{
|
{
|
||||||
OverlayScrollContainer scroll;
|
|
||||||
Info info;
|
Info info;
|
||||||
CommentsSection comments;
|
CommentsSection comments;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
background = new Box
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 20),
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
info = new Info(),
|
||||||
},
|
new ScoresContainer
|
||||||
scroll = new OverlayScrollContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ScrollbarVisible = false,
|
|
||||||
Child = new ReverseChildIDFillFlowContainer<BeatmapSetLayoutSection>
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
Beatmap = { BindTarget = Header.HeaderContent.Picker.Beatmap }
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Spacing = new Vector2(0, 20),
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new BeatmapSetLayoutSection
|
|
||||||
{
|
|
||||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
Header,
|
|
||||||
info = new Info()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new ScoresContainer
|
|
||||||
{
|
|
||||||
Beatmap = { BindTarget = Header.HeaderContent.Picker.Beatmap }
|
|
||||||
},
|
|
||||||
comments = new CommentsSection()
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
comments = new CommentsSection()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Header.BeatmapSet.BindTo(beatmapSet);
|
Header.BeatmapSet.BindTo(beatmapSet);
|
||||||
@ -91,16 +62,13 @@ namespace osu.Game.Overlays
|
|||||||
Header.HeaderContent.Picker.Beatmap.ValueChanged += b =>
|
Header.HeaderContent.Picker.Beatmap.ValueChanged += b =>
|
||||||
{
|
{
|
||||||
info.Beatmap = b.NewValue;
|
info.Beatmap = b.NewValue;
|
||||||
|
ScrollFlow.ScrollToStart();
|
||||||
scroll.ScrollToStart();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected override BeatmapSetHeader CreateHeader() => new BeatmapSetHeader();
|
||||||
private void load()
|
|
||||||
{
|
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
||||||
background.Colour = ColourProvider.Background6;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOutComplete()
|
protected override void PopOutComplete()
|
||||||
{
|
{
|
||||||
|
@ -11,22 +11,18 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays.Changelog;
|
using osu.Game.Overlays.Changelog;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class ChangelogOverlay : FullscreenOverlay<ChangelogHeader>
|
public class ChangelogOverlay : OnlineOverlay<ChangelogHeader>
|
||||||
{
|
{
|
||||||
public readonly Bindable<APIChangelogBuild> Current = new Bindable<APIChangelogBuild>();
|
public readonly Bindable<APIChangelogBuild> Current = new Bindable<APIChangelogBuild>();
|
||||||
|
|
||||||
private Container<ChangelogContent> content;
|
|
||||||
|
|
||||||
private SampleChannel sampleBack;
|
private SampleChannel sampleBack;
|
||||||
|
|
||||||
private List<APIChangelogBuild> builds;
|
private List<APIChangelogBuild> builds;
|
||||||
@ -34,45 +30,14 @@ namespace osu.Game.Overlays
|
|||||||
protected List<APIUpdateStream> Streams;
|
protected List<APIUpdateStream> Streams;
|
||||||
|
|
||||||
public ChangelogOverlay()
|
public ChangelogOverlay()
|
||||||
: base(OverlayColourScheme.Purple, new ChangelogHeader())
|
: base(OverlayColourScheme.Purple)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Header.Build.BindTarget = Current;
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourProvider.Background4,
|
|
||||||
},
|
|
||||||
new OverlayScrollContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ScrollbarVisible = false,
|
|
||||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
Header.With(h =>
|
|
||||||
{
|
|
||||||
h.ListingSelected = ShowListing;
|
|
||||||
h.Build.BindTarget = Current;
|
|
||||||
}),
|
|
||||||
content = new Container<ChangelogContent>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
|
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
|
||||||
|
|
||||||
@ -85,6 +50,13 @@ namespace osu.Game.Overlays
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ChangelogHeader CreateHeader() => new ChangelogHeader
|
||||||
|
{
|
||||||
|
ListingSelected = ShowListing,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override Color4 BackgroundColour => ColourProvider.Background4;
|
||||||
|
|
||||||
public void ShowListing()
|
public void ShowListing()
|
||||||
{
|
{
|
||||||
Current.Value = null;
|
Current.Value = null;
|
||||||
@ -198,16 +170,16 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private void loadContent(ChangelogContent newContent)
|
private void loadContent(ChangelogContent newContent)
|
||||||
{
|
{
|
||||||
content.FadeTo(0.2f, 300, Easing.OutQuint);
|
Content.FadeTo(0.2f, 300, Easing.OutQuint);
|
||||||
|
|
||||||
loadContentCancellation?.Cancel();
|
loadContentCancellation?.Cancel();
|
||||||
|
|
||||||
LoadComponentAsync(newContent, c =>
|
LoadComponentAsync(newContent, c =>
|
||||||
{
|
{
|
||||||
content.FadeIn(300, Easing.OutQuint);
|
Content.FadeIn(300, Easing.OutQuint);
|
||||||
|
|
||||||
c.BuildSelected = ShowBuild;
|
c.BuildSelected = ShowBuild;
|
||||||
content.Child = c;
|
Child = c;
|
||||||
}, (loadContentCancellation = new CancellationTokenSource()).Token);
|
}, (loadContentCancellation = new CancellationTokenSource()).Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,155 +2,35 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
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.Graphics.UserInterface;
|
|
||||||
using osu.Game.Online.API;
|
|
||||||
using osu.Game.Overlays.Dashboard;
|
using osu.Game.Overlays.Dashboard;
|
||||||
using osu.Game.Overlays.Dashboard.Friends;
|
using osu.Game.Overlays.Dashboard.Friends;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class DashboardOverlay : FullscreenOverlay<DashboardOverlayHeader>
|
public class DashboardOverlay : TabbableOnlineOverlay<DashboardOverlayHeader, DashboardOverlayTabs>
|
||||||
{
|
{
|
||||||
private CancellationTokenSource cancellationToken;
|
|
||||||
|
|
||||||
private Container content;
|
|
||||||
private LoadingLayer loading;
|
|
||||||
private OverlayScrollContainer scrollFlow;
|
|
||||||
|
|
||||||
public DashboardOverlay()
|
public DashboardOverlay()
|
||||||
: base(OverlayColourScheme.Purple, new DashboardOverlayHeader
|
: base(OverlayColourScheme.Purple)
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Depth = -float.MaxValue
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
protected override DashboardOverlayHeader CreateHeader() => new DashboardOverlayHeader();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected override void CreateDisplayToLoad(DashboardOverlayTabs tab)
|
||||||
private void load(IAPIProvider api)
|
|
||||||
{
|
{
|
||||||
apiState.BindTo(api.State);
|
switch (tab)
|
||||||
apiState.BindValueChanged(onlineStateChanged, true);
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourProvider.Background5
|
|
||||||
},
|
|
||||||
scrollFlow = new OverlayScrollContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ScrollbarVisible = false,
|
|
||||||
Child = new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
Header,
|
|
||||||
content = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
loading = new LoadingLayer(true),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
Header.Current.BindValueChanged(onTabChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool displayUpdateRequired = true;
|
|
||||||
|
|
||||||
protected override void PopIn()
|
|
||||||
{
|
|
||||||
base.PopIn();
|
|
||||||
|
|
||||||
// We don't want to create a new display on every call, only when exiting from fully closed state.
|
|
||||||
if (displayUpdateRequired)
|
|
||||||
{
|
|
||||||
Header.Current.TriggerChange();
|
|
||||||
displayUpdateRequired = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOutComplete()
|
|
||||||
{
|
|
||||||
base.PopOutComplete();
|
|
||||||
loadDisplay(Empty());
|
|
||||||
displayUpdateRequired = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadDisplay(Drawable display)
|
|
||||||
{
|
|
||||||
scrollFlow.ScrollToStart();
|
|
||||||
|
|
||||||
LoadComponentAsync(display, loaded =>
|
|
||||||
{
|
|
||||||
if (API.IsLoggedIn)
|
|
||||||
loading.Hide();
|
|
||||||
|
|
||||||
content.Child = loaded;
|
|
||||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onTabChanged(ValueChangedEvent<DashboardOverlayTabs> tab)
|
|
||||||
{
|
|
||||||
cancellationToken?.Cancel();
|
|
||||||
loading.Show();
|
|
||||||
|
|
||||||
if (!API.IsLoggedIn)
|
|
||||||
{
|
|
||||||
loadDisplay(Empty());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (tab.NewValue)
|
|
||||||
{
|
{
|
||||||
case DashboardOverlayTabs.Friends:
|
case DashboardOverlayTabs.Friends:
|
||||||
loadDisplay(new FriendDisplay());
|
LoadDisplay(new FriendDisplay());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DashboardOverlayTabs.CurrentlyPlaying:
|
case DashboardOverlayTabs.CurrentlyPlaying:
|
||||||
loadDisplay(new CurrentlyPlayingDisplay());
|
LoadDisplay(new CurrentlyPlayingDisplay());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Display for {tab.NewValue} tab is not implemented");
|
throw new NotImplementedException($"Display for {tab} tab is not implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
|
|
||||||
{
|
|
||||||
if (State.Value == Visibility.Hidden)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Header.Current.TriggerChange();
|
|
||||||
});
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
cancellationToken?.Cancel();
|
|
||||||
base.Dispose(isDisposing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
private readonly Container dialogContainer;
|
private readonly Container dialogContainer;
|
||||||
|
|
||||||
|
protected override string PopInSampleName => "UI/dialog-pop-in";
|
||||||
|
protected override string PopOutSampleName => "UI/dialog-pop-out";
|
||||||
|
|
||||||
public PopupDialog CurrentDialog { get; private set; }
|
public PopupDialog CurrentDialog { get; private set; }
|
||||||
|
|
||||||
public DialogOverlay()
|
public DialogOverlay()
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -15,21 +17,27 @@ namespace osu.Game.Overlays
|
|||||||
public abstract class FullscreenOverlay<T> : WaveOverlayContainer, INamedOverlayComponent
|
public abstract class FullscreenOverlay<T> : WaveOverlayContainer, INamedOverlayComponent
|
||||||
where T : OverlayHeader
|
where T : OverlayHeader
|
||||||
{
|
{
|
||||||
public virtual string IconTexture => Header?.Title.IconTexture ?? string.Empty;
|
public virtual string IconTexture => Header.Title.IconTexture ?? string.Empty;
|
||||||
public virtual string Title => Header?.Title.Title ?? string.Empty;
|
public virtual string Title => Header.Title.Title ?? string.Empty;
|
||||||
public virtual string Description => Header?.Title.Description ?? string.Empty;
|
public virtual string Description => Header.Title.Description ?? string.Empty;
|
||||||
|
|
||||||
public T Header { get; }
|
public T Header { get; }
|
||||||
|
|
||||||
|
protected virtual Color4 BackgroundColour => ColourProvider.Background5;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
protected IAPIProvider API { get; private set; }
|
protected IAPIProvider API { get; private set; }
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
protected readonly OverlayColourProvider ColourProvider;
|
protected readonly OverlayColourProvider ColourProvider;
|
||||||
|
|
||||||
protected FullscreenOverlay(OverlayColourScheme colourScheme, T header)
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
protected FullscreenOverlay(OverlayColourScheme colourScheme)
|
||||||
{
|
{
|
||||||
Header = header;
|
Header = CreateHeader();
|
||||||
|
|
||||||
ColourProvider = new OverlayColourProvider(colourScheme);
|
ColourProvider = new OverlayColourProvider(colourScheme);
|
||||||
|
|
||||||
@ -47,6 +55,19 @@ namespace osu.Game.Overlays
|
|||||||
Type = EdgeEffectType.Shadow,
|
Type = EdgeEffectType.Shadow,
|
||||||
Radius = 10
|
Radius = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
base.Content.AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = BackgroundColour
|
||||||
|
},
|
||||||
|
content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -58,6 +79,9 @@ namespace osu.Game.Overlays
|
|||||||
Waves.FourthWaveColour = ColourProvider.Dark3;
|
Waves.FourthWaveColour = ColourProvider.Dark3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NotNull]
|
||||||
|
protected abstract T CreateHeader();
|
||||||
|
|
||||||
public override void Show()
|
public override void Show()
|
||||||
{
|
{
|
||||||
if (State.Value == Visibility.Visible)
|
if (State.Value == Visibility.Visible)
|
||||||
|
@ -46,8 +46,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// Change the selected mod index of this button.
|
/// Change the selected mod index of this button.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newIndex">The new index.</param>
|
/// <param name="newIndex">The new index.</param>
|
||||||
|
/// <param name="resetSettings">Whether any settings applied to the mod should be reset on selection.</param>
|
||||||
/// <returns>Whether the selection changed.</returns>
|
/// <returns>Whether the selection changed.</returns>
|
||||||
private bool changeSelectedIndex(int newIndex)
|
private bool changeSelectedIndex(int newIndex, bool resetSettings = true)
|
||||||
{
|
{
|
||||||
if (newIndex == selectedIndex) return false;
|
if (newIndex == selectedIndex) return false;
|
||||||
|
|
||||||
@ -69,6 +70,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
Mod newSelection = SelectedMod ?? Mods[0];
|
Mod newSelection = SelectedMod ?? Mods[0];
|
||||||
|
|
||||||
|
if (resetSettings)
|
||||||
|
newSelection.ResetSettingsToDefaults();
|
||||||
|
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
if (beforeSelected != Selected)
|
if (beforeSelected != Selected)
|
||||||
@ -209,11 +213,17 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Deselect();
|
Deselect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SelectAt(int index)
|
/// <summary>
|
||||||
|
/// Select the mod at the provided index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The index to select.</param>
|
||||||
|
/// <param name="resetSettings">Whether any settings applied to the mod should be reset on selection.</param>
|
||||||
|
/// <returns>Whether the selection changed.</returns>
|
||||||
|
public bool SelectAt(int index, bool resetSettings = true)
|
||||||
{
|
{
|
||||||
if (!Mods[index].HasImplementation) return false;
|
if (!Mods[index].HasImplementation) return false;
|
||||||
|
|
||||||
changeSelectedIndex(index);
|
changeSelectedIndex(index, resetSettings);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +197,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var buttonMod = button.Mods[index];
|
var buttonMod = button.Mods[index];
|
||||||
|
|
||||||
|
// as this is likely coming from an external change, ensure the settings of the mod are in sync.
|
||||||
buttonMod.CopyFrom(mod);
|
buttonMod.CopyFrom(mod);
|
||||||
button.SelectAt(index);
|
|
||||||
|
button.SelectAt(index, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool Stacked => true;
|
protected virtual bool Stacked => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether configurable <see cref="Mod"/>s can be configured by the local user.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool AllowConfiguration => true;
|
||||||
|
|
||||||
[NotNull]
|
[NotNull]
|
||||||
private Func<Mod, bool> isValidMod = m => true;
|
private Func<Mod, bool> isValidMod = m => true;
|
||||||
|
|
||||||
@ -300,6 +305,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Text = "Customisation",
|
Text = "Customisation",
|
||||||
Action = () => ModSettingsContainer.ToggleVisibility(),
|
Action = () => ModSettingsContainer.ToggleVisibility(),
|
||||||
Enabled = { Value = false },
|
Enabled = { Value = false },
|
||||||
|
Alpha = AllowConfiguration ? 1 : 0,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
},
|
},
|
||||||
@ -372,7 +378,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
availableMods.BindValueChanged(_ => updateAvailableMods(), true);
|
availableMods.BindValueChanged(_ => updateAvailableMods(), true);
|
||||||
SelectedMods.BindValueChanged(_ => updateSelectedButtons(), true);
|
|
||||||
|
// intentionally bound after the above line to avoid a potential update feedback cycle.
|
||||||
|
// i haven't actually observed this happening but as updateAvailableMods() changes the selection it is plausible.
|
||||||
|
SelectedMods.BindValueChanged(_ => updateSelectedButtons());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
@ -479,10 +488,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
foreach (var section in ModSectionsContainer.Children)
|
foreach (var section in ModSectionsContainer.Children)
|
||||||
section.UpdateSelectedButtons(selectedMods);
|
section.UpdateSelectedButtons(selectedMods);
|
||||||
|
|
||||||
updateMods();
|
updateMultiplier();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMods()
|
private void updateMultiplier()
|
||||||
{
|
{
|
||||||
var multiplier = 1.0;
|
var multiplier = 1.0;
|
||||||
|
|
||||||
@ -509,7 +518,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
OnModSelected(selectedMod);
|
OnModSelected(selectedMod);
|
||||||
|
|
||||||
if (selectedMod.RequiresConfiguration) ModSettingsContainer.Show();
|
if (selectedMod.RequiresConfiguration && AllowConfiguration)
|
||||||
|
ModSettingsContainer.Show();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2,67 +2,22 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Overlays.News;
|
using osu.Game.Overlays.News;
|
||||||
using osu.Game.Overlays.News.Displays;
|
using osu.Game.Overlays.News.Displays;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class NewsOverlay : FullscreenOverlay<NewsHeader>
|
public class NewsOverlay : OnlineOverlay<NewsHeader>
|
||||||
{
|
{
|
||||||
private readonly Bindable<string> article = new Bindable<string>(null);
|
private readonly Bindable<string> article = new Bindable<string>(null);
|
||||||
|
|
||||||
private Container content;
|
|
||||||
private LoadingLayer loading;
|
|
||||||
private OverlayScrollContainer scrollFlow;
|
|
||||||
|
|
||||||
public NewsOverlay()
|
public NewsOverlay()
|
||||||
: base(OverlayColourScheme.Purple, new NewsHeader())
|
: base(OverlayColourScheme.Purple)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourProvider.Background5,
|
|
||||||
},
|
|
||||||
scrollFlow = new OverlayScrollContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ScrollbarVisible = false,
|
|
||||||
Child = new FillFlowContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
Header.With(h =>
|
|
||||||
{
|
|
||||||
h.ShowFrontPage = ShowFrontPage;
|
|
||||||
}),
|
|
||||||
content = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
loading = new LoadingLayer(true),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -71,6 +26,11 @@ namespace osu.Game.Overlays
|
|||||||
article.BindValueChanged(onArticleChanged);
|
article.BindValueChanged(onArticleChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override NewsHeader CreateHeader() => new NewsHeader
|
||||||
|
{
|
||||||
|
ShowFrontPage = ShowFrontPage
|
||||||
|
};
|
||||||
|
|
||||||
private bool displayUpdateRequired = true;
|
private bool displayUpdateRequired = true;
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
@ -107,7 +67,7 @@ namespace osu.Game.Overlays
|
|||||||
private void onArticleChanged(ValueChangedEvent<string> e)
|
private void onArticleChanged(ValueChangedEvent<string> e)
|
||||||
{
|
{
|
||||||
cancellationToken?.Cancel();
|
cancellationToken?.Cancel();
|
||||||
loading.Show();
|
Loading.Show();
|
||||||
|
|
||||||
if (e.NewValue == null)
|
if (e.NewValue == null)
|
||||||
{
|
{
|
||||||
@ -122,11 +82,11 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
protected void LoadDisplay(Drawable display)
|
protected void LoadDisplay(Drawable display)
|
||||||
{
|
{
|
||||||
scrollFlow.ScrollToStart();
|
ScrollFlow.ScrollToStart();
|
||||||
LoadComponentAsync(display, loaded =>
|
LoadComponentAsync(display, loaded =>
|
||||||
{
|
{
|
||||||
content.Child = loaded;
|
Child = loaded;
|
||||||
loading.Hide();
|
Loading.Hide();
|
||||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
@ -40,6 +42,11 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool DisplayOnTop => true;
|
public virtual bool DisplayOnTop => true;
|
||||||
|
|
||||||
|
private SampleChannel samplePopIn;
|
||||||
|
private SampleChannel samplePopOut;
|
||||||
|
protected virtual string PopInSampleName => "UI/notification-pop-in";
|
||||||
|
protected virtual string PopOutSampleName => "UI/overlay-pop-out"; // TODO: replace with a unique sample?
|
||||||
|
|
||||||
protected NotificationLight Light;
|
protected NotificationLight Light;
|
||||||
private readonly CloseButton closeButton;
|
private readonly CloseButton closeButton;
|
||||||
protected Container IconContent;
|
protected Container IconContent;
|
||||||
@ -107,7 +114,7 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
closeButton = new CloseButton
|
closeButton = new CloseButton
|
||||||
{
|
{
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
Action = Close,
|
Action = () => Close(),
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
@ -120,6 +127,13 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
|
{
|
||||||
|
samplePopIn = audio.Samples.Get(PopInSampleName);
|
||||||
|
samplePopOut = audio.Samples.Get(PopOutSampleName);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
closeButton.FadeIn(75);
|
closeButton.FadeIn(75);
|
||||||
@ -143,6 +157,9 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
samplePopIn?.Play();
|
||||||
|
|
||||||
this.FadeInFromZero(200);
|
this.FadeInFromZero(200);
|
||||||
NotificationContent.MoveToX(DrawSize.X);
|
NotificationContent.MoveToX(DrawSize.X);
|
||||||
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
|
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
|
||||||
@ -150,12 +167,15 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
|
|
||||||
public bool WasClosed;
|
public bool WasClosed;
|
||||||
|
|
||||||
public virtual void Close()
|
public virtual void Close(bool playSound = true)
|
||||||
{
|
{
|
||||||
if (WasClosed) return;
|
if (WasClosed) return;
|
||||||
|
|
||||||
WasClosed = true;
|
WasClosed = true;
|
||||||
|
|
||||||
|
if (playSound)
|
||||||
|
samplePopOut?.Play();
|
||||||
|
|
||||||
Closed?.Invoke();
|
Closed?.Invoke();
|
||||||
this.FadeOut(100);
|
this.FadeOut(100);
|
||||||
Expire();
|
Expire();
|
||||||
|
@ -109,7 +109,12 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
|
|
||||||
private void clearAll()
|
private void clearAll()
|
||||||
{
|
{
|
||||||
notifications.Children.ForEach(c => c.Close());
|
bool first = true;
|
||||||
|
notifications.Children.ForEach(c =>
|
||||||
|
{
|
||||||
|
c.Close(first);
|
||||||
|
first = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -150,12 +150,12 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
colourCancelled = colours.Red;
|
colourCancelled = colours.Red;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
public override void Close(bool playSound = true)
|
||||||
{
|
{
|
||||||
switch (State)
|
switch (State)
|
||||||
{
|
{
|
||||||
case ProgressNotificationState.Cancelled:
|
case ProgressNotificationState.Cancelled:
|
||||||
base.Close();
|
base.Close(playSound);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ProgressNotificationState.Active:
|
case ProgressNotificationState.Active:
|
||||||
|
17
osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
Normal file
17
osu.Game/Overlays/Notifications/SimpleErrorNotification.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 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.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Notifications
|
||||||
|
{
|
||||||
|
public class SimpleErrorNotification : SimpleNotification
|
||||||
|
{
|
||||||
|
protected override string PopInSampleName => "UI/error-notification-pop-in";
|
||||||
|
|
||||||
|
public SimpleErrorNotification()
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.Solid.Bomb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -51,6 +51,9 @@ namespace osu.Game.Overlays
|
|||||||
private Container dragContainer;
|
private Container dragContainer;
|
||||||
private Container playerContainer;
|
private Container playerContainer;
|
||||||
|
|
||||||
|
protected override string PopInSampleName => "UI/now-playing-pop-in";
|
||||||
|
protected override string PopOutSampleName => "UI/now-playing-pop-out";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provide a source for the toolbar height.
|
/// Provide a source for the toolbar height.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -84,11 +87,6 @@ namespace osu.Game.Overlays
|
|||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
playlist = new PlaylistOverlay
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Y = player_height + 10,
|
|
||||||
},
|
|
||||||
playerContainer = new Container
|
playerContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
@ -171,7 +169,7 @@ namespace osu.Game.Overlays
|
|||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Position = new Vector2(-bottom_black_area_height / 2, 0),
|
Position = new Vector2(-bottom_black_area_height / 2, 0),
|
||||||
Icon = FontAwesome.Solid.Bars,
|
Icon = FontAwesome.Solid.Bars,
|
||||||
Action = () => playlist.ToggleVisibility(),
|
Action = togglePlaylist
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -191,13 +189,35 @@ namespace osu.Game.Overlays
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void togglePlaylist()
|
||||||
|
{
|
||||||
|
if (playlist == null)
|
||||||
|
{
|
||||||
|
LoadComponentAsync(playlist = new PlaylistOverlay
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Y = player_height + 10,
|
||||||
|
}, _ =>
|
||||||
|
{
|
||||||
|
dragContainer.Add(playlist);
|
||||||
|
|
||||||
|
playlist.BeatmapSets.BindTo(musicController.BeatmapSets);
|
||||||
|
playlist.State.BindValueChanged(s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint), true);
|
||||||
|
|
||||||
|
togglePlaylist();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!beatmap.Disabled)
|
||||||
|
playlist.ToggleVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
playlist.BeatmapSets.BindTo(musicController.BeatmapSets);
|
|
||||||
playlist.State.BindValueChanged(s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint), true);
|
|
||||||
|
|
||||||
beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
|
beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
|
||||||
|
|
||||||
musicController.TrackChanged += trackChanged;
|
musicController.TrackChanged += trackChanged;
|
||||||
@ -306,7 +326,7 @@ namespace osu.Game.Overlays
|
|||||||
private void beatmapDisabledChanged(bool disabled)
|
private void beatmapDisabledChanged(bool disabled)
|
||||||
{
|
{
|
||||||
if (disabled)
|
if (disabled)
|
||||||
playlist.Hide();
|
playlist?.Hide();
|
||||||
|
|
||||||
prevButton.Enabled.Value = !disabled;
|
prevButton.Enabled.Value = !disabled;
|
||||||
nextButton.Enabled.Value = !disabled;
|
nextButton.Enabled.Value = !disabled;
|
||||||
@ -411,6 +431,11 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private class HoverableProgressBar : ProgressBar
|
private class HoverableProgressBar : ProgressBar
|
||||||
{
|
{
|
||||||
|
public HoverableProgressBar()
|
||||||
|
: base(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
this.ResizeHeightTo(progress_height, 500, Easing.OutQuint);
|
this.ResizeHeightTo(progress_height, 500, Easing.OutQuint);
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Configuration.Tracking;
|
using osu.Framework.Configuration.Tracking;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -19,6 +21,13 @@ namespace osu.Game.Overlays.OSD
|
|||||||
{
|
{
|
||||||
private const int lights_bottom_margin = 40;
|
private const int lights_bottom_margin = 40;
|
||||||
|
|
||||||
|
private readonly int optionCount;
|
||||||
|
private readonly int selectedOption = -1;
|
||||||
|
|
||||||
|
private SampleChannel sampleOn;
|
||||||
|
private SampleChannel sampleOff;
|
||||||
|
private SampleChannel sampleChange;
|
||||||
|
|
||||||
public TrackedSettingToast(SettingDescription description)
|
public TrackedSettingToast(SettingDescription description)
|
||||||
: base(description.Name, description.Value, description.Shortcut)
|
: base(description.Name, description.Value, description.Shortcut)
|
||||||
{
|
{
|
||||||
@ -46,9 +55,6 @@ namespace osu.Game.Overlays.OSD
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int optionCount = 0;
|
|
||||||
int selectedOption = -1;
|
|
||||||
|
|
||||||
switch (description.RawValue)
|
switch (description.RawValue)
|
||||||
{
|
{
|
||||||
case bool val:
|
case bool val:
|
||||||
@ -69,6 +75,34 @@ namespace osu.Game.Overlays.OSD
|
|||||||
optionLights.Add(new OptionLight { Glowing = i == selectedOption });
|
optionLights.Add(new OptionLight { Glowing = i == selectedOption });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (optionCount == 1)
|
||||||
|
{
|
||||||
|
if (selectedOption == 0)
|
||||||
|
sampleOn?.Play();
|
||||||
|
else
|
||||||
|
sampleOff?.Play();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (sampleChange == null) return;
|
||||||
|
|
||||||
|
sampleChange.Frequency.Value = 1 + (double)selectedOption / (optionCount - 1) * 0.25f;
|
||||||
|
sampleChange.Play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
|
{
|
||||||
|
sampleOn = audio.Samples.Get("UI/osd-on");
|
||||||
|
sampleOff = audio.Samples.Get("UI/osd-off");
|
||||||
|
sampleChange = audio.Samples.Get("UI/osd-change");
|
||||||
|
}
|
||||||
|
|
||||||
private class OptionLight : Container
|
private class OptionLight : Container
|
||||||
{
|
{
|
||||||
private Color4 glowingColour, idleColour;
|
private Color4 glowingColour, idleColour;
|
||||||
|
48
osu.Game/Overlays/OnlineOverlay.cs
Normal file
48
osu.Game/Overlays/OnlineOverlay.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays
|
||||||
|
{
|
||||||
|
public abstract class OnlineOverlay<T> : FullscreenOverlay<T>
|
||||||
|
where T : OverlayHeader
|
||||||
|
{
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
protected readonly OverlayScrollContainer ScrollFlow;
|
||||||
|
protected readonly LoadingLayer Loading;
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
protected OnlineOverlay(OverlayColourScheme colourScheme)
|
||||||
|
: base(colourScheme)
|
||||||
|
{
|
||||||
|
base.Content.AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
ScrollFlow = new OverlayScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ScrollbarVisible = false,
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,96 +4,32 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Overlays.Rankings;
|
using osu.Game.Overlays.Rankings;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using System.Threading;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Overlays.Rankings.Tables;
|
using osu.Game.Overlays.Rankings.Tables;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class RankingsOverlay : FullscreenOverlay<RankingsOverlayHeader>
|
public class RankingsOverlay : TabbableOnlineOverlay<RankingsOverlayHeader, RankingsScope>
|
||||||
{
|
{
|
||||||
protected Bindable<Country> Country => Header.Country;
|
protected Bindable<Country> Country => Header.Country;
|
||||||
|
|
||||||
protected Bindable<RankingsScope> Scope => Header.Current;
|
|
||||||
|
|
||||||
private readonly OverlayScrollContainer scrollFlow;
|
|
||||||
private readonly Container contentContainer;
|
|
||||||
private readonly LoadingLayer loading;
|
|
||||||
private readonly Box background;
|
|
||||||
|
|
||||||
private APIRequest lastRequest;
|
private APIRequest lastRequest;
|
||||||
private CancellationTokenSource cancellationToken;
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; }
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
public RankingsOverlay()
|
|
||||||
: base(OverlayColourScheme.Green, new RankingsOverlayHeader
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Depth = -float.MaxValue
|
|
||||||
})
|
|
||||||
{
|
|
||||||
loading = new LoadingLayer(true);
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
background = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both
|
|
||||||
},
|
|
||||||
scrollFlow = new OverlayScrollContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ScrollbarVisible = false,
|
|
||||||
Child = new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
Header,
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
contentContainer = new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Margin = new MarginPadding { Bottom = 10 }
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
loading
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
background.Colour = ColourProvider.Background5;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private Bindable<RulesetInfo> ruleset { get; set; }
|
private Bindable<RulesetInfo> ruleset { get; set; }
|
||||||
|
|
||||||
|
public RankingsOverlay()
|
||||||
|
: base(OverlayColourScheme.Green)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -104,31 +40,33 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
// if a country is requested, force performance scope.
|
// if a country is requested, force performance scope.
|
||||||
if (Country.Value != null)
|
if (Country.Value != null)
|
||||||
Scope.Value = RankingsScope.Performance;
|
Header.Current.Value = RankingsScope.Performance;
|
||||||
|
|
||||||
Scheduler.AddOnce(loadNewContent);
|
Scheduler.AddOnce(triggerTabChanged);
|
||||||
});
|
|
||||||
|
|
||||||
Scope.BindValueChanged(_ =>
|
|
||||||
{
|
|
||||||
// country filtering is only valid for performance scope.
|
|
||||||
if (Scope.Value != RankingsScope.Performance)
|
|
||||||
Country.Value = null;
|
|
||||||
|
|
||||||
Scheduler.AddOnce(loadNewContent);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ruleset.BindValueChanged(_ =>
|
ruleset.BindValueChanged(_ =>
|
||||||
{
|
{
|
||||||
if (Scope.Value == RankingsScope.Spotlights)
|
if (Header.Current.Value == RankingsScope.Spotlights)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Scheduler.AddOnce(loadNewContent);
|
Scheduler.AddOnce(triggerTabChanged);
|
||||||
});
|
});
|
||||||
|
|
||||||
Scheduler.AddOnce(loadNewContent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnTabChanged(RankingsScope tab)
|
||||||
|
{
|
||||||
|
// country filtering is only valid for performance scope.
|
||||||
|
if (Header.Current.Value != RankingsScope.Performance)
|
||||||
|
Country.Value = null;
|
||||||
|
|
||||||
|
Scheduler.AddOnce(triggerTabChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerTabChanged() => base.OnTabChanged(Header.Current.Value);
|
||||||
|
|
||||||
|
protected override RankingsOverlayHeader CreateHeader() => new RankingsOverlayHeader();
|
||||||
|
|
||||||
public void ShowCountry(Country requested)
|
public void ShowCountry(Country requested)
|
||||||
{
|
{
|
||||||
if (requested == null)
|
if (requested == null)
|
||||||
@ -139,22 +77,13 @@ namespace osu.Game.Overlays
|
|||||||
Country.Value = requested;
|
Country.Value = requested;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowSpotlights()
|
protected override void CreateDisplayToLoad(RankingsScope tab)
|
||||||
{
|
{
|
||||||
Scope.Value = RankingsScope.Spotlights;
|
|
||||||
Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadNewContent()
|
|
||||||
{
|
|
||||||
loading.Show();
|
|
||||||
|
|
||||||
cancellationToken?.Cancel();
|
|
||||||
lastRequest?.Cancel();
|
lastRequest?.Cancel();
|
||||||
|
|
||||||
if (Scope.Value == RankingsScope.Spotlights)
|
if (Header.Current.Value == RankingsScope.Spotlights)
|
||||||
{
|
{
|
||||||
loadContent(new SpotlightsLayout
|
LoadDisplay(new SpotlightsLayout
|
||||||
{
|
{
|
||||||
Ruleset = { BindTarget = ruleset }
|
Ruleset = { BindTarget = ruleset }
|
||||||
});
|
});
|
||||||
@ -166,19 +95,19 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
if (request == null)
|
if (request == null)
|
||||||
{
|
{
|
||||||
loadContent(null);
|
LoadDisplay(Empty());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
request.Success += () => Schedule(() => loadContent(createTableFromResponse(request)));
|
request.Success += () => Schedule(() => LoadDisplay(createTableFromResponse(request)));
|
||||||
request.Failure += _ => Schedule(() => loadContent(null));
|
request.Failure += _ => Schedule(() => LoadDisplay(Empty()));
|
||||||
|
|
||||||
api.Queue(request);
|
api.Queue(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private APIRequest createScopedRequest()
|
private APIRequest createScopedRequest()
|
||||||
{
|
{
|
||||||
switch (Scope.Value)
|
switch (Header.Current.Value)
|
||||||
{
|
{
|
||||||
case RankingsScope.Performance:
|
case RankingsScope.Performance:
|
||||||
return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName);
|
return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName);
|
||||||
@ -216,29 +145,9 @@ namespace osu.Game.Overlays
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadContent(Drawable content)
|
|
||||||
{
|
|
||||||
scrollFlow.ScrollToStart();
|
|
||||||
|
|
||||||
if (content == null)
|
|
||||||
{
|
|
||||||
contentContainer.Clear();
|
|
||||||
loading.Hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadComponentAsync(content, loaded =>
|
|
||||||
{
|
|
||||||
loading.Hide();
|
|
||||||
contentContainer.Child = loaded;
|
|
||||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
lastRequest?.Cancel();
|
lastRequest?.Cancel();
|
||||||
cancellationToken?.Cancel();
|
|
||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private SeekLimitedSearchTextBox searchTextBox;
|
private SeekLimitedSearchTextBox searchTextBox;
|
||||||
|
|
||||||
|
protected override string PopInSampleName => "UI/settings-pop-in";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provide a source for the toolbar height.
|
/// Provide a source for the toolbar height.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
101
osu.Game/Overlays/TabbableOnlineOverlay.cs
Normal file
101
osu.Game/Overlays/TabbableOnlineOverlay.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// 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.Threading;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays
|
||||||
|
{
|
||||||
|
public abstract class TabbableOnlineOverlay<THeader, TEnum> : OnlineOverlay<THeader>
|
||||||
|
where THeader : TabControlOverlayHeader<TEnum>
|
||||||
|
{
|
||||||
|
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
||||||
|
|
||||||
|
private CancellationTokenSource cancellationToken;
|
||||||
|
private bool displayUpdateRequired = true;
|
||||||
|
|
||||||
|
protected TabbableOnlineOverlay(OverlayColourScheme colourScheme)
|
||||||
|
: base(colourScheme)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IAPIProvider api)
|
||||||
|
{
|
||||||
|
apiState.BindTo(api.State);
|
||||||
|
apiState.BindValueChanged(onlineStateChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
Header.Current.BindValueChanged(tab => OnTabChanged(tab.NewValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
base.PopIn();
|
||||||
|
|
||||||
|
// We don't want to create a new display on every call, only when exiting from fully closed state.
|
||||||
|
if (displayUpdateRequired)
|
||||||
|
{
|
||||||
|
Header.Current.TriggerChange();
|
||||||
|
displayUpdateRequired = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOutComplete()
|
||||||
|
{
|
||||||
|
base.PopOutComplete();
|
||||||
|
LoadDisplay(Empty());
|
||||||
|
displayUpdateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void LoadDisplay(Drawable display)
|
||||||
|
{
|
||||||
|
ScrollFlow.ScrollToStart();
|
||||||
|
|
||||||
|
LoadComponentAsync(display, loaded =>
|
||||||
|
{
|
||||||
|
if (API.IsLoggedIn)
|
||||||
|
Loading.Hide();
|
||||||
|
|
||||||
|
Child = loaded;
|
||||||
|
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnTabChanged(TEnum tab)
|
||||||
|
{
|
||||||
|
cancellationToken?.Cancel();
|
||||||
|
Loading.Show();
|
||||||
|
|
||||||
|
if (!API.IsLoggedIn)
|
||||||
|
{
|
||||||
|
LoadDisplay(Empty());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateDisplayToLoad(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void CreateDisplayToLoad(TEnum tab);
|
||||||
|
|
||||||
|
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
|
||||||
|
{
|
||||||
|
if (State.Value == Visibility.Hidden)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Header.Current.TriggerChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
cancellationToken?.Cancel();
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
private KeyBindingStore keyBindings { get; set; }
|
private KeyBindingStore keyBindings { get; set; }
|
||||||
|
|
||||||
protected ToolbarButton()
|
protected ToolbarButton()
|
||||||
: base(HoverSampleSet.Loud)
|
: base(HoverSampleSet.Toolbar)
|
||||||
{
|
{
|
||||||
Width = Toolbar.HEIGHT;
|
Width = Toolbar.HEIGHT;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Overlays.Profile;
|
|||||||
using osu.Game.Overlays.Profile.Sections;
|
using osu.Game.Overlays.Profile.Sections;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
@ -29,10 +30,14 @@ namespace osu.Game.Overlays
|
|||||||
public const float CONTENT_X_MARGIN = 70;
|
public const float CONTENT_X_MARGIN = 70;
|
||||||
|
|
||||||
public UserProfileOverlay()
|
public UserProfileOverlay()
|
||||||
: base(OverlayColourScheme.Pink, new ProfileHeader())
|
: base(OverlayColourScheme.Pink)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ProfileHeader CreateHeader() => new ProfileHeader();
|
||||||
|
|
||||||
|
protected override Color4 BackgroundColour => ColourProvider.Background6;
|
||||||
|
|
||||||
public void ShowUser(int userId) => ShowUser(new User { Id = userId });
|
public void ShowUser(int userId) => ShowUser(new User { Id = userId });
|
||||||
|
|
||||||
public void ShowUser(User user, bool fetchOnline = true)
|
public void ShowUser(User user, bool fetchOnline = true)
|
||||||
@ -72,12 +77,6 @@ namespace osu.Game.Overlays
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
};
|
};
|
||||||
|
|
||||||
Add(new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourProvider.Background6
|
|
||||||
});
|
|
||||||
|
|
||||||
Add(sectionsContainer = new ProfileSectionsContainer
|
Add(sectionsContainer = new ProfileSectionsContainer
|
||||||
{
|
{
|
||||||
ExpandableHeader = Header,
|
ExpandableHeader = Header,
|
||||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
protected override bool StartHidden => true;
|
protected override bool StartHidden => true;
|
||||||
|
|
||||||
|
protected override string PopInSampleName => "UI/wave-pop-in";
|
||||||
|
|
||||||
protected WaveOverlayContainer()
|
protected WaveOverlayContainer()
|
||||||
{
|
{
|
||||||
AddInternal(Waves = new WaveContainer
|
AddInternal(Waves = new WaveContainer
|
||||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies mod setting values from <paramref name="source"/> into this instance.
|
/// Copies mod setting values from <paramref name="source"/> into this instance, overwriting all existing settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The mod to copy properties from.</param>
|
/// <param name="source">The mod to copy properties from.</param>
|
||||||
public void CopyFrom(Mod source)
|
public void CopyFrom(Mod source)
|
||||||
@ -147,9 +147,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
var targetBindable = (IBindable)prop.GetValue(this);
|
var targetBindable = (IBindable)prop.GetValue(this);
|
||||||
var sourceBindable = (IBindable)prop.GetValue(source);
|
var sourceBindable = (IBindable)prop.GetValue(source);
|
||||||
|
|
||||||
// we only care about changes that have been made away from defaults.
|
CopyAdjustedSetting(targetBindable, sourceBindable);
|
||||||
if (!sourceBindable.IsDefault)
|
|
||||||
CopyAdjustedSetting(targetBindable, sourceBindable);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,5 +173,10 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(IMod other) => GetType() == other?.GetType();
|
public bool Equals(IMod other) => GetType() == other?.GetType();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reset all custom settings for this mod back to their defaults.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void ResetSettingsToDefaults() => CopyFrom((Mod)Activator.CreateInstance(GetType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
protected const int LAST_SETTING_ORDER = 2;
|
protected const int LAST_SETTING_ORDER = 2;
|
||||||
|
|
||||||
[SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER)]
|
[SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER)]
|
||||||
public BindableNumber<float> DrainRate { get; } = new BindableFloat
|
public BindableNumber<float> DrainRate { get; } = new BindableFloatWithLimitExtension
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
};
|
};
|
||||||
|
|
||||||
[SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER)]
|
[SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER)]
|
||||||
public BindableNumber<float> OverallDifficulty { get; } = new BindableFloat
|
public BindableNumber<float> OverallDifficulty { get; } = new BindableFloatWithLimitExtension
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 0,
|
MinValue = 0,
|
||||||
@ -53,6 +53,24 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
Value = 5,
|
Value = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[SettingSource("Extended Limits", "Adjust difficulty beyond sane limits.")]
|
||||||
|
public BindableBool ExtendedLimits { get; } = new BindableBool();
|
||||||
|
|
||||||
|
protected ModDifficultyAdjust()
|
||||||
|
{
|
||||||
|
ExtendedLimits.BindValueChanged(extend => ApplyLimits(extend.NewValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the difficulty adjustment limits. Occurs when the value of <see cref="ExtendedLimits"/> is changed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="extended">Whether limits should extend beyond sane ranges.</param>
|
||||||
|
protected virtual void ApplyLimits(bool extended)
|
||||||
|
{
|
||||||
|
DrainRate.MaxValue = extended ? 11 : 10;
|
||||||
|
OverallDifficulty.MaxValue = extended ? 11 : 10;
|
||||||
|
}
|
||||||
|
|
||||||
public override string SettingDescription
|
public override string SettingDescription
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -141,5 +159,73 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
ApplySetting(DrainRate, dr => difficulty.DrainRate = dr);
|
ApplySetting(DrainRate, dr => difficulty.DrainRate = dr);
|
||||||
ApplySetting(OverallDifficulty, od => difficulty.OverallDifficulty = od);
|
ApplySetting(OverallDifficulty, od => difficulty.OverallDifficulty = od);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void ResetSettingsToDefaults()
|
||||||
|
{
|
||||||
|
base.ResetSettingsToDefaults();
|
||||||
|
|
||||||
|
if (difficulty != null)
|
||||||
|
{
|
||||||
|
// base implementation potentially overwrite modified defaults that came from a beatmap selection.
|
||||||
|
TransferSettings(difficulty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="BindableDouble"/> that extends its min/max values to support any assigned value.
|
||||||
|
/// </summary>
|
||||||
|
protected class BindableDoubleWithLimitExtension : BindableDouble
|
||||||
|
{
|
||||||
|
public override double Value
|
||||||
|
{
|
||||||
|
get => base.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < MinValue)
|
||||||
|
MinValue = value;
|
||||||
|
if (value > MaxValue)
|
||||||
|
MaxValue = value;
|
||||||
|
base.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="BindableFloat"/> that extends its min/max values to support any assigned value.
|
||||||
|
/// </summary>
|
||||||
|
protected class BindableFloatWithLimitExtension : BindableFloat
|
||||||
|
{
|
||||||
|
public override float Value
|
||||||
|
{
|
||||||
|
get => base.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < MinValue)
|
||||||
|
MinValue = value;
|
||||||
|
if (value > MaxValue)
|
||||||
|
MaxValue = value;
|
||||||
|
base.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="BindableInt"/> that extends its min/max values to support any assigned value.
|
||||||
|
/// </summary>
|
||||||
|
protected class BindableIntWithLimitExtension : BindableInt
|
||||||
|
{
|
||||||
|
public override int Value
|
||||||
|
{
|
||||||
|
get => base.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < MinValue)
|
||||||
|
MinValue = value;
|
||||||
|
if (value > MaxValue)
|
||||||
|
MaxValue = value;
|
||||||
|
base.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,10 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
return new RowAttribute("difficulty", () => $"{difficulty.SpeedMultiplier:n2}x", colour);
|
return new RowAttribute("difficulty", () => $"{difficulty.SpeedMultiplier:n2}x", colour);
|
||||||
|
|
||||||
case EffectControlPoint effect:
|
case EffectControlPoint effect:
|
||||||
return new RowAttribute("effect", () => $"{(effect.KiaiMode ? "Kiai " : "")}{(effect.OmitFirstBarLine ? "NoBarLine " : "")}", colour);
|
return new RowAttribute("effect", () => string.Join(" ",
|
||||||
|
effect.KiaiMode ? "Kiai" : string.Empty,
|
||||||
|
effect.OmitFirstBarLine ? "NoBarLine" : string.Empty
|
||||||
|
).Trim(), colour);
|
||||||
|
|
||||||
case SampleControlPoint sample:
|
case SampleControlPoint sample:
|
||||||
return new RowAttribute("sample", () => $"{sample.SampleBank} {sample.SampleVolume}%", colour);
|
return new RowAttribute("sample", () => $"{sample.SampleBank} {sample.SampleVolume}%", colour);
|
||||||
|
@ -129,7 +129,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
||||||
|
|
||||||
buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P));
|
buttonsTopLevel.Add(new Button(@"play", @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P));
|
||||||
buttonsTopLevel.Add(new Button(@"edit", @"button-generic-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
buttonsTopLevel.Add(new Button(@"edit", @"button-edit-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
||||||
buttonsTopLevel.Add(new Button(@"browse", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D));
|
buttonsTopLevel.Add(new Button(@"browse", @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.D));
|
||||||
|
|
||||||
if (host.CanExit)
|
if (host.CanExit)
|
||||||
|
@ -45,6 +45,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private SampleChannel sampleClick;
|
private SampleChannel sampleClick;
|
||||||
private SampleChannel sampleBeat;
|
private SampleChannel sampleBeat;
|
||||||
|
private SampleChannel sampleDownbeat;
|
||||||
|
|
||||||
private readonly Container colourAndTriangles;
|
private readonly Container colourAndTriangles;
|
||||||
private readonly Triangles triangles;
|
private readonly Triangles triangles;
|
||||||
@ -259,6 +260,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
|
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
|
||||||
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
|
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
|
||||||
|
sampleDownbeat = audio.Samples.Get(@"Menu/osu-logo-downbeat");
|
||||||
|
|
||||||
logo.Texture = textures.Get(@"Menu/logo");
|
logo.Texture = textures.Get(@"Menu/logo");
|
||||||
ripple.Texture = textures.Get(@"Menu/logo");
|
ripple.Texture = textures.Get(@"Menu/logo");
|
||||||
@ -281,7 +283,15 @@ namespace osu.Game.Screens.Menu
|
|||||||
if (beatIndex < 0) return;
|
if (beatIndex < 0) return;
|
||||||
|
|
||||||
if (IsHovered)
|
if (IsHovered)
|
||||||
this.Delay(early_activation).Schedule(() => sampleBeat.Play());
|
{
|
||||||
|
this.Delay(early_activation).Schedule(() =>
|
||||||
|
{
|
||||||
|
if (beatIndex % (int)timingPoint.TimeSignature == 0)
|
||||||
|
sampleDownbeat.Play();
|
||||||
|
else
|
||||||
|
sampleBeat.Play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
logoBeatContainer
|
logoBeatContainer
|
||||||
.ScaleTo(1 - 0.02f * amplitudeAdjust, early_activation, Easing.Out).Then()
|
.ScaleTo(1 - 0.02f * amplitudeAdjust, early_activation, Easing.Out).Then()
|
||||||
|
@ -20,17 +20,18 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
{
|
{
|
||||||
protected override bool Stacked => false;
|
protected override bool Stacked => false;
|
||||||
|
|
||||||
|
protected override bool AllowConfiguration => false;
|
||||||
|
|
||||||
public new Func<Mod, bool> IsValidMod
|
public new Func<Mod, bool> IsValidMod
|
||||||
{
|
{
|
||||||
get => base.IsValidMod;
|
get => base.IsValidMod;
|
||||||
set => base.IsValidMod = m => m.HasImplementation && !m.RequiresConfiguration && !(m is ModAutoplay) && value(m);
|
set => base.IsValidMod = m => m.HasImplementation && !(m is ModAutoplay) && value(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FreeModSelectOverlay()
|
public FreeModSelectOverlay()
|
||||||
{
|
{
|
||||||
IsValidMod = m => true;
|
IsValidMod = m => true;
|
||||||
|
|
||||||
CustomiseButton.Alpha = 0;
|
|
||||||
MultiplierSection.Alpha = 0;
|
MultiplierSection.Alpha = 0;
|
||||||
DeselectAllButton.Alpha = 0;
|
DeselectAllButton.Alpha = 0;
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
[Cached]
|
[Cached]
|
||||||
protected OnlinePlayBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; }
|
protected OnlinePlayBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; }
|
||||||
|
|
||||||
|
protected IBindable<BeatmapAvailability> BeatmapAvailability => BeatmapAvailablilityTracker.Availability;
|
||||||
|
|
||||||
protected RoomSubScreen()
|
protected RoomSubScreen()
|
||||||
{
|
{
|
||||||
AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
|
AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker
|
||||||
|
@ -59,6 +59,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
||||||
|
|
||||||
protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust) && !mod.RequiresConfiguration;
|
protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
@ -267,6 +269,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
||||||
|
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
|
||||||
UserMods.BindValueChanged(onUserModsChanged);
|
UserMods.BindValueChanged(onUserModsChanged);
|
||||||
|
|
||||||
client.LoadRequested += onLoadRequested;
|
client.LoadRequested += onLoadRequested;
|
||||||
@ -313,12 +316,46 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ModSettingChangeTracker modSettingChangeTracker;
|
||||||
|
private ScheduledDelegate debouncedModSettingsUpdate;
|
||||||
|
|
||||||
private void onUserModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
private void onUserModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||||
{
|
{
|
||||||
|
modSettingChangeTracker?.Dispose();
|
||||||
|
|
||||||
if (client.Room == null)
|
if (client.Room == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
client.ChangeUserMods(mods.NewValue);
|
client.ChangeUserMods(mods.NewValue);
|
||||||
|
|
||||||
|
modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
|
||||||
|
modSettingChangeTracker.SettingChanged += onModSettingsChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onModSettingsChanged(Mod mod)
|
||||||
|
{
|
||||||
|
// Debounce changes to mod settings so as to not thrash the network.
|
||||||
|
debouncedModSettingsUpdate?.Cancel();
|
||||||
|
debouncedModSettingsUpdate = Scheduler.AddDelayed(() =>
|
||||||
|
{
|
||||||
|
if (client.Room == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
client.ChangeUserMods(UserMods.Value);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBeatmapAvailability(ValueChangedEvent<BeatmapAvailability> availability)
|
||||||
|
{
|
||||||
|
if (client.Room == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
client.ChangeBeatmapAvailability(availability.NewValue);
|
||||||
|
|
||||||
|
// while this flow is handled server-side, this covers the edge case of the local user being in a ready state and then deleting the current beatmap.
|
||||||
|
if (availability.NewValue != Online.Rooms.BeatmapAvailability.LocallyAvailable()
|
||||||
|
&& client.LocalUser?.State == MultiplayerUserState.Ready)
|
||||||
|
client.ChangeState(MultiplayerUserState.Idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onReadyClick()
|
private void onReadyClick()
|
||||||
@ -371,14 +408,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
if (client != null)
|
if (client != null)
|
||||||
client.LoadRequested -= onLoadRequested;
|
client.LoadRequested -= onLoadRequested;
|
||||||
|
|
||||||
|
modSettingChangeTracker?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
|
private class UserModSelectOverlay : LocalPlayerModSelectOverlay
|
||||||
{
|
{
|
||||||
public UserModSelectOverlay()
|
|
||||||
{
|
|
||||||
CustomiseButton.Alpha = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
|
|
||||||
const double fade_time = 50;
|
const double fade_time = 50;
|
||||||
|
|
||||||
userStateDisplay.Status = User.State;
|
userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability);
|
||||||
|
|
||||||
if (Room.Host?.Equals(User) == true)
|
if (Room.Host?.Equals(User) == true)
|
||||||
crown.FadeIn(fade_time);
|
crown.FadeIn(fade_time);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -8,83 +10,94 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||||
{
|
{
|
||||||
public class StateDisplay : CompositeDrawable
|
public class StateDisplay : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
private const double fade_time = 50;
|
||||||
|
|
||||||
|
private SpriteIcon icon;
|
||||||
|
private OsuSpriteText text;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
public StateDisplay()
|
public StateDisplay()
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MultiplayerUserState status;
|
|
||||||
|
|
||||||
private OsuSpriteText text;
|
|
||||||
private SpriteIcon icon;
|
|
||||||
|
|
||||||
private const double fade_time = 50;
|
|
||||||
|
|
||||||
public MultiplayerUserState Status
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == status)
|
|
||||||
return;
|
|
||||||
|
|
||||||
status = value;
|
|
||||||
|
|
||||||
if (IsLoaded)
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
|
this.colours = colours;
|
||||||
|
|
||||||
InternalChild = new FillFlowContainer
|
InternalChild = new FillFlowContainer
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
Spacing = new Vector2(5),
|
Spacing = new Vector2(5),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
text = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 12),
|
|
||||||
Colour = Color4Extensions.FromHex("#DDFFFF")
|
|
||||||
},
|
|
||||||
icon = new SpriteIcon
|
icon = new SpriteIcon
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreRight,
|
||||||
Icon = FontAwesome.Solid.CheckCircle,
|
Icon = FontAwesome.Solid.CheckCircle,
|
||||||
Size = new Vector2(12),
|
Size = new Vector2(12),
|
||||||
}
|
},
|
||||||
|
new CircularContainer
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
progressBar = new ProgressBar(false)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
BackgroundColour = Color4.Black.Opacity(0.4f),
|
||||||
|
FillColour = colours.Blue,
|
||||||
|
Alpha = 0f,
|
||||||
|
},
|
||||||
|
text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Horizontal = 5f, Vertical = 1f },
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 12),
|
||||||
|
Colour = Color4Extensions.FromHex("#DDFFFF")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
private OsuColour colours;
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Resolved]
|
public void UpdateStatus(MultiplayerUserState state, BeatmapAvailability availability)
|
||||||
private OsuColour colours { get; set; }
|
|
||||||
|
|
||||||
private void updateStatus()
|
|
||||||
{
|
{
|
||||||
switch (status)
|
// the only case where the progress bar is used does its own local fade in.
|
||||||
|
// starting by fading out is a sane default.
|
||||||
|
progressBar.FadeOut(fade_time);
|
||||||
|
this.FadeIn(fade_time);
|
||||||
|
|
||||||
|
switch (state)
|
||||||
{
|
{
|
||||||
default:
|
case MultiplayerUserState.Idle:
|
||||||
this.FadeOut(fade_time);
|
showBeatmapAvailability(availability);
|
||||||
return;
|
break;
|
||||||
|
|
||||||
case MultiplayerUserState.Ready:
|
case MultiplayerUserState.Ready:
|
||||||
text.Text = "ready";
|
text.Text = "ready";
|
||||||
@ -121,9 +134,43 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
icon.Icon = FontAwesome.Solid.ArrowAltCircleUp;
|
icon.Icon = FontAwesome.Solid.ArrowAltCircleUp;
|
||||||
icon.Colour = colours.BlueLighter;
|
icon.Colour = colours.BlueLighter;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
this.FadeIn(fade_time);
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showBeatmapAvailability(BeatmapAvailability availability)
|
||||||
|
{
|
||||||
|
switch (availability.State)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
this.FadeOut(fade_time);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DownloadState.NotDownloaded:
|
||||||
|
text.Text = "no map";
|
||||||
|
icon.Icon = FontAwesome.Solid.MinusCircle;
|
||||||
|
icon.Colour = colours.RedLight;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DownloadState.Downloading:
|
||||||
|
Debug.Assert(availability.DownloadProgress != null);
|
||||||
|
|
||||||
|
progressBar.FadeIn(fade_time);
|
||||||
|
progressBar.CurrentTime = availability.DownloadProgress.Value;
|
||||||
|
|
||||||
|
text.Text = "downloading map";
|
||||||
|
icon.Icon = FontAwesome.Solid.ArrowAltCircleDown;
|
||||||
|
icon.Colour = colours.Blue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DownloadState.Importing:
|
||||||
|
text.Text = "importing map";
|
||||||
|
icon.Icon = FontAwesome.Solid.ArrowAltCircleDown;
|
||||||
|
icon.Colour = colours.Yellow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, OsuColour colours)
|
private void load(AudioManager audio, OsuColour colours)
|
||||||
{
|
{
|
||||||
sampleHover = audio.Samples.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}");
|
sampleHover = audio.Samples.Get("SongSelect/song-ping");
|
||||||
hoverLayer.Colour = colours.Blue.Opacity(0.1f);
|
hoverLayer.Colour = colours.Blue.Opacity(0.1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +99,11 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
sampleHover?.Play();
|
if (sampleHover != null)
|
||||||
|
{
|
||||||
|
sampleHover.Frequency.Value = 0.90 + RNG.NextDouble(0.2);
|
||||||
|
sampleHover.Play();
|
||||||
|
}
|
||||||
|
|
||||||
hoverLayer.FadeIn(100, Easing.OutQuint);
|
hoverLayer.FadeIn(100, Easing.OutQuint);
|
||||||
return base.OnHover(e);
|
return base.OnHover(e);
|
||||||
|
@ -18,7 +18,6 @@ using System.Threading;
|
|||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Overlays.Settings;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Details
|
namespace osu.Game.Screens.Select.Details
|
||||||
@ -83,32 +82,22 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
mods.BindValueChanged(modsChanged, true);
|
mods.BindValueChanged(modsChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<ISettingsItem> references = new List<ISettingsItem>();
|
private ModSettingChangeTracker modSettingChangeTracker;
|
||||||
|
private ScheduledDelegate debouncedStatisticsUpdate;
|
||||||
|
|
||||||
private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||||
{
|
{
|
||||||
// TODO: find a more permanent solution for this if/when it is needed in other components.
|
modSettingChangeTracker?.Dispose();
|
||||||
// this is generating drawables for the only purpose of storing bindable references.
|
|
||||||
foreach (var r in references)
|
|
||||||
r.Dispose();
|
|
||||||
|
|
||||||
references.Clear();
|
modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
|
||||||
|
modSettingChangeTracker.SettingChanged += m =>
|
||||||
ScheduledDelegate debounce = null;
|
|
||||||
|
|
||||||
foreach (var mod in mods.NewValue.OfType<IApplicableToDifficulty>())
|
|
||||||
{
|
{
|
||||||
foreach (var setting in mod.CreateSettingsControls().OfType<ISettingsItem>())
|
if (!(m is IApplicableToDifficulty))
|
||||||
{
|
return;
|
||||||
setting.SettingChanged += () =>
|
|
||||||
{
|
|
||||||
debounce?.Cancel();
|
|
||||||
debounce = Scheduler.AddDelayed(updateStatistics, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
references.Add(setting);
|
debouncedStatisticsUpdate?.Cancel();
|
||||||
}
|
debouncedStatisticsUpdate = Scheduler.AddDelayed(updateStatistics, 100);
|
||||||
}
|
};
|
||||||
|
|
||||||
updateStatistics();
|
updateStatistics();
|
||||||
}
|
}
|
||||||
@ -173,6 +162,7 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
modSettingChangeTracker?.Dispose();
|
||||||
starDifficultyCancellationSource?.Cancel();
|
starDifficultyCancellationSource?.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.128.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.128.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.0.1" />
|
<PackageReference Include="Sentry" Version="3.0.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.27.1" />
|
<PackageReference Include="SharpCompress" Version="0.27.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.128.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.128.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
Loading…
Reference in New Issue
Block a user