From abb3a6ca5bf610f50ff8519a4847bf1b650a2c67 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Nov 2019 22:51:49 +0900 Subject: [PATCH 01/14] Initial right click context menu implementation --- .../Compose/Components/BlueprintContainer.cs | 22 +++ .../Compose/Components/SelectionHandler.cs | 67 +++++++- osu.Game/Screens/Edit/Editor.cs | 143 +++++++++--------- 3 files changed, 162 insertions(+), 70 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 8fa022f129..af0ac22a70 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Edit.Compose.Components { @@ -95,12 +96,18 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { + if (e.Button == MouseButton.Right) + return false; + beginClickSelection(e); return true; } protected override bool OnClick(ClickEvent e) { + if (e.Button == MouseButton.Right) + return false; + // Deselection should only occur if no selected blueprints are hovered // A special case for when a blueprint was selected via this click is added since OnClick() may occur outside the hitobject and should not trigger deselection if (endClickSelection() || selectionHandler.SelectedBlueprints.Any(b => b.IsHovered)) @@ -112,6 +119,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDoubleClick(DoubleClickEvent e) { + if (e.Button == MouseButton.Right) + return false; + SelectionBlueprint clickedBlueprint = selectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered); if (clickedBlueprint == null) @@ -123,6 +133,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseUp(MouseUpEvent e) { + if (e.Button == MouseButton.Right) + return false; + // Special case for when a drag happened instead of a click Schedule(() => endClickSelection()); return true; @@ -141,6 +154,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDragStart(DragStartEvent e) { + if (e.Button == MouseButton.Right) + return false; + if (!beginSelectionMovement()) { dragBox.UpdateDrag(e); @@ -152,6 +168,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDrag(DragEvent e) { + if (e.Button == MouseButton.Right) + return false; + if (!moveCurrentSelection(e)) dragBox.UpdateDrag(e); @@ -160,6 +179,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDragEnd(DragEndEvent e) { + if (e.Button == MouseButton.Right) + return false; + if (!finishSelectionMovement()) { dragBox.FadeOut(250, Easing.OutQuint); diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 44bf22cfe1..779cd86318 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -7,11 +7,16 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Input.States; +using osu.Game.Audio; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -22,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// A component which outlines s and handles movement of selections. /// - public class SelectionHandler : CompositeDrawable, IKeyBindingHandler + public class SelectionHandler : CompositeDrawable, IKeyBindingHandler, IHasContextMenu { public const float BORDER_RADIUS = 2; @@ -142,6 +147,8 @@ namespace osu.Game.Screens.Edit.Compose.Components #endregion + #region Outline Display + /// /// Updates whether this is visible. /// @@ -176,5 +183,63 @@ namespace osu.Game.Screens.Edit.Compose.Components outline.Size = bottomRight - topLeft; outline.Position = topLeft; } + + #endregion + + #region Context Menu + + public virtual MenuItem[] ContextMenuItems + { + get + { + if (!SelectedBlueprints.Any()) + return Array.Empty(); + + return new MenuItem[] + { + new OsuMenuItem("hit sound") + { + Items = new[] + { + createHitSampleMenuItem(HitSampleInfo.HIT_WHISTLE), + createHitSampleMenuItem(HitSampleInfo.HIT_CLAP), + createHitSampleMenuItem(HitSampleInfo.HIT_FINISH) + } + } + }; + } + } + + private MenuItem createHitSampleMenuItem(string sampleName) + { + return new ToggleMenuItem(sampleName, MenuItemType.Standard, setHitSampleState) + { + State = { Value = getHitSampleState() } + }; + + void setHitSampleState(bool enabled) + { + if (enabled) + { + foreach (var h in SelectedHitObjects) + { + // Make sure there isn't already an existing sample + if (h.Samples.Any(s => s.Name == sampleName)) + continue; + + h.Samples.Add(new HitSampleInfo { Name = sampleName }); + } + } + else + { + foreach (var h in SelectedHitObjects) + h.Samples.RemoveAll(s => s.Name == sampleName); + } + } + + bool getHitSampleState() => SelectedHitObjects.All(h => h.Samples.Any(s => s.Name == sampleName)); + } + + #endregion } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 35408e4003..1ae3fd71ab 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -24,6 +24,7 @@ using osuTK.Input; using System.Collections.Generic; using osu.Framework; using osu.Framework.Input.Bindings; +using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Setup; @@ -85,87 +86,91 @@ namespace osu.Game.Screens.Edit fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); - InternalChildren = new[] + InternalChild = new OsuContextMenuContainer { - new Container + RelativeSizeAxes = Axes.Both, + Children = new[] { - Name = "Screen container", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 40, Bottom = 60 }, - Child = screenContainer = new Container + new Container { + Name = "Screen container", RelativeSizeAxes = Axes.Both, - Masking = true - } - }, - new Container - { - Name = "Top bar", - RelativeSizeAxes = Axes.X, - Height = 40, - Child = menuBar = new EditorMenuBar - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Items = new[] - { - new MenuItem("File") - { - Items = fileMenuItems - } - } - } - }, - new Container - { - Name = "Bottom bar", - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.X, - Height = 60, - Children = new Drawable[] - { - bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, - new Container + Padding = new MarginPadding { Top = 40, Bottom = 60 }, + Child = screenContainer = new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, - Child = new GridContainer + Masking = true + } + }, + new Container + { + Name = "Top bar", + RelativeSizeAxes = Axes.X, + Height = 40, + Child = menuBar = new EditorMenuBar + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Items = new[] + { + new MenuItem("File") + { + Items = fileMenuItems + } + } + } + }, + new Container + { + Name = "Bottom bar", + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 60, + Children = new Drawable[] + { + bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, + new Container { RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] + Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, + Child = new GridContainer { - new Dimension(GridSizeMode.Absolute, 220), - new Dimension(), - new Dimension(GridSizeMode.Absolute, 220) - }, - Content = new[] - { - new Drawable[] + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] { - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 10 }, - Child = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, - }, - new SummaryTimeline - { - RelativeSizeAxes = Axes.Both, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = 10 }, - Child = new PlaybackControl { RelativeSizeAxes = Axes.Both }, - } + new Dimension(GridSizeMode.Absolute, 220), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 220) }, - } - }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 10 }, + Child = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, + }, + new SummaryTimeline + { + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = 10 }, + Child = new PlaybackControl { RelativeSizeAxes = Axes.Both }, + } + }, + } + }, + } } - } - }, + }, + } }; menuBar.Mode.ValueChanged += onModeChanged; From 573d11503e27e7a99202a94ee08b2912e65bcb1f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 11:13:24 +0900 Subject: [PATCH 02/14] Remove unused using --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 779cd86318..43dbeb35f7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Audio; using osu.Game.Graphics; From 046f0b0fe5947f57ae312ee80c88e6e3d42b2529 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 13:40:47 +0900 Subject: [PATCH 03/14] Allow right-clicks to trigger selection --- .../Edit/Compose/Components/BlueprintContainer.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index af0ac22a70..e7877f962e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -96,11 +96,8 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { - if (e.Button == MouseButton.Right) - return false; - beginClickSelection(e); - return true; + return e.Button == MouseButton.Left; } protected override bool OnClick(ClickEvent e) @@ -133,12 +130,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseUp(MouseUpEvent e) { - if (e.Button == MouseButton.Right) - return false; - // Special case for when a drag happened instead of a click Schedule(() => endClickSelection()); - return true; + return e.Button == MouseButton.Left; } protected override bool OnMouseMove(MouseMoveEvent e) From 864b8db638b6b3a70eb3f6189d2a9761127b3e1d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 13:52:36 +0900 Subject: [PATCH 04/14] Use three states for the hitsound menu items --- .../Compose/Components/SelectionHandler.cs | 56 ++++++++++++------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 43dbeb35f7..dc147a92cd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -196,47 +196,61 @@ namespace osu.Game.Screens.Edit.Compose.Components return new MenuItem[] { - new OsuMenuItem("hit sound") + new OsuMenuItem("Sound") { Items = new[] { - createHitSampleMenuItem(HitSampleInfo.HIT_WHISTLE), - createHitSampleMenuItem(HitSampleInfo.HIT_CLAP), - createHitSampleMenuItem(HitSampleInfo.HIT_FINISH) + createHitSampleMenuItem("Whistle", HitSampleInfo.HIT_WHISTLE), + createHitSampleMenuItem("Clap", HitSampleInfo.HIT_CLAP), + createHitSampleMenuItem("Finish", HitSampleInfo.HIT_FINISH) } } }; } } - private MenuItem createHitSampleMenuItem(string sampleName) + private MenuItem createHitSampleMenuItem(string name, string sampleName) { - return new ToggleMenuItem(sampleName, MenuItemType.Standard, setHitSampleState) + return new ThreeStateMenuItem(name, MenuItemType.Standard, setHitSampleState) { State = { Value = getHitSampleState() } }; - void setHitSampleState(bool enabled) + void setHitSampleState(ThreeStates state) { - if (enabled) + switch (state) { - foreach (var h in SelectedHitObjects) - { - // Make sure there isn't already an existing sample - if (h.Samples.Any(s => s.Name == sampleName)) - continue; + case ThreeStates.Disabled: + foreach (var h in SelectedHitObjects) + h.Samples.RemoveAll(s => s.Name == sampleName); + break; - h.Samples.Add(new HitSampleInfo { Name = sampleName }); - } - } - else - { - foreach (var h in SelectedHitObjects) - h.Samples.RemoveAll(s => s.Name == sampleName); + case ThreeStates.Enabled: + foreach (var h in SelectedHitObjects) + { + // Make sure there isn't already an existing sample + if (h.Samples.Any(s => s.Name == sampleName)) + continue; + + h.Samples.Add(new HitSampleInfo { Name = sampleName }); + } + + break; } } - bool getHitSampleState() => SelectedHitObjects.All(h => h.Samples.Any(s => s.Name == sampleName)); + ThreeStates getHitSampleState() + { + int countExisting = SelectedHitObjects.Count(h => h.Samples.Any(s => s.Name == sampleName)); + + if (countExisting == 0) + return ThreeStates.Disabled; + + if (countExisting < SelectedHitObjects.Count()) + return ThreeStates.Indeterminate; + + return ThreeStates.Enabled; + } } #endregion From 6fc1be64c2c4adbb7335288d765c77e3343a5e73 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 14:04:57 +0900 Subject: [PATCH 05/14] Make hitobject samples a bindable list --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 2 +- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs | 6 +++--- osu.Game.Rulesets.Osu/Objects/Slider.cs | 6 +++--- .../Beatmaps/TaikoBeatmapConverter.cs | 6 +++--- osu.Game/Rulesets/Objects/HitObject.cs | 12 ++++++++---- .../Objects/Legacy/Catch/ConvertHitObjectParser.cs | 2 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 4 ++-- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- .../Objects/Legacy/Mania/ConvertHitObjectParser.cs | 2 +- .../Objects/Legacy/Osu/ConvertHitObjectParser.cs | 2 +- .../Objects/Legacy/Taiko/ConvertHitObjectParser.cs | 2 +- osu.Game/Rulesets/Objects/Types/IHasRepeats.cs | 2 +- .../Edit/Compose/Components/SelectionHandler.cs | 3 ++- 15 files changed, 30 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 0952e8981a..80a3af0aa0 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Objects public double Distance => Path.Distance; - public List> NodeSamples { get; set; } = new List>(); + public List> NodeSamples { get; set; } = new List>(); public double? LegacyLastTickOffset { get; set; } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index e10602312e..6c5bb304bf 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps /// /// The time to retrieve the sample info list from. /// - private List sampleInfoListAt(double time) + private IList sampleInfoListAt(double time) { var curveData = HitObject as IHasCurve; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index ea418eedb4..6297a68e08 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -472,7 +472,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The time to retrieve the sample info list from. /// - private List sampleInfoListAt(double time) + private IList sampleInfoListAt(double time) { var curveData = HitObject as IHasCurve; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index a955911bd5..46a2a36ac7 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -275,7 +275,7 @@ namespace osu.Game.Rulesets.Osu.Tests private Drawable createCatmull(int repeats = 0) { - var repeatSamples = new List>(); + var repeatSamples = new List>(); for (int i = 0; i < repeats; i++) repeatSamples.Add(new List()); @@ -297,9 +297,9 @@ namespace osu.Game.Rulesets.Osu.Tests return createDrawable(slider, 3, 1); } - private List> createEmptySamples(int repeats) + private List> createEmptySamples(int repeats) { - var repeatSamples = new List>(); + var repeatSamples = new List>(); for (int i = 0; i < repeats; i++) repeatSamples.Add(new List()); return repeatSamples; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index f60b7e67b2..c41026d954 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Objects /// internal float LazyTravelDistance; - public List> NodeSamples { get; set; } = new List>(); + public List> NodeSamples { get; set; } = new List>(); private int repeatCount; @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Objects foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) { - var firstSample = Samples.Find(s => s.Name == HitSampleInfo.HIT_NORMAL) + var firstSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) var sampleList = new List(); @@ -203,7 +203,7 @@ namespace osu.Game.Rulesets.Osu.Objects TailCircle.Position = EndPosition; } - private List getNodeSamples(int nodeIndex) => + private IList getNodeSamples(int nodeIndex) => nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples; public override Judgement CreateJudgement() => new OsuJudgement(); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index f0cf8d9c7d..180e0d8309 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps var curveData = obj as IHasCurve; // Old osu! used hit sounding to determine various hit type information - List samples = obj.Samples; + IList samples = obj.Samples; bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); @@ -117,13 +117,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) { - List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples }); + List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples }); int i = 0; for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) { - List currentSamples = allSamples[i]; + IList currentSamples = allSamples[i]; bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 6211fe50e6..ee0705ec5a 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Objects set => StartTimeBindable.Value = value; } - private List samples; + public readonly BindableList SamplesBindable = new BindableList(); /// /// The samples to be played when this hit object is hit. @@ -54,10 +54,14 @@ namespace osu.Game.Rulesets.Objects /// and can be treated as the default samples for the hit object. /// /// - public List Samples + public IList Samples { - get => samples ?? (samples = new List()); - set => samples = value; + get => SamplesBindable; + set + { + SamplesBindable.Clear(); + SamplesBindable.AddRange(value); + } } [JsonIgnore] diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 71e321f205..545cfe07f8 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, - List> nodeSamples) + List> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index e990938291..bbf46a0c8f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } // Generate the final per-node samples - var nodeSamples = new List>(nodes); + var nodeSamples = new List>(nodes); for (int i = 0; i < nodes; i++) nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); @@ -279,7 +279,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The samples to be played when the slider nodes are hit. This includes the head and tail of the slider. /// The hit object. protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, - List> nodeSamples); + List> nodeSamples); /// /// Creates a legacy Spinner-type hit object. diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index ff6b9be8b5..8d523022d6 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy public double Distance => Path.Distance; - public List> NodeSamples { get; set; } + public List> NodeSamples { get; set; } public int RepeatCount { get; set; } public double EndTime => StartTime + this.SpanCount() * Distance / Velocity; diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 94aba95e90..8012b4230f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, - List> nodeSamples) + List> nodeSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 65102f1e89..99872e630d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, - List> nodeSamples) + List> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index eb598f1368..9dc0c01932 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double? length, PathType pathType, int repeatCount, - List> nodeSamples) + List> nodeSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs index 697adeda98..b22752e902 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Objects.Types /// n-1: The last repeat.
/// n: The last node. ///
- List> NodeSamples { get; } + List> NodeSamples { get; } } public static class HasRepeatsExtensions diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index dc147a92cd..5f0bfb6d70 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -222,7 +222,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { case ThreeStates.Disabled: foreach (var h in SelectedHitObjects) - h.Samples.RemoveAll(s => s.Name == sampleName); + h.SamplesBindable.RemoveAll(s => s.Name == sampleName); + break; case ThreeStates.Enabled: From 53e6186b6d4421bdb4abb9feca0674ea827603fe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 14:59:47 +0900 Subject: [PATCH 06/14] Fix drawable hitobject samples not updating --- .../Objects/Drawables/DrawableHitObject.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1d9b66f88d..592cddeff6 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -78,6 +78,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public JudgementResult Result { get; private set; } + private BindableList samplesBindable; private Bindable startTimeBindable; private Bindable comboIndexBindable; @@ -108,20 +109,7 @@ namespace osu.Game.Rulesets.Objects.Drawables throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); } - var samples = GetSamples().ToArray(); - - if (samples.Length > 0) - { - if (HitObject.SampleControlPoint == null) - throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." - + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); - - samples = samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).ToArray(); - foreach (var s in samples) - s.Namespace = SampleNamespace; - - AddInternal(Samples = new SkinnableSound(samples)); - } + loadSamples(); } protected override void LoadComplete() @@ -139,10 +127,34 @@ namespace osu.Game.Rulesets.Objects.Drawables comboIndexBindable.BindValueChanged(_ => updateAccentColour(), true); } + samplesBindable = HitObject.SamplesBindable.GetBoundCopy(); + samplesBindable.ItemsAdded += _ => scheduleLoadSamples(); + samplesBindable.ItemsRemoved += _ => scheduleLoadSamples(); + updateState(ArmedState.Idle, true); onDefaultsApplied(); } + private void scheduleLoadSamples() => Scheduler.AddOnce(loadSamples); + + private void loadSamples() + { + var samples = GetSamples().ToArray(); + + if (samples.Length <= 0) + return; + + if (HitObject.SampleControlPoint == null) + throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." + + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); + + samples = samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).ToArray(); + foreach (var s in samples) + s.Namespace = SampleNamespace; + + AddInternal(Samples = new SkinnableSound(samples)); + } + private void onDefaultsApplied() => apply(HitObject); private void apply(HitObject hitObject) From df31acb2940ca5333e019e0340c2fdd67ff5f9e9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 15:39:07 +0900 Subject: [PATCH 07/14] Fix slider nested hitobject samples not getting updated --- .../TestSceneSlider.cs | 75 +++++++++++++++---- osu.Game.Rulesets.Osu/Objects/Slider.cs | 47 ++++++++---- 2 files changed, 93 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index 46a2a36ac7..5c656bf594 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -126,6 +126,67 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("body positioned correctly", () => slider.Position == slider.HitObject.StackedPosition); } + [Test] + public void TestChangeSamplesWithNoNodeSamples() + { + DrawableSlider slider = null; + + AddStep("create slider", () => + { + slider = (DrawableSlider)createSlider(repeats: 1); + Add(slider); + }); + + AddStep("change samples", () => slider.HitObject.Samples = new[] + { + new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP }, + new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE }, + }); + + AddAssert("head samples updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle)); + AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples)); + AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples)); + AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0); + + bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick"; + + bool assertSamples(HitObject hitObject) + { + return hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP) + && hitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_WHISTLE); + } + } + + [Test] + public void TestChangeSamplesWithNodeSamples() + { + DrawableSlider slider = null; + + AddStep("create slider", () => + { + slider = (DrawableSlider)createSlider(repeats: 1); + + for (int i = 0; i < 2; i++) + ((Slider)slider.HitObject).NodeSamples.Add(new List { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } }); + + Add(slider); + }); + + AddStep("change samples", () => slider.HitObject.Samples = new[] + { + new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP }, + new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE }, + }); + + AddAssert("head samples not updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle)); + AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples)); + AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples)); + AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0); + + bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick"; + bool assertSamples(HitObject hitObject) => hitObject.Samples.All(s => s.Name != HitSampleInfo.HIT_CLAP && s.Name != HitSampleInfo.HIT_WHISTLE); + } + private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); @@ -143,7 +204,6 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(52, -34) }, 700), RepeatCount = repeats, - NodeSamples = createEmptySamples(repeats), StackHeight = 10 }; @@ -174,7 +234,6 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(distance, 0), }, distance), RepeatCount = repeats, - NodeSamples = createEmptySamples(repeats), StackHeight = stackHeight }; @@ -194,7 +253,6 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(400, 0) }, 600), RepeatCount = repeats, - NodeSamples = createEmptySamples(repeats) }; return createDrawable(slider, 2, 3); @@ -218,7 +276,6 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(430, 0) }), RepeatCount = repeats, - NodeSamples = createEmptySamples(repeats) }; return createDrawable(slider, 2, 3); @@ -241,7 +298,6 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(430, 0) }), RepeatCount = repeats, - NodeSamples = createEmptySamples(repeats) }; return createDrawable(slider, 2, 3); @@ -265,7 +321,6 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(0, -200) }), RepeatCount = repeats, - NodeSamples = createEmptySamples(repeats) }; return createDrawable(slider, 2, 3); @@ -297,14 +352,6 @@ namespace osu.Game.Rulesets.Osu.Tests return createDrawable(slider, 3, 1); } - private List> createEmptySamples(int repeats) - { - var repeatSamples = new List>(); - for (int i = 0; i < repeats; i++) - repeatSamples.Add(new List()); - return repeatSamples; - } - private Drawable createDrawable(Slider slider, float circleSize, double speedMultiplier) { var cpi = new ControlPointInfo(); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index c41026d954..f3399af2de 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -108,6 +108,12 @@ namespace osu.Game.Rulesets.Osu.Objects public HitCircle HeadCircle; public SliderTailCircle TailCircle; + public Slider() + { + SamplesBindable.ItemsAdded += _ => updateNestedSamples(); + SamplesBindable.ItemsRemoved += _ => updateNestedSamples(); + } + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); @@ -128,18 +134,6 @@ namespace osu.Game.Rulesets.Osu.Objects foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset)) { - var firstSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) - ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) - var sampleList = new List(); - - if (firstSample != null) - sampleList.Add(new HitSampleInfo - { - Bank = firstSample.Bank, - Volume = firstSample.Volume, - Name = @"slidertick", - }); - switch (e.Type) { case SliderEventType.Tick: @@ -151,7 +145,6 @@ namespace osu.Game.Rulesets.Osu.Objects Position = Position + Path.PositionAt(e.PathProgress), StackHeight = StackHeight, Scale = Scale, - Samples = sampleList }); break; @@ -161,7 +154,6 @@ namespace osu.Game.Rulesets.Osu.Objects StartTime = e.Time, Position = Position, StackHeight = StackHeight, - Samples = getNodeSamples(0), SampleControlPoint = SampleControlPoint, }); break; @@ -187,11 +179,12 @@ namespace osu.Game.Rulesets.Osu.Objects Position = Position + Path.PositionAt(e.PathProgress), StackHeight = StackHeight, Scale = Scale, - Samples = getNodeSamples(e.SpanIndex + 1) }); break; } } + + updateNestedSamples(); } private void updateNestedPositions() @@ -203,6 +196,30 @@ namespace osu.Game.Rulesets.Osu.Objects TailCircle.Position = EndPosition; } + private void updateNestedSamples() + { + var firstSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) + ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) + var sampleList = new List(); + + if (firstSample != null) + sampleList.Add(new HitSampleInfo + { + Bank = firstSample.Bank, + Volume = firstSample.Volume, + Name = @"slidertick", + }); + + foreach (var tick in NestedHitObjects.OfType()) + tick.Samples = sampleList; + + foreach (var repeat in NestedHitObjects.OfType()) + repeat.Samples = getNodeSamples(repeat.RepeatIndex + 1); + + if (HeadCircle != null) + HeadCircle.Samples = getNodeSamples(0); + } + private IList getNodeSamples(int nodeIndex) => nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples; From a9b4106075614da8a60eae0afa3f30b85f7bf53d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 16:19:43 +0900 Subject: [PATCH 08/14] Remove unnecessary (for now) scheduling --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 592cddeff6..9c72a1a220 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -128,15 +128,13 @@ namespace osu.Game.Rulesets.Objects.Drawables } samplesBindable = HitObject.SamplesBindable.GetBoundCopy(); - samplesBindable.ItemsAdded += _ => scheduleLoadSamples(); - samplesBindable.ItemsRemoved += _ => scheduleLoadSamples(); + samplesBindable.ItemsAdded += _ => loadSamples(); + samplesBindable.ItemsRemoved += _ => loadSamples(); updateState(ArmedState.Idle, true); onDefaultsApplied(); } - private void scheduleLoadSamples() => Scheduler.AddOnce(loadSamples); - private void loadSamples() { var samples = GetSamples().ToArray(); From b4cb4c124366097b79239f267033eed6e7c3dffb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 16:19:55 +0900 Subject: [PATCH 09/14] Remove previous samples on change --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 9c72a1a220..818571bb23 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Objects.Drawables // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first protected virtual string SampleNamespace => null; - protected SkinnableSound Samples; + protected SkinnableSound Samples { get; private set; } protected virtual IEnumerable GetSamples() => HitObject.Samples; @@ -137,6 +137,12 @@ namespace osu.Game.Rulesets.Objects.Drawables private void loadSamples() { + if (Samples != null) + { + RemoveInternal(Samples); + Samples = null; + } + var samples = GetSamples().ToArray(); if (samples.Length <= 0) From df08a95734e8184847a55210f2da9eed9a6c30f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Nov 2019 16:46:58 +0900 Subject: [PATCH 10/14] Separate addition/removal into separate methods --- .../Compose/Components/SelectionHandler.cs | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 5f0bfb6d70..7b61640c89 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -185,6 +185,36 @@ namespace osu.Game.Screens.Edit.Compose.Components #endregion + #region Sample Changes + + /// + /// Adds a hit sample to all selected s. + /// + /// The name of the hit sample. + public void AddHitSample(string sampleName) + { + foreach (var h in SelectedHitObjects) + { + // Make sure there isn't already an existing sample + if (h.Samples.Any(s => s.Name == sampleName)) + continue; + + h.Samples.Add(new HitSampleInfo { Name = sampleName }); + } + } + + /// + /// Removes a hit sample from all selected s. + /// + /// The name of the hit sample. + public void RemoveHitSample(string sampleName) + { + foreach (var h in SelectedHitObjects) + h.SamplesBindable.RemoveAll(s => s.Name == sampleName); + } + + #endregion + #region Context Menu public virtual MenuItem[] ContextMenuItems @@ -221,21 +251,11 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (state) { case ThreeStates.Disabled: - foreach (var h in SelectedHitObjects) - h.SamplesBindable.RemoveAll(s => s.Name == sampleName); - + RemoveHitSample(sampleName); break; case ThreeStates.Enabled: - foreach (var h in SelectedHitObjects) - { - // Make sure there isn't already an existing sample - if (h.Samples.Any(s => s.Name == sampleName)) - continue; - - h.Samples.Add(new HitSampleInfo { Name = sampleName }); - } - + AddHitSample(sampleName); break; } } From a43b0ee01bc9b92db30ff4084c11c09b3547dd26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Nov 2019 10:45:46 +0900 Subject: [PATCH 11/14] Apply naming and styling changes --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 ++ .../Edit/Compose/Components/SelectionHandler.cs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index f3399af2de..c6f5a075e0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -203,12 +203,14 @@ namespace osu.Game.Rulesets.Osu.Objects var sampleList = new List(); if (firstSample != null) + { sampleList.Add(new HitSampleInfo { Bank = firstSample.Bank, Volume = firstSample.Volume, Name = @"slidertick", }); + } foreach (var tick in NestedHitObjects.OfType()) tick.Samples = sampleList; diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 7b61640c89..942643d116 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -241,36 +241,36 @@ namespace osu.Game.Screens.Edit.Compose.Components private MenuItem createHitSampleMenuItem(string name, string sampleName) { - return new ThreeStateMenuItem(name, MenuItemType.Standard, setHitSampleState) + return new TernaryStateMenuItem(name, MenuItemType.Standard, setHitSampleState) { State = { Value = getHitSampleState() } }; - void setHitSampleState(ThreeStates state) + void setHitSampleState(TernaryState state) { switch (state) { - case ThreeStates.Disabled: + case TernaryState.False: RemoveHitSample(sampleName); break; - case ThreeStates.Enabled: + case TernaryState.True: AddHitSample(sampleName); break; } } - ThreeStates getHitSampleState() + TernaryState getHitSampleState() { int countExisting = SelectedHitObjects.Count(h => h.Samples.Any(s => s.Name == sampleName)); if (countExisting == 0) - return ThreeStates.Disabled; + return TernaryState.False; if (countExisting < SelectedHitObjects.Count()) - return ThreeStates.Indeterminate; + return TernaryState.Indeterminate; - return ThreeStates.Enabled; + return TernaryState.True; } } From 9f1d490ac9876457e7299ed758da3858f893b105 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Nov 2019 12:18:24 +0900 Subject: [PATCH 12/14] Only handle selection input on blueprints --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 942643d116..258304d775 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -90,6 +90,8 @@ namespace osu.Game.Screens.Edit.Compose.Components public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => selectedBlueprints.Any(b => b.IsHovered); + #endregion #region Selection Handling From 3f8928ca253660e6ae7a686e5c6f0d3faad60075 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Nov 2019 13:41:54 +0900 Subject: [PATCH 13/14] Suppress warnings --- osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs | 3 ++- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 ++ osu.Game/Rulesets/RulesetConfigCache.cs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs index 179cd5e2dc..5a3ad5e786 100644 --- a/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs @@ -1,11 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Configuration.Tracking; namespace osu.Game.Rulesets.Configuration { - public interface IRulesetConfigManager : ITrackableConfigManager + public interface IRulesetConfigManager : ITrackableConfigManager, IDisposable { } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 818571bb23..35228e9ad1 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -149,8 +149,10 @@ namespace osu.Game.Rulesets.Objects.Drawables return; if (HitObject.SampleControlPoint == null) + { throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); + } samples = samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).ToArray(); foreach (var s in samples) diff --git a/osu.Game/Rulesets/RulesetConfigCache.cs b/osu.Game/Rulesets/RulesetConfigCache.cs index cdcd2666cf..d42428638c 100644 --- a/osu.Game/Rulesets/RulesetConfigCache.cs +++ b/osu.Game/Rulesets/RulesetConfigCache.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets // ensures any potential database operations are finalised before game destruction. foreach (var c in configCache.Values) - (c as IDisposable)?.Dispose(); + c?.Dispose(); } } } From b4525c1f6e95d61a56ca33b5a09cbeba38d60ab5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Nov 2019 14:42:30 +0900 Subject: [PATCH 14/14] Fix right clicking to select not showing context menu --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 258304d775..3a7c85d36c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -90,8 +90,6 @@ namespace osu.Game.Screens.Edit.Compose.Components public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => selectedBlueprints.Any(b => b.IsHovered); - #endregion #region Selection Handling @@ -223,7 +221,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { get { - if (!SelectedBlueprints.Any()) + if (!selectedBlueprints.Any(b => b.IsHovered)) return Array.Empty(); return new MenuItem[]