Last active
April 1, 2020 07:29
-
-
Save MSGhero/d96928cd3370f76e44abb097c07ad80e to your computer and use it in GitHub Desktop.
Layout-aware type text that word wraps without bumping words down
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is native text manipulation! No extra memory or CPU beyond storing the cache and fiddling with the layout groups.