From 1a24762f9b75582c0dd0bee7368835c9e04f0730 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 11 Oct 2022 14:11:45 +0900 Subject: [PATCH 1/2] Improve drag box selection logic `AllowDeselectionDuringDrag` is remove. Instead, selected hit objects are not automatically deselected when clock is seeked to a later time (the hit object is dead). Update drag box selection even if mouse is not moved (in case clock is running or scroll wheel is used). --- .../Compose/Components/BlueprintContainer.cs | 23 +++++++++++-------- .../Components/ComposeBlueprintContainer.cs | 20 ++++++++++++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 8aecc75824..b15cecd506 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -106,11 +107,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected virtual DragBox CreateDragBox() => new DragBox(); - /// - /// Whether this component is in a state where items outside a drag selection should be deselected. If false, selection will only be added to. - /// - protected virtual bool AllowDeselectionDuringDrag => true; - protected override bool OnMouseDown(MouseDownEvent e) { bool selectionPerformed = performMouseDownActions(e); @@ -389,12 +385,19 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (var blueprint in SelectionBlueprints) { - if (blueprint.IsSelected && !AllowDeselectionDuringDrag) - continue; + switch (blueprint.State) + { + case SelectionState.Selected: + // Selection is preserved even after blueprint becomes dead. + if (!quad.Contains(blueprint.ScreenSpaceSelectionPoint)) + blueprint.Deselect(); + break; - bool shouldBeSelected = blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint); - if (blueprint.IsSelected != shouldBeSelected) - blueprint.ToggleSelection(); + case SelectionState.NotSelected: + if (blueprint.IsAlive && blueprint.IsPresent && quad.Contains(blueprint.ScreenSpaceSelectionPoint)) + blueprint.Select(); + break; + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 43ead88d54..c8870d46a8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -39,6 +39,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private PlacementBlueprint currentPlacement; private InputManager inputManager; + private DragEvent lastDragEvent; + /// /// Positional input must be received outside the container's bounds, /// in order to handle composer blueprints which are partially offscreen. @@ -83,8 +85,6 @@ namespace osu.Game.Screens.Edit.Compose.Components } } - protected override bool AllowDeselectionDuringDrag => !EditorClock.IsRunning; - protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) { base.TransferBlueprintFor(hitObject, drawableObject); @@ -120,6 +120,18 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } + protected override void OnDrag(DragEvent e) + { + base.OnDrag(e); + lastDragEvent = e; + } + + protected override void OnDragEnd(DragEndEvent e) + { + base.OnDragEnd(e); + lastDragEvent = null; + } + /// /// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints). /// @@ -236,6 +248,10 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.Update(); + // trigger every frame so drags continue to update selection while seeking time. + if (lastDragEvent != null) + OnDrag(lastDragEvent); + if (currentPlacement != null) { switch (currentPlacement.PlacementActive) From fcb9e2cc00894cf686a9fb73cbe27fd8af6dc766 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 11 Oct 2022 22:39:53 +0900 Subject: [PATCH 2/2] Refactor blueprint container drag code --- .../Compose/Components/BlueprintContainer.cs | 28 +++++++---- .../Components/ComposeBlueprintContainer.cs | 24 +--------- .../Components/EditorBlueprintContainer.cs | 5 ++ .../Timeline/TimelineBlueprintContainer.cs | 47 ++++++------------- 4 files changed, 38 insertions(+), 66 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index b15cecd506..43ad270c16 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -170,11 +170,15 @@ namespace osu.Game.Screens.Edit.Compose.Components finishSelectionMovement(); } + private MouseButtonEvent lastDragEvent; + protected override bool OnDragStart(DragStartEvent e) { if (e.Button == MouseButton.Right) return false; + lastDragEvent = e; + if (movementBlueprints != null) { isDraggingBlueprint = true; @@ -189,22 +193,14 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void OnDrag(DragEvent e) { - if (e.Button == MouseButton.Right) - return; - - if (DragBox.State == Visibility.Visible) - { - DragBox.HandleDrag(e); - UpdateSelectionFromDragBox(); - } + lastDragEvent = e; moveCurrentSelection(e); } protected override void OnDragEnd(DragEndEvent e) { - if (e.Button == MouseButton.Right) - return; + lastDragEvent = null; if (isDraggingBlueprint) { @@ -215,6 +211,18 @@ namespace osu.Game.Screens.Edit.Compose.Components DragBox.Hide(); } + protected override void Update() + { + base.Update(); + + if (lastDragEvent != null && DragBox.State == Visibility.Visible) + { + lastDragEvent.Target = this; + DragBox.HandleDrag(lastDragEvent); + UpdateSelectionFromDragBox(); + } + } + /// /// Called whenever a drag operation completes, before any change transaction is committed. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index c8870d46a8..ec07da43a0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -12,7 +12,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Graphics.UserInterface; @@ -37,9 +36,6 @@ namespace osu.Game.Screens.Edit.Compose.Components protected new EditorSelectionHandler SelectionHandler => (EditorSelectionHandler)base.SelectionHandler; private PlacementBlueprint currentPlacement; - private InputManager inputManager; - - private DragEvent lastDragEvent; /// /// Positional input must be received outside the container's bounds, @@ -68,8 +64,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); - inputManager = GetContainingInputManager(); - Beatmap.HitObjectAdded += hitObjectAdded; // updates to selected are handled for us by SelectionHandler. @@ -120,18 +114,6 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - protected override void OnDrag(DragEvent e) - { - base.OnDrag(e); - lastDragEvent = e; - } - - protected override void OnDragEnd(DragEndEvent e) - { - base.OnDragEnd(e); - lastDragEvent = null; - } - /// /// Move the current selection spatially by the specified delta, in gamefield coordinates (ie. the same coordinates as the blueprints). /// @@ -234,7 +216,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updatePlacementPosition() { - var snapResult = Composer.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position); + var snapResult = Composer.FindSnappedPositionAndTime(InputManager.CurrentState.Mouse.Position); // if no time was found from positional snapping, we should still quantize to the beat. snapResult.Time ??= Beatmap.SnapTime(EditorClock.CurrentTime, null); @@ -248,10 +230,6 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.Update(); - // trigger every frame so drags continue to update selection while seeking time. - if (lastDragEvent != null) - OnDrag(lastDragEvent); - if (currentPlacement != null) { switch (currentPlacement.PlacementActive) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 6adaeb1a83..7423b368b4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -8,6 +8,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -27,6 +28,8 @@ namespace osu.Game.Screens.Edit.Compose.Components private HitObjectUsageEventBuffer usageEventBuffer; + protected InputManager InputManager { get; private set; } + protected EditorBlueprintContainer(HitObjectComposer composer) { Composer = composer; @@ -42,6 +45,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { base.LoadComplete(); + InputManager = GetContainingInputManager(); + Beatmap.HitObjectAdded += AddBlueprintFor; Beatmap.HitObjectRemoved += RemoveBlueprintFor; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 31990bfd35..b79c2675c8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -29,10 +29,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved(CanBeNull = true)] private Timeline timeline { get; set; } - private DragEvent lastDragEvent; private Bindable placement; private SelectionBlueprint placementBlueprint; + private bool hitObjectDragged; + /// /// Positional input must be received outside the container's bounds, /// in order to handle timeline blueprints which are stacked offscreen. @@ -98,24 +99,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline return base.OnDragStart(e); } - protected override void OnDrag(DragEvent e) - { - handleScrollViaDrag(e); - - base.OnDrag(e); - } - - protected override void OnDragEnd(DragEndEvent e) - { - base.OnDragEnd(e); - lastDragEvent = null; - } - protected override void Update() { - // trigger every frame so drags continue to update selection while playback is scrolling the timeline. - if (lastDragEvent != null) - OnDrag(lastDragEvent); + if (IsDragged || hitObjectDragged) + handleScrollViaDrag(); if (Composer != null && timeline != null) { @@ -170,7 +157,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { return new TimelineHitObjectBlueprint(item) { - OnDragHandled = handleScrollViaDrag, + OnDragHandled = e => hitObjectDragged = e != null, }; } @@ -197,24 +184,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - private void handleScrollViaDrag(DragEvent e) + private void handleScrollViaDrag() { - lastDragEvent = e; + if (timeline == null) return; - if (lastDragEvent == null) - return; + var timelineQuad = timeline.ScreenSpaceDrawQuad; + float mouseX = InputManager.CurrentState.Mouse.Position.X; - if (timeline != null) - { - var timelineQuad = timeline.ScreenSpaceDrawQuad; - float mouseX = e.ScreenSpaceMousePosition.X; - - // scroll if in a drag and dragging outside visible extents - if (mouseX > timelineQuad.TopRight.X) - timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime)); - else if (mouseX < timelineQuad.TopLeft.X) - timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime)); - } + // scroll if in a drag and dragging outside visible extents + if (mouseX > timelineQuad.TopRight.X) + timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime)); + else if (mouseX < timelineQuad.TopLeft.X) + timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime)); } private class SelectableAreaBackground : CompositeDrawable