From bcba0aa4148334e3c412ca7f895912a0303179b3 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 8 Nov 2020 14:31:08 +0900 Subject: [PATCH 1/3] Add content menu display support in test case. --- osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs b/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs index c086bec9b..59ecc7152 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Cursor; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Karaoke.Beatmaps; using osu.Game.Rulesets.Karaoke.Edit.Singers; @@ -70,7 +71,11 @@ private void load() Dependencies.CacheAs(editorClock); Dependencies.Cache(beatDivisor); - base.Content.Add(Content); + base.Content.Add(new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = Content + }); } [SetUp] From 56a2d4754d4b88c99c4939a48da103935391d2d8 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 8 Nov 2020 15:20:50 +0900 Subject: [PATCH 2/3] Implement lyric assign to singer logic in singer manager. --- .../Edit/TestSceneSinger.cs | 2 +- .../Timeline/LyricBlueprintContainer.cs | 2 - .../LyricTimelineHitObjectBlueprint.cs | 11 +++- .../Timeline/LyricTimelineSelectionHandler.cs | 16 +++--- .../Edit/Singers/SingerManager.cs | 53 +++++++++++++++++++ 5 files changed, 71 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs b/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs index 59ecc7152..c583e951c 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Edit/TestSceneSinger.cs @@ -88,7 +88,7 @@ public void SetUp() => Schedule(() => public void HoverToSingerArea() { // todo : add this step because description is not showing. - AddStep("Move mouse to singer area", () => InputManager.MoveMouseTo(Child, new Vector2(-400,-100))); + AddStep("Move mouse to singer area", () => InputManager.MoveMouseTo(Child, new Vector2(-450,-90))); } } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricBlueprintContainer.cs b/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricBlueprintContainer.cs index 2b89617a9..975ab7c91 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricBlueprintContainer.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricBlueprintContainer.cs @@ -24,8 +24,6 @@ protected override void AddBlueprintFor(HitObject hitObject) if (!(hitObject is Lyric)) return; - // todo : check lyric is at the same singer. - base.AddBlueprintFor(hitObject); } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineHitObjectBlueprint.cs b/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineHitObjectBlueprint.cs index f9bbc5c37..370cb1899 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineHitObjectBlueprint.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineHitObjectBlueprint.cs @@ -1,6 +1,7 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -24,6 +25,8 @@ public class LyricTimelineHitObjectBlueprint : TimelineHitObjectBlueprint, IHasC public LyricTimelineHitObjectBlueprint(HitObject hitObject, Singer singer) : base(hitObject) { + this.singer = singer; + // Use tricty way to hide the timeline's component. InternalChildren.ForEach(x => x.Alpha = 0); @@ -52,13 +55,17 @@ public LyricTimelineHitObjectBlueprint(HitObject hitObject, Singer singer) } } }); + } - if (hitObject is Lyric lyric) + [BackgroundDependencyLoader] + private void load(SingerManager singerManager) + { + if (HitObject is Lyric lyric) { lyric.SingersBindable.BindValueChanged(e => { // Check is lyric contains this singer, or default singer - var isSingerMatch = e.NewValue?.Contains(singer.ID) ?? singer.ID == 0; + var isSingerMatch = singerManager.SingerInLyric(singer, lyric); if (isSingerMatch) { Show(); diff --git a/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineSelectionHandler.cs b/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineSelectionHandler.cs index 31a060f04..c9a91f211 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineSelectionHandler.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Singers/Components/Timeline/LyricTimelineSelectionHandler.cs @@ -1,27 +1,27 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using Lucene.Net.Support; +using osu.Framework.Allocation; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Screens.Edit.Compose.Components; using System.Collections.Generic; +using System.Linq; using static osu.Game.Rulesets.Karaoke.Edit.Components.Timeline.TimelineBlueprintContainer; namespace osu.Game.Rulesets.Karaoke.Edit.Singers.Components.Timeline { internal class LyricTimelineSelectionHandler : TimelineSelectionHandler { + [Resolved] + private SingerManager singerManager { get; set; } + public override bool HandleMovement(MoveSelectionEvent moveEvent) => false; protected override IEnumerable GetContextMenuItemsForSelection(IEnumerable selection) - { - // todo : make implementation of singers selection. - var menu = new List - { - new OsuMenuItem("Test1", MenuItemType.Standard, () => { }) - }; - return menu.ToArray(); - } + => singerManager.CreateSingerContextMenu(selection.Select(x => x.HitObject).OfType().ToList()); } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs b/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs index 2db93e276..b23ac424c 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs @@ -1,12 +1,18 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Karaoke.Beatmaps; using osu.Game.Rulesets.Karaoke.Beatmaps.Metadatas; +using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Screens.Edit; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Rulesets.Karaoke.Edit.Singers { @@ -27,5 +33,52 @@ private void load() Singers.AddRange(karaokeBeatmap.SingerMetadata.Singers); } } + + public IEnumerable CreateSingerContextMenu(List lyrics) + { + return Singers.Select(singer => new OsuMenuItem(singer.Name, anySingerInLyric(singer) ? MenuItemType.Highlighted : MenuItemType.Standard, () => + { + // if only one lyric + if (allSingerInLyric(singer)) + { + lyrics.ForEach(lyric => RemoveSingerToLyric(singer, lyric)); + } + else + { + lyrics.ForEach(lyric => AddSingerToLyric(singer, lyric)); + } + })); + + bool anySingerInLyric(Singer singer) => lyrics.Any(lyric => SingerInLyric(singer, lyric)); + + bool allSingerInLyric(Singer singer) => lyrics.All(lyric => SingerInLyric(singer, lyric)); + } + + public void AddSingerToLyric(Singer singer, Lyric lyric) + { + if (SingerInLyric(singer, lyric)) + return; + + var existSingerList = lyric.Singers?.ToList() ?? new List(); + existSingerList.Add(singer.ID); + lyric.Singers = existSingerList.ToArray(); + } + + public void RemoveSingerToLyric(Singer singer, Lyric lyric) + { + if (!SingerInLyric(singer, lyric)) + return; + + lyric.Singers = lyric.Singers?.Where(x => x != singer.ID).ToArray(); + } + + public bool SingerInLyric(Singer singer, Lyric lyric) + { + // lyric belongs to default singer if no any singer in lyric. + if (lyric.Singers == null || !lyric.Singers.Any()) + return singer.ID == 0; + + return lyric.Singers?.Contains(singer.ID) ?? false; + } } } From 7340e423f1e86a1247eaeb3a3b7d8faa474391a3 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 8 Nov 2020 15:32:40 +0900 Subject: [PATCH 3/3] Handle batch change at single method. --- .../Edit/Singers/SingerManager.cs | 68 ++++++++++++++++--- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs b/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs index b23ac424c..633c16010 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Singers/SingerManager.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Karaoke.Beatmaps.Metadatas; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Screens.Edit; +using System; using System.Collections.Generic; using System.Linq; @@ -22,6 +23,9 @@ public class SingerManager : Component public readonly BindableList Singers = new BindableList(); + [Resolved(CanBeNull = true)] + private IEditorChangeHandler changeHandler { get; set; } + [Resolved] private EditorBeatmap beatmap { get; set; } @@ -54,22 +58,66 @@ public IEnumerable CreateSingerContextMenu(List lyrics) bool allSingerInLyric(Singer singer) => lyrics.All(lyric => SingerInLyric(singer, lyric)); } - public void AddSingerToLyric(Singer singer, Lyric lyric) + public void AddSingerToLyric(Singer singer, Lyric lyric) => AddSingersToLyrics(new List { singer }, new List { lyric }); + public void AddSingersToLyrics(List singers, List lyrics) { - if (SingerInLyric(singer, lyric)) - return; + if (!(singers?.Any() ?? false)) + throw new ArgumentNullException($"{nameof(singers)} cannot be numm or empty."); + + if (!(lyrics?.Any() ?? false)) + throw new ArgumentNullException($"{nameof(lyrics)} cannot be numm or empty."); + + changeHandler?.BeginChange(); + + foreach (var lyric in lyrics) + { + foreach (var singer in singers) + { + addSingerToLyric(singer, lyric); + } + } + + changeHandler?.EndChange(); - var existSingerList = lyric.Singers?.ToList() ?? new List(); - existSingerList.Add(singer.ID); - lyric.Singers = existSingerList.ToArray(); + void addSingerToLyric(Singer singer, Lyric lyric) + { + if (SingerInLyric(singer, lyric)) + return; + + var existSingerList = lyric.Singers?.ToList() ?? new List(); + existSingerList.Add(singer.ID); + lyric.Singers = existSingerList.ToArray(); + } } - public void RemoveSingerToLyric(Singer singer, Lyric lyric) + public void RemoveSingerToLyric(Singer singer, Lyric lyric) => RemoveSingersToLyrics(new List { singer }, new List { lyric }); + public void RemoveSingersToLyrics(List singers, List lyrics) { - if (!SingerInLyric(singer, lyric)) - return; + if (!(singers?.Any() ?? false)) + throw new ArgumentNullException($"{nameof(singers)} cannot be numm or empty."); - lyric.Singers = lyric.Singers?.Where(x => x != singer.ID).ToArray(); + if (!(lyrics?.Any() ?? false)) + throw new ArgumentNullException($"{nameof(lyrics)} cannot be numm or empty."); + + changeHandler?.BeginChange(); + + foreach (var lyric in lyrics) + { + foreach (var singer in singers) + { + removeSingerToLyric(singer, lyric); + } + } + + changeHandler?.EndChange(); + + void removeSingerToLyric(Singer singer, Lyric lyric) + { + if (!SingerInLyric(singer, lyric)) + return; + + lyric.Singers = lyric.Singers?.Where(x => x != singer.ID).ToArray(); + } } public bool SingerInLyric(Singer singer, Lyric lyric)