From ad2f556133ca365fa1a441ddaf1c35690947b8da Mon Sep 17 00:00:00 2001
From: smoogipoo <smoogipoo@smgi.me>
Date: Tue, 20 Feb 2018 18:01:45 +0900
Subject: [PATCH] Add hitobject overlays to selected hitobjects

---
 .../Selection/OsuHitObjectOverlayLayer.cs     | 26 ++++++++
 .../Selection/Overlays/HitCircleOverlay.cs    | 33 +++++++++++
 .../Selection/Overlays/SliderCircleOverlay.cs | 45 ++++++++++++++
 .../Selection/Overlays/SliderOverlay.cs       | 55 +++++++++++++++++
 .../Edit/OsuHitObjectComposer.cs              |  4 ++
 .../Objects/Drawables/DrawableSlider.cs       | 27 ++++++---
 .../osu.Game.Rulesets.Osu.csproj              |  4 ++
 .../Visual/TestCaseEditorSelectionLayer.cs    | 13 +++-
 osu.Game/Rulesets/Edit/HitObjectComposer.cs   | 59 +++++++++++++------
 .../Edit/Layers/Selection/HitObjectOverlay.cs | 20 +++++++
 .../Layers/Selection/HitObjectOverlayLayer.cs | 53 +++++++++++++++++
 osu.Game/osu.Game.csproj                      |  2 +
 12 files changed, 315 insertions(+), 26 deletions(-)
 create mode 100644 osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs
 create mode 100644 osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs
 create mode 100644 osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs
 create mode 100644 osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs
 create mode 100644 osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs
 create mode 100644 osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs

diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs
new file mode 100644
index 0000000000..e0d1b34ca5
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/OsuHitObjectOverlayLayer.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Edit.Layers.Selection;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection
+{
+    public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer
+    {
+        protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject)
+        {
+            switch (hitObject)
+            {
+                case DrawableHitCircle circle:
+                    return new HitCircleOverlay(circle);
+                case DrawableSlider slider:
+                    return new SliderOverlay(slider);
+            }
+
+            return base.CreateOverlayFor(hitObject);
+        }
+    }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs
new file mode 100644
index 0000000000..4e64783840
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleOverlay.cs
@@ -0,0 +1,33 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Allocation;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit.Layers.Selection;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+
+namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
+{
+    public class HitCircleOverlay : HitObjectOverlay
+    {
+        public HitCircleOverlay(DrawableHitCircle hitCircle)
+            : base(hitCircle)
+        {
+            Origin = Anchor.Centre;
+
+            Position = hitCircle.Position;
+            Size = hitCircle.Size;
+            Scale = hitCircle.Scale;
+
+            AddInternal(new RingPiece());
+        }
+
+        [BackgroundDependencyLoader]
+        private void load(OsuColour colours)
+        {
+            Colour = colours.Yellow;
+        }
+    }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs
new file mode 100644
index 0000000000..0d60f62a2f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs
@@ -0,0 +1,45 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit.Layers.Selection;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
+{
+    public class SliderCircleOverlay : HitObjectOverlay
+    {
+        public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider)
+            : this(sliderHead, sliderHead.Position, slider)
+        {
+        }
+
+        public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider)
+            : this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1) + slider.HitObject.StackOffset, slider)
+        {
+        }
+
+        private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
+            : base(hitObject)
+        {
+            Origin = Anchor.Centre;
+
+            Position = position;
+            Size = slider.HeadCircle.Size;
+            Scale = slider.HeadCircle.Scale;
+
+            AddInternal(new RingPiece());
+        }
+
+        [BackgroundDependencyLoader]
+        private void load(OsuColour colours)
+        {
+            Colour = colours.Yellow;
+        }
+    }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs
new file mode 100644
index 0000000000..0a9b5638ea
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs
@@ -0,0 +1,55 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit.Layers.Selection;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using OpenTK.Graphics;
+
+namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
+{
+    public class SliderOverlay : HitObjectOverlay
+    {
+        private readonly SliderBody body;
+
+        private readonly DrawableSlider hitObject;
+
+        public SliderOverlay(DrawableSlider slider)
+            : base(slider)
+        {
+            hitObject = slider;
+
+            var obj = (Slider)slider.HitObject;
+
+            InternalChildren = new Drawable[]
+            {
+                body = new SliderBody(obj)
+                {
+                    AccentColour = Color4.Transparent,
+                    Position = obj.StackedPosition,
+                    PathWidth = obj.Scale * 64
+                },
+                new SliderCircleOverlay(slider.HeadCircle, slider),
+                new SliderCircleOverlay(slider.TailCircle, slider),
+            };
+        }
+
+        [BackgroundDependencyLoader]
+        private void load(OsuColour colours)
+        {
+            body.BorderColour = colours.Yellow;
+        }
+
+        protected override void Update()
+        {
+            base.Update();
+
+            hitObject.GetCurrentProgress(out int span, out double progress);
+            body.UpdateProgress(progress, span);
+        }
+    }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index ae19706da3..70d49a6b4f 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -5,7 +5,9 @@ using System.Collections.Generic;
 using osu.Framework.Graphics;
 using osu.Game.Beatmaps;
 using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Layers.Selection;
 using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
 using osu.Game.Rulesets.Osu.Objects;
 using osu.Game.Rulesets.Osu.UI;
 using osu.Game.Rulesets.UI;
@@ -29,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Edit
         };
 
         protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
+
+        protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer();
     }
 }
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 41df7ae4a4..b3f2f1850c 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
         private readonly List<Drawable> components = new List<Drawable>();
 
         public readonly DrawableHitCircle HeadCircle;
+        public readonly DrawableSliderTail TailCircle;
+
         public readonly SliderBody Body;
         public readonly SliderBall Ball;
 
@@ -29,7 +31,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
         {
             slider = s;
 
-            DrawableSliderTail tail;
             Container<DrawableSliderTick> ticks;
             Container<DrawableRepeatPoint> repeatPoints;
 
@@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
                     Alpha = 0
                 },
                 HeadCircle = new DrawableHitCircle(s.HeadCircle),
-                tail = new DrawableSliderTail(s.TailCircle)
+                TailCircle = new DrawableSliderTail(s.TailCircle)
             };
 
             components.Add(Body);
@@ -59,8 +60,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
 
             AddNested(HeadCircle);
 
-            AddNested(tail);
-            components.Add(tail);
+            AddNested(TailCircle);
+            components.Add(TailCircle);
 
             foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
             {
@@ -96,10 +97,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
 
             Tracking = Ball.Tracking;
 
-            double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
-
-            int span = slider.SpanAt(progress);
-            progress = slider.ProgressAt(progress);
+            GetCurrentProgress(out int span, out double progress);
 
             if (span > currentSpan)
                 currentSpan = span;
@@ -155,6 +153,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
             }
         }
 
+        /// <summary>
+        /// Finds the progress along the slider at the current time.
+        /// </summary>
+        /// <param name="span">The current span.</param>
+        /// <param name="progress">The current progress in the current span.</param>
+        public void GetCurrentProgress(out int span, out double progress)
+        {
+            double offset = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
+
+            span = slider.SpanAt(offset);
+            progress = slider.ProgressAt(offset);
+        }
+
         public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
 
         public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index 7838fb7707..53923e36ba 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -64,6 +64,10 @@
   <ItemGroup>
     <Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
     <Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
+    <Compile Include="Edit\Layers\Selection\OsuHitObjectOverlayLayer.cs" />
+    <Compile Include="Edit\Layers\Selection\Overlays\HitCircleOverlay.cs" />
+    <Compile Include="Edit\Layers\Selection\Overlays\SliderCircleOverlay.cs" />
+    <Compile Include="Edit\Layers\Selection\Overlays\SliderOverlay.cs" />
     <Compile Include="Edit\OsuEditPlayfield.cs" />
     <Compile Include="Edit\OsuEditRulesetContainer.cs" />
     <Compile Include="Edit\OsuHitObjectComposer.cs" />
diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
index 5e0c0e165c..0db03b08a7 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
@@ -6,10 +6,13 @@ using System.Collections.Generic;
 using osu.Framework.Allocation;
 using OpenTK;
 using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Edit;
 using osu.Game.Rulesets.Edit.Layers.Selection;
 using osu.Game.Rulesets.Objects;
 using osu.Game.Rulesets.Osu;
 using osu.Game.Rulesets.Osu.Edit;
+using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
+using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
 using osu.Game.Rulesets.Osu.Objects;
 using osu.Game.Tests.Beatmaps;
 
@@ -21,7 +24,15 @@ namespace osu.Game.Tests.Visual
         {
             typeof(SelectionBox),
             typeof(SelectionLayer),
-            typeof(CaptureBox)
+            typeof(CaptureBox),
+            typeof(HitObjectComposer),
+            typeof(OsuHitObjectComposer),
+            typeof(HitObjectOverlayLayer),
+            typeof(OsuHitObjectOverlayLayer),
+            typeof(HitObjectOverlay),
+            typeof(HitCircleOverlay),
+            typeof(SliderOverlay),
+            typeof(SliderCircleOverlay)
         };
 
         [BackgroundDependencyLoader]
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 62669150aa..67d4e8cc92 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Edit
         protected ICompositionTool CurrentTool { get; private set; }
 
         private RulesetContainer rulesetContainer;
-        private readonly Container[] layerContainers = new Container[2];
+        private readonly ScalableContainer[] layerContainers = new ScalableContainer[2];
 
         protected HitObjectComposer(Ruleset ruleset)
         {
@@ -49,20 +49,6 @@ namespace osu.Game.Rulesets.Edit
                 return;
             }
 
-            layerContainers[0] = CreateLayerContainer();
-            layerContainers[0].Child = new Container
-            {
-                Name = "Border",
-                RelativeSizeAxes = Axes.Both,
-                Masking = true,
-                BorderColour = Color4.White,
-                BorderThickness = 2,
-                Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true }
-            };
-
-            layerContainers[1] = CreateLayerContainer();
-            layerContainers[1].Child = new SelectionLayer(rulesetContainer.Playfield);
-
             RadioButtonCollection toolboxCollection;
             InternalChild = new GridContainer
             {
@@ -87,9 +73,9 @@ namespace osu.Game.Rulesets.Edit
                             RelativeSizeAxes = Axes.Both,
                             Children = new Drawable[]
                             {
-                                layerContainers[0],
+                                createBottomLayer(),
                                 rulesetContainer,
-                                layerContainers[1]
+                                createTopLayer()
                             }
                         }
                     },
@@ -112,6 +98,40 @@ namespace osu.Game.Rulesets.Edit
             toolboxCollection.Items[0].Select();
         }
 
+        private ScalableContainer createBottomLayer()
+        {
+            layerContainers[0] = CreateLayerContainer();
+            layerContainers[0].Child = new Container
+            {
+                Name = "Border",
+                RelativeSizeAxes = Axes.Both,
+                Masking = true,
+                BorderColour = Color4.White,
+                BorderThickness = 2,
+                Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true }
+            };
+
+            return layerContainers[0];
+        }
+
+        private ScalableContainer createTopLayer()
+        {
+            var overlayLayer = CreateHitObjectOverlayLayer();
+            var selectionLayer = new SelectionLayer(rulesetContainer.Playfield);
+
+            selectionLayer.ObjectSelected += overlayLayer.AddOverlay;
+            selectionLayer.ObjectDeselected += overlayLayer.RemoveOverlay;
+
+            layerContainers[1] = CreateLayerContainer();
+            layerContainers[1].Children = new Drawable[]
+            {
+                overlayLayer,
+                selectionLayer,
+            };
+
+            return layerContainers[1];
+        }
+
         protected override void UpdateAfterChildren()
         {
             base.UpdateAfterChildren();
@@ -135,5 +155,10 @@ namespace osu.Game.Rulesets.Edit
         /// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
         /// </summary>
         protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer();
+
+        /// <summary>
+        /// Creates the <see cref="HitObjectOverlayLayer"/> which overlays selected <see cref="DrawableHitObject"/>s.
+        /// </summary>
+        protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer();
     }
 }
diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs
new file mode 100644
index 0000000000..e18627ea5d
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Edit.Layers.Selection
+{
+    public class HitObjectOverlay : CompositeDrawable
+    {
+        // ReSharper disable once NotAccessedField.Local
+        // This will be used later to handle drag movement, etc
+        private readonly DrawableHitObject hitObject;
+
+        public HitObjectOverlay(DrawableHitObject hitObject)
+        {
+            this.hitObject = hitObject;
+        }
+    }
+}
diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs
new file mode 100644
index 0000000000..0b6e63d1fe
--- /dev/null
+++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlayLayer.cs
@@ -0,0 +1,53 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Edit.Layers.Selection
+{
+    public class HitObjectOverlayLayer : CompositeDrawable
+    {
+        private readonly Dictionary<DrawableHitObject, HitObjectOverlay> existingOverlays = new Dictionary<DrawableHitObject, HitObjectOverlay>();
+
+        public HitObjectOverlayLayer()
+        {
+            RelativeSizeAxes = Axes.Both;
+        }
+
+        /// <summary>
+        /// Adds an overlay for a <see cref="DrawableHitObject"/> which adds movement support.
+        /// </summary>
+        /// <param name="hitObject">The <see cref="DrawableHitObject"/> to create an overlay for.</param>
+        public void AddOverlay(DrawableHitObject hitObject)
+        {
+            var overlay = CreateOverlayFor(hitObject);
+            if (overlay == null)
+                return;
+
+            existingOverlays[hitObject] = overlay;
+            AddInternal(overlay);
+        }
+
+        /// <summary>
+        /// Removes the overlay for a <see cref="DrawableHitObject"/>.
+        /// </summary>
+        /// <param name="hitObject">The <see cref="DrawableHitObject"/> to remove the overlay for.</param>
+        public void RemoveOverlay(DrawableHitObject hitObject)
+        {
+            if (!existingOverlays.TryGetValue(hitObject, out var existing))
+                return;
+
+            existing.Hide();
+            existing.Expire();
+        }
+
+        /// <summary>
+        /// Creates a <see cref="HitObjectOverlay"/> for a specific <see cref="DrawableHitObject"/>.
+        /// </summary>
+        /// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
+        protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null;
+    }
+}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 5a827e155b..e4ddea49e8 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -347,6 +347,8 @@
     <Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
     <Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
     <Compile Include="Rulesets\Edit\Layers\Selection\CaptureBox.cs" />
+    <Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlay.cs" />
+    <Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlayLayer.cs" />
     <Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
     <Compile Include="Rulesets\Mods\IApplicableMod.cs" />
     <Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />