diff --git a/osu.Android.props b/osu.Android.props
index 5078fee1cf..6e3d5eec1f 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
index cea27498c3..2fa3f378ff 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects.Drawables;
@@ -12,8 +11,8 @@ namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaBlueprintContainer : ComposeBlueprintContainer
{
- public ManiaBlueprintContainer(IEnumerable drawableHitObjects)
- : base(drawableHitObjects)
+ public ManiaBlueprintContainer(HitObjectComposer composer)
+ : base(composer)
{
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
index 7e2469a794..01d572447b 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -13,7 +13,6 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit.Compose.Components;
@@ -89,8 +88,8 @@ namespace osu.Game.Rulesets.Mania.Edit
return drawableRuleset;
}
- protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
- => new ManiaBlueprintContainer(hitObjects);
+ protected override ComposeBlueprintContainer CreateBlueprintContainer()
+ => new ManiaBlueprintContainer(this);
protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[]
{
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
index 330f34b85c..a68ed34e6b 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuBlueprintContainer.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
@@ -14,8 +13,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuBlueprintContainer : ComposeBlueprintContainer
{
- public OsuBlueprintContainer(IEnumerable drawableHitObjects)
- : base(drawableHitObjects)
+ public OsuBlueprintContainer(HitObjectComposer composer)
+ : base(composer)
{
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index edd684d886..bfa8ab4431 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -16,7 +16,6 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Components.TernaryButtons;
@@ -80,8 +79,8 @@ namespace osu.Game.Rulesets.Osu.Edit
updateDistanceSnapGrid();
}
- protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
- => new OsuBlueprintContainer(hitObjects);
+ protected override ComposeBlueprintContainer CreateBlueprintContainer()
+ => new OsuBlueprintContainer(this);
private DistanceSnapGrid distanceSnapGrid;
private Container distanceSnapGridContainer;
diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs
index 35227b3c64..8b41448c9d 100644
--- a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs
+++ b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs
@@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Edit.Blueprints;
@@ -11,8 +10,8 @@ namespace osu.Game.Rulesets.Taiko.Edit
{
public class TaikoBlueprintContainer : ComposeBlueprintContainer
{
- public TaikoBlueprintContainer(IEnumerable hitObjects)
- : base(hitObjects)
+ public TaikoBlueprintContainer(HitObjectComposer composer)
+ : base(composer)
{
}
diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs
index cdc9672a8e..161799c980 100644
--- a/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Taiko/Edit/TaikoHitObjectComposer.cs
@@ -4,7 +4,6 @@
using System.Collections.Generic;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Edit.Compose.Components;
@@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Edit
new SwellCompositionTool()
};
- protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
- => new TaikoBlueprintContainer(hitObjects);
+ protected override ComposeBlueprintContainer CreateBlueprintContainer()
+ => new TaikoBlueprintContainer(this);
}
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs
index e931be044c..5da63eddf1 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineBlueprintContainer.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Tests.Visual.Editing
[TestFixture]
public class TestSceneTimelineBlueprintContainer : TimelineTestScene
{
- public override Drawable CreateTestComponent() => new TimelineBlueprintContainer();
+ public override Drawable CreateTestComponent() => new TimelineBlueprintContainer(null);
protected override void LoadComplete()
{
diff --git a/osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs b/osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs
index 8ed7885101..c60d4c7834 100644
--- a/osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs
+++ b/osu.Game/Rulesets/Edit/DrawableEditRulesetWrapper.cs
@@ -1,7 +1,6 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -65,17 +64,13 @@ namespace osu.Game.Rulesets.Edit
private void addHitObject(HitObject hitObject)
{
- var drawableObject = drawableRuleset.CreateDrawableRepresentation((TObject)hitObject);
-
- drawableRuleset.Playfield.Add(drawableObject);
+ drawableRuleset.AddHitObject((TObject)hitObject);
drawableRuleset.Playfield.PostProcess();
}
private void removeHitObject(HitObject hitObject)
{
- var drawableObject = Playfield.AllHitObjects.Single(d => d.HitObject == hitObject);
-
- drawableRuleset.Playfield.Remove(drawableObject);
+ drawableRuleset.RemoveHitObject((TObject)hitObject);
drawableRuleset.Playfield.PostProcess();
}
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index c9dd061b48..b90aa6863a 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Edit
drawableRulesetWrapper,
// layers above playfield
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer()
- .WithChild(BlueprintContainer = CreateBlueprintContainer(HitObjects))
+ .WithChild(BlueprintContainer = CreateBlueprintContainer())
}
},
new FillFlowContainer
@@ -182,9 +182,8 @@ namespace osu.Game.Rulesets.Edit
///
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
///
- /// A live collection of all s in the editor beatmap.
- protected virtual ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
- => new ComposeBlueprintContainer(hitObjects);
+ protected virtual ComposeBlueprintContainer CreateBlueprintContainer()
+ => new ComposeBlueprintContainer(this);
///
/// Construct a drawable ruleset for the provided ruleset.
diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs
index 4164681ffc..bca2466968 100644
--- a/osu.Game/Rulesets/UI/HitObjectContainer.cs
+++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs
@@ -42,6 +42,32 @@ namespace osu.Game.Rulesets.UI
///
public event Action RevertResult;
+ ///
+ /// Invoked when a becomes used by a .
+ ///
+ ///
+ /// If this uses pooled objects, this represents the time when the s become alive.
+ ///
+ public event Action HitObjectUsageBegan;
+
+ ///
+ /// Invoked when a becomes unused by a .
+ ///
+ ///
+ /// If this uses pooled objects, this represents the time when the s become dead.
+ ///
+ public event Action HitObjectUsageFinished;
+
+ ///
+ /// The amount of time prior to the current time within which s should be considered alive.
+ ///
+ public double PastLifetimeExtension { get; set; }
+
+ ///
+ /// The amount of time after the current time within which s should be considered alive.
+ ///
+ public double FutureLifetimeExtension { get; set; }
+
private readonly Dictionary startTimeMap = new Dictionary();
private readonly Dictionary drawableMap = new Dictionary();
private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager();
@@ -88,6 +114,8 @@ namespace osu.Game.Rulesets.UI
bindStartTime(drawable);
AddInternal(drawableMap[entry] = drawable, false);
+
+ HitObjectUsageBegan?.Invoke(entry.HitObject);
}
private void removeDrawable(HitObjectLifetimeEntry entry)
@@ -103,6 +131,8 @@ namespace osu.Game.Rulesets.UI
unbindStartTime(drawable);
RemoveInternal(drawable);
+
+ HitObjectUsageFinished?.Invoke(entry.HitObject);
}
#endregion
@@ -159,7 +189,7 @@ namespace osu.Game.Rulesets.UI
protected override bool CheckChildrenLife()
{
bool aliveChanged = base.CheckChildrenLife();
- aliveChanged |= lifetimeManager.Update(Time.Current, Time.Current);
+ aliveChanged |= lifetimeManager.Update(Time.Current - PastLifetimeExtension, Time.Current + FutureLifetimeExtension);
return aliveChanged;
}
diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs
index 573a57d701..f12db57199 100644
--- a/osu.Game/Rulesets/UI/Playfield.cs
+++ b/osu.Game/Rulesets/UI/Playfield.cs
@@ -29,6 +29,22 @@ namespace osu.Game.Rulesets.UI
///
public event Action RevertResult;
+ ///
+ /// Invoked when a becomes used by a .
+ ///
+ ///
+ /// If this uses pooled objects, this represents the time when the s become alive.
+ ///
+ public event Action HitObjectUsageBegan;
+
+ ///
+ /// Invoked when a becomes unused by a .
+ ///
+ ///
+ /// If this uses pooled objects, this represents the time when the s become dead.
+ ///
+ public event Action HitObjectUsageFinished;
+
///
/// The contained in this Playfield.
///
@@ -88,6 +104,8 @@ namespace osu.Game.Rulesets.UI
{
h.NewResult += (d, r) => NewResult?.Invoke(d, r);
h.RevertResult += (d, r) => RevertResult?.Invoke(d, r);
+ h.HitObjectUsageBegan += o => HitObjectUsageBegan?.Invoke(o);
+ h.HitObjectUsageFinished += o => HitObjectUsageFinished?.Invoke(o);
}));
}
@@ -136,6 +154,8 @@ namespace osu.Game.Rulesets.UI
return false;
}
+ private readonly Dictionary lifetimeEntryMap = new Dictionary();
+
///
/// Adds a for a pooled to this .
///
@@ -143,6 +163,7 @@ namespace osu.Game.Rulesets.UI
public virtual void Add(HitObjectLifetimeEntry entry)
{
HitObjectContainer.Add(entry);
+ lifetimeEntryMap[entry.HitObject] = entry;
OnHitObjectAdded(entry.HitObject);
}
@@ -155,6 +176,7 @@ namespace osu.Game.Rulesets.UI
{
if (HitObjectContainer.Remove(entry))
{
+ lifetimeEntryMap.Remove(entry.HitObject);
OnHitObjectRemoved(entry.HitObject);
return true;
}
@@ -183,6 +205,77 @@ namespace osu.Game.Rulesets.UI
{
}
+ ///
+ /// Sets whether to keep a given always alive within this or any nested .
+ ///
+ /// The to set.
+ /// Whether to keep always alive.
+ public void SetKeepAlive(HitObject hitObject, bool keepAlive)
+ {
+ if (lifetimeEntryMap.TryGetValue(hitObject, out var entry))
+ {
+ entry.KeepAlive = keepAlive;
+ return;
+ }
+
+ if (!nestedPlayfields.IsValueCreated)
+ return;
+
+ foreach (var p in nestedPlayfields.Value)
+ p.SetKeepAlive(hitObject, keepAlive);
+ }
+
+ ///
+ /// Keeps all s alive within this and all nested s.
+ ///
+ public void KeepAllAlive()
+ {
+ foreach (var (_, entry) in lifetimeEntryMap)
+ entry.KeepAlive = true;
+
+ if (!nestedPlayfields.IsValueCreated)
+ return;
+
+ foreach (var p in nestedPlayfields.Value)
+ p.KeepAllAlive();
+ }
+
+ ///
+ /// The amount of time prior to the current time within which s should be considered alive.
+ ///
+ public double PastLifetimeExtension
+ {
+ get => HitObjectContainer.PastLifetimeExtension;
+ set
+ {
+ HitObjectContainer.PastLifetimeExtension = value;
+
+ if (!nestedPlayfields.IsValueCreated)
+ return;
+
+ foreach (var nested in nestedPlayfields.Value)
+ nested.PastLifetimeExtension = value;
+ }
+ }
+
+ ///
+ /// The amount of time after the current time within which s should be considered alive.
+ ///
+ public double FutureLifetimeExtension
+ {
+ get => HitObjectContainer.FutureLifetimeExtension;
+ set
+ {
+ HitObjectContainer.FutureLifetimeExtension = value;
+
+ if (!nestedPlayfields.IsValueCreated)
+ return;
+
+ foreach (var nested in nestedPlayfields.Value)
+ nested.FutureLifetimeExtension = value;
+ }
+ }
+
///
/// The cursor currently being used by this . May be null if no cursor is provided.
///
@@ -208,6 +301,8 @@ namespace osu.Game.Rulesets.UI
otherPlayfield.NewResult += (d, r) => NewResult?.Invoke(d, r);
otherPlayfield.RevertResult += (d, r) => RevertResult?.Invoke(d, r);
+ otherPlayfield.HitObjectUsageBegan += h => HitObjectUsageBegan?.Invoke(h);
+ otherPlayfield.HitObjectUsageFinished += h => HitObjectUsageFinished?.Invoke(h);
nestedPlayfields.Value.Add(otherPlayfield);
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index b67f6a6ba6..3229719d5a 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
@@ -34,6 +35,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected SelectionHandler SelectionHandler { get; private set; }
+ protected readonly HitObjectComposer Composer;
+
[Resolved(CanBeNull = true)]
private IEditorChangeHandler changeHandler { get; set; }
@@ -44,12 +47,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected EditorBeatmap Beatmap { get; private set; }
private readonly BindableList selectedHitObjects = new BindableList();
+ private readonly Dictionary blueprintMap = new Dictionary();
[Resolved(canBeNull: true)]
private IPositionSnapProvider snapProvider { get; set; }
- protected BlueprintContainer()
+ protected BlueprintContainer(HitObjectComposer composer)
{
+ Composer = composer;
+
RelativeSizeAxes = Axes.Both;
}
@@ -68,8 +74,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
DragBox.CreateProxy().With(p => p.Depth = float.MinValue)
});
- foreach (var obj in Beatmap.HitObjects)
- AddBlueprintFor(obj);
+ // For non-pooled rulesets, hitobjects are already present in the playfield which allows the blueprints to be loaded in the async context.
+ if (Composer != null)
+ {
+ foreach (var obj in Composer.HitObjects)
+ addBlueprintFor(obj.HitObject);
+ }
selectedHitObjects.BindTo(Beatmap.SelectedHitObjects);
selectedHitObjects.CollectionChanged += (selectedObjects, args) =>
@@ -94,8 +104,18 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
base.LoadComplete();
- Beatmap.HitObjectAdded += AddBlueprintFor;
+ Beatmap.HitObjectAdded += addBlueprintFor;
Beatmap.HitObjectRemoved += removeBlueprintFor;
+
+ if (Composer != null)
+ {
+ // For pooled rulesets, blueprints must be added for hitobjects already "current" as they would've not been "current" during the async load addition process above.
+ foreach (var obj in Composer.HitObjects)
+ addBlueprintFor(obj.HitObject);
+
+ Composer.Playfield.HitObjectUsageBegan += addBlueprintFor;
+ Composer.Playfield.HitObjectUsageFinished += removeBlueprintFor;
+ }
}
protected virtual Container CreateSelectionBlueprintContainer() =>
@@ -247,29 +267,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region Blueprint Addition/Removal
- private void removeBlueprintFor(HitObject hitObject)
+ private void addBlueprintFor(HitObject hitObject)
{
- var blueprint = SelectionBlueprints.SingleOrDefault(m => m.HitObject == hitObject);
- if (blueprint == null)
+ if (blueprintMap.ContainsKey(hitObject))
return;
- blueprint.Deselect();
-
- blueprint.Selected -= onBlueprintSelected;
- blueprint.Deselected -= onBlueprintDeselected;
-
- SelectionBlueprints.Remove(blueprint);
-
- if (movementBlueprint == blueprint)
- finishSelectionMovement();
- }
-
- protected virtual void AddBlueprintFor(HitObject hitObject)
- {
var blueprint = CreateBlueprintFor(hitObject);
if (blueprint == null)
return;
+ blueprintMap[hitObject] = blueprint;
+
blueprint.Selected += onBlueprintSelected;
blueprint.Deselected += onBlueprintDeselected;
@@ -277,6 +285,41 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.Select();
SelectionBlueprints.Add(blueprint);
+
+ OnBlueprintAdded(hitObject);
+ }
+
+ private void removeBlueprintFor(HitObject hitObject)
+ {
+ if (!blueprintMap.Remove(hitObject, out var blueprint))
+ return;
+
+ blueprint.Deselect();
+ blueprint.Selected -= onBlueprintSelected;
+ blueprint.Deselected -= onBlueprintDeselected;
+
+ SelectionBlueprints.Remove(blueprint);
+
+ if (movementBlueprint == blueprint)
+ finishSelectionMovement();
+
+ OnBlueprintRemoved(hitObject);
+ }
+
+ ///
+ /// Called after a blueprint has been added.
+ ///
+ /// The for which the blueprint has been added.
+ protected virtual void OnBlueprintAdded(HitObject hitObject)
+ {
+ }
+
+ ///
+ /// Called after a blueprint has been removed.
+ ///
+ /// The for which the blueprint has been removed.
+ protected virtual void OnBlueprintRemoved(HitObject hitObject)
+ {
}
#endregion
@@ -349,7 +392,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
/// Selects all s.
///
- private void selectAll() => SelectionBlueprints.ToList().ForEach(m => m.Select());
+ private void selectAll()
+ {
+ Composer.Playfield.KeepAllAlive();
+
+ // Scheduled to allow the change in lifetime to take place.
+ Schedule(() => SelectionBlueprints.ToList().ForEach(m => m.Select()));
+ }
///
/// Deselects all selected s.
@@ -360,12 +409,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
SelectionHandler.HandleSelected(blueprint);
SelectionBlueprints.ChangeChildDepth(blueprint, 1);
+
+ Composer.Playfield.SetKeepAlive(blueprint.HitObject, true);
}
private void onBlueprintDeselected(SelectionBlueprint blueprint)
{
SelectionHandler.HandleDeselected(blueprint);
SelectionBlueprints.ChangeChildDepth(blueprint, 0);
+
+ Composer.Playfield.SetKeepAlive(blueprint.HitObject, false);
}
#endregion
@@ -456,9 +509,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (Beatmap != null)
{
- Beatmap.HitObjectAdded -= AddBlueprintFor;
+ Beatmap.HitObjectAdded -= addBlueprintFor;
Beatmap.HitObjectRemoved -= removeBlueprintFor;
}
+
+ if (Composer != null)
+ {
+ Composer.Playfield.HitObjectUsageBegan -= addBlueprintFor;
+ Composer.Playfield.HitObjectUsageFinished -= removeBlueprintFor;
+ }
}
}
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
index 1527d20f54..0d2e2360b1 100644
--- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs
@@ -27,23 +27,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
public class ComposeBlueprintContainer : BlueprintContainer
{
- [Resolved]
- private HitObjectComposer composer { get; set; }
-
- private PlacementBlueprint currentPlacement;
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
private readonly Container placementBlueprintContainer;
- public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
-
+ private PlacementBlueprint currentPlacement;
private InputManager inputManager;
- private readonly IEnumerable drawableHitObjects;
-
- public ComposeBlueprintContainer(IEnumerable drawableHitObjects)
+ public ComposeBlueprintContainer(HitObjectComposer composer)
+ : base(composer)
{
- this.drawableHitObjects = drawableHitObjects;
-
placementBlueprintContainer = new Container
{
RelativeSizeAxes = Axes.Both
@@ -162,7 +155,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void updatePlacementPosition()
{
- var snapResult = composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position);
+ var snapResult = Composer.SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position);
currentPlacement.UpdatePosition(snapResult);
}
@@ -173,7 +166,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
base.Update();
- if (composer.CursorInPlacementArea)
+ if (Composer.CursorInPlacementArea)
createPlacement();
else if (currentPlacement?.PlacementActive == false)
removePlacement();
@@ -186,7 +179,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected sealed override SelectionBlueprint CreateBlueprintFor(HitObject hitObject)
{
- var drawable = drawableHitObjects.FirstOrDefault(d => d.HitObject == hitObject);
+ var drawable = Composer.HitObjects.FirstOrDefault(d => d.HitObject == hitObject);
if (drawable == null)
return null;
@@ -196,11 +189,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
public virtual OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null;
- protected override void AddBlueprintFor(HitObject hitObject)
+ protected override void OnBlueprintAdded(HitObject hitObject)
{
- refreshTool();
+ base.OnBlueprintAdded(hitObject);
- base.AddBlueprintFor(hitObject);
+ refreshTool();
// on successful placement, the new combo button should be reset as this is the most common user interaction.
if (Beatmap.SelectedHitObjects.Count == 0)
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index 7233faa955..f6675902fc 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -219,6 +219,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
[Resolved]
private IBeatSnapProvider beatSnapProvider { get; set; }
+ ///
+ /// The total amount of time visible on the timeline.
+ ///
+ public double VisibleRange => track.Length / Zoom;
+
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition))));
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs
index 10913a8bb9..0271b2def9 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs
@@ -26,12 +26,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private EditorBeatmap beatmap { get; set; }
private DragEvent lastDragEvent;
-
private Bindable placement;
-
private SelectionBlueprint placementBlueprint;
- public TimelineBlueprintContainer()
+ public TimelineBlueprintContainer(HitObjectComposer composer)
+ : base(composer)
{
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.Centre;
@@ -97,6 +96,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (lastDragEvent != null)
OnDrag(lastDragEvent);
+ if (Composer != null)
+ {
+ Composer.Playfield.PastLifetimeExtension = timeline.VisibleRange / 2;
+ Composer.Playfield.FutureLifetimeExtension = timeline.VisibleRange / 2;
+ }
+
base.Update();
}
diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
index 5282b4d998..d9948aa23c 100644
--- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
+++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs
@@ -53,6 +53,6 @@ namespace osu.Game.Screens.Edit.Compose
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer));
}
- protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer();
+ protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer(composer);
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 405fb1a6ca..1850ee3488 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 099ecd8319..2ac23f1503 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -88,7 +88,7 @@
-
+