From b903edca4581574a30eb4760eeb908df64a9317f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 Oct 2019 16:03:16 +0900 Subject: [PATCH 01/11] Don't snap slider control point placement --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 2fb18bf8ba..2fe294a7e0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; @@ -28,6 +29,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private readonly List segments = new List(); private Vector2 cursor; + private InputManager inputManager; private PlacementState state; @@ -52,6 +54,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders setState(PlacementState.Initial); } + protected override void LoadComplete() + { + base.LoadComplete(); + inputManager = GetContainingInputManager(); + } + public override void UpdatePosition(Vector2 screenSpacePosition) { switch (state) @@ -61,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case PlacementState.Body: - cursor = ToLocalSpace(screenSpacePosition) - HitObject.Position; + cursor = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; break; } } From 436941cda34b7f6dcbd9ca95a872056bc94e24a9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 Oct 2019 17:58:44 +0900 Subject: [PATCH 02/11] Add comment --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 2fe294a7e0..b7b8d0af88 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -69,6 +69,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case PlacementState.Body: + // The given screen-space position may have been externally snapped, but the unsnapped position from the input manager + // is used instead since snapping control points doesn't make much sense cursor = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; break; } From cef2318cf55861fc1c54ce14d868b28457372e90 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 Oct 2019 18:37:57 +0900 Subject: [PATCH 03/11] Move drag box drag handling to BlueprintContainer --- .../Compose/Components/BlueprintContainer.cs | 28 ++++++++++++++++--- .../Edit/Compose/Components/DragBox.cs | 21 +------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 4001a0f33a..c390ffe3f2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -23,6 +23,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { public event Action> SelectionChanged; + private DragBox dragBox; private SelectionBlueprintContainer selectionBlueprints; private Container placementBlueprintContainer; private PlacementBlueprint currentPlacement; @@ -46,12 +47,9 @@ namespace osu.Game.Screens.Edit.Compose.Components selectionHandler = composer.CreateSelectionHandler(); selectionHandler.DeselectAll = deselectAll; - var dragBox = new DragBox(select); - dragBox.DragEnd += () => selectionHandler.UpdateVisibility(); - InternalChildren = new[] { - dragBox, + dragBox = new DragBox(select), selectionHandler, selectionBlueprints = new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both }, placementBlueprintContainer = new Container { RelativeSizeAxes = Axes.Both }, @@ -229,6 +227,28 @@ namespace osu.Game.Screens.Edit.Compose.Components private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionHandler.HandleSelectionRequested(blueprint, state); + protected override bool OnDragStart(DragStartEvent e) + { + if (!selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) + dragBox.FadeIn(250, Easing.OutQuint); + + return true; + } + + protected override bool OnDrag(DragEvent e) + { + dragBox.UpdateDrag(e); + return true; + } + + protected override bool OnDragEnd(DragEndEvent e) + { + dragBox.FadeOut(250, Easing.OutQuint); + selectionHandler.UpdateVisibility(); + + return true; + } + private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) { HitObject draggedObject = blueprint.DrawableObject.HitObject; diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index 143615148a..7d892aa889 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -19,11 +19,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { private readonly Action performSelection; - /// - /// Invoked when the drag selection has finished. - /// - public event Action DragEnd; - private Drawable box; /// @@ -55,13 +50,7 @@ namespace osu.Game.Screens.Edit.Compose.Components }; } - protected override bool OnDragStart(DragStartEvent e) - { - this.FadeIn(250, Easing.OutQuint); - return true; - } - - protected override bool OnDrag(DragEvent e) + public void UpdateDrag(DragEvent e) { var dragPosition = e.ScreenSpaceMousePosition; var dragStartPosition = e.ScreenSpaceMouseDownPosition; @@ -78,14 +67,6 @@ namespace osu.Game.Screens.Edit.Compose.Components box.Size = bottomRight - topLeft; performSelection?.Invoke(dragRectangle); - return true; - } - - protected override bool OnDragEnd(DragEndEvent e) - { - this.FadeOut(250, Easing.OutQuint); - DragEnd?.Invoke(); - return true; } } } From 714c89faa49272b4b5bbb0c275a842e05ddc85c9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 Oct 2019 18:58:15 +0900 Subject: [PATCH 04/11] Move selection drag events to BlueprintContainer --- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 22 ------- .../Compose/Components/BlueprintContainer.cs | 64 ++++++++++++------- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 3076ad081a..8a6e8f6128 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -35,21 +35,11 @@ namespace osu.Game.Rulesets.Edit /// public event Action SelectionRequested; - /// - /// Invoked when this has requested drag. - /// - public event Action DragRequested; - /// /// The which this applies to. /// public readonly DrawableHitObject DrawableObject; - /// - /// The screen-space position of prior to handling a movement event. - /// - internal Vector2 ScreenSpaceMovementStartPosition { get; private set; } - protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected; public override bool HandlePositionalInput => ShouldBeAlive; public override bool RemoveWhenNotAlive => false; @@ -136,18 +126,6 @@ namespace osu.Game.Rulesets.Edit return base.OnClick(e); } - protected override bool OnDragStart(DragStartEvent e) - { - ScreenSpaceMovementStartPosition = DrawableObject.ToScreenSpace(DrawableObject.OriginPosition); - return true; - } - - protected override bool OnDrag(DragEvent e) - { - DragRequested?.Invoke(this, e); - return true; - } - /// /// The screen-space point that causes this to be selected. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index c390ffe3f2..2f401ede38 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -109,7 +110,6 @@ namespace osu.Game.Screens.Edit.Compose.Components blueprint.Selected -= onBlueprintSelected; blueprint.Deselected -= onBlueprintDeselected; blueprint.SelectionRequested -= onSelectionRequested; - blueprint.DragRequested -= onDragRequested; selectionBlueprints.Remove(blueprint); } @@ -125,7 +125,6 @@ namespace osu.Game.Screens.Edit.Compose.Components blueprint.Selected += onBlueprintSelected; blueprint.Deselected += onBlueprintDeselected; blueprint.SelectionRequested += onSelectionRequested; - blueprint.DragRequested += onDragRequested; selectionBlueprints.Add(blueprint); } @@ -227,9 +226,18 @@ namespace osu.Game.Screens.Edit.Compose.Components private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionHandler.HandleSelectionRequested(blueprint, state); + private Vector2? screenSpaceMovementStartPosition; + private SelectionBlueprint movementBlueprint; + protected override bool OnDragStart(DragStartEvent e) { - if (!selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) + if (selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) + { + // The earliest hitobject is used for drag-movement/snapping + movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.DrawableObject.HitObject.StartTime).First(); + screenSpaceMovementStartPosition = movementBlueprint.DrawableObject.ToScreenSpace(movementBlueprint.DrawableObject.OriginPosition); + } + else dragBox.FadeIn(250, Easing.OutQuint); return true; @@ -237,34 +245,46 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDrag(DragEvent e) { - dragBox.UpdateDrag(e); + if (movementBlueprint != null) + { + Debug.Assert(screenSpaceMovementStartPosition != null); + + Vector2 startPosition = screenSpaceMovementStartPosition.Value; + HitObject draggedObject = movementBlueprint.DrawableObject.HitObject; + + Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; + Vector2 snappedPosition = composer.GetSnappedPosition(ToLocalSpace(movePosition)); + + // Move the hitobjects + selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition))); + + // Apply the start time at the newly snapped-to position + double offset = composer.GetSnappedTime(draggedObject.StartTime, snappedPosition) - draggedObject.StartTime; + foreach (HitObject obj in selectionHandler.SelectedHitObjects) + obj.StartTime += offset; + } + else + dragBox.UpdateDrag(e); + return true; } protected override bool OnDragEnd(DragEndEvent e) { - dragBox.FadeOut(250, Easing.OutQuint); - selectionHandler.UpdateVisibility(); + if (movementBlueprint != null) + { + screenSpaceMovementStartPosition = null; + movementBlueprint = null; + } + else + { + dragBox.FadeOut(250, Easing.OutQuint); + selectionHandler.UpdateVisibility(); + } return true; } - private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) - { - HitObject draggedObject = blueprint.DrawableObject.HitObject; - - Vector2 movePosition = blueprint.ScreenSpaceMovementStartPosition + dragEvent.ScreenSpaceMousePosition - dragEvent.ScreenSpaceMouseDownPosition; - Vector2 snappedPosition = composer.GetSnappedPosition(ToLocalSpace(movePosition)); - - // Move the hitobjects - selectionHandler.HandleMovement(new MoveSelectionEvent(blueprint, blueprint.ScreenSpaceMovementStartPosition, ToScreenSpace(snappedPosition))); - - // Apply the start time at the newly snapped-to position - double offset = composer.GetSnappedTime(draggedObject.StartTime, snappedPosition) - draggedObject.StartTime; - foreach (HitObject obj in selectionHandler.SelectedHitObjects) - obj.StartTime += offset; - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From e04c77178cfa94d7e89a6e0527b749114c4a41ba Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 Oct 2019 14:58:02 +0900 Subject: [PATCH 05/11] Move selection events to BlueprintContainer --- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 35 ------------------- .../Compose/Components/BlueprintContainer.cs | 21 +++++++++-- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 8a6e8f6128..44f38acfd4 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -6,8 +6,6 @@ using osu.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; -using osu.Framework.Input.Events; -using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -29,12 +27,6 @@ namespace osu.Game.Rulesets.Edit /// public event Action Deselected; - /// - /// Invoked when this has requested selection. - /// Will fire even if already selected. Does not actually perform selection. - /// - public event Action SelectionRequested; - /// /// The which this applies to. /// @@ -99,33 +91,6 @@ namespace osu.Game.Rulesets.Edit public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos); - private bool selectionRequested; - - protected override bool OnMouseDown(MouseDownEvent e) - { - selectionRequested = false; - - if (State == SelectionState.NotSelected) - { - SelectionRequested?.Invoke(this, e.CurrentState); - selectionRequested = true; - } - - return IsSelected; - } - - protected override bool OnClick(ClickEvent e) - { - if (State == SelectionState.Selected && !selectionRequested) - { - selectionRequested = true; - SelectionRequested?.Invoke(this, e.CurrentState); - return true; - } - - return base.OnClick(e); - } - /// /// The screen-space point that causes this to be selected. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 2f401ede38..089f1bde9c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -109,7 +109,6 @@ namespace osu.Game.Screens.Edit.Compose.Components blueprint.Selected -= onBlueprintSelected; blueprint.Deselected -= onBlueprintDeselected; - blueprint.SelectionRequested -= onSelectionRequested; selectionBlueprints.Remove(blueprint); } @@ -124,15 +123,31 @@ namespace osu.Game.Screens.Edit.Compose.Components blueprint.Selected += onBlueprintSelected; blueprint.Deselected += onBlueprintDeselected; - blueprint.SelectionRequested += onSelectionRequested; selectionBlueprints.Add(blueprint); } private void removeBlueprintFor(DrawableHitObject hitObject) => removeBlueprintFor(hitObject.HitObject); + protected override bool OnMouseDown(MouseDownEvent e) + { + foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints) + { + if (blueprint.IsHovered) + { + selectionHandler.HandleSelectionRequested(blueprint, e.CurrentState); + break; + } + } + + return true; + } + protected override bool OnClick(ClickEvent e) { + if (selectionBlueprints.AliveBlueprints.Any(b => b.IsHovered)) + return true; + deselectAll(); return true; } @@ -298,6 +313,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private class SelectionBlueprintContainer : Container { + public IEnumerable AliveBlueprints => AliveInternalChildren.Cast(); + protected override int Compare(Drawable x, Drawable y) { if (!(x is SelectionBlueprint xBlueprint) || !(y is SelectionBlueprint yBlueprint)) From f128e99fb2a0e16043730d05164620aeeab362e1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 Oct 2019 15:07:04 +0900 Subject: [PATCH 06/11] Remove unused methods --- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 089f1bde9c..a2893c735e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Framework.Input.States; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; @@ -127,8 +126,6 @@ namespace osu.Game.Screens.Edit.Compose.Components selectionBlueprints.Add(blueprint); } - private void removeBlueprintFor(DrawableHitObject hitObject) => removeBlueprintFor(hitObject.HitObject); - protected override bool OnMouseDown(MouseDownEvent e) { foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints) @@ -239,8 +236,6 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects); } - private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionHandler.HandleSelectionRequested(blueprint, state); - private Vector2? screenSpaceMovementStartPosition; private SelectionBlueprint movementBlueprint; From a07e5a269b4d5b6dd0b23432779026e82847d371 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 Oct 2019 15:11:54 +0900 Subject: [PATCH 07/11] Extract drag events into multiple methods --- .../Compose/Components/BlueprintContainer.cs | 109 ++++++++++++------ 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index a2893c735e..e442e1b0b2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -160,6 +160,33 @@ namespace osu.Game.Screens.Edit.Compose.Components return base.OnMouseMove(e); } + protected override bool OnDragStart(DragStartEvent e) + { + if (!beginSelectionMovement()) + dragBox.FadeIn(250, Easing.OutQuint); + + return true; + } + + protected override bool OnDrag(DragEvent e) + { + if (!moveCurrentSelection(e)) + dragBox.UpdateDrag(e); + + return true; + } + + protected override bool OnDragEnd(DragEndEvent e) + { + if (!finishSelectionMovement()) + { + dragBox.FadeOut(250, Easing.OutQuint); + selectionHandler.UpdateVisibility(); + } + + return true; + } + protected override void Update() { base.Update(); @@ -239,58 +266,66 @@ namespace osu.Game.Screens.Edit.Compose.Components private Vector2? screenSpaceMovementStartPosition; private SelectionBlueprint movementBlueprint; - protected override bool OnDragStart(DragStartEvent e) + /// + /// Attempts to begin the movement of any selected blueprints. + /// + /// Whether movement began. + private bool beginSelectionMovement() { - if (selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) - { - // The earliest hitobject is used for drag-movement/snapping - movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.DrawableObject.HitObject.StartTime).First(); - screenSpaceMovementStartPosition = movementBlueprint.DrawableObject.ToScreenSpace(movementBlueprint.DrawableObject.OriginPosition); - } - else - dragBox.FadeIn(250, Easing.OutQuint); + Debug.Assert(movementBlueprint == null); + + // Any selected blueprints can begin the movement of the group, however only the earliest hitobject is used for movement + if (!selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) + return false; + + // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject + movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.DrawableObject.HitObject.StartTime).First(); + screenSpaceMovementStartPosition = movementBlueprint.DrawableObject.ToScreenSpace(movementBlueprint.DrawableObject.OriginPosition); return true; } - protected override bool OnDrag(DragEvent e) + /// + /// Moves the current selected blueprints. + /// + /// The defining the movement event. + /// Whether a movement was active. + private bool moveCurrentSelection(DragEvent e) { - if (movementBlueprint != null) - { - Debug.Assert(screenSpaceMovementStartPosition != null); + if (movementBlueprint == null) + return false; - Vector2 startPosition = screenSpaceMovementStartPosition.Value; - HitObject draggedObject = movementBlueprint.DrawableObject.HitObject; + Debug.Assert(screenSpaceMovementStartPosition != null); - Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; - Vector2 snappedPosition = composer.GetSnappedPosition(ToLocalSpace(movePosition)); + Vector2 startPosition = screenSpaceMovementStartPosition.Value; + HitObject draggedObject = movementBlueprint.DrawableObject.HitObject; - // Move the hitobjects - selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition))); + // The final movement position, relative to screenSpaceMovementStartPosition + Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; + Vector2 snappedPosition = composer.GetSnappedPosition(ToLocalSpace(movePosition)); - // Apply the start time at the newly snapped-to position - double offset = composer.GetSnappedTime(draggedObject.StartTime, snappedPosition) - draggedObject.StartTime; - foreach (HitObject obj in selectionHandler.SelectedHitObjects) - obj.StartTime += offset; - } - else - dragBox.UpdateDrag(e); + // Move the hitobjects + selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition))); + + // Apply the start time at the newly snapped-to position + double offset = composer.GetSnappedTime(draggedObject.StartTime, snappedPosition) - draggedObject.StartTime; + foreach (HitObject obj in selectionHandler.SelectedHitObjects) + obj.StartTime += offset; return true; } - protected override bool OnDragEnd(DragEndEvent e) + /// + /// Finishes the current movement of selected blueprints. + /// + /// Whether a movement was active. + private bool finishSelectionMovement() { - if (movementBlueprint != null) - { - screenSpaceMovementStartPosition = null; - movementBlueprint = null; - } - else - { - dragBox.FadeOut(250, Easing.OutQuint); - selectionHandler.UpdateVisibility(); - } + if (movementBlueprint == null) + return false; + + screenSpaceMovementStartPosition = null; + movementBlueprint = null; return true; } From 8e4a21bee756c610c61e4be2d1737b6d5d2aa7f7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 Oct 2019 15:58:22 +0900 Subject: [PATCH 08/11] Separate out mouse down/click/up handling --- .../Compose/Components/BlueprintContainer.cs | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index e442e1b0b2..123fdc78e6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Framework.Input.States; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; @@ -128,27 +129,27 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { - foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints) - { - if (blueprint.IsHovered) - { - selectionHandler.HandleSelectionRequested(blueprint, e.CurrentState); - break; - } - } - + beginClickSelection(e); return true; } protected override bool OnClick(ClickEvent e) { - if (selectionBlueprints.AliveBlueprints.Any(b => b.IsHovered)) + // clickSelectionBegan will be true if a mouse down occurred on the blueprint but the click event was received outside of the blueprint + // otherwise, deselection should only occur if the click event did not occur on top of a selected blueprint + if (clickSelectionBegan || selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) return true; deselectAll(); return true; } + protected override bool OnMouseUp(MouseUpEvent e) + { + endClickSelection(); + return true; + } + protected override bool OnMouseMove(MouseMoveEvent e) { if (currentPlacement != null) @@ -227,6 +228,40 @@ namespace osu.Game.Screens.Edit.Compose.Components currentPlacement.UpdatePosition(snappedScreenSpacePosition); } + #region Selection + + /// + /// Whether a blueprint was selected by a previous click event. + /// + private bool clickSelectionBegan; + + /// + /// Attempts to select any hovered blueprints. + /// + /// The input event that triggered this selection. + private void beginClickSelection(UIEvent e) + { + Debug.Assert(!clickSelectionBegan); + + foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints) + { + if (blueprint.IsHovered) + { + selectionHandler.HandleSelectionRequested(blueprint, e.CurrentState); + clickSelectionBegan = true; + break; + } + } + } + + /// + /// Finishes the current blueprint selection. + /// + private void endClickSelection() + { + clickSelectionBegan = false; + } + /// /// Select all masks in a given rectangle selection area. /// @@ -263,6 +298,10 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects); } + #endregion + + #region Selection Movement + private Vector2? screenSpaceMovementStartPosition; private SelectionBlueprint movementBlueprint; @@ -330,6 +369,8 @@ namespace osu.Game.Screens.Edit.Compose.Components return true; } + #endregion + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 45bd91f63fd10fd9c617fb6082f64166f90db160 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 Oct 2019 16:14:29 +0900 Subject: [PATCH 09/11] Add special cases for click-selection --- .../Compose/Components/BlueprintContainer.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 123fdc78e6..8d87af6931 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Framework.Input.States; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; @@ -135,9 +134,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnClick(ClickEvent e) { - // clickSelectionBegan will be true if a mouse down occurred on the blueprint but the click event was received outside of the blueprint - // otherwise, deselection should only occur if the click event did not occur on top of a selected blueprint - if (clickSelectionBegan || selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) + // Deselection should only occur if no selected blueprints are hovered + // A special case for when a blueprint was selected via this click is added since OnClick() may occur outside the hitobject and should not trigger deselection + if (endClickSelection() || selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) return true; deselectAll(); @@ -146,7 +145,8 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseUp(MouseUpEvent e) { - endClickSelection(); + // Special case for when a drag happened instead of a click + Schedule(() => endClickSelection()); return true; } @@ -257,9 +257,14 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Finishes the current blueprint selection. /// - private void endClickSelection() + /// Whether a click selection was active. + private bool endClickSelection() { + if (!clickSelectionBegan) + return false; + clickSelectionBegan = false; + return true; } /// @@ -313,8 +318,9 @@ namespace osu.Game.Screens.Edit.Compose.Components { Debug.Assert(movementBlueprint == null); - // Any selected blueprints can begin the movement of the group, however only the earliest hitobject is used for movement - if (!selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) + // Any selected blueprint that is hovered can begin the movement of the group, however only the earliest hitobject is used for movement + // A special case is added for when a click selection occurred before the drag + if (!clickSelectionBegan && !selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) return false; // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject From fb88001c0ed0476c767bab32182bd8eac3f7018c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 Oct 2019 16:17:48 +0900 Subject: [PATCH 10/11] Reorder blueprint addition/removal + add regions --- .../Compose/Components/BlueprintContainer.cs | 82 ++++++++++--------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 8d87af6931..d3cd0b1d65 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -89,43 +89,6 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - private void addBlueprintFor(HitObject hitObject) - { - var drawable = composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject); - if (drawable == null) - return; - - addBlueprintFor(drawable); - } - - private void removeBlueprintFor(HitObject hitObject) - { - var blueprint = selectionBlueprints.Single(m => m.DrawableObject.HitObject == hitObject); - if (blueprint == null) - return; - - blueprint.Deselect(); - - blueprint.Selected -= onBlueprintSelected; - blueprint.Deselected -= onBlueprintDeselected; - - selectionBlueprints.Remove(blueprint); - } - - private void addBlueprintFor(DrawableHitObject hitObject) - { - refreshTool(); - - var blueprint = composer.CreateBlueprintFor(hitObject); - if (blueprint == null) - return; - - blueprint.Selected += onBlueprintSelected; - blueprint.Deselected += onBlueprintDeselected; - - selectionBlueprints.Add(blueprint); - } - protected override bool OnMouseDown(MouseDownEvent e) { beginClickSelection(e); @@ -201,6 +164,49 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + #region Blueprint Addition/Removal + + private void addBlueprintFor(HitObject hitObject) + { + var drawable = composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject); + if (drawable == null) + return; + + addBlueprintFor(drawable); + } + + private void removeBlueprintFor(HitObject hitObject) + { + var blueprint = selectionBlueprints.Single(m => m.DrawableObject.HitObject == hitObject); + if (blueprint == null) + return; + + blueprint.Deselect(); + + blueprint.Selected -= onBlueprintSelected; + blueprint.Deselected -= onBlueprintDeselected; + + selectionBlueprints.Remove(blueprint); + } + + private void addBlueprintFor(DrawableHitObject hitObject) + { + refreshTool(); + + var blueprint = composer.CreateBlueprintFor(hitObject); + if (blueprint == null) + return; + + blueprint.Selected += onBlueprintSelected; + blueprint.Deselected += onBlueprintDeselected; + + selectionBlueprints.Add(blueprint); + } + + #endregion + + #region Placement + /// /// Refreshes the current placement tool. /// @@ -228,6 +234,8 @@ namespace osu.Game.Screens.Edit.Compose.Components currentPlacement.UpdatePosition(snappedScreenSpacePosition); } + #endregion + #region Selection /// From 7a71352684566cfa1649753181981160a9b203d4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 Oct 2019 17:22:14 +0900 Subject: [PATCH 11/11] Fix drag box being positioned incorrectly for 1 frame --- osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs | 3 +++ osu.Game/Screens/Edit/Compose/Components/DragBox.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index d3cd0b1d65..30f0f94128 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -127,7 +127,10 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDragStart(DragStartEvent e) { if (!beginSelectionMovement()) + { + dragBox.UpdateDrag(e); dragBox.FadeIn(250, Easing.OutQuint); + } return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs index 7d892aa889..2a510e74fd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Edit.Compose.Components }; } - public void UpdateDrag(DragEvent e) + public void UpdateDrag(MouseButtonEvent e) { var dragPosition = e.ScreenSpaceMousePosition; var dragStartPosition = e.ScreenSpaceMouseDownPosition;