1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 09:07:25 +08:00

Merge branch 'master' into history-graph

This commit is contained in:
Dean Herbert 2020-02-13 14:16:47 +09:00 committed by GitHub
commit cc9092063d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 714 additions and 303 deletions

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
protected override void OnMouseUp(MouseUpEvent e)
{
EndPlacement();
EndPlacement(true);
base.OnMouseUp(e);
}

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -30,12 +30,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
protected override bool OnClick(ClickEvent e)
{
EndPlacement();
EndPlacement(true);
return true;
}
public override void UpdatePosition(Vector2 screenSpacePosition)
{
BeginPlacement();
HitObject.Position = ToLocalSpace(screenSpacePosition);
}
}

View File

@ -68,6 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
switch (state)
{
case PlacementState.Initial:
BeginPlacement();
HitObject.Position = ToLocalSpace(screenSpacePosition);
break;
@ -132,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private void endCurve()
{
updateSlider();
EndPlacement();
EndPlacement(true);
}
protected override void Update()

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
return false;
HitObject.EndTime = EditorClock.CurrentTime;
EndPlacement();
EndPlacement(true);
}
else
{

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -5,9 +5,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Screens.Select.Details;
@ -22,6 +24,9 @@ namespace osu.Game.Tests.Visual.Online
private RatingsExposingDetails details;
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
[SetUp]
public void Setup() => Schedule(() =>
{
@ -55,8 +60,12 @@ namespace osu.Game.Tests.Visual.Online
{
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
}
},
}
},
OnlineInfo = new BeatmapSetOnlineInfo
{
Status = BeatmapSetOnlineStatus.Ranked
}
};
}

View File

@ -5,11 +5,13 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Screens.Select.Details;
using osuTK;
@ -26,6 +28,9 @@ namespace osu.Game.Tests.Visual.Online
private GraphExposingSuccessRate successRate;
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
[SetUp]
public void Setup() => Schedule(() =>
{

View File

@ -7,11 +7,16 @@ using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Game.Screens.Select.Leaderboards;
using osu.Framework.Allocation;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneLeaderboardScopeSelector : OsuTestScene
{
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(LeaderboardScopeSelector),

View File

@ -34,25 +34,7 @@ namespace osu.Game.Tests.Visual.Online
{
Current = { BindTarget = scope },
Country = { BindTarget = countryBindable },
Ruleset = { BindTarget = ruleset },
Spotlights = new[]
{
new Spotlight
{
Id = 1,
Text = "Spotlight 1"
},
new Spotlight
{
Id = 2,
Text = "Spotlight 2"
},
new Spotlight
{
Id = 3,
Text = "Spotlight 3"
}
}
Ruleset = { BindTarget = ruleset }
});
var country = new Country

View File

@ -35,6 +35,12 @@ namespace osu.Game.Tests.Visual.Online
Add(selector = new SpotlightSelector());
}
[Test]
public void TestVisibility()
{
AddStep("Toggle Visibility", selector.ToggleVisibility);
}
[Test]
public void TestLocalSpotlights()
{

View File

@ -0,0 +1,55 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.Rankings;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneSpotlightsLayout : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(SpotlightsLayout),
typeof(SpotlightSelector),
};
protected override bool UseOnlineAPI => true;
[Cached]
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green);
public TestSceneSpotlightsLayout()
{
var ruleset = new Bindable<RulesetInfo>(new OsuRuleset().RulesetInfo);
Add(new BasicScrollContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.8f,
Child = new SpotlightsLayout
{
Ruleset = { BindTarget = ruleset }
}
});
AddStep("Osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo);
AddStep("Mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo);
AddStep("Taiko ruleset", () => ruleset.Value = new TaikoRuleset().RulesetInfo);
AddStep("Catch ruleset", () => ruleset.Value = new CatchRuleset().RulesetInfo);
}
}
}

View File

@ -17,6 +17,7 @@ using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
@ -426,6 +427,44 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("start not requested", () => !startRequested);
}
[TestCase(false)]
[TestCase(true)]
public void TestExternalBeatmapChangeWhileFiltered(bool differentRuleset)
{
createSongSelect();
addManyTestMaps();
changeRuleset(0);
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = "nonono");
AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap);
AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmap == null);
BeatmapInfo target = null;
AddStep("select beatmap externally", () =>
{
target = manager.GetAllUsableBeatmapSets().Where(b => b.Beatmaps.Any(bi => bi.RulesetID == (differentRuleset ? 1 : 0)))
.ElementAt(5).Beatmaps.First();
Beatmap.Value = manager.GetWorkingBeatmap(target);
});
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID);
AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
AddStep("reset filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = string.Empty);
AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmap.OnlineBeatmapID == target.OnlineBeatmapID);
}
[Test]
public void TestAutoplayViaCtrlEnter()
{
@ -468,6 +507,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
private static int importId;
private int getImportId() => ++importId;
private void checkMusicPlaying(bool playing) =>
@ -551,6 +591,8 @@ namespace osu.Game.Tests.Visual.SongSelect
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
public new FilterControl FilterControl => base.FilterControl;
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel;

View File

@ -38,8 +38,8 @@ namespace osu.Game.Tests.Visual.UserInterface
public void TestButtonShowsOnCustomisableMod()
{
createModSelect();
openModSelect();
AddStep("open", () => modSelect.Show());
AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value);
AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded);
AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod));
@ -58,19 +58,21 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("mods still active", () => SelectedMods.Value.Count == 1);
AddStep("open", () => modSelect.Show());
openModSelect();
AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value);
}
[Test]
public void TestCustomisationOpensOnModSelect()
public void TestCustomisationMenuVisibility()
{
createModSelect();
openModSelect();
AddStep("open", () => modSelect.Show());
AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0);
AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.Alpha == 1);
AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0);
}
private void createModSelect()
@ -86,6 +88,12 @@ namespace osu.Game.Tests.Visual.UserInterface
});
}
private void openModSelect()
{
AddStep("open", () => modSelect.Show());
AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded);
}
private class TestModSelectOverlay : ModSelectOverlay
{
public new Container ModSettingsContainer => base.ModSettingsContainer;

View File

@ -3,7 +3,7 @@
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
</ItemGroup>

View File

@ -13,6 +13,7 @@ namespace osu.Game.Beatmaps.Drawables
public class BeatmapSetOnlineStatusPill : CircularContainer
{
private readonly OsuSpriteText statusText;
private readonly Box background;
private BeatmapSetOnlineStatus status;
@ -43,6 +44,12 @@ namespace osu.Game.Beatmaps.Drawables
set => statusText.Padding = value;
}
public Color4 BackgroundColour
{
get => background.Colour;
set => background.Colour = value;
}
public BeatmapSetOnlineStatusPill()
{
AutoSizeAxes = Axes.Both;
@ -50,7 +57,7 @@ namespace osu.Game.Beatmaps.Drawables
Children = new Drawable[]
{
new Box
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,

View File

@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps.Formats
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
}
protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal);
protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(' ') || line.StartsWith('_');
protected override void ParseLine(Beatmap beatmap, Section section, string line)
{

View File

@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps.Formats
if (ShouldSkipLine(line))
continue;
if (line.StartsWith(@"[", StringComparison.Ordinal) && line.EndsWith(@"]", StringComparison.Ordinal))
if (line.StartsWith('[') && line.EndsWith(']'))
{
if (!Enum.TryParse(line[1..^1], out section))
{

View File

@ -64,15 +64,16 @@ namespace osu.Game.Beatmaps.Formats
private void handleEvents(string line)
{
var depth = 0;
var lineSpan = line.AsSpan();
while (lineSpan.StartsWith(" ", StringComparison.Ordinal) || lineSpan.StartsWith("_", StringComparison.Ordinal))
foreach (char c in line)
{
lineSpan = lineSpan.Slice(1);
++depth;
if (c == ' ' || c == '_')
depth++;
else
break;
}
line = lineSpan.ToString();
line = line.Substring(depth);
decodeVariables(ref line);

View File

@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack(1000));
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
waveform = new RecyclableLazy<Waveform>(GetWaveform);
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
@ -48,7 +48,7 @@ namespace osu.Game.Beatmaps
total_count.Value++;
}
protected virtual Track GetVirtualTrack()
protected virtual Track GetVirtualTrack(double emptyLength = 0)
{
const double excess_length = 1000;
@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps
switch (lastObject)
{
case null:
length = excess_length;
length = emptyLength;
break;
case IHasEndTime endTime:

View File

@ -59,9 +59,9 @@ namespace osu.Game.Graphics.Containers
Track track = null;
IBeatmap beatmap = null;
double currentTrackTime;
TimingControlPoint timingPoint;
EffectControlPoint effectPoint;
double currentTrackTime = 0;
TimingControlPoint timingPoint = null;
EffectControlPoint effectPoint = null;
if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded)
{
@ -69,24 +69,18 @@ namespace osu.Game.Graphics.Containers
beatmap = Beatmap.Value.Beatmap;
}
if (track != null && beatmap != null && track.IsRunning)
if (track != null && beatmap != null && track.IsRunning && track.Length > 0)
{
currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;
currentTrackTime = track.CurrentTime + EarlyActivationMilliseconds;
timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0)
{
IsBeatSyncedWithTrack = false;
return;
}
IsBeatSyncedWithTrack = true;
}
else
IsBeatSyncedWithTrack = timingPoint?.BeatLength > 0;
if (timingPoint == null || !IsBeatSyncedWithTrack)
{
IsBeatSyncedWithTrack = false;
currentTrackTime = Clock.CurrentTime;
timingPoint = defaultTiming;
effectPoint = defaultEffect;

View File

@ -49,14 +49,7 @@ namespace osu.Game.Graphics.UserInterface
public GradientLine()
{
RelativeSizeAxes = Axes.X;
Size = new Vector2(0.8f, 1.5f);
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(mode: GridSizeMode.Relative, size: 0.4f),
new Dimension(),
};
Size = new Vector2(0.8f, 1f);
Content = new[]
{
@ -65,16 +58,12 @@ namespace osu.Game.Graphics.UserInterface
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.White)
Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Colour)
},
new Box
{
RelativeSizeAxes = Axes.Both,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Transparent)
Colour = ColourInfo.GradientHorizontal(Colour, Color4.Transparent)
},
}
};

View File

@ -2,13 +2,18 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
using osuTK;
namespace osu.Game.Graphics.UserInterface
{
@ -59,5 +64,91 @@ namespace osu.Game.Graphics.UserInterface
}
protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) };
protected override Caret CreateCaret() => new OsuCaret
{
CaretWidth = CaretWidth,
SelectionColour = SelectionColour,
};
private class OsuCaret : Caret
{
private const float caret_move_time = 60;
private readonly CaretBeatSyncedContainer beatSync;
public OsuCaret()
{
RelativeSizeAxes = Axes.Y;
Size = new Vector2(1, 0.9f);
Colour = Color4.Transparent;
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
Masking = true;
CornerRadius = 1;
InternalChild = beatSync = new CaretBeatSyncedContainer
{
RelativeSizeAxes = Axes.Both,
};
}
public override void Hide() => this.FadeOut(200);
public float CaretWidth { get; set; }
public Color4 SelectionColour { get; set; }
public override void DisplayAt(Vector2 position, float? selectionWidth)
{
beatSync.HasSelection = selectionWidth != null;
if (selectionWidth != null)
{
this.MoveTo(new Vector2(position.X, position.Y), 60, Easing.Out);
this.ResizeWidthTo(selectionWidth.Value + CaretWidth / 2, caret_move_time, Easing.Out);
this.FadeColour(SelectionColour, 200, Easing.Out);
}
else
{
this.MoveTo(new Vector2(position.X - CaretWidth / 2, position.Y), 60, Easing.Out);
this.ResizeWidthTo(CaretWidth, caret_move_time, Easing.Out);
this.FadeColour(Color4.White, 200, Easing.Out);
}
}
private class CaretBeatSyncedContainer : BeatSyncedContainer
{
private bool hasSelection;
public bool HasSelection
{
set
{
hasSelection = value;
if (value)
this.FadeTo(0.5f, 200, Easing.Out);
}
}
public CaretBeatSyncedContainer()
{
MinimumBeatLength = 300;
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
if (!hasSelection)
this.FadeTo(0.7f).FadeTo(0.4f, timingPoint.BeatLength, Easing.InOutSine);
}
}
}
}
}

View File

@ -26,6 +26,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"end_date")]
public DateTimeOffset EndDate;
[JsonProperty(@"participant_count")]
public int? Participants;
public override string ToString() => Name;
}
}

View File

@ -446,7 +446,7 @@ namespace osu.Game
/// </summary>
/// <param name="action">The action to perform once we are in the correct state.</param>
/// <param name="validScreens">An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. <see cref="MainMenu"/> is used if not specified.</param>
protected void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null)
public void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null)
{
performFromMainMenuTask?.Cancel();

View File

@ -150,7 +150,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
},
new OsuSpriteText
{
Text = BeatmapSet.Value.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty,
Text = getVideoSuffixText(),
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold)
},
};
@ -163,5 +163,13 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
private void userChanged(ValueChangedEvent<User> e) => button.Enabled.Value = !(e.NewValue is GuestUser);
private void enabledChanged(ValueChangedEvent<bool> e) => this.FadeColour(e.NewValue ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
private string getVideoSuffixText()
{
if (!BeatmapSet.Value.OnlineInfo.HasVideo)
return string.Empty;
return noVideo ? "without Video" : "with Video";
}
}
}

View File

@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -14,7 +13,6 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Direct;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Buttons
{
@ -22,7 +20,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
{
private const float transition_duration = 500;
private readonly Box bg, progress;
private readonly Box background, progress;
private readonly PlayButton playButton;
private PreviewTrack preview => playButton.Preview;
@ -40,10 +38,10 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
Children = new Drawable[]
{
bg = new Box
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.25f),
Alpha = 0.5f
},
new Container
{
@ -71,9 +69,10 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OsuColour colours, OverlayColourProvider colourProvider)
{
progress.Colour = colours.Yellow;
background.Colour = colourProvider.Background6;
}
protected override void Update()
@ -91,13 +90,13 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
protected override bool OnHover(HoverEvent e)
{
bg.FadeColour(Color4.Black.Opacity(0.5f), 100);
background.FadeTo(0.75f, 80);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
bg.FadeColour(Color4.Black.Opacity(0.25f), 100);
background.FadeTo(0.5f, 80);
base.OnHoverLost(e);
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -10,7 +9,6 @@ using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Screens.Select.Details;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
@ -21,6 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly PreviewButton preview;
private readonly BasicStats basic;
private readonly AdvancedStats advanced;
private readonly DetailBox ratingBox;
private BeatmapSetInfo beatmapSet;
@ -54,6 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void updateDisplay()
{
Ratings.Metrics = BeatmapSet?.Metrics;
ratingBox.Alpha = BeatmapSet?.OnlineInfo?.Status > 0 ? 1 : 0;
}
public Details()
@ -86,7 +86,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Vertical = 7.5f },
},
},
new DetailBox
ratingBox = new DetailBox
{
Child = Ratings = new UserRatings
{
@ -107,6 +107,8 @@ namespace osu.Game.Overlays.BeatmapSet
private class DetailBox : Container
{
private readonly Container content;
private readonly Box background;
protected override Container<Drawable> Content => content;
public DetailBox()
@ -116,10 +118,10 @@ namespace osu.Game.Overlays.BeatmapSet
InternalChildren = new Drawable[]
{
new Box
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
Alpha = 0.5f
},
content = new Container
{
@ -129,6 +131,12 @@ namespace osu.Game.Overlays.BeatmapSet
},
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
background.Colour = colourProvider.Background6;
}
}
}
}

View File

@ -1,4 +1,4 @@
// 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.
using System.Linq;
@ -30,6 +30,7 @@ namespace osu.Game.Overlays.BeatmapSet
private const float buttons_spacing = 5;
private readonly UpdateableBeatmapSetCover cover;
private readonly Box coverGradient;
private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author;
private readonly FillFlowContainer downloadButtonsContainer;
@ -93,10 +94,9 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.Both,
Masking = true,
},
new Box
coverGradient = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)),
RelativeSizeAxes = Axes.Both
},
},
},
@ -106,8 +106,7 @@ namespace osu.Game.Overlays.BeatmapSet
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Top = 20,
Bottom = 30,
Vertical = BeatmapSetOverlay.Y_PADDING,
Left = BeatmapSetOverlay.X_PADDING,
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
},
@ -130,11 +129,12 @@ namespace osu.Game.Overlays.BeatmapSet
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 15 },
Children = new Drawable[]
{
title = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true)
},
externalLink = new ExternalLinkButton
{
@ -144,7 +144,7 @@ namespace osu.Game.Overlays.BeatmapSet
},
}
},
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true) },
new Container
{
RelativeSizeAxes = Axes.X,
@ -187,7 +187,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = BeatmapSetOverlay.TOP_PADDING, Right = BeatmapSetOverlay.X_PADDING },
Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING },
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
@ -197,7 +197,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
TextPadding = new MarginPadding { Horizontal = 25, Vertical = 8 }
TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 }
},
Details = new Details(),
},
@ -215,8 +215,11 @@ namespace osu.Game.Overlays.BeatmapSet
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f));
onlineStatusPill.BackgroundColour = colourProvider.Background6;
State.BindValueChanged(_ => updateDownloadButtons());
BeatmapSet.BindValueChanged(setInfo =>

View File

@ -38,6 +38,8 @@ namespace osu.Game.Overlays.BeatmapSet
public Info()
{
MetadataSection source, tags, genre, language;
OsuSpriteText unrankedPlaceholder;
RelativeSizeAxes = Axes.X;
Height = 220;
Masking = true;
@ -110,6 +112,14 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 20, Horizontal = 15 },
},
unrankedPlaceholder = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0,
Text = "Unranked beatmap",
Font = OsuFont.GetFont(size: 12)
},
},
},
},
@ -122,6 +132,9 @@ namespace osu.Game.Overlays.BeatmapSet
tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty;
genre.Text = b.NewValue?.OnlineInfo?.Genre?.Name ?? string.Empty;
language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? string.Empty;
var setHasLeaderboard = b.NewValue?.OnlineInfo?.Status > 0;
successRate.Alpha = setHasLeaderboard ? 1 : 0;
unrankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1;
};
}

View File

@ -26,10 +26,10 @@ namespace osu.Game.Overlays.BeatmapSet
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
AccentColour = colours.Blue;
LineColour = Color4.Gray;
AccentColour = colourProvider.Highlight1;
LineColour = colourProvider.Background1;
}
private class ScopeSelectorTabItem : PageTabItem

View File

@ -77,9 +77,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
new TableColumn("rank", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), // grade
new TableColumn("score", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 60, maxSize: 70)),
new TableColumn("player", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 150)),
new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 90))
new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 110))
};
foreach (var statistic in score.SortedStatistics)

View File

@ -30,7 +30,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
RelativeSizeAxes = Axes.X;
Height = 25;
CornerRadius = 3;
CornerRadius = 5;
Masking = true;
InternalChildren = new Drawable[]

View File

@ -164,7 +164,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 10,
CornerRadius = 5,
Child = loading = new DimmedLoadingLayer(iconScale: 0.8f)
{
Alpha = 0,

View File

@ -118,26 +118,42 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
InternalChild = new GridContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 1),
Children = new[]
ColumnDimensions = new[]
{
text = new OsuSpriteText
new Dimension(GridSizeMode.AutoSize, minSize: minWidth ?? 0)
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Absolute, 4),
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new Drawable[]
{
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold),
Text = title.ToUpper()
text = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold),
Text = title.ToUpper()
}
},
separator = new Box
new Drawable[]
{
RelativeSizeAxes = minWidth == null ? Axes.X : Axes.None,
Width = minWidth ?? 1f,
Height = 2,
Margin = new MarginPadding { Top = 2 }
separator = new Box
{
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 2
}
},
content
new[]
{
content
}
}
};
}

View File

@ -96,13 +96,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold)
Font = OsuFont.GetFont(size: 10)
},
flag = new UpdateableFlag
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(19, 13),
Margin = new MarginPadding { Top = 3 }, // makes spacing look more even
ShowPlaceholderOnNull = false,
},
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet
int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0;
var rate = playCount != 0 ? (float)passCount / playCount : 0;
successPercent.Text = rate.ToString("0%");
successPercent.Text = rate.ToString("0.#%");
successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
@ -105,10 +105,10 @@ namespace osu.Game.Overlays.BeatmapSet
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OsuColour colours, OverlayColourProvider colourProvider)
{
successRate.AccentColour = colours.Green;
successRate.BackgroundColour = colours.GrayD;
successRate.BackgroundColour = colourProvider.Background6;
updateDisplay();
}

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays
public class BeatmapSetOverlay : FullscreenOverlay
{
public const float X_PADDING = 40;
public const float TOP_PADDING = 25;
public const float Y_PADDING = 25;
public const float RIGHT_WIDTH = 275;
protected readonly Header Header;

View File

@ -6,25 +6,14 @@ using osu.Framework.Bindables;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Users;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.Rankings
{
public class RankingsOverlayHeader : TabControlOverlayHeader<RankingsScope>
{
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
public readonly Bindable<Spotlight> Spotlight = new Bindable<Spotlight>();
public readonly Bindable<Country> Country = new Bindable<Country>();
public IEnumerable<Spotlight> Spotlights
{
get => spotlightsContainer.Spotlights;
set => spotlightsContainer.Spotlights = value;
}
protected override ScreenTitle CreateTitle() => new RankingsTitle
{
Scope = { BindTarget = Current }
@ -35,35 +24,11 @@ namespace osu.Game.Overlays.Rankings
Current = Ruleset
};
private SpotlightsContainer spotlightsContainer;
protected override Drawable CreateContent() => new FillFlowContainer
protected override Drawable CreateContent() => new CountryFilter
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new CountryFilter
{
Current = Country
},
spotlightsContainer = new SpotlightsContainer
{
Spotlight = { BindTarget = Spotlight }
}
}
Current = Country
};
protected override void LoadComplete()
{
Current.BindValueChanged(onCurrentChanged, true);
base.LoadComplete();
}
private void onCurrentChanged(ValueChangedEvent<RankingsScope> scope) =>
spotlightsContainer.FadeTo(scope.NewValue == RankingsScope.Spotlights ? 1 : 0, 200, Easing.OutQuint);
private class RankingsTitle : ScreenTitle
{
public readonly Bindable<RankingsScope> Scope = new Bindable<RankingsScope>();
@ -81,48 +46,6 @@ namespace osu.Game.Overlays.Rankings
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/rankings");
}
private class SpotlightsContainer : CompositeDrawable
{
public readonly Bindable<Spotlight> Spotlight = new Bindable<Spotlight>();
public IEnumerable<Spotlight> Spotlights
{
get => dropdown.Items;
set => dropdown.Items = value;
}
private readonly OsuDropdown<Spotlight> dropdown;
private readonly Box background;
public SpotlightsContainer()
{
Height = 100;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
dropdown = new OsuDropdown<Spotlight>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Width = 0.8f,
Current = Spotlight,
Y = 20,
}
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
background.Colour = colourProvider.Dark3;
}
}
}
public enum RankingsScope

View File

@ -1,18 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
namespace osu.Game.Overlays.Rankings
{
public class Spotlight
{
[JsonProperty("id")]
public int Id;
[JsonProperty("text")]
public string Text;
public override string ToString() => Text;
}
}

View File

@ -14,11 +14,14 @@ using osuTK;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Rankings
{
public class SpotlightSelector : CompositeDrawable, IHasCurrentValue<APISpotlight>
public class SpotlightSelector : VisibilityContainer, IHasCurrentValue<APISpotlight>
{
private const int duration = 300;
private readonly Box background;
private readonly SpotlightsDropdown dropdown;
@ -36,50 +39,60 @@ namespace osu.Game.Overlays.Rankings
set => dropdown.Items = value;
}
protected override bool StartHidden => true;
private readonly InfoColumn startDateColumn;
private readonly InfoColumn endDateColumn;
private readonly InfoColumn mapCountColumn;
private readonly InfoColumn participantsColumn;
private readonly Container content;
public SpotlightSelector()
{
RelativeSizeAxes = Axes.X;
Height = 100;
InternalChildren = new Drawable[]
Add(content = new Container
{
background = new Box
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
Children = new Drawable[]
background = new Box
{
dropdown = new SpotlightsDropdown
RelativeSizeAxes = Axes.Both,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
Children = new Drawable[]
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Current = Current,
Depth = -float.MaxValue
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new Drawable[]
dropdown = new SpotlightsDropdown
{
startDateColumn = new InfoColumn(@"Start Date"),
endDateColumn = new InfoColumn(@"End Date"),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Current = Current,
Depth = -float.MaxValue
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new Drawable[]
{
startDateColumn = new InfoColumn(@"Start Date"),
endDateColumn = new InfoColumn(@"End Date"),
mapCountColumn = new InfoColumn(@"Map Count"),
participantsColumn = new InfoColumn(@"Participants")
}
}
}
}
},
};
}
});
}
[BackgroundDependencyLoader]
@ -88,18 +101,17 @@ namespace osu.Game.Overlays.Rankings
background.Colour = colourProvider.Dark3;
}
protected override void LoadComplete()
public void ShowInfo(GetSpotlightRankingsResponse response)
{
base.LoadComplete();
Current.BindValueChanged(onCurrentChanged);
startDateColumn.Value = dateToString(response.Spotlight.StartDate);
endDateColumn.Value = dateToString(response.Spotlight.EndDate);
mapCountColumn.Value = response.BeatmapSets.Count.ToString();
participantsColumn.Value = response.Spotlight.Participants?.ToString("N0");
}
private void onCurrentChanged(ValueChangedEvent<APISpotlight> spotlight)
{
startDateColumn.Value = dateToString(spotlight.NewValue.StartDate);
endDateColumn.Value = dateToString(spotlight.NewValue.EndDate);
}
protected override void PopIn() => content.FadeIn(duration, Easing.OutQuint);
protected override void PopOut() => content.FadeOut(duration, Easing.OutQuint);
private string dateToString(DateTimeOffset date) => date.ToString("yyyy-MM-dd");

View File

@ -0,0 +1,161 @@
// 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.Bindables;
using osu.Game.Rulesets;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
using osu.Framework.Allocation;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Rankings.Tables;
using System.Linq;
using osu.Game.Overlays.Direct;
using System.Threading;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Rankings
{
public class SpotlightsLayout : CompositeDrawable
{
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private readonly Bindable<APISpotlight> selectedSpotlight = new Bindable<APISpotlight>();
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private RulesetStore rulesets { get; set; }
private CancellationTokenSource cancellationToken;
private GetSpotlightRankingsRequest getRankingsRequest;
private GetSpotlightsRequest spotlightsRequest;
private SpotlightSelector selector;
private Container content;
private DimmedLoadingLayer loading;
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = new ReverseChildIDFillFlowContainer<Drawable>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
selector = new SpotlightSelector
{
Current = selectedSpotlight,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
content = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Vertical = 10 }
},
loading = new DimmedLoadingLayer()
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
selector.Show();
selectedSpotlight.BindValueChanged(onSpotlightChanged);
Ruleset.BindValueChanged(onRulesetChanged);
getSpotlights();
}
private void getSpotlights()
{
spotlightsRequest = new GetSpotlightsRequest();
spotlightsRequest.Success += response => selector.Spotlights = response.Spotlights;
api.Queue(spotlightsRequest);
}
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
{
if (!selector.Spotlights.Any())
return;
selectedSpotlight.TriggerChange();
}
private void onSpotlightChanged(ValueChangedEvent<APISpotlight> spotlight)
{
loading.Show();
cancellationToken?.Cancel();
getRankingsRequest?.Cancel();
getRankingsRequest = new GetSpotlightRankingsRequest(Ruleset.Value, spotlight.NewValue.Id);
getRankingsRequest.Success += onSuccess;
api.Queue(getRankingsRequest);
}
private void onSuccess(GetSpotlightRankingsResponse response)
{
LoadComponentAsync(createContent(response), loaded =>
{
selector.ShowInfo(response);
content.Clear();
content.Add(loaded);
loading.Hide();
}, (cancellationToken = new CancellationTokenSource()).Token);
}
private Drawable createContent(GetSpotlightRankingsResponse response) => new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new Drawable[]
{
new ScoresTable(1, response.Users),
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Spacing = new Vector2(10),
Children = response.BeatmapSets.Select(b => new DirectGridPanel(b.ToBeatmapSet(rulesets))
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}).ToList()
}
}
};
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
spotlightsRequest?.Cancel();
getRankingsRequest?.Cancel();
cancellationToken?.Cancel();
}
}
}

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
private readonly BasicScrollContainer scrollFlow;
private readonly Container tableContainer;
private readonly Container contentContainer;
private readonly DimmedLoadingLayer loading;
private readonly Box background;
@ -69,13 +69,13 @@ namespace osu.Game.Overlays
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
tableContainer = new Container
contentContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Vertical = 10 }
Margin = new MarginPadding { Bottom = 10 }
},
loading = new DimmedLoadingLayer(),
}
@ -112,7 +112,13 @@ namespace osu.Game.Overlays
Scheduler.AddOnce(loadNewContent);
}, true);
ruleset.BindValueChanged(_ => Scheduler.AddOnce(loadNewContent), true);
ruleset.BindValueChanged(_ =>
{
if (Scope.Value == RankingsScope.Spotlights)
return;
Scheduler.AddOnce(loadNewContent);
}, true);
base.LoadComplete();
}
@ -134,17 +140,26 @@ namespace osu.Game.Overlays
cancellationToken?.Cancel();
lastRequest?.Cancel();
if (Scope.Value == RankingsScope.Spotlights)
{
loadContent(new SpotlightsLayout
{
Ruleset = { BindTarget = ruleset }
});
return;
}
var request = createScopedRequest();
lastRequest = request;
if (request == null)
{
loadTable(null);
loadContent(null);
return;
}
request.Success += () => loadTable(createTableFromResponse(request));
request.Failure += _ => loadTable(null);
request.Success += () => loadContent(createTableFromResponse(request));
request.Failure += _ => loadContent(null);
api.Queue(request);
}
@ -189,21 +204,21 @@ namespace osu.Game.Overlays
return null;
}
private void loadTable(Drawable table)
private void loadContent(Drawable content)
{
scrollFlow.ScrollToStart();
if (table == null)
if (content == null)
{
tableContainer.Clear();
contentContainer.Clear();
loading.Hide();
return;
}
LoadComponentAsync(table, t =>
LoadComponentAsync(content, loaded =>
{
loading.Hide();
tableContainer.Child = table;
contentContainer.Child = loaded;
}, (cancellationToken = new CancellationTokenSource()).Token);
}
}

View File

@ -251,15 +251,22 @@ namespace osu.Game.Rulesets.Edit
public void BeginPlacement(HitObject hitObject)
{
EditorBeatmap.PlacementObject.Value = hitObject;
if (distanceSnapGrid != null)
hitObject.StartTime = GetSnappedPosition(distanceSnapGrid.ToLocalSpace(inputManager.CurrentState.Mouse.Position), hitObject.StartTime).time;
}
public void EndPlacement(HitObject hitObject)
public void EndPlacement(HitObject hitObject, bool commit)
{
EditorBeatmap.Add(hitObject);
EditorBeatmap.PlacementObject.Value = null;
adjustableClock.Seek(hitObject.StartTime);
if (commit)
{
EditorBeatmap.Add(hitObject);
adjustableClock.Seek(hitObject.GetEndTime());
}
showGridFor(Enumerable.Empty<HitObject>());
}

View File

@ -103,11 +103,12 @@ namespace osu.Game.Rulesets.Edit
/// Signals that the placement of <see cref="HitObject"/> has finished.
/// This will destroy this <see cref="PlacementBlueprint"/>, and add the <see cref="HitObject"/> to the <see cref="Beatmap"/>.
/// </summary>
protected void EndPlacement()
/// <param name="commit">Whether the object should be committed.</param>
public void EndPlacement(bool commit)
{
if (!PlacementBegun)
BeginPlacement();
placementHandler.EndPlacement(HitObject);
placementHandler.EndPlacement(HitObject, commit);
}
/// <summary>

View File

@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected DragBox DragBox { get; private set; }
private Container<SelectionBlueprint> selectionBlueprints;
protected Container<SelectionBlueprint> SelectionBlueprints { get; private set; }
private SelectionHandler selectionHandler;
@ -62,7 +62,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
DragBox = CreateDragBox(select),
selectionHandler,
selectionBlueprints = CreateSelectionBlueprintContainer(),
SelectionBlueprints = CreateSelectionBlueprintContainer(),
DragBox.CreateProxy().With(p => p.Depth = float.MinValue)
});
@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectedHitObjects.ItemsAdded += objects =>
{
foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select();
SelectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select();
SelectionChanged?.Invoke(selectedHitObjects);
};
@ -81,7 +81,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectedHitObjects.ItemsRemoved += objects =>
{
foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
SelectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
SelectionChanged?.Invoke(selectedHitObjects);
};
@ -230,7 +230,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void removeBlueprintFor(HitObject hitObject)
{
var blueprint = selectionBlueprints.SingleOrDefault(m => m.HitObject == hitObject);
var blueprint = SelectionBlueprints.SingleOrDefault(m => m.HitObject == hitObject);
if (blueprint == null)
return;
@ -239,7 +239,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.Selected -= onBlueprintSelected;
blueprint.Deselected -= onBlueprintDeselected;
selectionBlueprints.Remove(blueprint);
SelectionBlueprints.Remove(blueprint);
}
protected virtual void AddBlueprintFor(HitObject hitObject)
@ -251,7 +251,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.Selected += onBlueprintSelected;
blueprint.Deselected += onBlueprintDeselected;
selectionBlueprints.Add(blueprint);
SelectionBlueprints.Add(blueprint);
}
#endregion
@ -278,7 +278,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (!allowDeselection && selectionHandler.SelectedBlueprints.Any(s => s.IsHovered))
return;
foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveChildren)
foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren)
{
if (blueprint.IsHovered)
{
@ -308,7 +308,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <param name="rect">The rectangle to perform a selection on in screen-space coordinates.</param>
private void select(RectangleF rect)
{
foreach (var blueprint in selectionBlueprints)
foreach (var blueprint in SelectionBlueprints)
{
if (blueprint.IsAlive && blueprint.IsPresent && rect.Contains(blueprint.SelectionPoint))
blueprint.Select();
@ -322,7 +322,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// </summary>
private void selectAll()
{
selectionBlueprints.ToList().ForEach(m => m.Select());
SelectionBlueprints.ToList().ForEach(m => m.Select());
selectionHandler.UpdateVisibility();
}
@ -334,14 +334,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void onBlueprintSelected(SelectionBlueprint blueprint)
{
selectionHandler.HandleSelected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 1);
SelectionBlueprints.ChangeChildDepth(blueprint, 1);
beatmap.SelectedHitObjects.Add(blueprint.HitObject);
}
private void onBlueprintDeselected(SelectionBlueprint blueprint)
{
selectionHandler.HandleDeselected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 0);
SelectionBlueprints.ChangeChildDepth(blueprint, 0);
beatmap.SelectedHitObjects.Remove(blueprint.HitObject);
}

View File

@ -63,6 +63,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void refreshTool()
{
placementBlueprintContainer.Clear();
currentPlacement?.EndPlacement(false);
currentPlacement = null;
var blueprint = CurrentTool?.CreatePlacementBlueprint();

View File

@ -3,6 +3,7 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
@ -21,8 +22,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
[Resolved(CanBeNull = true)]
private Timeline timeline { get; set; }
[Resolved]
private EditorBeatmap beatmap { get; set; }
private DragEvent lastDragEvent;
private Bindable<HitObject> placement;
private SelectionBlueprint placementBlueprint;
public TimelineBlueprintContainer()
{
RelativeSizeAxes = Axes.Both;
@ -43,6 +51,29 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
base.LoadComplete();
DragBox.Alpha = 0;
placement = beatmap.PlacementObject.GetBoundCopy();
placement.ValueChanged += placementChanged;
}
private void placementChanged(ValueChangedEvent<HitObject> obj)
{
if (obj.NewValue == null)
{
if (placementBlueprint != null)
{
SelectionBlueprints.Remove(placementBlueprint);
placementBlueprint = null;
}
}
else
{
placementBlueprint = CreateBlueprintFor(obj.NewValue);
placementBlueprint.Colour = Color4.MediumPurple;
SelectionBlueprints.Add(placementBlueprint);
}
}
protected override Container<SelectionBlueprint> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both };

View File

@ -17,7 +17,8 @@ namespace osu.Game.Screens.Edit.Compose
/// Notifies that a placement has finished.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> that has been placed.</param>
void EndPlacement(HitObject hitObject);
/// <param name="commit">Whether the object should be committed.</param>
void EndPlacement(HitObject hitObject, bool commit);
/// <summary>
/// Deletes a <see cref="HitObject"/>.

View File

@ -33,7 +33,15 @@ namespace osu.Game.Screens.Edit
/// </summary>
public event Action<HitObject> StartTimeChanged;
public BindableList<HitObject> SelectedHitObjects { get; } = new BindableList<HitObject>();
/// <summary>
/// All currently selected <see cref="HitObject"/>s.
/// </summary>
public readonly BindableList<HitObject> SelectedHitObjects = new BindableList<HitObject>();
/// <summary>
/// The current placement. Null if there's no active placement.
/// </summary>
public readonly Bindable<HitObject> PlacementObject = new Bindable<HitObject>();
public readonly IBeatmap PlayableBeatmap;

View File

@ -141,12 +141,15 @@ namespace osu.Game.Screens.Menu
preloadSongSelect();
}
[Resolved]
private OsuGame game { get; set; }
private void confirmAndExit()
{
if (exitConfirmed) return;
exitConfirmed = true;
this.Exit();
game.PerformFromScreen(menu => menu.Exit());
}
private void preloadSongSelect()

View File

@ -229,6 +229,17 @@ namespace osu.Game.Screens.Select
if (item != null)
{
select(item);
// if we got here and the set is filtered, it means we were bypassing filters.
// in this case, reapplying the filter is necessary to ensure the panel is in the correct place
// (since it is forcefully being included in the carousel).
if (set.Filtered.Value)
{
Debug.Assert(bypassFilters);
applyActiveCriteria(false, true);
}
return true;
}
}

View File

@ -62,7 +62,7 @@ namespace osu.Game.Screens.Select.Carousel
/// <summary>
/// All beatmaps which are not filtered and valid for display.
/// </summary>
protected IEnumerable<BeatmapInfo> ValidBeatmaps => Beatmaps.Where(b => !b.Filtered.Value).Select(b => b.Beatmap);
protected IEnumerable<BeatmapInfo> ValidBeatmaps => Beatmaps.Where(b => !b.Filtered.Value || b.State.Value == CarouselItemState.Selected).Select(b => b.Beatmap);
private int compareUsingAggregateMax(CarouselBeatmapSet other, Func<BeatmapInfo, double> func)
{

View File

@ -16,7 +16,7 @@ namespace osu.Game.Screens.Select.Carousel
/// <summary>
/// This item is not in a hidden state.
/// </summary>
public bool Visible => State.Value != CarouselItemState.Collapsed && !Filtered.Value;
public bool Visible => State.Value == CarouselItemState.Selected || (State.Value != CarouselItemState.Collapsed && !Filtered.Value);
public virtual List<DrawableCarouselItem> Drawables
{

View File

@ -376,16 +376,22 @@ namespace osu.Game.Screens.Select
private void workingBeatmapChanged(ValueChangedEvent<WorkingBeatmap> e)
{
if (e.NewValue is DummyWorkingBeatmap) return;
if (e.NewValue is DummyWorkingBeatmap || !this.IsCurrentScreen()) return;
if (this.IsCurrentScreen() && !Carousel.SelectBeatmap(e.NewValue?.BeatmapInfo, false))
if (!Carousel.SelectBeatmap(e.NewValue.BeatmapInfo, false))
{
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
if (e.NewValue?.BeatmapInfo?.Ruleset != null && !e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value))
// A selection may not have been possible with filters applied.
// There was possibly a ruleset mismatch. This is a case we can help things along by updating the game-wide ruleset to match.
if (e.NewValue.BeatmapInfo.Ruleset != null && !e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value))
{
Ruleset.Value = e.NewValue.BeatmapInfo.Ruleset;
Carousel.SelectBeatmap(e.NewValue.BeatmapInfo);
transferRulesetValue();
}
// Even if a ruleset mismatch was not the cause (ie. a text filter is applied),
// we still want to forcefully show the new beatmap, bypassing filters.
Carousel.SelectBeatmap(e.NewValue.BeatmapInfo);
}
}

View File

@ -53,9 +53,10 @@ namespace osu.Game.Tests.Visual
{
}
public void EndPlacement(HitObject hitObject)
public void EndPlacement(HitObject hitObject, bool commit)
{
AddHitObject(CreateHitObject(hitObject));
if (commit)
AddHitObject(CreateHitObject(hitObject));
Remove(currentBlueprint);
Add(currentBlueprint = CreateBlueprint());

View File

@ -24,7 +24,7 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.207.0" />
<PackageReference Include="Sentry" Version="2.0.1" />
<PackageReference Include="Sentry" Version="2.0.2" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />