You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
TTP# 336464 - Editor: Finish the Multiline Editable Text Block Abstracted away the SetEditableText and GetEditableText functions from SMultiLineEditableText into "text marshallers" which handle converting text to and from a TextLayout. There are three types of text marshallers currently implemented: * Plain-text * Rich-text * Syntax highlighting Text marshallers also have the ability to inject formatting "live" (as the text changes), which is how the syntax highlighting marshaller works. Added the ability for a run to query the information it was created with. This allows the rich-text marshaller to reconstruct the original rich-text from the styled runs. To test this out, I've implemented a simple WYSIWYG rich-text editor demo with the following features: * Two SMultiLineEditableText widgets showing the same source text, one using a rich-text marshaller, and one using a syntax highlighter marshaller. * A toolbar to allow you to control the style of the selected text. * A button to allow you to insert a hyperlink into the document. The demo also makes use of the meta-data stored in the runs (the same information used to reconstruct the original rich-text) to read the text style of whatever is currently under the cursor, live, as the cursor is moved. Miscellaneous fixes: * Fixed an issue where deleting text that spanned multiple runs could leave the remaining runs in a bad state, leading to phantom text appearing (see FTextLayout::RemoveAt). * Fixed an issue where new-lines at the end of a rich-text document would be lost (see CalculateLineRanges). * Fixed an issue where \\r\\n line endings werenÆt being handled correctly by the rich-text parser (see CalculateLineRanges). * Fixed an issue where the rich-text parser would treat an empty run as plain-text (see FRichTextMarkupProcessing::ParseLineRanges). * Fixed an issue where inserting a line break when the cursor was at the end of a line containing multiple runs could sometimes fail (see FTextLayout::SplitLineAt). * Fixed mouse cursor movement not working correctly with a FSlateHyperlinkRun (see FSlateHyperlinkRun::GetTextIndexAt). ReviewedBy Justin.Sargent [CL 2246838 by Jamie Dale in Main branch]
86 lines
2.5 KiB
C++
86 lines
2.5 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SlatePrivatePCH.h"
|
|
#include "SyntaxTokenizer.h"
|
|
#include "WordBreakIterator.h"
|
|
|
|
TSharedRef< FSyntaxTokenizer > FSyntaxTokenizer::Create(TArray<FRule> InRules)
|
|
{
|
|
return MakeShareable(new FSyntaxTokenizer(MoveTemp(InRules)));
|
|
}
|
|
|
|
FSyntaxTokenizer::~FSyntaxTokenizer()
|
|
{
|
|
}
|
|
|
|
void FSyntaxTokenizer::Process(TArray<FTokenizedLine>& OutTokenizedLines, const FString& Input)
|
|
{
|
|
#if UE_ENABLE_ICU
|
|
TArray<FTextRange> LineRanges;
|
|
FTextRange::CalculateLineRangesFromString(Input, LineRanges);
|
|
TokenizeLineRanges(Input, LineRanges, OutTokenizedLines);
|
|
#else
|
|
FTokenizedLine FakeTokenizedLine;
|
|
FakeTokenizedLine.Range = FTextRange(0, Input.Len());
|
|
FakeTokenizedLine.Tokens.Emplace(FToken(ETokenType::Literal, FakeTokenizedLine.Range));
|
|
OutTokenizedLines.Add(FakeTokenizedLine);
|
|
#endif
|
|
}
|
|
|
|
FSyntaxTokenizer::FSyntaxTokenizer(TArray<FRule> InRules)
|
|
: Rules(MoveTemp(InRules))
|
|
{
|
|
}
|
|
|
|
void FSyntaxTokenizer::TokenizeLineRanges(const FString& Input, const TArray<FTextRange>& LineRanges, TArray<FTokenizedLine>& OutTokenizedLines)
|
|
{
|
|
// Tokenize line ranges
|
|
FWordBreakIterator WBI(Input);
|
|
for(const FTextRange& LineRange : LineRanges)
|
|
{
|
|
FTokenizedLine TokenizedLine;
|
|
TokenizedLine.Range = LineRange;
|
|
|
|
if(TokenizedLine.Range.IsEmpty())
|
|
{
|
|
TokenizedLine.Tokens.Emplace(FToken(ETokenType::Literal, TokenizedLine.Range));
|
|
}
|
|
else
|
|
{
|
|
int32 CurrentOffset = LineRange.BeginIndex;
|
|
while(CurrentOffset < LineRange.EndIndex)
|
|
{
|
|
// First check for a match against any syntax token rules
|
|
bool bHasMatchedSyntax = false;
|
|
for(const FRule& Rule : Rules)
|
|
{
|
|
if(FCString::Strncmp(&Input[CurrentOffset], *Rule.MatchText, Rule.MatchText.Len()) == 0)
|
|
{
|
|
const int32 SyntaxTokenEnd = CurrentOffset + Rule.MatchText.Len();
|
|
TokenizedLine.Tokens.Emplace(FToken(ETokenType::Syntax, FTextRange(CurrentOffset, SyntaxTokenEnd)));
|
|
|
|
check(SyntaxTokenEnd <= LineRange.EndIndex);
|
|
|
|
bHasMatchedSyntax = true;
|
|
CurrentOffset = SyntaxTokenEnd;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(bHasMatchedSyntax)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If none matched, consume the character(s) as text
|
|
const int32 NextWordBoundary = WBI.MoveToCandidateAfter(CurrentOffset);
|
|
const int32 TextTokenEnd = (NextWordBoundary == INDEX_NONE) ? LineRange.EndIndex : FMath::Min(NextWordBoundary, LineRange.EndIndex);
|
|
TokenizedLine.Tokens.Emplace(FToken(ETokenType::Literal, FTextRange(CurrentOffset, TextTokenEnd)));
|
|
CurrentOffset = TextTokenEnd;
|
|
}
|
|
}
|
|
|
|
OutTokenizedLines.Add(TokenizedLine);
|
|
}
|
|
}
|