Skip to content

Commit

Permalink
Merge pull request ppy#29918 from bdach/control-drag
Browse files Browse the repository at this point in the history
Add to existing selection when dragging with control pressed
  • Loading branch information
peppy authored Sep 30, 2024
2 parents 71a5cd1 + d607331 commit a258059
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
45 changes: 45 additions & 0 deletions osu.Game.Tests/Visual/Editing/TestSceneComposerSelection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,51 @@ public void TestMultiSelect()
AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1]));
}

[Test]
public void TestMultiSelectWithDragBox()
{
var addedObjects = new[]
{
new HitCircle { StartTime = 100 },
new HitCircle { StartTime = 200, Position = new Vector2(100) },
new HitCircle { StartTime = 300, Position = new Vector2(512, 0) },
new HitCircle { StartTime = 400, Position = new Vector2(412, 100) },
};
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects));

AddStep("start dragging", () =>
{
InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre);
InputManager.PressButton(MouseButton.Left);
});
AddStep("drag to left corner", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.TopLeft - new Vector2(5)));
AddStep("end dragging", () => InputManager.ReleaseButton(MouseButton.Left));

AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects, () => Has.Count.EqualTo(2));

AddStep("start dragging with control", () =>
{
InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre);
InputManager.PressButton(MouseButton.Left);
InputManager.PressKey(Key.ControlLeft);
});
AddStep("drag to left corner", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.TopRight + new Vector2(5, -5)));
AddStep("end dragging", () => InputManager.ReleaseButton(MouseButton.Left));
AddStep("release control", () => InputManager.ReleaseKey(Key.ControlLeft));

AddAssert("4 hitobjects selected", () => EditorBeatmap.SelectedHitObjects, () => Has.Count.EqualTo(4));

AddStep("start dragging without control", () =>
{
InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre);
InputManager.PressButton(MouseButton.Left);
});
AddStep("drag to left corner", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.TopRight + new Vector2(5, -5)));
AddStep("end dragging", () => InputManager.ReleaseButton(MouseButton.Left));

AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects, () => Has.Count.EqualTo(2));
}

[Test]
public void TestNearestSelection()
{
Expand Down
18 changes: 13 additions & 5 deletions osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ protected override bool OnDragStart(DragStartEvent e)

DragBox.HandleDrag(e);
DragBox.Show();

selectionBeforeDrag.Clear();
if (e.ControlPressed)
selectionBeforeDrag.UnionWith(SelectedItems);

return true;
}

Expand All @@ -217,6 +222,7 @@ protected override void OnDragEnd(DragEndEvent e)
}

DragBox.Hide();
selectionBeforeDrag.Clear();
}

protected override void Update()
Expand All @@ -227,7 +233,7 @@ protected override void Update()
{
lastDragEvent.Target = this;
DragBox.HandleDrag(lastDragEvent);
UpdateSelectionFromDragBox();
UpdateSelectionFromDragBox(selectionBeforeDrag);
}
}

Expand Down Expand Up @@ -426,7 +432,7 @@ bool runForBlueprint(SelectionBlueprint<T> blueprint)
private bool endClickSelection(MouseButtonEvent e)
{
// If already handled a selection, double-click, or drag, we don't want to perform a mouse up / click action.
if (clickSelectionHandled || doubleClickHandled || isDraggingBlueprint) return true;
if (clickSelectionHandled || doubleClickHandled || isDraggingBlueprint || wasDragStarted) return true;

if (e.Button != MouseButton.Left) return false;

Expand All @@ -442,7 +448,7 @@ private bool endClickSelection(MouseButtonEvent e)
return false;
}

if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1)
if (selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1)
{
// If a click occurred and was handled by the currently selected blueprint but didn't result in a drag,
// cycle between other blueprints which are also under the cursor.
Expand Down Expand Up @@ -472,7 +478,7 @@ private bool endClickSelection(MouseButtonEvent e)
/// <summary>
/// Select all blueprints in a selection area specified by <see cref="DragBox"/>.
/// </summary>
protected virtual void UpdateSelectionFromDragBox()
protected virtual void UpdateSelectionFromDragBox(HashSet<T> selectionBeforeDrag)
{
var quad = DragBox.Box.ScreenSpaceDrawQuad;

Expand All @@ -482,7 +488,7 @@ protected virtual void UpdateSelectionFromDragBox()
{
case SelectionState.Selected:
// Selection is preserved even after blueprint becomes dead.
if (!quad.Contains(blueprint.ScreenSpaceSelectionPoint))
if (!quad.Contains(blueprint.ScreenSpaceSelectionPoint) && !selectionBeforeDrag.Contains(blueprint.Item))
blueprint.Deselect();
break;

Expand Down Expand Up @@ -535,6 +541,8 @@ protected virtual void OnBlueprintDeselected(SelectionBlueprint<T> blueprint)
/// </summary>
private bool wasDragStarted;

private readonly HashSet<T> selectionBeforeDrag = new HashSet<T>();

/// <summary>
/// Attempts to begin the movement of any selected blueprints.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ protected override SelectionBlueprint<HitObject> CreateBlueprintFor(HitObject it

protected sealed override DragBox CreateDragBox() => new TimelineDragBox();

protected override void UpdateSelectionFromDragBox()
protected override void UpdateSelectionFromDragBox(HashSet<HitObject> selectionBeforeDrag)
{
Composer.BlueprintContainer.CommitIfPlacementActive();

Expand All @@ -191,6 +191,9 @@ protected override void UpdateSelectionFromDragBox()

bool shouldBeSelected(HitObject hitObject)
{
if (selectionBeforeDrag.Contains(hitObject))
return true;

double midTime = (hitObject.StartTime + hitObject.GetEndTime()) / 2;
return minTime <= midTime && midTime <= maxTime;
}
Expand Down

0 comments on commit a258059

Please sign in to comment.