<commit_before>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Screens.Play.HUD
{
    public abstract class GameplayLeaderboard : CompositeDrawable
    {
        private readonly Cached sorting = new Cached();

        public Bindable<bool> Expanded = new Bindable<bool>();

        protected readonly FillFlowContainer<GameplayLeaderboardScore> Flow;

        private bool requiresScroll;
        private readonly OsuScrollContainer scroll;

        private GameplayLeaderboardScore? trackedScore;

        private const int max_panels = 8;

        /// <summary>
        /// Create a new leaderboard.
        /// </summary>
        protected GameplayLeaderboard()
        {
            Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH;

            InternalChildren = new Drawable[]
            {
                scroll = new InputDisabledScrollContainer
                {
                    RelativeSizeAxes = Axes.Both,
                    Child = Flow = new FillFlowContainer<GameplayLeaderboardScore>
                    {
                        RelativeSizeAxes = Axes.X,
                        X = GameplayLeaderboardScore.SHEAR_WIDTH,
                        AutoSizeAxes = Axes.Y,
                        Direction = FillDirection.Vertical,
                        Spacing = new Vector2(2.5f),
                        LayoutDuration = 450,
                        LayoutEasing = Easing.OutQuint,
                    }
                }
            };
        }

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

            Scheduler.AddDelayed(sort, 1000, true);
        }

        /// <summary>
        /// Adds a player to the leaderboard.
        /// </summary>
        /// <param name="user">The player.</param>
        /// <param name="isTracked">
        /// Whether the player should be tracked on the leaderboard.
        /// Set to <c>true</c> for the local player or a player whose replay is currently being played.
        /// </param>
        public ILeaderboardScore Add(IUser? user, bool isTracked)
        {
            var drawable = CreateLeaderboardScoreDrawable(user, isTracked);

            if (isTracked)
            {
                if (trackedScore != null)
                    throw new InvalidOperationException("Cannot track more than one score.");

                trackedScore = drawable;
            }

            drawable.Expanded.BindTo(Expanded);

            Flow.Add(drawable);
            drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true);
            drawable.DisplayOrder.BindValueChanged(_ => sorting.Invalidate(), true);

            int displayCount = Math.Min(Flow.Count, max_panels);
            Height = displayCount * (GameplayLeaderboardScore.PANEL_HEIGHT + Flow.Spacing.Y);
            // Add extra margin space to flow equal to height of leaderboard.
            // This ensures the content is always on screen, but also accounts for the fact that scroll operations
            // without animation were actually forcing the local score to a location it can't usually reside at.
            //
            // Basically, the local score was in the scroll extension region (due to always trying to scroll the
            // local player to the middle of the display, but there being no other content below the local player
            // to scroll up by).
            Flow.Margin = new MarginPadding { Bottom = Height };
            requiresScroll = displayCount != Flow.Count;

            return drawable;
        }

        public void Clear()
        {
            Flow.Clear();
            trackedScore = null;
            scroll.ScrollToStart(false);
        }

        protected virtual GameplayLeaderboardScore CreateLeaderboardScoreDrawable(IUser? user, bool isTracked) =>
            new GameplayLeaderboardScore(user, isTracked);

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

            if (requiresScroll && trackedScore != null)
            {
                float scrollTarget = scroll.GetChildPosInContent(trackedScore) + trackedScore.DrawHeight / 2 - scroll.DrawHeight / 2;
                scroll.ScrollTo(scrollTarget);
            }

            const float panel_height = GameplayLeaderboardScore.PANEL_HEIGHT;

            float fadeBottom = scroll.Current + scroll.DrawHeight;
            float fadeTop = scroll.Current + panel_height;

            if (scroll.Current <= 0) fadeTop -= panel_height;
            if (!scroll.IsScrolledToEnd()) fadeBottom -= panel_height;

            // logic is mostly shared with Leaderboard, copied here for simplicity.
            foreach (var c in Flow.Children)
            {
                float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, Flow).Y;
                float bottomY = topY + panel_height;

                bool requireTopFade = requiresScroll && topY <= fadeTop;<commit_msg>Fix fade being applied for too long when leaderboard scrolls to start<commit_after>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Screens.Play.HUD
{
    public abstract class GameplayLeaderboard : CompositeDrawable
    {
        private readonly Cached sorting = new Cached();

        public Bindable<bool> Expanded = new Bindable<bool>();

        protected readonly FillFlowContainer<GameplayLeaderboardScore> Flow;

        private bool requiresScroll;
        private readonly OsuScrollContainer scroll;

        private GameplayLeaderboardScore? trackedScore;

        private const int max_panels = 8;

        /// <summary>
        /// Create a new leaderboard.
        /// </summary>
        protected GameplayLeaderboard()
        {
            Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH;

            InternalChildren = new Drawable[]
            {
                scroll = new InputDisabledScrollContainer
                {
                    RelativeSizeAxes = Axes.Both,
                    Child = Flow = new FillFlowContainer<GameplayLeaderboardScore>
                    {
                        RelativeSizeAxes = Axes.X,
                        X = GameplayLeaderboardScore.SHEAR_WIDTH,
                        AutoSizeAxes = Axes.Y,
                        Direction = FillDirection.Vertical,
                        Spacing = new Vector2(2.5f),
                        LayoutDuration = 450,
                        LayoutEasing = Easing.OutQuint,
                    }
                }
            };
        }

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

            Scheduler.AddDelayed(sort, 1000, true);
        }

        /// <summary>
        /// Adds a player to the leaderboard.
        /// </summary>
        /// <param name="user">The player.</param>
        /// <param name="isTracked">
        /// Whether the player should be tracked on the leaderboard.
        /// Set to <c>true</c> for the local player or a player whose replay is currently being played.
        /// </param>
        public ILeaderboardScore Add(IUser? user, bool isTracked)
        {
            var drawable = CreateLeaderboardScoreDrawable(user, isTracked);

            if (isTracked)
            {
                if (trackedScore != null)
                    throw new InvalidOperationException("Cannot track more than one score.");

                trackedScore = drawable;
            }

            drawable.Expanded.BindTo(Expanded);

            Flow.Add(drawable);
            drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true);
            drawable.DisplayOrder.BindValueChanged(_ => sorting.Invalidate(), true);

            int displayCount = Math.Min(Flow.Count, max_panels);
            Height = displayCount * (GameplayLeaderboardScore.PANEL_HEIGHT + Flow.Spacing.Y);
            // Add extra margin space to flow equal to height of leaderboard.
            // This ensures the content is always on screen, but also accounts for the fact that scroll operations
            // without animation were actually forcing the local score to a location it can't usually reside at.
            //
            // Basically, the local score was in the scroll extension region (due to always trying to scroll the
            // local player to the middle of the display, but there being no other content below the local player
            // to scroll up by).
            Flow.Margin = new MarginPadding { Bottom = Height };
            requiresScroll = displayCount != Flow.Count;

            return drawable;
        }

        public void Clear()
        {
            Flow.Clear();
            trackedScore = null;
            scroll.ScrollToStart(false);
        }

        protected virtual GameplayLeaderboardScore CreateLeaderboardScoreDrawable(IUser? user, bool isTracked) =>
            new GameplayLeaderboardScore(user, isTracked);

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

            if (requiresScroll && trackedScore != null)
            {
                float scrollTarget = scroll.GetChildPosInContent(trackedScore) + trackedScore.DrawHeight / 2 - scroll.DrawHeight / 2;
                scroll.ScrollTo(scrollTarget);
            }

            const float panel_height = GameplayLeaderboardScore.PANEL_HEIGHT;

            float fadeBottom = scroll.Current + scroll.DrawHeight;
            float fadeTop = scroll.Current + panel_height;

            if (scroll.IsScrolledToStart()) fadeTop -= panel_height;
            if (!scroll.IsScrolledToEnd()) fadeBottom -= panel_height;

            // logic is mostly shared with Leaderboard, copied here for simplicity.
            foreach (var c in Flow.Children)
            {
                float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, Flow).Y;
                float bottomY = topY + panel_height;

                bool requireTopFade = requiresScroll && topY <= fadeTop;
{'fraction_non_alphanumeric': 0.07855763039278815, 'fraction_numerical': 0.0028516235856866894, 'mean_word_length': 1.9131832797427653, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5037351', 'n_tokens_mistral': 2801, 'n_tokens_neox': 2634, 'n_words': 783}