3 de octubre de 2018

Advanced Text Styling with Rich Text Block

Por Cody Albert

When working with game UI, the typical UMG Text block offers a healthy variety of style options and customizations. However, many developers have found the need for more flexible text that supports markup for things like style changes, inline images, hyperlinks, and much more. Much of this has already been possible with the experimental Rich Text Block widget through code. 

For the 4.20 release, the widget was made available to UMG and in the process it’s workflow polished to be more flexible and extensible than before. As part of this polishing phase, rather than attempt to support every possible customization a developer may need, we’ve set the Rich Text Block up to accept Decorator classes, enabling you to define the markup behavior you need for your project. You’ll find that a single Decorator class has been provided as an example for setting up your own decorators with RichTextBlockImageDecorator.cpp.

In this blog post, I’ll go over both how to work with the new Rich Text Block widget in UMG as a UI designer and as a programmer. I’ll explain how you can extend it with additional functionality using Decorator classes. Before diving right into adding your own customizations, it may be helpful to familiarize yourself with the newly exposed Rich Text Block functionality in UMG. The Rich Text Block widget enables you to customize the contents of your block using text styles and decorators using Data Table Assets or your own Decorator classes. 

The Data Table Asset you create is used to store any type of data based on any user-defined struct. When you create a Data Table, you'll notice there are two provided: Text Style Row and Image Row. Text Style Row structs are part of the built-in stylings provided with Rich Text Block that enables you to define the font type, whether it is outlined, what type of color and size it should be, and much more. The Image Row is part of the provided RichTextBlockImageDecorator example class that enables you to specify any Decorator classes in the Rich Text Block widget. With it, like the Text Style Row struct, you'll be able to define properties for in-line images such as size scaling, tint, alignment, and more. 

The RichTextBlockImageDecorator class example provides you with a starting point to create your own markup text that can be replaced with whatever Slate content you want, including things such as images, hyperlinks, and even entire widgets! Data Tables can hold any type of data and be set up in the Editor through a Data Table Asset.

Getting Started

First of all, we’ll need to create a new Widget Blueprint and use the Palette to drag a Rich Text Block onto the Canvas. With the Rich Text Block selected, use the Details panel to locate the Text Styles Set. This asset assignment slot lets us pass in a data table of styles that defines both our Text Style and any number of additional styles we may wish to use. You can go ahead and create a new Data Table by selecting this assignment dropdown and selecting Data Table or by using the Content Browser and selecting Add New > Miscellaneous > Data Table. 

When you create a Data Table, you can go ahead and create it right from inside the Details panel in UMG. Choose Rich Text Style Row for the row struct from the Pick Structure window and dropdown.

Open up your new Data Table by double-clicking it in the Details panel in UMG or from the Content Browser. In the Data Table Editor, we’ll start by creating a new row name “Default.” This row represents our text in its default state and will automatically be used by the Rich Text Block for any text where it’s not been explicitly set to a different style. 
Explore the different options under your newly created Default row and set the baseline style you want for the type of font, size, typeface, and more. Once you’re happy with that, go ahead and add some additional styles by clicking the plus (+) button to add a new row and then enter a name in the Row Name text box.

Back in the Widget Blueprint with your Data Table asset assigned, go ahead and add some text to the Text section of the Rich Text Block through the Details panel.

In the UMG toolbar, you may need to click the Compile button to display text in the Rich Text Block widget.

The text here is using the Default row we set up. To apply another one of your styles created in your Data Table Asset, use the following format to apply style to the wrapped text: 

In this example, we wrapped the text we want to inherit the style in our tag that is used in our Data Table Asset for this row using Rich. In doing so, the text now inherits the properties we set, in this case, orange text with a black outline.

Keep in mind that this style tag does not require any prefix or postfix, such as RichText.* to work, and it is not case-sensitive. 

Using Decorators

At this point, we’re now able to apply different styles to text within the Rich Text Block but what if we wanted to inject something other text? We can use Decorators to do that by setting up our own markup tags. These markup tags let us use Slate to render anything we want seamlessly within our text. 

To get you started, we’ve included an example RichTextBlockImageDecorator class that can be used to add an image to a Rich Text Block using a Decorator class. Like the built-in stylings of the Rich Text Style Row, the Rich Image Row struct defines all the images we would want to support with in line text.

Now, we’ll continue by extending the provided RichTextBlockImageDecorator class so that we can point our subclass at the Rich Image Row Data Table that we just created. The simplest way to do this is to create a new Blueprint class, choose the RichTextBlockImageDecorator class that is provided as the parent class, open the Blueprint and assign the Data Table to the Image Set property of the class. 

Take note that in 4.20, the image decorator doesn’t appear in the list of parent classes, however, this will be supported in 4.21. For the time being, you can extend it as a C++ class and either assign your data table in the C++ file or add Blueprintable to the UCLASS macro at the top of the header in order to extend your subclass as a Blueprint. 

Once your Decorator is set up, add it to your Rich Text Block’s Decorator Classes array and insert images from your table using the following markup:
<img id="ImageNameFromTable"/>
You may need to hit the Compile button in the toolbar for the changes to display and take effect. 

Adding New Decorators

While we do provide the RichTextBlockImageDecorator class as an example, the best part about the Rich Text Block is that you can now define your own custom decorators that leverage the full power of Slate, enabling you to add anything you want in line with your your text. To do this, you’ll need to write two classes: a URichTextBlockDecorator and an FRichTextDecorator. Once these classes are set up, you’ll be able to add your decorator to any Rich Text Block via the DecoratorClasses array in the Rich Text Block widget in UMG. You’ll also be able to use your markup to parse text with your decorators.

URichTextBlockDecorator defines an UObject class that can expose properties to the Editor’s Details panel. At a bare minimum, you’ll need to implement CreateDecorator, which should return a SharedPtr to the FRichTextDecorator instance you’ll create to do all your heavy lifting. You’re welcome to also implement any properties and utility functions here, which will let your designers create subclasses of your Decorator as a Blueprint in order to pass in whatever data they need. You might recall that the RichTextBlockImageDecorator defines a data table property so that we can pass in our table of images. Anything you want to be modified in Blueprint should exist here on the UObject. 

FRichTextDecorator is responsible for the actual parsing/substitution of markup tags and requires two functions to be implemented. The first function, Supports, is given the contents of the markup tag via an FTextRunParseResults and simply returns true or false depending on whether the decorator is actually responsible for handling of this tag. Supports will be called for every decorator in your DecoratorClasses array before falling back to just displaying the tag as regular text if we can’t find the decorator to handle it. The second function, CreateDecoratorWidget, is called to actually construct and return the widget by replacing the supported markup tag. You’ll have access to FTextRunInfo if you need any text or metadata in the tag. 

In the case of the RichTextBlockImageDecorator class, we define an SRichInlineImage widget and populate it with the correct image from our data table that is passed to the UObject wrapper class before passing it as the return value for the CreateDecoratorWidget. 

Using Rich Text in Your Project

We’ve gone over how to use the Rich Text Block’s built-in features and how to write Decorator classes to add your own functionality. Now, how does this fit into your project’s workflow?

If you find yourself setting up the Decorator Classes array with the same set of decorators every time you make a Rich Text Block, it can be useful to your own subclass of the Rich Text widget with a default set of decorators. If you need even more control, you’ll also notice that SRich Text Block has support for custom parsers and marshallers. You can write your own parsers to change the rules on how markup tags are detected and processed, and a custom marshaller will give you control over how your text is laid out within the widget. 

While it may be tempting to start using Rich Text Blocks widgets in place of all your project’s text areas, there is a performance cost associated with parsing the text in search of tags. For anything where performance may be a concern, you’ll want to continue to use regular text blocks wherever the extra features of the Rich Text Block are not needed. 

The Rich Text Block widget opens up a plethora of possibilities for applying interesting effects to your project’s text. With it, you’ll be able to apply simple effects like text highlighting and gamepad buttons or more interactive effects like inline buttons and loot links, with decorators letting you add in anything you can imagine! 

Visit our documentation for Rich Text Block to dive in and get started using it!