1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 15:47:26 +08:00

Add hiding support for beatmap difficulties

This commit is contained in:
Dean Herbert 2017-08-31 15:49:56 +09:00
parent 5a58489adf
commit 1f646e6d54
10 changed files with 136 additions and 59 deletions

@ -1 +1 @@
Subproject commit 167d5cda8f3ddae702ffc8d8d22dac67e48b509c
Subproject commit 2804e052ca2ce068f015771d28170d18573687e1

View File

@ -52,6 +52,8 @@ namespace osu.Game.Beatmaps
[JsonProperty("file_sha2")]
public string Hash { get; set; }
public bool Hidden { get; set; }
/// <summary>
/// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
/// </summary>

View File

@ -33,11 +33,21 @@ namespace osu.Game.Beatmaps
/// </summary>
public event Action<BeatmapSetInfo> BeatmapSetAdded;
/// <summary>
/// Fired when a single difficulty has been hidden.
/// </summary>
public event Action<BeatmapInfo> BeatmapHidden;
/// <summary>
/// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database.
/// </summary>
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
/// <summary>
/// Fired when a single difficulty has been restored.
/// </summary>
public event Action<BeatmapInfo> BeatmapRestored;
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
@ -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,8 +174,7 @@ 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);
}
/// <summary>
@ -173,22 +184,23 @@ namespace osu.Game.Beatmaps
/// <param name="beatmapSet">The beatmap set to delete.</param>
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());
}
/// <summary>
/// Delete a beatmap from the manager.
/// Is a no-op for already deleted beatmaps.
/// Delete a beatmap difficulty.
/// </summary>
/// <param name="beatmap">The beatmap difficulty to delete.</param>
public void Delete(BeatmapInfo beatmap)
{
//todo: implement
}
/// <param name="beatmap">The beatmap difficulty to hide.</param>
public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap);
/// <summary>
/// Restore a beatmap difficulty.
/// </summary>
/// <param name="beatmap">The beatmap difficulty to restore.</param>
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
/// <summary>
/// Returns a <see cref="BeatmapSetInfo"/> to a usable state if it has previously been deleted but not yet purged.
@ -197,8 +209,7 @@ namespace osu.Game.Beatmaps
/// <param name="beatmapSet">The beatmap to restore.</param>
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());
@ -258,6 +269,13 @@ namespace osu.Game.Beatmaps
}
}
/// <summary>
/// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
/// </summary>
/// <param name="beatmapSet">A stale instance.</param>
/// <returns>A fresh instance.</returns>
public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.ID == beatmapSet.ID);
/// <summary>
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
/// </summary>
@ -265,7 +283,7 @@ namespace osu.Game.Beatmaps
/// <returns>Results from the provided query.</returns>
public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query)
{
lock (beatmaps) return beatmaps.QueryAndPopulate(query);
return beatmaps.QueryAndPopulate(query);
}
/// <summary>
@ -275,15 +293,12 @@ namespace osu.Game.Beatmaps
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query)
{
lock (beatmaps)
{
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
if (set != null)
beatmaps.Populate(set);
if (set != null)
beatmaps.Populate(set);
return set;
}
return set;
}
/// <summary>

View File

@ -16,11 +16,14 @@ namespace osu.Game.Beatmaps
public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
public event Action<BeatmapInfo> BeatmapHidden;
public event Action<BeatmapInfo> BeatmapRestored;
/// <summary>
/// The current version of this store. Used for migrations (see <see cref="PerformMigration(int, int)"/>).
/// The initial version is 1.
/// </summary>
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<BeatmapInfo>();
break;
case 4:
// Added Hidden column to BeatmapInfo
Connection.MigrateTable<BeatmapInfo>();
break;
}
}
}
@ -100,7 +107,7 @@ namespace osu.Game.Beatmaps
}
/// <summary>
/// Delete a <see cref="BeatmapSetInfo"/> to the database.
/// Delete a <see cref="BeatmapSetInfo"/> from the database.
/// </summary>
/// <param name="beatmapSet">The beatmap to delete.</param>
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
@ -131,6 +138,38 @@ namespace osu.Game.Beatmaps
return true;
}
/// <summary>
/// Hide a <see cref="BeatmapInfo"/> in the database.
/// </summary>
/// <param name="beatmap">The beatmap to hide.</param>
/// <returns>Whether the beatmap's <see cref="BeatmapInfo.Hidden"/> was changed.</returns>
public bool Hide(BeatmapInfo beatmap)
{
if (beatmap.Hidden) return false;
beatmap.Hidden = true;
Connection.Update(beatmap);
BeatmapHidden?.Invoke(beatmap);
return true;
}
/// <summary>
/// Restore a previously hidden <see cref="BeatmapInfo"/>.
/// </summary>
/// <param name="beatmap">The beatmap to restore.</param>
/// <returns>Whether the beatmap's <see cref="BeatmapInfo.Hidden"/> was changed.</returns>
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(() =>

View File

@ -25,6 +25,8 @@ namespace osu.Game.Beatmaps.Drawables
public Action<BeatmapSetInfo> DeleteRequested;
public Action<BeatmapSetInfo> RestoreHiddenRequested;
public Action<BeatmapInfo> DeleteDifficultyRequested;
public BeatmapSetHeader Header;
@ -71,10 +73,11 @@ namespace osu.Game.Beatmaps.Drawables
{
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,

View File

@ -158,7 +158,7 @@ namespace osu.Game.Beatmaps.Drawables
{
new OsuMenuItem("Play", MenuItemType.Highlighted, () => StartRequested?.Invoke(this)),
new OsuMenuItem("Edit", MenuItemType.Standard, () => EditRequested?.Invoke(this)),
new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke(Beatmap)),
new OsuMenuItem("Hide", MenuItemType.Destructive, () => DeleteRequested?.Invoke(Beatmap)),
};
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
@ -25,6 +26,8 @@ namespace osu.Game.Beatmaps.Drawables
public Action<BeatmapSetInfo> DeleteRequested;
public Action<BeatmapSetInfo> RestoreHiddenRequested;
private readonly SpriteText title;
private readonly SpriteText artist;
@ -164,6 +167,9 @@ namespace osu.Game.Beatmaps.Drawables
if (State == PanelSelectedState.NotSelected)
items.Add(new OsuMenuItem("Expand", MenuItemType.Highlighted, () => State = PanelSelectedState.Selected));
if (beatmap.BeatmapSetInfo.Beatmaps.Any(b => b.Hidden))
items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => RestoreHiddenRequested?.Invoke(beatmap.BeatmapSetInfo)));
items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke(beatmap.BeatmapSetInfo)));
return items.ToArray();

View File

@ -107,12 +107,39 @@ namespace osu.Game.Screens.Select
});
}
public void RemoveBeatmap(BeatmapSetInfo beatmapSet)
public void RemoveBeatmap(BeatmapSetInfo beatmapSet) => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID));
internal void UpdateBeatmap(BeatmapInfo beatmap)
{
Schedule(delegate
// todo: this method should not run more than once for the same BeatmapSetInfo.
var set = manager.Refresh(beatmap.BeatmapSet);
// todo: this method should be smarter as to not recreate panels that haven't changed, etc.
var group = groups.Find(b => b.BeatmapSet.ID == set.ID);
if (group == null)
return;
var newGroup = createGroup(set);
int i = groups.IndexOf(group);
groups.RemoveAt(i);
groups.Insert(i, newGroup);
if (selectedGroup == group && newGroup.BeatmapPanels.Count == 0)
selectedGroup = null;
Filter(null, false);
//check if we can/need to maintain our current selection.
if (selectedGroup == group && newGroup.BeatmapPanels.Count > 0)
{
removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID));
});
var newSelection =
newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID) ??
newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))];
selectGroup(newGroup, newSelection);
}
}
public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true)
@ -142,6 +169,8 @@ namespace osu.Game.Screens.Select
public Action<BeatmapSetInfo> DeleteRequested;
public Action<BeatmapSetInfo> RestoreRequested;
public Action<BeatmapInfo> DeleteDifficultyRequested;
public void SelectNext(int direction = 1, bool skipDifficulties = true)
@ -310,6 +339,7 @@ namespace osu.Game.Screens.Select
SelectionChanged = (g, p) => selectGroup(g, p),
StartRequested = b => StartRequested?.Invoke(),
DeleteRequested = b => DeleteRequested?.Invoke(b),
RestoreHiddenRequested = s => RestoreRequested?.Invoke(s),
DeleteDifficultyRequested = b => DeleteDifficultyRequested?.Invoke(b),
State = BeatmapGroupState.Collapsed
};

View File

@ -1,7 +1,6 @@
// 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 osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
@ -13,28 +12,16 @@ namespace osu.Game.Screens.Select
{
private BeatmapManager manager;
private readonly Action deleteAction;
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmapManager)
{
manager = beatmapManager;
}
public BeatmapDeleteDialog(BeatmapSetInfo beatmap) : this()
public BeatmapDeleteDialog(BeatmapSetInfo beatmap)
{
BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title} (ALL DIFFICULTIES)";
deleteAction = () => manager.Delete(beatmap);
}
BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
public BeatmapDeleteDialog(BeatmapInfo beatmap) : this()
{
BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title} [{beatmap.Version}]";
deleteAction = () => manager.Delete(beatmap);
}
public BeatmapDeleteDialog()
{
Icon = FontAwesome.fa_trash_o;
HeaderText = @"Confirm deletion of";
Buttons = new PopupDialogButton[]
@ -42,7 +29,7 @@ namespace osu.Game.Screens.Select
new PopupDialogOkButton
{
Text = @"Yes. Totally. Delete it.",
Action = () => deleteAction(),
Action = () => manager.Delete(beatmap),
},
new PopupDialogCancelButton
{

View File

@ -107,7 +107,8 @@ namespace osu.Game.Screens.Select
SelectionChanged = carouselSelectionChanged,
BeatmapsChanged = carouselBeatmapsLoaded,
DeleteRequested = b => promptDelete(b),
DeleteDifficultyRequested = b => promptDelete(b),
RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); },
DeleteDifficultyRequested = b => manager.Hide(b),
StartRequested = () => carouselRaisedStart(),
});
Add(FilterControl = new FilterControl
@ -176,6 +177,8 @@ namespace osu.Game.Screens.Select
manager.BeatmapSetAdded += onBeatmapSetAdded;
manager.BeatmapSetRemoved += onBeatmapSetRemoved;
manager.BeatmapHidden += onBeatmapHidden;
manager.BeatmapRestored += onBeatmapRestored;
dialogOverlay = dialog;
@ -192,6 +195,9 @@ namespace osu.Game.Screens.Select
carousel.AllowSelection = !Beatmap.Disabled;
}
private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmap(b);
private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmap(b);
private void carouselBeatmapsLoaded()
{
if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
@ -380,10 +386,7 @@ namespace osu.Game.Screens.Select
}
}
private void addBeatmapSet(BeatmapSetInfo beatmapSet)
{
carousel.AddBeatmap(beatmapSet);
}
private void addBeatmapSet(BeatmapSetInfo beatmapSet) => carousel.AddBeatmap(beatmapSet);
private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
{
@ -400,14 +403,6 @@ namespace osu.Game.Screens.Select
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
}
private void promptDelete(BeatmapInfo beatmap)
{
if (beatmap == null)
return;
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;