1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-11 02:17:19 +08:00

Add transition for selecting a beatmap

This commit is contained in:
Dean Herbert 2025-03-04 18:09:58 +09:00
parent f0d6641adf
commit 1be3b990e7
No known key found for this signature in database
3 changed files with 52 additions and 19 deletions

View File

@ -10,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Pooling;
using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -260,6 +261,32 @@ namespace osu.Game.Screens.SelectV2
#endregion #endregion
#region Animation
/// <summary>
/// Moves non-selected beatmaps to the right, hiding off-screen.
/// </summary>
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 #region Filtering
public FilterCriteria Criteria { get; private set; } = new FilterCriteria(); public FilterCriteria Criteria { get; private set; } = new FilterCriteria();

View File

@ -75,7 +75,7 @@ namespace osu.Game.Screens.SelectV2
/// <summary> /// <summary>
/// The number of items currently actualised into drawables. /// The number of items currently actualised into drawables.
/// </summary> /// </summary>
public int VisibleItems => scroll.Panels.Count; public int VisibleItems => Scroll.Panels.Count;
/// <summary> /// <summary>
/// The currently selected model. Generally of type T. /// The currently selected model. Generally of type T.
@ -185,7 +185,7 @@ namespace osu.Game.Screens.SelectV2
/// <param name="item">The item to find a related drawable representation.</param> /// <param name="item">The item to find a related drawable representation.</param>
/// <returns>The drawable representation if it exists.</returns> /// <returns>The drawable representation if it exists.</returns>
protected Drawable? GetMaterialisedDrawableForItem(CarouselItem item) => protected Drawable? GetMaterialisedDrawableForItem(CarouselItem item) =>
scroll.Panels.SingleOrDefault(p => ((ICarouselPanel)p).Item == item); Scroll.Panels.SingleOrDefault(p => ((ICarouselPanel)p).Item == item);
/// <summary> /// <summary>
/// When a user is traversing the carousel via group selection keys, assert whether the item provided is a valid target. /// 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 #region Initialisation
private readonly CarouselScrollContainer scroll; protected readonly CarouselScrollContainer Scroll;
protected Carousel() protected Carousel()
{ {
InternalChild = scroll = new CarouselScrollContainer InternalChild = Scroll = new CarouselScrollContainer
{ {
RelativeSizeAxes = Axes.Both, 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. // 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. // 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) 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() private void scrollToSelection()
{ {
if (currentKeyboardSelection.CarouselItem != null) if (currentKeyboardSelection.CarouselItem != null)
scroll.ScrollTo(currentKeyboardSelection.CarouselItem.CarouselYPosition - visibleHalfHeight); Scroll.ScrollTo(currentKeyboardSelection.CarouselItem.CarouselYPosition - visibleHalfHeight);
} }
#endregion #endregion
@ -519,12 +519,12 @@ namespace osu.Game.Screens.SelectV2
/// <summary> /// <summary>
/// The position of the lower visible bound with respect to the current scroll position. /// The position of the lower visible bound with respect to the current scroll position.
/// </summary> /// </summary>
private float visibleBottomBound => (float)(scroll.Current + DrawHeight + BleedBottom); private float visibleBottomBound => (float)(Scroll.Current + DrawHeight + BleedBottom);
/// <summary> /// <summary>
/// The position of the upper visible bound with respect to the current scroll position. /// The position of the upper visible bound with respect to the current scroll position.
/// </summary> /// </summary>
private float visibleUpperBound => (float)(scroll.Current - BleedTop); private float visibleUpperBound => (float)(Scroll.Current - BleedTop);
/// <summary> /// <summary>
/// Half the height of the visible content. /// Half the height of the visible content.
@ -557,7 +557,7 @@ namespace osu.Game.Screens.SelectV2
double selectedYPos = currentSelection.CarouselItem?.CarouselYPosition ?? 0; double selectedYPos = currentSelection.CarouselItem?.CarouselYPosition ?? 0;
foreach (var panel in scroll.Panels) foreach (var panel in Scroll.Panels)
{ {
var c = (ICarouselPanel)panel; var c = (ICarouselPanel)panel;
@ -566,12 +566,12 @@ namespace osu.Game.Screens.SelectV2
continue; continue;
float normalisedDepth = (float)(Math.Abs(selectedYPos - c.DrawYPosition) / DrawHeight); 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) if (c.DrawYPosition != c.Item.CarouselYPosition)
c.DrawYPosition = Interpolation.DampContinuously(c.DrawYPosition, c.Item.CarouselYPosition, 50, Time.Elapsed); 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); float dist = Math.Abs(1f - posInScroll.Y / visibleHalfHeight);
panel.X = offsetX(dist, visibleHalfHeight); panel.X = offsetX(dist, visibleHalfHeight);
@ -628,7 +628,7 @@ namespace osu.Game.Screens.SelectV2
toDisplay.RemoveAll(i => !i.IsVisible); toDisplay.RemoveAll(i => !i.IsVisible);
// Iterate over all panels which are already displayed and figure which need to be displayed / removed. // 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; var carouselPanel = (ICarouselPanel)panel;
@ -658,7 +658,7 @@ namespace osu.Game.Screens.SelectV2
carouselPanel.DrawYPosition = item.CarouselYPosition; carouselPanel.DrawYPosition = item.CarouselYPosition;
carouselPanel.Item = item; 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 // 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) if (carouselItems.Count > 0)
{ {
var lastItem = carouselItems[^1]; var lastItem = carouselItems[^1];
scroll.SetLayoutHeight((float)(lastItem.CarouselYPosition + lastItem.DrawHeight + visibleHalfHeight)); Scroll.SetLayoutHeight((float)(lastItem.CarouselYPosition + lastItem.DrawHeight + visibleHalfHeight));
} }
else else
scroll.SetLayoutHeight(0); Scroll.SetLayoutHeight(0);
} }
private static void expirePanelImmediately(Drawable panel) 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 <c>double</c> precision /// Implementation of scroll container which handles very large vertical lists by internally using <c>double</c> precision
/// for pre-display Y values. /// for pre-display Y values.
/// </summary> /// </summary>
private partial class CarouselScrollContainer : UserTrackingScrollContainer, IKeyBindingHandler<GlobalAction> protected partial class CarouselScrollContainer : UserTrackingScrollContainer, IKeyBindingHandler<GlobalAction>
{ {
public readonly Container Panels; public readonly Container Panels;

View File

@ -99,9 +99,13 @@ namespace osu.Game.Screens.SelectV2
base.OnEntering(e); base.OnEntering(e);
} }
private const double fade_duration = 300;
public override void OnResuming(ScreenTransitionEvent e) 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 // required due to https://github.com/ppy/osu-framework/issues/3218
modSelectOverlay.SelectedMods.Disabled = false; modSelectOverlay.SelectedMods.Disabled = false;
@ -112,16 +116,18 @@ namespace osu.Game.Screens.SelectV2
public override void OnSuspending(ScreenTransitionEvent e) public override void OnSuspending(ScreenTransitionEvent e)
{ {
this.Delay(400).FadeOut(); this.Delay(100).FadeOut(fade_duration, Easing.OutQuint);
modSelectOverlay.SelectedMods.UnbindFrom(Mods); modSelectOverlay.SelectedMods.UnbindFrom(Mods);
carousel.VisuallyFocusSelected = true;
base.OnSuspending(e); base.OnSuspending(e);
} }
public override bool OnExiting(ScreenExitEvent e) public override bool OnExiting(ScreenExitEvent e)
{ {
this.Delay(400).FadeOut(); this.FadeOut(fade_duration, Easing.OutQuint);
return base.OnExiting(e); return base.OnExiting(e);
} }