Skip to content

Instantly share code, notes, and snippets.

@BaeMinCheon
Last active August 4, 2022 15:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BaeMinCheon/6521a88bce86bd4cd60b7b383cf45637 to your computer and use it in GitHub Desktop.
Save BaeMinCheon/6521a88bce86bd4cd60b7b383cf45637 to your computer and use it in GitHub Desktop.
Custom RichTextBlockDecorator example code #2
// https://baemincheon.github.io/2022/07/28/how-the-rich-text-block-works-in-unrealengine/
#include "UObject/SoftObjectPtr.h"
#include "Rendering/DrawElements.h"
#include "Framework/Text/SlateTextRun.h"
#include "Framework/Text/SlateTextLayout.h"
#include "Slate/SlateGameResources.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Framework/Application/SlateApplication.h"
#include "Fonts/FontMeasure.h"
#include "Math/UnrealMathUtility.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Layout/SScaleBox.h"
#include "Widgets/Layout/SBox.h"
#include "Misc/DefaultValueHelper.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/Package.h"
#include "RichTextBlockSomeWidgetDecorator.h"
class SRichInlineSomeWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SRichInlineSomeWidget)
{}
SLATE_END_ARGS()
public:
void Construct(const FArguments& InArgs, const FRichSomeWidgetRow* Row, const FTextBlockStyle& TextStyle, TOptional<int32> Width, TOptional<int32> Height, EStretch::Type Stretch);
};
void SRichInlineSomeWidget::Construct(const FArguments& InArgs, const FRichSomeWidgetRow* Row, const FTextBlockStyle& TextStyle, TOptional<int32> Width, TOptional<int32> Height, EStretch::Type Stretch)
{
const FSlateBrush* InBrush = &(Row->Brush);
check(InBrush)
const FText InText = Row->Text;
const TSharedRef<FSlateFontMeasure> FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
const float MaxHeight = FontMeasure->GetMaxCharacterHeight(TextStyle.Font, 1.0f);
float IconHeight = FMath::Max(MaxHeight, InBrush->ImageSize.Y);
if (Height.IsSet())
{
IconHeight = Height.GetValue();
}
float IconWidth = IconHeight;
if (Width.IsSet())
{
IconWidth = Width.GetValue();
}
ChildSlot
[
SNew(SBox)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(SImage)
.Image(InBrush)
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(InText)
]
]
];
}
class FRichInlineSomeWidget : public FRichTextDecorator
{
public:
FRichInlineSomeWidget(URichTextBlock* InOwner, URichTextBlockSomeWidgetDecorator* InDecorator)
: FRichTextDecorator(InOwner)
, Decorator(InDecorator)
{
}
virtual bool Supports(const FTextRunParseResults& RunParseResult, const FString& Text) const override;
protected:
virtual TSharedPtr<SWidget> CreateDecoratorWidget(const FTextRunInfo& RunInfo, const FTextBlockStyle& TextStyle) const override;
private:
URichTextBlockSomeWidgetDecorator* Decorator;
};
bool FRichInlineSomeWidget::Supports(const FTextRunParseResults& RunParseResult, const FString& Text) const
{
bool Result = false;
const bool IsContainId = RunParseResult.MetaData.Contains(TEXT("id"));
const bool IsNameSomeWidget = RunParseResult.Name == TEXT("somewidget");
if (IsContainId && IsNameSomeWidget)
{
const FTextRange& IdRange = RunParseResult.MetaData[TEXT("id")];
const FString TagId = Text.Mid(IdRange.BeginIndex, IdRange.EndIndex - IdRange.BeginIndex);
const bool bWarnIfMissing = false;
Result = Decorator->FindSomeWidgetRow(*TagId, bWarnIfMissing) != nullptr;
}
return Result;
}
TSharedPtr<SWidget> FRichInlineSomeWidget::CreateDecoratorWidget(const FTextRunInfo& RunInfo, const FTextBlockStyle& TextStyle) const
{
TSharedPtr<SWidget> Result;
const bool bWarnIfMissing = true;
const FString IDString = RunInfo.MetaData[TEXT("id")];
const FRichSomeWidgetRow* SomeWidgetRow = Decorator->FindSomeWidgetRow(*IDString, bWarnIfMissing);
const FSlateBrush* Brush = &(SomeWidgetRow->Brush);
const FText Text = SomeWidgetRow->Text;
if (ensure(Brush))
{
TOptional<int32> Width;
if (const FString* WidthString = RunInfo.MetaData.Find(TEXT("width")))
{
int32 WidthTemp;
if (FDefaultValueHelper::ParseInt(*WidthString, WidthTemp))
{
Width = WidthTemp;
}
else if (FCString::Stricmp(GetData(*WidthString), TEXT("desired")) == 0)
{
Width = Brush->ImageSize.X;
}
}
TOptional<int32> Height;
if (const FString* HeightString = RunInfo.MetaData.Find(TEXT("height")))
{
int32 HeightTemp;
if (FDefaultValueHelper::ParseInt(*HeightString, HeightTemp))
{
Height = HeightTemp;
}
else if (FCString::Stricmp(GetData(*HeightString), TEXT("desired")) == 0)
{
Height = Brush->ImageSize.Y;
}
}
EStretch::Type Stretch = EStretch::ScaleToFit;
if (const FString* StretchString = RunInfo.MetaData.Find(TEXT("stretch")))
{
const UEnum* StretchEnum = StaticEnum<EStretch::Type>();
const int64 StretchValue = StretchEnum->GetValueByNameString(*StretchString);
if (StretchValue != INDEX_NONE)
{
Stretch = static_cast<EStretch::Type>(StretchValue);
}
}
Result = SNew(SRichInlineSomeWidget, SomeWidgetRow, TextStyle, Width, Height, Stretch);
}
return Result;
}
URichTextBlockSomeWidgetDecorator::URichTextBlockSomeWidgetDecorator(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
TSharedPtr<ITextDecorator> URichTextBlockSomeWidgetDecorator::CreateDecorator(URichTextBlock* InOwner)
{
return MakeShareable(new FRichInlineSomeWidget(InOwner, this));
}
const FRichSomeWidgetRow* URichTextBlockSomeWidgetDecorator::FindSomeWidgetRow(FName TagOrId, bool bWarnIfMissing)
{
return FindRow(TagOrId, bWarnIfMissing);
}
FRichSomeWidgetRow* URichTextBlockSomeWidgetDecorator::FindRow(FName TagOrId, bool bWarnIfMissing)
{
FRichSomeWidgetRow* Result = nullptr;
if (ImageSet)
{
FString ContextString;
Result = ImageSet->FindRow<FRichSomeWidgetRow>(TagOrId, ContextString, bWarnIfMissing);
}
return Result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment