Typewriter Effect with Rich Text + *Correct* Text Wrapping

Overview

Typewriter Effect with Rich Text + Correct Text Wrapping

I've spent way too long getting this right. This is meant as a base class for a UMG dialogue box that uses a typewriter effect to display text. Meant for use with rich text - basic text boxes would use a simpler method involving Slate's font measurement system. If anyone is interested, I can post that version as well.

This is not meant to be used as-is as it uses some types specific to my project, but it should be simple to swap them out for whatever you want.

QY50iAnFHU

Known Issues/Limitations

  • Most if not all rich text features are supported, including basic formatting and custom decorators (and images!).
  • Changing font-size (or using a decorator that's taller than the existing text) anywhere except at the beginning of a line will result in the entire line "jumping" down slightly to accomodate the new text size. I don't have a solution to this yet and don't see myself using different font sizes much, so it isn't something I'm likely to get to any time soon.
  • Text wrapping is only calculated a single time when the first character is played. If the widget is resized for any reason, text will not respect the new boundaries. This should be simple to solve, I just haven't done it yet.
  • There may be some hidden i18n issues due to all the conversions between FString/FText and string indexing.
  • Some types (such as FTalkLine) are from my own dialogue system. It should be trivial to swap them out.
  • This has been tested with UE5.0EA, though it should work fine with earlier/later versions.
  • The current implementation was quickly thrown together (see: hacky) and somewhat unoptimized. Some data is duplicated more than it needs to be, and "segment" calculation is a bit more complex than I'd like.

What's wrong with the "naive" approach?

There are two issues - one that affects any sort of typewriter effect with text wrapping, and one that specifically effects rich text (at least as it is implemented in Slate).

1. Auto Text Wrapping

The problem with just using auto text wrapping is that words towards the end of a line may "jump" to the next line as they are typed out. For example, take this "dialogue box":

+----------------------+
| Hello, world! This   |
| dialogue box is cool.|
+----------------------+

While the word "dialogue" is being written, it can appear like this:

+----------------------+
| Hello, world! This di|
|                      |
+----------------------+

but as soon as the next letter is written, "dialogue" will jump to the next line due to being too long:

+----------------------+
| Hello, world! This   |
| dia                  |
+----------------------+

This is jarring, especially if the reader is reading at the same speed that the text is being typed out. The general fix is to precalculate where the text needs to wrap, and then follow those line breaks instead of calculating new ones every time the effect adds a new letter.

With normal text blocks this is (mostly) simple: use the slate font measurement service to figure out how long text is and programmatically insert newlines until it fits within the bounds of your dialogue box. Rich text requires some more trickery due to supporting arbitrary numbers of fonts, sizes, weights, and even images.

2. Rich Text Tags

Rich text adds in another level of complications - you can't just iterate over the text to display it, as you'll end up with partial markup tags. Take this string:

Hello, World!

If you were to use a "naive" typewriter effect, you'd end up sending partial tags at various points during the effect. A couple of examples:

  • Hello, (what's doing there?)
  • Hello, World (missing the closing tag)

Unreal's rich text makes this even worse by outright not rendering sections of "bad" markup, meaning that world will suddenly pop into existence when the closing tag is written rather than smoothly appearing letter-by-letter.

The Solution

My current solution is a bit hacky and I haven't taken the time to optimize it (yet - there are some obvious areas that I may go back and fix). The basic idea is to hook into the rich text block's creation of its FSlateTextLayout and FRichTextLayoutMarshaller which, together, is what figures out how to parse and draw rich text on the screen.

Once we have pointers to both of those, we can briefly ask them to do some layout work for us on the final string we want to print, grab the different "runs" that make up the layout, then return control back to the rich text widget. We can then use the information about how the layout is going to work to insert our own newlines and move the markup tags around.

This would be much simpler if there were APIs to pass pre-parsed text data into a rich text widget, but no such APIs exist without maintaining our own separate copy of it (which is something I might do in the future, but this was much quicker to implement).

Alternative Method (that would be better but doesn't work)

The main alternative method that I tried was implementing IBreakIterator and passing it in to the text layout. This (theoretically) lets us tell the layout where it is allowed to insert a line break. We can use the normal line break iterator to pre-process the entire text and decide where line breaks should go. We store that line break data and then instead of processing the incoming text from the layout as we're running the effect, we use the cached line break information from earlier.

This would work if it wasn't for one sort-of-bug with text layout - if you don't tell the text layout it can place a line break at the final character of a string then it never ends up drawing that final section.

You might also like...
This is no malware, This is no virus. This is my implementation of the effect from Mrs.Major3.
This is no malware, This is no virus. This is my implementation of the effect from Mrs.Major3.

BloodMelter This is no malware, This is no virus. This is a very small effect of very small blood for a some PC. Table Of Contents Preview About Warni

Project to create a teensy based gamecube controller with hall effect sensors, snapback filtering, and notch calibration
Project to create a teensy based gamecube controller with hall effect sensors, snapback filtering, and notch calibration

PhobGCC Gamecube controller motherboard using a teensy as the microcontroller. Aim is to make an accessible and consistent controller. Has the option

Unix pager (with very rich functionality) designed for work with tables. Designed for PostgreSQL, but MySQL is supported too. Works well with pgcli too. Can be used as CSV or TSV viewer too. It supports searching, selecting rows, columns, or block and export selected area to clipboard.
Unix pager (with very rich functionality) designed for work with tables. Designed for PostgreSQL, but MySQL is supported too. Works well with pgcli too. Can be used as CSV or TSV viewer too. It supports searching, selecting rows, columns, or block and export selected area to clipboard.

Unix pager (with very rich functionality) designed for work with tables. Designed for PostgreSQL, but MySQL is supported too. Works well with pgcli too. Can be used as CSV or TSV viewer too. It supports searching, selecting rows, columns, or block and export selected area to clipboard.

 TIDAL - Discord Rich Presence plug-in (UNOFFICIAL)
TIDAL - Discord Rich Presence plug-in (UNOFFICIAL)

TIDAL - Discord Rich Presence plug-in (UNOFFICIAL) Unofficial plug in to obtain Discord Rich Presence. Feel free to report any bugs or make suggestion

Simple, fully external, smart, fast, JSON-configurated, feature-rich Windows x86 DLL Memory Dumper with Code Generation. Written in Modern C++.

altdumper Simple, fully external, smart, fast, JSON-configurated, feature-rich Windows x86 DLL Memory Dumper with Code Generation. Written in Modern C

Discord Rich Presence for Flutter & Dart.
Discord Rich Presence for Flutter & Dart.

dart_discord_rpc Discord Rich Presence for Flutter & Dart apps & games. Install Flutter dependencies: .. dart_discord_rpc: ^0.0.1 Dart CLI depende

An incomplete CS:GO internal with a rich SDK

🛰 About This is an internal project I was working on about a year ago. The features are incomplete because I spent most of my time reversing interfac

Text - A spicy text library for C++ that has the explicit goal of enabling the entire ecosystem to share in proper forward progress towards a bright Unicode future.

ztd.text Because if text works well in two of the most popular systems programming languages, the entire world over can start to benefit properly. Thi

Jittey  - A public domain text editor written in C and Win32
Jittey - A public domain text editor written in C and Win32

Jittey (Jacob's Terrific Text Editor) is a single-file basic text editor written in pure C and Win32, there is no real reason to use it, but it

Owner
Sam Bloomberg
Software Engineer at 343 Industries / MS Studios Quality
Sam Bloomberg
Macro magic for declaring/calling Objective-C APIs from C11 or C++. Preloads selectors, chooses the correct objc_msgSend to call per method/platform.

OC - Easily Declare/Invoke Objective-C APIs from C11 or C++11 Usage // Call class and instance methods: NSWindow* const nswindow = oc_cls(NSWindow,new

Garett Bass 46 Dec 24, 2022
MSP external wrapping stb_hexwave.h

hexwave_msp MSP external wrapping stb_hexwave.h by Sean Barrett. see the included maxpat for details. arguments: freq (float) reflect (bool, default 1

miunau 3 Aug 3, 2021
Robust multi-prompt delimited control and effect handlers in C/C++

libmprompt Note: The library is under development and not yet complete. This library should not be used in production code. Latest release: v0.2, 2021

Koka Language and Related Tools 94 Dec 27, 2022
No loss LV2 sound effect plugin

B.Spacr Description: LV2 sound effect plugin B.Spacr is a unique LV2 effect plugin that enables a clear and brilliant audibility of your music product

null 14 Aug 24, 2022
Acrylic & aero blur effect on Flutter Windows 💙.

flutter_acrylic Acrylic & aero blur effect on Flutter Windows. Installation Mention in your pubspec.yaml.

Hitesh Kumar Saini 441 Jan 8, 2023
Demonstrates implementation of the Windows 10 Acrylic Effect on C++ Win32 Apps using DWM Private APIs and Direct Composition

Win32 Acrylic Effect A Demonstration of Acrylic Effect on C++ Win32 applications using Direct Composition and DWM private APIs. Table of Contents Over

Selastin 132 Dec 21, 2022
Create a firework effect with WS2812b LED and a MCU

LED-Fireworks Firework effect with WS2812b LED and a MCU This project uses FastLED library to control the LED strip. WS2812b IC is embedded into each

null 75 Dec 5, 2022
Acrylic effect for all existing Win32 context menus

AcrylicMenus This is a proof-of-concept tiny application that applies acrylic effect to almost all existing Win32 context menus on Windows 10 and Wind

null 411 Jan 1, 2023
IPlug "Tale" edition example (tremolo effect).

IPLUG EXAMPLE A simple tremolo audio effect plugin (AU/VST2), serving as an example for the IPlug "Tale" edition plugin framework. GETTING STARTED

null 3 Dec 8, 2022
A fork of the kwin blur effect that solve the corners bug.

Kwin blur effect - Respect rounded corners This kwin effect is a fork of the default kwin blur effect, with minimal changes to solve the "plasma korne

Alban Boissard 101 Dec 29, 2022