mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Initial carousel infrastructue changes
This commit is contained in:
@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK.Input;
|
||||
@ -15,17 +14,19 @@ using osu.Framework.MathUtils;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class BeatmapCarousel : OsuScrollContainer
|
||||
{
|
||||
public BeatmapInfo SelectedBeatmap => selectedPanel?.Beatmap;
|
||||
public BeatmapInfo SelectedBeatmap => selectedBeatmap?.Beatmap;
|
||||
|
||||
public override bool HandleInput => AllowSelection;
|
||||
|
||||
@ -33,27 +34,30 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> Beatmaps
|
||||
{
|
||||
get { return groups.Select(g => g.BeatmapSet); }
|
||||
get { return carouselSets.Select(g => g.BeatmapSet); }
|
||||
set
|
||||
{
|
||||
scrollableContent.Clear(false);
|
||||
panels.Clear();
|
||||
groups.Clear();
|
||||
items.Clear();
|
||||
carouselSets.Clear();
|
||||
|
||||
List<BeatmapGroup> newGroups = null;
|
||||
List<CarouselBeatmapSet> newSets = null;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
newGroups = value.Select(createGroup).Where(g => g != null).ToList();
|
||||
criteria.Filter(newGroups);
|
||||
newSets = value.Select(createGroup).Where(g => g != null).ToList();
|
||||
newSets.ForEach(g => g.Filter(criteria));
|
||||
}).ContinueWith(t =>
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
foreach (var g in newGroups)
|
||||
foreach (var g in newSets)
|
||||
addGroup(g);
|
||||
|
||||
computeYPositions();
|
||||
root = new CarouselGroup(newSets.OfType<CarouselItem>().ToList());
|
||||
items = root.Drawables.Value.ToList();
|
||||
|
||||
yPositionsCache.Invalidate();
|
||||
BeatmapsChanged?.Invoke();
|
||||
});
|
||||
});
|
||||
@ -62,24 +66,19 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private readonly List<float> yPositions = new List<float>();
|
||||
|
||||
/// <summary>
|
||||
/// Required for now unfortunately.
|
||||
/// </summary>
|
||||
private BeatmapManager manager;
|
||||
private readonly Container<DrawableCarouselItem> scrollableContent;
|
||||
|
||||
private readonly Container<Panel> scrollableContent;
|
||||
|
||||
private readonly List<BeatmapGroup> groups = new List<BeatmapGroup>();
|
||||
private readonly List<CarouselBeatmapSet> carouselSets = new List<CarouselBeatmapSet>();
|
||||
|
||||
private Bindable<SelectionRandomType> randomType;
|
||||
private readonly List<BeatmapGroup> seenGroups = new List<BeatmapGroup>();
|
||||
private readonly List<CarouselBeatmapSet> seenSets = new List<CarouselBeatmapSet>();
|
||||
|
||||
private readonly List<Panel> panels = new List<Panel>();
|
||||
private List<DrawableCarouselItem> items = new List<DrawableCarouselItem>();
|
||||
private CarouselGroup root = new CarouselGroup();
|
||||
|
||||
private readonly Stack<KeyValuePair<BeatmapGroup, BeatmapPanel>> randomSelectedBeatmaps = new Stack<KeyValuePair<BeatmapGroup, BeatmapPanel>>();
|
||||
private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>();
|
||||
|
||||
private BeatmapGroup selectedGroup;
|
||||
private BeatmapPanel selectedPanel;
|
||||
private CarouselBeatmap selectedBeatmap;
|
||||
|
||||
public BeatmapCarousel()
|
||||
{
|
||||
@ -87,7 +86,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = scrollableContent = new Container<Panel>
|
||||
Child = scrollableContent = new Container<DrawableCarouselItem>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
}
|
||||
@ -96,45 +95,44 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public void RemoveBeatmap(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID)));
|
||||
Schedule(() => removeGroup(carouselSets.Find(b => b.BeatmapSet.ID == beatmapSet.ID)));
|
||||
}
|
||||
|
||||
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
// todo: this method should be smarter as to not recreate panels that haven't changed, etc.
|
||||
var oldGroup = groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID);
|
||||
// todo: this method should be smarter as to not recreate items that haven't changed, etc.
|
||||
var oldGroup = carouselSets.Find(b => b.BeatmapSet.ID == beatmapSet.ID);
|
||||
|
||||
var newGroup = createGroup(beatmapSet);
|
||||
bool hadSelection = oldGroup?.State == CarouselItemState.Selected;
|
||||
|
||||
int index = groups.IndexOf(oldGroup);
|
||||
var newSet = createGroup(beatmapSet);
|
||||
|
||||
int index = carouselSets.IndexOf(oldGroup);
|
||||
if (index >= 0)
|
||||
groups.RemoveAt(index);
|
||||
carouselSets.RemoveAt(index);
|
||||
|
||||
if (newGroup != null)
|
||||
if (newSet != null)
|
||||
{
|
||||
if (index >= 0)
|
||||
groups.Insert(index, newGroup);
|
||||
carouselSets.Insert(index, newSet);
|
||||
else
|
||||
addGroup(newGroup);
|
||||
addGroup(newSet);
|
||||
}
|
||||
|
||||
bool hadSelection = selectedGroup == oldGroup;
|
||||
|
||||
if (hadSelection && newGroup == null)
|
||||
selectedGroup = null;
|
||||
if (hadSelection && newSet == null)
|
||||
SelectNext();
|
||||
|
||||
Filter(null, false);
|
||||
|
||||
//check if we can/need to maintain our current selection.
|
||||
if (hadSelection && newGroup != null)
|
||||
if (hadSelection && newSet != null)
|
||||
{
|
||||
var newSelection =
|
||||
newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID);
|
||||
var newSelection = newSet.Beatmaps.Find(b => b.Beatmap.ID == selectedBeatmap?.Beatmap.ID);
|
||||
|
||||
if (newSelection == null && oldGroup != null && selectedPanel != null)
|
||||
newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, oldGroup.BeatmapPanels.IndexOf(selectedPanel))];
|
||||
if (newSelection == null && selectedBeatmap != null)
|
||||
newSelection = newSet.Beatmaps[Math.Min(newSet.Beatmaps.Count - 1, oldGroup.Beatmaps.IndexOf(selectedBeatmap))];
|
||||
|
||||
selectGroup(newGroup, newSelection);
|
||||
select(newSelection);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,12 +146,12 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (beatmap == SelectedBeatmap) return;
|
||||
|
||||
foreach (BeatmapGroup group in groups)
|
||||
foreach (CarouselBeatmapSet group in carouselSets)
|
||||
{
|
||||
var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
|
||||
if (panel != null)
|
||||
var item = group.Beatmaps.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
|
||||
if (item != null)
|
||||
{
|
||||
selectGroup(group, panel, animated);
|
||||
select(item, animated);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -161,20 +159,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public Action<BeatmapInfo> SelectionChanged;
|
||||
|
||||
public Action StartRequested;
|
||||
|
||||
public Action<BeatmapSetInfo> DeleteRequested;
|
||||
|
||||
public Action<BeatmapSetInfo> RestoreRequested;
|
||||
|
||||
public Action<BeatmapInfo> EditRequested;
|
||||
|
||||
public Action<BeatmapInfo> HideDifficultyRequested;
|
||||
|
||||
private void selectNullBeatmap()
|
||||
{
|
||||
selectedGroup = null;
|
||||
selectedPanel = null;
|
||||
selectedBeatmap = null;
|
||||
SelectionChanged?.Invoke(null);
|
||||
}
|
||||
|
||||
@ -186,90 +173,71 @@ namespace osu.Game.Screens.Select
|
||||
public void SelectNext(int direction = 1, bool skipDifficulties = true)
|
||||
{
|
||||
// todo: we may want to refactor and remove this as an optimisation in the future.
|
||||
if (groups.All(g => g.State == BeatmapGroupState.Hidden))
|
||||
if (carouselSets.All(g => g.State == CarouselItemState.Hidden))
|
||||
{
|
||||
selectNullBeatmap();
|
||||
return;
|
||||
}
|
||||
|
||||
int originalIndex = Math.Max(0, groups.IndexOf(selectedGroup));
|
||||
int originalIndex = Math.Max(0, items.IndexOf(selectedBeatmap?.Drawables.Value.First()));
|
||||
int currentIndex = originalIndex;
|
||||
|
||||
// local function to increment the index in the required direction, wrapping over extremities.
|
||||
int incrementIndex() => currentIndex = (currentIndex + direction + groups.Count) % groups.Count;
|
||||
int incrementIndex() => currentIndex = (currentIndex + direction + items.Count) % items.Count;
|
||||
|
||||
// in the case we are skipping difficulties, we want to increment the index once before starting to find out new target
|
||||
// (we don't care about the currently selected group).
|
||||
if (skipDifficulties)
|
||||
incrementIndex();
|
||||
|
||||
do
|
||||
while (incrementIndex() != originalIndex)
|
||||
{
|
||||
var group = groups[currentIndex];
|
||||
var item = items[currentIndex].Item;
|
||||
|
||||
if (group.State == BeatmapGroupState.Hidden) continue;
|
||||
if (item.Filtered || item.State == CarouselItemState.Selected) continue;
|
||||
|
||||
// we are only interested in non-filtered panels.
|
||||
IEnumerable<BeatmapPanel> validPanels = group.BeatmapPanels.Where(p => !p.Filtered);
|
||||
|
||||
// if we are considering difficulties, we need to do a few extrea steps.
|
||||
if (!skipDifficulties)
|
||||
switch (item)
|
||||
{
|
||||
// we want to reverse the panel order if we are searching backwards.
|
||||
if (direction < 0)
|
||||
validPanels = validPanels.Reverse();
|
||||
|
||||
// if we are currently on the selected panel, let's try to find a valid difficulty before leaving to the next group.
|
||||
// the first valid difficulty is found by skipping to the selected panel and then one further.
|
||||
if (currentIndex == originalIndex)
|
||||
validPanels = validPanels.SkipWhile(p => p != selectedPanel).Skip(1);
|
||||
case CarouselBeatmap beatmap:
|
||||
if (skipDifficulties) continue;
|
||||
select(beatmap);
|
||||
return;
|
||||
case CarouselBeatmapSet set:
|
||||
select(set);
|
||||
return;
|
||||
}
|
||||
|
||||
var next = validPanels.FirstOrDefault();
|
||||
|
||||
// at this point, we can perform the selection change if we have a valid new target, else continue to increment in the specified direction.
|
||||
if (next != null)
|
||||
{
|
||||
selectGroup(group, next);
|
||||
return;
|
||||
}
|
||||
} while (incrementIndex() != originalIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<BeatmapGroup> getVisibleGroups() => groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden);
|
||||
private IEnumerable<CarouselBeatmapSet> getVisibleGroups() => carouselSets.Where(select => select.State != CarouselItemState.NotSelected);
|
||||
|
||||
public void SelectNextRandom()
|
||||
{
|
||||
if (groups.Count == 0)
|
||||
if (carouselSets.Count == 0)
|
||||
return;
|
||||
|
||||
var visibleGroups = getVisibleGroups();
|
||||
if (!visibleGroups.Any())
|
||||
return;
|
||||
|
||||
if (selectedGroup != null)
|
||||
randomSelectedBeatmaps.Push(new KeyValuePair<BeatmapGroup, BeatmapPanel>(selectedGroup, selectedGroup.SelectedPanel));
|
||||
if (selectedBeatmap != null)
|
||||
randomSelectedBeatmaps.Push(selectedBeatmap);
|
||||
|
||||
BeatmapGroup group;
|
||||
CarouselBeatmapSet group;
|
||||
|
||||
if (randomType == SelectionRandomType.RandomPermutation)
|
||||
{
|
||||
var notSeenGroups = visibleGroups.Except(seenGroups);
|
||||
var notSeenGroups = visibleGroups.Except(seenSets);
|
||||
if (!notSeenGroups.Any())
|
||||
{
|
||||
seenGroups.Clear();
|
||||
seenSets.Clear();
|
||||
notSeenGroups = visibleGroups;
|
||||
}
|
||||
|
||||
group = notSeenGroups.ElementAt(RNG.Next(notSeenGroups.Count()));
|
||||
seenGroups.Add(group);
|
||||
seenSets.Add(group);
|
||||
}
|
||||
else
|
||||
group = visibleGroups.ElementAt(RNG.Next(visibleGroups.Count()));
|
||||
|
||||
BeatmapPanel panel = group.BeatmapPanels[RNG.Next(group.BeatmapPanels.Count)];
|
||||
CarouselBeatmap item = group.Beatmaps[RNG.Next(group.Beatmaps.Count)];
|
||||
|
||||
selectGroup(group, panel);
|
||||
select(item);
|
||||
}
|
||||
|
||||
public void SelectPreviousRandom()
|
||||
@ -277,17 +245,13 @@ namespace osu.Game.Screens.Select
|
||||
if (!randomSelectedBeatmaps.Any())
|
||||
return;
|
||||
|
||||
var visibleGroups = getVisibleGroups();
|
||||
if (!visibleGroups.Any())
|
||||
return;
|
||||
|
||||
while (randomSelectedBeatmaps.Any())
|
||||
{
|
||||
var beatmapCoordinates = randomSelectedBeatmaps.Pop();
|
||||
var group = beatmapCoordinates.Key;
|
||||
if (visibleGroups.Contains(group))
|
||||
var beatmap = randomSelectedBeatmaps.Pop();
|
||||
|
||||
if (beatmap.Visible)
|
||||
{
|
||||
selectGroup(group, beatmapCoordinates.Value);
|
||||
select(beatmap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -314,25 +278,14 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
filterTask = null;
|
||||
|
||||
criteria.Filter(groups);
|
||||
carouselSets.ForEach(s => s.Filter(criteria));
|
||||
|
||||
var filtered = new List<BeatmapGroup>(groups);
|
||||
yPositionsCache.Invalidate();
|
||||
|
||||
scrollableContent.Clear(false);
|
||||
panels.Clear();
|
||||
groups.Clear();
|
||||
|
||||
foreach (var g in filtered)
|
||||
addGroup(g);
|
||||
|
||||
computeYPositions();
|
||||
|
||||
selectedGroup?.UpdateState();
|
||||
|
||||
if (selectedGroup == null || selectedGroup.State == BeatmapGroupState.Hidden)
|
||||
if (selectedBeatmap?.Visible != true)
|
||||
SelectNext();
|
||||
else
|
||||
selectGroup(selectedGroup, selectedPanel);
|
||||
select(selectedBeatmap);
|
||||
};
|
||||
|
||||
filterTask?.Cancel();
|
||||
@ -350,54 +303,60 @@ namespace osu.Game.Screens.Select
|
||||
ScrollTo(selectedY, animated);
|
||||
}
|
||||
|
||||
private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet)
|
||||
private CarouselBeatmapSet createGroup(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
if (beatmapSet.Beatmaps.All(b => b.Hidden))
|
||||
return null;
|
||||
|
||||
// todo: remove the need for this.
|
||||
foreach (var b in beatmapSet.Beatmaps)
|
||||
{
|
||||
if (b.Metadata == null)
|
||||
b.Metadata = beatmapSet.Metadata;
|
||||
}
|
||||
|
||||
return new BeatmapGroup(beatmapSet, manager)
|
||||
var set = new CarouselBeatmapSet(beatmapSet);
|
||||
|
||||
foreach (var c in set.Beatmaps)
|
||||
{
|
||||
SelectionChanged = (g, p) => selectGroup(g, p),
|
||||
StartRequested = b => StartRequested?.Invoke(),
|
||||
DeleteRequested = b => DeleteRequested?.Invoke(b),
|
||||
RestoreHiddenRequested = s => RestoreRequested?.Invoke(s),
|
||||
EditRequested = b => EditRequested?.Invoke(b),
|
||||
HideDifficultyRequested = b => HideDifficultyRequested?.Invoke(b),
|
||||
State = BeatmapGroupState.Collapsed
|
||||
};
|
||||
c.State.ValueChanged += v =>
|
||||
{
|
||||
if (v == CarouselItemState.Selected)
|
||||
{
|
||||
selectedBeatmap = c;
|
||||
SelectionChanged?.Invoke(c.Beatmap);
|
||||
yPositionsCache.Invalidate();
|
||||
Schedule(() => ScrollToSelected());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(BeatmapManager manager, OsuConfigManager config)
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
this.manager = manager;
|
||||
|
||||
randomType = config.GetBindable<SelectionRandomType>(OsuSetting.SelectionRandomType);
|
||||
}
|
||||
|
||||
private void addGroup(BeatmapGroup group)
|
||||
private void addGroup(CarouselBeatmapSet set)
|
||||
{
|
||||
// prevent duplicates by concurrent independent actions trying to add a group
|
||||
if (groups.Any(g => g.BeatmapSet.ID == group.BeatmapSet.ID))
|
||||
//todo: check this
|
||||
if (carouselSets.Any(g => g.BeatmapSet.ID == set.BeatmapSet.ID))
|
||||
return;
|
||||
|
||||
groups.Add(group);
|
||||
panels.Add(group.Header);
|
||||
panels.AddRange(group.BeatmapPanels);
|
||||
//todo: add to root
|
||||
carouselSets.Add(set);
|
||||
}
|
||||
|
||||
private void removeGroup(BeatmapGroup group)
|
||||
private void removeGroup(CarouselBeatmapSet set)
|
||||
{
|
||||
if (group == null)
|
||||
if (set == null)
|
||||
return;
|
||||
|
||||
if (selectedGroup == group)
|
||||
if (set.State == CarouselItemState.Selected)
|
||||
{
|
||||
if (getVisibleGroups().Count() == 1)
|
||||
selectNullBeatmap();
|
||||
@ -405,21 +364,23 @@ namespace osu.Game.Screens.Select
|
||||
SelectNext();
|
||||
}
|
||||
|
||||
groups.Remove(group);
|
||||
panels.Remove(group.Header);
|
||||
foreach (var p in group.BeatmapPanels)
|
||||
panels.Remove(p);
|
||||
carouselSets.Remove(set);
|
||||
|
||||
scrollableContent.Remove(group.Header);
|
||||
scrollableContent.RemoveRange(group.BeatmapPanels);
|
||||
foreach (var d in set.Drawables.Value)
|
||||
{
|
||||
items.Remove(d);
|
||||
scrollableContent.Remove(d);
|
||||
}
|
||||
|
||||
computeYPositions();
|
||||
yPositionsCache.Invalidate();
|
||||
}
|
||||
|
||||
private Cached yPositionsCache = new Cached();
|
||||
|
||||
/// <summary>
|
||||
/// Computes the target Y positions for every panel in the carousel.
|
||||
/// Computes the target Y positions for every item in the carousel.
|
||||
/// </summary>
|
||||
/// <returns>The Y position of the currently selected panel.</returns>
|
||||
/// <returns>The Y position of the currently selected item.</returns>
|
||||
private float computeYPositions(bool animated = true)
|
||||
{
|
||||
yPositions.Clear();
|
||||
@ -427,88 +388,61 @@ namespace osu.Game.Screens.Select
|
||||
float currentY = DrawHeight / 2;
|
||||
float selectedY = currentY;
|
||||
|
||||
foreach (BeatmapGroup group in groups)
|
||||
float lastSetY = 0;
|
||||
|
||||
foreach (DrawableCarouselItem d in items)
|
||||
{
|
||||
movePanel(group.Header, group.State != BeatmapGroupState.Hidden, animated, ref currentY);
|
||||
|
||||
if (group.State == BeatmapGroupState.Expanded)
|
||||
switch (d)
|
||||
{
|
||||
group.Header.MoveToX(-100, 500, Easing.OutExpo);
|
||||
var headerY = group.Header.Position.Y;
|
||||
case DrawableCarouselBeatmapSet set:
|
||||
set.MoveToX(set.Item.State == CarouselItemState.Selected ? -100 : 0, 500, Easing.OutExpo);
|
||||
|
||||
foreach (BeatmapPanel panel in group.BeatmapPanels)
|
||||
{
|
||||
if (panel == selectedPanel)
|
||||
selectedY = currentY + panel.DrawHeight / 2 - DrawHeight / 2;
|
||||
lastSetY = set.Position.Y;
|
||||
|
||||
panel.MoveToX(-50, 500, Easing.OutExpo);
|
||||
movePanel(set, set.Item.Visible, animated, ref currentY);
|
||||
break;
|
||||
case DrawableCarouselBeatmap beatmap:
|
||||
beatmap.MoveToX(beatmap.Item.State == CarouselItemState.Selected ? -50 : 0, 500, Easing.OutExpo);
|
||||
|
||||
bool isHidden = panel.State == PanelSelectedState.Hidden;
|
||||
if (beatmap.Item == selectedBeatmap)
|
||||
selectedY = currentY + beatmap.DrawHeight / 2 - DrawHeight / 2;
|
||||
|
||||
//on first display we want to begin hidden under our group's header.
|
||||
if (isHidden || panel.Alpha == 0)
|
||||
panel.MoveToY(headerY);
|
||||
// on first display we want to begin hidden under our group's header.
|
||||
if (animated && !beatmap.IsPresent)
|
||||
beatmap.MoveToY(lastSetY);
|
||||
|
||||
movePanel(panel, !isHidden, animated, ref currentY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
group.Header.MoveToX(0, 500, Easing.OutExpo);
|
||||
|
||||
foreach (BeatmapPanel panel in group.BeatmapPanels)
|
||||
{
|
||||
panel.MoveToX(0, 500, Easing.OutExpo);
|
||||
movePanel(panel, false, animated, ref currentY);
|
||||
}
|
||||
movePanel(beatmap, beatmap.Item.Visible, animated, ref currentY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentY += DrawHeight / 2;
|
||||
scrollableContent.Height = currentY;
|
||||
|
||||
yPositionsCache.Validate();
|
||||
|
||||
return selectedY;
|
||||
}
|
||||
|
||||
private void movePanel(Panel panel, bool advance, bool animated, ref float currentY)
|
||||
private void movePanel(DrawableCarouselItem item, bool advance, bool animated, ref float currentY)
|
||||
{
|
||||
yPositions.Add(currentY);
|
||||
panel.MoveToY(currentY, animated ? 750 : 0, Easing.OutExpo);
|
||||
item.MoveToY(currentY, animated ? 750 : 0, Easing.OutExpo);
|
||||
|
||||
if (advance)
|
||||
currentY += panel.DrawHeight + 5;
|
||||
currentY += item.DrawHeight + 5;
|
||||
}
|
||||
|
||||
private void selectGroup(BeatmapGroup group, BeatmapPanel panel = null, bool animated = true)
|
||||
private void select(CarouselBeatmapSet beatmapSet = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (panel == null || panel.Filtered == true)
|
||||
panel = group.BeatmapPanels.First(p => !p.Filtered);
|
||||
if (beatmapSet == null) return;
|
||||
beatmapSet.State.Value = CarouselItemState.Selected;
|
||||
}
|
||||
|
||||
if (selectedPanel == panel) return;
|
||||
|
||||
Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group");
|
||||
|
||||
if (selectedGroup != null && selectedGroup != group && selectedGroup.State != BeatmapGroupState.Hidden)
|
||||
selectedGroup.State = BeatmapGroupState.Collapsed;
|
||||
|
||||
group.State = BeatmapGroupState.Expanded;
|
||||
group.SelectedPanel = panel;
|
||||
|
||||
panel.State = PanelSelectedState.Selected;
|
||||
|
||||
if (selectedPanel == panel) return;
|
||||
|
||||
selectedPanel = panel;
|
||||
selectedGroup = group;
|
||||
|
||||
SelectionChanged?.Invoke(panel.Beatmap);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ScrollToSelected(animated);
|
||||
}
|
||||
private void select(CarouselBeatmap beatmap = null, bool animated = true)
|
||||
{
|
||||
if (beatmap == null) return;
|
||||
beatmap.State.Value = CarouselItemState.Selected;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
@ -547,66 +481,67 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
float drawHeight = DrawHeight;
|
||||
|
||||
// Remove all panels that should no longer be on-screen
|
||||
scrollableContent.RemoveAll(delegate(Panel p)
|
||||
if (!yPositionsCache.IsValid)
|
||||
computeYPositions();
|
||||
|
||||
// Remove all items that should no longer be on-screen
|
||||
scrollableContent.RemoveAll(delegate (DrawableCarouselItem p)
|
||||
{
|
||||
float panelPosY = p.Position.Y;
|
||||
bool remove = panelPosY < Current - p.DrawHeight || panelPosY > Current + drawHeight || !p.IsPresent;
|
||||
float itemPosY = p.Position.Y;
|
||||
bool remove = itemPosY < Current - p.DrawHeight || itemPosY > Current + drawHeight || !p.IsPresent;
|
||||
return remove;
|
||||
});
|
||||
|
||||
// Find index range of all panels that should be on-screen
|
||||
Trace.Assert(panels.Count == yPositions.Count);
|
||||
// Find index range of all items that should be on-screen
|
||||
Trace.Assert(items.Count == yPositions.Count);
|
||||
|
||||
int firstIndex = yPositions.BinarySearch(Current - Panel.MAX_HEIGHT);
|
||||
int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT);
|
||||
if (firstIndex < 0) firstIndex = ~firstIndex;
|
||||
int lastIndex = yPositions.BinarySearch(Current + drawHeight);
|
||||
if (lastIndex < 0)
|
||||
{
|
||||
lastIndex = ~lastIndex;
|
||||
|
||||
// Add the first panel of the last visible beatmap group to preload its data.
|
||||
if (lastIndex != 0 && panels[lastIndex - 1] is BeatmapSetHeader)
|
||||
// Add the first item of the last visible beatmap group to preload its data.
|
||||
if (lastIndex != 0 && items[lastIndex - 1] is DrawableCarouselBeatmapSet)
|
||||
lastIndex++;
|
||||
}
|
||||
|
||||
// Add those panels within the previously found index range that should be displayed.
|
||||
// Add those items within the previously found index range that should be displayed.
|
||||
for (int i = firstIndex; i < lastIndex; ++i)
|
||||
{
|
||||
Panel panel = panels[i];
|
||||
if (panel.State == PanelSelectedState.Hidden)
|
||||
continue;
|
||||
DrawableCarouselItem item = items[i];
|
||||
|
||||
// Only add if we're not already part of the content.
|
||||
if (!scrollableContent.Contains(panel))
|
||||
if (!scrollableContent.Contains(item))
|
||||
{
|
||||
// Makes sure headers are always _below_ panels,
|
||||
// Makes sure headers are always _below_ items,
|
||||
// and depth flows downward.
|
||||
panel.Depth = i + (panel is BeatmapSetHeader ? panels.Count : 0);
|
||||
item.Depth = i + (item is DrawableCarouselBeatmapSet ? items.Count : 0);
|
||||
|
||||
switch (panel.LoadState)
|
||||
switch (item.LoadState)
|
||||
{
|
||||
case LoadState.NotLoaded:
|
||||
LoadComponentAsync(panel);
|
||||
LoadComponentAsync(item);
|
||||
break;
|
||||
case LoadState.Loading:
|
||||
break;
|
||||
default:
|
||||
scrollableContent.Add(panel);
|
||||
scrollableContent.Add(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update externally controlled state of currently visible panels
|
||||
// Update externally controlled state of currently visible items
|
||||
// (e.g. x-offset and opacity).
|
||||
float halfHeight = drawHeight / 2;
|
||||
foreach (Panel p in scrollableContent.Children)
|
||||
updatePanel(p, halfHeight);
|
||||
foreach (DrawableCarouselItem p in scrollableContent.Children)
|
||||
updateItem(p, halfHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the x-offset of currently visible panels. Makes the carousel appear round.
|
||||
/// Computes the x-offset of currently visible items. Makes the carousel appear round.
|
||||
/// </summary>
|
||||
/// <param name="dist">
|
||||
/// Vertical distance from the center of the carousel container
|
||||
@ -624,20 +559,20 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a panel's x position and multiplicative alpha based on its y position and
|
||||
/// Update a item'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="p">The item to be updated.</param>
|
||||
/// <param name="halfHeight">Half the draw height of the carousel container.</param>
|
||||
private void updatePanel(Panel p, float halfHeight)
|
||||
private void updateItem(DrawableCarouselItem 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);
|
||||
float itemDrawY = p.Position.Y - Current + height / 2;
|
||||
float dist = Math.Abs(1f - itemDrawY / 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
|
||||
// local transformation we may want to apply (e.g. when a item gets selected, we
|
||||
// may want to smoothly transform it leftwards.)
|
||||
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
|
||||
|
||||
|
42
osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
Normal file
42
osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// 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.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
public class CarouselBeatmap : CarouselItem
|
||||
{
|
||||
public readonly BeatmapInfo Beatmap;
|
||||
|
||||
public CarouselBeatmap(BeatmapInfo beatmap)
|
||||
{
|
||||
Beatmap = beatmap;
|
||||
State.Value = CarouselItemState.Hidden;
|
||||
}
|
||||
|
||||
protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmap(this)
|
||||
{
|
||||
/*GainedSelection = panelGainedSelection,
|
||||
HideRequested = p => HideDifficultyRequested?.Invoke(p),
|
||||
StartRequested = p => StartRequested?.Invoke(p.beatmap),
|
||||
EditRequested = p => EditRequested?.Invoke(p.beatmap),*/
|
||||
};
|
||||
|
||||
public override void Filter(FilterCriteria criteria)
|
||||
{
|
||||
base.Filter(criteria);
|
||||
|
||||
bool match = criteria.Ruleset == null || (Beatmap.RulesetID == criteria.Ruleset.ID || Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps);
|
||||
|
||||
if (!string.IsNullOrEmpty(criteria.SearchText))
|
||||
match &=
|
||||
Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteria.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0) ||
|
||||
Beatmap.Version.IndexOf(criteria.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0;
|
||||
|
||||
Filtered.Value = !match;
|
||||
}
|
||||
}
|
||||
}
|
62
osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs
Normal file
62
osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs
Normal file
@ -0,0 +1,62 @@
|
||||
// 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;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
public class CarouselBeatmapSet : CarouselGroupEagerSelect
|
||||
{
|
||||
public readonly List<CarouselBeatmap> Beatmaps;
|
||||
|
||||
public BeatmapSetInfo BeatmapSet;
|
||||
|
||||
public CarouselBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
if (beatmapSet == null) throw new ArgumentNullException(nameof(beatmapSet));
|
||||
|
||||
BeatmapSet = beatmapSet;
|
||||
|
||||
Children = Beatmaps = beatmapSet.Beatmaps
|
||||
.Where(b => !b.Hidden)
|
||||
.OrderBy(b => b.RulesetID).ThenBy(b => b.StarDifficulty)
|
||||
.Select(b => new CarouselBeatmap(b))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this);
|
||||
|
||||
public override void Filter(FilterCriteria criteria)
|
||||
{
|
||||
base.Filter(criteria);
|
||||
Filtered.Value = Children.All(i => i.Filtered);
|
||||
|
||||
/*switch (criteria.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.Username, y.BeatmapSet.Metadata.Author.Username, StringComparison.InvariantCultureIgnoreCase));
|
||||
break;
|
||||
case SortMode.Difficulty:
|
||||
groups.Sort((x, y) => x.BeatmapSet.MaxStarDifficulty.CompareTo(y.BeatmapSet.MaxStarDifficulty));
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
public enum CarouselItemState
|
||||
{
|
||||
Hidden,
|
||||
NotSelected,
|
||||
Selected,
|
||||
}
|
||||
}
|
54
osu.Game/Screens/Select/Carousel/CarouselGroup.cs
Normal file
54
osu.Game/Screens/Select/Carousel/CarouselGroup.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// 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.Collections.Generic;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
/// <summary>
|
||||
/// A group which ensures only one child is selected.
|
||||
/// </summary>
|
||||
public class CarouselGroup : CarouselItem
|
||||
{
|
||||
private readonly List<CarouselItem> items;
|
||||
|
||||
public readonly Bindable<CarouselItem> Selected = new Bindable<CarouselItem>();
|
||||
|
||||
protected override DrawableCarouselItem CreateDrawableRepresentation() => null;
|
||||
|
||||
protected override IEnumerable<CarouselItem> Children
|
||||
{
|
||||
get { return base.Children; }
|
||||
set
|
||||
{
|
||||
base.Children = value;
|
||||
value.ForEach(i => i.State.ValueChanged += v => itemStateChanged(i, v));
|
||||
}
|
||||
}
|
||||
|
||||
public CarouselGroup(List<CarouselItem> items = null)
|
||||
{
|
||||
if (items != null) Children = items;
|
||||
}
|
||||
|
||||
private void itemStateChanged(CarouselItem item, CarouselItemState value)
|
||||
{
|
||||
// todo: check state of selected item.
|
||||
|
||||
// ensure we are the only item selected
|
||||
if (value == CarouselItemState.Selected)
|
||||
{
|
||||
foreach (var b in Children)
|
||||
{
|
||||
if (item == b) continue;
|
||||
b.State.Value = CarouselItemState.NotSelected;
|
||||
}
|
||||
|
||||
State.Value = CarouselItemState.Selected;
|
||||
Selected.Value = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs
Normal file
28
osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// 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.Linq;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
/// <summary>
|
||||
/// A group which ensures at least one child is selected (if the group itself is selected).
|
||||
/// </summary>
|
||||
public class CarouselGroupEagerSelect : CarouselGroup
|
||||
{
|
||||
public CarouselGroupEagerSelect()
|
||||
{
|
||||
State.ValueChanged += v =>
|
||||
{
|
||||
if (v == CarouselItemState.Selected)
|
||||
{
|
||||
foreach (var c in Children.Where(c => c.State.Value == CarouselItemState.Hidden))
|
||||
c.State.Value = CarouselItemState.NotSelected;
|
||||
|
||||
if (Children.Any(c => c.Visible) && Children.All(c => c.State != CarouselItemState.Selected))
|
||||
Children.First(c => c.Visible).State.Value = CarouselItemState.Selected;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
57
osu.Game/Screens/Select/Carousel/CarouselItem.cs
Normal file
57
osu.Game/Screens/Select/Carousel/CarouselItem.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// 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 osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
public abstract class CarouselItem
|
||||
{
|
||||
public readonly BindableBool Filtered = new BindableBool();
|
||||
|
||||
public readonly Bindable<CarouselItemState> State = new Bindable<CarouselItemState>(CarouselItemState.NotSelected);
|
||||
|
||||
protected virtual IEnumerable<CarouselItem> Children { get; set; }
|
||||
|
||||
public bool Visible => State.Value != CarouselItemState.Hidden && !Filtered.Value;
|
||||
|
||||
public readonly Lazy<IEnumerable<DrawableCarouselItem>> Drawables;
|
||||
|
||||
protected CarouselItem()
|
||||
{
|
||||
Drawables = new Lazy<IEnumerable<DrawableCarouselItem>>(() =>
|
||||
{
|
||||
List<DrawableCarouselItem> items = new List<DrawableCarouselItem>();
|
||||
|
||||
var self = CreateDrawableRepresentation();
|
||||
if (self != null) items.Add(self);
|
||||
|
||||
if (Children != null)
|
||||
foreach (var c in Children)
|
||||
items.AddRange(c.Drawables.Value);
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
State.ValueChanged += v =>
|
||||
{
|
||||
if (Children == null) return;
|
||||
|
||||
switch (v)
|
||||
{
|
||||
case CarouselItemState.Hidden:
|
||||
case CarouselItemState.NotSelected:
|
||||
Children.ForEach(c => c.State.Value = CarouselItemState.Hidden);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract DrawableCarouselItem CreateDrawableRepresentation();
|
||||
|
||||
public virtual void Filter(FilterCriteria criteria) => Children?.ForEach(c => c.Filter(criteria));
|
||||
}
|
||||
}
|
171
osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
Normal file
171
osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
Normal file
@ -0,0 +1,171 @@
|
||||
// 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.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
public class DrawableCarouselBeatmap : DrawableCarouselItem, IHasContextMenu
|
||||
{
|
||||
private readonly BeatmapInfo beatmap;
|
||||
|
||||
private readonly Sprite background;
|
||||
|
||||
public Action<BeatmapInfo> StartRequested;
|
||||
public Action<BeatmapInfo> EditRequested;
|
||||
public Action<BeatmapInfo> HideRequested;
|
||||
|
||||
private readonly Triangles triangles;
|
||||
private readonly StarCounter starCounter;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(SongSelect songSelect)
|
||||
{
|
||||
StartRequested = songSelect.Start;
|
||||
EditRequested = songSelect.Edit;
|
||||
}
|
||||
|
||||
public DrawableCarouselBeatmap(CarouselBeatmap panel)
|
||||
: base(panel)
|
||||
{
|
||||
beatmap = panel.Beatmap;
|
||||
Height *= 0.60f;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
triangles = new Triangles
|
||||
{
|
||||
TriangleScale = 2,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColourLight = OsuColour.FromHex(@"3a7285"),
|
||||
ColourDark = OsuColour.FromHex(@"123744")
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding(5),
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DifficultyIcon(beatmap)
|
||||
{
|
||||
Scale = new Vector2(1.8f),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding { Left = 5 },
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(4, 0),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-Medium",
|
||||
Text = beatmap.Version,
|
||||
TextSize = 20,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-Medium",
|
||||
Text = "mapped by",
|
||||
TextSize = 16,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-MediumItalic",
|
||||
Text = $"{(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author.Username}",
|
||||
TextSize = 16,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft
|
||||
},
|
||||
}
|
||||
},
|
||||
starCounter = new StarCounter
|
||||
{
|
||||
CountStars = (float)beatmap.StarDifficulty,
|
||||
Scale = new Vector2(0.8f),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Selected()
|
||||
{
|
||||
base.Selected();
|
||||
|
||||
background.Colour = ColourInfo.GradientVertical(
|
||||
new Color4(20, 43, 51, 255),
|
||||
new Color4(40, 86, 102, 255));
|
||||
|
||||
triangles.Colour = Color4.White;
|
||||
}
|
||||
|
||||
protected override void Deselected()
|
||||
{
|
||||
base.Deselected();
|
||||
|
||||
background.Colour = new Color4(20, 43, 51, 255);
|
||||
triangles.Colour = OsuColour.Gray(0.5f);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
if (Item.State == CarouselItemState.Selected)
|
||||
StartRequested?.Invoke(beatmap);
|
||||
|
||||
return base.OnClick(state);
|
||||
}
|
||||
|
||||
protected override void ApplyState()
|
||||
{
|
||||
if (Item.State.Value != CarouselItemState.Hidden && Alpha == 0)
|
||||
starCounter.ReplayAnimation();
|
||||
|
||||
base.ApplyState();
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem("Play", MenuItemType.Highlighted, () => StartRequested?.Invoke(beatmap)),
|
||||
new OsuMenuItem("Edit", MenuItemType.Standard, () => EditRequested?.Invoke(beatmap)),
|
||||
new OsuMenuItem("Hide", MenuItemType.Destructive, () => HideRequested?.Invoke(beatmap)),
|
||||
};
|
||||
}
|
||||
}
|
188
osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
Normal file
188
osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
Normal file
@ -0,0 +1,188 @@
|
||||
// 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.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
public class DrawableCarouselBeatmapSet : DrawableCarouselItem, IHasContextMenu
|
||||
{
|
||||
public Action<DrawableCarouselBeatmapSet> GainedSelection;
|
||||
|
||||
public Action<BeatmapSetInfo> DeleteRequested;
|
||||
|
||||
public Action<BeatmapSetInfo> RestoreHiddenRequested;
|
||||
|
||||
private readonly BeatmapSetInfo beatmapSet;
|
||||
|
||||
private readonly FillFlowContainer difficultyIcons;
|
||||
|
||||
public DrawableCarouselBeatmapSet(CarouselBeatmapSet set)
|
||||
: base(set)
|
||||
{
|
||||
beatmapSet = set.BeatmapSet;
|
||||
}
|
||||
|
||||
protected override void Selected()
|
||||
{
|
||||
base.Selected();
|
||||
GainedSelection?.Invoke(this);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(LocalisationEngine localisation, BeatmapManager manager)
|
||||
{
|
||||
if (localisation == null)
|
||||
throw new ArgumentNullException(nameof(localisation));
|
||||
|
||||
var working = manager.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault());
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DelayedLoadWrapper(
|
||||
new PanelBackground(working)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
|
||||
}, 300),
|
||||
new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
Padding = new MarginPadding { Top = 5, Left = 18, Right = 10, Bottom = 10 },
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
Current = localisation.GetUnicodePreference(beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title),
|
||||
TextSize = 22,
|
||||
Shadow = true,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-SemiBoldItalic",
|
||||
Current = localisation.GetUnicodePreference(beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist),
|
||||
TextSize = 17,
|
||||
Shadow = true,
|
||||
},
|
||||
new FillFlowContainer<FilterableDifficultyIcon>
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = ((CarouselBeatmapSet)Item).Beatmaps.Select(b => new FilterableDifficultyIcon(b)).ToList()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class PanelBackground : BufferedContainer
|
||||
{
|
||||
public PanelBackground(WorkingBeatmap working)
|
||||
{
|
||||
CacheDrawnFrameBuffer = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BeatmapBackgroundSprite(working)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Depth = -1,
|
||||
Direction = FillDirection.Horizontal,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
// This makes the gradient not be perfectly horizontal, but diagonal at a ~40° angle
|
||||
Shear = new Vector2(0.8f, 0),
|
||||
Alpha = 0.5f,
|
||||
Children = new[]
|
||||
{
|
||||
// The left half with no gradient applied
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Width = 0.4f,
|
||||
},
|
||||
// Piecewise-linear gradient with 3 segments to make it appear smoother
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientHorizontal(
|
||||
Color4.Black, new Color4(0f, 0f, 0f, 0.9f)),
|
||||
Width = 0.05f,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientHorizontal(
|
||||
new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)),
|
||||
Width = 0.2f,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientHorizontal(
|
||||
new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)),
|
||||
Width = 0.05f,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems
|
||||
{
|
||||
get
|
||||
{
|
||||
List<MenuItem> items = new List<MenuItem>();
|
||||
|
||||
if (Item.State == CarouselItemState.NotSelected)
|
||||
items.Add(new OsuMenuItem("Expand", MenuItemType.Highlighted, () => Item.State.Value = CarouselItemState.Selected));
|
||||
|
||||
if (beatmapSet.Beatmaps.Any(b => b.Hidden))
|
||||
items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => RestoreHiddenRequested?.Invoke(beatmapSet)));
|
||||
|
||||
items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke(beatmapSet)));
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class FilterableDifficultyIcon : DifficultyIcon
|
||||
{
|
||||
private readonly BindableBool filtered = new BindableBool();
|
||||
|
||||
public FilterableDifficultyIcon(CarouselBeatmap item)
|
||||
: base(item.Beatmap)
|
||||
{
|
||||
filtered.BindTo(item.Filtered);
|
||||
filtered.ValueChanged += v => Schedule(() => this.FadeTo(v ? 0.1f : 1, 100));
|
||||
filtered.TriggerChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
152
osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs
Normal file
152
osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
public abstract class DrawableCarouselItem : Container
|
||||
{
|
||||
public const float MAX_HEIGHT = 80;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public override bool IsPresent => base.IsPresent || Item.Visible;
|
||||
|
||||
public readonly CarouselItem Item;
|
||||
|
||||
private readonly Container nestedContainer;
|
||||
private readonly Container borderContainer;
|
||||
|
||||
private readonly Box hoverLayer;
|
||||
|
||||
protected override Container<Drawable> Content => nestedContainer;
|
||||
|
||||
protected DrawableCarouselItem(CarouselItem item)
|
||||
{
|
||||
Item = item;
|
||||
Item.Filtered.ValueChanged += v => Schedule(ApplyState);
|
||||
Item.State.ValueChanged += v => Schedule(ApplyState);
|
||||
|
||||
Height = MAX_HEIGHT;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
AddInternal(borderContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 10,
|
||||
BorderColour = new Color4(221, 255, 255, 255),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
nestedContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
hoverLayer = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Blending = BlendingMode.Additive,
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private SampleChannel sampleHover;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, OsuColour colours)
|
||||
{
|
||||
sampleHover = audio.Sample.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}");
|
||||
hoverLayer.Colour = colours.Blue.Opacity(0.1f);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
sampleHover?.Play();
|
||||
|
||||
hoverLayer.FadeIn(100, Easing.OutQuint);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
hoverLayer.FadeOut(1000, Easing.OutQuint);
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
public void SetMultiplicativeAlpha(float alpha) => borderContainer.Alpha = alpha;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
ApplyState();
|
||||
}
|
||||
|
||||
protected virtual void ApplyState()
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
switch (Item.State.Value)
|
||||
{
|
||||
case CarouselItemState.NotSelected:
|
||||
Deselected();
|
||||
break;
|
||||
case CarouselItemState.Selected:
|
||||
Selected();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Item.Visible)
|
||||
this.FadeOut(300, Easing.OutQuint);
|
||||
else
|
||||
this.FadeIn(250);
|
||||
}
|
||||
|
||||
protected virtual void Selected()
|
||||
{
|
||||
Item.State.Value = CarouselItemState.Selected;
|
||||
|
||||
borderContainer.BorderThickness = 2.5f;
|
||||
borderContainer.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = new Color4(130, 204, 255, 150),
|
||||
Radius = 20,
|
||||
Roundness = 10,
|
||||
};
|
||||
}
|
||||
|
||||
protected virtual void Deselected()
|
||||
{
|
||||
Item.State.Value = CarouselItemState.NotSelected;
|
||||
|
||||
borderContainer.BorderThickness = 0;
|
||||
borderContainer.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(1),
|
||||
Radius = 10,
|
||||
Colour = Color4.Black.Opacity(100),
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
Item.State.Value = CarouselItemState.Selected;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class EditSongSelect : SongSelect
|
||||
{
|
||||
protected override bool ShowFooter => false;
|
||||
|
||||
protected override void OnSelected(InputState state) => Exit();
|
||||
protected override void Start() => Exit();
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
|
||||
@ -18,54 +13,5 @@ namespace osu.Game.Screens.Select
|
||||
public string SearchText;
|
||||
public RulesetInfo Ruleset;
|
||||
public bool AllowConvertedBeatmaps;
|
||||
|
||||
private bool canConvert(BeatmapInfo beatmapInfo) => beatmapInfo.RulesetID == Ruleset.ID || beatmapInfo.RulesetID == 0 && Ruleset.ID > 0 && AllowConvertedBeatmaps;
|
||||
|
||||
public void Filter(List<BeatmapGroup> groups)
|
||||
{
|
||||
foreach (var g in groups)
|
||||
{
|
||||
var set = g.BeatmapSet;
|
||||
|
||||
// we only support converts from osu! mode to other modes for now.
|
||||
// in the future this will have to change, at which point this condition will become a touch more complicated.
|
||||
bool hasCurrentMode = set.Beatmaps.Any(canConvert);
|
||||
|
||||
bool match = hasCurrentMode;
|
||||
|
||||
if (!string.IsNullOrEmpty(SearchText))
|
||||
match &= set.Metadata.SearchableTerms.Any(term => term.IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0);
|
||||
|
||||
foreach (var panel in g.BeatmapPanels)
|
||||
panel.Filtered.Value = !canConvert(panel.Beatmap);
|
||||
|
||||
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.Username, y.BeatmapSet.Metadata.Author.Username, StringComparison.InvariantCultureIgnoreCase));
|
||||
break;
|
||||
case SortMode.Difficulty:
|
||||
groups.Sort((x, y) => x.BeatmapSet.MaxStarDifficulty.CompareTo(y.BeatmapSet.MaxStarDifficulty));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class MatchSongSelect : SongSelect
|
||||
{
|
||||
protected override void OnSelected(InputState state) => Exit();
|
||||
protected override void Start() => Exit();
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
@ -114,22 +113,22 @@ namespace osu.Game.Screens.Select
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnSelected(InputState state)
|
||||
protected override void Start()
|
||||
{
|
||||
if (player != null) return;
|
||||
|
||||
if (state?.Keyboard.ControlPressed == true)
|
||||
{
|
||||
var auto = Ruleset.Value.CreateInstance().GetAutoplayMod();
|
||||
var autoType = auto.GetType();
|
||||
//if (state?.Keyboard.ControlPressed == true)
|
||||
//{
|
||||
// var auto = Ruleset.Value.CreateInstance().GetAutoplayMod();
|
||||
// var autoType = auto.GetType();
|
||||
|
||||
var mods = modSelect.SelectedMods.Value;
|
||||
if (mods.All(m => m.GetType() != autoType))
|
||||
{
|
||||
modSelect.SelectedMods.Value = mods.Concat(new[] { auto });
|
||||
removeAutoModOnResume = true;
|
||||
}
|
||||
}
|
||||
// var mods = modSelect.SelectedMods.Value;
|
||||
// if (mods.All(m => m.GetType() != autoType))
|
||||
// {
|
||||
// modSelect.SelectedMods.Value = mods.Concat(new[] { auto });
|
||||
// removeAutoModOnResume = true;
|
||||
// }
|
||||
//}
|
||||
|
||||
Beatmap.Value.Track.Looping = false;
|
||||
Beatmap.Disabled = true;
|
||||
|
@ -67,6 +67,10 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public readonly FilterControl FilterControl;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
|
||||
|
||||
protected SongSelect()
|
||||
{
|
||||
const float carousel_width = 640;
|
||||
@ -106,13 +110,11 @@ namespace osu.Game.Screens.Select
|
||||
Size = new Vector2(carousel_width, 1),
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
|
||||
//todo: clicking play on another map doesn't work bindable disabled
|
||||
SelectionChanged = carouselSelectionChanged,
|
||||
BeatmapsChanged = carouselBeatmapsLoaded,
|
||||
DeleteRequested = promptDelete,
|
||||
RestoreRequested = s => { foreach (var b in s.Beatmaps) beatmaps.Restore(b); },
|
||||
EditRequested = editRequested,
|
||||
HideDifficultyRequested = b => beatmaps.Hide(b),
|
||||
StartRequested = () => carouselRaisedStart(),
|
||||
//RestoreRequested = s => { foreach (var b in s.Beatmaps) beatmaps.Restore(b); },
|
||||
});
|
||||
Add(FilterControl = new FilterControl
|
||||
{
|
||||
@ -163,12 +165,14 @@ namespace osu.Game.Screens.Select
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours)
|
||||
{
|
||||
dependencies.Cache(this);
|
||||
|
||||
if (Footer != null)
|
||||
{
|
||||
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
|
||||
Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3);
|
||||
|
||||
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => promptDelete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
|
||||
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => Delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
|
||||
}
|
||||
|
||||
if (this.beatmaps == null)
|
||||
@ -197,7 +201,7 @@ namespace osu.Game.Screens.Select
|
||||
carousel.AllowSelection = !Beatmap.Disabled;
|
||||
}
|
||||
|
||||
private void editRequested(BeatmapInfo beatmap)
|
||||
public void Edit(BeatmapInfo beatmap)
|
||||
{
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap);
|
||||
Push(new Editor());
|
||||
@ -229,7 +233,7 @@ namespace osu.Game.Screens.Select
|
||||
carousel.SelectNextRandom();
|
||||
}
|
||||
|
||||
private void carouselRaisedStart(InputState state = null)
|
||||
public void Start(BeatmapInfo beatmap)
|
||||
{
|
||||
// if we have a pending filter operation, we want to run it now.
|
||||
// it could change selection (ie. if the ruleset has been changed).
|
||||
@ -242,7 +246,9 @@ namespace osu.Game.Screens.Select
|
||||
selectionChangedDebounce = null;
|
||||
}
|
||||
|
||||
OnSelected(state);
|
||||
carousel.SelectBeatmap(beatmap);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
private ScheduledDelegate selectionChangedDebounce;
|
||||
@ -261,7 +267,7 @@ namespace osu.Game.Screens.Select
|
||||
// In these cases, the other component has already loaded the beatmap, so we don't need to do so again.
|
||||
if (beatmap?.Equals(Beatmap.Value.BeatmapInfo) != true)
|
||||
{
|
||||
bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value.BeatmapInfo.BeatmapSetInfoID;
|
||||
bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
|
||||
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap);
|
||||
ensurePlayingSelected(preview);
|
||||
@ -301,7 +307,7 @@ namespace osu.Game.Screens.Select
|
||||
carousel.SelectNextRandom();
|
||||
}
|
||||
|
||||
protected abstract void OnSelected(InputState state);
|
||||
protected abstract void Start();
|
||||
|
||||
private void filterChanged(FilterCriteria criteria, bool debounce = true)
|
||||
{
|
||||
@ -346,7 +352,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
logo.Action = () =>
|
||||
{
|
||||
carouselRaisedStart();
|
||||
Start();
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@ -458,7 +464,7 @@ namespace osu.Game.Screens.Select
|
||||
Beatmap.SetDefault();
|
||||
}
|
||||
|
||||
private void promptDelete(BeatmapSetInfo beatmap)
|
||||
public void Delete(BeatmapSetInfo beatmap)
|
||||
{
|
||||
if (beatmap == null)
|
||||
return;
|
||||
@ -474,13 +480,13 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
case Key.KeypadEnter:
|
||||
case Key.Enter:
|
||||
carouselRaisedStart(state);
|
||||
Start();
|
||||
return true;
|
||||
case Key.Delete:
|
||||
if (state.Keyboard.ShiftPressed)
|
||||
{
|
||||
if (!Beatmap.IsDefault)
|
||||
promptDelete(Beatmap.Value.BeatmapSetInfo);
|
||||
Delete(Beatmap.Value.BeatmapSetInfo);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
Reference in New Issue
Block a user