Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ppy
GitHub Repository: ppy/osu
Path: blob/master/osu.Game/Screens/Edit/Setup/FormSampleSetChooser.cs
4311 views
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Globalization;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Localisation;

namespace osu.Game.Screens.Edit.Setup
{
    public partial class FormSampleSetChooser : FormDropdown<EditorBeatmapSkin.SampleSet?>, IHasPopover
    {
        private EditorBeatmapSkin? beatmapSkin;

        public FormSampleSetChooser()
        {
            Caption = EditorSetupStrings.CustomSampleSets;
        }

        [BackgroundDependencyLoader]
        private void load(EditorBeatmap editorBeatmap)
        {
            beatmapSkin = editorBeatmap.BeatmapSkin;
        }

        protected override void LoadComplete()
        {
            base.LoadComplete();

            populateItems();
            if (beatmapSkin != null)
                beatmapSkin.BeatmapSkinChanged += scheduleItemPopulation;

            Current.Value = Items.First(i => i?.SampleSetIndex > 0);
            Current.BindValueChanged(val =>
            {
                if (val.NewValue?.SampleSetIndex == -1)
                    this.ShowPopover();
            });
        }

        private void populateItems()
        {
            var items = beatmapSkin?.GetAvailableSampleSets().ToList() ?? [new EditorBeatmapSkin.SampleSet(1)];
            items.Add(new EditorBeatmapSkin.SampleSet(-1, "Add new..."));
            Items = items;
        }

        private void scheduleItemPopulation() => Schedule(populateItems);

        protected override LocalisableString GenerateItemText(EditorBeatmapSkin.SampleSet? item)
        {
            if (item == null)
                return string.Empty;

            return base.GenerateItemText(item);
        }

        public Popover GetPopover() => new NewSampleSetPopover(
            Items.Any(i => i?.SampleSetIndex > 0) ? Items.Max(i => i!.SampleSetIndex) : 0,
            idx =>
            {
                if (idx == null)
                {
                    Current.Value = Items.FirstOrDefault(i => i?.SampleSetIndex > 0);
                    return;
                }

                if (Items.SingleOrDefault(i => i?.SampleSetIndex == idx) is EditorBeatmapSkin.SampleSet existing)
                {
                    Current.Value = existing;
                    return;
                }

                var sampleSet = new EditorBeatmapSkin.SampleSet(idx.Value, $@"Custom #{idx}");
                var newItems = Items.ToList();
                newItems.Insert(newItems.Count - 1, sampleSet);
                Items = newItems;
                Current.Value = sampleSet;
            });

        protected override void Dispose(bool isDisposing)
        {
            if (beatmapSkin != null)
                beatmapSkin.BeatmapSkinChanged -= scheduleItemPopulation;

            base.Dispose(isDisposing);
        }

        private partial class NewSampleSetPopover : OsuPopover
        {
            private readonly int currentLargestIndex;
            private readonly Action<int?> onCommit;

            private int? committedIndex;

            private LabelledNumberBox numberBox = null!;

            public NewSampleSetPopover(int currentLargestIndex, Action<int?> onCommit)
            {
                this.currentLargestIndex = currentLargestIndex;
                this.onCommit = onCommit;
            }

            [BackgroundDependencyLoader]
            private void load()
            {
                Child = numberBox = new LabelledNumberBox
                {
                    RelativeSizeAxes = Axes.None,
                    Width = 250,
                    Label = "Sample set index",
                    Current = { Value = (currentLargestIndex + 1).ToString(CultureInfo.InvariantCulture) }
                };
                numberBox.OnCommit += (_, _) =>
                {
                    if (int.TryParse(numberBox.Current.Value, out int parsed))
                        committedIndex = parsed;
                    Hide();
                };
            }

            protected override void OnFocus(FocusEvent e)
            {
                base.OnFocus(e);
                // avoids infinite refocus loop
                if (committedIndex == null)
                    GetContainingFocusManager()?.ChangeFocus(numberBox);
            }

            public override void Hide()
            {
                if (State.Value == Visibility.Visible)
                    onCommit.Invoke(committedIndex > 0 ? committedIndex : null);
                base.Hide();
            }
        }
    }
}