diff --git a/osu.Android.props b/osu.Android.props
index c5714caf4c..43c1302e54 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -62,6 +62,6 @@
-
+
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs
index da7708081b..6b8daa531f 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -14,6 +15,7 @@ using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Tests.Visual;
using osuTK;
using osuTK.Graphics;
@@ -25,6 +27,11 @@ namespace osu.Game.Rulesets.Osu.Tests
private const double beat_length = 100;
private static readonly Vector2 grid_position = new Vector2(512, 384);
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(CircularDistanceSnapGrid)
+ };
+
[Cached(typeof(IEditorBeatmap))]
private readonly EditorBeatmap editorBeatmap;
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index 6a4201f84d..4893ebfdd4 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -111,6 +111,21 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
}
+ [Test]
+ public void TestChangeStackHeight()
+ {
+ DrawableSlider slider = null;
+
+ AddStep("create slider", () =>
+ {
+ slider = (DrawableSlider)createSlider(repeats: 1);
+ Add(slider);
+ });
+
+ AddStep("change stack height", () => slider.HitObject.StackHeight = 10);
+ AddAssert("body positioned correctly", () => slider.Position == slider.HitObject.StackedPosition);
+ }
+
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs
index ec23ec31b2..5df0b70f12 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSelectionBlueprint.cs
@@ -78,6 +78,13 @@ namespace osu.Game.Rulesets.Osu.Tests
checkPositions();
}
+ [Test]
+ public void TestStackedHitObject()
+ {
+ AddStep("set stacking", () => slider.StackHeight = 5);
+ checkPositions();
+ }
+
private void moveHitObject()
{
AddStep("move hitobject", () =>
@@ -88,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void checkPositions()
{
- AddAssert("body positioned correctly", () => blueprint.BodyPiece.Position == slider.Position);
+ AddAssert("body positioned correctly", () => blueprint.BodyPiece.Position == slider.StackedPosition);
AddAssert("head positioned correctly",
() => Precision.AlmostEquals(blueprint.HeadBlueprint.CirclePiece.ScreenSpaceDrawQuad.Centre, drawableObject.HeadCircle.ScreenSpaceDrawQuad.Centre));
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
index 2fb18bf8ba..b7b8d0af88 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,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
break;
case PlacementState.Body:
- cursor = ToLocalSpace(screenSpacePosition) - HitObject.Position;
+ // 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;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 6d45bb9ac4..433d29f2e4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -34,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly Slider slider;
private readonly IBindable positionBindable = new Bindable();
+ private readonly IBindable stackHeightBindable = new Bindable();
private readonly IBindable scaleBindable = new Bindable();
private readonly IBindable pathBindable = new Bindable();
@@ -72,6 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut);
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
+ stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
scaleBindable.BindValueChanged(scale =>
{
updatePathRadius();
@@ -79,6 +81,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
});
positionBindable.BindTo(HitObject.PositionBindable);
+ stackHeightBindable.BindTo(HitObject.StackHeightBindable);
scaleBindable.BindTo(HitObject.ScaleBindable);
pathBindable.BindTo(slider.PathBindable);
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index b506c1f918..0ba712a83f 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -1,6 +1,7 @@
// 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 osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
@@ -98,6 +99,15 @@ namespace osu.Game.Rulesets.Osu.Objects
set => LastInComboBindable.Value = value;
}
+ protected OsuHitObject()
+ {
+ StackHeightBindable.BindValueChanged(height =>
+ {
+ foreach (var nested in NestedHitObjects.OfType())
+ nested.StackHeight = height.NewValue;
+ });
+ }
+
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index d98d72331a..010bf072e8 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -163,6 +163,7 @@ namespace osu.Game.Rulesets.Osu.Objects
{
StartTime = e.Time,
Position = Position,
+ StackHeight = StackHeight,
Samples = getNodeSamples(0),
SampleControlPoint = SampleControlPoint,
});
@@ -176,6 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects
{
StartTime = e.Time,
Position = EndPosition,
+ StackHeight = StackHeight
});
break;
diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json
index b994cbd85a..004e7940d1 100644
--- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json
+++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json
@@ -143,14 +143,14 @@
"Objects": [{
"StartTime": 34989,
"EndTime": 34989,
- "X": 163,
- "Y": 138
+ "X": 156.597382,
+ "Y": 131.597382
},
{
"StartTime": 35018,
"EndTime": 35018,
- "X": 188,
- "Y": 138
+ "X": 181.597382,
+ "Y": 131.597382
}
]
},
@@ -159,14 +159,14 @@
"Objects": [{
"StartTime": 35106,
"EndTime": 35106,
- "X": 163,
- "Y": 138
+ "X": 159.798691,
+ "Y": 134.798691
},
{
"StartTime": 35135,
"EndTime": 35135,
- "X": 188,
- "Y": 138
+ "X": 184.798691,
+ "Y": 134.798691
}
]
},
@@ -191,20 +191,20 @@
"Objects": [{
"StartTime": 35695,
"EndTime": 35695,
- "X": 166,
- "Y": 76
+ "X": 162.798691,
+ "Y": 72.79869
},
{
"StartTime": 35871,
"EndTime": 35871,
- "X": 240.99855,
- "Y": 75.53417
+ "X": 237.797241,
+ "Y": 72.33286
},
{
"StartTime": 36011,
"EndTime": 36011,
- "X": 315.9971,
- "Y": 75.0683441
+ "X": 312.795776,
+ "Y": 71.8670349
}
]
},
@@ -235,20 +235,20 @@
"Objects": [{
"StartTime": 36518,
"EndTime": 36518,
- "X": 166,
- "Y": 76
+ "X": 169.201309,
+ "Y": 79.20131
},
{
"StartTime": 36694,
"EndTime": 36694,
- "X": 240.99855,
- "Y": 75.53417
+ "X": 244.19986,
+ "Y": 78.73548
},
{
"StartTime": 36834,
"EndTime": 36834,
- "X": 315.9971,
- "Y": 75.0683441
+ "X": 319.198425,
+ "Y": 78.26965
}
]
},
@@ -257,20 +257,20 @@
"Objects": [{
"StartTime": 36929,
"EndTime": 36929,
- "X": 315,
- "Y": 75
+ "X": 324.603943,
+ "Y": 84.6039352
},
{
"StartTime": 37105,
"EndTime": 37105,
- "X": 240.001526,
- "Y": 75.47769
+ "X": 249.605469,
+ "Y": 85.08163
},
{
"StartTime": 37245,
"EndTime": 37245,
- "X": 165.003052,
- "Y": 75.95539
+ "X": 174.607,
+ "Y": 85.5593262
}
]
}
diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs
index c01674f5b4..8b87ddaa20 100644
--- a/osu.Game/Graphics/Containers/WaveContainer.cs
+++ b/osu.Game/Graphics/Containers/WaveContainer.cs
@@ -159,8 +159,15 @@ namespace osu.Game.Graphics.Containers
Height = Parent.Parent.DrawSize.Y * 1.5f;
}
- protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
- protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
+ protected override void PopIn() => Schedule(() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show));
+
+ protected override void PopOut()
+ {
+ double duration = IsLoaded ? DISAPPEAR_DURATION : 0;
+
+ // scheduling is required as parent may not be present at the time this is called.
+ Schedule(() => this.MoveToY(Parent.Parent.DrawSize.Y, duration, easing_hide));
+ }
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
index 5c706781e6..11aba80d76 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -17,7 +17,7 @@ using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
public class OsuSliderBar : SliderBar, IHasTooltip, IHasAccentColour
- where T : struct, IEquatable, IComparable, IConvertible
+ where T : struct, IEquatable, IComparable, IConvertible
{
///
/// Maximum number of decimal digits to be displayed in the tooltip.
diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
index e136fc1403..6de14c51ee 100644
--- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
+++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs
@@ -138,18 +138,13 @@ namespace osu.Game.Overlays.AccountCreation
passwordTextBox.Current.ValueChanged += password => { characterCheckText.ForEach(s => s.Colour = password.NewValue.Length == 0 ? Color4.White : Interpolation.ValueAt(password.NewValue.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); };
}
- protected override void Update()
- {
- base.Update();
-
- if (host?.OnScreenKeyboardOverlapsGameWindow != true && !textboxes.Any(t => t.HasFocus))
- focusNextTextbox();
- }
-
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
processingOverlay.Hide();
+
+ if (host?.OnScreenKeyboardOverlapsGameWindow != true)
+ focusNextTextbox();
}
private void performRegistration()
diff --git a/osu.Game/Overlays/Settings/SettingsSlider.cs b/osu.Game/Overlays/Settings/SettingsSlider.cs
index fd96ea972a..20e08c0cd8 100644
--- a/osu.Game/Overlays/Settings/SettingsSlider.cs
+++ b/osu.Game/Overlays/Settings/SettingsSlider.cs
@@ -8,12 +8,12 @@ using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
public class SettingsSlider : SettingsSlider>
- where T : struct, IEquatable, IComparable, IConvertible
+ where T : struct, IEquatable, IComparable, IConvertible
{
}
public class SettingsSlider : SettingsItem
- where T : struct, IEquatable, IComparable, IConvertible
+ where T : struct, IEquatable, IComparable, IConvertible
where U : OsuSliderBar, new()
{
protected override Drawable CreateControl() => new U
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 51155ce3fd..6396301add 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -162,11 +162,13 @@ namespace osu.Game.Rulesets.Edit
inputManager = GetContainingInputManager();
}
+ private double lastGridUpdateTime;
+
protected override void Update()
{
base.Update();
- if (EditorClock.ElapsedFrameTime != 0 && blueprintContainer.CurrentTool != null)
+ if (EditorClock.CurrentTime != lastGridUpdateTime && blueprintContainer.CurrentTool != null)
showGridFor(Enumerable.Empty());
}
@@ -213,6 +215,8 @@ namespace osu.Game.Rulesets.Edit
distanceSnapGridContainer.Child = distanceSnapGrid;
distanceSnapGridContainer.Show();
}
+
+ lastGridUpdateTime = EditorClock.CurrentTime;
}
private ScheduledDelegate scheduledUpdate;
diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
index 3076ad081a..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,27 +27,11 @@ 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;
-
- ///
- /// 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;
@@ -109,45 +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);
- }
-
- 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/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs
index 2aeb1ef04b..a724f354e7 100644
--- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs
+++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs
@@ -10,7 +10,7 @@ using osuTK.Graphics;
namespace osu.Game.Screens.Edit
{
- public class BindableBeatDivisor : BindableNumber
+ public class BindableBeatDivisor : BindableInt
{
public static readonly int[] VALID_DIVISORS = { 1, 2, 3, 4, 6, 8, 12, 16 };
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index 4001a0f33a..30f0f94128 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;
@@ -10,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;
@@ -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 },
@@ -91,6 +89,86 @@ namespace osu.Game.Screens.Edit.Compose.Components
}
}
+ protected override bool OnMouseDown(MouseDownEvent e)
+ {
+ beginClickSelection(e);
+ return true;
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ // 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();
+ return true;
+ }
+
+ protected override bool OnMouseUp(MouseUpEvent e)
+ {
+ // Special case for when a drag happened instead of a click
+ Schedule(() => endClickSelection());
+ return true;
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ if (currentPlacement != null)
+ {
+ updatePlacementPosition(e.ScreenSpaceMousePosition);
+ return true;
+ }
+
+ return base.OnMouseMove(e);
+ }
+
+ protected override bool OnDragStart(DragStartEvent e)
+ {
+ if (!beginSelectionMovement())
+ {
+ dragBox.UpdateDrag(e);
+ 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();
+
+ if (currentPlacement != null)
+ {
+ if (composer.CursorInPlacementArea)
+ currentPlacement.State = PlacementState.Shown;
+ else if (currentPlacement?.PlacementBegun == false)
+ currentPlacement.State = PlacementState.Hidden;
+ }
+ }
+
+ #region Blueprint Addition/Removal
+
private void addBlueprintFor(HitObject hitObject)
{
var drawable = composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject);
@@ -110,8 +188,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.Selected -= onBlueprintSelected;
blueprint.Deselected -= onBlueprintDeselected;
- blueprint.SelectionRequested -= onSelectionRequested;
- blueprint.DragRequested -= onDragRequested;
selectionBlueprints.Remove(blueprint);
}
@@ -126,43 +202,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.Selected += onBlueprintSelected;
blueprint.Deselected += onBlueprintDeselected;
- blueprint.SelectionRequested += onSelectionRequested;
- blueprint.DragRequested += onDragRequested;
selectionBlueprints.Add(blueprint);
}
- private void removeBlueprintFor(DrawableHitObject hitObject) => removeBlueprintFor(hitObject.HitObject);
+ #endregion
- protected override bool OnClick(ClickEvent e)
- {
- deselectAll();
- return true;
- }
-
- protected override bool OnMouseMove(MouseMoveEvent e)
- {
- if (currentPlacement != null)
- {
- updatePlacementPosition(e.ScreenSpaceMousePosition);
- return true;
- }
-
- return base.OnMouseMove(e);
- }
-
- protected override void Update()
- {
- base.Update();
-
- if (currentPlacement != null)
- {
- if (composer.CursorInPlacementArea)
- currentPlacement.State = PlacementState.Shown;
- else if (currentPlacement?.PlacementBegun == false)
- currentPlacement.State = PlacementState.Hidden;
- }
- }
+ #region Placement
///
/// Refreshes the current placement tool.
@@ -191,6 +237,47 @@ namespace osu.Game.Screens.Edit.Compose.Components
currentPlacement.UpdatePosition(snappedScreenSpacePosition);
}
+ #endregion
+
+ #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.
+ ///
+ /// Whether a click selection was active.
+ private bool endClickSelection()
+ {
+ if (!clickSelectionBegan)
+ return false;
+
+ clickSelectionBegan = false;
+ return true;
+ }
+
///
/// Select all masks in a given rectangle selection area.
///
@@ -227,24 +314,80 @@ namespace osu.Game.Screens.Edit.Compose.Components
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
}
- private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionHandler.HandleSelectionRequested(blueprint, state);
+ #endregion
- private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent)
+ #region Selection Movement
+
+ private Vector2? screenSpaceMovementStartPosition;
+ private SelectionBlueprint movementBlueprint;
+
+ ///
+ /// Attempts to begin the movement of any selected blueprints.
+ ///
+ /// Whether movement began.
+ private bool beginSelectionMovement()
{
- HitObject draggedObject = blueprint.DrawableObject.HitObject;
+ Debug.Assert(movementBlueprint == null);
- Vector2 movePosition = blueprint.ScreenSpaceMovementStartPosition + dragEvent.ScreenSpaceMousePosition - dragEvent.ScreenSpaceMouseDownPosition;
+ // 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
+ movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.DrawableObject.HitObject.StartTime).First();
+ screenSpaceMovementStartPosition = movementBlueprint.DrawableObject.ToScreenSpace(movementBlueprint.DrawableObject.OriginPosition);
+
+ return true;
+ }
+
+ ///
+ /// Moves the current selected blueprints.
+ ///
+ /// The defining the movement event.
+ /// Whether a movement was active.
+ private bool moveCurrentSelection(DragEvent e)
+ {
+ if (movementBlueprint == null)
+ return false;
+
+ Debug.Assert(screenSpaceMovementStartPosition != null);
+
+ Vector2 startPosition = screenSpaceMovementStartPosition.Value;
+ HitObject draggedObject = movementBlueprint.DrawableObject.HitObject;
+
+ // The final movement position, relative to screenSpaceMovementStartPosition
+ Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
Vector2 snappedPosition = composer.GetSnappedPosition(ToLocalSpace(movePosition));
// Move the hitobjects
- selectionHandler.HandleMovement(new MoveSelectionEvent(blueprint, blueprint.ScreenSpaceMovementStartPosition, ToScreenSpace(snappedPosition)));
+ 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;
}
+ ///
+ /// Finishes the current movement of selected blueprints.
+ ///
+ /// Whether a movement was active.
+ private bool finishSelectionMovement()
+ {
+ if (movementBlueprint == null)
+ return false;
+
+ screenSpaceMovementStartPosition = null;
+ movementBlueprint = null;
+
+ return true;
+ }
+
+ #endregion
+
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
@@ -258,6 +401,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))
diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs
index 3cbf926d4f..a644e51c13 100644
--- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs
@@ -3,6 +3,7 @@
using System;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Rulesets.Objects;
using osuTK;
@@ -18,10 +19,32 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected override void CreateContent(Vector2 centrePosition)
{
+ const float crosshair_thickness = 1;
+ const float crosshair_max_size = 10;
+
+ AddRangeInternal(new[]
+ {
+ new Box
+ {
+ Origin = Anchor.Centre,
+ Position = centrePosition,
+ Width = crosshair_thickness,
+ EdgeSmoothness = new Vector2(1),
+ Height = Math.Min(crosshair_max_size, DistanceSpacing * 2),
+ },
+ new Box
+ {
+ Origin = Anchor.Centre,
+ Position = centrePosition,
+ EdgeSmoothness = new Vector2(1),
+ Width = Math.Min(crosshair_max_size, DistanceSpacing * 2),
+ Height = crosshair_thickness,
+ }
+ });
+
float dx = Math.Max(centrePosition.X, DrawWidth - centrePosition.X);
float dy = Math.Max(centrePosition.Y, DrawHeight - centrePosition.Y);
float maxDistance = new Vector2(dx, dy).Length;
-
int requiredCircles = (int)(maxDistance / DistanceSpacing);
for (int i = 0; i < requiredCircles; i++)
diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
index 299e78b7c0..096ff0a6dd 100644
--- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs
@@ -36,15 +36,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
protected readonly Vector2 CentrePosition;
+ [Resolved]
+ protected OsuColour Colours { get; private set; }
+
[Resolved]
private IEditorBeatmap beatmap { get; set; }
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
- [Resolved]
- private OsuColour colours { get; set; }
-
private readonly Cached gridCache = new Cached();
private readonly HitObject hitObject;
@@ -136,7 +136,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected ColourInfo GetColourForBeatIndex(int index)
{
int beat = (index + 1) % beatDivisor.Value;
- ColourInfo colour = colours.Gray5;
+ ColourInfo colour = Colours.Gray5;
for (int i = 0; i < BindableBeatDivisor.VALID_DIVISORS.Length; i++)
{
@@ -144,7 +144,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if ((beat * divisor) % beatDivisor.Value == 0)
{
- colour = BindableBeatDivisor.GetColourFor(divisor, colours);
+ colour = BindableBeatDivisor.GetColourFor(divisor, Colours);
break;
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/DragBox.cs b/osu.Game/Screens/Edit/Compose/Components/DragBox.cs
index 143615148a..2a510e74fd 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(MouseButtonEvent 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;
}
}
}
diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs
index 28fe1f35ca..c8e281195a 100644
--- a/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs
+++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSliderBar.cs
@@ -11,7 +11,7 @@ using osu.Game.Overlays.Settings;
namespace osu.Game.Screens.Play.PlayerSettings
{
public class PlayerSliderBar : SettingsSlider
- where T : struct, IEquatable, IComparable, IConvertible
+ where T : struct, IEquatable, IComparable, IConvertible
{
public OsuSliderBar Bar => (OsuSliderBar)Control;
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 64172a0954..e898a001de 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 5d384888d2..656c60543e 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -118,8 +118,8 @@
-
-
+
+