From 7e99fc47e2bb154eb2f21e2691766254ff3a8aee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Apr 2017 15:20:39 +0900 Subject: [PATCH] wip --- .../Tests/TestCaseSongProgress.cs | 404 +++++++++++++++++- osu.Game/Overlays/DragBar.cs | 5 +- osu.Game/Screens/Play/Player.cs | 4 +- osu.Game/Screens/Play/SongProgress.cs | 41 +- osu.Game/Screens/Play/SongProgressBar.cs | 4 +- osu.Game/Screens/Play/SongProgressGraph.cs | 2 +- 6 files changed, 423 insertions(+), 37 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs b/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs index 13a77855d9..feb7aac0ce 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseSongProgress.cs @@ -1,11 +1,22 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; +using System.Linq; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using osu.Framework.Testing; -using osu.Game.Screens.Play; +using osu.Game.Graphics; +using osu.Game.Overlays; +using OpenTK; +using OpenTK.Graphics; namespace osu.Desktop.VisualTests.Tests { @@ -24,12 +35,12 @@ namespace osu.Desktop.VisualTests.Tests Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, - Length = 100, - OnSeek = time => progress.CurrentTime = time, }); - AddStep("Toggle Bar", progress.ToggleVisibility); - AddStep("New Values", displayNewValues); + AddStep("Toggle Bar", progress.ToggleBar); + AddWaitStep(5); + //AddStep("Toggle Bar", progress.ToggleVisibility); + //AddStep("New Values", displayNewValues); displayNewValues(); } @@ -43,7 +54,388 @@ namespace osu.Desktop.VisualTests.Tests } progress.Values = newValues.ToArray(); - progress.CurrentTime = RNG.Next(0, 100); + progress.Progress = RNG.NextDouble(); + } + } + + public class SongProgress : OverlayContainer + { + private const int progress_height = 5; + + private static readonly Vector2 handle_size = new Vector2(14, 25); + + private const float transition_duration = 200; + + private readonly SongProgressBar bar; + private readonly SongProgressGraph graph; + + public Action OnSeek; + + private double progress; + public double Progress + { + get { return progress; } + set + { + progress = value; + updateProgress(); + } + } + + public int[] Values + { + get { return graph.Values; } + set { graph.Values = value; } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + graph.FillColour = bar.FillColour = colours.BlueLighter; + } + + public SongProgress() + { + RelativeSizeAxes = Axes.X; + Height = progress_height + SongProgressGraph.Column.HEIGHT + handle_size.Y; + + Children = new Drawable[] + { + graph = new SongProgressGraph + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Height = SongProgressGraph.Column.HEIGHT, + Margin = new MarginPadding { Bottom = progress_height }, + }, + bar = new SongProgressBar(progress_height, SongProgressGraph.Column.HEIGHT, handle_size) + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Alpha = 0, + SeekRequested = delegate (float position) + { + OnSeek?.Invoke(position); + }, + }, + }; + } + + private void updateProgress() + { + bar.UpdatePosition((float)progress); + graph.Progress = (int)(graph.ColumnCount * progress); + } + + private bool barVisible; + + public void ToggleBar() + { + barVisible = !barVisible; + + updateBarVisibility(); + } + + private void updateBarVisibility() + { + bar.FadeTo(barVisible ? 1 : 0, transition_duration, EasingTypes.In); + MoveTo(new Vector2(0, barVisible ? 0 : progress_height), transition_duration, EasingTypes.In); + } + + protected override void PopIn() + { + updateBarVisibility(); + } + + protected override void PopOut() + { + } + + protected override void Update() + { + base.Update(); + + updateProgress(); + } + } + + public class SongProgressGraph : BufferedContainer + { + private Game.Screens.Play.SongProgressGraph.Column[] columns = { }; + + public int ColumnCount => columns.Length; + + public override bool HandleInput => false; + + private int progress; + public int Progress + { + get { return progress; } + set + { + if (value == progress) return; + progress = value; + + redrawProgress(); + } + } + + private int[] calculatedValues = { }; // values but adjusted to fit the amount of columns + private int[] values; + public int[] Values + { + get { return values; } + set + { + if (value == values) return; + values = value; + recreateGraph(); + } + } + + private Color4 fillColour; + public Color4 FillColour + { + get { return fillColour; } + set + { + if (value == fillColour) return; + fillColour = value; + + redrawFilled(); + } + } + + public SongProgressGraph() + { + CacheDrawnFrameBuffer = true; + PixelSnapping = true; + } + + private float lastDrawWidth; + protected override void Update() + { + base.Update(); + + // todo: Recreating in update is probably not the best idea + if (DrawWidth == lastDrawWidth) return; + recreateGraph(); + lastDrawWidth = DrawWidth; + } + + /// + /// Redraws all the columns to match their lit/dimmed state. + /// + private void redrawProgress() + { + for (int i = 0; i < columns.Length; i++) + { + columns[i].State = i <= progress ? Game.Screens.Play.SongProgressGraph.ColumnState.Lit : Game.Screens.Play.SongProgressGraph.ColumnState.Dimmed; + } + + ForceRedraw(); + } + + /// + /// Redraws the filled amount of all the columns. + /// + private void redrawFilled() + { + for (int i = 0; i < ColumnCount; i++) + { + columns[i].Filled = calculatedValues.ElementAtOrDefault(i); + } + } + + /// + /// Takes and adjusts it to fit the amount of columns. + /// + private void recalculateValues() + { + var newValues = new List(); + + if (values == null) + { + for (float i = 0; i < ColumnCount; i++) + newValues.Add(0); + + return; + } + + float step = values.Length / (float)ColumnCount; + for (float i = 0; i < values.Length; i += step) + { + newValues.Add(values[(int)i]); + } + + calculatedValues = newValues.ToArray(); + } + + /// + /// Recreates the entire graph. + /// + private void recreateGraph() + { + var newColumns = new List(); + + for (float x = 0; x < DrawWidth; x += Game.Screens.Play.SongProgressGraph.Column.WIDTH) + { + newColumns.Add(new Game.Screens.Play.SongProgressGraph.Column(fillColour) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Position = new Vector2(x, 0), + State = Game.Screens.Play.SongProgressGraph.ColumnState.Dimmed, + }); + } + + columns = newColumns.ToArray(); + Children = columns; + + recalculateValues(); + redrawFilled(); + redrawProgress(); + } + + public class Column : Container, IStateful + { + private readonly Color4 emptyColour = Color4.White.Opacity(100); + private readonly Color4 litColour; + private readonly Color4 dimmedColour = Color4.White.Opacity(175); + + private const float cube_count = 6; + private const float cube_size = 4; + private const float padding = 2; + public const float WIDTH = cube_size + padding; + public const float HEIGHT = cube_count * WIDTH + padding; + + private readonly List drawableRows = new List(); + + private int filled; + public int Filled + { + get { return filled; } + set + { + if (value == filled) return; + filled = value; + + fillActive(); + } + } + + private Game.Screens.Play.SongProgressGraph.ColumnState state; + public Game.Screens.Play.SongProgressGraph.ColumnState State + { + get { return state; } + set + { + if (value == state) return; + state = value; + + fillActive(); + } + } + + public Column(Color4 litColour) + { + Size = new Vector2(WIDTH, HEIGHT); + this.litColour = litColour; + + for (int r = 0; r < cube_count; r++) + { + drawableRows.Add(new Box + { + EdgeSmoothness = new Vector2(padding / 4), + Size = new Vector2(cube_size), + Position = new Vector2(0, r * WIDTH + padding), + }); + } + + Children = drawableRows; + + // Reverse drawableRows so when iterating through them they start at the bottom + drawableRows.Reverse(); + } + + private void fillActive() + { + Color4 colour = State == Game.Screens.Play.SongProgressGraph.ColumnState.Lit ? litColour : dimmedColour; + + for (int i = 0; i < drawableRows.Count; i++) + { + if (Filled == 0) // i <= Filled doesn't work for zero fill + drawableRows[i].Colour = emptyColour; + else + drawableRows[i].Colour = i <= Filled ? colour : emptyColour; + } + } + } + + public enum ColumnState + { + Lit, + Dimmed + } + } + + public class SongProgressBar : DragBar + { + public Color4 FillColour + { + get { return FillContainer.Colour; } + set { FillContainer.Colour = value; } + } + + public SongProgressBar(float barHeight, float handleBarHeight, Vector2 handleSize) + { + Height = barHeight + handleBarHeight + handleSize.Y; + FillContainer.RelativeSizeAxes = Axes.X; + FillContainer.Height = barHeight; + + Add(new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = barHeight, + Colour = Color4.Black, + Alpha = 0.5f, + Depth = 1 + }); + FillContainer.Add(new Container + { + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + Width = 2, + Height = barHeight + handleBarHeight, + Colour = Color4.White, + Position = new Vector2(2, 0), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.TopCentre, + Size = handleSize, + CornerRadius = 5, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White + } + } + } + } + }); } } } diff --git a/osu.Game/Overlays/DragBar.cs b/osu.Game/Overlays/DragBar.cs index 2ee986cd29..123cd404c7 100644 --- a/osu.Game/Overlays/DragBar.cs +++ b/osu.Game/Overlays/DragBar.cs @@ -14,7 +14,6 @@ namespace osu.Game.Overlays public class DragBar : Container { protected readonly Container FillContainer; - protected readonly Box Fill; public Action SeekRequested; @@ -46,7 +45,7 @@ namespace osu.Game.Overlays Width = 0, Children = new Drawable[] { - Fill = new Box + new Box { RelativeSizeAxes = Axes.Both } @@ -73,7 +72,7 @@ namespace osu.Game.Overlays private void updatePosition(float position) { position = MathHelper.Clamp(position, 0, 1); - fill.TransformTo(fill.Width, position, 200, EasingTypes.OutQuint, new TransformSeek()); + FillContainer.TransformTo(FillContainer.Width, position, 200, EasingTypes.OutQuint, new TransformSeek()); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index df2645af7b..9b3ca0df26 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -121,8 +121,6 @@ namespace osu.Game.Screens.Play hudOverlay.BindHitRenderer(HitRenderer); hudOverlay.Progress.Hide(); - hudOverlay.Progress.Length = Beatmap?.Track?.Length ?? 0; - //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) HitRenderer.OnAllJudged += onCompletion; @@ -167,7 +165,7 @@ namespace osu.Game.Screens.Play { base.Update(); - hudOverlay.Progress.CurrentTime = Beatmap.Track.CurrentTime; + hudOverlay.Progress.Progress = Beatmap.Track.CurrentTime / Beatmap.Track.Length; } private void initializeSkipButton() diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 783b7611a2..b2e289c422 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -14,7 +14,9 @@ namespace osu.Game.Screens.Play public class SongProgress : OverlayContainer { private const int progress_height = 5; - private readonly Vector2 handleSize = new Vector2(14, 25); + + private static readonly Vector2 handle_size = new Vector2(14, 25); + private const float transition_duration = 200; private readonly SongProgressBar bar; @@ -22,24 +24,13 @@ namespace osu.Game.Screens.Play public Action OnSeek; - private double currentTime; - public double CurrentTime + private double progress; + public double Progress { - get { return currentTime; } + get { return progress; } set { - currentTime = value; - updateProgress(); - } - } - - private double length; - public double Length - { - get { return length; } - set - { - length = value; + progress = value; updateProgress(); } } @@ -59,7 +50,7 @@ namespace osu.Game.Screens.Play public SongProgress() { RelativeSizeAxes = Axes.X; - Height = progress_height + SongProgressGraph.Column.HEIGHT + handleSize.Y; + Height = progress_height + SongProgressGraph.Column.HEIGHT + handle_size.Y; Children = new Drawable[] { @@ -71,13 +62,13 @@ namespace osu.Game.Screens.Play Height = SongProgressGraph.Column.HEIGHT, Margin = new MarginPadding { Bottom = progress_height }, }, - bar = new SongProgressBar(progress_height, SongProgressGraph.Column.HEIGHT, handleSize) + bar = new SongProgressBar(progress_height, SongProgressGraph.Column.HEIGHT, handle_size) { Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, SeekRequested = delegate (float position) { - OnSeek?.Invoke(Length * position); + OnSeek?.Invoke(position); }, }, }; @@ -85,9 +76,8 @@ namespace osu.Game.Screens.Play private void updateProgress() { - float currentProgress = (float)(CurrentTime / Length); - bar.UpdatePosition(currentProgress); - graph.Progress = (int)(graph.ColumnCount * currentProgress); + bar.UpdatePosition((float)progress); + graph.Progress = (int)(graph.ColumnCount * progress); } protected override void PopIn() @@ -105,5 +95,12 @@ namespace osu.Game.Screens.Play bar.FadeOut(transition_duration, EasingTypes.In); MoveTo(new Vector2(0f, progress_height), transition_duration, EasingTypes.In); } + + protected override void Update() + { + base.Update(); + + updateProgress(); + } } } diff --git a/osu.Game/Screens/Play/SongProgressBar.cs b/osu.Game/Screens/Play/SongProgressBar.cs index f8ee030d21..a42122d9e1 100644 --- a/osu.Game/Screens/Play/SongProgressBar.cs +++ b/osu.Game/Screens/Play/SongProgressBar.cs @@ -14,8 +14,8 @@ namespace osu.Game.Screens.Play { public Color4 FillColour { - get { return Fill.Colour; } - set { Fill.Colour = value; } + get { return FillContainer.Colour; } + set { FillContainer.Colour = value; } } public SongProgressBar(float barHeight, float handleBarHeight, Vector2 handleSize) diff --git a/osu.Game/Screens/Play/SongProgressGraph.cs b/osu.Game/Screens/Play/SongProgressGraph.cs index 9bac81a721..4f0cdbf67a 100644 --- a/osu.Game/Screens/Play/SongProgressGraph.cs +++ b/osu.Game/Screens/Play/SongProgressGraph.cs @@ -117,7 +117,7 @@ namespace osu.Game.Screens.Play } float step = values.Length / (float)ColumnCount; - for (float i = 0; i < values.Length; i += step) + for (float i = 0; i < values.Length; i += step) { newValues.Add(values[(int)i]); }