Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ppy
GitHub Repository: ppy/osu
Path: blob/master/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyStatisticsDisplay.cs
2264 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Layout;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Screens.SelectV2
{
    public partial class BeatmapTitleWedge
    {
        public partial class DifficultyStatisticsDisplay : CompositeDrawable
        {
            private readonly bool autoSize;
            private readonly FillFlowContainer<StatisticDifficulty> statisticsFlow;
            private readonly GridContainer tinyStatisticsGrid;

            private IReadOnlyList<StatisticDifficulty.Data> statistics = Array.Empty<StatisticDifficulty.Data>();

            public IReadOnlyList<StatisticDifficulty.Data> Statistics
            {
                get => statistics;
                set
                {
                    statistics = value;

                    if (IsLoaded)
                    {
                        updateStatistics();
                        updateTinyStatistics();
                    }
                }
            }

            private Color4 accentColour;

            public Color4 AccentColour
            {
                get => accentColour;
                set
                {
                    if (accentColour == value)
                        return;

                    accentColour = value;

                    foreach (var statistic in statisticsFlow)
                        statistic.AccentColour = value;
                }
            }

            private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize);

            [Resolved]
            private OverlayColourProvider colourProvider { get; set; } = null!;

            public DifficultyStatisticsDisplay(bool autoSize = false)
            {
                this.autoSize = autoSize;

                if (autoSize)
                    AutoSizeAxes = Axes.Both;
                else
                    AutoSizeAxes = Axes.Y;

                InternalChildren = new Drawable[]
                {
                    statisticsFlow = new FillFlowContainer<StatisticDifficulty>
                    {
                        AutoSizeAxes = Axes.Both,
                        Spacing = new Vector2(8f, 0f),
                        Direction = FillDirection.Horizontal,
                        AlwaysPresent = true,
                    },
                    tinyStatisticsGrid = new GridContainer
                    {
                        Alpha = 0f,
                        AutoSizeAxes = Axes.Both,
                        Anchor = Anchor.CentreLeft,
                        Origin = Anchor.CentreLeft,
                        ColumnDimensions = new[]
                        {
                            new Dimension(GridSizeMode.AutoSize),
                            new Dimension(GridSizeMode.Absolute, 8),
                            new Dimension(GridSizeMode.AutoSize),
                        }
                    },
                };

                AddLayout(drawSizeLayout);
            }

            [Resolved]
            private LocalisationManager localisations { get; set; } = null!;

            private IBindable<LocalisationParameters>? localisationParameters;

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

                localisationParameters = localisations.CurrentParameters.GetBoundCopy();
                localisationParameters.BindValueChanged(_ => updateStatisticsSizing());

                updateStatistics();
                updateTinyStatistics();
            }

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

                if (!drawSizeLayout.IsValid)
                {
                    updateLayout();
                    drawSizeLayout.Validate();
                }
            }

            private bool displayedTinyStatistics;

            private void updateLayout()
            {
                if (statisticsFlow.Count == 0)
                    return;

                float flowWidth = statisticsFlow[0].Width * statisticsFlow.Count + statisticsFlow.Spacing.X * (statisticsFlow.Count - 1);
                bool tiny = !autoSize && DrawWidth < flowWidth - 20;

                if (displayedTinyStatistics != tiny)
                {
                    if (tiny)
                    {
                        statisticsFlow.Hide();
                        // Slow fade hides fill flow layout weirdness.
                        tinyStatisticsGrid.FadeIn(200, Easing.InQuint);
                    }
                    else
                    {
                        tinyStatisticsGrid.Hide();
                        // Slow fade hides fill flow layout weirdness.
                        statisticsFlow.FadeIn(200, Easing.InQuint);
                    }

                    displayedTinyStatistics = tiny;
                }
            }

            private void updateStatisticsSizing() => SchedulerAfterChildren.AddOnce(() =>
            {
                if (statisticsFlow.Count == 0)
                    return;

                float statisticWidth = Math.Max(65, statisticsFlow.Max(s => s.LabelWidth));

                foreach (var statistic in statisticsFlow)
                {
                    statistic.Width = statisticWidth;
                    // Slow fade hides fill flow layout weirdness.
                    statistic.FadeIn(200, Easing.InQuint);
                }

                drawSizeLayout.Invalidate();
            });

            private void updateStatistics() => Scheduler.AddOnce(() =>
            {
                if (statisticsFlow.Select(s => s.Value.Label)
                                  .SequenceEqual(statistics.Select(s => s.Label)))
                {
                    for (int i = 0; i < statistics.Count; i++)
                        statisticsFlow[i].Value = statistics[i];
                }
                else
                {
                    statisticsFlow.ChildrenEnumerable = statistics.Select(d => new StatisticDifficulty
                    {
                        Alpha = 0,
                        AccentColour = accentColour,
                        Value = d
                    });
                    updateStatisticsSizing();
                }
            });

            private void updateTinyStatistics()
            {
                tinyStatisticsGrid.RowDimensions = statistics.Select(_ => new Dimension(GridSizeMode.AutoSize)).ToArray();
                tinyStatisticsGrid.Content = statistics.Select(s => new[]
                {
                    new OsuSpriteText
                    {
                        Text = s.Label,
                        Font = OsuFont.Style.Caption2.With(weight: FontWeight.SemiBold),
                        Colour = colourProvider.Content2,
                    },
                    Empty(),
                    new OsuSpriteText
                    {
                        Font = OsuFont.Style.Caption2.With(weight: FontWeight.SemiBold),
                        Text = s.Content ?? s.Value.ToLocalisableString("0.##"),
                        Colour = colourProvider.Content1,
                    },
                }).ToArray();
            }
        }
    }
}