diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index 5699be4560..64cf0e7512 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -135,11 +135,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected override bool OnMouseDown(MouseDownEvent e)
{
- if (!beginClickSelection(e)) return true;
+ bool selectionPerformed = beginClickSelection(e);
+ // even if a selection didn't occur, a drag event may still move the selection.
prepareSelectionMovement();
- return e.Button == MouseButton.Left;
+ return selectionPerformed || e.Button == MouseButton.Left;
}
private SelectionBlueprint clickedBlueprint;
@@ -154,7 +155,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
// 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() || clickedBlueprint != null)
+ if (endClickSelection(e) || clickedBlueprint != null)
return true;
deselectAll();
@@ -177,7 +178,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected override void OnMouseUp(MouseUpEvent e)
{
// Special case for when a drag happened instead of a click
- Schedule(() => endClickSelection());
+ Schedule(() =>
+ {
+ endClickSelection(e);
+ clickSelectionBegan = false;
+ isDraggingBlueprint = false;
+ });
finishSelectionMovement();
}
@@ -226,7 +232,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
Beatmap.Update(obj);
changeHandler?.EndChange();
- isDraggingBlueprint = false;
}
if (DragBox.State == Visibility.Visible)
@@ -355,13 +360,28 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
/// Finishes the current blueprint selection.
///
+ /// The mouse event which triggered end of selection.
/// Whether a click selection was active.
- private bool endClickSelection()
+ private bool endClickSelection(MouseButtonEvent e)
{
- if (!clickSelectionBegan)
- return false;
+ if (!clickSelectionBegan && !isDraggingBlueprint)
+ {
+ // if a selection didn't occur, we may want to trigger a deselection.
+ if (e.ControlPressed && e.Button == MouseButton.Left)
+ {
+ // Iterate from the top of the input stack (blueprints closest to the front of the screen first).
+ // Priority is given to already-selected blueprints.
+ foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected))
+ {
+ if (!blueprint.IsHovered) continue;
+
+ return clickSelectionBegan = SelectionHandler.HandleDeselectionRequested(blueprint, e);
+ }
+ }
+
+ return false;
+ }
- clickSelectionBegan = false;
return true;
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
index 018d4d081c..e5e1100797 100644
--- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
@@ -228,12 +228,31 @@ namespace osu.Game.Screens.Edit.Compose.Components
return false;
}
- if (e.ControlPressed && e.Button == MouseButton.Left)
+ // while holding control, we only want to add to selection, not replace an existing selection.
+ if (e.ControlPressed && e.Button == MouseButton.Left && !blueprint.IsSelected)
+ {
blueprint.ToggleSelection();
- else
- ensureSelected(blueprint);
+ return true;
+ }
- return true;
+ return ensureSelected(blueprint);
+ }
+
+ ///
+ /// Handle a blueprint requesting selection.
+ ///
+ /// The blueprint.
+ /// The mouse event responsible for deselection.
+ /// Whether a deselection was performed.
+ internal bool HandleDeselectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e)
+ {
+ if (blueprint.IsSelected)
+ {
+ blueprint.ToggleSelection();
+ return true;
+ }
+
+ return false;
}
private void handleQuickDeletion(SelectionBlueprint blueprint)
@@ -247,13 +266,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
deleteSelected();
}
- private void ensureSelected(SelectionBlueprint blueprint)
+ ///
+ /// Ensure the blueprint is in a selected state.
+ ///
+ /// The blueprint to select.
+ /// Whether selection state was changed.
+ private bool ensureSelected(SelectionBlueprint blueprint)
{
if (blueprint.IsSelected)
- return;
+ return false;
DeselectAll?.Invoke();
blueprint.Select();
+ return true;
}
private void deleteSelected()