From 1ba8cc904a5b72693bad9f5309be5f4ae5f095fd Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 7 Feb 2020 21:42:47 +0100 Subject: [PATCH 1/9] Make the caret blink to the beat --- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index f5b7bc3073..36740fb015 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; @@ -9,6 +10,9 @@ using osu.Game.Graphics.Sprites; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Input.Events; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; +using osuTK; namespace osu.Game.Graphics.UserInterface { @@ -59,5 +63,47 @@ namespace osu.Game.Graphics.UserInterface } protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) }; + + protected override Caret CreateCaret() => new BeatCaret + { + CaretWidth = CaretWidth, + SelectionColour = SelectionColour, + }; + + private class BeatCaret : BasicCaret + { + private bool hasSelection; + + public BeatCaret() + { + AddInternal(new CaretBeatSyncedContainer(this)); + } + + public override void DisplayAt(Vector2 position, float? selectionWidth) + { + base.DisplayAt(position, selectionWidth); + + hasSelection = selectionWidth != null; + if (selectionWidth == null) + ClearTransforms(targetMember: nameof(Alpha)); + } + + private class CaretBeatSyncedContainer : BeatSyncedContainer + { + private readonly BeatCaret caret; + + public CaretBeatSyncedContainer(BeatCaret caret) + { + this.caret = caret; + MinimumBeatLength = 300; + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) + { + if (!caret.hasSelection) + caret.FadeTo(0.7f).FadeTo(0.4f, timingPoint.BeatLength, Easing.InOutSine); + } + } + } } } From c2e0c83724f0d981c780d5a1fb337c84f4248db7 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sat, 8 Feb 2020 20:25:16 +0100 Subject: [PATCH 2/9] change the hierarchy layout --- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 69 +++++++++++++++---- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 36740fb015..d58a22685e 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.Sprites; using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; @@ -70,38 +71,82 @@ namespace osu.Game.Graphics.UserInterface SelectionColour = SelectionColour, }; - private class BeatCaret : BasicCaret + private class BeatCaret : Caret { - private bool hasSelection; + private const float caret_move_time = 60; + + private readonly CaretBeatSyncedContainer beatSync; public BeatCaret() { - AddInternal(new CaretBeatSyncedContainer(this)); + RelativeSizeAxes = Axes.Y; + Size = new Vector2(1, 0.9f); + + Colour = Color4.Transparent; + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + + Masking = true; + CornerRadius = 1; + InternalChild = beatSync = new CaretBeatSyncedContainer + { + RelativeSizeAxes = Axes.Both, + }; } + public override void Hide() => this.FadeOut(200); + + public float CaretWidth { get; set; } + + public Color4 SelectionColour { get; set; } + public override void DisplayAt(Vector2 position, float? selectionWidth) { - base.DisplayAt(position, selectionWidth); + beatSync.HasSelection = selectionWidth != null; - hasSelection = selectionWidth != null; - if (selectionWidth == null) - ClearTransforms(targetMember: nameof(Alpha)); + if (selectionWidth != null) + { + this.MoveTo(new Vector2(position.X, position.Y), 60, Easing.Out); + this.ResizeWidthTo(selectionWidth.Value + CaretWidth / 2, caret_move_time, Easing.Out); + this.FadeColour(SelectionColour, 200, Easing.Out); + } + else + { + this.MoveTo(new Vector2(position.X - CaretWidth / 2, position.Y), 60, Easing.Out); + this.ResizeWidthTo(CaretWidth, caret_move_time, Easing.Out); + this.FadeColour(Color4.White, 200, Easing.Out); + } } private class CaretBeatSyncedContainer : BeatSyncedContainer { - private readonly BeatCaret caret; + private bool hasSelection; - public CaretBeatSyncedContainer(BeatCaret caret) + public bool HasSelection + { + set + { + hasSelection = value; + if (value) + + this.FadeTo(0.5f, 200, Easing.Out); + } + } + + public CaretBeatSyncedContainer() { - this.caret = caret; MinimumBeatLength = 300; + InternalChild = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }; } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { - if (!caret.hasSelection) - caret.FadeTo(0.7f).FadeTo(0.4f, timingPoint.BeatLength, Easing.InOutSine); + if (!hasSelection) + this.FadeTo(0.7f).FadeTo(0.4f, timingPoint.BeatLength, Easing.InOutSine); } } } From 93ff25d2a43ee62c40c46e4489515e51f39f5395 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Feb 2020 15:36:44 +0900 Subject: [PATCH 3/9] Rename caret class --- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index d58a22685e..4abbf8db57 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -65,19 +65,19 @@ namespace osu.Game.Graphics.UserInterface protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) }; - protected override Caret CreateCaret() => new BeatCaret + protected override Caret CreateCaret() => new OsuCaret { CaretWidth = CaretWidth, SelectionColour = SelectionColour, }; - private class BeatCaret : Caret + private class OsuCaret : Caret { private const float caret_move_time = 60; private readonly CaretBeatSyncedContainer beatSync; - public BeatCaret() + public OsuCaret() { RelativeSizeAxes = Axes.Y; Size = new Vector2(1, 0.9f); From 590429b43ba9ba9f12e0e7914d52fe866daf1a03 Mon Sep 17 00:00:00 2001 From: ProTheory8 Date: Sun, 9 Feb 2020 09:15:32 +0000 Subject: [PATCH 4/9] Now TestAutoOpenOnModSelect checks if customisation closes after deselecting mod --- osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 22ba972390..a9353d189f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestCustomisationOpensOnModSelect() + public void TestAutoOpenOnModSelect() { createModSelect(); @@ -71,6 +71,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0); AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.Alpha == 1); + AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); + AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0); } private void createModSelect() From d73ef7c37e86639fbbeac95431077ba2d374201d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Feb 2020 21:25:11 +0900 Subject: [PATCH 5/9] Change DummyBeatmap's track to be 0 length --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 05c344b199..30f2045a83 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps switch (lastObject) { case null: - length = excess_length; + length = 0; break; case IHasEndTime endTime: From c1f52ef5941dd5c84c3dd6432ccf261558dce8ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Feb 2020 21:25:28 +0900 Subject: [PATCH 6/9] Refactor BeatSyncContainer to handle zero length tracks --- .../Containers/BeatSyncedContainer.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index be9aefa359..f36079682e 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -59,9 +59,9 @@ namespace osu.Game.Graphics.Containers Track track = null; IBeatmap beatmap = null; - double currentTrackTime; - TimingControlPoint timingPoint; - EffectControlPoint effectPoint; + double currentTrackTime = 0; + TimingControlPoint timingPoint = null; + EffectControlPoint effectPoint = null; if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded) { @@ -69,24 +69,18 @@ namespace osu.Game.Graphics.Containers beatmap = Beatmap.Value.Beatmap; } - if (track != null && beatmap != null && track.IsRunning) + if (track != null && beatmap != null && track.IsRunning && track.Length > 0) { - currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; + currentTrackTime = track.CurrentTime + EarlyActivationMilliseconds; timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); - - if (timingPoint.BeatLength == 0) - { - IsBeatSyncedWithTrack = false; - return; - } - - IsBeatSyncedWithTrack = true; } - else + + IsBeatSyncedWithTrack = timingPoint?.BeatLength > 0; + + if (timingPoint == null || !IsBeatSyncedWithTrack) { - IsBeatSyncedWithTrack = false; currentTrackTime = Clock.CurrentTime; timingPoint = defaultTiming; effectPoint = defaultEffect; From 96574a98adc8b48c2637aabc3287562eb8893f06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 9 Feb 2020 21:34:56 +0900 Subject: [PATCH 7/9] Use non-zero length for fallback virtual track (allows tests to work as expected) --- osu.Game/Beatmaps/WorkingBeatmap.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 30f2045a83..5dc483b61c 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps BeatmapSetInfo = beatmapInfo.BeatmapSet; Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); - track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack()); + track = new RecyclableLazy(() => GetTrack() ?? GetVirtualTrack(1000)); background = new RecyclableLazy(GetBackground, BackgroundStillValid); waveform = new RecyclableLazy(GetWaveform); storyboard = new RecyclableLazy(GetStoryboard); @@ -48,7 +48,7 @@ namespace osu.Game.Beatmaps total_count.Value++; } - protected virtual Track GetVirtualTrack() + protected virtual Track GetVirtualTrack(double emptyLength = 0) { const double excess_length = 1000; @@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps switch (lastObject) { case null: - length = 0; + length = emptyLength; break; case IHasEndTime endTime: From 404cb613429289faa85d05bdfcf1be181ecb6c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 9 Feb 2020 14:39:27 +0100 Subject: [PATCH 8/9] Open mod select in a more reliable way --- .../Visual/UserInterface/TestSceneModSettings.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index a9353d189f..7781eeb315 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -38,8 +38,8 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestButtonShowsOnCustomisableMod() { createModSelect(); + openModSelect(); - AddStep("open", () => modSelect.Show()); AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value); AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded); AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod)); @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("mods still active", () => SelectedMods.Value.Count == 1); - AddStep("open", () => modSelect.Show()); + openModSelect(); AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); } @@ -66,8 +66,8 @@ namespace osu.Game.Tests.Visual.UserInterface public void TestAutoOpenOnModSelect() { createModSelect(); + openModSelect(); - AddStep("open", () => modSelect.Show()); AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.Alpha == 0); AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod)); AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.Alpha == 1); @@ -88,6 +88,12 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + private void openModSelect() + { + AddStep("open", () => modSelect.Show()); + AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded); + } + private class TestModSelectOverlay : ModSelectOverlay { public new Container ModSettingsContainer => base.ModSettingsContainer; From 7e28f2fe6aae19fdf91e68211cca5c0d19bee6be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 9 Feb 2020 14:41:02 +0100 Subject: [PATCH 9/9] Rename test to better reflect its purpose --- osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 7781eeb315..7ff463361a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestAutoOpenOnModSelect() + public void TestCustomisationMenuVisibility() { createModSelect(); openModSelect();