diff --git a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs
index 7372847402..1c730169eb 100644
--- a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs
+++ b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs
@@ -10,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Pooling;
+using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
@@ -260,6 +261,32 @@ namespace osu.Game.Screens.SelectV2
#endregion
+ #region Animation
+
+ ///
+ /// Moves non-selected beatmaps to the right, hiding off-screen.
+ ///
+ public bool VisuallyFocusSelected { get; set; }
+
+ private float selectionFocusOffset;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ selectionFocusOffset = (float)Interpolation.DampContinuously(selectionFocusOffset, VisuallyFocusSelected ? 300 : 0, 100, Time.Elapsed);
+
+ foreach (var panel in Scroll.Panels)
+ {
+ var c = (ICarouselPanel)panel;
+
+ if (!c.Selected.Value)
+ panel.X += selectionFocusOffset;
+ }
+ }
+
+ #endregion
+
#region Filtering
public FilterCriteria Criteria { get; private set; } = new FilterCriteria();
diff --git a/osu.Game/Screens/SelectV2/Carousel.cs b/osu.Game/Screens/SelectV2/Carousel.cs
index e50281e713..1a120e69e7 100644
--- a/osu.Game/Screens/SelectV2/Carousel.cs
+++ b/osu.Game/Screens/SelectV2/Carousel.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Screens.SelectV2
///
/// The number of items currently actualised into drawables.
///
- public int VisibleItems => scroll.Panels.Count;
+ public int VisibleItems => Scroll.Panels.Count;
///
/// The currently selected model. Generally of type T.
@@ -185,7 +185,7 @@ namespace osu.Game.Screens.SelectV2
/// The item to find a related drawable representation.
/// The drawable representation if it exists.
protected Drawable? GetMaterialisedDrawableForItem(CarouselItem item) =>
- scroll.Panels.SingleOrDefault(p => ((ICarouselPanel)p).Item == item);
+ Scroll.Panels.SingleOrDefault(p => ((ICarouselPanel)p).Item == item);
///
/// When a user is traversing the carousel via group selection keys, assert whether the item provided is a valid target.
@@ -222,11 +222,11 @@ namespace osu.Game.Screens.SelectV2
#region Initialisation
- private readonly CarouselScrollContainer scroll;
+ protected readonly CarouselScrollContainer Scroll;
protected Carousel()
{
- InternalChild = scroll = new CarouselScrollContainer
+ InternalChild = Scroll = new CarouselScrollContainer
{
RelativeSizeAxes = Axes.Both,
};
@@ -499,13 +499,13 @@ namespace osu.Game.Screens.SelectV2
// If a keyboard selection is currently made, we want to keep the view stable around the selection.
// That means that we should offset the immediate scroll position by any change in Y position for the selection.
if (prevKeyboard.YPosition != null && currentKeyboardSelection.YPosition != prevKeyboard.YPosition)
- scroll.OffsetScrollPosition((float)(currentKeyboardSelection.YPosition!.Value - prevKeyboard.YPosition.Value));
+ Scroll.OffsetScrollPosition((float)(currentKeyboardSelection.YPosition!.Value - prevKeyboard.YPosition.Value));
}
private void scrollToSelection()
{
if (currentKeyboardSelection.CarouselItem != null)
- scroll.ScrollTo(currentKeyboardSelection.CarouselItem.CarouselYPosition - visibleHalfHeight);
+ Scroll.ScrollTo(currentKeyboardSelection.CarouselItem.CarouselYPosition - visibleHalfHeight);
}
#endregion
@@ -519,12 +519,12 @@ namespace osu.Game.Screens.SelectV2
///
/// The position of the lower visible bound with respect to the current scroll position.
///
- private float visibleBottomBound => (float)(scroll.Current + DrawHeight + BleedBottom);
+ private float visibleBottomBound => (float)(Scroll.Current + DrawHeight + BleedBottom);
///
/// The position of the upper visible bound with respect to the current scroll position.
///
- private float visibleUpperBound => (float)(scroll.Current - BleedTop);
+ private float visibleUpperBound => (float)(Scroll.Current - BleedTop);
///
/// Half the height of the visible content.
@@ -557,7 +557,7 @@ namespace osu.Game.Screens.SelectV2
double selectedYPos = currentSelection.CarouselItem?.CarouselYPosition ?? 0;
- foreach (var panel in scroll.Panels)
+ foreach (var panel in Scroll.Panels)
{
var c = (ICarouselPanel)panel;
@@ -566,12 +566,12 @@ namespace osu.Game.Screens.SelectV2
continue;
float normalisedDepth = (float)(Math.Abs(selectedYPos - c.DrawYPosition) / DrawHeight);
- scroll.Panels.ChangeChildDepth(panel, c.Item.DepthLayer + normalisedDepth);
+ Scroll.Panels.ChangeChildDepth(panel, c.Item.DepthLayer + normalisedDepth);
if (c.DrawYPosition != c.Item.CarouselYPosition)
c.DrawYPosition = Interpolation.DampContinuously(c.DrawYPosition, c.Item.CarouselYPosition, 50, Time.Elapsed);
- Vector2 posInScroll = scroll.ToLocalSpace(panel.ScreenSpaceDrawQuad.Centre);
+ Vector2 posInScroll = Scroll.ToLocalSpace(panel.ScreenSpaceDrawQuad.Centre);
float dist = Math.Abs(1f - posInScroll.Y / visibleHalfHeight);
panel.X = offsetX(dist, visibleHalfHeight);
@@ -628,7 +628,7 @@ namespace osu.Game.Screens.SelectV2
toDisplay.RemoveAll(i => !i.IsVisible);
// Iterate over all panels which are already displayed and figure which need to be displayed / removed.
- foreach (var panel in scroll.Panels)
+ foreach (var panel in Scroll.Panels)
{
var carouselPanel = (ICarouselPanel)panel;
@@ -658,7 +658,7 @@ namespace osu.Game.Screens.SelectV2
carouselPanel.DrawYPosition = item.CarouselYPosition;
carouselPanel.Item = item;
- scroll.Add(drawable);
+ Scroll.Add(drawable);
}
// Update the total height of all items (to make the scroll container scrollable through the full height even though
@@ -666,10 +666,10 @@ namespace osu.Game.Screens.SelectV2
if (carouselItems.Count > 0)
{
var lastItem = carouselItems[^1];
- scroll.SetLayoutHeight((float)(lastItem.CarouselYPosition + lastItem.DrawHeight + visibleHalfHeight));
+ Scroll.SetLayoutHeight((float)(lastItem.CarouselYPosition + lastItem.DrawHeight + visibleHalfHeight));
}
else
- scroll.SetLayoutHeight(0);
+ Scroll.SetLayoutHeight(0);
}
private static void expirePanelImmediately(Drawable panel)
@@ -713,7 +713,7 @@ namespace osu.Game.Screens.SelectV2
/// Implementation of scroll container which handles very large vertical lists by internally using double precision
/// for pre-display Y values.
///
- private partial class CarouselScrollContainer : UserTrackingScrollContainer, IKeyBindingHandler
+ protected partial class CarouselScrollContainer : UserTrackingScrollContainer, IKeyBindingHandler
{
public readonly Container Panels;
diff --git a/osu.Game/Screens/SelectV2/SongSelect.cs b/osu.Game/Screens/SelectV2/SongSelect.cs
index 5458a02583..70452de99a 100644
--- a/osu.Game/Screens/SelectV2/SongSelect.cs
+++ b/osu.Game/Screens/SelectV2/SongSelect.cs
@@ -99,9 +99,13 @@ namespace osu.Game.Screens.SelectV2
base.OnEntering(e);
}
+ private const double fade_duration = 300;
+
public override void OnResuming(ScreenTransitionEvent e)
{
- this.FadeIn();
+ this.FadeIn(fade_duration, Easing.OutQuint);
+
+ carousel.VisuallyFocusSelected = false;
// required due to https://github.com/ppy/osu-framework/issues/3218
modSelectOverlay.SelectedMods.Disabled = false;
@@ -112,16 +116,18 @@ namespace osu.Game.Screens.SelectV2
public override void OnSuspending(ScreenTransitionEvent e)
{
- this.Delay(400).FadeOut();
+ this.Delay(100).FadeOut(fade_duration, Easing.OutQuint);
modSelectOverlay.SelectedMods.UnbindFrom(Mods);
+ carousel.VisuallyFocusSelected = true;
+
base.OnSuspending(e);
}
public override bool OnExiting(ScreenExitEvent e)
{
- this.Delay(400).FadeOut();
+ this.FadeOut(fade_duration, Easing.OutQuint);
return base.OnExiting(e);
}