diff --git a/.vscode/launch.json b/.vscode/launch.json index b981556649..c836ff97bc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "preLaunchTask": "build", "runtimeExecutable": null, "env": {}, - "externalConsole": false + "console": "internalConsole" }, { "name": "Launch Desktop", @@ -23,7 +23,7 @@ "preLaunchTask": "build", "runtimeExecutable": null, "env": {}, - "externalConsole": false + "console": "internalConsole" }, { "name": "Attach", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0c0e79f7fb..03f5bc4c6c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,25 +2,23 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "0.1.0", - "windows": { - "command": "msbuild" - }, - "linux": { - "command": "xbuild" - }, - "args": [ - // Ask msbuild to generate full paths for file names. - "/property:GenerateFullPaths=true" - ], "taskSelector": "/t:", - "showOutput": "silent", "tasks": [ { "taskName": "build", - // Show the output window only if unrecognized errors occur. + "isShellCommand": true, "showOutput": "silent", + "command": "xbuild", + "windows": { + "command": "msbuild" + }, + "args": [ + // Ask msbuild to generate full paths for file names. + "/property:GenerateFullPaths=true" + ], // Use the standard MS compiler pattern to detect errors, warnings and infos - "problemMatcher": "$msCompile" + "problemMatcher": "$msCompile", + "isBuildCommand": true } ] } \ No newline at end of file diff --git a/osu-framework b/osu-framework index a7c99e06ff..1490f00327 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit a7c99e06ff4c3f56fad24bec170eb93f42b1e149 +Subproject commit 1490f003273d7aab6589e489f6e4b02d204c9f27 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index 99da7d1c73..3d9f3aad79 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -99,6 +99,7 @@ namespace osu.Desktop.VisualTests.Tests AddToggleStep(@"auto", state => { auto = state; load(mode); }); + BasicSliderBar sliderBar; Add(new Container { Anchor = Anchor.TopRight, @@ -107,16 +108,17 @@ namespace osu.Desktop.VisualTests.Tests Children = new Drawable[] { new SpriteText { Text = "Playback Speed" }, - new BasicSliderBar + sliderBar = new BasicSliderBar { Width = 150, Height = 10, SelectionColor = Color4.Orange, - Value = playbackSpeed } } }); + sliderBar.Current.BindTo(playbackSpeed); + framedClock.ProcessFrame(); var clockAdjustContainer = new Container diff --git a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs index 7e7782662b..b1b9ddbcda 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs @@ -44,6 +44,8 @@ namespace osu.Desktop.VisualTests.Tests kc.Add(new KeyCounterKeyboard(key)); }); + TestSliderBar sliderBar; + Add(new Container { Anchor = Anchor.TopRight, @@ -52,16 +54,17 @@ namespace osu.Desktop.VisualTests.Tests Children = new Drawable[] { new SpriteText { Text = "FadeTime" }, - new TestSliderBar + sliderBar =new TestSliderBar { Width = 150, Height = 10, SelectionColor = Color4.Orange, - Value = bindable } } }); + sliderBar.Current.BindTo(bindable); + Add(kc); } private class TestSliderBar : SliderBar where T : struct diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs index c0c17cd50e..5665bf859a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs @@ -30,7 +30,9 @@ namespace osu.Desktop.VisualTests.Tests Anchor = Anchor.Centre }; Add(mc); - AddToggleStep(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden); + + AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden); + AddStep(@"show", () => mc.State = Visibility.Visible); } } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs b/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs index 2d3969b822..b72abd1992 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs @@ -36,7 +36,7 @@ namespace osu.Desktop.VisualTests.Tests filter.PinItem(GroupMode.All); filter.PinItem(GroupMode.RecentlyPlayed); - filter.SelectedItem.ValueChanged += newFilter => + filter.Current.ValueChanged += newFilter => { text.Text = "Currently Selected: " + newFilter.ToString(); }; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs index 88a037afee..a8e4382ebb 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; @@ -11,20 +12,21 @@ using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects.Drawables; using osu.Game.Modes.Taiko.UI; +using System; namespace osu.Desktop.VisualTests.Tests { internal class TestCaseTaikoPlayfield : TestCase { - public override string Description => "Taiko playfield"; + private const double default_duration = 300; + private const float scroll_time = 1000; - private TaikoPlayfield playfield; + public override string Description => "Taiko playfield"; protected override double TimePerAction => default_duration * 2; - private const double default_duration = 300; - - private const float scroll_time = 1000; + private readonly Random rng = new Random(1337); + private TaikoPlayfield playfield; public override void Reset() { @@ -41,7 +43,11 @@ namespace osu.Desktop.VisualTests.Tests AddStep("Strong Rim", () => addRimHit(true)); AddStep("Add bar line", () => addBarLine(false)); AddStep("Add major bar line", () => addBarLine(true)); - + AddStep("Height test 1", () => changePlayfieldSize(1)); + AddStep("Height test 2", () => changePlayfieldSize(2)); + AddStep("Height test 3", () => changePlayfieldSize(3)); + AddStep("Height test 4", () => changePlayfieldSize(4)); + AddStep("Height test 5", () => changePlayfieldSize(5)); var rateAdjustClock = new StopwatchClock(true) { Rate = 1 }; @@ -57,21 +63,53 @@ namespace osu.Desktop.VisualTests.Tests }); } + private void changePlayfieldSize(int step) + { + switch (step) + { + case 1: + addCentreHit(false); + break; + case 2: + addCentreHit(true); + break; + case 3: + addDrumRoll(false); + break; + case 4: + addDrumRoll(true); + break; + case 5: + addSwell(1000); + playfield.Delay(scroll_time - 100); + break; + } + + playfield.ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); + } + private void addHitJudgement() { TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great; - playfield.OnJudgement(new DrawableTestHit(new Hit()) + var h = new DrawableTestHit(new Hit()) { X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f), Judgement = new TaikoJudgement { Result = HitResult.Hit, TaikoResult = hitResult, - TimeOffset = 0, - SecondHit = RNG.Next(10) == 0 + TimeOffset = 0 } - }); + }; + + playfield.OnJudgement(h); + + if (RNG.Next(10) == 0) + { + h.Judgement.SecondHit = true; + playfield.OnJudgement(h); + } } private void addMissJudgement() diff --git a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs index cf1a665470..f8792a7fd5 100644 --- a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs @@ -14,8 +14,7 @@ namespace osu.Game.Modes.Catch.UI { public CatchPlayfield() { - RelativeSizeAxes = Axes.Y; - Size = new Vector2(512, 0.9f); + Size = new Vector2(1, 0.9f); Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; diff --git a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs index 670d18f71f..deb4ebac25 100644 --- a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs @@ -15,8 +15,7 @@ namespace osu.Game.Modes.Mania.UI { public ManiaPlayfield(int columns) { - RelativeSizeAxes = Axes.Both; - Size = new Vector2(columns / 20f, 1f); + Size = new Vector2(0.8f, 1f); Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; diff --git a/osu.Game.Modes.Osu/OsuKeyConversionInputManager.cs b/osu.Game.Modes.Osu/OsuKeyConversionInputManager.cs index 986240b37f..567c7a35b1 100644 --- a/osu.Game.Modes.Osu/OsuKeyConversionInputManager.cs +++ b/osu.Game.Modes.Osu/OsuKeyConversionInputManager.cs @@ -42,14 +42,14 @@ namespace osu.Game.Modes.Osu { if (mouseDisabled.Value) { - mouse.PressedButtons.Remove(MouseButton.Left); - mouse.PressedButtons.Remove(MouseButton.Right); + mouse.SetPressed(MouseButton.Left, false); + mouse.SetPressed(MouseButton.Right, false); } if (leftViaKeyboard) - mouse.PressedButtons.Add(MouseButton.Left); + mouse.SetPressed(MouseButton.Left, true); if (rightViaKeyboard) - mouse.PressedButtons.Add(MouseButton.Right); + mouse.SetPressed(MouseButton.Right, true); } } } diff --git a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs index ca9ff6fc61..7e314c5ba1 100644 --- a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs +++ b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; using osu.Game.Beatmaps; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Osu.Beatmaps; @@ -46,5 +47,7 @@ namespace osu.Game.Modes.Osu.UI return new DrawableSpinner(spinner); return null; } + + protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); } } diff --git a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs index d89bbfd131..4164607b4d 100644 --- a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs @@ -38,8 +38,6 @@ namespace osu.Game.Modes.Osu.UI { Anchor = Anchor.Centre; Origin = Anchor.Centre; - RelativeSizeAxes = Axes.Both; - Size = new Vector2(0.75f); Add(new Drawable[] { diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs index f325026be9..167fbebd7b 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableHit.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects.Drawables.Pieces; using OpenTK.Input; namespace osu.Game.Modes.Taiko.Objects.Drawables @@ -66,6 +67,10 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables { Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true); + var circlePiece = MainPiece as CirclePiece; + + circlePiece?.FlashBox.Flush(); + switch (State) { case ArmedState.Idle: @@ -77,6 +82,16 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables case ArmedState.Hit: FadeOut(600); + var flash = circlePiece?.FlashBox; + if (flash != null) + { + flash.FadeTo(0.9f); + flash.FadeOut(300); + } + + + FadeOut(800); + const float gravity_time = 300; const float gravity_travel_height = 200; diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs index e1a590a025..1e440df69a 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/DrawableSwell.cs @@ -65,7 +65,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, Alpha = 0, - Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), + Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER), BlendingMode = BlendingMode.Additive, Masking = true, Children = new [] @@ -82,7 +82,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables Name = "Target ring (thick border)", Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), + Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER), Masking = true, BorderThickness = target_ring_thick_border, BlendingMode = BlendingMode.Additive, diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs index 6ea1494ea7..216e05ebc4 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/CirclePiece.cs @@ -19,15 +19,10 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces /// public class CirclePiece : TaikoPiece { - public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f; + public const float SYMBOL_SIZE = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER * 0.45f; public const float SYMBOL_BORDER = 8; public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER; - /// - /// The amount to scale up the base circle to show it as a "strong" piece. - /// - private const float strong_scale = 1.5f; - /// /// The colour of the inner circle and outer glows. /// @@ -64,6 +59,8 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces private readonly Container background; + public Box FlashBox; + public CirclePiece(bool isStrong = false) { AddInternal(new Drawable[] @@ -104,11 +101,13 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces Masking = true, Children = new[] { - new Box + FlashBox = new Box { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + BlendingMode = BlendingMode.Additive, Alpha = 0, AlwaysPresent = true } @@ -125,10 +124,10 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces if (isStrong) { - Size *= strong_scale; + Size *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE; //default for symbols etc. - Content.Scale *= strong_scale; + Content.Scale *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE; } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs index a0c8865c59..2220438a4a 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TaikoPiece.cs @@ -39,7 +39,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces public TaikoPiece() { //just a default - Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2); + Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER); } } } diff --git a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TickPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TickPiece.cs index 697102eb22..53e795e2e2 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TickPiece.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawables/Pieces/TickPiece.cs @@ -15,12 +15,12 @@ namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces /// Any tick that is not the first for a drumroll is not filled, but is instead displayed /// as a hollow circle. This is what controls the border width of that circle. /// - private const float tick_border_width = TaikoHitObject.CIRCLE_RADIUS / 2 / 4; + private const float tick_border_width = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER / 16; /// /// The size of a tick. /// - private const float tick_size = TaikoHitObject.CIRCLE_RADIUS / 2; + private const float tick_size = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER / 4; private bool filled; public bool Filled diff --git a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs index 54ab8c5300..ebc9b19d3a 100644 --- a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs @@ -4,15 +4,31 @@ using osu.Game.Beatmaps.Timing; using osu.Game.Database; using osu.Game.Modes.Objects; +using osu.Game.Modes.Taiko.UI; namespace osu.Game.Modes.Taiko.Objects { public abstract class TaikoHitObject : HitObject { /// - /// HitCircle radius. + /// Diameter of a circle relative to the size of the . /// - public const float CIRCLE_RADIUS = 42f; + public const float PLAYFIELD_RELATIVE_DIAMETER = 0.5f; + + /// + /// Scale multiplier for a strong circle. + /// + public const float STRONG_CIRCLE_DIAMETER_SCALE = 1.5f; + + /// + /// Default circle diameter. + /// + public const float DEFAULT_CIRCLE_DIAMETER = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT * PLAYFIELD_RELATIVE_DIAMETER; + + /// + /// Default strong circle diameter. + /// + public const float DEFAULT_STRONG_CIRCLE_DIAMETER = DEFAULT_CIRCLE_DIAMETER * STRONG_CIRCLE_DIAMETER_SCALE; /// /// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a of 1000ms. diff --git a/osu.Game.Modes.Taiko/UI/HitExplosion.cs b/osu.Game.Modes.Taiko/UI/HitExplosion.cs index eb43c1a5d0..e4e329523f 100644 --- a/osu.Game.Modes.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Modes.Taiko/UI/HitExplosion.cs @@ -18,11 +18,6 @@ namespace osu.Game.Modes.Taiko.UI /// internal class HitExplosion : CircularContainer { - /// - /// The size multiplier of a hit explosion if a hit object has been hit with the second key. - /// - private const float secondhit_size_multiplier = 1.5f; - /// /// The judgement this hit explosion visualises. /// @@ -34,7 +29,7 @@ namespace osu.Game.Modes.Taiko.UI { Judgement = judgement; - Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2); + Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER); Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -85,7 +80,7 @@ namespace osu.Game.Modes.Taiko.UI /// public void VisualiseSecondHit() { - ResizeTo(Size * secondhit_size_multiplier, 50); + ResizeTo(Size * TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE, 50); } } } diff --git a/osu.Game.Modes.Taiko/UI/HitTarget.cs b/osu.Game.Modes.Taiko/UI/HitTarget.cs index a17480628d..b22dc1d647 100644 --- a/osu.Game.Modes.Taiko/UI/HitTarget.cs +++ b/osu.Game.Modes.Taiko/UI/HitTarget.cs @@ -15,16 +15,6 @@ namespace osu.Game.Modes.Taiko.UI /// internal class HitTarget : Container { - /// - /// Diameter of normal hit object circles. - /// - private const float normal_diameter = TaikoHitObject.CIRCLE_RADIUS * 2; - - /// - /// Diameter of strong hit object circles. - /// - private const float strong_hit_diameter = normal_diameter * 1.5f; - /// /// The 1px inner border of the taiko playfield. /// @@ -37,7 +27,7 @@ namespace osu.Game.Modes.Taiko.UI public HitTarget() { - RelativeSizeAxes = Axes.Y; + Size = new Vector2(TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT); Children = new Drawable[] { @@ -47,7 +37,7 @@ namespace osu.Game.Modes.Taiko.UI Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Y = border_offset, - Size = new Vector2(border_thickness, (TaikoPlayfield.PLAYFIELD_HEIGHT - strong_hit_diameter) / 2f - border_offset), + Size = new Vector2(border_thickness, (TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT - TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER) / 2f - border_offset), Alpha = 0.1f }, new CircularContainer @@ -55,7 +45,7 @@ namespace osu.Game.Modes.Taiko.UI Name = "Strong Hit Ring", Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(strong_hit_diameter), + Size = new Vector2(TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER), Masking = true, BorderColour = Color4.White, BorderThickness = border_thickness, @@ -75,7 +65,7 @@ namespace osu.Game.Modes.Taiko.UI Name = "Normal Hit Ring", Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(normal_diameter), + Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER), Masking = true, BorderColour = Color4.White, BorderThickness = border_thickness, @@ -96,7 +86,7 @@ namespace osu.Game.Modes.Taiko.UI Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Y = -border_offset, - Size = new Vector2(border_thickness, (TaikoPlayfield.PLAYFIELD_HEIGHT - strong_hit_diameter) / 2f - border_offset), + Size = new Vector2(border_thickness, (TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT - TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER) / 2f - border_offset), Alpha = 0.1f }, }; diff --git a/osu.Game.Modes.Taiko/UI/InputDrum.cs b/osu.Game.Modes.Taiko/UI/InputDrum.cs index 0c1e1105cb..d238c38e74 100644 --- a/osu.Game.Modes.Taiko/UI/InputDrum.cs +++ b/osu.Game.Modes.Taiko/UI/InputDrum.cs @@ -21,7 +21,7 @@ namespace osu.Game.Modes.Taiko.UI { public InputDrum() { - Size = new Vector2(TaikoPlayfield.PLAYFIELD_HEIGHT); + Size = new Vector2(TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT); const float middle_split = 10; diff --git a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs index 29fa693d58..32476dff7f 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs @@ -17,6 +17,7 @@ using osu.Game.Modes.Taiko.Objects.Drawables; using osu.Game.Modes.Taiko.Scoring; using osu.Game.Modes.UI; using osu.Game.Modes.Taiko.Replays; +using OpenTK; namespace osu.Game.Modes.Taiko.UI { @@ -100,6 +101,17 @@ namespace osu.Game.Modes.Taiko.UI } } + protected override Vector2 GetPlayfieldAspectAdjust() + { + const float default_relative_height = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT / 768; + const float default_aspect = 16f / 9f; + + float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect; + + return new Vector2(1, default_relative_height * aspectAdjust); + } + + public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); protected override IBeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(); diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs index 9e7eb571a1..db3a1bc84e 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs @@ -16,21 +16,21 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Primitives; using System.Linq; using osu.Game.Modes.Taiko.Objects.Drawables; +using System; namespace osu.Game.Modes.Taiko.UI { public class TaikoPlayfield : Playfield { /// - /// The play field height. This is relative to the size of hit objects - /// such that the playfield is just a bit larger than strong hits. + /// The default play field height. /// - public const float PLAYFIELD_HEIGHT = TaikoHitObject.CIRCLE_RADIUS * 2 * 2; + public const float DEFAULT_PLAYFIELD_HEIGHT = 168f; /// /// The offset from which the center of the hit target lies at. /// - private const float hit_target_offset = TaikoHitObject.CIRCLE_RADIUS * 1.5f + 40; + private const float hit_target_offset = TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER / 2f + 40; /// /// The size of the left area of the playfield. This area contains the input drum. @@ -52,13 +52,11 @@ namespace osu.Game.Modes.Taiko.UI public TaikoPlayfield() { - RelativeSizeAxes = Axes.X; - Height = PLAYFIELD_HEIGHT; - AddInternal(new Drawable[] { rightBackgroundContainer = new Container { + Name = "Transparent playfield background", RelativeSizeAxes = Axes.Both, BorderThickness = 2, Masking = true, @@ -77,76 +75,88 @@ namespace osu.Game.Modes.Taiko.UI }, } }, - new Container + new ScaleFixContainer { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = left_area_size }, - Children = new Drawable[] + RelativeSizeAxes = Axes.X, + Height = DEFAULT_PLAYFIELD_HEIGHT, + Children = new[] { new Container { - X = hit_target_offset, + Name = "Transparent playfield elements", RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = left_area_size }, Children = new Drawable[] { - hitExplosionContainer = new Container + new Container { - Anchor = Anchor.CentreLeft, + Name = "Hit target container", + X = hit_target_offset, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + hitExplosionContainer = new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + BlendingMode = BlendingMode.Additive + }, + barLineContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + new HitTarget + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + }, + hitObjectContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + judgementContainer = new Container + { + RelativeSizeAxes = Axes.Y, + BlendingMode = BlendingMode.Additive + }, + }, + }, + } + }, + leftBackgroundContainer = new Container + { + Name = "Left overlay", + Size = new Vector2(left_area_size, DEFAULT_PLAYFIELD_HEIGHT), + BorderThickness = 1, + Children = new Drawable[] + { + leftBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new InputDrum + { + Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), - BlendingMode = BlendingMode.Additive + RelativePositionAxes = Axes.X, + Position = new Vector2(0.10f, 0), + Scale = new Vector2(0.9f) }, - barLineContainer = new Container + new Box { - RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + ColourInfo = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), }, - new HitTarget - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - }, - hitObjectContainer = new Container - { - RelativeSizeAxes = Axes.Both, - }, - judgementContainer = new Container - { - RelativeSizeAxes = Axes.Both, - BlendingMode = BlendingMode.Additive - }, - }, - }, - } - }, - leftBackgroundContainer = new Container - { - Size = new Vector2(left_area_size, PLAYFIELD_HEIGHT), - BorderThickness = 1, - Children = new Drawable[] - { - leftBackground = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new InputDrum - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.X, - Position = new Vector2(0.10f, 0), - Scale = new Vector2(0.9f) - }, - new Box - { - Anchor = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 10, - ColourInfo = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + } }, } }, topLevelHitContainer = new Container { + Name = "Top level hit objects", RelativeSizeAxes = Axes.Both, } }); @@ -208,5 +218,56 @@ namespace osu.Game.Modes.Taiko.UI else hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit(); } + + /// + /// This is a very special type of container. It serves a similar purpose to , however unlike , + /// this will only adjust the scale relative to the height of its parent and will maintain the original width relative to its parent. + /// + /// + /// By adjusting the scale relative to the height of its parent, the aspect ratio of this container's children is maintained, however this is undesirable + /// in the case where the hit object container should not have its width adjusted by scale. To counteract this, another container is nested inside this + /// container which takes care of reversing the width adjustment while appearing transparent to the user. + /// + /// + private class ScaleFixContainer : Container + { + protected override Container Content => widthAdjustmentContainer; + private readonly WidthAdjustmentContainer widthAdjustmentContainer; + + /// + /// We only want to apply DrawScale in the Y-axis to preserve aspect ratio and doesn't care about having its width adjusted. + /// + protected override Vector2 DrawScale => Scale * RelativeToAbsoluteFactor.Y / DrawHeight; + + public ScaleFixContainer() + { + AddInternal(widthAdjustmentContainer = new WidthAdjustmentContainer { ParentDrawScaleReference = () => DrawScale.X }); + } + + /// + /// The container type that reverses the width adjustment. + /// + private class WidthAdjustmentContainer : Container + { + /// + /// This container needs to know its parent's so it can reverse the width adjustment caused by . + /// + public Func ParentDrawScaleReference; + + public WidthAdjustmentContainer() + { + // This container doesn't care about height, it should always fill its parent + RelativeSizeAxes = Axes.Y; + } + + protected override void Update() + { + base.Update(); + + // Reverse the DrawScale adjustment + Width = Parent.DrawSize.X / ParentDrawScaleReference(); + } + } + } } } \ No newline at end of file diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 7d7c61b69a..e2f33479c0 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -46,6 +46,7 @@ namespace osu.Game.Configuration Set(OsuConfig.AutomaticDownload, true).Disabled = true; Set(OsuConfig.AutomaticDownloadNoVideo, false).Disabled = true; Set(OsuConfig.BlockNonFriendPM, false).Disabled = true; + Set(OsuConfig.Bloom, false).Disabled = true; Set(OsuConfig.BloomSoftening, false).Disabled = true; Set(OsuConfig.BossKeyFirstActivation, true).Disabled = true; Set(OsuConfig.ChatAudibleHighlight, true).Disabled = true; diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index e150c7dc07..82ede8f079 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -3,8 +3,8 @@ using OpenTK; using OpenTK.Graphics; -using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -12,18 +12,18 @@ using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { - public class Nub : CircularContainer, IStateful + public class Nub : CircularContainer, IHasCurrentValue { public const float COLLAPSED_SIZE = 20; public const float EXPANDED_SIZE = 40; - private readonly Box fill; - private const float border_width = 3; private Color4 glowingColour, idleColour; public Nub() { + Box fill; + Size = new Vector2(COLLAPSED_SIZE, 12); BorderColour = Color4.White; @@ -40,6 +40,14 @@ namespace osu.Game.Graphics.UserInterface AlwaysPresent = true, }, }; + + Current.ValueChanged += newValue => + { + if (newValue) + fill.FadeIn(200, EasingTypes.OutQuint); + else + fill.FadeTo(0.01f, 200, EasingTypes.OutQuint); //todo: remove once we figure why containers aren't drawing at all times + }; } [BackgroundDependencyLoader] @@ -84,28 +92,6 @@ namespace osu.Game.Graphics.UserInterface } } - private CheckboxState state; - - public CheckboxState State - { - get - { - return state; - } - set - { - state = value; - - switch (state) - { - case CheckboxState.Checked: - fill.FadeIn(200, EasingTypes.OutQuint); - break; - case CheckboxState.Unchecked: - fill.FadeTo(0.01f, 200, EasingTypes.OutQuint); //todo: remove once we figure why containers aren't drawing at all times - break; - } - } - } + public Bindable Current { get; } = new Bindable(); } } diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 6a5151b90c..d339388aa5 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -23,18 +23,9 @@ namespace osu.Game.Graphics.UserInterface { set { - if (bindable != null) - bindable.ValueChanged -= bindableValueChanged; bindable = value; - if (bindable != null) - { - bool state = State == CheckboxState.Checked; - if (state != bindable.Value) - State = bindable.Value ? CheckboxState.Checked : CheckboxState.Unchecked; - bindable.ValueChanged += bindableValueChanged; - } - - if (bindable?.Disabled ?? true) + Current.BindTo(bindable); + if (value?.Disabled ?? true) Alpha = 0.3f; } } @@ -83,18 +74,16 @@ namespace osu.Game.Graphics.UserInterface Margin = new MarginPadding { Right = 5 }, } }; - } - private void bindableValueChanged(bool isChecked) - { - State = isChecked ? CheckboxState.Checked : CheckboxState.Unchecked; - } + nub.Current.BindTo(Current); - protected override void Dispose(bool isDisposing) - { - if (bindable != null) - bindable.ValueChanged -= bindableValueChanged; - base.Dispose(isDisposing); + Current.ValueChanged += newValue => + { + if (newValue) + sampleChecked?.Play(); + else + sampleUnchecked?.Play(); + }; } protected override bool OnHover(InputState state) @@ -117,23 +106,5 @@ namespace osu.Game.Graphics.UserInterface sampleChecked = audio.Sample.Get(@"Checkbox/check-on"); sampleUnchecked = audio.Sample.Get(@"Checkbox/check-off"); } - - protected override void OnChecked() - { - sampleChecked?.Play(); - nub.State = CheckboxState.Checked; - - if (bindable != null) - bindable.Value = true; - } - - protected override void OnUnchecked() - { - sampleUnchecked?.Play(); - nub.State = CheckboxState.Unchecked; - - if (bindable != null) - bindable.Value = false; - } } } diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 3466fb1a60..9bb0d15545 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.UserInterface private class OsuDropdownMenuItem : DropdownMenuItem { - public OsuDropdownMenuItem(string text, T value) : base(text, value) + public OsuDropdownMenuItem(string text, T current) : base(text, current) { Foreground.Padding = new MarginPadding(2); diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 078c8564d7..91fb1c672a 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -50,7 +50,6 @@ namespace osu.Game.Graphics.UserInterface nub = new Nub { Origin = Anchor.TopCentre, - State = CheckboxState.Unchecked, Expanded = true, } }; @@ -94,13 +93,13 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - nub.State = CheckboxState.Checked; + nub.Current.Value = true; return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - nub.State = CheckboxState.Unchecked; + nub.Current.Value = false; return base.OnMouseUp(state, args); } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 5914d0ba4c..f732916889 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; @@ -24,8 +23,6 @@ namespace osu.Game.Graphics.UserInterface private readonly SpriteText text; private readonly TextAwesome icon; - public event EventHandler Action; - private Color4? accentColour; public Color4 AccentColour { @@ -34,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface { accentColour = value; - if (State != CheckboxState.Checked) + if (Current) { text.Colour = AccentColour; icon.Colour = AccentColour; @@ -48,20 +45,6 @@ namespace osu.Game.Graphics.UserInterface set { text.Text = value; } } - protected override void OnChecked() - { - fadeIn(); - icon.Icon = FontAwesome.fa_check_circle_o; - Action?.Invoke(this, State); - } - - protected override void OnUnchecked() - { - fadeOut(); - icon.Icon = FontAwesome.fa_circle_o; - Action?.Invoke(this, State); - } - private const float transition_length = 500; private void fadeIn() @@ -84,7 +67,7 @@ namespace osu.Game.Graphics.UserInterface protected override void OnHoverLost(InputState state) { - if (State == CheckboxState.Unchecked) + if (!Current) fadeOut(); base.OnHoverLost(state); @@ -134,6 +117,20 @@ namespace osu.Game.Graphics.UserInterface Anchor = Anchor.BottomLeft, } }; + + Current.ValueChanged += v => + { + if (v) + { + fadeIn(); + icon.Icon = FontAwesome.fa_check_circle_o; + } + else + { + fadeOut(); + icon.Icon = FontAwesome.fa_circle_o; + } + }; } } } diff --git a/osu.Game/Modes/Replays/FramedReplayInputHandler.cs b/osu.Game/Modes/Replays/FramedReplayInputHandler.cs index ae20ece515..0c1e140ce4 100644 --- a/osu.Game/Modes/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Modes/Replays/FramedReplayInputHandler.cs @@ -136,7 +136,7 @@ namespace osu.Game.Modes.Replays public ReplayMouseState(Vector2 position, IEnumerable list) { Position = position; - list.ForEach(b => PressedButtons.Add(b)); + list.ForEach(b => SetPressed(b, true)); } } @@ -148,4 +148,4 @@ namespace osu.Game.Modes.Replays } } } -} \ No newline at end of file +} diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index a958c61c68..afc525d686 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -16,6 +16,7 @@ using System.Diagnostics; using System.Linq; using osu.Game.Modes.Replays; using osu.Game.Modes.Scoring; +using OpenTK; namespace osu.Game.Modes.UI { @@ -167,6 +168,11 @@ namespace osu.Game.Modes.UI { public event Action OnJudgement; + /// + /// Whether to apply adjustments to the child based on our own size. + /// + public bool AspectAdjust = true; + public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor; protected override Container Content => content; @@ -219,6 +225,19 @@ namespace osu.Game.Modes.UI Playfield.PostProcess(); } + protected override void Update() + { + base.Update(); + + Playfield.Size = AspectAdjust ? GetPlayfieldAspectAdjust() : Vector2.One; + } + + /// + /// In some cases we want to apply changes to the relative size of our contained based on custom conditions. + /// + /// + protected virtual Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); //a sane default + /// /// Triggered when an object's Judgement is updated. /// diff --git a/osu.Game/Modes/UI/Playfield.cs b/osu.Game/Modes/UI/Playfield.cs index f31ee0f189..bf5f0acde5 100644 --- a/osu.Game/Modes/UI/Playfield.cs +++ b/osu.Game/Modes/UI/Playfield.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Modes.Objects; @@ -63,6 +64,12 @@ namespace osu.Game.Modes.UI Add(HitObjects); } + public override Axes RelativeSizeAxes + { + get { return Axes.Both; } + set { throw new InvalidOperationException($@"{nameof(Playfield)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); } + } + /// /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield. /// diff --git a/osu.Game/Modes/UI/StandardHudOverlay.cs b/osu.Game/Modes/UI/StandardHudOverlay.cs index f77191adf7..f07e421f00 100644 --- a/osu.Game/Modes/UI/StandardHudOverlay.cs +++ b/osu.Game/Modes/UI/StandardHudOverlay.cs @@ -40,6 +40,7 @@ namespace osu.Game.Modes.UI Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Margin = new MarginPadding(10), + Y = - TwoLayerButton.SIZE_RETRACTED.Y, }; protected override ScoreCounter CreateScoreCounter() => new ScoreCounter(6) diff --git a/osu.Game/Overlays/DragBar.cs b/osu.Game/Overlays/DragBar.cs index a9cf735171..90991bb195 100644 --- a/osu.Game/Overlays/DragBar.cs +++ b/osu.Game/Overlays/DragBar.cs @@ -5,7 +5,9 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; using osu.Framework.Input; +using OpenTK; namespace osu.Game.Overlays { @@ -14,9 +16,10 @@ namespace osu.Game.Overlays private readonly Box fill; public Action SeekRequested; - private bool isDragging; - private bool enabled; + public bool IsSeeking { get; private set; } + + private bool enabled = true; public bool IsEnabled { get { return enabled; } @@ -46,9 +49,9 @@ namespace osu.Game.Overlays public void UpdatePosition(float position) { - if (isDragging || !IsEnabled) return; + if (IsSeeking || !IsEnabled) return; - fill.Width = position; + updatePosition(position); } private void seek(InputState state) @@ -56,7 +59,13 @@ namespace osu.Game.Overlays if (!IsEnabled) return; float seekLocation = state.Mouse.Position.X / DrawWidth; SeekRequested?.Invoke(seekLocation); - fill.Width = seekLocation; + updatePosition(seekLocation); + } + + private void updatePosition(float position) + { + position = MathHelper.Clamp(position, 0, 1); + fill.TransformTo(fill.Width, position, 200, EasingTypes.OutQuint, new TransformSeek()); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) @@ -71,12 +80,21 @@ namespace osu.Game.Overlays return true; } - protected override bool OnDragStart(InputState state) => isDragging = true; + protected override bool OnDragStart(InputState state) => IsSeeking = true; protected override bool OnDragEnd(InputState state) { - isDragging = false; + IsSeeking = false; return true; } + + private class TransformSeek : TransformFloat + { + public override void Apply(Drawable d) + { + base.Apply(d); + d.Width = CurrentValue; + } + } } } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 9f3eeb47a0..9d21a0341c 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays { private Drawable currentBackground; private DragBar progress; - private TextAwesome playButton; + private Button playButton; private SpriteText title, artist; private List playList; @@ -46,6 +46,10 @@ namespace osu.Game.Overlays private Container dragContainer; + private const float progress_height = 10; + + private const float bottom_black_area_height = 55; + public MusicController() { Width = 400; @@ -115,89 +119,64 @@ namespace osu.Game.Overlays Text = @"Nothing to play", Font = @"Exo2.0-BoldItalic" }, - new ClickableContainer + new Container { - AutoSizeAxes = Axes.Both, - Origin = Anchor.Centre, + Padding = new MarginPadding { Bottom = progress_height }, + Height = bottom_black_area_height, + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, - Position = new Vector2(0, -30), - Action = () => - { - if (current?.Track == null) return; - if (current.Track.IsRunning) - current.Track.Stop(); - else - current.Track.Start(); - }, Children = new Drawable[] { - playButton = new TextAwesome + new FillFlowContainer