Rich text style "inheritance"

We should be able to define styles in a rich text stylesheet in a way that lets us specify only certain properties to override from the default. For instance, if I create a Default style that uses Roboto at size 24 with a particular shadow offset, etc., I would like to be able to create, say, a “Bold” style that “inherits” everything from Default and only overrides the Typeface to specify Bold. As it is now, as far as I can tell, if I want to do that, I have to duplicate my Default style and make my changes whenever I want another style that accomplishes this outcome. The obvious drawback to this is that if I change my mind about the style details that I want to use by default, I have to go through all my styles and manually make those changes one at a time.

Additionally, it would be nice to have nested tags, so that I could have <bold>some bold text here with <italic>further emphasis</> thrown into the middle</>, and not have to worry about creating all the possible style overlaps that I might need.

Agreed, coming from a web gaming background widgets and slate are painfully underdeveloped.

The closest solution I can find is to write a JS function that creates each combination of the desired tags for each supported font size and serializes them to a JSON file. That JSON can then be imported into an unreal rich text data table.

Doing this by hand is an extreme waste of resources.

Take the following example:

  • Font Sizes: 10, 16, 24
  • Colors: Silver, Gray, Amber, Red, Purple
  • Decorators: Bold, Underline, Italic, Subscript, Superscript

13 tags with a (hopefully maximum) subset of 6 in use at a time results in 1716 feasible combinations. Even limiting the subset to only 4 items (size, color, & 2 additional decorations) still comes out to 715 combinations that would need to be manually entered.

It is possible using C++, you need to:

  • extend FRichTextDecorator
  • override Supports & CreateDecoratorText
  • extend URichTextBlockDecorator
  • override CreateDecorator
  • Add your created class to Decorator Classes property of the RichText

Example for coloring text green with tag <profit>200€</>.


// header.h
#pragma once

#include "CoreMinimal.h"
#include "Components/RichTextBlockDecorator.h"
#include "ProfitMoneyAmountRichTextDecorator.generated.h"

UCLASS()
class DUKES_API UProfitMoneyAmountRichTextDecorator : public URichTextBlockDecorator
{
	GENERATED_BODY()

public:
	UProfitMoneyAmountRichTextDecorator(const FObjectInitializer& ObjectInitializer);

	virtual TSharedPtr<ITextDecorator> CreateDecorator(URichTextBlock* InOwner) override;

};

// content.cpp
#include "ProfitMoneyAmountRichTextDecorator.h"

class FProfitMoneyAmountRichTextDecorator : public FRichTextDecorator
{
private:
	UProfitMoneyAmountRichTextDecorator* Decorator;
	
public:
	FProfitMoneyAmountRichTextDecorator(URichTextBlock* InOwner, UProfitMoneyAmountRichTextDecorator* InDecorator)
		: FRichTextDecorator(InOwner)
		, Decorator(InDecorator)
	{
	}

	virtual bool Supports(const FTextRunParseResults& RunInfo, const FString& Text) const override
	{
		if (RunInfo.Name == TEXT("profit"))
		{
			return true;
		}
		return false;
	}

	virtual void CreateDecoratorText(const FTextRunInfo& RunInfo, FTextBlockStyle& InOutTextStyle, FString& InOutString) const override
	{
		InOutTextStyle.SetColorAndOpacity(FSlateColor(FLinearColor(0.0f, 0.7f, 0.0f)));
		InOutString += *RunInfo.Content.ToString();
	}
};

UProfitMoneyAmountRichTextDecorator::UProfitMoneyAmountRichTextDecorator(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}

TSharedPtr<ITextDecorator> UProfitMoneyAmountRichTextDecorator::CreateDecorator(URichTextBlock* InOwner)
{
	return MakeShareable(new FProfitMoneyAmountRichTextDecorator(InOwner, this));
}


It probably makes sense to only create one of these, then you could even build your own “class” system using tag metadata.
For reference see: Unreals Example FRichInlineImage class

More info here: https://www.unrealengine.com/de/tech-blog/advanced-text-styling-with-rich-text-block