Hi there!
Our RPG features the ability to read documents within a specific interface. These documents (scrolls, books) can contain large images or texts (which I combine in the term of “Fragment”) and cover several pages which can be browsed. See attached image.
I need to build a layout system to assign fragments to pages, based on the occupied pixel height. My blocking point is that I do not know how to efficiently mesure the height of text fragments. I am breaking down the text into paragraphs, and instantitate a UserWidget with a RichTextBlock to display this paragraph text. What I need, is to compute the height of this text, based on the pixel width of the page, to determine if I have an overflow an need to proceed to the next page. I will ask my writers to write small paragraphs, so that the paging feeds a new page once a fragment does not hold inside the current page, without going into the complexity of paging line by line.
I assume I coule let UMG compute the height of the elements automatically, and then dispatch on the next page during each rendering frame, once the computation pass has been done. But I think it would be better to compute in advance the height of each fragment, to properly assign them to pages.
Can I achieve this with a TextMarshaller? I am not sure where to start and did not find proper examples on the Web.
Looking into the code, it seems SRichTextBlock uses FSlateTextBlockLayout::ComputeDesiredSize, based on the Widget constraints, but this is some internal code which cannot be called from outside, and I am not sure of timing of this call (maybe during a repaint stage?).
Thanks!
Steps to Reproduce
Hi there!
Our RPG features the ability to read documents within a specific interface. These documents (scrolls, books) can contain large images or texts (which I combine in the term of “Fragment”) and cover several pages which can be browsed. See attached image.
I need to build a layout system to assign fragments to pages, based on the occupied pixel height. My blocking point is that I do not know how to efficiently mesure the height of text fragments. I am breaking down the text into paragraphs, and instantitate a UserWidget with a RichTextBlock to display this paragraph text. What I need, is to compute the height of this text, based on the pixel width of the page, to determine if I have an overflow an need to proceed to the next page. I will ask my writers to write small paragraphs, so that the paging feeds a new page once a fragment does not hold inside the current page, without going into the complexity of paging line by line.
I assume I coule let UMG compute the height of the elements automatically, and then dispatch on the next page during each rendering frame, once the computation pass has been done. But I think it would be better to compute in advance the height of each fragment, to properly assign them to pages.
Can I achieve this with a TextMarshaller? I am not sure where to start and did not find proper examples on the Web.
Looking into the code, it seems SRichTextBlock uses FSlateTextBlockLayout::ComputeDesiredSize, based on the Widget constraints, but this is some internal code which cannot be called from outside, and I am not sure of timing of this call (maybe during a repaint stage?).
Thanks!
Hey Cody,
Thanks for input, I will try this.
Just a question though: this assumes the text block has been parented to a container with the proper width? When is this layout properly computed? As soon as I add the RichTextBlock with AddChild()? Do I need to wait one frame to repaint/layout?
Thanks!
Mathieu
Well, meanwhile I simply used a timer to let the childran stabilize their layout in a staging container of the same width, then used their actual height to do the proper paging, and it works. I understand it will be hard to anticipate the height anyway, and this practical method is sufficient for me, especially as there is no need to later Unreal’s code.
Thanks!
Hi,
You can use FSlateFontMeasure to get the measurements for a given string at a specific font/scale, but it won’t account for wrapping. You could still use this to get the max height, then multiply this by your line count to get an accurate calculation of the vertical space needed to display the whole text block.
Getting the line count is a bit trickier, I have an experimental change shelves that exposes the LineViews of the layout to SRichTextBlock which lets you get an array of strings representing each line of text post-layout. If you want to try something similar, you’ll need to expose the array of line views in SlateTextBlockLayout:
const TArray< FTextLayout::FLineView >& FSlateTextBlockLayout::GetLineViews() const
{
return TextLayout->GetLineViews();
}
Then, in SRichTextBlock you can access the line views to build the array of strings:
TArray<FString> SRichTextBlock::GetLines() const
{
TArray<FString> TextLines;
const TArray< FTextLayout::FLineView >& LineViews = TextLayoutCache->GetLineViews();
for (const FTextLayout::FLineView& LineView : LineViews)
{
FString CombinedLine = FString();
for (const TSharedRef< ILayoutBlock >& Block : LineView.Blocks)
{
FTextRange BlockRange = Block->GetTextRange();
Block->GetRun()->AppendTextTo(CombinedLine, BlockRange);
}
TextLines.Add(CombinedLine);
}
return TextLines;
}
The goal of that experiment was getting the exact contents of each line, but the size of the array should be representative of the line count in the actual widget.
Ultimately, it may be easier to just create rich text widgets for each paragraph and let the layout system flow/wrap the text, then use the desired size of the resulting widget to decide what will fit. Another idea you could explore would be using a Wrap Box and clamping the individual text blocks to a max size (using a size box), then letting the wrap box flow them automatically based on what fits. You could then put that entire thing into a scroll box so that other pages are clipped, and your page buttons could “scroll” the box over to the next page.
Best,
Cody
Hi,
That’s right, it’ll need to be in some sort of container that limits the width to your page width (be that the page itself or a similarly sized box), and may need two frames to fully stabilize since word wrap tends to take an extra frame once the width is known.
Ah perfect, I’m hoping we can expose more of the metrics at some point to give a bit more control over the end result of wrapped text but tricking the layout system into doing the work for you and measuring the result is a good low-impact workaround. Let me know if you have any issues and we’ll have a look.
Best,
Cody