diff --git a/osu-framework b/osu-framework
index 167d5cda8f..2bd341b29d 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 167d5cda8f3ddae702ffc8d8d22dac67e48b509c
+Subproject commit 2bd341b29d6a7ed864aa9c1c5fad4668dafe03a4
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index ebf77bf9df..c962201fe3 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -52,6 +52,8 @@ namespace osu.Game.Beatmaps
[JsonProperty("file_sha2")]
public string Hash { get; set; }
+ public bool Hidden { get; set; }
+
///
/// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
///
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 1dcab6cb5d..551612330b 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -33,11 +33,21 @@ namespace osu.Game.Beatmaps
///
public event Action BeatmapSetAdded;
+ ///
+ /// Fired when a single difficulty has been hidden.
+ ///
+ public event Action BeatmapHidden;
+
///
/// Fired when a is removed from the database.
///
public event Action BeatmapSetRemoved;
+ ///
+ /// Fired when a single difficulty has been restored.
+ ///
+ public event Action BeatmapRestored;
+
///
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
///
@@ -71,6 +81,8 @@ namespace osu.Game.Beatmaps
beatmaps = new BeatmapStore(connection);
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
+ beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
+ beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
this.storage = storage;
this.files = files;
@@ -162,24 +174,34 @@ namespace osu.Game.Beatmaps
// If we have an ID then we already exist in the database.
if (beatmapSetInfo.ID != 0) return;
- lock (beatmaps)
- beatmaps.Add(beatmapSetInfo);
+ beatmaps.Add(beatmapSetInfo);
}
///
/// Delete a beatmap from the manager.
/// Is a no-op for already deleted beatmaps.
///
- /// The beatmap to delete.
+ /// The beatmap set to delete.
public void Delete(BeatmapSetInfo beatmapSet)
{
- lock (beatmaps)
- if (!beatmaps.Delete(beatmapSet)) return;
+ if (!beatmaps.Delete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
}
+ ///
+ /// Delete a beatmap difficulty.
+ ///
+ /// The beatmap difficulty to hide.
+ public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap);
+
+ ///
+ /// Restore a beatmap difficulty.
+ ///
+ /// The beatmap difficulty to restore.
+ public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
+
///
/// Returns a to a usable state if it has previously been deleted but not yet purged.
/// Is a no-op for already usable beatmaps.
@@ -187,8 +209,7 @@ namespace osu.Game.Beatmaps
/// The beatmap to restore.
public void Undelete(BeatmapSetInfo beatmapSet)
{
- lock (beatmaps)
- if (!beatmaps.Undelete(beatmapSet)) return;
+ if (!beatmaps.Undelete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
@@ -248,6 +269,13 @@ namespace osu.Game.Beatmaps
}
}
+ ///
+ /// Refresh an existing instance of a from the store.
+ ///
+ /// A stale instance.
+ /// A fresh instance.
+ public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.ID == beatmapSet.ID);
+
///
/// Perform a lookup query on available s.
///
@@ -255,7 +283,7 @@ namespace osu.Game.Beatmaps
/// Results from the provided query.
public List QueryBeatmapSets(Expression> query)
{
- lock (beatmaps) return beatmaps.QueryAndPopulate(query);
+ return beatmaps.QueryAndPopulate(query);
}
///
@@ -265,15 +293,12 @@ namespace osu.Game.Beatmaps
/// The first result for the provided query, or null if no results were found.
public BeatmapInfo QueryBeatmap(Func query)
{
- lock (beatmaps)
- {
- BeatmapInfo set = beatmaps.Query().FirstOrDefault(query);
+ BeatmapInfo set = beatmaps.Query().FirstOrDefault(query);
- if (set != null)
- beatmaps.Populate(set);
+ if (set != null)
+ beatmaps.Populate(set);
- return set;
- }
+ return set;
}
///
diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs
index 8212712bf9..0f2d8cffa6 100644
--- a/osu.Game/Beatmaps/BeatmapStore.cs
+++ b/osu.Game/Beatmaps/BeatmapStore.cs
@@ -16,11 +16,14 @@ namespace osu.Game.Beatmaps
public event Action BeatmapSetAdded;
public event Action BeatmapSetRemoved;
+ public event Action BeatmapHidden;
+ public event Action BeatmapRestored;
+
///
/// The current version of this store. Used for migrations (see ).
/// The initial version is 1.
///
- protected override int StoreVersion => 3;
+ protected override int StoreVersion => 4;
public BeatmapStore(SQLiteConnection connection)
: base(connection)
@@ -81,6 +84,10 @@ namespace osu.Game.Beatmaps
// Added MD5Hash column to BeatmapInfo
Connection.MigrateTable();
break;
+ case 4:
+ // Added Hidden column to BeatmapInfo
+ Connection.MigrateTable();
+ break;
}
}
}
@@ -100,7 +107,7 @@ namespace osu.Game.Beatmaps
}
///
- /// Delete a to the database.
+ /// Delete a from the database.
///
/// The beatmap to delete.
/// Whether the beatmap's was changed.
@@ -131,6 +138,38 @@ namespace osu.Game.Beatmaps
return true;
}
+ ///
+ /// Hide a in the database.
+ ///
+ /// The beatmap to hide.
+ /// Whether the beatmap's was changed.
+ public bool Hide(BeatmapInfo beatmap)
+ {
+ if (beatmap.Hidden) return false;
+
+ beatmap.Hidden = true;
+ Connection.Update(beatmap);
+
+ BeatmapHidden?.Invoke(beatmap);
+ return true;
+ }
+
+ ///
+ /// Restore a previously hidden .
+ ///
+ /// The beatmap to restore.
+ /// Whether the beatmap's was changed.
+ public bool Restore(BeatmapInfo beatmap)
+ {
+ if (!beatmap.Hidden) return false;
+
+ beatmap.Hidden = false;
+ Connection.Update(beatmap);
+
+ BeatmapRestored?.Invoke(beatmap);
+ return true;
+ }
+
private void cleanupPendingDeletions()
{
Connection.RunInTransaction(() =>
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
index ad9a0a787b..4a389101e2 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs
@@ -23,6 +23,12 @@ namespace osu.Game.Beatmaps.Drawables
///
public Action StartRequested;
+ public Action DeleteRequested;
+
+ public Action RestoreHiddenRequested;
+
+ public Action HideDifficultyRequested;
+
public BeatmapSetHeader Header;
private BeatmapGroupState state;
@@ -66,14 +72,17 @@ namespace osu.Game.Beatmaps.Drawables
Header = new BeatmapSetHeader(beatmap)
{
GainedSelection = headerGainedSelection,
+ DeleteRequested = b => DeleteRequested(b),
+ RestoreHiddenRequested = b => RestoreHiddenRequested(b),
RelativeSizeAxes = Axes.X,
};
- BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.OrderBy(b => b.StarDifficulty).ToList();
+ BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).ToList();
BeatmapPanels = BeatmapSet.Beatmaps.Select(b => new BeatmapPanel(b)
{
Alpha = 0,
GainedSelection = panelGainedSelection,
+ HideRequested = p => HideDifficultyRequested?.Invoke(p),
StartRequested = p => { StartRequested?.Invoke(p.Beatmap); },
RelativeSizeAxes = Axes.X,
}).ToList();
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
index 429ecaf416..e216f1b83e 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
@@ -14,16 +15,20 @@ using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Beatmaps.Drawables
{
- public class BeatmapPanel : Panel
+ public class BeatmapPanel : Panel, IHasContextMenu
{
public BeatmapInfo Beatmap;
private readonly Sprite background;
public Action GainedSelection;
public Action StartRequested;
+ public Action EditRequested;
+ public Action HideRequested;
+
private readonly Triangles triangles;
private readonly StarCounter starCounter;
@@ -148,5 +153,12 @@ namespace osu.Game.Beatmaps.Drawables
}
};
}
+
+ public MenuItem[] ContextMenuItems => new MenuItem[]
+ {
+ new OsuMenuItem("Play", MenuItemType.Highlighted, () => StartRequested?.Invoke(this)),
+ new OsuMenuItem("Edit", MenuItemType.Standard, () => EditRequested?.Invoke(this)),
+ new OsuMenuItem("Hide", MenuItemType.Destructive, () => HideRequested?.Invoke(Beatmap)),
+ };
}
}
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
index a2457ba78e..ee75b77747 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs
@@ -3,22 +3,31 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Game.Graphics.UserInterface;
namespace osu.Game.Beatmaps.Drawables
{
- public class BeatmapSetHeader : Panel
+ public class BeatmapSetHeader : Panel, IHasContextMenu
{
public Action GainedSelection;
+
+ public Action DeleteRequested;
+
+ public Action RestoreHiddenRequested;
+
private readonly SpriteText title;
private readonly SpriteText artist;
@@ -148,5 +157,23 @@ namespace osu.Game.Beatmaps.Drawables
foreach (var p in panels)
difficultyIcons.Add(new DifficultyIcon(p.Beatmap));
}
+
+ public MenuItem[] ContextMenuItems
+ {
+ get
+ {
+ List