// Copyright Epic Games, Inc. All Rights Reserved. #include "Framework/Text/RichTextLayoutMarshaller.h" #include "Styling/ISlateStyle.h" #include "Styling/SlateTypes.h" #include "Framework/Text/SlateTextRun.h" #include "Framework/Text/IRichTextMarkupWriter.h" #include "Framework/Text/RichTextMarkupProcessing.h" #include "Framework/Text/SlateTextLayout.h" #include "Framework/Text/SlateTextUnderlineLineHighlighter.h" #if WITH_FANCY_TEXT TSharedRef< FRichTextLayoutMarshaller > FRichTextLayoutMarshaller::Create(TArray< TSharedRef< ITextDecorator > > InDecorators, const ISlateStyle* const InDecoratorStyleSet) { return MakeShareable(new FRichTextLayoutMarshaller(MoveTemp(InDecorators), InDecoratorStyleSet)); } TSharedRef< FRichTextLayoutMarshaller > FRichTextLayoutMarshaller::Create(TSharedPtr< IRichTextMarkupParser > InParser, TSharedPtr< IRichTextMarkupWriter > InWriter, TArray< TSharedRef< ITextDecorator > > InDecorators, const ISlateStyle* const InDecoratorStyleSet) { return MakeShareable(new FRichTextLayoutMarshaller(MoveTemp(InParser), MoveTemp(InWriter), MoveTemp(InDecorators), InDecoratorStyleSet)); } FRichTextLayoutMarshaller::~FRichTextLayoutMarshaller() { } void FRichTextLayoutMarshaller::SetText(const FString& SourceString, FTextLayout& TargetTextLayout) { const FTextBlockStyle& DefaultTextStyle = static_cast(TargetTextLayout).GetDefaultTextStyle(); TArray LineParseResultsArray; FString ProcessedString; Parser->Process(LineParseResultsArray, SourceString, ProcessedString); TArray LinesToAdd; LinesToAdd.Reserve(LineParseResultsArray.Num()); TArray LineHighlightsToAdd; TMap> CachedUnderlineHighlighters; TMap> CachedStrikeLineHighlighters; // Iterate through parsed line results and create processed lines with runs. for (int32 LineIndex = 0; LineIndex < LineParseResultsArray.Num(); ++LineIndex) { const FTextLineParseResults& LineParseResults = LineParseResultsArray[LineIndex]; TSharedRef ModelString = MakeShareable(new FString()); TArray< TSharedRef< IRun > > Runs; for (const FTextRunParseResults& RunParseResult : LineParseResults.Runs) { AppendRunsForText(LineIndex, RunParseResult, ProcessedString, DefaultTextStyle, ModelString, TargetTextLayout, Runs, LineHighlightsToAdd, CachedUnderlineHighlighters, CachedStrikeLineHighlighters); } LinesToAdd.Emplace(MoveTemp(ModelString), MoveTemp(Runs)); } TargetTextLayout.AddLines(LinesToAdd); TargetTextLayout.SetLineHighlights(LineHighlightsToAdd); } void FRichTextLayoutMarshaller::GetText(FString& TargetString, const FTextLayout& SourceTextLayout) { const auto& SourceLineModels = SourceTextLayout.GetLineModels(); TArray WriterLines; WriterLines.Reserve(SourceLineModels.Num()); for (const FTextLayout::FLineModel& LineModel : SourceLineModels) { IRichTextMarkupWriter::FRichTextLine WriterLine; WriterLine.Runs.Reserve(LineModel.Runs.Num()); for (const FTextLayout::FRunModel& RunModel : LineModel.Runs) { FString RunText; RunModel.AppendTextTo(RunText); WriterLine.Runs.Emplace(IRichTextMarkupWriter::FRichTextRun(RunModel.GetRun()->GetRunInfo(), RunText)); } WriterLines.Add(WriterLine); } Writer->Write(WriterLines, TargetString); } void FRichTextLayoutMarshaller::SetFontSizeMultiplier(const float NewFontSizeMultiplier) { if (ensure(NewFontSizeMultiplier != 0.0f)) { FontSizeMultiplier = NewFontSizeMultiplier; } } FRichTextLayoutMarshaller::FRichTextLayoutMarshaller(TArray< TSharedRef< ITextDecorator > > InDecorators, const ISlateStyle* const InDecoratorStyleSet) : Parser(FDefaultRichTextMarkupParser::GetStaticInstance()) , Writer(FDefaultRichTextMarkupWriter::Create()) , Decorators(MoveTemp(InDecorators)) , InlineDecorators() , DecoratorStyleSet(InDecoratorStyleSet) { } FRichTextLayoutMarshaller::FRichTextLayoutMarshaller(TSharedPtr< IRichTextMarkupParser > InParser, TSharedPtr< IRichTextMarkupWriter > InWriter, TArray< TSharedRef< ITextDecorator > > InDecorators, const ISlateStyle* const InDecoratorStyleSet) : Parser(MoveTemp(InParser)) , Writer(MoveTemp(InWriter)) , Decorators(MoveTemp(InDecorators)) , InlineDecorators() , DecoratorStyleSet(InDecoratorStyleSet) { } TSharedPtr< ITextDecorator > FRichTextLayoutMarshaller::TryGetDecorator(const FString& Line, const FTextRunParseResults& TextRun) const { for (auto DecoratorIter = InlineDecorators.CreateConstIterator(); DecoratorIter; ++DecoratorIter) { if ((*DecoratorIter)->Supports(TextRun, Line)) { return *DecoratorIter; } } for (auto DecoratorIter = Decorators.CreateConstIterator(); DecoratorIter; ++DecoratorIter) { if ((*DecoratorIter)->Supports(TextRun, Line)) { return *DecoratorIter; } } return nullptr; } void FRichTextLayoutMarshaller::AppendRunsForText( const int32 LineIndex, const FTextRunParseResults& TextRun, const FString& ProcessedString, const FTextBlockStyle& DefaultTextStyle, const TSharedRef& InOutModelText, FTextLayout& TargetTextLayout, TArray>& Runs, TArray& LineHighlights, TMap>& CachedUnderlineHighlighters, TMap>& CachedStrikeLineHighlighters ) { TSharedPtr< ISlateRun > Run; TSharedPtr< ITextDecorator > Decorator = TryGetDecorator(ProcessedString, TextRun); if (Decorator.IsValid()) { // Create run and update model string. Run = Decorator->Create(TargetTextLayout.AsShared(), TextRun, ProcessedString, InOutModelText, DecoratorStyleSet); } else { FRunInfo RunInfo(TextRun.Name); for (const TPair& Pair : TextRun.MetaData) { int32 Length = FMath::Max(0, Pair.Value.EndIndex - Pair.Value.BeginIndex); RunInfo.MetaData.Add(Pair.Key, ProcessedString.Mid(Pair.Value.BeginIndex, Length)); } const FTextBlockStyle* TextBlockStyle; FTextRange ModelRange; ModelRange.BeginIndex = InOutModelText->Len(); if (!(TextRun.Name.IsEmpty()) && DecoratorStyleSet->HasWidgetStyle< FTextBlockStyle >(FName(*TextRun.Name))) { *InOutModelText += ProcessedString.Mid(TextRun.ContentRange.BeginIndex, TextRun.ContentRange.EndIndex - TextRun.ContentRange.BeginIndex); TextBlockStyle = &(DecoratorStyleSet->GetWidgetStyle< FTextBlockStyle >(FName(*TextRun.Name))); } else { *InOutModelText += ProcessedString.Mid(TextRun.OriginalRange.BeginIndex, TextRun.OriginalRange.EndIndex - TextRun.OriginalRange.BeginIndex); TextBlockStyle = &DefaultTextStyle; } ModelRange.EndIndex = InOutModelText->Len(); // Create run. TSharedPtr< FSlateTextRun > SlateTextRun = FSlateTextRun::Create(RunInfo, InOutModelText, *TextBlockStyle, ModelRange); if (SlateTextRun) { // Apply the FontSizeMultiplier at the style use by the IRun SlateTextRun->ApplyFontSizeMultiplierOnTextStyle(FontSizeMultiplier); } Run = SlateTextRun; if (!TextBlockStyle->UnderlineBrush.GetResourceName().IsNone()) { TSharedPtr UnderlineLineHighlighter = CachedUnderlineHighlighters.FindRef(TextBlockStyle); if (!UnderlineLineHighlighter.IsValid()) { UnderlineLineHighlighter = FSlateTextUnderlineLineHighlighter::Create(TextBlockStyle->UnderlineBrush, TextBlockStyle->Font, TextBlockStyle->ColorAndOpacity, TextBlockStyle->ShadowOffset, TextBlockStyle->ShadowColorAndOpacity); CachedUnderlineHighlighters.Add(TextBlockStyle, UnderlineLineHighlighter); } LineHighlights.Add(FTextLineHighlight(LineIndex, ModelRange, FSlateTextUnderlineLineHighlighter::DefaultZIndex, UnderlineLineHighlighter.ToSharedRef())); } if (!TextBlockStyle->StrikeBrush.GetResourceName().IsNone()) { TSharedPtr StrikeLineHighlighter = CachedStrikeLineHighlighters.FindRef(TextBlockStyle); if (!StrikeLineHighlighter.IsValid()) { StrikeLineHighlighter = FSlateTextStrikeLineHighlighter::Create(TextBlockStyle->StrikeBrush, TextBlockStyle->Font, TextBlockStyle->ColorAndOpacity, TextBlockStyle->ShadowOffset, TextBlockStyle->ShadowColorAndOpacity); CachedStrikeLineHighlighters.Add(TextBlockStyle, StrikeLineHighlighter); } LineHighlights.Add(FTextLineHighlight(LineIndex, ModelRange, FSlateTextStrikeLineHighlighter::DefaultZIndex, StrikeLineHighlighter.ToSharedRef())); } } Runs.Add(Run.ToSharedRef()); } #endif //WITH_FANCY_TEXT