// 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.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;

namespace osu.Game.Graphics.Containers
{
    public abstract class OsuRearrangeableListContainer<TModel> : RearrangeableListContainer<TModel>
    {
        /// <summary>
        /// Whether any item is currently being dragged. Used to hide other items' drag handles.
        /// </summary>
        protected readonly BindableBool DragActive = new BindableBool();

        protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer();

        protected sealed override RearrangeableListItem<TModel> CreateDrawable(TModel item) => CreateOsuDrawable(item).With(d =>
        {
            d.DragActive.BindTo(DragActive);
        });

        protected abstract OsuRearrangeableListItem<TModel> CreateOsuDrawable(TModel item);
    }
}

// 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.Collections.Specialized;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;

namespace osu.Game.Graphics.Containers
{
    public abstract class OsuRearrangeableListContainer<TModel> : RearrangeableListContainer<TModel>
    {
        /// <summary>
        /// Whether any item is currently being dragged. Used to hide other items' drag handles.
        /// </summary>
        protected readonly BindableBool DragActive = new BindableBool();

        protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer();

        private Sample sampleSwap;
        private double sampleLastPlaybackTime;

        protected sealed override RearrangeableListItem<TModel> CreateDrawable(TModel item) => CreateOsuDrawable(item).With(d =>
        {
            d.DragActive.BindTo(DragActive);
        });

        protected abstract OsuRearrangeableListItem<TModel> CreateOsuDrawable(TModel item);

        protected OsuRearrangeableListContainer()
        {
            Items.CollectionChanged += (_, args) =>
            {
                if (args.Action == NotifyCollectionChangedAction.Move)
                    playSwapSample();
            };
        }

        private void playSwapSample()
        {
            if (Time.Current - sampleLastPlaybackTime <= 35)
                return;

            var channel = sampleSwap?.GetChannel();
            if (channel == null)
                return;

            channel.Frequency.Value = 0.96 + RNG.NextDouble(0.08);
            channel.Play();
            sampleLastPlaybackTime = Time.Current;
        }

        [BackgroundDependencyLoader]
        private void load(AudioManager audio)
        {
            sampleSwap = audio.Samples.Get(@"UI/item-swap");
            sampleLastPlaybackTime = Time.Current;
        }
    }
}

Add audio feedback for rearranging list items
Add audio feedback for rearranging list items
