Merge branch 'line-splitting'

This commit is contained in:
Steve Streeting 2023-04-20 16:38:11 +01:00
commit d102c5de67
2 changed files with 106 additions and 9 deletions

View File

@ -69,13 +69,19 @@ FText UTypewriterTextWidget::GetText() const
}
void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed)
{
CurrentLine = InLine;
RemainingLinePart = CurrentLine.ToString();
PlayNextLinePart(Speed);
}
void UTypewriterTextWidget::PlayNextLinePart(float Speed)
{
check(GetWorld());
FTimerManager& TimerManager = GetWorld()->GetTimerManager();
TimerManager.ClearTimer(LetterTimer);
CurrentLine = InLine;
CurrentRunName = "";
CurrentLetterIndex = 0;
CachedLetterIndex = 0;
@ -87,7 +93,7 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed)
Segments.Empty();
CachedSegmentText.Empty();
if (CurrentLine.IsEmpty())
if (RemainingLinePart.IsEmpty())
{
if (IsValid(LineText))
{
@ -107,6 +113,7 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed)
LineText->SetText(FText::GetEmpty());
}
bHasMoreLineParts = false;
bHasFinishedPlaying = false;
if (bFirstPlayLine)
@ -127,7 +134,20 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed)
void UTypewriterTextWidget::StartPlayLine()
{
CalculateWrappedString();
CalculateWrappedString(RemainingLinePart);
if (MaxNumberOfLines > 0 && NumberOfLines > MaxNumberOfLines)
{
int MaxLength = CalculateMaxLength();
int TerminatorIndex = FindLastTerminator(RemainingLinePart, MaxLength);
int Length = TerminatorIndex + 1;
const FString& FirstLinePart = RemainingLinePart.Left(Length);
CalculateWrappedString(FirstLinePart);
RemainingLinePart.RightChopInline(Length);
bHasMoreLineParts = true;
}
FTimerDelegate Delegate;
Delegate.BindUObject(this, &ThisClass::PlayNextLetter);
@ -214,7 +234,56 @@ bool UTypewriterTextWidget::IsSentenceTerminator(TCHAR Letter)
return Letter == '.' || Letter == '!' || Letter == '?';
}
void UTypewriterTextWidget::CalculateWrappedString()
bool UTypewriterTextWidget::IsClauseTerminator(TCHAR Letter)
{
return Letter == ',' || Letter == ';';
}
int UTypewriterTextWidget::FindLastTerminator(const FString& CurrentLineString, int Count)
{
int TerminatorIndex = CurrentLineString.FindLastCharByPredicate(IsSentenceTerminator, Count);
if (TerminatorIndex != INDEX_NONE)
{
return TerminatorIndex;
}
TerminatorIndex = CurrentLineString.FindLastCharByPredicate(IsClauseTerminator, Count);
if (TerminatorIndex != INDEX_NONE)
{
return TerminatorIndex;
}
TerminatorIndex = CurrentLineString.FindLastCharByPredicate(FText::IsWhitespace, Count);
if (TerminatorIndex != INDEX_NONE)
{
return TerminatorIndex;
}
return (Count - 1);
}
int UTypewriterTextWidget::CalculateMaxLength()
{
int MaxLength = 0;
int CurrentNumberOfLines = 1;
for (int i = 0; i < Segments.Num(); i++)
{
const FTypewriterTextSegment& Segment = Segments[i];
MaxLength += Segment.Text.Len();
if (Segment.Text.Equals(FString(TEXT("\n"))))
{
CurrentNumberOfLines++;
if (MaxNumberOfLines > 0 && CurrentNumberOfLines > MaxNumberOfLines)
{
break;
}
}
}
return MaxLength;
}
void UTypewriterTextWidget::CalculateWrappedString(const FString& CurrentLineString)
{
// Rich Text views give you:
// - A blank block at the start for some reason
@ -223,6 +292,9 @@ void UTypewriterTextWidget::CalculateWrappedString()
// - The newlines we add are the only newlines in the output so that's the number of lines
// If we've got here, that means the text isn't empty so 1 line at least
NumberOfLines = 1;
MaxLetterIndex = 0;
CombinedTextHeight = 0;
Segments.Empty();
if (IsValid(LineText) && LineText->GetTextLayout().IsValid())
{
TSharedPtr<FSlateTextLayout> Layout = LineText->GetTextLayout();
@ -233,7 +305,7 @@ void UTypewriterTextWidget::CalculateWrappedString()
Layout->ClearLines();
Layout->SetWrappingWidth(TextBoxSize.X);
Marshaller->SetText(CurrentLine.ToString(), *Layout.Get());
Marshaller->SetText(CurrentLineString, *Layout.Get());
Layout->UpdateLayout();
bool bHasWrittenText = false;
@ -299,7 +371,7 @@ void UTypewriterTextWidget::CalculateWrappedString()
}
else
{
Segments.Add(FTypewriterTextSegment{CurrentLine.ToString()});
Segments.Add(FTypewriterTextSegment{CurrentLineString});
MaxLetterIndex = Segments[0].Text.Len();
}

View File

@ -84,6 +84,10 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter")
float PauseTimeAtSentenceTerminators = 0.5f;
/// If set > 0, splits a single PlayLine into multiple segments of this number of lines maximum
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter")
int MaxNumberOfLines = 0;
/// Set Text immediately
UFUNCTION(BlueprintCallable)
void SetText(const FText& InText);
@ -92,16 +96,33 @@ public:
UFUNCTION(BlueprintCallable)
FText GetText() const;
/**
* Play a line of text.
* Note: if, when line splits are calculated, this line exceeds MaxNumberOfLines, then only this number of lines
* will be played by this call. In that case, HasMoreLineParts() will return true, and you will need to call
* PlayNextLinePart() to play the remainder of the line.
* @param InLine The input line
* @param Speed
*/
UFUNCTION(BlueprintCallable, Category = "Typewriter")
void PlayLine(const FText& InLine, float Speed = 1.0f);
UFUNCTION(BlueprintCallable, Category = "Typewriter")
void GetCurrentLine(FText& OutLine) const { OutLine = CurrentLine; }
/// Return whether the entire line has finished playing
UFUNCTION(BlueprintCallable, Category = "Typewriter")
bool HasFinishedPlayingLine() const { return bHasFinishedPlaying; }
/// Returns whether the number of lines exceeded MaxNumberOfLines and there are still parts to play.
UFUNCTION(BlueprintCallable, Category = "Typewriter")
bool HasMoreLineParts() const { return bHasMoreLineParts; }
/// If HasMoreLineParts() is true, play the next part of the line originally requested by PlayLine
UFUNCTION(BlueprintCallable, Category = "Typewriter")
void PlayNextLinePart(float Speed = 1.0f);
UFUNCTION(BlueprintCallable, Category = "Typewriter")
void SkipToLineEnd();
@ -127,15 +148,18 @@ protected:
private:
void PlayNextLetter();
static bool IsSentenceTerminator(TCHAR Letter);
static bool IsClauseTerminator(TCHAR Letter);
static int FindLastTerminator(const FString& CurrentLineString, int Count);
void CalculateWrappedString();
int CalculateMaxLength();
void CalculateWrappedString(const FString& CurrentLineString);
FString CalculateSegments(FString* OutCurrentRunName);
void StartPlayLine();
UPROPERTY()
FText CurrentLine;
FString RemainingLinePart;
struct FTypewriterTextSegment
{
@ -158,6 +182,7 @@ private:
float CombinedTextHeight = 0;
uint32 bHasFinishedPlaying : 1;
uint32 bHasMoreLineParts : 1;
FTimerHandle LetterTimer;
float CurrentPlaySpeed = 1;