diff --git a/osu.Framework.Font.Tests/Visual/Sprites/TestSceneLyricSpriteTextCharacterPosition.cs b/osu.Framework.Font.Tests/Visual/Sprites/TestSceneLyricSpriteTextCharacterPosition.cs index 7820ed6..7815e5f 100644 --- a/osu.Framework.Font.Tests/Visual/Sprites/TestSceneLyricSpriteTextCharacterPosition.cs +++ b/osu.Framework.Font.Tests/Visual/Sprites/TestSceneLyricSpriteTextCharacterPosition.cs @@ -93,11 +93,11 @@ public TestSceneLyricSpriteTextCharacterPosition() [TestCase(6, TextIndex.IndexState.End, true)] [TestCase(-1, TextIndex.IndexState.End, false)] [TestCase(7, TextIndex.IndexState.Start, false)] - public void TestGetTextIndexPosition(int index, TextIndex.IndexState state, bool valid) + public void TestGetTextIndexXPosition(int index, TextIndex.IndexState state, bool valid) { prepareTestCase(() => { - var position = lyricSpriteText.GetTextIndexPosition(new TextIndex(index, state)); + var position = lyricSpriteText.GetTextIndexXPosition(new TextIndex(index, state)); return new RectangleF(position, 20, 1, 64); }, valid); } @@ -106,9 +106,9 @@ public void TestGetTextIndexPosition(int index, TextIndex.IndexState state, bool [TestCase(6, true)] [TestCase(-1, false)] [TestCase(7, false)] - public void TestGetCharacterRectangle(int index, bool valid) + public void TestGetCharacterDrawRectangle(int index, bool valid) { - prepareTestCase(() => lyricSpriteText.GetCharacterRectangle(index), valid); + prepareTestCase(() => lyricSpriteText.GetCharacterDrawRectangle(index), valid); } [TestCase("[0,1]:か", true)] @@ -119,9 +119,9 @@ public void TestGetCharacterRectangle(int index, bool valid) [TestCase("[-1,1]:か", false)] [TestCase("[0,2]:か", false)] [TestCase("[0,1]:?", false)] - public void TestGetRubyTagPosition(string rubyTag, bool valid) + public void TestGetRubyTagDrawRectangle(string rubyTag, bool valid) { - prepareTestCase(() => lyricSpriteText.GetRubyTagPosition(TestCaseTagHelper.ParsePositionText(rubyTag)), valid); + prepareTestCase(() => lyricSpriteText.GetRubyTagDrawRectangle(TestCaseTagHelper.ParsePositionText(rubyTag)), valid); } [TestCase("[0,1]:ka", true)] @@ -132,9 +132,9 @@ public void TestGetRubyTagPosition(string rubyTag, bool valid) [TestCase("[-1,1]:ka", false)] [TestCase("[0,2]:ka", false)] [TestCase("[0,1]:?", false)] - public void TestGetRomajiTagPosition(string rubyTag, bool valid) + public void TestGetRomajiTagDrawRectangle(string rubyTag, bool valid) { - prepareTestCase(() => lyricSpriteText.GetRomajiTagPosition(TestCaseTagHelper.ParsePositionText(rubyTag)), valid); + prepareTestCase(() => lyricSpriteText.GetRomajiTagDrawRectangle(TestCaseTagHelper.ParsePositionText(rubyTag)), valid); } private void prepareTestCase(Func func, bool valid) diff --git a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs index 4d091d0..1e674fd 100644 --- a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs +++ b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText.cs @@ -67,7 +67,7 @@ private void load(ShaderManager shaderManager) RoundedTextureShader = shaderManager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); } - #region frame buffer + #region Frame buffer public DrawColourInfo? FrameBufferDrawColour => base.DrawColourInfo; @@ -168,7 +168,7 @@ public IReadOnlyList RightLyricTextShaders #endregion - #region text + #region Text public string Text { @@ -208,7 +208,7 @@ public IReadOnlyList Romajies #endregion - #region font + #region Font public FontUsage Font { @@ -248,7 +248,7 @@ public FontUsage RomajiFont #endregion - #region style + #region Style public ColourInfo LeftTextColour { @@ -298,7 +298,7 @@ public LyricTextAlignment RomajiAlignment #endregion - #region text spacing + #region Text spacing public Vector2 Spacing { @@ -338,7 +338,7 @@ public Vector2 RomajiSpacing #endregion - #region margin/padding + #region Margin/padding public int RubyMargin { diff --git a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_Transform.cs b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_Transform.cs index 6c2f910..b2c1d98 100644 --- a/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_Transform.cs +++ b/osu.Framework.Font/Graphics/Sprites/KaraokeSpriteText_Transform.cs @@ -179,8 +179,8 @@ IEnumerable> getInterpolatedTimeTagBetweenTwoTim private float getTextIndexPosition(TextIndex index) { - var leftTextIndexPosition = leftLyricText.GetTextIndexPosition(index); - var rightTextIndexPosition = rightLyricText.GetTextIndexPosition(index); + var leftTextIndexPosition = leftLyricText.GetTextIndexXPosition(index); + var rightTextIndexPosition = rightLyricText.GetTextIndexXPosition(index); return index.State == TextIndex.IndexState.Start ? Math.Min(leftTextIndexPosition, rightTextIndexPosition) : Math.Max(leftTextIndexPosition, rightTextIndexPosition); diff --git a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs index 4f2b995..a7f8c04 100644 --- a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs +++ b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText.cs @@ -11,8 +11,6 @@ using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.UserInterface; using osu.Framework.IO.Stores; -using osu.Framework.Layout; -using osu.Framework.Text; using osu.Framework.Utils; using osuTK; using osuTK.Graphics; @@ -60,7 +58,7 @@ private void load(ShaderManager shaderManager) } } - #region frame buffer + #region Frame buffer public DrawColourInfo? FrameBufferDrawColour => new DrawColourInfo(Color4.White); @@ -126,7 +124,7 @@ public IReadOnlyList Shaders #endregion - #region text + #region Text private string text = string.Empty; @@ -203,7 +201,7 @@ public IReadOnlyList Romajies #endregion - #region font + #region Font private FontUsage font = FontUsage.Default; @@ -255,7 +253,7 @@ public FontUsage RomajiFont #endregion - #region style + #region Style private bool allowMultiline = true; @@ -382,7 +380,7 @@ public string EllipsisString #endregion - #region size + #region Size private bool requiresAutoSizedWidth => explicitWidth == null && (RelativeSizeAxes & Axes.X) == 0; @@ -479,7 +477,7 @@ public override Vector2 Size #endregion - #region text spacing + #region Text spacing private Vector2 spacing; @@ -540,7 +538,7 @@ public Vector2 RomajiSpacing #endregion - #region margin/padding + #region Margin/padding private MarginPadding padding; @@ -661,95 +659,8 @@ private void invalidate(bool characters = false, bool textBuilder = false) #endregion - /// - /// The characters that should be excluded from fixed-width application. Defaults to (".", ",", ":", " ") if null. - /// - protected virtual char[] FixedWidthExcludeCharacters => null; - - /// - /// The character to use to calculate the fixed width width. Defaults to 'm'. - /// - protected virtual char FixedWidthReferenceCharacter => 'm'; - - /// - /// The character to fallback to use if a character glyph lookup failed. - /// - protected virtual char FallbackCharacter => '?'; - - private readonly LayoutValue textBuilderCache = new LayoutValue(Invalidation.DrawSize, InvalidationSource.Parent); - - /// - /// Invalidates the current , causing a new one to be created next time it's required via . - /// - protected void InvalidateTextBuilder() => textBuilderCache.Invalidate(); - - /// - /// Creates a to generate the character layout for this . - /// - /// The where characters should be retrieved from. - /// The . - protected virtual TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) - { - var excludeCharacters = FixedWidthExcludeCharacters ?? default_never_fixed_width_characters; - - var rubyHeight = ReserveRubyHeight || Rubies.Any() ? RubyFont.Size : 0; - var romajiHeight = ReserveRomajiHeight || Romajies.Any() ? RomajiFont.Size : 0; - var startOffset = new Vector2(Padding.Left, Padding.Top + rubyHeight); - var spacing = Spacing + new Vector2(0, rubyHeight + romajiHeight); - - float builderMaxWidth = requiresAutoSizedWidth - ? MaxWidth - : ApplyRelativeAxes(RelativeSizeAxes, new Vector2(Math.Min(MaxWidth, base.Width), base.Height), FillMode).X - Padding.Right; - - if (AllowMultiline) - { - return new MultilineTextBuilder(store, Font, builderMaxWidth, UseFullGlyphHeight, startOffset, spacing, charactersBacking, - excludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter); - } - - if (Truncate) - { - return new TruncatingTextBuilder(store, Font, builderMaxWidth, ellipsisString, UseFullGlyphHeight, startOffset, spacing, charactersBacking, - excludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter); - } - - return new TextBuilder(store, Font, builderMaxWidth, UseFullGlyphHeight, startOffset, spacing, charactersBacking, - excludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter); - } - - protected virtual PositionTextBuilder CreateRubyTextBuilder(ITexturedGlyphLookupStore store) - { - const int builder_max_width = int.MaxValue; - return new PositionTextBuilder(store, RubyFont, builder_max_width, UseFullGlyphHeight, - new Vector2(0, -rubyMargin), rubySpacing, charactersBacking, FixedWidthExcludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter, RelativePosition.Top, rubyAlignment); - } - - protected virtual PositionTextBuilder CreateRomajiTextBuilder(ITexturedGlyphLookupStore store) - { - const int builder_max_width = int.MaxValue; - return new PositionTextBuilder(store, RomajiFont, builder_max_width, UseFullGlyphHeight, - new Vector2(0, romajiMargin), romajiSpacing, charactersBacking, FixedWidthExcludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter, RelativePosition.Bottom, romajiAlignment); - } - - private TextBuilder getTextBuilder() - { - if (!textBuilderCache.IsValid) - textBuilderCache.Value = CreateTextBuilder(store); - - return textBuilderCache.Value; - } - public override string ToString() => $@"""{displayedText}"" " + base.ToString(); - public float LineBaseHeight - { - get - { - computeCharacters(); - return textBuilderCache.Value.LineBaseHeight; - } - } - public IEnumerable FilterTerms => displayedText.Yield(); } } diff --git a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_Characters.cs b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_Characters.cs index 5d0c70f..baeb234 100644 --- a/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_Characters.cs +++ b/osu.Framework.Font/Graphics/Sprites/LyricSpriteText_Characters.cs @@ -15,6 +15,99 @@ namespace osu.Framework.Graphics.Sprites { public partial class LyricSpriteText { + #region Text builder + + /// + /// The characters that should be excluded from fixed-width application. Defaults to (".", ",", ":", " ") if null. + /// + protected virtual char[] FixedWidthExcludeCharacters => null; + + /// + /// The character to use to calculate the fixed width width. Defaults to 'm'. + /// + protected virtual char FixedWidthReferenceCharacter => 'm'; + + /// + /// The character to fallback to use if a character glyph lookup failed. + /// + protected virtual char FallbackCharacter => '?'; + + private readonly LayoutValue textBuilderCache = new LayoutValue(Invalidation.DrawSize, InvalidationSource.Parent); + + /// + /// Invalidates the current , causing a new one to be created next time it's required via . + /// + protected void InvalidateTextBuilder() => textBuilderCache.Invalidate(); + + /// + /// Creates a to generate the character layout for this . + /// + /// The where characters should be retrieved from. + /// The . + protected virtual TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) + { + var excludeCharacters = FixedWidthExcludeCharacters ?? default_never_fixed_width_characters; + + var rubyHeight = ReserveRubyHeight || Rubies.Any() ? RubyFont.Size : 0; + var romajiHeight = ReserveRomajiHeight || Romajies.Any() ? RomajiFont.Size : 0; + var startOffset = new Vector2(Padding.Left, Padding.Top + rubyHeight); + var spacing = Spacing + new Vector2(0, rubyHeight + romajiHeight); + + float builderMaxWidth = requiresAutoSizedWidth + ? MaxWidth + : ApplyRelativeAxes(RelativeSizeAxes, new Vector2(Math.Min(MaxWidth, base.Width), base.Height), FillMode).X - Padding.Right; + + if (AllowMultiline) + { + return new MultilineTextBuilder(store, Font, builderMaxWidth, UseFullGlyphHeight, startOffset, spacing, charactersBacking, + excludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter); + } + + if (Truncate) + { + return new TruncatingTextBuilder(store, Font, builderMaxWidth, ellipsisString, UseFullGlyphHeight, startOffset, spacing, charactersBacking, + excludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter); + } + + return new TextBuilder(store, Font, builderMaxWidth, UseFullGlyphHeight, startOffset, spacing, charactersBacking, + excludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter); + } + + protected virtual PositionTextBuilder CreateRubyTextBuilder(ITexturedGlyphLookupStore store) + { + const int builder_max_width = int.MaxValue; + return new PositionTextBuilder(store, RubyFont, builder_max_width, UseFullGlyphHeight, + new Vector2(0, -rubyMargin), rubySpacing, charactersBacking, FixedWidthExcludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter, RelativePosition.Top, rubyAlignment); + } + + protected virtual PositionTextBuilder CreateRomajiTextBuilder(ITexturedGlyphLookupStore store) + { + const int builder_max_width = int.MaxValue; + return new PositionTextBuilder(store, RomajiFont, builder_max_width, UseFullGlyphHeight, + new Vector2(0, romajiMargin), romajiSpacing, charactersBacking, FixedWidthExcludeCharacters, FallbackCharacter, FixedWidthReferenceCharacter, RelativePosition.Bottom, romajiAlignment); + } + + private TextBuilder getTextBuilder() + { + if (!textBuilderCache.IsValid) + textBuilderCache.Value = CreateTextBuilder(store); + + return textBuilderCache.Value; + } + + public float LineBaseHeight + { + get + { + computeCharacters(); + return textBuilderCache.Value.LineBaseHeight; + } + } + + #endregion + + #region Characters + private readonly LayoutValue charactersCache = new LayoutValue(Invalidation.DrawSize | Invalidation.Presence, InvalidationSource.Parent); /// @@ -67,8 +160,8 @@ private void computeCharacters() textBuilder.AddText(displayedText); textBounds = textBuilder.Bounds; - var fixedRubies = getFixedPositionText(rubies, displayedText); - var fixedRomajies = getFixedPositionText(romajies, displayedText); + var fixedRubies = getFixedPositionTexts(rubies, displayedText); + var fixedRomajies = getFixedPositionTexts(romajies, displayedText); if (fixedRubies.Any()) { @@ -98,7 +191,7 @@ private void computeCharacters() charactersCache.Validate(); } - static List getFixedPositionText(IEnumerable positionTexts, string lyricText) + static List getFixedPositionTexts(IEnumerable positionTexts, string lyricText) => positionTexts .Where(x => !string.IsNullOrEmpty(x.Text)) .Select(x => GetFixedPositionText(x, lyricText)) @@ -112,6 +205,10 @@ internal static PositionText GetFixedPositionText(PositionText positionText, str return new PositionText(positionText.Text, Math.Min(startIndex, endIndex), Math.Max(startIndex, endIndex)); } + #endregion + + #region Screen space characters + private readonly LayoutValue parentScreenSpaceCache = new LayoutValue(Invalidation.DrawSize | Invalidation.Presence | Invalidation.DrawInfo, InvalidationSource.Parent); private readonly LayoutValue localScreenSpaceCache = new LayoutValue(Invalidation.MiscGeometry, InvalidationSource.Self); @@ -157,13 +254,17 @@ private void computeScreenSpaceCharacters() localScreenSpaceCache.Validate(); } - public float GetTextIndexPosition(TextIndex index) + #endregion + + #region Character position + + public float GetTextIndexXPosition(TextIndex index) { - var computedRectangle = GetCharacterRectangle(index.Index); + var computedRectangle = GetCharacterDrawRectangle(index.Index); return index.State == TextIndex.IndexState.Start ? computedRectangle.Left : computedRectangle.Right; } - public RectangleF GetCharacterRectangle(int index, bool drawSizeOnly = false) + public RectangleF GetCharacterDrawRectangle(int index, bool drawSizeOnly = false) { int charIndex = Math.Clamp(index, 0, Text.Length - 1); if (charIndex != index) @@ -174,7 +275,7 @@ public RectangleF GetCharacterRectangle(int index, bool drawSizeOnly = false) return getComputeCharacterDrawRectangle(drawRectangle); } - public RectangleF GetRubyTagPosition(PositionText rubyTag, bool drawSizeOnly = false) + public RectangleF GetRubyTagDrawRectangle(PositionText rubyTag, bool drawSizeOnly = false) { int rubyIndex = Rubies.ToList().IndexOf(rubyTag); if (rubyIndex < 0) @@ -189,7 +290,7 @@ public RectangleF GetRubyTagPosition(PositionText rubyTag, bool drawSizeOnly = f return getComputeCharacterDrawRectangle(drawRectangle); } - public RectangleF GetRomajiTagPosition(PositionText romajiTag, bool drawSizeOnly = false) + public RectangleF GetRomajiTagDrawRectangle(PositionText romajiTag, bool drawSizeOnly = false) { int romajiIndex = Romajies.ToList().IndexOf(romajiTag); if (romajiIndex < 0) @@ -214,5 +315,7 @@ private RectangleF getComputeCharacterDrawRectangle(RectangleF originalCharacter .Select(x => x.ComputeCharacterDrawRectangle(originalCharacterDrawRectangle)) .Aggregate(originalCharacterDrawRectangle, RectangleF.Union); } + + #endregion } }