mirror of
https://github.com/osukey/osukey.git
synced 2025-08-03 22:56:36 +09:00
Merge branch 'master' into top-rank-badge-order
This commit is contained in:
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -49,31 +47,31 @@ namespace osu.Game.Screens.Select
|
||||
/// <summary>
|
||||
/// Triggered when the <see cref="BeatmapSets"/> loaded change and are completely loaded.
|
||||
/// </summary>
|
||||
public Action BeatmapSetsChanged;
|
||||
public Action? BeatmapSetsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected beatmap.
|
||||
/// </summary>
|
||||
public BeatmapInfo SelectedBeatmapInfo => selectedBeatmap?.BeatmapInfo;
|
||||
public BeatmapInfo? SelectedBeatmapInfo => selectedBeatmap?.BeatmapInfo;
|
||||
|
||||
private CarouselBeatmap selectedBeatmap => selectedBeatmapSet?.Beatmaps.FirstOrDefault(s => s.State.Value == CarouselItemState.Selected);
|
||||
private CarouselBeatmap? selectedBeatmap => selectedBeatmapSet?.Beatmaps.FirstOrDefault(s => s.State.Value == CarouselItemState.Selected);
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected beatmap set.
|
||||
/// </summary>
|
||||
public BeatmapSetInfo SelectedBeatmapSet => selectedBeatmapSet?.BeatmapSet;
|
||||
public BeatmapSetInfo? SelectedBeatmapSet => selectedBeatmapSet?.BeatmapSet;
|
||||
|
||||
/// <summary>
|
||||
/// A function to optionally decide on a recommended difficulty from a beatmap set.
|
||||
/// </summary>
|
||||
public Func<IEnumerable<BeatmapInfo>, BeatmapInfo> GetRecommendedBeatmap;
|
||||
public Func<IEnumerable<BeatmapInfo>, BeatmapInfo>? GetRecommendedBeatmap;
|
||||
|
||||
private CarouselBeatmapSet selectedBeatmapSet;
|
||||
private CarouselBeatmapSet? selectedBeatmapSet;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the <see cref="SelectedBeatmapInfo"/> is changed.
|
||||
/// </summary>
|
||||
public Action<BeatmapInfo> SelectionChanged;
|
||||
public Action<BeatmapInfo?>? SelectionChanged;
|
||||
|
||||
public override bool HandleNonPositionalInput => AllowSelection;
|
||||
public override bool HandlePositionalInput => AllowSelection;
|
||||
@ -151,15 +149,15 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private CarouselRoot root;
|
||||
|
||||
private IDisposable subscriptionSets;
|
||||
private IDisposable subscriptionDeletedSets;
|
||||
private IDisposable subscriptionBeatmaps;
|
||||
private IDisposable subscriptionHiddenBeatmaps;
|
||||
private IDisposable? subscriptionSets;
|
||||
private IDisposable? subscriptionDeletedSets;
|
||||
private IDisposable? subscriptionBeatmaps;
|
||||
private IDisposable? subscriptionHiddenBeatmaps;
|
||||
|
||||
private readonly DrawablePool<DrawableCarouselBeatmapSet> setPool = new DrawablePool<DrawableCarouselBeatmapSet>(100);
|
||||
|
||||
private Sample spinSample;
|
||||
private Sample randomSelectSample;
|
||||
private Sample? spinSample;
|
||||
private Sample? randomSelectSample;
|
||||
|
||||
private int visibleSetsCount;
|
||||
|
||||
@ -200,7 +198,7 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; }
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
@ -215,7 +213,7 @@ namespace osu.Game.Screens.Select
|
||||
subscriptionHiddenBeatmaps = realm.RegisterForNotifications(r => r.All<BeatmapInfo>().Where(b => b.Hidden), beatmapsChanged);
|
||||
}
|
||||
|
||||
private void deletedBeatmapSetsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet changes, Exception error)
|
||||
private void deletedBeatmapSetsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet? changes, Exception? error)
|
||||
{
|
||||
// If loading test beatmaps, avoid overwriting with realm subscription callbacks.
|
||||
if (loadedTestBeatmaps)
|
||||
@ -228,7 +226,7 @@ namespace osu.Game.Screens.Select
|
||||
removeBeatmapSet(sender[i].ID);
|
||||
}
|
||||
|
||||
private void beatmapSetsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet changes, Exception error)
|
||||
private void beatmapSetsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet? changes, Exception? error)
|
||||
{
|
||||
// If loading test beatmaps, avoid overwriting with realm subscription callbacks.
|
||||
if (loadedTestBeatmaps)
|
||||
@ -265,9 +263,49 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
foreach (int i in changes.InsertedIndices)
|
||||
UpdateBeatmapSet(sender[i].Detach());
|
||||
|
||||
if (changes.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null)
|
||||
{
|
||||
// If SelectedBeatmapInfo is non-null, the set should also be non-null.
|
||||
Debug.Assert(SelectedBeatmapSet != null);
|
||||
|
||||
// To handle the beatmap update flow, attempt to track selection changes across delete-insert transactions.
|
||||
// When an update occurs, the previous beatmap set is either soft or hard deleted.
|
||||
// Check if the current selection was potentially deleted by re-querying its validity.
|
||||
bool selectedSetMarkedDeleted = realm.Run(r => r.Find<BeatmapSetInfo>(SelectedBeatmapSet.ID))?.DeletePending != false;
|
||||
|
||||
int[] modifiedAndInserted = changes.NewModifiedIndices.Concat(changes.InsertedIndices).ToArray();
|
||||
|
||||
if (selectedSetMarkedDeleted && modifiedAndInserted.Any())
|
||||
{
|
||||
// If it is no longer valid, make the bold assumption that an updated version will be available in the modified/inserted indices.
|
||||
// This relies on the full update operation being in a single transaction, so please don't change that.
|
||||
foreach (int i in modifiedAndInserted)
|
||||
{
|
||||
var beatmapSetInfo = sender[i];
|
||||
|
||||
foreach (var beatmapInfo in beatmapSetInfo.Beatmaps)
|
||||
{
|
||||
if (!((IBeatmapMetadataInfo)beatmapInfo.Metadata).Equals(SelectedBeatmapInfo.Metadata))
|
||||
continue;
|
||||
|
||||
// Best effort matching. We can't use ID because in the update flow a new version will get its own GUID.
|
||||
if (beatmapInfo.DifficultyName == SelectedBeatmapInfo.DifficultyName)
|
||||
{
|
||||
SelectBeatmap(beatmapInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a direct selection couldn't be made, it's feasible that the difficulty name (or beatmap metadata) changed.
|
||||
// Let's attempt to follow set-level selection anyway.
|
||||
SelectBeatmap(sender[modifiedAndInserted.First()].Beatmaps.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void beatmapsChanged(IRealmCollection<BeatmapInfo> sender, ChangeSet changes, Exception error)
|
||||
private void beatmapsChanged(IRealmCollection<BeatmapInfo> sender, ChangeSet? changes, Exception? error)
|
||||
{
|
||||
// we only care about actual changes in hidden status.
|
||||
if (changes == null)
|
||||
@ -330,7 +368,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
// check if we can/need to maintain our current selection.
|
||||
if (previouslySelectedID != null)
|
||||
select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet);
|
||||
select((CarouselItem?)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet);
|
||||
}
|
||||
|
||||
itemsCache.Invalidate();
|
||||
@ -347,7 +385,7 @@ namespace osu.Game.Screens.Select
|
||||
/// <param name="beatmapInfo">The beatmap to select.</param>
|
||||
/// <param name="bypassFilters">Whether to select the beatmap even if it is filtered (i.e., not visible on carousel).</param>
|
||||
/// <returns>True if a selection was made, False if it wasn't.</returns>
|
||||
public bool SelectBeatmap(BeatmapInfo beatmapInfo, bool bypassFilters = true)
|
||||
public bool SelectBeatmap(BeatmapInfo? beatmapInfo, bool bypassFilters = true)
|
||||
{
|
||||
// ensure that any pending events from BeatmapManager have been run before attempting a selection.
|
||||
Scheduler.Update();
|
||||
@ -405,6 +443,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void selectNextSet(int direction, bool skipDifficulties)
|
||||
{
|
||||
if (selectedBeatmap == null || selectedBeatmapSet == null)
|
||||
return;
|
||||
|
||||
var unfilteredSets = beatmapSets.Where(s => !s.Filtered.Value).ToList();
|
||||
|
||||
var nextSet = unfilteredSets[(unfilteredSets.IndexOf(selectedBeatmapSet) + direction + unfilteredSets.Count) % unfilteredSets.Count];
|
||||
@ -417,7 +458,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void selectNextDifficulty(int direction)
|
||||
{
|
||||
if (selectedBeatmap == null)
|
||||
if (selectedBeatmap == null || selectedBeatmapSet == null)
|
||||
return;
|
||||
|
||||
var unfilteredDifficulties = selectedBeatmapSet.Items.Where(s => !s.Filtered.Value).ToList();
|
||||
@ -446,7 +487,7 @@ namespace osu.Game.Screens.Select
|
||||
if (!visibleSets.Any())
|
||||
return false;
|
||||
|
||||
if (selectedBeatmap != null)
|
||||
if (selectedBeatmap != null && selectedBeatmapSet != null)
|
||||
{
|
||||
randomSelectedBeatmaps.Push(selectedBeatmap);
|
||||
|
||||
@ -489,11 +530,13 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (!beatmap.Filtered.Value)
|
||||
{
|
||||
if (RandomAlgorithm.Value == RandomSelectAlgorithm.RandomPermutation)
|
||||
previouslyVisitedRandomSets.Remove(selectedBeatmapSet);
|
||||
|
||||
if (selectedBeatmapSet != null)
|
||||
{
|
||||
if (RandomAlgorithm.Value == RandomSelectAlgorithm.RandomPermutation)
|
||||
previouslyVisitedRandomSets.Remove(selectedBeatmapSet);
|
||||
|
||||
playSpinSample(distanceBetween(beatmap, selectedBeatmapSet));
|
||||
}
|
||||
|
||||
select(beatmap);
|
||||
break;
|
||||
@ -505,14 +548,18 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void playSpinSample(double distance)
|
||||
{
|
||||
var chan = spinSample.GetChannel();
|
||||
chan.Frequency.Value = 1f + Math.Min(1f, distance / visibleSetsCount);
|
||||
chan.Play();
|
||||
var chan = spinSample?.GetChannel();
|
||||
|
||||
if (chan != null)
|
||||
{
|
||||
chan.Frequency.Value = 1f + Math.Min(1f, distance / visibleSetsCount);
|
||||
chan.Play();
|
||||
}
|
||||
|
||||
randomSelectSample?.Play();
|
||||
}
|
||||
|
||||
private void select(CarouselItem item)
|
||||
private void select(CarouselItem? item)
|
||||
{
|
||||
if (!AllowSelection)
|
||||
return;
|
||||
@ -524,7 +571,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private FilterCriteria activeCriteria = new FilterCriteria();
|
||||
|
||||
protected ScheduledDelegate PendingFilter;
|
||||
protected ScheduledDelegate? PendingFilter;
|
||||
|
||||
public bool AllowSelection = true;
|
||||
|
||||
@ -556,7 +603,7 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
}
|
||||
|
||||
public void Filter(FilterCriteria newCriteria, bool debounce = true)
|
||||
public void Filter(FilterCriteria? newCriteria, bool debounce = true)
|
||||
{
|
||||
if (newCriteria != null)
|
||||
activeCriteria = newCriteria;
|
||||
@ -759,7 +806,7 @@ namespace osu.Game.Screens.Select
|
||||
return (firstIndex, lastIndex);
|
||||
}
|
||||
|
||||
private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet)
|
||||
private CarouselBeatmapSet? createCarouselSet(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
// This can be moved to the realm query if required using:
|
||||
// .Filter("DeletePending == false && Protected == false && ANY Beatmaps.Hidden == false")
|
||||
@ -925,7 +972,7 @@ namespace osu.Game.Screens.Select
|
||||
/// </summary>
|
||||
/// <param name="item">The item to be updated.</param>
|
||||
/// <param name="parent">For nested items, the parent of the item to be updated.</param>
|
||||
private void updateItem(DrawableCarouselItem item, DrawableCarouselItem parent = null)
|
||||
private void updateItem(DrawableCarouselItem item, DrawableCarouselItem? parent = null)
|
||||
{
|
||||
Vector2 posInScroll = Scroll.ScrollContent.ToLocalSpace(item.Header.ScreenSpaceDrawQuad.Centre);
|
||||
float itemDrawY = posInScroll.Y - visibleUpperBound;
|
||||
@ -953,13 +1000,13 @@ namespace osu.Game.Screens.Select
|
||||
/// </summary>
|
||||
private class CarouselBoundsItem : CarouselItem
|
||||
{
|
||||
public override DrawableCarouselItem CreateDrawableRepresentation() =>
|
||||
throw new NotImplementedException();
|
||||
public override DrawableCarouselItem CreateDrawableRepresentation() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class CarouselRoot : CarouselGroupEagerSelect
|
||||
{
|
||||
private readonly BeatmapCarousel carousel;
|
||||
// May only be null during construction (State.Value set causes PerformSelection to be triggered).
|
||||
private readonly BeatmapCarousel? carousel;
|
||||
|
||||
public readonly Dictionary<Guid, CarouselBeatmapSet> BeatmapSetsByID = new Dictionary<Guid, CarouselBeatmapSet>();
|
||||
|
||||
@ -980,7 +1027,7 @@ namespace osu.Game.Screens.Select
|
||||
base.AddItem(i);
|
||||
}
|
||||
|
||||
public CarouselBeatmapSet RemoveChild(Guid beatmapSetID)
|
||||
public CarouselBeatmapSet? RemoveChild(Guid beatmapSetID)
|
||||
{
|
||||
if (BeatmapSetsByID.TryGetValue(beatmapSetID, out var carouselBeatmapSet))
|
||||
{
|
||||
|
@ -1,43 +1,27 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class BeatmapClearScoresDialog : PopupDialog
|
||||
public class BeatmapClearScoresDialog : DeleteConfirmationDialog
|
||||
{
|
||||
[Resolved]
|
||||
private ScoreManager scoreManager { get; set; }
|
||||
private ScoreManager scoreManager { get; set; } = null!;
|
||||
|
||||
public BeatmapClearScoresDialog(BeatmapInfo beatmapInfo, Action onCompletion)
|
||||
{
|
||||
BodyText = beatmapInfo.GetDisplayTitle();
|
||||
Icon = FontAwesome.Solid.Eraser;
|
||||
HeaderText = @"Clearing all local scores. Are you sure?";
|
||||
Buttons = new PopupDialogButton[]
|
||||
BodyText = $"All local scores on {beatmapInfo.GetDisplayTitle()}";
|
||||
DeleteAction = () =>
|
||||
{
|
||||
new PopupDialogOkButton
|
||||
{
|
||||
Text = @"Yes. Please.",
|
||||
Action = () =>
|
||||
{
|
||||
Task.Run(() => scoreManager.Delete(beatmapInfo))
|
||||
.ContinueWith(_ => onCompletion);
|
||||
}
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = @"No, I'm still attached.",
|
||||
},
|
||||
Task.Run(() => scoreManager.Delete(beatmapInfo))
|
||||
.ContinueWith(_ => onCompletion);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class BeatmapDeleteDialog : PopupDialog
|
||||
public class BeatmapDeleteDialog : DeleteConfirmationDialog
|
||||
{
|
||||
private BeatmapManager manager;
|
||||
private readonly BeatmapSetInfo beatmapSet;
|
||||
|
||||
public BeatmapDeleteDialog(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
this.beatmapSet = beatmapSet;
|
||||
BodyText = $@"{beatmapSet.Metadata.Artist} - {beatmapSet.Metadata.Title}";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmapManager)
|
||||
{
|
||||
manager = beatmapManager;
|
||||
}
|
||||
|
||||
public BeatmapDeleteDialog(BeatmapSetInfo beatmap)
|
||||
{
|
||||
BodyText = $@"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}";
|
||||
|
||||
Icon = FontAwesome.Regular.TrashAlt;
|
||||
HeaderText = @"Confirm deletion of";
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
new PopupDialogDangerousButton
|
||||
{
|
||||
Text = @"Yes. Totally. Delete it.",
|
||||
Action = () => manager?.Delete(beatmap),
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = @"Firetruck, I didn't mean to!",
|
||||
},
|
||||
};
|
||||
DeleteAction = () => beatmapManager.Delete(beatmapSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +55,6 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(BeatmapInfo.Metadata.Artist) ||
|
||||
criteria.Artist.Matches(BeatmapInfo.Metadata.ArtistUnicode);
|
||||
|
||||
match &= criteria.Sort != SortMode.DateRanked || BeatmapInfo.BeatmapSet?.DateRanked != null;
|
||||
|
||||
match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(BeatmapInfo.StarRating);
|
||||
|
||||
if (match && criteria.SearchTerms.Length > 0)
|
||||
@ -76,7 +74,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
}
|
||||
|
||||
if (match)
|
||||
match &= criteria.Collection?.BeatmapHashes.Contains(BeatmapInfo.MD5Hash) ?? true;
|
||||
match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true;
|
||||
|
||||
if (match && criteria.RulesetCriteria != null)
|
||||
match &= criteria.RulesetCriteria.Matches(BeatmapInfo);
|
||||
|
@ -99,6 +99,13 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
case SortMode.Difficulty:
|
||||
return compareUsingAggregateMax(otherSet, b => b.StarRating);
|
||||
|
||||
case SortMode.DateSubmitted:
|
||||
// Beatmaps which have no submitted date should already be filtered away in this mode.
|
||||
if (BeatmapSet.DateSubmitted == null || otherSet.BeatmapSet.DateSubmitted == null)
|
||||
return 0;
|
||||
|
||||
return otherSet.BeatmapSet.DateSubmitted.Value.CompareTo(BeatmapSet.DateSubmitted.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +129,13 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
public override void Filter(FilterCriteria criteria)
|
||||
{
|
||||
base.Filter(criteria);
|
||||
Filtered.Value = Items.All(i => i.Filtered.Value);
|
||||
|
||||
bool filtered = Items.All(i => i.Filtered.Value);
|
||||
|
||||
filtered |= criteria.Sort == SortMode.DateRanked && BeatmapSet?.DateRanked == null;
|
||||
filtered |= criteria.Sort == SortMode.DateSubmitted && BeatmapSet?.DateSubmitted == null;
|
||||
|
||||
Filtered.Value = filtered;
|
||||
}
|
||||
|
||||
public override string ToString() => BeatmapSet.ToString();
|
||||
|
@ -2,12 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
/// <summary>
|
||||
/// A group which ensures only one child is selected.
|
||||
/// A group which ensures only one item is selected.
|
||||
/// </summary>
|
||||
public class CarouselGroup : CarouselItem
|
||||
{
|
||||
@ -15,16 +14,15 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
public IReadOnlyList<CarouselItem> Items => items;
|
||||
|
||||
private List<CarouselItem> items = new List<CarouselItem>();
|
||||
private readonly List<CarouselItem> items = new List<CarouselItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to assign a monotonically increasing ID to children as they are added. This member is
|
||||
/// incremented whenever a child is added.
|
||||
/// Used to assign a monotonically increasing ID to items as they are added. This member is
|
||||
/// incremented whenever an item is added.
|
||||
/// </summary>
|
||||
private ulong currentChildID;
|
||||
private ulong currentItemID;
|
||||
|
||||
private Comparer<CarouselItem>? criteriaComparer;
|
||||
|
||||
private FilterCriteria? lastCriteria;
|
||||
|
||||
protected int GetIndexOfItem(CarouselItem lastSelected) => items.IndexOf(lastSelected);
|
||||
@ -41,7 +39,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
public virtual void AddItem(CarouselItem i)
|
||||
{
|
||||
i.State.ValueChanged += state => ChildItemStateChanged(i, state.NewValue);
|
||||
i.ChildID = ++currentChildID;
|
||||
i.ItemID = ++currentItemID;
|
||||
|
||||
if (lastCriteria != null)
|
||||
{
|
||||
@ -88,9 +86,16 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
items.ForEach(c => c.Filter(criteria));
|
||||
|
||||
// IEnumerable<T>.OrderBy() is used instead of List<T>.Sort() to ensure sorting stability
|
||||
criteriaComparer = Comparer<CarouselItem>.Create((x, y) => x.CompareTo(criteria, y));
|
||||
items = items.OrderBy(c => c, criteriaComparer).ToList();
|
||||
criteriaComparer = Comparer<CarouselItem>.Create((x, y) =>
|
||||
{
|
||||
int comparison = x.CompareTo(criteria, y);
|
||||
if (comparison != 0)
|
||||
return comparison;
|
||||
|
||||
return x.ItemID.CompareTo(y.ItemID);
|
||||
});
|
||||
|
||||
items.Sort(criteriaComparer);
|
||||
|
||||
lastCriteria = criteria;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ using System.Linq;
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
/// <summary>
|
||||
/// A group which ensures at least one child is selected (if the group itself is selected).
|
||||
/// A group which ensures at least one item is selected (if the group itself is selected).
|
||||
/// </summary>
|
||||
public class CarouselGroupEagerSelect : CarouselGroup
|
||||
{
|
||||
@ -35,16 +35,16 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
/// <summary>
|
||||
/// To avoid overhead during filter operations, we don't attempt any selections until after all
|
||||
/// children have been filtered. This bool will be true during the base <see cref="Filter(FilterCriteria)"/>
|
||||
/// items have been filtered. This bool will be true during the base <see cref="Filter(FilterCriteria)"/>
|
||||
/// operation.
|
||||
/// </summary>
|
||||
private bool filteringChildren;
|
||||
private bool filteringItems;
|
||||
|
||||
public override void Filter(FilterCriteria criteria)
|
||||
{
|
||||
filteringChildren = true;
|
||||
filteringItems = true;
|
||||
base.Filter(criteria);
|
||||
filteringChildren = false;
|
||||
filteringItems = false;
|
||||
|
||||
attemptSelection();
|
||||
}
|
||||
@ -97,12 +97,12 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
private void attemptSelection()
|
||||
{
|
||||
if (filteringChildren) return;
|
||||
if (filteringItems) return;
|
||||
|
||||
// we only perform eager selection if we are a currently selected group.
|
||||
if (State.Value != CarouselItemState.Selected) return;
|
||||
|
||||
// we only perform eager selection if none of our children are in a selected state already.
|
||||
// we only perform eager selection if none of our items are in a selected state already.
|
||||
if (Items.Any(i => i.State.Value == CarouselItemState.Selected)) return;
|
||||
|
||||
PerformSelection();
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
/// <summary>
|
||||
/// Used as a default sort method for <see cref="CarouselItem"/>s of differing types.
|
||||
/// </summary>
|
||||
internal ulong ChildID;
|
||||
internal ulong ItemID;
|
||||
|
||||
/// <summary>
|
||||
/// Create a fresh drawable version of this item.
|
||||
@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
}
|
||||
|
||||
public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => ChildID.CompareTo(other.ChildID);
|
||||
public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => ItemID.CompareTo(other.ItemID);
|
||||
|
||||
public int CompareTo(CarouselItem other) => CarouselYPosition.CompareTo(other.CarouselYPosition);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Collections;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -63,12 +64,12 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private CollectionManager collectionManager { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ManageCollectionsDialog manageCollectionsDialog { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; }
|
||||
|
||||
private IBindable<StarDifficulty?> starDifficultyBindable;
|
||||
private CancellationTokenSource starDifficultyCancellationSource;
|
||||
|
||||
@ -237,14 +238,11 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
if (beatmapInfo.OnlineID > 0 && beatmapOverlay != null)
|
||||
items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => beatmapOverlay.FetchAndShowBeatmap(beatmapInfo.OnlineID)));
|
||||
|
||||
if (collectionManager != null)
|
||||
{
|
||||
var collectionItems = collectionManager.Collections.Select(c => new CollectionToggleMenuItem(c, beatmapInfo)).Cast<OsuMenuItem>().ToList();
|
||||
if (manageCollectionsDialog != null)
|
||||
collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
|
||||
var collectionItems = realm.Realm.All<BeatmapCollection>().AsEnumerable().Select(c => new CollectionToggleMenuItem(c.ToLive(realm), beatmapInfo)).Cast<OsuMenuItem>().ToList();
|
||||
if (manageCollectionsDialog != null)
|
||||
collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
|
||||
|
||||
items.Add(new OsuMenuItem("Collections") { Items = collectionItems });
|
||||
}
|
||||
items.Add(new OsuMenuItem("Collections") { Items = collectionItems });
|
||||
|
||||
if (hideRequested != null)
|
||||
items.Add(new OsuMenuItem(CommonStrings.ButtonsHide.ToSentence(), MenuItemType.Destructive, () => hideRequested(beatmapInfo)));
|
||||
|
@ -17,6 +17,7 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Collections;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
@ -32,12 +33,12 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private CollectionManager collectionManager { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ManageCollectionsDialog manageCollectionsDialog { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; }
|
||||
|
||||
public IEnumerable<DrawableCarouselItem> DrawableBeatmaps => beatmapContainer?.IsLoaded != true ? Enumerable.Empty<DrawableCarouselItem>() : beatmapContainer.AliveChildren;
|
||||
|
||||
[CanBeNull]
|
||||
@ -223,14 +224,11 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
if (beatmapSet.OnlineID > 0 && viewDetails != null)
|
||||
items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineID)));
|
||||
|
||||
if (collectionManager != null)
|
||||
{
|
||||
var collectionItems = collectionManager.Collections.Select(createCollectionMenuItem).ToList();
|
||||
if (manageCollectionsDialog != null)
|
||||
collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
|
||||
var collectionItems = realm.Realm.All<BeatmapCollection>().AsEnumerable().Select(createCollectionMenuItem).ToList();
|
||||
if (manageCollectionsDialog != null)
|
||||
collectionItems.Add(new OsuMenuItem("Manage...", MenuItemType.Standard, manageCollectionsDialog.Show));
|
||||
|
||||
items.Add(new OsuMenuItem("Collections") { Items = collectionItems });
|
||||
}
|
||||
items.Add(new OsuMenuItem("Collections") { Items = collectionItems });
|
||||
|
||||
if (beatmapSet.Beatmaps.Any(b => b.Hidden))
|
||||
items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => restoreHiddenRequested(beatmapSet)));
|
||||
@ -247,7 +245,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
TernaryState state;
|
||||
|
||||
int countExisting = beatmapSet.Beatmaps.Count(b => collection.BeatmapHashes.Contains(b.MD5Hash));
|
||||
int countExisting = beatmapSet.Beatmaps.Count(b => collection.BeatmapMD5Hashes.Contains(b.MD5Hash));
|
||||
|
||||
if (countExisting == beatmapSet.Beatmaps.Count)
|
||||
state = TernaryState.True;
|
||||
@ -256,24 +254,29 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
else
|
||||
state = TernaryState.False;
|
||||
|
||||
return new TernaryStateToggleMenuItem(collection.Name.Value, MenuItemType.Standard, s =>
|
||||
var liveCollection = collection.ToLive(realm);
|
||||
|
||||
return new TernaryStateToggleMenuItem(collection.Name, MenuItemType.Standard, s =>
|
||||
{
|
||||
foreach (var b in beatmapSet.Beatmaps)
|
||||
liveCollection.PerformWrite(c =>
|
||||
{
|
||||
switch (s)
|
||||
foreach (var b in beatmapSet.Beatmaps)
|
||||
{
|
||||
case TernaryState.True:
|
||||
if (collection.BeatmapHashes.Contains(b.MD5Hash))
|
||||
continue;
|
||||
switch (s)
|
||||
{
|
||||
case TernaryState.True:
|
||||
if (c.BeatmapMD5Hashes.Contains(b.MD5Hash))
|
||||
continue;
|
||||
|
||||
collection.BeatmapHashes.Add(b.MD5Hash);
|
||||
break;
|
||||
c.BeatmapMD5Hashes.Add(b.MD5Hash);
|
||||
break;
|
||||
|
||||
case TernaryState.False:
|
||||
collection.BeatmapHashes.Remove(b.MD5Hash);
|
||||
break;
|
||||
case TernaryState.False:
|
||||
c.BeatmapMD5Hashes.Remove(b.MD5Hash);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
{
|
||||
State = { Value = state }
|
||||
|
@ -3,11 +3,8 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -39,7 +36,6 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private IDisposable? scoreSubscription;
|
||||
private CancellationTokenSource? scoreOrderCancellationSource;
|
||||
|
||||
private readonly UpdateableRank updateable;
|
||||
|
||||
@ -83,25 +79,16 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
if (changes?.HasCollectionChanges() == false)
|
||||
return;
|
||||
|
||||
scoreOrderCancellationSource?.Cancel();
|
||||
ScoreInfo? topScore = scoreManager.OrderByTotalScore(sender.Detach()).FirstOrDefault();
|
||||
|
||||
scoreManager.OrderByTotalScoreAsync(sender.Detach().ToArray(), (scoreOrderCancellationSource = new CancellationTokenSource()).Token)
|
||||
.ContinueWith(ordered => Schedule(() =>
|
||||
{
|
||||
if (scoreOrderCancellationSource.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
updateable.Rank = ordered.GetResultSafely().FirstOrDefault()?.Rank;
|
||||
updateable.Alpha = updateable.Rank != null ? 1 : 0;
|
||||
}), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
updateable.Rank = topScore?.Rank;
|
||||
updateable.Alpha = topScore != null ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
scoreOrderCancellationSource?.Cancel();
|
||||
scoreSubscription?.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
Action = () =>
|
||||
{
|
||||
beatmapDownloader.Download(beatmapSetInfo);
|
||||
beatmapDownloader.DownloadAsUpdate(beatmapSetInfo);
|
||||
attachExistingDownload();
|
||||
};
|
||||
}
|
||||
|
@ -65,6 +65,13 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private class MinimumStarsSlider : StarsSlider
|
||||
{
|
||||
public MinimumStarsSlider()
|
||||
: base("0")
|
||||
{
|
||||
}
|
||||
|
||||
public override LocalisableString TooltipText => Current.Value.ToString(@"0.## stars");
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -82,6 +89,11 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private class MaximumStarsSlider : StarsSlider
|
||||
{
|
||||
public MaximumStarsSlider()
|
||||
: base("∞")
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -96,10 +108,17 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private class StarsSlider : OsuSliderBar<double>
|
||||
{
|
||||
private readonly string defaultString;
|
||||
|
||||
public override LocalisableString TooltipText => Current.IsDefault
|
||||
? UserInterfaceStrings.NoLimit
|
||||
: Current.Value.ToString(@"0.## stars");
|
||||
|
||||
protected StarsSlider(string defaultString)
|
||||
{
|
||||
this.defaultString = defaultString;
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
@ -125,7 +144,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
Current.BindValueChanged(current =>
|
||||
{
|
||||
currentDisplay.Text = current.NewValue != Current.Default ? current.NewValue.ToString("N1") : "∞";
|
||||
currentDisplay.Text = current.NewValue != Current.Default ? current.NewValue.ToString("N1") : defaultString;
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ namespace osu.Game.Screens.Select.Filter
|
||||
[LocalisableDescription(typeof(SortStrings), nameof(SortStrings.ArtistTracksBpm))]
|
||||
BPM,
|
||||
|
||||
[Description("Date Submitted")]
|
||||
DateSubmitted,
|
||||
|
||||
[Description("Date Added")]
|
||||
DateAdded,
|
||||
|
||||
|
@ -39,6 +39,10 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private Bindable<GroupMode> groupMode;
|
||||
|
||||
private SeekLimitedSearchTextBox searchTextBox;
|
||||
|
||||
private CollectionDropdown collectionDropdown;
|
||||
|
||||
public FilterCriteria CreateCriteria()
|
||||
{
|
||||
string query = searchTextBox.Text;
|
||||
@ -49,7 +53,7 @@ namespace osu.Game.Screens.Select
|
||||
Sort = sortMode.Value,
|
||||
AllowConvertedBeatmaps = showConverted.Value,
|
||||
Ruleset = ruleset.Value,
|
||||
Collection = collectionDropdown?.Current.Value?.Collection
|
||||
CollectionBeatmapMD5Hashes = collectionDropdown.Current.Value?.Collection?.PerformRead(c => c.BeatmapMD5Hashes)
|
||||
};
|
||||
|
||||
if (!minimumStars.IsDefault)
|
||||
@ -64,10 +68,6 @@ namespace osu.Game.Screens.Select
|
||||
return criteria;
|
||||
}
|
||||
|
||||
private SeekLimitedSearchTextBox searchTextBox;
|
||||
|
||||
private CollectionFilterDropdown collectionDropdown;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
||||
base.ReceivePositionalInputAt(screenSpacePos) || sortTabs.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
@ -179,10 +179,11 @@ namespace osu.Game.Screens.Select
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.48f,
|
||||
},
|
||||
collectionDropdown = new CollectionFilterDropdown
|
||||
collectionDropdown = new CollectionDropdown
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RequestFilter = updateCriteria,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Y = 4,
|
||||
Width = 0.5f,
|
||||
@ -209,15 +210,6 @@ namespace osu.Game.Screens.Select
|
||||
groupMode.BindValueChanged(_ => updateCriteria());
|
||||
sortMode.BindValueChanged(_ => updateCriteria());
|
||||
|
||||
collectionDropdown.Current.ValueChanged += val =>
|
||||
{
|
||||
if (val.NewValue == null)
|
||||
// may be null briefly while menu is repopulated.
|
||||
return;
|
||||
|
||||
updateCriteria();
|
||||
};
|
||||
|
||||
searchTextBox.Current.ValueChanged += _ => updateCriteria();
|
||||
|
||||
updateCriteria();
|
||||
|
@ -68,10 +68,10 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection to filter beatmaps from.
|
||||
/// Hashes from the <see cref="BeatmapCollection"/> to filter to.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public BeatmapCollection Collection;
|
||||
public IEnumerable<string> CollectionBeatmapMD5Hashes { get; set; }
|
||||
|
||||
[CanBeNull]
|
||||
public IRulesetFilterCriteria RulesetCriteria { get; set; }
|
||||
|
@ -8,10 +8,8 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
@ -150,17 +148,12 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
var req = new GetScoresRequest(fetchBeatmapInfo, fetchRuleset, Scope, requestMods);
|
||||
|
||||
req.Success += r =>
|
||||
req.Success += r => Schedule(() =>
|
||||
{
|
||||
scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.ToScoreInfo(rulesets, fetchBeatmapInfo)).ToArray(), cancellationToken)
|
||||
.ContinueWith(task => Schedule(() =>
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
SetScores(task.GetResultSafely(), r.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo));
|
||||
}), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
};
|
||||
SetScores(
|
||||
scoreManager.OrderByTotalScore(r.Scores.Select(s => s.ToScoreInfo(rulesets, fetchBeatmapInfo))),
|
||||
r.UserScore?.CreateScoreInfo(rulesets, fetchBeatmapInfo));
|
||||
});
|
||||
|
||||
return req;
|
||||
}
|
||||
@ -213,16 +206,9 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym)));
|
||||
}
|
||||
|
||||
scores = scores.Detach();
|
||||
scores = scoreManager.OrderByTotalScore(scores.Detach());
|
||||
|
||||
scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken)
|
||||
.ContinueWith(ordered => Schedule(() =>
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
SetScores(ordered.GetResultSafely());
|
||||
}), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
Schedule(() => SetScores(scores));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Scoring;
|
||||
@ -12,44 +10,25 @@ using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class LocalScoreDeleteDialog : PopupDialog
|
||||
public class LocalScoreDeleteDialog : DeleteConfirmationDialog
|
||||
{
|
||||
private readonly ScoreInfo score;
|
||||
|
||||
[Resolved]
|
||||
private ScoreManager scoreManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
|
||||
public LocalScoreDeleteDialog(ScoreInfo score)
|
||||
{
|
||||
this.score = score;
|
||||
Debug.Assert(score != null);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(BeatmapManager beatmapManager, ScoreManager scoreManager)
|
||||
{
|
||||
BeatmapInfo beatmapInfo = beatmapManager.QueryBeatmap(b => b.ID == score.BeatmapInfoID);
|
||||
BeatmapInfo? beatmapInfo = beatmapManager.QueryBeatmap(b => b.ID == score.BeatmapInfoID);
|
||||
Debug.Assert(beatmapInfo != null);
|
||||
|
||||
BodyText = $"{score.User} ({score.DisplayAccuracy}, {score.Rank})";
|
||||
|
||||
Icon = FontAwesome.Regular.TrashAlt;
|
||||
HeaderText = "Confirm deletion of local score";
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
new PopupDialogDangerousButton
|
||||
{
|
||||
Text = "Yes. Please.",
|
||||
Action = () => scoreManager?.Delete(score)
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = "No, I'm still attached.",
|
||||
},
|
||||
};
|
||||
DeleteAction = () => scoreManager.Delete(score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,10 +127,10 @@ namespace osu.Game.Screens.Select
|
||||
config.SetValue(OsuSetting.DisplayStarsMaximum, 10.1);
|
||||
});
|
||||
|
||||
string lowerStar = filter.UserStarDifficulty.Min == null ? "∞" : $"{filter.UserStarDifficulty.Min:N1}";
|
||||
string lowerStar = $"{filter.UserStarDifficulty.Min ?? 0:N1}";
|
||||
string upperStar = filter.UserStarDifficulty.Max == null ? "∞" : $"{filter.UserStarDifficulty.Max:N1}";
|
||||
|
||||
textFlow.AddText($" the {lowerStar}-{upperStar} star difficulty filter.");
|
||||
textFlow.AddText($" the {lowerStar} - {upperStar} star difficulty filter.");
|
||||
}
|
||||
|
||||
// TODO: Add realm queries to hint at which ruleset results are available in (and allow clicking to switch).
|
||||
|
@ -1,43 +1,29 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class SkinDeleteDialog : PopupDialog
|
||||
public class SkinDeleteDialog : DeleteConfirmationDialog
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager manager { get; set; }
|
||||
private readonly Skin skin;
|
||||
|
||||
public SkinDeleteDialog(Skin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
BodyText = skin.SkinInfo.Value.Name;
|
||||
Icon = FontAwesome.Regular.TrashAlt;
|
||||
HeaderText = @"Confirm deletion of";
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
new PopupDialogDangerousButton
|
||||
{
|
||||
Text = @"Yes. Totally. Delete it.",
|
||||
Action = () =>
|
||||
{
|
||||
if (manager == null)
|
||||
return;
|
||||
}
|
||||
|
||||
manager.Delete(skin.SkinInfo.Value);
|
||||
manager.CurrentSkinInfo.SetDefault();
|
||||
},
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = @"Firetruck, I didn't mean to!",
|
||||
},
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(SkinManager manager)
|
||||
{
|
||||
DeleteAction = () =>
|
||||
{
|
||||
manager.Delete(skin.SkinInfo.Value);
|
||||
manager.CurrentSkinInfo.SetDefault();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ namespace osu.Game.Screens.Select
|
||||
(new FooterButtonOptions(), BeatmapOptions)
|
||||
};
|
||||
|
||||
protected virtual ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay();
|
||||
protected virtual ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay();
|
||||
|
||||
protected virtual void ApplyFilterToCarousel(FilterCriteria criteria)
|
||||
{
|
||||
@ -405,20 +405,21 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private ScheduledDelegate selectionChangedDebounce;
|
||||
|
||||
private void workingBeatmapChanged(ValueChangedEvent<WorkingBeatmap> e)
|
||||
private void updateCarouselSelection(ValueChangedEvent<WorkingBeatmap> e = null)
|
||||
{
|
||||
if (e.NewValue is DummyWorkingBeatmap || !this.IsCurrentScreen()) return;
|
||||
var beatmap = e?.NewValue ?? Beatmap.Value;
|
||||
if (beatmap is DummyWorkingBeatmap || !this.IsCurrentScreen()) return;
|
||||
|
||||
Logger.Log($"Song select working beatmap updated to {e.NewValue}");
|
||||
Logger.Log($"Song select working beatmap updated to {beatmap}");
|
||||
|
||||
if (!Carousel.SelectBeatmap(e.NewValue.BeatmapInfo, false))
|
||||
if (!Carousel.SelectBeatmap(beatmap.BeatmapInfo, false))
|
||||
{
|
||||
// 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.Equals(decoupledRuleset.Value))
|
||||
if (!beatmap.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value))
|
||||
{
|
||||
Ruleset.Value = e.NewValue.BeatmapInfo.Ruleset;
|
||||
Ruleset.Value = beatmap.BeatmapInfo.Ruleset;
|
||||
transferRulesetValue();
|
||||
}
|
||||
|
||||
@ -426,10 +427,10 @@ namespace osu.Game.Screens.Select
|
||||
// we still want to temporarily show the new beatmap, bypassing filters.
|
||||
// This will be undone the next time the user changes the filter.
|
||||
var criteria = FilterControl.CreateCriteria();
|
||||
criteria.SelectedBeatmapSet = e.NewValue.BeatmapInfo.BeatmapSet;
|
||||
criteria.SelectedBeatmapSet = beatmap.BeatmapInfo.BeatmapSet;
|
||||
Carousel.Filter(criteria);
|
||||
|
||||
Carousel.SelectBeatmap(e.NewValue.BeatmapInfo);
|
||||
Carousel.SelectBeatmap(beatmap.BeatmapInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +598,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
|
||||
{
|
||||
updateCarouselSelection();
|
||||
|
||||
updateComponentFromBeatmap(Beatmap.Value);
|
||||
|
||||
if (ControlGlobalMusic)
|
||||
@ -680,7 +683,7 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
private void ensureTrackLooping(IWorkingBeatmap beatmap, TrackChangeDirection changeDirection)
|
||||
=> beatmap.PrepareTrackForPreviewLooping();
|
||||
=> beatmap.PrepareTrackForPreview(true);
|
||||
|
||||
public override bool OnBackButton()
|
||||
{
|
||||
@ -805,7 +808,7 @@ namespace osu.Game.Screens.Select
|
||||
};
|
||||
decoupledRuleset.DisabledChanged += r => Ruleset.Disabled = r;
|
||||
|
||||
Beatmap.BindValueChanged(workingBeatmapChanged);
|
||||
Beatmap.BindValueChanged(updateCarouselSelection);
|
||||
|
||||
boundLocalBindables = true;
|
||||
}
|
||||
@ -924,5 +927,10 @@ namespace osu.Game.Screens.Select
|
||||
return base.OnHover(e);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SoloModSelectOverlay : UserModSelectOverlay
|
||||
{
|
||||
protected override bool ShowPresets => true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user