From 4a5cd6520ccd4c3e5977744763da3da4240c4e4a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 13:50:31 +0900 Subject: [PATCH 01/20] Extract playfield scaling into a separate class And make it more general. --- osu.Game/Rulesets/UI/Playfield.cs | 52 +++-------- osu.Game/Rulesets/UI/ScalableContainer.cs | 86 +++++++++++++++++++ .../UI/Scrolling/ScrollingPlayfield.cs | 11 ++- osu.Game/osu.Game.csproj | 1 + 4 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Rulesets/UI/ScalableContainer.cs diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index a7fed7059b..bbf20c2c26 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -3,52 +3,37 @@ using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; using osu.Framework.Allocation; namespace osu.Game.Rulesets.UI { - public abstract class Playfield : Container + public abstract class Playfield : ScalableContainer { /// /// The HitObjects contained in this Playfield. /// public HitObjectContainer HitObjects { get; private set; } - public Container ScaledContent; - - protected override Container Content => content; - private readonly Container content; - - private List nestedPlayfields; - /// /// All the s nested inside this playfield. /// public IReadOnlyList NestedPlayfields => nestedPlayfields; + private List nestedPlayfields; /// /// A container for keeping track of DrawableHitObjects. /// - /// Whether we want our internal coordinate system to be scaled to a specified width. - protected Playfield(float? customWidth = null) + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected Playfield(float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) { RelativeSizeAxes = Axes.Both; - - AddInternal(ScaledContent = new ScaledContainer - { - CustomWidth = customWidth, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - content = new Container - { - RelativeSizeAxes = Axes.Both, - } - } - }); } [BackgroundDependencyLoader] @@ -94,22 +79,5 @@ namespace osu.Game.Rulesets.UI /// Creates the container that will be used to contain the s. /// protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); - - private class ScaledContainer : Container - { - /// - /// A value (in game pixels that we should scale our content to match). - /// - public float? CustomWidth; - - //dividing by the customwidth will effectively scale our content to the required container size. - protected override Vector2 DrawScale => CustomWidth.HasValue ? new Vector2(DrawSize.X / CustomWidth.Value) : base.DrawScale; - - protected override void Update() - { - base.Update(); - RelativeChildSize = new Vector2(DrawScale.X, RelativeChildSize.Y); - } - } } } diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs new file mode 100644 index 0000000000..e1c1427470 --- /dev/null +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -0,0 +1,86 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + public class ScalableContainer : Container + { + /// + /// The scaled content. + /// + public readonly Container ScaledContent; + + protected override Container Content => content; + private readonly Container content; + + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + public ScalableContainer(float? customWidth = null, float? customHeight = null) + { + AddInternal(ScaledContent = new ScaledContainer + { + CustomWidth = customWidth, + CustomHeight = customHeight, + RelativeSizeAxes = Axes.Both, + Child = content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + + public class ScaledContainer : Container + { + /// + /// The value to scale the width of the content to match. + /// If null, is used. + /// + public float? CustomWidth; + + /// + /// The value to scale the height of the content to match. + /// if null, is used. + /// + public float? CustomHeight; + + /// + /// The scale that is required for the size of the content to match and . + /// + private Vector2 sizeScale + { + get + { + if (CustomWidth.HasValue && CustomHeight.HasValue) + return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value)); + if (CustomWidth.HasValue) + return new Vector2(DrawSize.X / CustomWidth.Value); + if (CustomHeight.HasValue) + return new Vector2(DrawSize.Y / CustomHeight.Value); + return Vector2.One; + } + } + + /// + /// Scale the content to the required container size by multiplying by . + /// + protected override Vector2 DrawScale => sizeScale * base.DrawScale; + + protected override void Update() + { + base.Update(); + RelativeChildSize = sizeScale; + } + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index e168f6daec..1c1c8f7f61 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -62,9 +62,14 @@ namespace osu.Game.Rulesets.UI.Scrolling /// Creates a new . /// /// The direction in which s in this container should scroll. - /// Whether we want our internal coordinate system to be scaled to a specified width - protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null) - : base(customWidth) + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) { this.direction = direction; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 70c904e8b9..5a827e155b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -355,6 +355,7 @@ + From 2c37a7e19fd64ec35628c40a3aa16528246c1647 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 13:50:55 +0900 Subject: [PATCH 02/20] Rewrite SelectionLayer testcase to construct an entire HitObjectComposer --- .../Visual/TestCaseEditorSelectionLayer.cs | 61 +++++++------------ 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 50a39e6c33..5e0c0e165c 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -5,15 +5,13 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; 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.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { @@ -27,44 +25,31 @@ namespace osu.Game.Tests.Visual }; [BackgroundDependencyLoader] - private void load() + private void load(OsuGameBase osuGame) { - var playfield = new OsuEditPlayfield(); - - Children = new Drawable[] + osuGame.Beatmap.Value = new TestWorkingBeatmap(new Beatmap { - new Container + HitObjects = new List { - RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(new StopwatchClock()), - Child = playfield + new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, + new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, + new Slider + { + ControlPoints = new List + { + new Vector2(128, 256), + new Vector2(344, 256), + }, + Distance = 400, + Position = new Vector2(128, 256), + Velocity = 1, + TickDistance = 100, + Scale = 0.5f, + } }, - new SelectionLayer(playfield) - }; + }); - var hitCircle1 = new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }; - var hitCircle2 = new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }; - var slider = new Slider - { - ControlPoints = new List - { - new Vector2(128, 256), - new Vector2(344, 256), - }, - Distance = 400, - Position = new Vector2(128, 256), - Velocity = 1, - TickDistance = 100, - Scale = 0.5f, - }; - - hitCircle1.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - hitCircle2.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - playfield.Add(new DrawableHitCircle(hitCircle1)); - playfield.Add(new DrawableHitCircle(hitCircle2)); - playfield.Add(new DrawableSlider(slider)); + Child = new OsuHitObjectComposer(new OsuRuleset()); } } } From 8f25a5da664991ad72ceb3f6da6442fe27d33cf6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 14:01:33 +0900 Subject: [PATCH 03/20] Add playfield aspect ratio + scaling modifications to composer layers --- .../Edit/OsuHitObjectComposer.cs | 4 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 50 +++++++++++++++---- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 6652a5fde2..ae19706da3 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -2,10 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Edit @@ -25,5 +27,7 @@ namespace osu.Game.Rulesets.Osu.Edit new HitObjectCompositionTool(), new HitObjectCompositionTool() }; + + protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 7f22b3764c..62669150aa 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -25,6 +26,9 @@ namespace osu.Game.Rulesets.Edit protected ICompositionTool CurrentTool { get; private set; } + private RulesetContainer rulesetContainer; + private readonly Container[] layerContainers = new Container[2]; + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; @@ -35,7 +39,6 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { - RulesetContainer rulesetContainer; try { rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); @@ -46,6 +49,20 @@ 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 { @@ -66,20 +83,13 @@ namespace osu.Game.Rulesets.Edit }, new Container { + Name = "Content", RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, + layerContainers[0], rulesetContainer, - new SelectionLayer(rulesetContainer.Playfield) + layerContainers[1] } } }, @@ -102,10 +112,28 @@ namespace osu.Game.Rulesets.Edit toolboxCollection.Items[0].Select(); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + layerContainers.ForEach(l => + { + l.Anchor = rulesetContainer.Playfield.Anchor; + l.Origin = rulesetContainer.Playfield.Origin; + l.Position = rulesetContainer.Playfield.Position; + l.Size = rulesetContainer.Playfield.Size; + }); + } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); protected abstract IReadOnlyList CompositionTools { get; } + + /// + /// Creates a which provides a layer above or below the . + /// + protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer(); } } From 87065a0b13618458cad8478d83bd31104dd15a94 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 14:38:57 +0900 Subject: [PATCH 04/20] Rename + publicise DeselectAll --- osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index bda613f617..f397f15e39 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - clearSelection(); + DeselectAll(); return true; } @@ -77,9 +77,10 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// /// Deselects all selected s. /// - private void clearSelection() + public void DeselectAll() { selectedHitObjects.Clear(); + captureBox?.Hide(); captureBox?.Expire(); } From 995ecb7d94fcfee4bc916e6d491137e1999ade61 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 14:48:35 +0900 Subject: [PATCH 05/20] Make selectedHitObjects into a HashSet for quick Contains checks --- osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index f397f15e39..3a43abd0b0 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; @@ -27,7 +28,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection private SelectionBox selectionBox; private CaptureBox captureBox; - private readonly List selectedHitObjects = new List(); + private readonly HashSet selectedHitObjects = new HashSet(); protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { From 285df52f43dffe9fadbb90fd6c7ffaa5cbef2112 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 14:54:45 +0900 Subject: [PATCH 06/20] Encapsulate logic for selecting/deselecting further --- .../Edit/Layers/Selection/SelectionLayer.cs | 77 +++++++++++++++++-- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index 3a43abd0b0..1131389367 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; @@ -16,6 +17,9 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection { public class SelectionLayer : CompositeDrawable { + public event Action ObjectSelected; + public event Action ObjectDeselected; + private readonly Playfield playfield; public SelectionLayer(Playfield playfield) @@ -75,15 +79,69 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection return true; } + /// + /// Selects a . + /// + /// The to select. + public void Select(DrawableHitObject hitObject) + { + if (!select(hitObject)) + return; + + clearCapture(); + finishSelection(); + } + + /// + /// Selects a without performing capture updates. + /// + /// The to select. + /// Whether was selected. + private bool select(DrawableHitObject hitObject) + { + if (!selectedHitObjects.Add(hitObject)) + return false; + + ObjectSelected?.Invoke(hitObject); + return true; + } + + /// + /// Deselects a . + /// + /// The to deselect. + public void Deselect(DrawableHitObject hitObject) + { + if (!deselect(hitObject)) + return; + + clearCapture(); + finishSelection(); + } + + /// + /// Deselects a without performing capture updates. + /// + /// The to deselect. + /// Whether the was deselected. + private bool deselect(DrawableHitObject hitObject) + { + if (!selectedHitObjects.Remove(hitObject)) + return false; + + ObjectDeselected?.Invoke(hitObject); + return true; + } + /// /// Deselects all selected s. /// public void DeselectAll() { + selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h)); selectedHitObjects.Clear(); - captureBox?.Hide(); - captureBox?.Expire(); + clearCapture(); } /// @@ -92,8 +150,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// The selection . private void selectQuad(Quad screenSpaceQuad) { - foreach (var obj in playfield.HitObjects.Objects.Where(h => h.IsAlive && h.IsPresent && screenSpaceQuad.Contains(h.SelectionPoint))) - selectedHitObjects.Add(obj); + playfield.HitObjects.Objects.Where(h => h.IsAlive && h.IsPresent && screenSpaceQuad.Contains(h.SelectionPoint)).ForEach(h => select(h)); } /// @@ -102,11 +159,17 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// The to select at. private void selectPoint(Vector2 screenSpacePoint) { - var selected = playfield.HitObjects.Objects.Reverse().Where(h => h.IsAlive && h.IsPresent).FirstOrDefault(h => h.ReceiveMouseInputAt(screenSpacePoint)); - if (selected == null) + var target = playfield.HitObjects.Objects.Reverse().Where(h => h.IsAlive && h.IsPresent).FirstOrDefault(h => h.ReceiveMouseInputAt(screenSpacePoint)); + if (target == null) return; - selectedHitObjects.Add(selected); + select(target); + } + + private void clearCapture() + { + captureBox?.Hide(); + captureBox?.Expire(); } private void finishSelection() From 2f4925d031bde025574d532a75816425e0bd09a6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 15:06:14 +0900 Subject: [PATCH 07/20] Add some xmldocs --- osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index 1131389367..d827cc3b85 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -17,7 +17,14 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection { public class SelectionLayer : CompositeDrawable { + /// + /// Invoked when a is selected. + /// public event Action ObjectSelected; + + /// + /// Invoked when a is deselected. + /// public event Action ObjectDeselected; private readonly Playfield playfield; From 2a5bfdb4b85ecce29c159ed6fa208625fede7ac9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 15:06:51 +0900 Subject: [PATCH 08/20] Deselect hitobjects that are now not in the selection quad --- .../Rulesets/Edit/Layers/Selection/SelectionLayer.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs index d827cc3b85..3895d34d7f 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/SelectionLayer.cs @@ -155,9 +155,16 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection /// Selects all hitobjects that are present within the area of a . /// /// The selection . + // Todo: If needed we can severely reduce allocations in this method private void selectQuad(Quad screenSpaceQuad) { - playfield.HitObjects.Objects.Where(h => h.IsAlive && h.IsPresent && screenSpaceQuad.Contains(h.SelectionPoint)).ForEach(h => select(h)); + var expectedSelection = playfield.HitObjects.Objects.Where(h => h.IsAlive && h.IsPresent && screenSpaceQuad.Contains(h.SelectionPoint)).ToList(); + + var toRemove = selectedHitObjects.Except(expectedSelection).ToList(); + foreach (var obj in toRemove) + deselect(obj); + + expectedSelection.ForEach(h => select(h)); } /// From ad2f556133ca365fa1a441ddaf1c35690947b8da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 18:01:45 +0900 Subject: [PATCH 09/20] 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 . +// 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 . +// 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 . +// 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 . +// 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 components = new List(); 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 ticks; Container 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()) { @@ -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 } } + /// + /// Finds the progress along the slider at the current time. + /// + /// The current span. + /// The current progress in the current span. + 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 @@ + + + + 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 which provides a layer above or below the . /// protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer(); + + /// + /// Creates the which overlays selected s. + /// + 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 . +// 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 . +// 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 existingOverlays = new Dictionary(); + + public HitObjectOverlayLayer() + { + RelativeSizeAxes = Axes.Both; + } + + /// + /// Adds an overlay for a which adds movement support. + /// + /// The to create an overlay for. + public void AddOverlay(DrawableHitObject hitObject) + { + var overlay = CreateOverlayFor(hitObject); + if (overlay == null) + return; + + existingOverlays[hitObject] = overlay; + AddInternal(overlay); + } + + /// + /// Removes the overlay for a . + /// + /// The to remove the overlay for. + public void RemoveOverlay(DrawableHitObject hitObject) + { + if (!existingOverlays.TryGetValue(hitObject, out var existing)) + return; + + existing.Hide(); + existing.Expire(); + } + + /// + /// Creates a for a specific . + /// + /// The to create the overlay for. + 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 @@ + + From 604b0fa20abe871b1369a16ebc7258e2e4111fbc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Feb 2018 14:11:06 +0900 Subject: [PATCH 10/20] Fix post-merge issues --- .../Edit/Layers/Selection/Overlays/SliderOverlay.cs | 8 ++------ .../Objects/Drawables/DrawableSlider.cs | 13 ------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs index 0a9b5638ea..4de44a7ae1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs @@ -16,13 +16,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { private readonly SliderBody body; - private readonly DrawableSlider hitObject; - public SliderOverlay(DrawableSlider slider) : base(slider) { - hitObject = slider; - var obj = (Slider)slider.HitObject; InternalChildren = new Drawable[] @@ -48,8 +44,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { base.Update(); - hitObject.GetCurrentProgress(out int span, out double progress); - body.UpdateProgress(progress, span); + // Need to cause one update + body.UpdateProgress(0); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 278f3d79f4..cc6f8ad34f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -157,19 +157,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - /// - /// Finds the progress along the slider at the current time. - /// - /// The current span. - /// The current progress in the current span. - 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); From 07252254eb1cf769e3be516d3dae5c5d5bf82ad6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Feb 2018 14:22:04 +0900 Subject: [PATCH 11/20] Fix post-merge issues --- .../Visual/TestCaseEditorSelectionLayer.cs | 1 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 1724acaf1a..0db03b08a7 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -6,6 +6,7 @@ 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; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 0754a67bd2..4420f1a6b8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -51,14 +50,17 @@ namespace osu.Game.Rulesets.Edit return; } - ScalableContainer createLayerContainerWithContent(Drawable content) + ScalableContainer createLayerContainerWithContent(params Drawable[] content) { var container = CreateLayerContainer(); - container.Child = content; + container.Children = content; layerContainers.Add(container); return container; } + HitObjectOverlayLayer hitObjectOverlayLayer; + SelectionLayer selectionLayer; + RadioButtonCollection toolboxCollection; InternalChild = new GridContainer { @@ -93,7 +95,11 @@ namespace osu.Game.Rulesets.Edit Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } }), rulesetContainer, - createLayerContainerWithContent(new SelectionLayer(rulesetContainer.Playfield)) + createLayerContainerWithContent + ( + hitObjectOverlayLayer = CreateHitObjectOverlayLayer(), + selectionLayer = new SelectionLayer(rulesetContainer.Playfield) + ) } } }, @@ -104,6 +110,9 @@ namespace osu.Game.Rulesets.Edit } }; + selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay; + selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay; + toolboxCollection.Items = new[] { new RadioButton("Select", () => setCompositionTool(null)) } .Concat( From 844e87aafeb7cfc7ef43066222dd21f0e39fc242 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Feb 2018 14:25:10 +0900 Subject: [PATCH 12/20] Use the true head position rather than capturing current position --- .../Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs index 0d60f62a2f..b3bf25fe67 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays public class SliderCircleOverlay : HitObjectOverlay { public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider) - : this(sliderHead, sliderHead.Position, slider) + : this(sliderHead, ((Slider)slider.HitObject).StackedPositionAt(0), slider) { } From 45c579630daf86bdcd0bb836fba89da1714974bf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Feb 2018 14:34:37 +0900 Subject: [PATCH 13/20] Make selectionLayer handle input below object overlays --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 4420f1a6b8..2a799a460f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -58,8 +58,8 @@ namespace osu.Game.Rulesets.Edit return container; } - HitObjectOverlayLayer hitObjectOverlayLayer; - SelectionLayer selectionLayer; + HitObjectOverlayLayer hitObjectOverlayLayer = CreateHitObjectOverlayLayer(); + SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield); RadioButtonCollection toolboxCollection; InternalChild = new GridContainer @@ -97,8 +97,9 @@ namespace osu.Game.Rulesets.Edit rulesetContainer, createLayerContainerWithContent ( - hitObjectOverlayLayer = CreateHitObjectOverlayLayer(), - selectionLayer = new SelectionLayer(rulesetContainer.Playfield) + selectionLayer, // Below object overlays for input + hitObjectOverlayLayer, + selectionLayer.CreateProxy() // Proxy above object overlays for selections ) } } From 0cdf6fa7e2bc847024d34bd25558d4565a84f834 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Feb 2018 14:36:58 +0900 Subject: [PATCH 14/20] Make HitObjectOverlay into an OverlayContainer --- .../Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs index e18627ea5d..543dd2cc54 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectOverlay.cs @@ -6,7 +6,7 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Edit.Layers.Selection { - public class HitObjectOverlay : CompositeDrawable + public class HitObjectOverlay : OverlayContainer { // ReSharper disable once NotAccessedField.Local // This will be used later to handle drag movement, etc @@ -15,6 +15,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection public HitObjectOverlay(DrawableHitObject hitObject) { this.hitObject = hitObject; + + State = Visibility.Visible; } + + protected override void PopIn() => Alpha = 1; + protected override void PopOut() => Alpha = 0; } } From d72290ee241cbcadab934c4c0960255aabcfda7b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 15:06:08 +0900 Subject: [PATCH 15/20] Make ScaledContainer private --- osu.Game/Rulesets/UI/ScalableContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs index 43ed770f77..9762828e7d 100644 --- a/osu.Game/Rulesets/UI/ScalableContainer.cs +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.UI }); } - public class ScaledContainer : Container + private class ScaledContainer : Container { /// /// The value to scale the width of the content to match. From 278b25bcbd6568c6da03e0c25934d089a928bd63 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 15:29:56 +0900 Subject: [PATCH 16/20] Fix border being the wrong size --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 45 +++++++++----------- osu.Game/Rulesets/Edit/Layers/BorderLayer.cs | 38 +++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 3 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/Layers/BorderLayer.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 2a799a460f..e6a51cc39b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -4,14 +4,13 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Layers; using osu.Game.Rulesets.Edit.Layers.Selection; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.UI; @@ -50,17 +49,26 @@ namespace osu.Game.Rulesets.Edit return; } - ScalableContainer createLayerContainerWithContent(params Drawable[] content) - { - var container = CreateLayerContainer(); - container.Children = content; - layerContainers.Add(container); - return container; - } - HitObjectOverlayLayer hitObjectOverlayLayer = CreateHitObjectOverlayLayer(); SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield); + var layerBelowRuleset = new BorderLayer + { + RelativeSizeAxes = Axes.Both, + Child = CreateLayerContainer() + }; + + var layerAboveRuleset = CreateLayerContainer(); + layerAboveRuleset.Children = new Drawable[] + { + selectionLayer, // Below object overlays for input + hitObjectOverlayLayer, + selectionLayer.CreateProxy() // Proxy above object overlays for selections + }; + + layerContainers.Add(layerBelowRuleset); + layerContainers.Add(layerAboveRuleset); + RadioButtonCollection toolboxCollection; InternalChild = new GridContainer { @@ -85,22 +93,9 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - createLayerContainerWithContent(new Container - { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, - Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } - }), + layerBelowRuleset, rulesetContainer, - createLayerContainerWithContent - ( - selectionLayer, // Below object overlays for input - hitObjectOverlayLayer, - selectionLayer.CreateProxy() // Proxy above object overlays for selections - ) + layerAboveRuleset } } }, diff --git a/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs b/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs new file mode 100644 index 0000000000..54c30b8d89 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Layers/BorderLayer.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Edit.Layers +{ + public class BorderLayer : Container + { + protected override Container Content => content; + private readonly Container content; + + public BorderLayer() + { + InternalChildren = new Drawable[] + { + new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + }, + content = new Container { RelativeSizeAxes = Axes.Both } + }; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5bd279d2a8..2fe059cb84 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -348,6 +348,7 @@ + From e62fab6cc21321d93e212940b691a873d5d01692 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 14:34:31 +0900 Subject: [PATCH 17/20] Fix overlay positions/sizes due to sliders now being fixed --- .../Selection/Overlays/SliderCircleOverlay.cs | 16 +++++++++++++--- .../Layers/Selection/Overlays/SliderOverlay.cs | 8 +++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs index b3bf25fe67..3c7f8a067b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleOverlay.cs @@ -5,7 +5,6 @@ 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; @@ -15,18 +14,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays public class SliderCircleOverlay : HitObjectOverlay { public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider) - : this(sliderHead, ((Slider)slider.HitObject).StackedPositionAt(0), slider) + : this(sliderHead, sliderHead.Position, slider) { } public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider) - : this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1) + slider.HitObject.StackOffset, slider) + : this(sliderTail, sliderTail.Position, slider) { } + private readonly DrawableOsuHitObject hitObject; + private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider) : base(hitObject) { + this.hitObject = hitObject; + Origin = Anchor.Centre; Position = position; @@ -41,5 +44,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { Colour = colours.Yellow; } + + protected override void Update() + { + base.Update(); + + RelativeAnchorPosition = hitObject.RelativeAnchorPosition; + } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs index 4de44a7ae1..a035a683e9 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderOverlay.cs @@ -15,10 +15,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays public class SliderOverlay : HitObjectOverlay { private readonly SliderBody body; + private readonly DrawableSlider slider; public SliderOverlay(DrawableSlider slider) : base(slider) { + this.slider = slider; + var obj = (Slider)slider.HitObject; InternalChildren = new Drawable[] @@ -26,7 +29,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays body = new SliderBody(obj) { AccentColour = Color4.Transparent, - Position = obj.StackedPosition, PathWidth = obj.Scale * 64 }, new SliderCircleOverlay(slider.HeadCircle, slider), @@ -44,6 +46,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays { base.Update(); + Position = slider.Position; + Size = slider.Size; + OriginPosition = slider.OriginPosition; + // Need to cause one update body.UpdateProgress(0); } From 6dc9411a90e63d72e76d9f0f20aa0fa1c9d378ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Mar 2018 16:01:32 +0900 Subject: [PATCH 18/20] Don't use BufferedContainer for OsuLogo Minor performance improvement. Not sure why this was done but it's not required any more. --- osu.Game/Screens/Menu/OsuLogo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index b91ff0d74b..3fcb885655 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -143,7 +143,7 @@ namespace osu.Game.Screens.Menu Alpha = 0.5f, Size = new Vector2(0.96f) }, - new BufferedContainer + new Container { AutoSizeAxes = Axes.Both, Children = new Drawable[] From 8cfb1193836a35fa2d52a43d97b27ad2bc5239eb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Mar 2018 16:17:01 +0900 Subject: [PATCH 19/20] Fix FastRandom using uint instead of int for NextDouble() --- osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs index e14473c478..c8277af415 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/FastRandom.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils /// internal class FastRandom { - private const double uint_to_real = 1.0 / (uint.MaxValue + 1.0); + private const double int_to_real = 1.0 / (int.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; private const uint y = 842502087; private const uint z = 3579807591; @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils /// Generates a random double value within the range [0, 1). /// /// The random value. - public double NextDouble() => uint_to_real * NextUInt(); + public double NextDouble() => int_to_real * Next(); private uint bitBuffer; private int bitIndex = 32; From 0704d823a954402522cb2d3a7782a75e726680ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 1 Mar 2018 22:02:53 +0900 Subject: [PATCH 20/20] Fix slider selection point not being set to new origin --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index fd944dbdae..f715ed075c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); - public override Vector2 SelectionPoint => ToScreenSpace(Body.Position); + public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); public override Quad SelectionQuad => Body.PathDrawQuad; } }