Skip to content

Instantly share code, notes, and snippets.

@MSGhero
Last active April 1, 2020 07:29
Show Gist options
  • Save MSGhero/d96928cd3370f76e44abb097c07ad80e to your computer and use it in GitHub Desktop.
Save MSGhero/d96928cd3370f76e44abb097c07ad80e to your computer and use it in GitHub Desktop.
Layout-aware type text that word wraps without bumping words down
// just contains excerpts and comments
// does not work on Flash. Fine on Windows and HTML5 at least
// involves diving into OpenFL's private API, so no guarantees this will work forever. But it works now!
// probably errors if you start fiddling with the textfield, like setting formats or appendText()
// SmartWordWrap.hx
class SmartWordWrap extends openfl.text.TextField { // gotta extend TextField to have access to internals
var layoutGroups:openfl.Vector<openfl._internal.text.TextLayoutGroup> = new Vector(); // this will cache character information
// ... etc code
// either override set_text or make a new function
function setFancyText(text:String):Void {
this.text = text;
// you want to set any textformats (bold, colors, whatever) HERE before continuing, or they will get erased or cause issues
__updateLayout(); // tells OpenFL to lay the text out into its final position. This is where the smart word wrap comes from since we will cache this info
var lg:TextLayoutGroup, newLG:TextLayoutGroup;
layoutGroups.length = 0; // clear the cache
// this breaks up the natural text layout groups (low-level text formats) into one per character
var j = 0, k = 0, delta = 0, offsetX = 0.0;
for (i in 0...__textEngine.layoutGroups.length) { // __textEngine.layoutGroups is the native array of text info, where the cache comes from
lg = __textEngine.layoutGroups[i];
delta = lg.endIndex - lg.startIndex;
j = 0; k = 0;
offsetX = lg.offsetX;
while (j < delta) {
// make a new layout group for each character, copying from the original layout group (aka cache text info)
newLG = new TextLayoutGroup(lg.format.clone(), lg.startIndex + j, lg.startIndex + j + 1); // clone the textformat so characters don't share
newLG.ascent = lg.ascent;
newLG.descent = lg.descent;
newLG.height = lg.height;
newLG.leading = lg.leading;
newLG.lineIndex = lg.lineIndex;
newLG.offsetX = offsetX;
newLG.offsetY = lg.offsetY;
newLG.positions = [];
newLG.width = lg.getAdvance(k);
offsetX += newLG.width; // so each character is aware of its correct position independent of surrounding characters
j++;
// this is for Unicode character on Neko and Mac and stuff. Not 100% sure it works
// skip buffer 0s, aka find the real character that contains width
while (k < lg.positions.length && lg.getAdvance(k + 1) == 0) {
newLG.positions.push(lg.positions[k]);
k++;
}
newLG.positions.push(lg.positions[k]);
k++;
layoutGroups.push(newLG); // gather all the layout groups into a new array that we cache for later
}
}
__textEngine.layoutGroups.length = 0; // delete all characters from the textfield's internal array of characters
// __dirty = true; // idk if this is needed. It's not in my code but I'm not sure why! If your text doesn't look empty at first, try this line
}
// here is your existing typing text code. You're probably doing textfield.appendText(), or even worse text += nextChar
// So, stop doing that. layoutGroups contains all the info for each character that the text renderer wants
// Instead of adding a character to the text string, we will add layout groups from the stored layoutGroups cache to the native __textEngine.layoutGroups
function addNextCharacter():Void {
if (__textEngine.layoutGroups.length < layoutGroups.length) { // if there are characters left to type
__textEngine.layoutGroups.push(layoutGroups[__textEngine.layoutGroups.length]); // push the next character from layoutGroups (cached character info) to __textEngine.layoutGroups (the real character info location)
__dirty = true; // VERY IMPORTANT!!! Tells OpenFL that the text needs to be re-rendered. It doesn't do this already because we bypassed the public API where this normally gets set
}
}
// also, feel free to fiddle with the characters in __textEngine.layoutGroups!
// set lg.format.color to change each character's color individually
// lg.offsetX and lg.offsetY are the positions of each character. You can move characters all over (wave effect, jitter, fly into place...)
// just know the overall TF has a crop on it that may cut off your flying letters
}
@intoxopox
Copy link

This is swell!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment