From a940676fc2ae5d24b34a8c500a8d6bc9fc4a57a2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 May 2022 21:10:02 +0900 Subject: [PATCH 1/5] Add adjustment buttons --- .../Editing/TestSceneTapTimingControl.cs | 6 + osu.Game/Screens/Edit/Timing/AdjustButton.cs | 235 ++++++++++++++++++ .../Screens/Edit/Timing/TapTimingControl.cs | 65 ++++- 3 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/Edit/Timing/AdjustButton.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs index 46b45979ea..8dd368f2a9 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs @@ -77,6 +77,12 @@ namespace osu.Game.Tests.Visual.Editing timingInfo.Text = $"offset: {selectedGroup.Value.Time:N2} bpm: {selectedGroup.Value.ControlPoints.OfType().First().BPM:N2}"; } + [Test] + public void TestNoop() + { + AddStep("do nothing", () => { }); + } + [Test] public void TestTapThenReset() { diff --git a/osu.Game/Screens/Edit/Timing/AdjustButton.cs b/osu.Game/Screens/Edit/Timing/AdjustButton.cs new file mode 100644 index 0000000000..b0a0517e94 --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/AdjustButton.cs @@ -0,0 +1,235 @@ +// Copyright (c) ppy Pty Ltd . 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.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Framework.Threading; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; + +namespace osu.Game.Screens.Edit.Timing +{ + public class AdjustButton : CompositeDrawable + { + public Action Action; + + private readonly double adjustAmount; + private ScheduledDelegate adjustDelegate; + + private const int adjust_levels = 4; + + private const double initial_delay = 300; + + private const double minimum_delay = 80; + + public Container Content { get; set; } + + private double adjustDelay = initial_delay; + + private readonly Box background; + + private readonly OsuSpriteText text; + + public LocalisableString Text + { + get => text?.Text ?? default; + set + { + if (text != null) + text.Text = value; + } + } + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + public AdjustButton(double adjustAmount) + { + this.adjustAmount = adjustAmount; + + CornerRadius = 5; + Masking = true; + + AddInternal(Content = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue + }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Default.With(weight: FontWeight.SemiBold), + Padding = new MarginPadding(5), + Depth = float.MinValue + } + } + }); + } + + [BackgroundDependencyLoader] + private void load() + { + background.Colour = colourProvider.Background3; + + for (int i = 1; i <= adjust_levels; i++) + { + Content.Add(new IncrementBox(i, adjustAmount)); + Content.Add(new IncrementBox(-i, adjustAmount)); + } + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + beginRepeat(); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + adjustDelegate?.Cancel(); + base.OnMouseUp(e); + } + + private void beginRepeat() + { + adjustDelegate?.Cancel(); + + adjustDelay = initial_delay; + adjustNext(); + + void adjustNext() + { + var hoveredBox = Content.OfType().FirstOrDefault(d => d.IsHovered); + + if (hoveredBox != null) + { + Action(adjustAmount * hoveredBox.Multiplier); + + adjustDelay = Math.Max(minimum_delay, adjustDelay * 0.9f); + + hoveredBox.Flash(); + } + else + { + adjustDelay = initial_delay; + } + + adjustDelegate = Scheduler.AddDelayed(adjustNext, adjustDelay); + } + } + + private class IncrementBox : CompositeDrawable + { + public readonly float Multiplier; + + private readonly Box box; + private readonly OsuSpriteText text; + + public IncrementBox(int index, double amount) + { + Multiplier = Math.Sign(index) * convertMultiplier(index); + + float ratio = (float)index / adjust_levels; + + RelativeSizeAxes = Axes.Both; + + Width = 0.5f * Math.Abs(ratio); + + Anchor direction = index < 0 ? Anchor.x2 : Anchor.x0; + + Origin |= direction; + + Depth = Math.Abs(index); + + Anchor = Anchor.TopCentre; + + InternalChildren = new Drawable[] + { + box = new Box + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive + }, + text = new OsuSpriteText + { + Anchor = direction, + Origin = direction, + Font = OsuFont.Default.With(size: 10, weight: FontWeight.Bold), + Text = $"{(index > 0 ? "+" : "-")}{Math.Abs(Multiplier * amount)}", + Padding = new MarginPadding(5), + Alpha = 0, + } + }; + } + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + box.Colour = colourProvider.Background1; + box.Alpha = 0.1f; + } + + private float convertMultiplier(int m) + { + switch (Math.Abs(m)) + { + default: return 1; + + case 2: return 2; + + case 3: return 5; + + case 4: return 10; + } + } + + protected override bool OnHover(HoverEvent e) + { + box.Colour = colourProvider.Colour0; + + box.FadeTo(0.2f, 100, Easing.OutQuint); + text.FadeIn(100, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + box.Colour = colourProvider.Background1; + + box.FadeTo(0.1f, 500, Easing.OutQuint); + text.FadeOut(100, Easing.OutQuint); + base.OnHoverLost(e); + } + + public void Flash() + { + box + .FadeTo(0.4f, 20, Easing.OutQuint) + .Then() + .FadeTo(0.2f, 400, Easing.OutQuint); + + text + .MoveToY(-5, 20, Easing.OutQuint) + .Then() + .MoveToY(0, 400, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index d0ab4d1f98..f9f5a20405 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -18,6 +19,9 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private EditorClock editorClock { get; set; } + [Resolved] + private EditorBeatmap beatmap { get; set; } + [Resolved] private Bindable selectedGroup { get; set; } @@ -45,6 +49,7 @@ namespace osu.Game.Screens.Edit.Timing { new Dimension(GridSizeMode.Absolute, 200), new Dimension(GridSizeMode.Absolute, 60), + new Dimension(GridSizeMode.Absolute, 60), }, Content = new[] { @@ -77,7 +82,36 @@ namespace osu.Game.Screens.Edit.Timing }, } } - } + }, + }, + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(10), + Children = new Drawable[] + { + new AdjustButton(1) + { + Text = "Offset", + RelativeSizeAxes = Axes.X, + Width = 0.48f, + Height = 50, + Action = adjustOffset, + }, + new AdjustButton(0.1) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = "BPM", + RelativeSizeAxes = Axes.X, + Width = 0.48f, + Height = 50, + Action = adjustBpm, + } + } + }, }, new Drawable[] { @@ -113,6 +147,35 @@ namespace osu.Game.Screens.Edit.Timing }; } + private void adjustOffset(double adjust) + { + // VERY TEMPORARY + var currentGroupItems = selectedGroup.Value.ControlPoints.ToArray(); + + beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value); + + double newOffset = selectedGroup.Value.Time + adjust; + + foreach (var cp in currentGroupItems) + beatmap.ControlPointInfo.Add(newOffset, cp); + + // the control point might not necessarily exist yet, if currentGroupItems was empty. + selectedGroup.Value = beatmap.ControlPointInfo.GroupAt(newOffset, true); + + if (!editorClock.IsRunning) + editorClock.Seek(newOffset); + } + + private void adjustBpm(double adjust) + { + var timing = selectedGroup.Value.ControlPoints.OfType().FirstOrDefault(); + + if (timing == null) + return; + + timing.BeatLength = 60000 / (timing.BPM + adjust); + } + private void tap() { editorClock.Seek(selectedGroup.Value.Time); From bc22079fdcf4c06d89c38298ba83a526abc6f7b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 31 May 2022 22:02:30 +0900 Subject: [PATCH 2/5] Fix row selected colour flicker when changing offset rapidly --- osu.Game/Screens/Edit/EditorTable.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index a67a060134..26819dcfe7 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -99,6 +99,15 @@ namespace osu.Game.Screens.Edit colourSelected = colours.Colour3; } + protected override void LoadComplete() + { + base.LoadComplete(); + + // Reduce flicker of rows when offset is being changed rapidly. + // Probably need to reconsider this. + FinishTransforms(true); + } + private bool selected; public bool Selected From 8c54bd46bb2209e8f09965b2a2e5e06d82311e2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Jun 2022 17:46:05 +0900 Subject: [PATCH 3/5] Rename button to more appropriate name --- osu.Game/Screens/Edit/Timing/TapTimingControl.cs | 4 ++-- .../Edit/Timing/{AdjustButton.cs => TimingAdjustButton.cs} | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) rename osu.Game/Screens/Edit/Timing/{AdjustButton.cs => TimingAdjustButton.cs} (96%) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index f9f5a20405..990f8d2ce0 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit.Timing Padding = new MarginPadding(10), Children = new Drawable[] { - new AdjustButton(1) + new TimingAdjustButton(1) { Text = "Offset", RelativeSizeAxes = Axes.X, @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Edit.Timing Height = 50, Action = adjustOffset, }, - new AdjustButton(0.1) + new TimingAdjustButton(0.1) { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, diff --git a/osu.Game/Screens/Edit/Timing/AdjustButton.cs b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs similarity index 96% rename from osu.Game/Screens/Edit/Timing/AdjustButton.cs rename to osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs index b0a0517e94..335f593772 100644 --- a/osu.Game/Screens/Edit/Timing/AdjustButton.cs +++ b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs @@ -16,7 +16,10 @@ using osu.Game.Overlays; namespace osu.Game.Screens.Edit.Timing { - public class AdjustButton : CompositeDrawable + /// + /// A button with variable constant output based on hold position and length. + /// + public class TimingAdjustButton : CompositeDrawable { public Action Action; @@ -50,7 +53,7 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private OverlayColourProvider colourProvider { get; set; } - public AdjustButton(double adjustAmount) + public TimingAdjustButton(double adjustAmount) { this.adjustAmount = adjustAmount; From 1293bbdbd931a7f17f5c5249e69593d0f16430c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Jun 2022 17:46:33 +0900 Subject: [PATCH 4/5] Remove unnecessary null checks on `Text` property --- osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs index 335f593772..46bb8356f7 100644 --- a/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs +++ b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs @@ -42,12 +42,8 @@ namespace osu.Game.Screens.Edit.Timing public LocalisableString Text { - get => text?.Text ?? default; - set - { - if (text != null) - text.Text = value; - } + get => text.Text; + set => text.Text = value; } [Resolved] From a4ec32b499dabcc1280033b1fe37688fbec1f5c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 1 Jun 2022 18:02:17 +0900 Subject: [PATCH 5/5] Add button sound effect --- .../Screens/Edit/Timing/TimingAdjustButton.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs index 46bb8356f7..9fc7e56a3d 100644 --- a/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs +++ b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs @@ -4,6 +4,8 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -26,6 +28,8 @@ namespace osu.Game.Screens.Edit.Timing private readonly double adjustAmount; private ScheduledDelegate adjustDelegate; + private const int max_multiplier = 10; + private const int adjust_levels = 4; private const double initial_delay = 300; @@ -40,6 +44,8 @@ namespace osu.Game.Screens.Edit.Timing private readonly OsuSpriteText text; + private Sample sample; + public LocalisableString Text { get => text.Text; @@ -79,8 +85,10 @@ namespace osu.Game.Screens.Edit.Timing } [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { + sample = audio.Samples.Get(@"UI/notch-tick"); + background.Colour = colourProvider.Background3; for (int i = 1; i <= adjust_levels; i++) @@ -120,6 +128,17 @@ namespace osu.Game.Screens.Edit.Timing adjustDelay = Math.Max(minimum_delay, adjustDelay * 0.9f); hoveredBox.Flash(); + + var channel = sample?.GetChannel(); + + if (channel != null) + { + double repeatModifier = 0.05f * (Math.Abs(adjustDelay - initial_delay) / minimum_delay); + double multiplierModifier = (hoveredBox.Multiplier / max_multiplier) * 0.2f; + + channel.Frequency.Value = 1 + multiplierModifier + repeatModifier; + channel.Play(); + } } else { @@ -195,7 +214,8 @@ namespace osu.Game.Screens.Edit.Timing case 3: return 5; - case 4: return 10; + case 4: + return max_multiplier; } }