From 3175f5cbd423b549c82171c639cba83d6731d7e9 Mon Sep 17 00:00:00 2001 From: lumpn Date: Mon, 10 Apr 2023 16:15:03 +0800 Subject: [PATCH 01/13] Breaking overly long text in typewriter WIP --- .../Private/StevesUI/TypewriterTextWidget.cpp | 52 +++++++++++++++++-- .../Public/StevesUI/TypewriterTextWidget.h | 2 +- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index 26813a7..97322a4 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -110,7 +110,51 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) bHasFinishedPlaying = false; LineText->SetText(FText()); - CalculateWrappedString(); + + const FString& currentLineString = CurrentLine.ToString(); + CalculateWrappedString(currentLineString); + + int maxLines = 3; + if (NumberOfLines > maxLines) + { + int numLetters = 0; + int currentLine = 1; + for (int i = 0; i < Segments.Num(); i++) + { + const auto& segment = Segments[i]; + if (segment.Text.Equals(FString(TEXT("\n")))) + { + currentLine++; + if (currentLine > maxLines) + { + break; + } + } + else + { + numLetters += segment.Text.Len(); + } + } + + int lastTerminator = currentLineString.FindLastCharByPredicate(IsSentenceTerminator, numLetters); + if (lastTerminator != INDEX_NONE) + { + const FString& shortenedString = currentLineString.Left(lastTerminator + 1); + + CurrentRunName = ""; + CurrentLetterIndex = 0; + CachedLetterIndex = 0; + CurrentSegmentIndex = 0; + MaxLetterIndex = 0; + NumberOfLines = 0; + CombinedTextHeight = 0; + CurrentPlaySpeed = Speed; + Segments.Empty(); + CachedSegmentText.Empty(); + + CalculateWrappedString(shortenedString); + } + } FTimerDelegate Delegate; Delegate.BindUObject(this, &ThisClass::PlayNextLetter); @@ -185,7 +229,7 @@ bool UTypewriterTextWidget::IsSentenceTerminator(TCHAR Letter) return Letter == '.' || Letter == '!' || Letter == '?'; } -void UTypewriterTextWidget::CalculateWrappedString() +void UTypewriterTextWidget::CalculateWrappedString(const FString& CurrentLineString) { // Rich Text views give you: // - A blank block at the start for some reason @@ -204,7 +248,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; @@ -270,7 +314,7 @@ void UTypewriterTextWidget::CalculateWrappedString() } else { - Segments.Add(FTypewriterTextSegment{CurrentLine.ToString()}); + Segments.Add(FTypewriterTextSegment{CurrentLineString}); MaxLetterIndex = Segments[0].Text.Len(); } diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index b090c78..b9ff31f 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -126,7 +126,7 @@ private: void PlayNextLetter(); static bool IsSentenceTerminator(TCHAR Letter); - void CalculateWrappedString(); + void CalculateWrappedString(const FString& CurrentLineString); FString CalculateSegments(FString* OutCurrentRunName); UPROPERTY() From e4af81cd359cc199a1f3e670af11e5047a775496 Mon Sep 17 00:00:00 2001 From: lumpn Date: Mon, 10 Apr 2023 16:28:26 +0800 Subject: [PATCH 02/13] Added TODO --- .../Private/StevesUI/TypewriterTextWidget.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index 97322a4..681a377 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -153,6 +153,11 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) CachedSegmentText.Empty(); CalculateWrappedString(shortenedString); + + // TODO Jonas: Play remaining line after the shortened string. + // If this is done purely in the typewriter then advancing the dialog would + // skip over all but the last part of the text. Alternatively advancing has + // to get rerouted to trigger a dialogue action only for the last part. } } From f0bd405cb215097c06831eb3e049e3567482dccb Mon Sep 17 00:00:00 2001 From: lumpn Date: Mon, 10 Apr 2023 18:20:51 +0800 Subject: [PATCH 03/13] Implemented line part playing --- .../Private/StevesUI/TypewriterTextWidget.cpp | 38 +++++++++++++------ .../Public/StevesUI/TypewriterTextWidget.h | 10 ++++- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index 681a377..ff0777b 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -42,6 +42,7 @@ TSharedRef URichTextBlockForTypewriter::RebuildWidget() UTypewriterTextWidget::UTypewriterTextWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { + bHasMoreLineParts = false; bHasFinishedPlaying = true; } @@ -54,6 +55,7 @@ void UTypewriterTextWidget::SetText(const FText& InText) LineText->SetText(InText); + bHasMoreLineParts = false; bHasFinishedPlaying = true; } } @@ -69,13 +71,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,13 +95,14 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) Segments.Empty(); CachedSegmentText.Empty(); - if (CurrentLine.IsEmpty()) + if (RemainingLinePart.IsEmpty()) { if (IsValid(LineText)) { LineText->SetText(FText::GetEmpty()); } + bHasMoreLineParts = false; bHasFinishedPlaying = true; OnTypewriterLineFinished.Broadcast(this); OnLineFinishedPlaying(); @@ -107,11 +116,12 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) LineText->SetText(FText::GetEmpty()); } + bHasMoreLineParts = false; bHasFinishedPlaying = false; LineText->SetText(FText()); - const FString& currentLineString = CurrentLine.ToString(); + const FString& currentLineString = RemainingLinePart; CalculateWrappedString(currentLineString); int maxLines = 3; @@ -139,7 +149,8 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) int lastTerminator = currentLineString.FindLastCharByPredicate(IsSentenceTerminator, numLetters); if (lastTerminator != INDEX_NONE) { - const FString& shortenedString = currentLineString.Left(lastTerminator + 1); + int count = lastTerminator + 1; + const FString& shortenedString = currentLineString.Left(count); CurrentRunName = ""; CurrentLetterIndex = 0; @@ -154,10 +165,8 @@ void UTypewriterTextWidget::PlayLine(const FText& InLine, float Speed) CalculateWrappedString(shortenedString); - // TODO Jonas: Play remaining line after the shortened string. - // If this is done purely in the typewriter then advancing the dialog would - // skip over all but the last part of the text. Alternatively advancing has - // to get rerouted to trigger a dialogue action only for the last part. + RemainingLinePart.RightInline(count); + bHasMoreLineParts = true; } } @@ -181,9 +190,16 @@ void UTypewriterTextWidget::SkipToLineEnd() LineText->SetText(FText::FromString(CalculateSegments(nullptr))); } - bHasFinishedPlaying = true; - OnTypewriterLineFinished.Broadcast(this); - OnLineFinishedPlaying(); + if (bHasMoreLineParts) + { + OnTypewriterLinePartFinished.Broadcast(this); + } + else + { + bHasFinishedPlaying = true; + OnTypewriterLineFinished.Broadcast(this); + OnLineFinishedPlaying(); + } } void UTypewriterTextWidget::PlayNextLetter() diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index b9ff31f..e453961 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -56,6 +56,10 @@ class STEVESUEHELPERS_API UTypewriterTextWidget : public UUserWidget public: UTypewriterTextWidget(const FObjectInitializer& ObjectInitializer); + /// Event called when a line part has finished playing, whether on its own or when skipped to end + UPROPERTY(BlueprintAssignable) + FOnTypewriterLineFinished OnTypewriterLinePartFinished; + /// Event called when a line has finished playing, whether on its own or when skipped to end UPROPERTY(BlueprintAssignable) FOnTypewriterLineFinished OnTypewriterLineFinished; @@ -102,6 +106,9 @@ public: UFUNCTION(BlueprintCallable, Category = "Typewriter") bool HasFinishedPlayingLine() const { return bHasFinishedPlaying; } + UFUNCTION(BlueprintCallable, Category = "Typewriter") + void PlayNextLinePart(float Speed = 1.0f); + UFUNCTION(BlueprintCallable, Category = "Typewriter") void SkipToLineEnd(); @@ -132,7 +139,7 @@ private: UPROPERTY() FText CurrentLine; - + FString RemainingLinePart; struct FTypewriterTextSegment { @@ -155,6 +162,7 @@ private: float CombinedTextHeight = 0; uint32 bHasFinishedPlaying : 1; + uint32 bHasMoreLineParts : 1; FTimerHandle LetterTimer; float CurrentPlaySpeed = 1; From 7aac7d9d753e6a1323039c188554ff333bf691c1 Mon Sep 17 00:00:00 2001 From: lumpn Date: Mon, 10 Apr 2023 18:56:20 +0800 Subject: [PATCH 04/13] Implemented line chopping --- .../Private/StevesUI/TypewriterTextWidget.cpp | 18 ++++-------------- .../Public/StevesUI/TypewriterTextWidget.h | 7 +++---- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index ff0777b..80e8c06 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -42,7 +42,6 @@ TSharedRef URichTextBlockForTypewriter::RebuildWidget() UTypewriterTextWidget::UTypewriterTextWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - bHasMoreLineParts = false; bHasFinishedPlaying = true; } @@ -55,7 +54,6 @@ void UTypewriterTextWidget::SetText(const FText& InText) LineText->SetText(InText); - bHasMoreLineParts = false; bHasFinishedPlaying = true; } } @@ -102,7 +100,6 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) LineText->SetText(FText::GetEmpty()); } - bHasMoreLineParts = false; bHasFinishedPlaying = true; OnTypewriterLineFinished.Broadcast(this); OnLineFinishedPlaying(); @@ -165,7 +162,7 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) CalculateWrappedString(shortenedString); - RemainingLinePart.RightInline(count); + RemainingLinePart.RightChopInline(count); bHasMoreLineParts = true; } } @@ -190,16 +187,9 @@ void UTypewriterTextWidget::SkipToLineEnd() LineText->SetText(FText::FromString(CalculateSegments(nullptr))); } - if (bHasMoreLineParts) - { - OnTypewriterLinePartFinished.Broadcast(this); - } - else - { - bHasFinishedPlaying = true; - OnTypewriterLineFinished.Broadcast(this); - OnLineFinishedPlaying(); - } + bHasFinishedPlaying = true; + OnTypewriterLineFinished.Broadcast(this); + OnLineFinishedPlaying(); } void UTypewriterTextWidget::PlayNextLetter() diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index e453961..07a8b6b 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -56,10 +56,6 @@ class STEVESUEHELPERS_API UTypewriterTextWidget : public UUserWidget public: UTypewriterTextWidget(const FObjectInitializer& ObjectInitializer); - /// Event called when a line part has finished playing, whether on its own or when skipped to end - UPROPERTY(BlueprintAssignable) - FOnTypewriterLineFinished OnTypewriterLinePartFinished; - /// Event called when a line has finished playing, whether on its own or when skipped to end UPROPERTY(BlueprintAssignable) FOnTypewriterLineFinished OnTypewriterLineFinished; @@ -106,6 +102,9 @@ public: UFUNCTION(BlueprintCallable, Category = "Typewriter") bool HasFinishedPlayingLine() const { return bHasFinishedPlaying; } + UFUNCTION(BlueprintCallable, Category = "Typewriter") + bool HasMoreLineParts() const { return bHasMoreLineParts; } + UFUNCTION(BlueprintCallable, Category = "Typewriter") void PlayNextLinePart(float Speed = 1.0f); From e41fbbeb89f8dc2ef4e0195ff338994535aac330 Mon Sep 17 00:00:00 2001 From: lumpn Date: Mon, 10 Apr 2023 19:13:31 +0800 Subject: [PATCH 05/13] Added TODOs --- .../Private/StevesUI/TypewriterTextWidget.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index 80e8c06..ea4d295 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -118,9 +118,9 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) LineText->SetText(FText()); - const FString& currentLineString = RemainingLinePart; - CalculateWrappedString(currentLineString); + CalculateWrappedString(RemainingLinePart); + // TODO Jonas: Promote maxLines to variable. Might want maxLines = 1 for text before choices. int maxLines = 3; if (NumberOfLines > maxLines) { @@ -129,6 +129,7 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) for (int i = 0; i < Segments.Num(); i++) { const auto& segment = Segments[i]; + // TODO Jonas: Mark line break segments in CalculateWrappedString instead. if (segment.Text.Equals(FString(TEXT("\n")))) { currentLine++; @@ -143,12 +144,14 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) } } - int lastTerminator = currentLineString.FindLastCharByPredicate(IsSentenceTerminator, numLetters); + // TODO Jonas: Find lesser terminators like commas or spaces if no sentence terminator can be found. + int lastTerminator = RemainingLinePart.FindLastCharByPredicate(IsSentenceTerminator, numLetters); if (lastTerminator != INDEX_NONE) { int count = lastTerminator + 1; - const FString& shortenedString = currentLineString.Left(count); + const FString& shortenedString = RemainingLinePart.Left(count); + // TODO Jonas: Clean up CurrentRunName = ""; CurrentLetterIndex = 0; CachedLetterIndex = 0; From 4805f49ccbb4190548af140889152da5f0d2e520 Mon Sep 17 00:00:00 2001 From: lumpn Date: Thu, 20 Apr 2023 13:15:09 +0800 Subject: [PATCH 06/13] Extracted functions --- .../Private/StevesUI/TypewriterTextWidget.cpp | 95 ++++++++++--------- .../Public/StevesUI/TypewriterTextWidget.h | 2 + 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index ea4d295..1cf2522 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -120,54 +120,19 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) CalculateWrappedString(RemainingLinePart); - // TODO Jonas: Promote maxLines to variable. Might want maxLines = 1 for text before choices. - int maxLines = 3; - if (NumberOfLines > maxLines) + // TODO Jonas: Promote MaxLines to variable. Might want MaxLines = 1 for text before choices. + int MaxLines = 3; + if (NumberOfLines > MaxLines) { - int numLetters = 0; - int currentLine = 1; - for (int i = 0; i < Segments.Num(); i++) - { - const auto& segment = Segments[i]; - // TODO Jonas: Mark line break segments in CalculateWrappedString instead. - if (segment.Text.Equals(FString(TEXT("\n")))) - { - currentLine++; - if (currentLine > maxLines) - { - break; - } - } - else - { - numLetters += segment.Text.Len(); - } - } + int MaxLength = CalculateMaxLength(MaxLines); + int TerminatorIndex = FindLastTerminator(RemainingLinePart, MaxLength); + int Length = TerminatorIndex + 1; + const FString& FirstLinePart = RemainingLinePart.Left(Length); - // TODO Jonas: Find lesser terminators like commas or spaces if no sentence terminator can be found. - int lastTerminator = RemainingLinePart.FindLastCharByPredicate(IsSentenceTerminator, numLetters); - if (lastTerminator != INDEX_NONE) - { - int count = lastTerminator + 1; - const FString& shortenedString = RemainingLinePart.Left(count); + CalculateWrappedString(FirstLinePart); - // TODO Jonas: Clean up - CurrentRunName = ""; - CurrentLetterIndex = 0; - CachedLetterIndex = 0; - CurrentSegmentIndex = 0; - MaxLetterIndex = 0; - NumberOfLines = 0; - CombinedTextHeight = 0; - CurrentPlaySpeed = Speed; - Segments.Empty(); - CachedSegmentText.Empty(); - - CalculateWrappedString(shortenedString); - - RemainingLinePart.RightChopInline(count); - bHasMoreLineParts = true; - } + RemainingLinePart.RightChopInline(Length); + bHasMoreLineParts = true; } FTimerDelegate Delegate; @@ -243,6 +208,43 @@ bool UTypewriterTextWidget::IsSentenceTerminator(TCHAR Letter) return Letter == '.' || Letter == '!' || Letter == '?'; } +int UTypewriterTextWidget::FindLastTerminator(const FString& CurrentLineString, int Count) +{ + // TODO Jonas: Find lesser terminators like commas or spaces if no sentence terminator can be found. + int TerminatorIndex = CurrentLineString.FindLastCharByPredicate(IsSentenceTerminator, Count); + if (TerminatorIndex != INDEX_NONE) + { + return TerminatorIndex; + } + + return Count; +} + +int UTypewriterTextWidget::CalculateMaxLength(int MaxNumberOfLines) +{ + int MaxLength = 0; + int CurrentNumberOfLines = 1; + for (int i = 0; i < Segments.Num(); i++) + { + const FTypewriterTextSegment& Segment = Segments[i]; + // TODO Jonas: Mark line break segments as such in CalculateWrappedString instead. + if (Segment.Text.Equals(FString(TEXT("\n")))) + { + CurrentNumberOfLines++; + if (CurrentNumberOfLines > MaxNumberOfLines) + { + break; + } + } + else + { + MaxLength += Segment.Text.Len(); + } + } + + return MaxLength; +} + void UTypewriterTextWidget::CalculateWrappedString(const FString& CurrentLineString) { // Rich Text views give you: @@ -252,6 +254,9 @@ void UTypewriterTextWidget::CalculateWrappedString(const FString& CurrentLineStr // - 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 Layout = LineText->GetTextLayout(); diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index 07a8b6b..70e54e3 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -131,7 +131,9 @@ protected: private: void PlayNextLetter(); static bool IsSentenceTerminator(TCHAR Letter); + static int FindLastTerminator(const FString& CurrentLineString, int Count); + int CalculateMaxLength(int MaxNumberOfLines); void CalculateWrappedString(const FString& CurrentLineString); FString CalculateSegments(FString* OutCurrentRunName); From c20d1113c1fb02109b82e57a4f6c5fbd4e50ac1e Mon Sep 17 00:00:00 2001 From: lumpn Date: Thu, 20 Apr 2023 13:43:17 +0800 Subject: [PATCH 07/13] Implemented fallback to lesser terminators for line splitting --- .../Private/StevesUI/TypewriterTextWidget.cpp | 20 +++++++++++++++++-- .../Public/StevesUI/TypewriterTextWidget.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index 1cf2522..fe5bbe0 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -208,16 +208,32 @@ bool UTypewriterTextWidget::IsSentenceTerminator(TCHAR Letter) return Letter == '.' || Letter == '!' || Letter == '?'; } +bool UTypewriterTextWidget::IsClauseTerminator(TCHAR Letter) +{ + return Letter == ',' || Letter == ';'; +} + int UTypewriterTextWidget::FindLastTerminator(const FString& CurrentLineString, int Count) { - // TODO Jonas: Find lesser terminators like commas or spaces if no sentence terminator can be found. int TerminatorIndex = CurrentLineString.FindLastCharByPredicate(IsSentenceTerminator, Count); if (TerminatorIndex != INDEX_NONE) { return TerminatorIndex; } - return Count; + 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 MaxNumberOfLines) diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index 70e54e3..d75ac50 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -131,6 +131,7 @@ protected: private: void PlayNextLetter(); static bool IsSentenceTerminator(TCHAR Letter); + static bool IsClauseTerminator(TCHAR Letter); static int FindLastTerminator(const FString& CurrentLineString, int Count); int CalculateMaxLength(int MaxNumberOfLines); From 40398ef85127f39383d34ddf1a5eae42e138cc29 Mon Sep 17 00:00:00 2001 From: lumpn Date: Thu, 20 Apr 2023 13:56:01 +0800 Subject: [PATCH 08/13] Promoted `MaxNumberOfLines` to a property --- .../Private/StevesUI/TypewriterTextWidget.cpp | 9 +++------ .../Public/StevesUI/TypewriterTextWidget.h | 6 +++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index fe5bbe0..94a6925 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -120,11 +120,9 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) CalculateWrappedString(RemainingLinePart); - // TODO Jonas: Promote MaxLines to variable. Might want MaxLines = 1 for text before choices. - int MaxLines = 3; - if (NumberOfLines > MaxLines) + if (NumberOfLines > MaxNumberOfLines) { - int MaxLength = CalculateMaxLength(MaxLines); + int MaxLength = CalculateMaxLength(); int TerminatorIndex = FindLastTerminator(RemainingLinePart, MaxLength); int Length = TerminatorIndex + 1; const FString& FirstLinePart = RemainingLinePart.Left(Length); @@ -236,14 +234,13 @@ int UTypewriterTextWidget::FindLastTerminator(const FString& CurrentLineString, return (Count - 1); } -int UTypewriterTextWidget::CalculateMaxLength(int MaxNumberOfLines) +int UTypewriterTextWidget::CalculateMaxLength() { int MaxLength = 0; int CurrentNumberOfLines = 1; for (int i = 0; i < Segments.Num(); i++) { const FTypewriterTextSegment& Segment = Segments[i]; - // TODO Jonas: Mark line break segments as such in CalculateWrappedString instead. if (Segment.Text.Equals(FString(TEXT("\n")))) { CurrentNumberOfLines++; diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index d75ac50..e8b8c49 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -84,6 +84,10 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") float PauseTimeAtSentenceTerminators = 0.5f; + /// How many lines of text at most to print at once. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") + int MaxNumberOfLines = 3; + /// Set Text immediately UFUNCTION(BlueprintCallable) void SetText(const FText& InText); @@ -134,7 +138,7 @@ private: static bool IsClauseTerminator(TCHAR Letter); static int FindLastTerminator(const FString& CurrentLineString, int Count); - int CalculateMaxLength(int MaxNumberOfLines); + int CalculateMaxLength(); void CalculateWrappedString(const FString& CurrentLineString); FString CalculateSegments(FString* OutCurrentRunName); From a0810d0781b82b6e6308bbb82036c636a7b86df0 Mon Sep 17 00:00:00 2001 From: lumpn Date: Thu, 20 Apr 2023 15:09:46 +0800 Subject: [PATCH 09/13] Fixed formatting --- .../Private/StevesUI/TypewriterTextWidget.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index 45d5e60..20c6ab2 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -134,20 +134,20 @@ void UTypewriterTextWidget::PlayNextLinePart(float Speed) void UTypewriterTextWidget::StartPlayLine() { - CalculateWrappedString(RemainingLinePart); + CalculateWrappedString(RemainingLinePart); - if (NumberOfLines > MaxNumberOfLines) - { - int MaxLength = CalculateMaxLength(); - int TerminatorIndex = FindLastTerminator(RemainingLinePart, MaxLength); - int Length = TerminatorIndex + 1; - const FString& FirstLinePart = RemainingLinePart.Left(Length); + if (NumberOfLines > MaxNumberOfLines) + { + int MaxLength = CalculateMaxLength(); + int TerminatorIndex = FindLastTerminator(RemainingLinePart, MaxLength); + int Length = TerminatorIndex + 1; + const FString& FirstLinePart = RemainingLinePart.Left(Length); - CalculateWrappedString(FirstLinePart); + CalculateWrappedString(FirstLinePart); - RemainingLinePart.RightChopInline(Length); - bHasMoreLineParts = true; - } + RemainingLinePart.RightChopInline(Length); + bHasMoreLineParts = true; + } FTimerDelegate Delegate; Delegate.BindUObject(this, &ThisClass::PlayNextLetter); From 59e0e5414f834870d41eeee76cf6807b4cb696f8 Mon Sep 17 00:00:00 2001 From: lumpn Date: Thu, 20 Apr 2023 22:42:27 +0800 Subject: [PATCH 10/13] Disabled line splitting by default --- .../StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp | 2 +- Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index 20c6ab2..a98672b 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -136,7 +136,7 @@ void UTypewriterTextWidget::StartPlayLine() { CalculateWrappedString(RemainingLinePart); - if (NumberOfLines > MaxNumberOfLines) + if (MaxNumberOfLines > 0 && NumberOfLines > MaxNumberOfLines) { int MaxLength = CalculateMaxLength(); int TerminatorIndex = FindLastTerminator(RemainingLinePart, MaxLength); diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index 7c1cac9..5e79515 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -84,9 +84,9 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") float PauseTimeAtSentenceTerminators = 0.5f; - /// How many lines of text at most to print at once. + /// How many lines of text at most to print at once. (disabled by default) UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") - int MaxNumberOfLines = 3; + int MaxNumberOfLines = 0; /// Set Text immediately UFUNCTION(BlueprintCallable) From e3da91dc7b0338e3044077f898e8c5d101e7c45b Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Thu, 20 Apr 2023 16:05:03 +0100 Subject: [PATCH 11/13] Safety check, in case this method is used out of context --- .../StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index a98672b..d1277e7 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -272,7 +272,7 @@ int UTypewriterTextWidget::CalculateMaxLength() if (Segment.Text.Equals(FString(TEXT("\n")))) { CurrentNumberOfLines++; - if (CurrentNumberOfLines > MaxNumberOfLines) + if (MaxNumberOfLines > 0 && CurrentNumberOfLines > MaxNumberOfLines) { break; } From ff35eefc88d36dd424ca2df5656254f8b2869458 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Thu, 20 Apr 2023 16:06:18 +0100 Subject: [PATCH 12/13] Improved documentation --- .../Public/StevesUI/TypewriterTextWidget.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h index 5e79515..ead9679 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h +++ b/Source/StevesUEHelpers/Public/StevesUI/TypewriterTextWidget.h @@ -84,7 +84,7 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") float PauseTimeAtSentenceTerminators = 0.5f; - /// How many lines of text at most to print at once. (disabled by default) + /// If set > 0, splits a single PlayLine into multiple segments of this number of lines maximum UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Typewriter") int MaxNumberOfLines = 0; @@ -96,19 +96,30 @@ 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); From 92f49949b600ebbbbab33dcde69709bf5650d581 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Thu, 20 Apr 2023 16:30:19 +0100 Subject: [PATCH 13/13] Fixed not counting embedded \n chars in CalculateMaxLength --- .../Private/StevesUI/TypewriterTextWidget.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp index d1277e7..8c70c16 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/TypewriterTextWidget.cpp @@ -269,6 +269,7 @@ int UTypewriterTextWidget::CalculateMaxLength() 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++; @@ -277,10 +278,6 @@ int UTypewriterTextWidget::CalculateMaxLength() break; } } - else - { - MaxLength += Segment.Text.Len(); - } } return MaxLength;