Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
private TextFlowContainer textContainer = null!;

[SetUp]
public void Setup() => Schedule(() =>
public void Setup() => Schedule(() => createContainer(default_text));

private void createContainer(string text = default_text)
{
Child = topLevelContainer = new Container
{
Expand All @@ -43,11 +45,11 @@
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Text = default_text
Text = text
}
}
};
});
}

[TestCase(Anchor.TopLeft)]
[TestCase(Anchor.TopCentre)]
Expand Down Expand Up @@ -123,6 +125,53 @@
AddStep("set cjk text", () => textContainer.Text = "日本の桜は世界中から観光客を引きつけています。寿司は美味しい伝統的な日本食です。東京タワーは景色が美しいです。速い新幹線は、便利な交通手段です。富士山は、その美しさと完全な形状で知られています。日本文化は、優雅さと繊細さを象徴しています。抹茶は特別な日本の茶です。着物は、伝統的な日本の衣装で、特別な場面でよく着用されます。");
}

[Test]
public void TestOverflowCharacterSplitting()
{
string overflowText = "LoremipsumdolorsitametconsecteturadipiscingelitIntegermattiseuturpisvitaeposuereOrcivariusnatoquepenatibusetmagnisdisparturientmontesnasceturridiculusmusEtiammaurisnibhfaucibusmaximusornareeuultricesutipsumProinrhoncusnuncetfaucibuspretiumnislnuncdapibusmassaetscelerisquenibhligulaidodioPraesentdapibusexsednuncegestasinplaceratrisusmattisNullasedligulavelitVestibulumauctorportaerosetcondimentumEtiamlaoreetnuncneclaciniapulvinarMaurishendreritmiataliquetcondimentumexexcursusdolornonportaeraterosidjustoCrasmalesuadatinciduntnuncattinciduntrisuseleifendidMaecenashendreritvenenatismietlobortisEtiamsemtortorelementumegetlacusnonportatristiquequamMorbisedlaciniaodioPhasellusutpretiumnuncFuscevitaemollismagnavelscelerisquedui";

AddStep("set text to overflow on creation", () =>
{
createContainer(overflowText);
});
assertSpriteTextCount(overflowText.Length);

AddStep("set overflow text post-creation", () => textContainer.Text = overflowText);
assertSpriteTextCount(overflowText.Length);

AddStep("set overflow text mid-sentence", () =>
{
textContainer.Text = "start/" + overflowText + "/end";
});
assertSpriteTextCount(overflowText.Length + 3);

AddStep("set relative width", () =>
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
{
topLevelContainer.AutoSizeAxes = textContainer.AutoSizeAxes = Axes.Y;
topLevelContainer.RelativeSizeAxes = textContainer.RelativeSizeAxes = Axes.X;
topLevelContainer.Width = textContainer.Width = 0.5f;
textContainer.Text = overflowText;
});
assertSpriteTextCount(overflowText.Length);

AddStep("set absolute width", () =>
{
topLevelContainer.AutoSizeAxes = textContainer.AutoSizeAxes = Axes.Y;
topLevelContainer.RelativeSizeAxes = textContainer.RelativeSizeAxes = Axes.None;
topLevelContainer.Width = textContainer.Width = 200f;
textContainer.Text = overflowText;
});
assertSpriteTextCount(overflowText.Length);

AddStep("set autosize width", () =>
{
topLevelContainer.RelativeSizeAxes = textContainer.RelativeSizeAxes = Axes.None;
topLevelContainer.AutoSizeAxes = textContainer.AutoSizeAxes = Axes.Both;
textContainer.Text = overflowText;
});
assertSpriteTextCount(1);
}

[Test]
public void TestSizing()
{
Expand Down
23 changes: 23 additions & 0 deletions osu.Framework.Tests/Visual/Sprites/TestSceneTextFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public TestSceneTextFlow()
textFlowContainer.AddText(" (and so are inline styles!)", t => t.Colour = Color4.Yellow);
textFlowContainer.AddParagraph("There's 2 line breaks\n\ninside this paragraph!", t => t.Colour = Color4.GreenYellow);
textFlowContainer.AddParagraph("Make\nTextFlowContainer\ngreat\nagain!", t => t.Colour = Color4.Red);
textFlowContainer.AddText("\ntext overflowwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", t => t.Colour = Color4.Blue);

paragraphContainer.Add(new TextFlowContainer
{
Expand Down Expand Up @@ -106,6 +107,28 @@ public TestSceneTextFlow()
Text = "Test icons [RedBox] interleaved\n[GreenBox] with other [0] text, also [[0]] escaping stuff is possible."
});

paragraphContainer.Add(new CustomText
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Placeholders = new Drawable[]
{
new LineBaseBox
{
Colour = Color4.Purple,
LineBaseHeight = 25f,
Size = new Vector2(25, 25)
}.WithEffect(new OutlineEffect
{
Strength = 20f,
PadExtent = true,
BlurSigma = new Vector2(5f),
Colour = Color4.White
})
},
Text = "Test icons interleaved with wrapping overflowwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww[0]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
});

paragraphContainer.Add(new Container
{
Size = new Vector2(300),
Expand Down
21 changes: 20 additions & 1 deletion osu.Framework/Graphics/Containers/TextChunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,31 @@

var textSprite = CreateSpriteText(textFlowContainer);
textSprite.Text = word;
sprites.Add(textSprite);

// Avoid splitting if we don't know what the container's size will be (this check will short circut if so)
if (textFlowContainer.Parent != null && !textFlowContainer.TextFitsInFlow(textSprite))
{
addCharacters(word);
}
else
{
sprites.Add(textSprite);
}
}

first = false;
}

void addCharacters(string text)
{
foreach (char character in text)
{
var characterSprite = CreateSpriteText(textFlowContainer);
characterSprite.Text = character.ToString();

sprites.Add(characterSprite);
}
}
return sprites;
}

Expand Down
27 changes: 26 additions & 1 deletion osu.Framework/Graphics/Containers/TextFlowContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@
base.LoadAsyncComplete();

localisationParameters.Value = Localisation.CurrentParameters.Value;
RecreateAllParts();
}

protected override void LoadComplete()
Expand All @@ -203,6 +202,7 @@

localisationParameters.BindValueChanged(_ => partsCache.Invalidate());
((IBindable<LocalisationParameters>)localisationParameters).BindTo(Localisation.CurrentParameters);
RecreateAllParts();
}

protected override void Update()
Expand Down Expand Up @@ -287,6 +287,26 @@

protected internal virtual SpriteText CreateSpriteText() => new SpriteText();

/// <summary>
/// Checks whether the width of a <see cref="SpriteText"/> would fit within the bounds of this TextFlowContainer when drawn.
/// </summary>
/// <param name="spriteText">The <see cref="SpriteText"/> to check.</param>
/// <returns>Whether the text fits within the bounds of this TextFlowContainer.</returns>
/// <exception cref="InvalidOperationException">If this container's <see cref="RelativeSizeAxes"/> == <see cref="Axes.X"/> and/or it hasn't loaded (Parent == null)</exception>
/// <remarks>The <paramref name="spriteText"/> provided will be pre-loaded by being passed into <see cref="CompositeDrawable.LoadComponent{TLoadable}(TLoadable)"/> to get its width.</remarks>
public bool TextFitsInFlow(SpriteText spriteText)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should definitely not be public unless there's a use case for it.

{
if (AutoSizeAxes.HasFlagFast(Axes.X))
return true;
if (Parent == null)
throw new InvalidOperationException($"Cannot invoke {nameof(TextFitsInFlow)} before this {nameof(TextFlowContainer)} has a parent. Consider calling after this container has loaded.");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should definitely not throw because there is no way a user can fix this.

if (Flow.LoadState < LoadState.Ready)
throw new InvalidOperationException($"Cannot invoke {nameof(TextFitsInFlow)} before this container's {nameof(InnerFlow)} is ready. Consider calling after this container has loaded.");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above


Flow.LoadSpriteTextComponent(spriteText);
return spriteText.Width <= Flow.ChildSize.X;
}

internal void ApplyDefaultCreationParameters(SpriteText spriteText) => defaultCreationParameters?.Invoke(spriteText);

public void Clear(bool disposeChildren = true)
Expand Down Expand Up @@ -347,6 +367,11 @@

protected partial class InnerFlow : FillFlowContainer
{
protected internal void LoadSpriteTextComponent(SpriteText item)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this just cannot exist. you haven't even explained what or why or a

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was included so the TextFitsInFlow method could know what a SpriteText's size would be in relation to the InnerFlow if it were added, without needing to actually add it as a child to the InnerFlow. (If a large SpriteText were unknowingly added as a child, it would be loaded only to be removed from InnerFlow soon after and have each of its characters re-created & added, leading to unwanted results like flashing.)

The tradeoffs of this may not be reasonably worth it though. I agree that its best for this not to be included.

{
LoadComponent(item);
}

private float firstLineIndent;

/// <summary>
Expand Down
Loading