From decd8803bc6daa0669003e22115672dbda48033f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 24 Apr 2021 07:44:33 +0300 Subject: [PATCH 01/23] Abstract base appearence and hover update from drag handles --- .../Compose/Components/SelectionBoxControl.cs | 96 +++++++++++++++++++ .../Components/SelectionBoxDragHandle.cs | 77 +-------------- .../Components/SelectionBoxScaleHandle.cs | 17 ++++ 3 files changed, 116 insertions(+), 74 deletions(-) create mode 100644 osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs create mode 100644 osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs new file mode 100644 index 0000000000..3432334deb --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -0,0 +1,96 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + /// + /// Represents the base appearance for UI controls of the , + /// such as scale handles, rotation handles, buttons, etc... + /// + public abstract class SelectionBoxControl : CompositeDrawable + { + public event Action OperationStarted; + public event Action OperationEnded; + + internal event Action HoverGained; + internal event Action HoverLost; + + private Circle circle; + + [Resolved] + protected OsuColour Colours { get; private set; } + + [BackgroundDependencyLoader] + private void load() + { + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + circle = new Circle + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + UpdateHoverState(); + } + + protected override bool OnHover(HoverEvent e) + { + UpdateHoverState(); + HoverGained?.Invoke(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + HoverLost?.Invoke(); + UpdateHoverState(); + } + + /// + /// Whether this control is currently handling mouse down input. + /// + protected bool HandlingMouse { get; set; } + + protected override bool OnMouseDown(MouseDownEvent e) + { + HandlingMouse = true; + UpdateHoverState(); + return true; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + HandlingMouse = false; + UpdateHoverState(); + base.OnMouseUp(e); + } + + protected virtual void UpdateHoverState() + { + circle.Colour = HandlingMouse ? Colours.GrayF : (IsHovered ? Colours.Red : Colours.YellowDark); + this.ScaleTo(HandlingMouse || IsHovered ? 1.5f : 1, 100, Easing.OutQuint); + } + + protected void OnOperationStarted() => OperationStarted?.Invoke(); + + protected void OnOperationEnded() => OperationEnded?.Invoke(); + } +} diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs index 921b4eb042..ea4f9704ef 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs @@ -2,75 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System; -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.Game.Graphics; -using osuTK; namespace osu.Game.Screens.Edit.Compose.Components { - public class SelectionBoxDragHandle : Container + public abstract class SelectionBoxDragHandle : SelectionBoxControl { - public Action OperationStarted; - public Action OperationEnded; - public Action HandleDrag { get; set; } - private Circle circle; - - [Resolved] - private OsuColour colours { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - Size = new Vector2(10); - Origin = Anchor.Centre; - - InternalChildren = new Drawable[] - { - circle = new Circle - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - UpdateHoverState(); - } - - protected override bool OnHover(HoverEvent e) - { - UpdateHoverState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - UpdateHoverState(); - } - - protected bool HandlingMouse; - - protected override bool OnMouseDown(MouseDownEvent e) - { - HandlingMouse = true; - UpdateHoverState(); - return true; - } - protected override bool OnDragStart(DragStartEvent e) { - OperationStarted?.Invoke(); + OnOperationStarted(); return true; } @@ -83,23 +25,10 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void OnDragEnd(DragEndEvent e) { HandlingMouse = false; - OperationEnded?.Invoke(); + OnOperationEnded(); UpdateHoverState(); base.OnDragEnd(e); } - - protected override void OnMouseUp(MouseUpEvent e) - { - HandlingMouse = false; - UpdateHoverState(); - base.OnMouseUp(e); - } - - protected virtual void UpdateHoverState() - { - circle.Colour = HandlingMouse ? colours.GrayF : (IsHovered ? colours.Red : colours.YellowDark); - this.ScaleTo(HandlingMouse || IsHovered ? 1.5f : 1, 100, Easing.OutQuint); - } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs new file mode 100644 index 0000000000..a87c661f45 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxScaleHandle.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osuTK; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + public class SelectionBoxScaleHandle : SelectionBoxDragHandle + { + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(10); + } + } +} From 4bfa9cd6b6014a40666bf00492c95c8d888560f7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 24 Apr 2021 07:46:38 +0300 Subject: [PATCH 02/23] Change inheritance of selection box buttons to base control instead --- ...BoxDragHandleButton.cs => SelectionBoxButton.cs} | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) rename osu.Game/Screens/Edit/Compose/Components/{SelectionBoxDragHandleButton.cs => SelectionBoxButton.cs} (77%) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleButton.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs similarity index 77% rename from osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleButton.cs rename to osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs index 74ae949389..917e5189b2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs @@ -12,10 +12,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components { - /// - /// A drag "handle" which shares the visual appearance but behaves more like a clickable button. - /// - public sealed class SelectionBoxDragHandleButton : SelectionBoxDragHandle, IHasTooltip + public sealed class SelectionBoxButton : SelectionBoxControl, IHasTooltip { private SpriteIcon icon; @@ -23,7 +20,7 @@ namespace osu.Game.Screens.Edit.Compose.Components public Action Action; - public SelectionBoxDragHandleButton(IconUsage iconUsage, string tooltip) + public SelectionBoxButton(IconUsage iconUsage, string tooltip) { this.iconUsage = iconUsage; @@ -36,7 +33,7 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { - Size *= 2; + Size = new Vector2(20); AddInternal(icon = new SpriteIcon { RelativeSizeAxes = Axes.Both, @@ -49,9 +46,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnClick(ClickEvent e) { - OperationStarted?.Invoke(); + OnOperationStarted(); Action?.Invoke(); - OperationEnded?.Invoke(); + OnOperationEnded(); return true; } From 206fc94b8c9e71be4dc3049003f63147bf735a22 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 24 Apr 2021 07:47:15 +0300 Subject: [PATCH 03/23] Add rotation drag handle component --- .../Components/SelectionBoxRotationHandle.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs new file mode 100644 index 0000000000..c4a4fafdc2 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.EnumExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + public class SelectionBoxRotationHandle : SelectionBoxDragHandle + { + private SpriteIcon icon; + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(15f); + AddInternal(icon = new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Redo, + Scale = new Vector2 + { + X = Anchor.HasFlagFast(Anchor.x0) ? 1f : -1f, + Y = Anchor.HasFlagFast(Anchor.y0) ? 1f : -1f + } + }); + } + + protected override void UpdateHoverState() + { + icon.Colour = !HandlingMouse && IsHovered ? Color4.White : Color4.Black; + base.UpdateHoverState(); + } + } +} From 62bcc5f76d891f45addc79500350be050f3b7fe9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 07:49:31 +0300 Subject: [PATCH 04/23] Add property for tracking whether control is during operation --- .../Compose/Components/SelectionBoxControl.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 3432334deb..081848f372 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -25,6 +25,11 @@ namespace osu.Game.Screens.Edit.Compose.Components private Circle circle; + /// + /// Whether an operation has began from this control. + /// + public bool InOperation { get; private set; } + [Resolved] protected OsuColour Colours { get; private set; } @@ -89,8 +94,16 @@ namespace osu.Game.Screens.Edit.Compose.Components this.ScaleTo(HandlingMouse || IsHovered ? 1.5f : 1, 100, Easing.OutQuint); } - protected void OnOperationStarted() => OperationStarted?.Invoke(); + protected void OnOperationStarted() + { + InOperation = true; + OperationStarted?.Invoke(); + } - protected void OnOperationEnded() => OperationEnded?.Invoke(); + protected void OnOperationEnded() + { + InOperation = false; + OperationEnded?.Invoke(); + } } } From ab7178267459a27b5a3a9896013cd09fe250dac9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 07:43:56 +0300 Subject: [PATCH 05/23] Use colour fade transform for selection box controls To become harminous with the fade transforms of the rotation control --- .../Edit/Compose/Components/SelectionBoxButton.cs | 2 +- .../Edit/Compose/Components/SelectionBoxControl.cs | 10 ++++++++-- .../Compose/Components/SelectionBoxRotationHandle.cs | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs index 917e5189b2..fbbebe3288 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void UpdateHoverState() { base.UpdateHoverState(); - icon.Colour = !HandlingMouse && IsHovered ? Color4.White : Color4.Black; + icon.FadeColour(!HandlingMouse && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); } public string TooltipText { get; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 081848f372..374af20742 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -17,6 +17,8 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public abstract class SelectionBoxControl : CompositeDrawable { + public const double TRANSFORM_DURATION = 100; + public event Action OperationStarted; public event Action OperationEnded; @@ -90,8 +92,12 @@ namespace osu.Game.Screens.Edit.Compose.Components protected virtual void UpdateHoverState() { - circle.Colour = HandlingMouse ? Colours.GrayF : (IsHovered ? Colours.Red : Colours.YellowDark); - this.ScaleTo(HandlingMouse || IsHovered ? 1.5f : 1, 100, Easing.OutQuint); + if (HandlingMouse) + circle.FadeColour(Colours.GrayF, TRANSFORM_DURATION, Easing.OutQuint); + else + circle.FadeColour(IsHovered ? Colours.Red : Colours.YellowDark, TRANSFORM_DURATION, Easing.OutQuint); + + this.ScaleTo(HandlingMouse || IsHovered ? 1.5f : 1, TRANSFORM_DURATION, Easing.OutQuint); } protected void OnOperationStarted() diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index c4a4fafdc2..6303caf9ed 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -35,8 +35,8 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void UpdateHoverState() { - icon.Colour = !HandlingMouse && IsHovered ? Color4.White : Color4.Black; base.UpdateHoverState(); + icon.FadeColour(!HandlingMouse && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); } } } From 77f7d4c9632e281d8665a81c55252f12b181e163 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 24 Apr 2021 08:06:00 +0300 Subject: [PATCH 06/23] Add composite managing display of selection box drag handles --- .../SelectionBoxDragHandleDisplay.cs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleDisplay.cs diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleDisplay.cs new file mode 100644 index 0000000000..1cba8ca6b3 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleDisplay.cs @@ -0,0 +1,103 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Compose.Components +{ + /// + /// Represents a display composite containing and managing the visibility state of the selection box's drag handles. + /// + public class SelectionBoxDragHandleDisplay : CompositeDrawable + { + private Container scaleHandles; + private Container rotationHandles; + + private readonly List allDragHandles = new List(); + + public new MarginPadding Padding + { + get => base.Padding; + set => base.Padding = value; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + scaleHandles = new Container + { + RelativeSizeAxes = Axes.Both, + }, + rotationHandles = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(-12.5f), + }, + }; + } + + public void AddScaleHandle(SelectionBoxScaleHandle handle) + { + bindDragHandle(handle); + scaleHandles.Add(handle); + } + + public void AddRotationHandle(SelectionBoxRotationHandle handle) + { + handle.Alpha = 0; + handle.AlwaysPresent = true; + + bindDragHandle(handle); + rotationHandles.Add(handle); + } + + private void bindDragHandle(SelectionBoxDragHandle handle) + { + handle.HoverGained += updateRotationHandlesVisibility; + handle.HoverLost += updateRotationHandlesVisibility; + handle.OperationStarted += updateRotationHandlesVisibility; + handle.OperationEnded += updateRotationHandlesVisibility; + allDragHandles.Add(handle); + } + + private SelectionBoxRotationHandle displayedRotationHandle; + private SelectionBoxDragHandle activeHandle; + + private void updateRotationHandlesVisibility() + { + if (activeHandle?.InOperation == true || activeHandle?.IsHovered == true) + return; + + displayedRotationHandle?.FadeOut(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); + displayedRotationHandle = null; + + activeHandle = allDragHandles.SingleOrDefault(h => h.InOperation); + activeHandle ??= allDragHandles.SingleOrDefault(h => h.IsHovered); + + if (activeHandle != null) + { + displayedRotationHandle = getCorrespondingRotationHandle(activeHandle, rotationHandles); + displayedRotationHandle?.FadeIn(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); + } + } + + /// + /// Gets the rotation handle corresponding to the given handle. + /// + [CanBeNull] + private static SelectionBoxRotationHandle getCorrespondingRotationHandle(SelectionBoxDragHandle handle, IEnumerable rotationHandles) + { + if (handle is SelectionBoxRotationHandle rotationHandle) + return rotationHandle; + + return rotationHandles.SingleOrDefault(r => r.Anchor == handle.Anchor); + } + } +} From a8c460f7df0924830c3ad7c3b657a04eda8ed4c7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 24 Apr 2021 08:18:46 +0300 Subject: [PATCH 07/23] Replace separated top-centered rotation button with rotation drag handles --- .../Edit/Compose/Components/SelectionBox.cs | 87 ++++++++++--------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 9d6b44e207..d7db98f6a2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - private Container dragHandles; + private SelectionBoxDragHandleDisplay dragHandles; private FillFlowContainer buttons; public const float BORDER_RADIUS = 3; @@ -161,7 +161,7 @@ namespace osu.Game.Screens.Edit.Compose.Components }, } }, - dragHandles = new Container + dragHandles = new SelectionBoxDragHandleDisplay { RelativeSizeAxes = Axes.Both, // ensures that the centres of all drag handles line up with the middle of the selection box border. @@ -186,75 +186,76 @@ namespace osu.Game.Screens.Edit.Compose.Components private void addRotationComponents() { - const float separation = 40; - addButton(FontAwesome.Solid.Undo, "Rotate 90 degrees counter-clockwise", () => OnRotation?.Invoke(-90)); addButton(FontAwesome.Solid.Redo, "Rotate 90 degrees clockwise", () => OnRotation?.Invoke(90)); - AddRangeInternal(new Drawable[] - { - new Box - { - Depth = float.MaxValue, - Colour = colours.YellowLight, - Blending = BlendingParameters.Additive, - Alpha = 0.3f, - Size = new Vector2(BORDER_RADIUS, separation), - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - }, - new SelectionBoxDragHandleButton(FontAwesome.Solid.Redo, "Free rotate") - { - Anchor = Anchor.TopCentre, - Y = -separation, - HandleDrag = e => OnRotation?.Invoke(convertDragEventToAngleOfRotation(e)), - OperationStarted = operationStarted, - OperationEnded = operationEnded - } - }); + addRotateHandle(Anchor.TopLeft); + addRotateHandle(Anchor.TopRight); + addRotateHandle(Anchor.BottomLeft); + addRotateHandle(Anchor.BottomRight); } private void addYScaleComponents() { addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically (Ctrl-J)", () => OnFlip?.Invoke(Direction.Vertical)); - addDragHandle(Anchor.TopCentre); - addDragHandle(Anchor.BottomCentre); + addScaleHandle(Anchor.TopCentre); + addScaleHandle(Anchor.BottomCentre); } private void addFullScaleComponents() { - addDragHandle(Anchor.TopLeft); - addDragHandle(Anchor.TopRight); - addDragHandle(Anchor.BottomLeft); - addDragHandle(Anchor.BottomRight); + addScaleHandle(Anchor.TopLeft); + addScaleHandle(Anchor.TopRight); + addScaleHandle(Anchor.BottomLeft); + addScaleHandle(Anchor.BottomRight); } private void addXScaleComponents() { addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally (Ctrl-H)", () => OnFlip?.Invoke(Direction.Horizontal)); - addDragHandle(Anchor.CentreLeft); - addDragHandle(Anchor.CentreRight); + addScaleHandle(Anchor.CentreLeft); + addScaleHandle(Anchor.CentreRight); } private void addButton(IconUsage icon, string tooltip, Action action) { - buttons.Add(new SelectionBoxDragHandleButton(icon, tooltip) + var button = new SelectionBoxButton(icon, tooltip) { - OperationStarted = operationStarted, - OperationEnded = operationEnded, Action = action - }); + }; + + button.OperationStarted += operationStarted; + button.OperationEnded += operationEnded; + buttons.Add(button); } - private void addDragHandle(Anchor anchor) => dragHandles.Add(new SelectionBoxDragHandle + private void addScaleHandle(Anchor anchor) { - Anchor = anchor, - HandleDrag = e => OnScale?.Invoke(e.Delta, anchor), - OperationStarted = operationStarted, - OperationEnded = operationEnded - }); + var handle = new SelectionBoxScaleHandle + { + Anchor = anchor, + HandleDrag = e => OnScale?.Invoke(e.Delta, anchor) + }; + + handle.OperationStarted += operationStarted; + handle.OperationEnded += operationEnded; + dragHandles.AddScaleHandle(handle); + } + + private void addRotateHandle(Anchor anchor) + { + var handle = new SelectionBoxRotationHandle + { + Anchor = anchor, + HandleDrag = e => OnRotation?.Invoke(convertDragEventToAngleOfRotation(e)) + }; + + handle.OperationStarted += operationStarted; + handle.OperationEnded += operationEnded; + dragHandles.AddRotationHandle(handle); + } private int activeOperations; From 11318fd9fcbf8ffc4b9830dd4285037237e0f7b8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 24 Apr 2021 09:35:02 +0300 Subject: [PATCH 08/23] Add test coverage --- .../Editing/TestSceneComposeSelectBox.cs | 98 ++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index cf5f1b8818..13b563619a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -1,21 +1,24 @@ // 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 NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Screens.Edit.Compose.Components; using osuTK; +using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public class TestSceneComposeSelectBox : OsuTestScene + public class TestSceneComposeSelectBox : OsuManualInputManagerTestScene { private Container selectionArea; + private SelectionBox selectionBox; public TestSceneComposeSelectBox() { - SelectionBox selectionBox = null; - AddStep("create box", () => Child = selectionArea = new Container { @@ -68,5 +71,94 @@ namespace osu.Game.Tests.Visual.Editing selectionArea.Rotation += angle; return true; } + + [SetUp] + public void SetUp() => Schedule(() => + { + InputManager.MoveMouseTo(selectionBox); + selectionBox.CanRotate = true; + selectionBox.CanScaleX = true; + selectionBox.CanScaleY = true; + }); + + [Test] + public void TestRotationHandleShownOnHover() + { + SelectionBoxRotationHandle rotationHandle = null; + + AddStep("enable rotation", () => selectionBox.CanRotate = true); + AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType().First()); + + AddAssert("handle hidden", () => rotationHandle.Alpha == 0); + AddStep("hover over handle", () => InputManager.MoveMouseTo(rotationHandle)); + AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1); + + AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox)); + AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0); + } + + [Test] + public void TestRotationHandleShownOnHoveringClosestScaleHandler() + { + SelectionBoxRotationHandle rotationHandle = null; + + AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType().First()); + + AddAssert("rotation handle hidden", () => rotationHandle.Alpha == 0); + AddStep("hover over closest scale handle", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single(s => s.Anchor == rotationHandle.Anchor)); + }); + AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1); + + AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox)); + AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0); + } + + [Test] + public void TestHoverRotationHandleFromScaleHandle() + { + SelectionBoxRotationHandle rotationHandle = null; + + AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType().First()); + + AddAssert("rotation handle hidden", () => rotationHandle.Alpha == 0); + AddStep("hover over closest scale handle", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single(s => s.Anchor == rotationHandle.Anchor)); + }); + AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1); + AddAssert("rotation handle not hovered", () => !rotationHandle.IsHovered); + + AddStep("hover over rotation handle", () => InputManager.MoveMouseTo(rotationHandle)); + AddAssert("rotation handle still shown", () => rotationHandle.Alpha == 1); + AddAssert("rotation handle hovered", () => rotationHandle.IsHovered); + + AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox)); + AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0); + } + + [Test] + public void TestDraggingScaleHandleKeepsCorrespondingRotationHandleShown() + { + SelectionBoxRotationHandle rotationHandle = null; + + AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType().First()); + + AddAssert("rotation handle hidden", () => rotationHandle.Alpha == 0); + AddStep("hover over and hold closest scale handle", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single(s => s.Anchor == rotationHandle.Anchor)); + InputManager.PressButton(MouseButton.Left); + }); + AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1); + + AddStep("drag to centre", () => InputManager.MoveMouseTo(selectionBox)); + AddAssert("rotation handle still shown", () => rotationHandle.Alpha > 0); + + AddStep("unhold left", () => InputManager.ReleaseButton(MouseButton.Left)); + AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox, new Vector2(20))); + AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0); + } } } From 79e2b232d8a18775731bd3cca9c479d787ca64b8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 19:26:53 +0300 Subject: [PATCH 09/23] Improve English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 374af20742..9ad30a366d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private Circle circle; /// - /// Whether an operation has began from this control. + /// Whether this control is currently being operated on by the user. /// public bool InOperation { get; private set; } From 7490511ebf4b869a1b98793ac773fb92b1f6714e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 19:57:16 +0300 Subject: [PATCH 10/23] Instantiate selection box on SetUp --- .../Editing/TestSceneComposeSelectBox.cs | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index 13b563619a..7b939245a4 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -17,32 +17,30 @@ namespace osu.Game.Tests.Visual.Editing private Container selectionArea; private SelectionBox selectionBox; - public TestSceneComposeSelectBox() + [SetUp] + public void SetUp() => Schedule(() => { - AddStep("create box", () => - Child = selectionArea = new Container + Child = selectionArea = new Container + { + Size = new Vector2(400), + Position = -new Vector2(150), + Anchor = Anchor.Centre, + Children = new Drawable[] { - Size = new Vector2(400), - Position = -new Vector2(150), - Anchor = Anchor.Centre, - Children = new Drawable[] + selectionBox = new SelectionBox { - selectionBox = new SelectionBox - { - CanRotate = true, - CanScaleX = true, - CanScaleY = true, + CanRotate = true, + CanScaleX = true, + CanScaleY = true, - OnRotation = handleRotation, - OnScale = handleScale - } + OnRotation = handleRotation, + OnScale = handleScale } - }); + } + }; - AddToggleStep("toggle rotation", state => selectionBox.CanRotate = state); - AddToggleStep("toggle x", state => selectionBox.CanScaleX = state); - AddToggleStep("toggle y", state => selectionBox.CanScaleY = state); - } + InputManager.MoveMouseTo(selectionBox); + }); private bool handleScale(Vector2 amount, Anchor reference) { @@ -72,15 +70,6 @@ namespace osu.Game.Tests.Visual.Editing return true; } - [SetUp] - public void SetUp() => Schedule(() => - { - InputManager.MoveMouseTo(selectionBox); - selectionBox.CanRotate = true; - selectionBox.CanScaleX = true; - selectionBox.CanScaleY = true; - }); - [Test] public void TestRotationHandleShownOnHover() { From c58ef4230da768aad729e4785a156c80e83bad54 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 19:58:50 +0300 Subject: [PATCH 11/23] Inherit `VisibilityContainer` and make duration constant protected --- .../Edit/Compose/Components/SelectionBoxControl.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 9ad30a366d..501ce6381a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -15,9 +15,9 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Represents the base appearance for UI controls of the , /// such as scale handles, rotation handles, buttons, etc... /// - public abstract class SelectionBoxControl : CompositeDrawable + public abstract class SelectionBoxControl : VisibilityContainer { - public const double TRANSFORM_DURATION = 100; + protected const double TRANSFORM_DURATION = 100; public event Action OperationStarted; public event Action OperationEnded; @@ -90,6 +90,10 @@ namespace osu.Game.Screens.Edit.Compose.Components base.OnMouseUp(e); } + protected override void PopIn() => this.FadeIn(TRANSFORM_DURATION, Easing.OutQuint); + + protected override void PopOut() => this.FadeOut(TRANSFORM_DURATION, Easing.OutQuint); + protected virtual void UpdateHoverState() { if (HandlingMouse) From 7390a12e4b9bda91f0d4f4d374094abaf3fece92 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 19:59:08 +0300 Subject: [PATCH 12/23] SelectionBoxDragHandleDisplay -> SelectionBoxDragHandleContainer --- osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs | 4 ++-- ...gHandleDisplay.cs => SelectionBoxDragHandleContainer.cs} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game/Screens/Edit/Compose/Components/{SelectionBoxDragHandleDisplay.cs => SelectionBoxDragHandleContainer.cs} (92%) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index d7db98f6a2..774ea076a5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - private SelectionBoxDragHandleDisplay dragHandles; + private SelectionBoxDragHandleContainer dragHandles; private FillFlowContainer buttons; public const float BORDER_RADIUS = 3; @@ -161,7 +161,7 @@ namespace osu.Game.Screens.Edit.Compose.Components }, } }, - dragHandles = new SelectionBoxDragHandleDisplay + dragHandles = new SelectionBoxDragHandleContainer { RelativeSizeAxes = Axes.Both, // ensures that the centres of all drag handles line up with the middle of the selection box border. diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs similarity index 92% rename from osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleDisplay.cs rename to osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs index 1cba8ca6b3..151c169a33 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Represents a display composite containing and managing the visibility state of the selection box's drag handles. /// - public class SelectionBoxDragHandleDisplay : CompositeDrawable + public class SelectionBoxDragHandleContainer : CompositeDrawable { private Container scaleHandles; private Container rotationHandles; @@ -75,7 +75,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (activeHandle?.InOperation == true || activeHandle?.IsHovered == true) return; - displayedRotationHandle?.FadeOut(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); + displayedRotationHandle?.Hide(); displayedRotationHandle = null; activeHandle = allDragHandles.SingleOrDefault(h => h.InOperation); @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (activeHandle != null) { displayedRotationHandle = getCorrespondingRotationHandle(activeHandle, rotationHandles); - displayedRotationHandle?.FadeIn(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); + displayedRotationHandle?.Show(); } } From 334927ed35f3de3edae71467fafe69808c660008 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 20:11:41 +0300 Subject: [PATCH 13/23] Remove leftover step --- osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index 7b939245a4..914b8b6f27 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -75,7 +75,6 @@ namespace osu.Game.Tests.Visual.Editing { SelectionBoxRotationHandle rotationHandle = null; - AddStep("enable rotation", () => selectionBox.CanRotate = true); AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType().First()); AddAssert("handle hidden", () => rotationHandle.Alpha == 0); From b252485e1ab0dd5bfa38b35ce315219869fb45b0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 25 Apr 2021 20:13:21 +0300 Subject: [PATCH 14/23] Remove protected expose of `HandlingMouse` setter Regardless of `OnDragEnd()`, `OnMouseUp()` will still be called resetting the value of that state back. --- osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs | 2 +- .../Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 501ce6381a..3a2fc7f83a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Whether this control is currently handling mouse down input. /// - protected bool HandlingMouse { get; set; } + protected bool HandlingMouse { get; private set; } protected override bool OnMouseDown(MouseDownEvent e) { diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs index ea4f9704ef..d7e58df748 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs @@ -24,7 +24,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void OnDragEnd(DragEndEvent e) { - HandlingMouse = false; OnOperationEnded(); UpdateHoverState(); From 485da47d89d9fa4786935fcbd64c9a1d3b6eaac4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 26 Apr 2021 01:41:36 +0300 Subject: [PATCH 15/23] Revert "Inherit `VisibilityContainer` and make duration constant protected" This reverts commit c58ef4230da768aad729e4785a156c80e83bad54. Uhh, how did I push this.. --- .../Edit/Compose/Components/SelectionBoxControl.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 3a2fc7f83a..38ed23fa13 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -15,9 +15,9 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Represents the base appearance for UI controls of the , /// such as scale handles, rotation handles, buttons, etc... /// - public abstract class SelectionBoxControl : VisibilityContainer + public abstract class SelectionBoxControl : CompositeDrawable { - protected const double TRANSFORM_DURATION = 100; + public const double TRANSFORM_DURATION = 100; public event Action OperationStarted; public event Action OperationEnded; @@ -90,10 +90,6 @@ namespace osu.Game.Screens.Edit.Compose.Components base.OnMouseUp(e); } - protected override void PopIn() => this.FadeIn(TRANSFORM_DURATION, Easing.OutQuint); - - protected override void PopOut() => this.FadeOut(TRANSFORM_DURATION, Easing.OutQuint); - protected virtual void UpdateHoverState() { if (HandlingMouse) From beee318acc92bd0831bcd19984807c7f029e54bf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 26 Apr 2021 01:45:50 +0300 Subject: [PATCH 16/23] Add more distance between each hit object in editor selection test To avoid potentially hovering over the rotation control instead of wherever the test desired to move the mouse to. --- .../Visual/Editing/TestSceneEditorSelection.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs index b82842e30d..4ce1d995a5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs @@ -50,9 +50,9 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] { new HitCircle { StartTime = 100 }, - new HitCircle { StartTime = 200, Position = new Vector2(50) }, - new HitCircle { StartTime = 300, Position = new Vector2(100) }, - new HitCircle { StartTime = 400, Position = new Vector2(150) }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, })); AddStep("select objects", () => EditorBeatmap.SelectedHitObjects.AddRange(addedObjects)); @@ -95,9 +95,9 @@ namespace osu.Game.Tests.Visual.Editing var addedObjects = new[] { new HitCircle { StartTime = 100 }, - new HitCircle { StartTime = 200, Position = new Vector2(50) }, - new HitCircle { StartTime = 300, Position = new Vector2(100) }, - new HitCircle { StartTime = 400, Position = new Vector2(150) }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, }; AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); @@ -131,9 +131,9 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] { new HitCircle { StartTime = 100 }, - new HitCircle { StartTime = 200, Position = new Vector2(50) }, - new HitCircle { StartTime = 300, Position = new Vector2(100) }, - new HitCircle { StartTime = 400, Position = new Vector2(150) }, + new HitCircle { StartTime = 200, Position = new Vector2(100) }, + new HitCircle { StartTime = 300, Position = new Vector2(200) }, + new HitCircle { StartTime = 400, Position = new Vector2(300) }, })); moveMouseToObject(() => addedObjects[0]); From 840c22a3b17bd01dea5b79c4a6bdd0e124b04241 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 3 May 2021 12:16:40 +0300 Subject: [PATCH 17/23] Add back mis-removed fade transform --- .../Compose/Components/SelectionBoxDragHandleContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs index 151c169a33..7db2cdbbf5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs @@ -75,7 +75,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (activeHandle?.InOperation == true || activeHandle?.IsHovered == true) return; - displayedRotationHandle?.Hide(); + displayedRotationHandle?.FadeOut(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); displayedRotationHandle = null; activeHandle = allDragHandles.SingleOrDefault(h => h.InOperation); @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (activeHandle != null) { displayedRotationHandle = getCorrespondingRotationHandle(activeHandle, rotationHandles); - displayedRotationHandle?.Show(); + displayedRotationHandle?.FadeIn(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); } } From fd7a6b3a7c953c682154395abc517607dfff66bf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 3 May 2021 12:19:34 +0300 Subject: [PATCH 18/23] Finish transforms on controls load complete --- osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 38ed23fa13..9cba07e267 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -54,7 +54,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void LoadComplete() { base.LoadComplete(); + UpdateHoverState(); + FinishTransforms(true); } protected override bool OnHover(HoverEvent e) From 5f33c3514ec43a3df22ade1dc86e2a8b384c0c83 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 06:37:18 +0300 Subject: [PATCH 19/23] Move selection box control internal events to drag handles --- .../Compose/Components/SelectionBoxControl.cs | 7 ------- .../Components/SelectionBoxDragHandle.cs | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 9cba07e267..4406cc055d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -22,9 +22,6 @@ namespace osu.Game.Screens.Edit.Compose.Components public event Action OperationStarted; public event Action OperationEnded; - internal event Action HoverGained; - internal event Action HoverLost; - private Circle circle; /// @@ -62,14 +59,11 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnHover(HoverEvent e) { UpdateHoverState(); - HoverGained?.Invoke(); return true; } protected override void OnHoverLost(HoverLostEvent e) { - base.OnHoverLost(e); - HoverLost?.Invoke(); UpdateHoverState(); } @@ -89,7 +83,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { HandlingMouse = false; UpdateHoverState(); - base.OnMouseUp(e); } protected virtual void UpdateHoverState() diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs index d7e58df748..ed7c451b7e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs @@ -29,5 +29,25 @@ namespace osu.Game.Screens.Edit.Compose.Components UpdateHoverState(); base.OnDragEnd(e); } + + #region Internal events for SelectionBoxDragHandleContainer + + internal event Action HoverGained; + internal event Action HoverLost; + + protected override bool OnHover(HoverEvent e) + { + bool result = base.OnHover(e); + HoverGained?.Invoke(); + return result; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + HoverLost?.Invoke(); + } + + #endregion } } From b2a0c2b5631ac3a3e5d746909d141fafbf655ce3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 06:40:43 +0300 Subject: [PATCH 20/23] Consider drag handles active using mouse down instead of when dragged --- .../Compose/Components/SelectionBoxButton.cs | 2 +- .../Compose/Components/SelectionBoxControl.cs | 29 +++++-------------- .../Components/SelectionBoxDragHandle.cs | 15 ++++++++++ .../SelectionBoxDragHandleContainer.cs | 8 ++--- .../Components/SelectionBoxRotationHandle.cs | 2 +- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs index fbbebe3288..43cbbb617b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void UpdateHoverState() { base.UpdateHoverState(); - icon.FadeColour(!HandlingMouse && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); + icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); } public string TooltipText { get; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 4406cc055d..159886648e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -25,9 +25,9 @@ namespace osu.Game.Screens.Edit.Compose.Components private Circle circle; /// - /// Whether this control is currently being operated on by the user. + /// Whether the user is currently holding the control with mouse. /// - public bool InOperation { get; private set; } + public bool IsHeld { get; private set; } [Resolved] protected OsuColour Colours { get; private set; } @@ -67,44 +67,31 @@ namespace osu.Game.Screens.Edit.Compose.Components UpdateHoverState(); } - /// - /// Whether this control is currently handling mouse down input. - /// - protected bool HandlingMouse { get; private set; } - protected override bool OnMouseDown(MouseDownEvent e) { - HandlingMouse = true; + IsHeld = true; UpdateHoverState(); return true; } protected override void OnMouseUp(MouseUpEvent e) { - HandlingMouse = false; + IsHeld = false; UpdateHoverState(); } protected virtual void UpdateHoverState() { - if (HandlingMouse) + if (IsHeld) circle.FadeColour(Colours.GrayF, TRANSFORM_DURATION, Easing.OutQuint); else circle.FadeColour(IsHovered ? Colours.Red : Colours.YellowDark, TRANSFORM_DURATION, Easing.OutQuint); - this.ScaleTo(HandlingMouse || IsHovered ? 1.5f : 1, TRANSFORM_DURATION, Easing.OutQuint); + this.ScaleTo(IsHeld || IsHovered ? 1.5f : 1, TRANSFORM_DURATION, Easing.OutQuint); } - protected void OnOperationStarted() - { - InOperation = true; - OperationStarted?.Invoke(); - } + protected void OnOperationStarted() => OperationStarted?.Invoke(); - protected void OnOperationEnded() - { - InOperation = false; - OperationEnded?.Invoke(); - } + protected void OnOperationEnded() => OperationEnded?.Invoke(); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs index ed7c451b7e..3c1741e24d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs @@ -34,6 +34,8 @@ namespace osu.Game.Screens.Edit.Compose.Components internal event Action HoverGained; internal event Action HoverLost; + internal event Action MouseDown; + internal event Action MouseUp; protected override bool OnHover(HoverEvent e) { @@ -48,6 +50,19 @@ namespace osu.Game.Screens.Edit.Compose.Components HoverLost?.Invoke(); } + protected override bool OnMouseDown(MouseDownEvent e) + { + bool result = base.OnMouseDown(e); + MouseDown?.Invoke(); + return result; + } + + protected override void OnMouseUp(MouseUpEvent e) + { + base.OnMouseUp(e); + MouseUp?.Invoke(); + } + #endregion } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs index 7db2cdbbf5..c78514b7db 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs @@ -62,8 +62,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { handle.HoverGained += updateRotationHandlesVisibility; handle.HoverLost += updateRotationHandlesVisibility; - handle.OperationStarted += updateRotationHandlesVisibility; - handle.OperationEnded += updateRotationHandlesVisibility; + handle.MouseDown += updateRotationHandlesVisibility; + handle.MouseUp += updateRotationHandlesVisibility; allDragHandles.Add(handle); } @@ -72,13 +72,13 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateRotationHandlesVisibility() { - if (activeHandle?.InOperation == true || activeHandle?.IsHovered == true) + if (activeHandle?.IsHeld == true || activeHandle?.IsHovered == true) return; displayedRotationHandle?.FadeOut(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); displayedRotationHandle = null; - activeHandle = allDragHandles.SingleOrDefault(h => h.InOperation); + activeHandle = allDragHandles.SingleOrDefault(h => h.IsHeld); activeHandle ??= allDragHandles.SingleOrDefault(h => h.IsHovered); if (activeHandle != null) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 6303caf9ed..65a54292ab 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void UpdateHoverState() { base.UpdateHoverState(); - icon.FadeColour(!HandlingMouse && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); + icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint); } } } From 8abff4881b731819535c9f603dc820c193abb010 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 4 May 2021 07:31:52 +0300 Subject: [PATCH 21/23] Hide the corresponding rotation handle when holding scale handle --- .../Editing/TestSceneComposeSelectBox.cs | 29 +++++++++++++++---- .../SelectionBoxDragHandleContainer.cs | 10 +++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index 914b8b6f27..efcd7864d7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; +using osu.Framework.Threading; using osu.Game.Screens.Edit.Compose.Components; using osuTK; using osuTK.Input; @@ -40,6 +41,7 @@ namespace osu.Game.Tests.Visual.Editing }; InputManager.MoveMouseTo(selectionBox); + InputManager.ReleaseButton(MouseButton.Left); }); private bool handleScale(Vector2 amount, Anchor reference) @@ -127,26 +129,41 @@ namespace osu.Game.Tests.Visual.Editing } [Test] - public void TestDraggingScaleHandleKeepsCorrespondingRotationHandleShown() + public void TestHoldingScaleHandleHidesCorrespondingRotationHandle() { SelectionBoxRotationHandle rotationHandle = null; AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType().First()); AddAssert("rotation handle hidden", () => rotationHandle.Alpha == 0); - AddStep("hover over and hold closest scale handle", () => + AddStep("hover over closest scale handle", () => { InputManager.MoveMouseTo(this.ChildrenOfType().Single(s => s.Anchor == rotationHandle.Anchor)); - InputManager.PressButton(MouseButton.Left); }); AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1); + AddStep("hold scale handle", () => InputManager.PressButton(MouseButton.Left)); + AddUntilStep("rotation handle hidden", () => rotationHandle.Alpha == 0); - AddStep("drag to centre", () => InputManager.MoveMouseTo(selectionBox)); - AddAssert("rotation handle still shown", () => rotationHandle.Alpha > 0); + int i; + ScheduledDelegate mouseMove = null; + AddStep("start dragging", () => + { + i = 0; + + mouseMove = Scheduler.AddDelayed(() => + { + InputManager.MoveMouseTo(selectionBox.ScreenSpaceDrawQuad.TopLeft + Vector2.One * (5 * ++i)); + }, 100, true); + }); + AddAssert("rotation handle still hidden", () => rotationHandle.Alpha == 0); + + AddStep("end dragging", () => mouseMove.Cancel()); + AddAssert("rotation handle still hidden", () => rotationHandle.Alpha == 0); AddStep("unhold left", () => InputManager.ReleaseButton(MouseButton.Left)); + AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1); AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox, new Vector2(20))); - AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0); + AddUntilStep("rotation handle hidden", () => rotationHandle.Alpha == 0); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs index c78514b7db..456f72878d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandleContainer.cs @@ -72,13 +72,19 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateRotationHandlesVisibility() { - if (activeHandle?.IsHeld == true || activeHandle?.IsHovered == true) + // if the active handle is a rotation handle and is held or hovered, + // then no need to perform any updates to the rotation handles visibility. + if (activeHandle is SelectionBoxRotationHandle && (activeHandle?.IsHeld == true || activeHandle?.IsHovered == true)) return; displayedRotationHandle?.FadeOut(SelectionBoxControl.TRANSFORM_DURATION, Easing.OutQuint); displayedRotationHandle = null; - activeHandle = allDragHandles.SingleOrDefault(h => h.IsHeld); + // if the active handle is not a rotation handle but is held, then keep the rotation handle hidden. + if (activeHandle?.IsHeld == true) + return; + + activeHandle = rotationHandles.SingleOrDefault(h => h.IsHeld || h.IsHovered); activeHandle ??= allDragHandles.SingleOrDefault(h => h.IsHovered); if (activeHandle != null) From 12c1ded7a8f3c6c6e76cee5125d15a57530e70f7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 00:28:49 +0300 Subject: [PATCH 22/23] Fix test scene broken on master --- osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs index efcd7864d7..e383aa8008 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs @@ -30,6 +30,8 @@ namespace osu.Game.Tests.Visual.Editing { selectionBox = new SelectionBox { + RelativeSizeAxes = Axes.Both, + CanRotate = true, CanScaleX = true, CanScaleY = true, From 2a67361dc01963e10e850ac5523858578c613f67 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 May 2021 21:50:11 +0300 Subject: [PATCH 23/23] OnOperation -> TriggerOperation --- .../Screens/Edit/Compose/Components/SelectionBoxButton.cs | 4 ++-- .../Screens/Edit/Compose/Components/SelectionBoxControl.cs | 4 ++-- .../Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs index 43cbbb617b..3b1dae6c3d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxButton.cs @@ -46,9 +46,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnClick(ClickEvent e) { - OnOperationStarted(); + TriggerOperationStarted(); Action?.Invoke(); - OnOperationEnded(); + TriggerOperatoinEnded(); return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs index 159886648e..40d367bb80 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxControl.cs @@ -90,8 +90,8 @@ namespace osu.Game.Screens.Edit.Compose.Components this.ScaleTo(IsHeld || IsHovered ? 1.5f : 1, TRANSFORM_DURATION, Easing.OutQuint); } - protected void OnOperationStarted() => OperationStarted?.Invoke(); + protected void TriggerOperationStarted() => OperationStarted?.Invoke(); - protected void OnOperationEnded() => OperationEnded?.Invoke(); + protected void TriggerOperatoinEnded() => OperationEnded?.Invoke(); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs index 3c1741e24d..65a95951cf 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxDragHandle.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDragStart(DragStartEvent e) { - OnOperationStarted(); + TriggerOperationStarted(); return true; } @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void OnDragEnd(DragEndEvent e) { - OnOperationEnded(); + TriggerOperatoinEnded(); UpdateHoverState(); base.OnDragEnd(e);