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

Re-implement statistics as a click-in panel

This commit is contained in:
smoogipoo 2020-06-18 16:50:45 +09:00
parent 69d85ca3ae
commit c31a05977d
4 changed files with 135 additions and 149 deletions

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
@ -11,7 +10,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Framework.Utils;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
@ -46,11 +44,9 @@ namespace osu.Game.Screens.Ranking
[Resolved]
private IAPIProvider api { get; set; }
private Container<ScorePanel> scorePanelContainer;
private ResultsScrollContainer scrollContainer;
private Container expandedPanelProxyContainer;
private StatisticsPanel statisticsPanel;
private Drawable bottomPanel;
private ScorePanelList panels;
private ScorePanelList scorePanelList;
protected ResultsScreen(ScoreInfo score, bool allowRetry = true)
{
@ -63,13 +59,6 @@ namespace osu.Game.Screens.Ranking
[BackgroundDependencyLoader]
private void load()
{
scorePanelContainer = new Container<ScorePanel>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
};
FillFlowContainer buttons;
InternalChild = new GridContainer
@ -84,30 +73,26 @@ namespace osu.Game.Screens.Ranking
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
scorePanelContainer,
scrollContainer = new ResultsScrollContainer
new OsuScrollContainer
{
Child = new FillFlowContainer
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Height = screen_height,
Children = new Drawable[]
{
panels = new ScorePanelList(scorePanelContainer)
scorePanelList = new ScorePanelList
{
RelativeSizeAxes = Axes.X,
Height = screen_height,
SelectedScore = { BindTarget = SelectedScore }
RelativeSizeAxes = Axes.Both,
SelectedScore = { BindTarget = SelectedScore },
PostExpandAction = onExpandedPanelClicked
},
new StatisticsPanel(Score)
{
RelativeSizeAxes = Axes.X,
Height = screen_height,
}
statisticsPanel = new StatisticsPanel(Score) { RelativeSizeAxes = Axes.Both }
}
}
},
expandedPanelProxyContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
},
@ -155,7 +140,7 @@ namespace osu.Game.Screens.Ranking
};
if (Score != null)
panels.AddScore(Score);
scorePanelList.AddScore(Score);
if (player != null && allowRetry)
{
@ -180,7 +165,7 @@ namespace osu.Game.Screens.Ranking
var req = FetchScores(scores => Schedule(() =>
{
foreach (var s in scores)
panels.AddScore(s);
scorePanelList.AddScore(s);
}));
if (req != null)
@ -194,21 +179,6 @@ namespace osu.Game.Screens.Ranking
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
protected virtual APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback) => null;
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
ScorePanel expandedPanel = scorePanelContainer.Single(p => p.State == PanelState.Expanded);
expandedPanel.Tracking = false;
expandedPanel.Anchor = Anchor.Centre;
expandedPanel.Origin = Anchor.Centre;
scorePanelContainer.X = (float)Interpolation.Lerp(0, -DrawWidth / 2 + ScorePanel.EXPANDED_WIDTH / 2f, Math.Clamp(scrollContainer.Current / (screen_height * 0.8f), 0, 1));
if (expandedPanelProxyContainer.Count == 0)
expandedPanelProxyContainer.Add(expandedPanel.CreateProxy());
}
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
@ -226,36 +196,39 @@ namespace osu.Game.Screens.Ranking
return base.OnExiting(next);
}
[Cached]
private class ResultsScrollContainer : OsuScrollContainer
private void onExpandedPanelClicked()
{
public ResultsScrollContainer()
statisticsPanel.ToggleVisibility();
if (statisticsPanel.State.Value == Visibility.Hidden)
{
RelativeSizeAxes = Axes.Both;
ScrollbarVisible = false;
foreach (var panel in scorePanelList.Panels)
{
if (panel.State == PanelState.Contracted)
panel.FadeIn(150);
else
{
panel.MoveTo(panel.GetTrackingPosition(), 150, Easing.OutQuint).OnComplete(p =>
{
scorePanelList.HandleScroll = true;
p.Tracking = true;
});
}
}
}
protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default)
else
{
if (!animated)
foreach (var panel in scorePanelList.Panels)
{
// If the user is scrolling via mouse drag, follow the mouse 1:1.
base.OnUserScroll(value, false, distanceDecay);
return;
}
if (panel.State == PanelState.Contracted)
panel.FadeOut(150, Easing.OutQuint);
else
{
scorePanelList.HandleScroll = false;
float direction = Math.Sign(value - Target);
float target = Target + direction * screen_height;
if (target <= -screen_height / 2 || target >= ScrollableExtent + screen_height / 2)
{
// If the user is already at either extent and scrolling in the clamped direction, we want to follow the default scroll exactly so that the bounces aren't too harsh.
base.OnUserScroll(value, true, distanceDecay);
}
else
{
// Otherwise, scroll one screen in the target direction.
base.OnUserScroll(target, true, distanceDecay);
panel.Tracking = false;
panel.MoveTo(new Vector2(scorePanelList.CurrentScrollPosition, panel.GetTrackingPosition().Y), 150, Easing.OutQuint);
}
}
}
}

View File

@ -76,6 +76,18 @@ namespace osu.Game.Screens.Ranking
private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#353535");
public event Action<PanelState> StateChanged;
public Action PostExpandAction;
/// <summary>
/// Whether this <see cref="ScorePanel"/> should track the position of the tracking component created via <see cref="CreateTrackingComponent"/>.
/// </summary>
public bool Tracking;
/// <summary>
/// Whether this <see cref="ScorePanel"/> can enter into an <see cref="PanelState.Expanded"/> state.
/// </summary>
public bool CanExpand = true;
public readonly ScoreInfo Score;
private Container content;
@ -182,38 +194,18 @@ namespace osu.Game.Screens.Ranking
}
}
private bool tracking;
private Vector2 lastNonTrackingPosition;
/// <summary>
/// Whether this <see cref="ScorePanel"/> should track the position of the tracking component created via <see cref="CreateTrackingComponent"/>.
/// </summary>
public bool Tracking
{
get => tracking;
set
{
if (tracking == value)
return;
tracking = value;
if (tracking)
lastNonTrackingPosition = Position;
else
Position = lastNonTrackingPosition;
}
}
protected override void Update()
{
base.Update();
if (Tracking && trackingComponent != null)
{
Vector2 topLeftPos = Parent.ToLocalSpace(trackingComponent.ScreenSpaceDrawQuad.TopLeft);
Position = topLeftPos - AnchorPosition + OriginPosition;
}
Position = GetTrackingPosition();
}
public Vector2 GetTrackingPosition()
{
Vector2 topLeftPos = Parent.ToLocalSpace(trackingComponent.ScreenSpaceDrawQuad.TopLeft);
return topLeftPos - AnchorPosition + OriginPosition;
}
private void updateState()
@ -270,10 +262,28 @@ namespace osu.Game.Screens.Ranking
}
}
public override Vector2 Size
{
get => base.Size;
set
{
base.Size = value;
if (trackingComponent != null)
trackingComponent.Size = value;
}
}
protected override bool OnClick(ClickEvent e)
{
if (State == PanelState.Contracted)
State = PanelState.Expanded;
{
if (CanExpand)
State = PanelState.Expanded;
return true;
}
PostExpandAction?.Invoke();
return true;
}
@ -296,17 +306,13 @@ namespace osu.Game.Screens.Ranking
Panel = panel;
}
protected override void Update()
{
base.Update();
Size = Panel.DrawSize;
}
// In ScorePanelList, score panels are added _before_ the flow, but this means that input will be blocked by the scroll container.
// So by forwarding input events, we remove the need to consider the order in which input is handled.
protected override bool OnClick(ClickEvent e) => Panel.TriggerEvent(e);
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Panel.ReceivePositionalInputAt(screenSpacePos);
public override bool IsPresent => Panel.IsPresent;
}
}
}

View File

@ -26,9 +26,15 @@ namespace osu.Game.Screens.Ranking
/// </summary>
private const float expanded_panel_spacing = 15;
public Action PostExpandAction;
public readonly Bindable<ScoreInfo> SelectedScore = new Bindable<ScoreInfo>();
public float CurrentScrollPosition => scroll.Current;
public IReadOnlyList<ScorePanel> Panels => panels;
private readonly Container<ScorePanel> panels;
private readonly Flow flow;
private readonly Scroll scroll;
private ScorePanel expandedPanel;
@ -36,39 +42,27 @@ namespace osu.Game.Screens.Ranking
/// <summary>
/// Creates a new <see cref="ScorePanelList"/>.
/// </summary>
/// <param name="panelTarget">The target container in which <see cref="ScorePanel"/>s should reside.
/// <see cref="ScorePanel"/>s are set to track by default, but this allows
/// This should be placed _before_ the <see cref="ScorePanelList"/> in the hierarchy.
/// <remarks></remarks>
/// </param>
public ScorePanelList(Container<ScorePanel> panelTarget = null)
public ScorePanelList()
{
RelativeSizeAxes = Axes.Both;
InternalChild = scroll = new Scroll
{
RelativeSizeAxes = Axes.Both,
Child = flow = new Flow
HandleScroll = () => HandleScroll && expandedPanel?.IsHovered != true, // handle horizontal scroll only when not hovering the expanded panel.
Children = new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(panel_spacing, 0),
AutoSizeAxes = Axes.Both,
panels = new Container<ScorePanel> { RelativeSizeAxes = Axes.Both },
flow = new Flow
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(panel_spacing, 0),
AutoSizeAxes = Axes.Both,
},
}
};
if (panelTarget == null)
{
// To prevent 1-frame sizing issues, the panel container is added _before_ the scroll + flow containers
AddInternal(panels = new Container<ScorePanel>
{
RelativeSizeAxes = Axes.Both,
Depth = 1
});
}
else
panels = panelTarget;
}
protected override void LoadComplete()
@ -78,6 +72,25 @@ namespace osu.Game.Screens.Ranking
SelectedScore.BindValueChanged(selectedScoreChanged, true);
}
private bool handleScroll = true;
public bool HandleScroll
{
get => handleScroll;
set
{
handleScroll = value;
foreach (var p in panels)
p.CanExpand = value;
scroll.ScrollbarVisible = value;
if (!value)
scroll.ScrollTo(CurrentScrollPosition, false);
}
}
/// <summary>
/// Adds a <see cref="ScoreInfo"/> to this list.
/// </summary>
@ -86,7 +99,8 @@ namespace osu.Game.Screens.Ranking
{
var panel = new ScorePanel(score)
{
Tracking = true
Tracking = true,
PostExpandAction = () => PostExpandAction?.Invoke()
}.With(p =>
{
p.StateChanged += s =>
@ -137,9 +151,6 @@ namespace osu.Game.Screens.Ranking
var expandedTrackingComponent = flow.SingleOrDefault(t => t.Panel.Score == score.NewValue);
expandedPanel = expandedTrackingComponent?.Panel;
// handle horizontal scroll only when not hovering the expanded panel.
scroll.HandleScroll = () => expandedPanel?.IsHovered != true;
if (expandedPanel == null)
return;

View File

@ -1,17 +1,17 @@
// 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.
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Screens.Ranking.Statistics
{
public class StatisticsPanel : CompositeDrawable
public class StatisticsPanel : VisibilityContainer
{
protected override bool StartHidden => true;
public StatisticsPanel(ScoreInfo score)
{
// Todo: Not correct.
@ -19,27 +19,19 @@ namespace osu.Game.Screens.Ranking.Statistics
FillFlowContainer statisticRows;
InternalChildren = new Drawable[]
InternalChild = new Container
{
new Box
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#333")
Left = ScorePanel.EXPANDED_WIDTH + 30 + 50,
Right = 50
},
new Container
Child = statisticRows = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Left = ScorePanel.EXPANDED_WIDTH + 30 + 50,
Right = 50
},
Child = statisticRows = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(30, 15),
}
Direction = FillDirection.Vertical,
Spacing = new Vector2(30, 15),
}
};
@ -55,5 +47,9 @@ namespace osu.Game.Screens.Ranking.Statistics
});
}
}
protected override void PopIn() => this.FadeIn();
protected override void PopOut() => this.FadeOut();
}
}