osu/osu.Game/Rulesets/UI/HitObjectContainer.cs

101 lines
3.7 KiB
C#
Raw Normal View History

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2018-04-13 09:19:50 +00:00
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
2018-04-13 09:19:50 +00:00
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
2020-10-29 06:20:10 +00:00
using osu.Framework.Graphics.Performance;
2018-04-13 09:19:50 +00:00
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.UI
{
public class HitObjectContainer : LifetimeManagementContainer
2018-04-13 09:19:50 +00:00
{
public IEnumerable<DrawableHitObject> Objects => InternalChildren.Cast<DrawableHitObject>().OrderBy(h => h.HitObject.StartTime);
public IEnumerable<DrawableHitObject> AliveObjects => AliveInternalChildren.Cast<DrawableHitObject>().OrderBy(h => h.HitObject.StartTime);
2018-04-13 09:19:50 +00:00
2020-11-06 15:57:33 +00:00
private readonly Dictionary<DrawableHitObject, (IBindable<double> bindable, double timeAtAdd)> startTimeMap = new Dictionary<DrawableHitObject, (IBindable<double>, double)>();
2018-09-21 05:35:50 +00:00
public HitObjectContainer()
{
RelativeSizeAxes = Axes.Both;
}
public virtual void Add(DrawableHitObject hitObject)
{
2020-11-06 15:57:33 +00:00
// Added first for the comparer to remain ordered during AddInternal
startTimeMap[hitObject] = (hitObject.HitObject.StartTimeBindable.GetBoundCopy(), hitObject.HitObject.StartTime);
startTimeMap[hitObject].bindable.BindValueChanged(_ => onStartTimeChanged(hitObject));
AddInternal(hitObject);
}
public virtual bool Remove(DrawableHitObject hitObject)
{
2019-12-18 03:03:15 +00:00
if (!RemoveInternal(hitObject))
return false;
2020-11-06 15:57:33 +00:00
// Removed last for the comparer to remain ordered during RemoveInternal
startTimeMap[hitObject].bindable.UnbindAll();
startTimeMap.Remove(hitObject);
2019-12-18 03:03:15 +00:00
return true;
}
2020-11-06 15:57:33 +00:00
protected override void Dispose(bool isDisposing)
2020-04-23 02:16:59 +00:00
{
2020-11-06 15:57:33 +00:00
base.Dispose(isDisposing);
unbindStartTimeMap();
}
2020-04-23 02:16:59 +00:00
2020-11-06 15:57:33 +00:00
public virtual void Clear(bool disposeChildren = true)
{
2020-11-06 15:57:33 +00:00
ClearInternal(disposeChildren);
unbindStartTimeMap();
2020-04-23 02:16:59 +00:00
}
2020-11-06 15:57:33 +00:00
private void unbindStartTimeMap()
{
2020-11-06 15:57:33 +00:00
foreach (var kvp in startTimeMap)
kvp.Value.bindable.UnbindAll();
startTimeMap.Clear();
}
2020-11-06 15:57:33 +00:00
public int IndexOf(DrawableHitObject hitObject) => IndexOfInternal(hitObject);
2020-11-06 15:57:33 +00:00
private void onStartTimeChanged(DrawableHitObject hitObject)
{
2020-11-06 15:57:33 +00:00
if (!RemoveInternal(hitObject))
return;
2018-04-13 09:19:50 +00:00
2020-11-06 15:57:33 +00:00
// Update the stored time, preserving the existing bindable
startTimeMap[hitObject] = (startTimeMap[hitObject].bindable, hitObject.HitObject.StartTime);
AddInternal(hitObject);
}
2018-04-13 09:19:50 +00:00
protected override int Compare(Drawable x, Drawable y)
{
if (!(x is DrawableHitObject xObj) || !(y is DrawableHitObject yObj))
return base.Compare(x, y);
// Put earlier hitobjects towards the end of the list, so they handle input first
2020-11-06 15:57:33 +00:00
int i = startTimeMap[yObj].timeAtAdd.CompareTo(startTimeMap[xObj].timeAtAdd);
2018-04-13 09:19:50 +00:00
return i == 0 ? CompareReverseChildID(x, y) : i;
}
2020-11-06 15:57:33 +00:00
protected override void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e)
{
2020-11-06 15:57:33 +00:00
if (!(e.Child is DrawableHitObject hitObject))
return;
if ((e.Kind == LifetimeBoundaryKind.End && e.Direction == LifetimeBoundaryCrossingDirection.Forward)
|| (e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward))
{
hitObject.OnKilled();
}
}
2018-04-13 09:19:50 +00:00
}
}