Last active
February 9, 2025 21:06
-
-
Save BaeMinCheon/6521a88bce86bd4cd60b7b383cf45637 to your computer and use it in GitHub Desktop.
Custom RichTextBlockDecorator example code #2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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