diff --git a/osu.Framework.Font.Tests/Visual/Sprites/TestSceneKaraokeSpriteText.cs b/osu.Framework.Font.Tests/Visual/Sprites/TestSceneKaraokeSpriteText.cs index a361c02..afb66d8 100644 --- a/osu.Framework.Font.Tests/Visual/Sprites/TestSceneKaraokeSpriteText.cs +++ b/osu.Framework.Font.Tests/Visual/Sprites/TestSceneKaraokeSpriteText.cs @@ -2,14 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using NUnit.Framework; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; +using osu.Framework.Timing; using osuTK.Graphics; namespace osu.Framework.Font.Tests.Visual.Sprites { public class TestSceneKaraokeSpriteText : TestScene { + private readonly ManualClock manualClock = new(); private readonly KaraokeSpriteText karaokeSpriteText; public TestSceneKaraokeSpriteText() @@ -50,11 +53,25 @@ public TestSceneKaraokeSpriteText() LeftTextColour = Color4.Green, RightTextColour = Color4.Red, }; + } + + [TestCase(false)] + [TestCase(true)] + public void TestKaraokeSpriteTextTimeTag(bool manualTime) + { + if (manualTime) + { + AddSliderStep("Here can adjust time", 0, 3000, 1000, time => + { + manualClock.CurrentTime = time; + }); + } - AddLabel("Test time tag"); AddStep("Default time tag", () => { - var startTime = Time.Current; + var startTime = getStartTime(); + + karaokeSpriteText.Clock = manualTime ? new FramedClock(manualClock) : Clock; karaokeSpriteText.TimeTags = new Dictionary { { new TextIndex(0), startTime + 500 }, @@ -66,7 +83,9 @@ public TestSceneKaraokeSpriteText() }); AddStep("Time tag with end state", () => { - var startTime = Time.Current; + var startTime = getStartTime(); + + karaokeSpriteText.Clock = manualTime ? new FramedClock(manualClock) : Clock; karaokeSpriteText.TimeTags = new Dictionary { // カ @@ -88,7 +107,9 @@ public TestSceneKaraokeSpriteText() }); AddStep("Time tag with wrong order", () => { - var startTime = Time.Current; + var startTime = getStartTime(); + + karaokeSpriteText.Clock = manualTime ? new FramedClock(manualClock) : Clock; karaokeSpriteText.TimeTags = new Dictionary { { new TextIndex(4), startTime + 2000 }, @@ -100,7 +121,9 @@ public TestSceneKaraokeSpriteText() }); AddStep("Time tag with out of range", () => { - var startTime = Time.Current; + var startTime = getStartTime(); + + karaokeSpriteText.Clock = manualTime ? new FramedClock(manualClock) : Clock; karaokeSpriteText.TimeTags = new Dictionary { { new TextIndex(-1), startTime + 0 }, @@ -112,6 +135,26 @@ public TestSceneKaraokeSpriteText() { new TextIndex(8), startTime + 2500 }, }; }); + + AddStep("Only one time-tag", () => + { + var startTime = getStartTime(); + + karaokeSpriteText.Clock = manualTime ? new FramedClock(manualClock) : Clock; + karaokeSpriteText.TimeTags = new Dictionary + { + { new TextIndex(0), startTime + 500 }, + }; + }); + + AddStep("None time-tag", () => + { + karaokeSpriteText.Clock = manualTime ? new FramedClock(manualClock) : Clock; + karaokeSpriteText.TimeTags = null; + }); + + double getStartTime() + => manualTime ? 0 : Clock.CurrentTime; } } } diff --git a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs index 9500650..60025dc 100644 --- a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs +++ b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs @@ -383,34 +383,45 @@ protected override bool OnInvalidate(Invalidation invalidation, InvalidationSour if (!invalidation.HasFlag(Invalidation.Presence) || !hasTimeTag || !hasText) return result; + // set initial width. + frontLyricTextContainer.Width = 0; + backLyricTextContainer.Width = DrawWidth; + // reset masking transform. frontLyricTextContainer.ClearTransforms(); backLyricTextContainer.ClearTransforms(); - // process time-tag should in the text-range + // filter valid time-tag with order. var characters = frontLyricText.Characters; var validTimeTag = TimeTags .Where(x => x.Key.Index >= 0 && x.Key.Index < Text.Length) - .OrderBy(x => x.Value); + .OrderBy(x => x.Value).ToArray(); + + // get first time-tag relative start time. + var relativeTime = validTimeTag.FirstOrDefault().Value - Time.Current; - var startTime = validTimeTag.FirstOrDefault().Value; + // should use absolute time to process time-tags. + using (frontLyricTextContainer.BeginAbsoluteSequence(Time.Current)) + using (frontLyricTextContainer.BeginAbsoluteSequence(Time.Current)) + { + // get transform sequence and set initial delay time. + var frontTransformSequence = frontLyricTextContainer.Delay(relativeTime).Then(); + var backTransformSequence = backLyricTextContainer.Delay(relativeTime).Then(); - // get transform sequence and set initial delay time. - var frontTransformSequence = frontLyricTextContainer.Delay(startTime - Time.Current).Then(); - var backTransformSequence = backLyricTextContainer.Delay(startTime - Time.Current).Then(); + foreach (var (textIndex, time) in validTimeTag) + { + // calculate position and duration relative to precious time-tag time. + var characterRectangle = characters[textIndex.Index].DrawRectangle; + var position = textIndex.State == TextIndex.IndexState.Start ? characterRectangle.Left : characterRectangle.Right; + var duration = Math.Max(time - relativeTime, 0); - var previousTime = startTime; + // apply the position with delay time. + frontTransformSequence.ResizeWidthTo(position, duration).Then(); + backTransformSequence.ResizeWidthTo(DrawWidth - position, duration).Then(); - foreach (var (textIndex, time) in validTimeTag) - { - // text-index should be in the range. - var characterRectangle = characters[textIndex.Index].DrawRectangle; - var position = textIndex.State == TextIndex.IndexState.Start ? characterRectangle.Left : characterRectangle.Right; - var duration = Math.Max(time - previousTime, 0); - frontTransformSequence.ResizeWidthTo(position, duration).Then(); - backTransformSequence.ResizeWidthTo(DrawWidth - position, duration).Then(); - - previousTime = time; + // save current time-tag time for letting next time-tag able to calculate duration. + relativeTime = time; + } } return true;