From efbde06c11229a3a8501ece576b1a10c13cc70ae Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 3 Jun 2022 05:02:00 +0300 Subject: [PATCH 1/4] Split button repeating logic from `TimingAdjustButton` to own component --- .../Edit/Timing/RepeatingButtonBehaviour.cs | 93 +++++++++++++++++++ .../Screens/Edit/Timing/TimingAdjustButton.cs | 80 ++++------------ 2 files changed, 110 insertions(+), 63 deletions(-) create mode 100644 osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs diff --git a/osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs b/osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs new file mode 100644 index 0000000000..5c9f384a2e --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs @@ -0,0 +1,93 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Framework.Threading; + +namespace osu.Game.Screens.Edit.Timing +{ + /// + /// Represents a component that provides the behaviour of triggering button clicks repeatedly while holding with mouse. + /// + public class RepeatingButtonBehaviour : CompositeDrawable + { + private const double initial_delay = 300; + private const double minimum_delay = 80; + + private readonly Drawable button; + + private Sample sample; + + /// + /// An additive modifier for the frequency of the sample played on next actuation. + /// This can be adjusted during the button's event to affect the repeat sample playback of that click. + /// + public double SampleFrequencyModifier { get; set; } + + public RepeatingButtonBehaviour(Drawable button) + { + this.button = button; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sample = audio.Samples.Get(@"UI/notch-tick"); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + beginRepeat(); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + adjustDelegate?.Cancel(); + base.OnMouseUp(e); + } + + private ScheduledDelegate adjustDelegate; + private double adjustDelay = initial_delay; + + private void beginRepeat() + { + adjustDelegate?.Cancel(); + + adjustDelay = initial_delay; + adjustNext(); + + void adjustNext() + { + if (IsHovered) + { + button.TriggerClick(); + adjustDelay = Math.Max(minimum_delay, adjustDelay * 0.9f); + + var channel = sample?.GetChannel(); + + if (channel != null) + { + double repeatModifier = 0.05f * (Math.Abs(adjustDelay - initial_delay) / minimum_delay); + channel.Frequency.Value = 1 + repeatModifier + SampleFrequencyModifier; + channel.Play(); + } + } + else + { + adjustDelay = initial_delay; + } + + adjustDelegate = Scheduler.AddDelayed(adjustNext, adjustDelay); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs index 9fc7e56a3d..6e7338fa15 100644 --- a/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs +++ b/osu.Game/Screens/Edit/Timing/TimingAdjustButton.cs @@ -4,14 +4,11 @@ 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; 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; @@ -26,32 +23,24 @@ namespace osu.Game.Screens.Edit.Timing public Action Action; 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; - - private const double minimum_delay = 80; - public Container Content { get; set; } - private double adjustDelay = initial_delay; - private readonly Box background; private readonly OsuSpriteText text; - private Sample sample; - public LocalisableString Text { get => text.Text; set => text.Text = value; } + private readonly RepeatingButtonBehaviour repeatBehaviour; + [Resolved] private OverlayColourProvider colourProvider { get; set; } @@ -82,13 +71,13 @@ namespace osu.Game.Screens.Edit.Timing } } }); + + AddInternal(repeatBehaviour = new RepeatingButtonBehaviour(this)); } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load() { - sample = audio.Samples.Get(@"UI/notch-tick"); - background.Colour = colourProvider.Background3; for (int i = 1; i <= adjust_levels; i++) @@ -98,57 +87,22 @@ namespace osu.Game.Screens.Edit.Timing } } - protected override bool OnMouseDown(MouseDownEvent e) + protected override bool OnHover(HoverEvent e) => true; + + protected override bool OnClick(ClickEvent e) { - beginRepeat(); + var hoveredBox = Content.OfType().FirstOrDefault(d => d.IsHovered); + if (hoveredBox == null) + return false; + + Action(adjustAmount * hoveredBox.Multiplier); + + hoveredBox.Flash(); + + repeatBehaviour.SampleFrequencyModifier = (hoveredBox.Multiplier / max_multiplier) * 0.2; 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(); - - 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 - { - adjustDelay = initial_delay; - } - - adjustDelegate = Scheduler.AddDelayed(adjustNext, adjustDelay); - } - } - private class IncrementBox : CompositeDrawable { public readonly float Multiplier; From b51e0a50472e2415928a38dd9263bdfa04e3e8d5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 3 Jun 2022 05:03:22 +0300 Subject: [PATCH 2/4] Share button repeating logic with `TimelineButton` for better UX --- .../Components/Timeline/TimelineButton.cs | 47 ++----------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs index 5550c6a748..4c0c0335dd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs @@ -6,13 +6,11 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; -using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Edit.Timing; using osuTK; using osuTK.Graphics; -using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { @@ -27,7 +25,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline set => button.Icon = value; } - private readonly IconButton button; + private readonly TimelineIconButton button; public TimelineButton() { @@ -54,46 +52,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline IconHoverColour = Color4.White; HoverColour = OsuColour.Gray(0.25f); FlashColour = OsuColour.Gray(0.5f); + + Add(new RepeatingButtonBehaviour(this)); } - private ScheduledDelegate repeatSchedule; - - /// - /// The initial delay before mouse down repeat begins. - /// - private const int repeat_initial_delay = 250; - - /// - /// The delay between mouse down repeats after the initial repeat. - /// - private const int repeat_tick_rate = 70; - - protected override bool OnClick(ClickEvent e) - { - // don't actuate a click since we are manually handling repeats. - return true; - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - if (e.Button == MouseButton.Left) - { - Action clickAction = () => base.OnClick(new ClickEvent(e.CurrentState, e.Button)); - - // run once for initial down - clickAction(); - - Scheduler.Add(repeatSchedule = new ScheduledDelegate(clickAction, Clock.CurrentTime + repeat_initial_delay, repeat_tick_rate)); - } - - return base.OnMouseDown(e); - } - - protected override void OnMouseUp(MouseUpEvent e) - { - repeatSchedule?.Cancel(); - base.OnMouseUp(e); - } + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); } } } From e779b460e42cb89f4a196035164dda9f4cae9dd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 Jun 2022 16:49:08 +0900 Subject: [PATCH 3/4] Use `Component` instead of `CompositeDrawable` --- osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs b/osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs index 5c9f384a2e..595305b20f 100644 --- a/osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs +++ b/osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs @@ -6,7 +6,6 @@ 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.Input.Events; using osu.Framework.Threading; @@ -15,7 +14,7 @@ namespace osu.Game.Screens.Edit.Timing /// /// Represents a component that provides the behaviour of triggering button clicks repeatedly while holding with mouse. /// - public class RepeatingButtonBehaviour : CompositeDrawable + public class RepeatingButtonBehaviour : Component { private const double initial_delay = 300; private const double minimum_delay = 80; From cd3edc869c3455fd77aa0348566faccd079a1b97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 3 Jun 2022 17:00:00 +0900 Subject: [PATCH 4/4] Remove unnecessary nesting of `IconButton` and update design a touch --- .../Components/Timeline/TimelineButton.cs | 63 +++++-------------- 1 file changed, 15 insertions(+), 48 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs index 4c0c0335dd..e0b21b2e22 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineButton.cs @@ -1,62 +1,29 @@ // 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 osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; +using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; using osu.Game.Screens.Edit.Timing; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class TimelineButton : CompositeDrawable + public class TimelineButton : IconButton { - public Action Action; - public readonly BindableBool Enabled = new BindableBool(true); - - public IconUsage Icon + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) { - get => button.Icon; - set => button.Icon = value; + // These are using colourProvider but don't match the design. + // Just something to fit until someone implements the updated design. + IconColour = colourProvider.Background1; + IconHoverColour = colourProvider.Content2; + + HoverColour = colourProvider.Background1; + FlashColour = colourProvider.Content2; + + Add(new RepeatingButtonBehaviour(this)); } - private readonly TimelineIconButton button; - - public TimelineButton() - { - InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() }; - - button.Enabled.BindTo(Enabled); - Width = button.Width; - } - - protected override void Update() - { - base.Update(); - - button.Size = new Vector2(button.Width, DrawHeight); - } - - private class TimelineIconButton : IconButton - { - public TimelineIconButton() - { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - IconColour = OsuColour.Gray(0.35f); - IconHoverColour = Color4.White; - HoverColour = OsuColour.Gray(0.25f); - FlashColour = OsuColour.Gray(0.5f); - - Add(new RepeatingButtonBehaviour(this)); - } - - protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); - } + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); } }