From b107e42b53224eda45afedeb02ef652cc24ddec0 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 11 Dec 2020 01:00:00 +0900 Subject: [PATCH 1/5] Implement time tag cursor and enable it to move left or right. Notice that time tag manager is not required, if not regist means lyric editor cannot view or edit time tag. --- .../Edit/Lyrics/Components/LyricControl.cs | 55 +++++++-- .../TimeTags/DrawableTimeTagCursor.cs | 44 +++++++ .../Edit/Lyrics/LyricEditor.cs | 31 +++++ .../Edit/Lyrics/LyricEditorScreen.cs | 4 + .../Edit/Lyrics/TimeTagManager.cs | 114 ++++++++++++++++++ 5 files changed, 237 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs index 70c6233d1..448c4287f 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs @@ -34,9 +34,13 @@ public LyricControl(Lyric lyric) } [BackgroundDependencyLoader] - private void load(IFrameBasedClock framedClock) + private void load(IFrameBasedClock framedClock, TimeTagManager timeTagManager) { drawableLyric.Clock = framedClock; + timeTagManager.BindableCursorPosition.BindValueChanged(e => + { + drawableLyric.UpdateTimeTagCursoe(e.NewValue); + }, true); } public class DrawableEditorLyric : DrawableLyric @@ -44,19 +48,42 @@ public class DrawableEditorLyric : DrawableLyric private const int time_tag_spacing = 4; private readonly Container timeTagContainer; + private readonly Container timeTagCursorContainer; public DrawableEditorLyric(Lyric lyric) : base(lyric) { - AddInternal(timeTagContainer = new Container + AddRangeInternal(new[] { - RelativeSizeAxes = Axes.Both + timeTagContainer = new Container + { + RelativeSizeAxes = Axes.Both + }, + timeTagCursorContainer = new Container + { + RelativeSizeAxes = Axes.Both + } }); DisplayRuby = true; DisplayRomaji = true; } + public void UpdateTimeTagCursoe(Tuple cursor) + { + timeTagCursorContainer.Clear(); + if (TimeTagsBindable.Value.Contains(cursor)) + { + var spacing = timeTagPosition(cursor); + timeTagCursorContainer.Add(new DrawableTimeTagCursor(cursor) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + X = spacing + }); + } + } + protected override void LoadComplete() { base.LoadComplete(); @@ -110,21 +137,27 @@ protected void UpdateTimeTags() foreach (var timeTag in timeTags) { - var index = Math.Min(timeTag.Item1.Index, HitObject.Text.Length - 1); - var percentage = timeTag.Item1.State == TimeTagIndex.IndexState.Start ? 0 : 1; - var position = karaokeText.GetPercentageWidth(index, index + 1, percentage); - - var duplicatedTagAmount = timeTags.SkipWhile(t => t != timeTag).Count(x => x.Item1 == timeTag.Item1) - 1; - var spacing = duplicatedTagAmount * time_tag_spacing * (timeTag.Item1.State == TimeTagIndex.IndexState.Start ? 1 : -1); - + var spacing = timeTagPosition(timeTag); timeTagContainer.Add(new DrawableTimeTag(timeTag) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - X = position + spacing + X = spacing }); } } + + private float timeTagPosition(Tuple timeTag) + { + var index = Math.Min(timeTag.Item1.Index, HitObject.Text.Length - 1); + var percentage = timeTag.Item1.State == TimeTagIndex.IndexState.Start ? 0 : 1; + var position = karaokeText.GetPercentageWidth(index, index + 1, percentage); + + var timeTags = TimeTagsBindable.Value; + var duplicatedTagAmount = timeTags.SkipWhile(t => t != timeTag).Count(x => x.Item1 == timeTag.Item1) - 1; + var spacing = duplicatedTagAmount * time_tag_spacing * (timeTag.Item1.State == TimeTagIndex.IndexState.Start ? 1 : -1); + return position + spacing; + } } } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs new file mode 100644 index 000000000..6da09d7aa --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs @@ -0,0 +1,44 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Rulesets.Karaoke.Graphics.Shapes; +using osuTK; +using System; + +namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics.Components.TimeTags +{ + public class DrawableTimeTagCursor : CompositeDrawable + { + /// + /// Height of major bar line triangles. + /// + private const float triangle_width = 4; + + private readonly Tuple timeTag; + + public DrawableTimeTagCursor(Tuple timeTag) + { + this.timeTag = timeTag; + + InternalChild = new RightTriangle + { + Name = "Time tag triangle", + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Size = new Vector2(triangle_width), + Scale = new Vector2(timeTag.Item1.State == TimeTagIndex.IndexState.Start ? 1 : -1, 1) + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChild.Colour = timeTag.Item2.HasValue ? colours.YellowDarker : colours.Gray9; + } + } +} diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditor.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditor.cs index fec5233ed..a8ea20269 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditor.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditor.cs @@ -4,10 +4,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit; using osu.Game.Skinning; +using osuTK.Input; namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics { @@ -16,6 +18,9 @@ public class LyricEditor : Container [Resolved] private EditorBeatmap beatmap { get; set; } + [Resolved(canBeNull: true)] + private TimeTagManager timeTagManager { get; set; } + private readonly KaraokeLyricEditorSkin skin; private readonly DrawableLyricEditList container; @@ -44,6 +49,32 @@ protected override void LoadComplete() beatmap.HitObjectAdded += addHitObject; beatmap.HitObjectRemoved += removeHitObject; + + timeTagManager?.MoveCursor(CursorAction.First); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (timeTagManager == null) + return false; + + switch (e.Key) + { + case Key.Up: + return timeTagManager.MoveCursor(CursorAction.MoveUp); + case Key.Down: + return timeTagManager.MoveCursor(CursorAction.MoveDown); + case Key.Left: + return timeTagManager.MoveCursor(CursorAction.MoveLeft); + case Key.Right: + return timeTagManager.MoveCursor(CursorAction.MoveRight); + case Key.PageUp: + return timeTagManager.MoveCursor(CursorAction.First); + case Key.PageDown: + return timeTagManager.MoveCursor(CursorAction.Last); + default: + return base.OnKeyDown(e); + } } private void addHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditorScreen.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditorScreen.cs index b62acd4e8..d4e86ced8 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditorScreen.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/LyricEditorScreen.cs @@ -32,9 +32,13 @@ public class LyricEditorScreen : EditorScreenWithTimeline, ICanAcceptFiles [Resolved(CanBeNull = true)] private KaraokeHitObjectComposer composer { get; set; } + [Cached] + protected readonly TimeTagManager TranslateManager; + public LyricEditorScreen() : base(EditorScreenMode.Compose) { + Content.Add(TranslateManager = new TimeTagManager()); } Task ICanAcceptFiles.Import(params string[] paths) diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs index 0679c625f..aa14d3be5 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Karaoke.Edit.Generator.TimeTags.Ja; @@ -21,6 +23,8 @@ public class TimeTagManager : Component [Resolved(CanBeNull = true)] private IEditorChangeHandler changeHandler { get; set; } + public Bindable> BindableCursorPosition { get; set; } = new Bindable>(); + /// /// Will auto-detect each 's and apply on them. /// @@ -43,6 +47,101 @@ public void AutoGenerateTimeTags() changeHandler?.EndChange(); } + public bool MoveCursor(CursorAction action) + { + var currentTimeTag = BindableCursorPosition.Value; + + Tuple nextTimeTag = null; + switch (action) + { + case CursorAction.MoveUp: + nextTimeTag = getPreviousLyricTimeTag(currentTimeTag); + break; + case CursorAction.MoveDown: + nextTimeTag = getNextLyricTimeTag(currentTimeTag); + break; + case CursorAction.MoveLeft: + nextTimeTag = getPreviousTimeTag(currentTimeTag); + break; + case CursorAction.MoveRight: + nextTimeTag = getNextTimeTag(currentTimeTag); + break; + case CursorAction.First: + nextTimeTag = getFirstTimeTag(currentTimeTag); + break; + case CursorAction.Last: + nextTimeTag = getLastTimeTag(currentTimeTag); + break; + } + + if (nextTimeTag == null) + return false; + + moveCursorTo(nextTimeTag); + return true; + } + + public bool MoveCursorToTargetPosition(Tuple timeTag) + { + if (timeTagInLyric(timeTag) == null) + return false; + + moveCursorTo(timeTag); + return true; + } + + private Lyric timeTagInLyric(Tuple timeTag) + { + if (timeTag == null) + return null; + + return beatmap.HitObjects.OfType().FirstOrDefault(x => x.TimeTags?.Contains(timeTag) ?? false); + } + + private Tuple getPreviousLyricTimeTag(Tuple timeTag) + { + // todo : need to implement + return null; + } + + public Tuple getNextLyricTimeTag(Tuple timeTag) + { + // todo : need to implement + return null; + } + + private Tuple getPreviousTimeTag(Tuple timeTag) + { + var timeTags = beatmap.HitObjects.OfType().SelectMany(x => x.TimeTags).ToArray(); + return timeTags.GetPrevious(timeTag); + } + + public Tuple getNextTimeTag(Tuple timeTag) + { + var timeTags = beatmap.HitObjects.OfType().SelectMany(x => x.TimeTags).ToArray(); + return timeTags.GetNext(timeTag); + } + + private Tuple getFirstTimeTag(Tuple timeTag) + { + var timeTags = beatmap.HitObjects.OfType().SelectMany(x => x.TimeTags).ToArray(); + return timeTags.FirstOrDefault(); + } + + public Tuple getLastTimeTag(Tuple timeTag) + { + var timeTags = beatmap.HitObjects.OfType().SelectMany(x => x.TimeTags).ToArray(); + return timeTags.LastOrDefault(); + } + + private void moveCursorTo(Tuple timeTag) + { + if (timeTag == null) + return; + + BindableCursorPosition.Value = timeTag; + } + public class TimeTagGeneratorSelector { private readonly Lazy jaTimeTagGenerator; @@ -82,4 +181,19 @@ public TimeTagGeneratorSelector() } } } + + public enum CursorAction + { + MoveUp, + + MoveDown, + + MoveLeft, + + MoveRight, + + First, + + Last, + } } From aa51afc424d064eb8480d1dbb2ced3d3cfa67b33 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 11 Dec 2020 01:15:26 +0900 Subject: [PATCH 2/5] Make import time-tag works. --- .../ImportLyric/GenerateTimeTag/GenerateTimeTagSubScreen.cs | 3 +++ .../Edit/Lyrics/Components/LyricControl.cs | 6 +++--- osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke/Edit/ImportLyric/GenerateTimeTag/GenerateTimeTagSubScreen.cs b/osu.Game.Rulesets.Karaoke/Edit/ImportLyric/GenerateTimeTag/GenerateTimeTagSubScreen.cs index fdadbabec..288e923fb 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/ImportLyric/GenerateTimeTag/GenerateTimeTagSubScreen.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/ImportLyric/GenerateTimeTag/GenerateTimeTagSubScreen.cs @@ -65,7 +65,10 @@ protected void AskForAutoGenerateTimeTag() DialogOverlay.Push(new UseAutoGenerateTimeTagPopupDialog(ok => { if (ok) + { TimeTagManager.AutoGenerateTimeTags(); + TimeTagManager.MoveCursor(CursorAction.First); + } })); } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs index 448c4287f..f2a9dc24e 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs @@ -33,13 +33,13 @@ public LyricControl(Lyric lyric) }; } - [BackgroundDependencyLoader] + [BackgroundDependencyLoader(true)] private void load(IFrameBasedClock framedClock, TimeTagManager timeTagManager) { drawableLyric.Clock = framedClock; - timeTagManager.BindableCursorPosition.BindValueChanged(e => + timeTagManager?.BindableCursorPosition.BindValueChanged(e => { - drawableLyric.UpdateTimeTagCursoe(e.NewValue); + drawableLyric?.UpdateTimeTagCursoe(e.NewValue); }, true); } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs index aa14d3be5..f3f880c68 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs @@ -15,6 +15,10 @@ namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics { + /// + /// Handle view or edit time-tag in lyrics. + /// Notice that is not strictly needed, just not showing time-tag if not regist this manager. + /// public class TimeTagManager : Component { [Resolved] From c65c89ca1f6e0c8c4088ea7c9ea0e8582b9ef7ed Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 11 Dec 2020 02:08:55 +0900 Subject: [PATCH 3/5] Adjust color and style. --- .../Edit/Lyrics/Components/LyricControl.cs | 7 ++++--- .../Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs index e1325d8aa..80be160ea 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/LyricControl.cs @@ -150,12 +150,13 @@ protected void UpdateTimeTags() private float timeTagPosition(TimeTag timeTag) { var index = Math.Min(timeTag.Index.Index, HitObject.Text.Length - 1); - var percentage = timeTag.Index.State == TimeTagIndex.IndexState.Start ? 0 : 1; + var isStart = timeTag.Index.State == TimeTagIndex.IndexState.Start; + var percentage = isStart ? 0 : 1; var position = karaokeText.GetPercentageWidth(index, index + 1, percentage); - var timeTags = TimeTagsBindable.Value; + var timeTags = isStart ? TimeTagsBindable.Value.Reverse() : TimeTagsBindable.Value; var duplicatedTagAmount = timeTags.SkipWhile(t => t != timeTag).Count(x => x.Index == timeTag.Index) - 1; - var spacing = duplicatedTagAmount * time_tag_spacing * (timeTag.Index.State == TimeTagIndex.IndexState.Start ? 1 : -1); + var spacing = duplicatedTagAmount * time_tag_spacing * (isStart ? 1 : -1); return position + spacing; } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs index 168b667c0..ba7926838 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTagCursor.cs @@ -39,7 +39,7 @@ public DrawableTimeTagCursor(TimeTag timeTag) [BackgroundDependencyLoader] private void load(OsuColour colours) { - InternalChild.Colour = timeTag.Time.HasValue ? colours.YellowDarker : colours.Gray9; + InternalChild.Colour = timeTag.Time.HasValue ? colours.YellowDarker : colours.Gray3; } } } From eedcba155b3d1b6e9e326193b1d848872ef013bf Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 11 Dec 2020 22:37:42 +0900 Subject: [PATCH 4/5] Now lyric cursor can move up and down. --- .../Edit/Lyrics/TimeTagManager.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs index 4464a0c6a..90d8c7eb0 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs @@ -104,14 +104,16 @@ private Lyric timeTagInLyric(TimeTag timeTag) private TimeTag getPreviousLyricTimeTag(TimeTag timeTag) { - // todo : need to implement - return null; + var lyrics = beatmap.HitObjects.OfType().ToList(); + var currentLyric = timeTagInLyric(timeTag); + return lyrics.GetPrevious(currentLyric)?.TimeTags?.FirstOrDefault(x => x.Index >= timeTag.Index); } public TimeTag getNextLyricTimeTag(TimeTag timeTag) { - // todo : need to implement - return null; + var lyrics = beatmap.HitObjects.OfType().ToList(); + var currentLyric = timeTagInLyric(timeTag); + return lyrics.GetNext(currentLyric)?.TimeTags?.FirstOrDefault(x => x.Index >= timeTag.Index); } private TimeTag getPreviousTimeTag(TimeTag timeTag) From 9ed54147980ad352caf162499228a16af9dce2d8 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Fri, 11 Dec 2020 22:51:29 +0900 Subject: [PATCH 5/5] Adding triggner to target position if click the timetag. --- .../Edit/Lyrics/Components/TimeTags/DrawableTimeTag.cs | 9 +++++++++ osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTag.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTag.cs index c1beaeacf..f7b8292da 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTag.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Components/TimeTags/DrawableTimeTag.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Karaoke.Graphics.Shapes; using osu.Game.Rulesets.Karaoke.Objects; @@ -22,6 +23,9 @@ public class DrawableTimeTag : CompositeDrawable private readonly TimeTag timeTag; + [Resolved(canBeNull: true)] + private TimeTagManager timeTagManager { get; set; } + public DrawableTimeTag(TimeTag timeTag) { this.timeTag = timeTag; @@ -36,6 +40,11 @@ public DrawableTimeTag(TimeTag timeTag) }; } + protected override bool OnClick(ClickEvent e) + { + return timeTagManager?.MoveCursorToTargetPosition(timeTag) ?? false; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs index 90d8c7eb0..e5c105dfd 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/TimeTagManager.cs @@ -5,7 +5,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Karaoke.Edit.Generator.TimeTags.Ja; using osu.Game.Rulesets.Karaoke.Edit.Generator.TimeTags.Zh; using osu.Game.Rulesets.Karaoke.Objects;