1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-16 13:22:55 +08:00

Merge remote-tracking branch 'origin/master' into taiko_drumroll_base

This commit is contained in:
smoogipooo 2017-03-20 13:01:43 +09:00
commit 7b7255863c
39 changed files with 831 additions and 404 deletions

@ -1 +1 @@
Subproject commit db310bfc10cd1c9ed12c9e19cdc0edfa53117353 Subproject commit e6394035d443d4498b71e845e5763dd3faf98c7c

@ -1 +1 @@
Subproject commit 4f9ed4e703777ede98737c7e2af31efa4694c395 Subproject commit f85c594c182db2b01233e29ca52639b7baa00402

View File

@ -105,6 +105,9 @@
<ItemGroup> <ItemGroup>
<Folder Include="Properties\" /> <Folder Include="Properties\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -1,7 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Game.Graphics;
using osu.Game.Screens.Select.Options; using osu.Game.Screens.Select.Options;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
@ -16,6 +19,11 @@ namespace osu.Desktop.VisualTests.Tests
var overlay = new BeatmapOptionsOverlay(); var overlay = new BeatmapOptionsOverlay();
overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, Color4.Purple, null, Key.Number1);
overlay.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, Color4.Purple, null, Key.Number2);
overlay.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, Color4.Yellow, null, Key.Number3);
overlay.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, Color4.Pink, null, Key.Number4, float.MaxValue);
Add(overlay); Add(overlay);
AddButton(@"Toggle", overlay.ToggleVisibility); AddButton(@"Toggle", overlay.ToggleVisibility);

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Platform;
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Game; using osu.Game;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
@ -19,5 +20,11 @@ namespace osu.Desktop.VisualTests
// we depend on some dependencies to be loaded within OsuGameBase.load(). // we depend on some dependencies to be loaded within OsuGameBase.load().
Add(new TestBrowser()); Add(new TestBrowser());
} }
public override void SetHost(GameHost host)
{
base.SetHost(host);
host.Window.CursorState = CursorState.Hidden;
}
} }
} }

View File

@ -43,6 +43,8 @@ namespace osu.Desktop
var desktopWindow = host.Window as DesktopGameWindow; var desktopWindow = host.Window as DesktopGameWindow;
if (desktopWindow != null) if (desktopWindow != null)
{ {
desktopWindow.CursorState = CursorState.Hidden;
desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
desktopWindow.Title = Name; desktopWindow.Title = Name;

View File

@ -128,9 +128,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
case ArmedState.Idle: case ArmedState.Idle:
Delay(duration + TIME_PREEMPT); Delay(duration + TIME_PREEMPT);
FadeOut(TIME_FADEOUT); FadeOut(TIME_FADEOUT);
Expire(true);
break; break;
case ArmedState.Miss: case ArmedState.Miss:
FadeOut(TIME_FADEOUT / 5); FadeOut(TIME_FADEOUT / 5);
Expire();
break; break;
case ArmedState.Hit: case ArmedState.Hit:
const double flash_in = 40; const double flash_in = 40;
@ -150,6 +152,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
FadeOut(800); FadeOut(800);
ScaleTo(Scale * 1.5f, 400, EasingTypes.OutQuad); ScaleTo(Scale * 1.5f, 400, EasingTypes.OutQuad);
Expire();
break; break;
} }
} }

View File

@ -168,6 +168,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ball.FadeOut(160); ball.FadeOut(160);
FadeOut(800); FadeOut(800);
Expire();
} }
public Drawable ProxiedLayer => initialCircle.ApproachCircle; public Drawable ProxiedLayer => initialCircle.ApproachCircle;

View File

@ -146,9 +146,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
case ArmedState.Hit: case ArmedState.Hit:
ScaleTo(Scale * 1.2f, 320, EasingTypes.Out); ScaleTo(Scale * 1.2f, 320, EasingTypes.Out);
Expire();
break; break;
case ArmedState.Miss: case ArmedState.Miss:
ScaleTo(Scale * 0.8f, 320, EasingTypes.In); ScaleTo(Scale * 0.8f, 320, EasingTypes.In);
Expire();
break; break;
} }
} }

View File

@ -12,7 +12,6 @@ using osu.Game.Modes.UI;
using System.Linq; using System.Linq;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Judgements;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.UI namespace osu.Game.Modes.Osu.UI
{ {
@ -63,7 +62,7 @@ namespace osu.Game.Modes.Osu.UI
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
AddInternal(new OsuCursorContainer { Colour = Color4.LightYellow }); AddInternal(new GameplayCursor());
} }
public override void Add(DrawableHitObject<OsuHitObject, OsuJudgementInfo> h) public override void Add(DrawableHitObject<OsuHitObject, OsuJudgementInfo> h)

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps.Samples;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
@ -40,23 +39,6 @@ namespace osu.Game.Modes.Taiko.Objects
/// </summary> /// </summary>
public bool Kiai; public bool Kiai;
/// <summary>
/// The type of HitObject.
/// </summary>
public virtual TaikoHitType Type
{
get
{
SampleType st = Sample?.Type ?? SampleType.None;
return
// Centre/Rim
((st & ~(SampleType.Finish | SampleType.Normal)) == 0 ? TaikoHitType.CentreHit : TaikoHitType.RimHit)
// Finisher
| ((st & SampleType.Finish) > 0 ? TaikoHitType.Finisher : TaikoHitType.None);
}
}
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{ {
base.ApplyDefaults(timing, difficulty); base.ApplyDefaults(timing, difficulty);

View File

@ -55,5 +55,14 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
/// <returns>The star difficulty.</returns> /// <returns>The star difficulty.</returns>
public double CalculateStarDifficulty() => Ruleset.GetRuleset(BeatmapInfo.Mode).CreateDifficultyCalculator(this).Calculate(); public double CalculateStarDifficulty() => Ruleset.GetRuleset(BeatmapInfo.Mode).CreateDifficultyCalculator(this).Calculate();
/// <summary>
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null)
: base(original)
{
}
} }
} }

View File

@ -10,7 +10,7 @@ using osu.Game.Database;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
internal class BeatmapGroup : IStateful<BeatmapGroupState> public class BeatmapGroup : IStateful<BeatmapGroupState>
{ {
public BeatmapPanel SelectedPanel; public BeatmapPanel SelectedPanel;
@ -63,8 +63,6 @@ namespace osu.Game.Beatmaps.Drawables
{ {
BeatmapSet = beatmapSet; BeatmapSet = beatmapSet;
WorkingBeatmap beatmap = database.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault()); WorkingBeatmap beatmap = database.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault());
foreach (var b in BeatmapSet.Beatmaps)
b.StarDifficulty = (float)(database.GetWorkingBeatmap(b).Beatmap?.CalculateStarDifficulty() ?? -1f);
Header = new BeatmapSetHeader(beatmap) Header = new BeatmapSetHeader(beatmap)
{ {

View File

@ -18,7 +18,7 @@ using osu.Game.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
internal class BeatmapPanel : Panel public class BeatmapPanel : Panel
{ {
public BeatmapInfo Beatmap; public BeatmapInfo Beatmap;
private Sprite background; private Sprite background;
@ -59,6 +59,8 @@ namespace osu.Game.Beatmaps.Drawables
protected override void ApplyState(PanelSelectedState last = PanelSelectedState.Hidden) protected override void ApplyState(PanelSelectedState last = PanelSelectedState.Hidden)
{ {
if (!IsLoaded) return;
base.ApplyState(last); base.ApplyState(last);
if (last == PanelSelectedState.Hidden && State != last) if (last == PanelSelectedState.Hidden && State != last)
@ -138,7 +140,7 @@ namespace osu.Game.Beatmaps.Drawables
}, },
starCounter = new StarCounter starCounter = new StarCounter
{ {
Count = beatmap.StarDifficulty, Count = (float)beatmap.StarDifficulty,
Scale = new Vector2(0.8f), Scale = new Vector2(0.8f),
} }
} }

View File

@ -17,7 +17,7 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
internal class BeatmapSetHeader : Panel public class BeatmapSetHeader : Panel
{ {
public Action<BeatmapSetHeader> GainedSelection; public Action<BeatmapSetHeader> GainedSelection;
private SpriteText title, artist; private SpriteText title, artist;
@ -32,10 +32,6 @@ namespace osu.Game.Beatmaps.Drawables
Children = new Drawable[] Children = new Drawable[]
{ {
new PanelBackground(beatmap)
{
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer new FillFlowContainer
{ {
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
@ -74,13 +70,23 @@ namespace osu.Game.Beatmaps.Drawables
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config, OsuGameBase game)
{ {
this.config = config; this.config = config;
preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode); preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode);
preferUnicode.ValueChanged += preferUnicode_changed; preferUnicode.ValueChanged += preferUnicode_changed;
preferUnicode_changed(preferUnicode, null); preferUnicode_changed(preferUnicode, null);
new PanelBackground(beatmap)
{
RelativeSizeAxes = Axes.Both,
Depth = 1,
}.LoadAsync(game, b =>
{
Add(b);
b.FadeInFromZero(200);
});
} }
private void preferUnicode_changed(object sender, EventArgs e) private void preferUnicode_changed(object sender, EventArgs e)
@ -98,16 +104,18 @@ namespace osu.Game.Beatmaps.Drawables
private class PanelBackground : BufferedContainer private class PanelBackground : BufferedContainer
{ {
private readonly WorkingBeatmap working;
public PanelBackground(WorkingBeatmap working) public PanelBackground(WorkingBeatmap working)
{ {
this.working = working;
CacheDrawnFrameBuffer = true; CacheDrawnFrameBuffer = true;
Children = new[] Children = new Drawable[]
{ {
new BeatmapBackgroundSprite(working)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
},
new FillFlowContainer new FillFlowContainer
{ {
Depth = -1, Depth = -1,
@ -151,21 +159,6 @@ namespace osu.Game.Beatmaps.Drawables
}, },
}; };
} }
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
new BeatmapBackgroundSprite(working)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
}.LoadAsync(game, bg =>
{
Add(bg);
ForceRedraw();
});
}
} }
public void AddDifficultyIcons(IEnumerable<BeatmapPanel> panels) public void AddDifficultyIcons(IEnumerable<BeatmapPanel> panels)

View File

@ -12,7 +12,7 @@ using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
internal class Panel : Container, IStateful<PanelSelectedState> public class Panel : Container, IStateful<PanelSelectedState>
{ {
public const float MAX_HEIGHT = 80; public const float MAX_HEIGHT = 80;
@ -51,6 +51,8 @@ namespace osu.Game.Beatmaps.Drawables
protected virtual void ApplyState(PanelSelectedState last = PanelSelectedState.Hidden) protected virtual void ApplyState(PanelSelectedState last = PanelSelectedState.Hidden)
{ {
if (!IsLoaded) return;
switch (state) switch (state)
{ {
case PanelSelectedState.Hidden: case PanelSelectedState.Hidden:
@ -115,7 +117,7 @@ namespace osu.Game.Beatmaps.Drawables
} }
} }
internal enum PanelSelectedState public enum PanelSelectedState
{ {
Hidden, Hidden,
NotSelected, NotSelected,

View File

@ -10,6 +10,7 @@ using osu.Game.Beatmaps.Samples;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Beatmaps.Formats namespace osu.Game.Beatmaps.Formats
{ {
@ -244,6 +245,16 @@ namespace osu.Game.Beatmaps.Formats
} }
} }
protected override Beatmap ParseFile(TextReader stream)
{
return new LegacyBeatmap(base.ParseFile(stream));
}
public override Beatmap Decode(TextReader stream)
{
return new LegacyBeatmap(base.Decode(stream));
}
protected override void ParseFile(TextReader stream, Beatmap beatmap) protected override void ParseFile(TextReader stream, Beatmap beatmap)
{ {
HitObjectParser parser = null; HitObjectParser parser = null;

View File

@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps.Legacy
{
/// <summary>
/// A type of Beatmap loaded from a legacy .osu beatmap file (version &lt;=15).
/// </summary>
public class LegacyBeatmap : Beatmap
{
/// <summary>
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
internal LegacyBeatmap(Beatmap original = null)
: base(original)
{
HitObjects = original?.HitObjects;
}
}
}

View File

@ -123,16 +123,15 @@ namespace osu.Game.Database
/// <param name="paths">Multiple locations on disk</param> /// <param name="paths">Multiple locations on disk</param>
public void Import(IEnumerable<string> paths) public void Import(IEnumerable<string> paths)
{ {
Stack<BeatmapSetInfo> sets = new Stack<BeatmapSetInfo>();
foreach (string p in paths) foreach (string p in paths)
{
try try
{ {
BeatmapSetInfo set = getBeatmapSet(p); BeatmapSetInfo set = getBeatmapSet(p);
//If we have an ID then we already exist in the database. //If we have an ID then we already exist in the database.
if (set.ID == 0) if (set.ID == 0)
sets.Push(set); Import(new[] { set });
// We may or may not want to delete the file depending on where it is stored. // We may or may not want to delete the file depending on where it is stored.
// e.g. reconstructing/repairing database with beatmaps from default storage. // e.g. reconstructing/repairing database with beatmaps from default storage.
@ -152,9 +151,7 @@ namespace osu.Game.Database
e = e.InnerException ?? e; e = e.InnerException ?? e;
Logger.Error(e, @"Could not import beatmap set"); Logger.Error(e, @"Could not import beatmap set");
} }
}
// Batch commit with multiple sets to database
Import(sets);
} }
/// <summary> /// <summary>
@ -236,6 +233,8 @@ namespace osu.Game.Database
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary // TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null; beatmap.BeatmapInfo.Metadata = null;
beatmap.BeatmapInfo.StarDifficulty = beatmap.CalculateStarDifficulty();
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
} }
beatmapSet.StoryboardFile = archive.StoryboardFilename; beatmapSet.StoryboardFile = archive.StoryboardFilename;

View File

@ -75,16 +75,7 @@ namespace osu.Game.Database
// Metadata // Metadata
public string Version { get; set; } public string Version { get; set; }
private float starDifficulty = -1; public double StarDifficulty { get; set; }
public float StarDifficulty
{
get
{
return starDifficulty < 0 ? (Difficulty?.OverallDifficulty ?? 5) : starDifficulty;
}
set { starDifficulty = value; }
}
public bool Equals(BeatmapInfo other) public bool Equals(BeatmapInfo other)
{ {

View File

@ -24,7 +24,7 @@ namespace osu.Game.Database
[OneToMany(CascadeOperations = CascadeOperation.All)] [OneToMany(CascadeOperations = CascadeOperation.All)]
public List<BeatmapInfo> Beatmaps { get; set; } public List<BeatmapInfo> Beatmaps { get; set; }
public float MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty); public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty);
public bool DeletePending { get; set; } public bool DeletePending { get; set; }

View File

@ -0,0 +1,131 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Configuration;
using System;
namespace osu.Game.Graphics.Cursor
{
public class GameplayCursor : CursorContainer
{
protected override Drawable CreateCursor() => new OsuCursor();
public GameplayCursor()
{
Add(new CursorTrail { Depth = 1 });
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
ActiveCursor.Scale = new Vector2(1);
ActiveCursor.ScaleTo(1.2f, 100, EasingTypes.OutQuad);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (!state.Mouse.HasMainButtonPressed)
ActiveCursor.ScaleTo(1, 200, EasingTypes.OutQuad);
return base.OnMouseUp(state, args);
}
public class OsuCursor : Container
{
private Container cursorContainer;
private Bindable<double> cursorScale;
public OsuCursor()
{
Origin = Anchor.Centre;
Size = new Vector2(42);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
cursorScale = config.GetBindable<double>(OsuConfig.CursorSize);
Children = new Drawable[]
{
cursorContainer = new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2((float)cursorScale),
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
},
};
cursorScale.ValueChanged += scaleChanged;
}
private void scaleChanged(object sender, EventArgs e)
{
cursorContainer.Scale = new Vector2((float)cursorScale);
}
}
}
}

View File

@ -0,0 +1,134 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Configuration;
using System;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
namespace osu.Game.Graphics.Cursor
{
public class MenuCursor : CursorContainer
{
protected override Drawable CreateCursor() => new Cursor();
protected override bool OnMouseMove(InputState state)
{
if (state.Mouse.HasMainButtonPressed)
{
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown ?? state.Mouse.Delta;
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
// Always rotate in the direction of least distance
float diff = (degrees - ActiveCursor.Rotation) % 360;
if (diff < -180) diff += 360;
if (diff > 180) diff -= 360;
degrees = ActiveCursor.Rotation + diff;
ActiveCursor.RotateTo(degrees, 600, EasingTypes.OutQuint);
}
return base.OnMouseMove(state);
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
ActiveCursor.Scale = new Vector2(1);
ActiveCursor.ScaleTo(0.90f, 800, EasingTypes.OutQuint);
((Cursor)ActiveCursor).AdditiveLayer.Alpha = 0;
((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, EasingTypes.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (!state.Mouse.HasMainButtonPressed)
{
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, EasingTypes.OutQuint);
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), EasingTypes.OutElasticHalf);
ActiveCursor.ScaleTo(1, 500, EasingTypes.OutElastic);
}
return base.OnMouseUp(state, args);
}
protected override bool OnClick(InputState state)
{
((Cursor)ActiveCursor).AdditiveLayer.FadeOutFromOne(500, EasingTypes.OutQuint);
return base.OnClick(state);
}
protected override void PopIn()
{
ActiveCursor.FadeTo(1, 250, EasingTypes.OutQuint);
ActiveCursor.ScaleTo(1, 1000, EasingTypes.OutElastic);
}
protected override void PopOut()
{
ActiveCursor.FadeTo(0, 1400, EasingTypes.OutQuint);
ActiveCursor.ScaleTo(1.1f, 100, EasingTypes.Out);
ActiveCursor.Delay(100);
ActiveCursor.ScaleTo(0, 500, EasingTypes.In);
}
public class Cursor : Container
{
private Container cursorContainer;
private Bindable<double> cursorScale;
public Sprite AdditiveLayer;
public Cursor()
{
Size = new Vector2(42);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, TextureStore textures, OsuColour colour)
{
cursorScale = config.GetBindable<double>(OsuConfig.CursorSize);
Children = new Drawable[]
{
cursorContainer = new Container
{
Size = new Vector2(32),
Children = new Drawable[]
{
new Sprite
{
FillMode = FillMode.Fit,
Texture = textures.Get(@"Cursor/menu-cursor"),
},
AdditiveLayer = new Sprite
{
FillMode = FillMode.Fit,
BlendingMode = BlendingMode.Additive,
Colour = colour.Pink,
Alpha = 0,
Texture = textures.Get(@"Cursor/menu-cursor-additive"),
},
}
}
};
cursorScale.ValueChanged += scaleChanged;
}
private void scaleChanged(object sender, EventArgs e)
{
cursorContainer.Scale = new Vector2((float)cursorScale);
}
}
}
}

View File

@ -34,12 +34,11 @@ namespace osu.Game.Modes.Objects.Drawables
set set
{ {
if (state == value) return; if (state == value)
return;
state = value; state = value;
UpdateState(state); UpdateState(state);
if (IsLoaded)
Expire();
if (State == ArmedState.Hit) if (State == ArmedState.Hit)
PlaySample(); PlaySample();
@ -63,8 +62,6 @@ namespace osu.Game.Modes.Objects.Drawables
//force application of the state that was set before we loaded. //force application of the state that was set before we loaded.
UpdateState(State); UpdateState(State);
Expire(true);
} }
} }

View File

@ -220,7 +220,7 @@ namespace osu.Game
} }
}; };
Cursor.Alpha = 0; Cursor.State = Visibility.Hidden;
} }
private bool globalHotkeyPressed(InputState state, KeyDownEventArgs args) private bool globalHotkeyPressed(InputState state, KeyDownEventArgs args)
@ -264,10 +264,20 @@ namespace osu.Game
private Container overlayContent; private Container overlayContent;
private OsuScreen currentScreen;
private void screenChanged(Screen newScreen) private void screenChanged(Screen newScreen)
{ {
currentScreen = newScreen as OsuScreen;
if (currentScreen == null)
{
Exit();
return;
}
//central game mode change logic. //central game mode change logic.
if ((newScreen as OsuScreen)?.ShowOverlays != true) if (!currentScreen.ShowOverlays)
{ {
Toolbar.State = Visibility.Hidden; Toolbar.State = Visibility.Hidden;
musicController.State = Visibility.Hidden; musicController.State = Visibility.Hidden;
@ -278,13 +288,7 @@ namespace osu.Game
Toolbar.State = Visibility.Visible; Toolbar.State = Visibility.Visible;
} }
if (newScreen is MainMenu)
Cursor.FadeIn(100);
ScreenChanged?.Invoke(newScreen); ScreenChanged?.Invoke(newScreen);
if (newScreen == null)
Exit();
} }
protected override bool OnExiting() protected override bool OnExiting()
@ -308,6 +312,8 @@ namespace osu.Game
if (intro?.ChildScreen != null) if (intro?.ChildScreen != null)
intro.ChildScreen.Padding = new MarginPadding { Top = Toolbar.Position.Y + Toolbar.DrawHeight }; intro.ChildScreen.Padding = new MarginPadding { Top = Toolbar.Position.Y + Toolbar.DrawHeight };
Cursor.State = currentScreen == null || currentScreen.HasLocalCursorDisplayed ? Visibility.Hidden : Visibility.Visible;
} }
private void screenAdded(Screen newScreen) private void screenAdded(Screen newScreen)

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -38,7 +37,7 @@ namespace osu.Game
private RatioAdjust ratioContainer; private RatioAdjust ratioContainer;
protected CursorContainer Cursor; protected MenuCursor Cursor;
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>(); public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
@ -137,7 +136,7 @@ namespace osu.Game
{ {
Children = new[] Children = new[]
{ {
Cursor = new OsuCursorContainer { Depth = float.MinValue } Cursor = new MenuCursor { Depth = float.MinValue }
} }
}); });
} }

View File

@ -65,7 +65,7 @@ namespace osu.Game.Overlays
Vector2 change = state.Mouse.Position - state.Mouse.PositionMouseDown.Value; Vector2 change = state.Mouse.Position - state.Mouse.PositionMouseDown.Value;
// Diminish the drag distance as we go further to simulate "rubber band" feeling. // Diminish the drag distance as we go further to simulate "rubber band" feeling.
change *= (float)Math.Pow(change.Length, 0.7f) / change.Length; change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
dragContainer.MoveTo(change); dragContainer.MoveTo(change);
return base.OnDrag(state); return base.OnDrag(state);

View File

@ -21,6 +21,8 @@ namespace osu.Game.Screens.Menu
internal override bool ShowOverlays => false; internal override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => true;
public Disclaimer() public Disclaimer()
{ {
ValidForResume = false; ValidForResume = false;

View File

@ -28,6 +28,8 @@ namespace osu.Game.Screens.Menu
private SampleChannel seeya; private SampleChannel seeya;
private Track bgm; private Track bgm;
internal override bool HasLocalCursorDisplayed => true;
internal override bool ShowOverlays => false; internal override bool ShowOverlays => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty(); protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty();

View File

@ -27,6 +27,7 @@ namespace osu.Game.Screens.Menu
internal override bool ShowOverlays => buttons.State != MenuState.Initial; internal override bool ShowOverlays => buttons.State != MenuState.Initial;
private BackgroundScreen background; private BackgroundScreen background;
private Screen songSelect;
protected override BackgroundScreen CreateBackground() => background; protected override BackgroundScreen CreateBackground() => background;
@ -46,7 +47,7 @@ namespace osu.Game.Screens.Menu
OnChart = delegate { Push(new ChartListing()); }, OnChart = delegate { Push(new ChartListing()); },
OnDirect = delegate { Push(new OnlineListing()); }, OnDirect = delegate { Push(new OnlineListing()); },
OnEdit = delegate { Push(new Editor()); }, OnEdit = delegate { Push(new Editor()); },
OnSolo = delegate { Push(new PlaySongSelect()); }, OnSolo = delegate { Push(consumeSongSelect()); },
OnMulti = delegate { Push(new Lobby()); }, OnMulti = delegate { Push(new Lobby()); },
OnTest = delegate { Push(new TestBrowser()); }, OnTest = delegate { Push(new TestBrowser()); },
OnExit = delegate { Exit(); }, OnExit = delegate { Exit(); },
@ -62,6 +63,24 @@ namespace osu.Game.Screens.Menu
background.LoadAsync(game); background.LoadAsync(game);
buttons.OnSettings = game.ToggleOptions; buttons.OnSettings = game.ToggleOptions;
preloadSongSelect();
}
private void preloadSongSelect()
{
if (songSelect == null)
{
songSelect = new PlaySongSelect();
songSelect.LoadAsync(Game);
}
}
private Screen consumeSongSelect()
{
var s = songSelect;
songSelect = null;
return s;
} }
protected override void OnEntering(Screen last) protected override void OnEntering(Screen last)
@ -86,6 +105,9 @@ namespace osu.Game.Screens.Menu
{ {
base.OnResuming(last); base.OnResuming(last);
//we may have consumed our preloaded instance, so let's make another.
preloadSongSelect();
const float length = 300; const float length = 300;
buttons.State = MenuState.TopLevel; buttons.State = MenuState.TopLevel;

View File

@ -24,6 +24,8 @@ namespace osu.Game.Screens
protected new OsuGameBase Game => base.Game as OsuGameBase; protected new OsuGameBase Game => base.Game as OsuGameBase;
internal virtual bool HasLocalCursorDisplayed => false;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>(); private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
public WorkingBeatmap Beatmap public WorkingBeatmap Beatmap

View File

@ -78,6 +78,8 @@ namespace osu.Game.Screens.Play
// Don't let mouse down events through the overlay or people can click circles while paused. // Don't let mouse down events through the overlay or people can click circles while paused.
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
protected override bool OnMouseMove(InputState state) => true;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{ {
if (args.Key == Key.Escape) if (args.Key == Key.Escape)

View File

@ -32,6 +32,10 @@ namespace osu.Game.Screens.Play
internal override bool ShowOverlays => false; internal override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => !hasReplayLoaded && !IsPaused;
private bool hasReplayLoaded => hitRenderer.InputManager.ReplayInputHandler != null;
public BeatmapInfo BeatmapInfo; public BeatmapInfo BeatmapInfo;
public bool IsPaused { get; private set; } public bool IsPaused { get; private set; }
@ -304,7 +308,7 @@ namespace osu.Game.Screens.Play
{ {
if (pauseOverlay == null) return false; if (pauseOverlay == null) return false;
if (hitRenderer.InputManager.ReplayInputHandler != null) if (hasReplayLoaded)
return false; return false;
if (pauseOverlay.State != Visibility.Visible && !canPause) return true; if (pauseOverlay.State != Visibility.Visible && !canPause) return true;

View File

@ -15,47 +15,234 @@ using OpenTK.Input;
using System.Collections; using System.Collections;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using System.Diagnostics; using System.Diagnostics;
using osu.Game.Screens.Select.Filter; using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Threading;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
internal class CarouselContainer : ScrollContainer, IEnumerable<BeatmapGroup> internal class BeatmapCarousel : ScrollContainer, IEnumerable<BeatmapGroup>
{ {
private Container<Panel> scrollableContent; public BeatmapInfo SelectedBeatmap => selectedPanel?.Beatmap;
private List<BeatmapGroup> groups = new List<BeatmapGroup>();
private List<Panel> panels = new List<Panel>();
public BeatmapGroup SelectedGroup { get; private set; } public Action BeatmapsChanged;
public BeatmapPanel SelectedPanel { get; private set; }
public IEnumerable<BeatmapSetInfo> Beatmaps
{
get
{
return groups.Select(g => g.BeatmapSet);
}
set
{
scrollableContent.Clear(false);
panels.Clear();
groups.Clear();
List<BeatmapGroup> newGroups = null;
Task.Run(() =>
{
newGroups = value.Select(createGroup).ToList();
criteria.Filter(newGroups);
}).ContinueWith(t =>
{
Schedule(() =>
{
foreach (var g in newGroups)
addGroup(g);
computeYPositions();
BeatmapsChanged?.Invoke();
});
});
}
}
private List<float> yPositions = new List<float>(); private List<float> yPositions = new List<float>();
public CarouselContainer() /// <summary>
{ /// Required for now unfortunately.
DistanceDecayJump = 0.01; /// </summary>
private BeatmapDatabase database;
private Container<Panel> scrollableContent;
private List<BeatmapGroup> groups = new List<BeatmapGroup>();
private List<Panel> panels = new List<Panel>();
private BeatmapGroup selectedGroup;
private BeatmapPanel selectedPanel;
public BeatmapCarousel()
{
Add(scrollableContent = new Container<Panel> Add(scrollableContent = new Container<Panel>
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
}); });
} }
public void AddGroup(BeatmapGroup group) public void AddBeatmap(BeatmapSetInfo beatmapSet)
{ {
groups.Add(group); var group = createGroup(beatmapSet);
panels.Add(group.Header); //for the time being, let's completely load the difficulty panels in the background.
group.Header.UpdateClock(Clock); //this likely won't scale so well, but allows us to completely async the loading flow.
foreach (BeatmapPanel panel in group.BeatmapPanels) Schedule(delegate
{ {
panels.Add(panel); addGroup(group);
panel.UpdateClock(Clock); computeYPositions();
} if (selectedGroup == null)
selectGroup(group);
computeYPositions(); });
} }
public void RemoveGroup(BeatmapGroup group) public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true)
{
if (beatmap == null)
{
SelectNext();
return;
}
foreach (BeatmapGroup group in groups)
{
var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
if (panel != null)
{
selectGroup(group, panel, animated);
return;
}
}
}
public void RemoveBeatmap(BeatmapSetInfo info) => removeGroup(groups.Find(b => b.BeatmapSet.ID == info.ID));
public Action<BeatmapGroup, BeatmapInfo> SelectionChanged;
public Action StartRequested;
public void SelectNext(int direction = 1, bool skipDifficulties = true)
{
if (groups.Count == 0)
{
selectedGroup = null;
selectedPanel = null;
return;
}
if (!skipDifficulties && selectedGroup != null)
{
int i = selectedGroup.BeatmapPanels.IndexOf(selectedPanel) + direction;
if (i >= 0 && i < selectedGroup.BeatmapPanels.Count)
{
//changing difficulty panel, not set.
selectGroup(selectedGroup, selectedGroup.BeatmapPanels[i]);
return;
}
}
int startIndex = groups.IndexOf(selectedGroup);
int index = startIndex;
do
{
index = (index + direction + groups.Count) % groups.Count;
if (groups[index].State != BeatmapGroupState.Hidden)
{
SelectBeatmap(groups[index].BeatmapPanels.First().Beatmap);
return;
}
} while (index != startIndex);
}
public void SelectRandom()
{
List<BeatmapGroup> visibleGroups = groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden).ToList();
if (visibleGroups.Count < 1)
return;
BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)];
BeatmapPanel panel = group?.BeatmapPanels.First();
if (panel == null)
return;
selectGroup(group, panel);
}
private FilterCriteria criteria = new FilterCriteria();
private ScheduledDelegate filterTask;
public void Filter(FilterCriteria newCriteria = null, bool debounce = true)
{
if (!IsLoaded) return;
criteria = newCriteria ?? criteria ?? new FilterCriteria();
Action perform = delegate
{
filterTask = null;
criteria.Filter(groups);
var filtered = new List<BeatmapGroup>(groups);
scrollableContent.Clear(false);
panels.Clear();
groups.Clear();
foreach (var g in filtered)
addGroup(g);
computeYPositions();
if (selectedGroup == null || selectedGroup.State == BeatmapGroupState.Hidden)
SelectNext();
};
filterTask?.Cancel();
if (debounce)
filterTask = Scheduler.AddDelayed(perform, 250);
else
perform();
}
public IEnumerator<BeatmapGroup> GetEnumerator() => groups.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet)
{
database.GetChildren(beatmapSet);
beatmapSet.Beatmaps.ForEach(b => { if (b.Metadata == null) b.Metadata = beatmapSet.Metadata; });
return new BeatmapGroup(beatmapSet, database)
{
SelectionChanged = SelectionChanged,
StartRequested = b => StartRequested?.Invoke(),
State = BeatmapGroupState.Collapsed
};
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapDatabase database)
{
this.database = database;
}
private void addGroup(BeatmapGroup group)
{
groups.Add(group);
panels.Add(group.Header);
panels.AddRange(group.BeatmapPanels);
}
private void removeGroup(BeatmapGroup group)
{ {
groups.Remove(group); groups.Remove(group);
panels.Remove(group.Header); panels.Remove(group.Header);
@ -65,18 +252,12 @@ namespace osu.Game.Screens.Select
scrollableContent.Remove(group.Header); scrollableContent.Remove(group.Header);
scrollableContent.Remove(group.BeatmapPanels); scrollableContent.Remove(group.BeatmapPanels);
if (selectedGroup == group)
SelectNext();
computeYPositions(); computeYPositions();
} }
private void movePanel(Panel panel, bool advance, bool animated, ref float currentY)
{
yPositions.Add(currentY);
panel.MoveToY(currentY, animated ? 750 : 0, EasingTypes.OutExpo);
if (advance)
currentY += panel.DrawHeight + 5;
}
/// <summary> /// <summary>
/// Computes the target Y positions for every panel in the carousel. /// Computes the target Y positions for every panel in the carousel.
/// </summary> /// </summary>
@ -99,7 +280,7 @@ namespace osu.Game.Screens.Select
foreach (BeatmapPanel panel in group.BeatmapPanels) foreach (BeatmapPanel panel in group.BeatmapPanels)
{ {
if (panel == SelectedPanel) if (panel == selectedPanel)
selectedY = currentY + panel.DrawHeight / 2 - DrawHeight / 2; selectedY = currentY + panel.DrawHeight / 2 - DrawHeight / 2;
panel.MoveToX(-50, 500, EasingTypes.OutExpo); panel.MoveToX(-50, 500, EasingTypes.OutExpo);
@ -129,105 +310,62 @@ namespace osu.Game.Screens.Select
return selectedY; return selectedY;
} }
public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true) private void movePanel(Panel panel, bool advance, bool animated, ref float currentY)
{ {
foreach (BeatmapGroup group in groups) yPositions.Add(currentY);
{ panel.MoveToY(currentY, animated ? 750 : 0, EasingTypes.OutExpo);
var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
if (panel != null) if (advance)
{ currentY += panel.DrawHeight + 5;
selectGroup(group, panel, animated);
return;
}
}
} }
private void selectGroup(BeatmapGroup group, BeatmapPanel panel, bool animated = true) private void selectGroup(BeatmapGroup group, BeatmapPanel panel = null, bool animated = true)
{ {
if (panel == null)
panel = group.BeatmapPanels.First();
Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group"); Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group");
if (SelectedGroup != null && SelectedGroup != group && SelectedGroup.State != BeatmapGroupState.Hidden) if (selectedGroup != null && selectedGroup != group && selectedGroup.State != BeatmapGroupState.Hidden)
SelectedGroup.State = BeatmapGroupState.Collapsed; selectedGroup.State = BeatmapGroupState.Collapsed;
group.State = BeatmapGroupState.Expanded; group.State = BeatmapGroupState.Expanded;
SelectedGroup = group; selectedGroup = group;
panel.State = PanelSelectedState.Selected; panel.State = PanelSelectedState.Selected;
SelectedPanel = panel; selectedPanel = panel;
float selectedY = computeYPositions(animated); float selectedY = computeYPositions(animated);
ScrollTo(selectedY, animated); ScrollTo(selectedY, animated);
} }
public void Sort(SortMode mode) protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{ {
List<BeatmapGroup> sortedGroups = new List<BeatmapGroup>(groups); int direction = 0;
switch (mode) bool skipDifficulties = false;
switch (args.Key)
{ {
case SortMode.Artist: case Key.Up:
sortedGroups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist, StringComparison.InvariantCultureIgnoreCase)); direction = -1;
break; break;
case SortMode.Title: case Key.Down:
sortedGroups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title, StringComparison.InvariantCultureIgnoreCase)); direction = 1;
break; break;
case SortMode.Author: case Key.Left:
sortedGroups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author, StringComparison.InvariantCultureIgnoreCase)); direction = -1;
skipDifficulties = true;
break; break;
case SortMode.Difficulty: case Key.Right:
sortedGroups.Sort((x, y) => x.BeatmapSet.MaxStarDifficulty.CompareTo(y.BeatmapSet.MaxStarDifficulty)); direction = 1;
break; skipDifficulties = true;
default:
Sort(SortMode.Artist); // Temporary
break; break;
} }
scrollableContent.Clear(false); if (direction == 0)
panels.Clear(); return base.OnKeyDown(state, args);
groups.Clear();
foreach (BeatmapGroup group in sortedGroups) SelectNext(direction, skipDifficulties);
AddGroup(group); return true;
}
/// <summary>
/// Computes the x-offset of currently visible panels. Makes the carousel appear round.
/// </summary>
/// <param name="dist">
/// Vertical distance from the center of the carousel container
/// ranging from -1 to 1.
/// </param>
/// <param name="halfHeight">Half the height of the carousel container.</param>
private static float offsetX(float dist, float halfHeight)
{
// The radius of the circle the carousel moves on.
const float circle_radius = 3;
double discriminant = Math.Max(0, circle_radius * circle_radius - dist * dist);
float x = (circle_radius - (float)Math.Sqrt(discriminant)) * halfHeight;
return 125 + x;
}
/// <summary>
/// Update a panel's x position and multiplicative alpha based on its y position and
/// the current scroll position.
/// </summary>
/// <param name="p">The panel to be updated.</param>
/// <param name="halfHeight">Half the draw height of the carousel container.</param>
private void updatePanel(Panel p, float halfHeight)
{
var height = p.IsPresent ? p.DrawHeight : 0;
float panelDrawY = p.Position.Y - Current + height / 2;
float dist = Math.Abs(1f - panelDrawY / halfHeight);
// Setting the origin position serves as an additive position on top of potential
// local transformation we may want to apply (e.g. when a panel gets selected, we
// may want to smoothly transform it leftwards.)
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
// We are applying a multiplicative alpha (which is internally done by nesting an
// additional container and setting that container's alpha) such that we can
// layer transformations on top, with a similar reasoning to the previous comment.
p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1));
} }
protected override void Update() protected override void Update()
@ -276,80 +414,46 @@ namespace osu.Game.Screens.Select
updatePanel(p, halfHeight); updatePanel(p, halfHeight);
} }
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) /// <summary>
/// Computes the x-offset of currently visible panels. Makes the carousel appear round.
/// </summary>
/// <param name="dist">
/// Vertical distance from the center of the carousel container
/// ranging from -1 to 1.
/// </param>
/// <param name="halfHeight">Half the height of the carousel container.</param>
private static float offsetX(float dist, float halfHeight)
{ {
int direction = 0; // The radius of the circle the carousel moves on.
bool skipDifficulties = false; const float circle_radius = 3;
double discriminant = Math.Max(0, circle_radius * circle_radius - dist * dist);
float x = (circle_radius - (float)Math.Sqrt(discriminant)) * halfHeight;
switch (args.Key) return 125 + x;
{
case Key.Up:
direction = -1;
break;
case Key.Down:
direction = 1;
break;
case Key.Left:
direction = -1;
skipDifficulties = true;
break;
case Key.Right:
direction = 1;
skipDifficulties = true;
break;
}
if (direction == 0)
return base.OnKeyDown(state, args);
SelectNext(direction, skipDifficulties);
return true;
} }
public void SelectNext(int direction = 1, bool skipDifficulties = true) /// <summary>
/// Update a panel's x position and multiplicative alpha based on its y position and
/// the current scroll position.
/// </summary>
/// <param name="p">The panel to be updated.</param>
/// <param name="halfHeight">Half the draw height of the carousel container.</param>
private void updatePanel(Panel p, float halfHeight)
{ {
if (!skipDifficulties && SelectedGroup != null) var height = p.IsPresent ? p.DrawHeight : 0;
{
int i = SelectedGroup.BeatmapPanels.IndexOf(SelectedPanel) + direction;
if (i >= 0 && i < SelectedGroup.BeatmapPanels.Count) float panelDrawY = p.Position.Y - Current + height / 2;
{ float dist = Math.Abs(1f - panelDrawY / halfHeight);
//changing difficulty panel, not set.
selectGroup(SelectedGroup, SelectedGroup.BeatmapPanels[i]);
return;
}
}
int startIndex = groups.IndexOf(SelectedGroup); // Setting the origin position serves as an additive position on top of potential
int index = startIndex; // local transformation we may want to apply (e.g. when a panel gets selected, we
// may want to smoothly transform it leftwards.)
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
do // We are applying a multiplicative alpha (which is internally done by nesting an
{ // additional container and setting that container's alpha) such that we can
index = (index + direction + groups.Count) % groups.Count; // layer transformations on top, with a similar reasoning to the previous comment.
if (groups[index].State != BeatmapGroupState.Hidden) p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1));
{
SelectBeatmap(groups[index].BeatmapPanels.First().Beatmap);
return;
}
} while (index != startIndex);
} }
public void SelectRandom()
{
List<BeatmapGroup> visibleGroups = groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden).ToList();
if (visibleGroups.Count < 1)
return;
BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)];
BeatmapPanel panel = group?.BeatmapPanels.First();
if (panel == null)
return;
selectGroup(group, panel);
}
public IEnumerator<BeatmapGroup> GetEnumerator() => groups.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
@ -15,14 +16,13 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Filter; using osu.Game.Screens.Select.Filter;
using Container = osu.Framework.Graphics.Containers.Container; using Container = osu.Framework.Graphics.Containers.Container;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Modes;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
public class FilterControl : Container public class FilterControl : Container
{ {
public Action FilterChanged; public Action<FilterCriteria> FilterChanged;
public string Search => searchTextBox.Text;
private OsuTabControl<SortMode> sortTabs; private OsuTabControl<SortMode> sortTabs;
@ -30,16 +30,16 @@ namespace osu.Game.Screens.Select
private SortMode sort = SortMode.Title; private SortMode sort = SortMode.Title;
public SortMode Sort public SortMode Sort
{ {
get { return sort; } get { return sort; }
set set
{ {
if (sort != value) if (sort != value)
{ {
sort = value; sort = value;
FilterChanged?.Invoke(); FilterChanged?.Invoke(CreateCriteria());
} }
} }
} }
private GroupMode group = GroupMode.All; private GroupMode group = GroupMode.All;
@ -51,11 +51,19 @@ namespace osu.Game.Screens.Select
if (group != value) if (group != value)
{ {
group = value; group = value;
FilterChanged?.Invoke(); FilterChanged?.Invoke(CreateCriteria());
} }
} }
} }
public FilterCriteria CreateCriteria() => new FilterCriteria
{
Group = group,
Sort = sort,
SearchText = searchTextBox.Text,
Mode = playMode
};
public Action Exit; public Action Exit;
private SearchTextBox searchTextBox; private SearchTextBox searchTextBox;
@ -88,7 +96,7 @@ namespace osu.Game.Screens.Select
OnChange = (sender, newText) => OnChange = (sender, newText) =>
{ {
if (newText) if (newText)
FilterChanged?.Invoke(); FilterChanged?.Invoke(CreateCriteria());
}, },
Exit = () => Exit?.Invoke(), Exit = () => Exit?.Invoke(),
}, },
@ -152,16 +160,21 @@ namespace osu.Game.Screens.Select
searchTextBox.HoldFocus = false; searchTextBox.HoldFocus = false;
searchTextBox.TriggerFocusLost(); searchTextBox.TriggerFocusLost();
} }
public void Activate() public void Activate()
{ {
searchTextBox.HoldFocus = true; searchTextBox.HoldFocus = true;
} }
[BackgroundDependencyLoader] private readonly Bindable<PlayMode> playMode = new Bindable<PlayMode>();
private void load(OsuColour colours)
[BackgroundDependencyLoader(permitNulls:true)]
private void load(OsuColour colours, OsuGame osu)
{ {
sortTabs.AccentColour = colours.GreenLight; sortTabs.AccentColour = colours.GreenLight;
if (osu != null)
playMode.BindTo(osu.PlayMode);
} }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;

View File

@ -0,0 +1,65 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Modes;
using osu.Game.Screens.Select.Filter;
namespace osu.Game.Screens.Select
{
public class FilterCriteria
{
public GroupMode Group;
public SortMode Sort;
public string SearchText;
public PlayMode Mode;
public void Filter(List<BeatmapGroup> groups)
{
foreach (var g in groups)
{
var set = g.BeatmapSet;
bool hasCurrentMode = set.Beatmaps.Any(bm => bm.Mode == Mode);
bool match = hasCurrentMode;
match &= string.IsNullOrEmpty(SearchText)
|| (set.Metadata.Artist ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.ArtistUnicode ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.Title ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.TitleUnicode ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1;
switch (g.State)
{
case BeatmapGroupState.Hidden:
if (match) g.State = BeatmapGroupState.Collapsed;
break;
default:
if (!match) g.State = BeatmapGroupState.Hidden;
break;
}
}
switch (Sort)
{
default:
case SortMode.Artist:
groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist, StringComparison.InvariantCultureIgnoreCase));
break;
case SortMode.Title:
groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title, StringComparison.InvariantCultureIgnoreCase));
break;
case SortMode.Author:
groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author, StringComparison.InvariantCultureIgnoreCase));
break;
case SortMode.Difficulty:
groups.Sort((x, y) => x.BeatmapSet.MaxStarDifficulty.CompareTo(y.BeatmapSet.MaxStarDifficulty));
break;
}
}
}
}

View File

@ -19,7 +19,8 @@ namespace osu.Game.Screens.Select.Options
public class BeatmapOptionsOverlay : FocusedOverlayContainer public class BeatmapOptionsOverlay : FocusedOverlayContainer
{ {
private const float transition_duration = 500; private const float transition_duration = 500;
private const float x_position = 290; private const float x_position = 0.2f;
private const float x_movement = 0.8f;
private const float height = 100; private const float height = 100;
@ -30,10 +31,10 @@ namespace osu.Game.Screens.Select.Options
{ {
base.PopIn(); base.PopIn();
if (buttonsContainer.Position.X >= DrawWidth || buttonsContainer.Alpha <= 0) FadeIn(transition_duration, EasingTypes.OutQuint);
buttonsContainer.MoveToX(-buttonsContainer.DrawWidth);
buttonsContainer.Alpha = 1; if (buttonsContainer.Position.X == 1 || Alpha == 0)
buttonsContainer.MoveToX(x_position - x_movement);
holder.ScaleTo(new Vector2(1, 1), transition_duration / 2, EasingTypes.OutQuint); holder.ScaleTo(new Vector2(1, 1), transition_duration / 2, EasingTypes.OutQuint);
@ -47,15 +48,10 @@ namespace osu.Game.Screens.Select.Options
holder.ScaleTo(new Vector2(1, 0), transition_duration / 2, EasingTypes.InSine); holder.ScaleTo(new Vector2(1, 0), transition_duration / 2, EasingTypes.InSine);
buttonsContainer.MoveToX(DrawWidth, transition_duration, EasingTypes.InSine); buttonsContainer.MoveToX(x_position + x_movement, transition_duration, EasingTypes.InSine);
buttonsContainer.TransformSpacingTo(new Vector2(200f, 0f), transition_duration, EasingTypes.InSine); buttonsContainer.TransformSpacingTo(new Vector2(200f, 0f), transition_duration, EasingTypes.InSine);
Delay(transition_duration); FadeOut(transition_duration, EasingTypes.InQuint);
Schedule(() =>
{
if (State == Visibility.Hidden)
buttonsContainer.Alpha = 0;
});
} }
public BeatmapOptionsOverlay() public BeatmapOptionsOverlay()
@ -79,6 +75,7 @@ namespace osu.Game.Screens.Select.Options
buttonsContainer = new ButtonFlow buttonsContainer = new ButtonFlow
{ {
Height = height, Height = height,
RelativePositionAxes = Axes.X,
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,

View File

@ -2,10 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using OpenTK; using OpenTK;
using OpenTK.Input; using OpenTK.Input;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -19,7 +17,6 @@ using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Database; using osu.Game.Database;
@ -38,7 +35,7 @@ namespace osu.Game.Screens.Select
private BeatmapDatabase database; private BeatmapDatabase database;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
private CarouselContainer carousel; private BeatmapCarousel carousel;
private TrackManager trackManager; private TrackManager trackManager;
private DialogOverlay dialogOverlay; private DialogOverlay dialogOverlay;
@ -51,8 +48,6 @@ namespace osu.Game.Screens.Select
private SampleChannel sampleChangeDifficulty; private SampleChannel sampleChangeDifficulty;
private SampleChannel sampleChangeBeatmap; private SampleChannel sampleChangeBeatmap;
private List<BeatmapGroup> beatmapGroups;
protected virtual bool ShowFooter => true; protected virtual bool ShowFooter => true;
/// <summary> /// <summary>
@ -72,7 +67,6 @@ namespace osu.Game.Screens.Select
const float carousel_width = 640; const float carousel_width = 640;
const float filter_height = 100; const float filter_height = 100;
beatmapGroups = new List<BeatmapGroup>();
Add(new ParallaxContainer Add(new ParallaxContainer
{ {
Padding = new MarginPadding { Top = filter_height }, Padding = new MarginPadding { Top = filter_height },
@ -87,18 +81,20 @@ namespace osu.Game.Screens.Select
} }
} }
}); });
Add(carousel = new CarouselContainer Add(carousel = new BeatmapCarousel
{ {
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Size = new Vector2(carousel_width, 1), Size = new Vector2(carousel_width, 1),
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
SelectionChanged = selectionChanged,
StartRequested = raiseSelect
}); });
Add(FilterControl = new FilterControl Add(FilterControl = new FilterControl
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = filter_height, Height = filter_height,
FilterChanged = () => filterChanged(), FilterChanged = criteria => filterChanged(criteria),
Exit = Exit, Exit = Exit,
}); });
Add(beatmapInfoWedge = new BeatmapInfoWedge Add(beatmapInfoWedge = new BeatmapInfoWedge
@ -132,8 +128,7 @@ namespace osu.Game.Screens.Select
} }
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, Framework.Game game, private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours)
OsuGame osu, OsuColour colours)
{ {
if (Footer != null) if (Footer != null)
{ {
@ -161,7 +156,16 @@ namespace osu.Game.Screens.Select
initialAddSetsTask = new CancellationTokenSource(); initialAddSetsTask = new CancellationTokenSource();
Task.Factory.StartNew(() => addBeatmapSets(game, initialAddSetsTask.Token), initialAddSetsTask.Token); carousel.BeatmapsChanged = beatmapsLoaded;
carousel.Beatmaps = database.Query<BeatmapSetInfo>().Where(b => !b.DeletePending);
}
private void beatmapsLoaded()
{
if (Beatmap != null)
carousel.SelectBeatmap(Beatmap.BeatmapInfo, false);
else
carousel.SelectNext();
} }
private void raiseSelect() private void raiseSelect()
@ -173,61 +177,15 @@ namespace osu.Game.Screens.Select
} }
public void SelectRandom() => carousel.SelectRandom(); public void SelectRandom() => carousel.SelectRandom();
protected abstract void OnSelected(); protected abstract void OnSelected();
private ScheduledDelegate filterTask; private void filterChanged(FilterCriteria criteria, bool debounce = true)
private void filterChanged(bool debounce = true, bool eagerSelection = true)
{ {
filterTask?.Cancel(); carousel.Filter(criteria, debounce);
filterTask = Scheduler.AddDelayed(() =>
{
filterTask = null;
var search = FilterControl.Search;
BeatmapGroup newSelection = null;
carousel.Sort(FilterControl.Sort);
foreach (var beatmapGroup in carousel)
{
var set = beatmapGroup.BeatmapSet;
bool hasCurrentMode = set.Beatmaps.Any(bm => bm.Mode == playMode);
bool match = hasCurrentMode;
match &= string.IsNullOrEmpty(search)
|| (set.Metadata.Artist ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.ArtistUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.Title ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.TitleUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1;
if (match)
{
if (newSelection == null || beatmapGroup.BeatmapSet.OnlineBeatmapSetID == Beatmap.BeatmapSetInfo.OnlineBeatmapSetID)
{
if (newSelection != null)
newSelection.State = BeatmapGroupState.Collapsed;
newSelection = beatmapGroup;
}
else
beatmapGroup.State = BeatmapGroupState.Collapsed;
}
else
{
beatmapGroup.State = BeatmapGroupState.Hidden;
}
}
if (newSelection != null)
{
if (newSelection.BeatmapPanels.Any(b => b.Beatmap.ID == Beatmap.BeatmapInfo.ID))
carousel.SelectBeatmap(Beatmap.BeatmapInfo, false);
else if (eagerSelection)
carousel.SelectBeatmap(newSelection.BeatmapSet.Beatmaps[0], false);
}
}, debounce ? 250 : 0);
} }
private void onBeatmapSetAdded(BeatmapSetInfo s) => Schedule(() => addBeatmapSet(s, Game, true)); private void onBeatmapSetAdded(BeatmapSetInfo s) => carousel.AddBeatmap(s);
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s)); private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s));
@ -288,10 +246,7 @@ namespace osu.Game.Screens.Select
initialAddSetsTask.Cancel(); initialAddSetsTask.Cancel();
} }
private void playMode_ValueChanged(object sender, EventArgs e) private void playMode_ValueChanged(object sender, EventArgs e) => carousel.Filter();
{
filterChanged(false);
}
private void changeBackground(WorkingBeatmap beatmap) private void changeBackground(WorkingBeatmap beatmap)
{ {
@ -352,63 +307,18 @@ namespace osu.Game.Screens.Select
} }
} }
private void addBeatmapSet(BeatmapSetInfo beatmapSet, Framework.Game game, bool select = false) private void selectBeatmap(BeatmapSetInfo beatmapSet = null)
{ {
beatmapSet = database.GetWithChildren<BeatmapSetInfo>(beatmapSet.ID); carousel.SelectBeatmap(beatmapSet != null ? beatmapSet.Beatmaps.First() : Beatmap?.BeatmapInfo);
beatmapSet.Beatmaps.ForEach(b =>
{
database.GetChildren(b);
if (b.Metadata == null) b.Metadata = beatmapSet.Metadata;
});
var group = new BeatmapGroup(beatmapSet, database)
{
SelectionChanged = selectionChanged,
StartRequested = b => raiseSelect()
};
//for the time being, let's completely load the difficulty panels in the background.
//this likely won't scale so well, but allows us to completely async the loading flow.
Task.WhenAll(group.BeatmapPanels.Select(panel => panel.LoadAsync(game))).ContinueWith(task => Schedule(delegate
{
beatmapGroups.Add(group);
group.State = BeatmapGroupState.Collapsed;
carousel.AddGroup(group);
filterChanged(false, false);
if (Beatmap == null || select)
carousel.SelectBeatmap(beatmapSet.Beatmaps.First());
else
carousel.SelectBeatmap(Beatmap.BeatmapInfo);
}));
} }
private void removeBeatmapSet(BeatmapSetInfo beatmapSet) private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
{ {
var group = beatmapGroups.Find(b => b.BeatmapSet.ID == beatmapSet.ID); carousel.RemoveBeatmap(beatmapSet);
if (group == null) return; if (carousel.SelectedBeatmap == null)
if (carousel.SelectedGroup == group)
carousel.SelectNext();
beatmapGroups.Remove(group);
carousel.RemoveGroup(group);
if (beatmapGroups.Count == 0)
Beatmap = null; Beatmap = null;
} }
private void addBeatmapSets(Framework.Game game, CancellationToken token)
{
foreach (var beatmapSet in database.Query<BeatmapSetInfo>().Where(b => !b.DeletePending))
{
if (token.IsCancellationRequested) return;
addBeatmapSet(beatmapSet, game);
}
}
private void promptDelete() private void promptDelete()
{ {
if (Beatmap != null) if (Beatmap != null)

View File

@ -75,10 +75,12 @@
<Compile Include="Beatmaps\DifficultyCalculator.cs" /> <Compile Include="Beatmaps\DifficultyCalculator.cs" />
<Compile Include="Beatmaps\IBeatmapCoverter.cs" /> <Compile Include="Beatmaps\IBeatmapCoverter.cs" />
<Compile Include="Beatmaps\IBeatmapProcessor.cs" /> <Compile Include="Beatmaps\IBeatmapProcessor.cs" />
<Compile Include="Beatmaps\Legacy\LegacyBeatmap.cs" />
<Compile Include="Beatmaps\Timing\TimingInfo.cs" /> <Compile Include="Beatmaps\Timing\TimingInfo.cs" />
<Compile Include="Database\ScoreDatabase.cs" /> <Compile Include="Database\ScoreDatabase.cs" />
<Compile Include="Graphics\Backgrounds\Triangles.cs" /> <Compile Include="Graphics\Backgrounds\Triangles.cs" />
<Compile Include="Graphics\Cursor\CursorTrail.cs" /> <Compile Include="Graphics\Cursor\CursorTrail.cs" />
<Compile Include="Graphics\Cursor\GameplayCursor.cs" />
<Compile Include="Graphics\Sprites\OsuSpriteText.cs" /> <Compile Include="Graphics\Sprites\OsuSpriteText.cs" />
<Compile Include="Graphics\UserInterface\BackButton.cs" /> <Compile Include="Graphics\UserInterface\BackButton.cs" />
<Compile Include="Graphics\UserInterface\FocusedTextBox.cs" /> <Compile Include="Graphics\UserInterface\FocusedTextBox.cs" />
@ -196,7 +198,8 @@
<Compile Include="Screens\Play\PlayerLoader.cs" /> <Compile Include="Screens\Play\PlayerLoader.cs" />
<Compile Include="Screens\Play\SkipButton.cs" /> <Compile Include="Screens\Play\SkipButton.cs" />
<Compile Include="Modes\UI\StandardComboCounter.cs" /> <Compile Include="Modes\UI\StandardComboCounter.cs" />
<Compile Include="Screens\Select\CarouselContainer.cs" /> <Compile Include="Screens\Select\BeatmapCarousel.cs" />
<Compile Include="Screens\Select\FilterCriteria.cs" />
<Compile Include="Screens\Select\Filter\GroupMode.cs" /> <Compile Include="Screens\Select\Filter\GroupMode.cs" />
<Compile Include="Screens\Select\Filter\SortMode.cs" /> <Compile Include="Screens\Select\Filter\SortMode.cs" />
<Compile Include="Screens\Select\MatchSongSelect.cs" /> <Compile Include="Screens\Select\MatchSongSelect.cs" />
@ -221,7 +224,7 @@
<Compile Include="Input\GlobalHotkeys.cs" /> <Compile Include="Input\GlobalHotkeys.cs" />
<Compile Include="Graphics\Backgrounds\Background.cs" /> <Compile Include="Graphics\Backgrounds\Background.cs" />
<Compile Include="Graphics\Containers\ParallaxContainer.cs" /> <Compile Include="Graphics\Containers\ParallaxContainer.cs" />
<Compile Include="Graphics\Cursor\OsuCursorContainer.cs" /> <Compile Include="Graphics\Cursor\MenuCursor.cs" />
<Compile Include="Graphics\Processing\RatioAdjust.cs" /> <Compile Include="Graphics\Processing\RatioAdjust.cs" />
<Compile Include="Graphics\TextAwesome.cs" /> <Compile Include="Graphics\TextAwesome.cs" />
<Compile Include="Screens\Play\KeyCounter.cs" /> <Compile Include="Screens\Play\KeyCounter.cs" />