This is part 2 in an ongoing series about the Flash Text Engine. You can read part 1 here.
To clarify, this series isn’t about Adobe’s Text Layout Framework, which is an advanced typography and text layout framework. The Flash Text Engine is the low-level API that TLF is built on. In Flash Player 10, the FTE resides in the flash.text.engine package.
Interaction in the Flash Text Engine
In my previous post on the Flash Text Engine, I ran through the basics of what you need to get the FTE to render TextLines. While rendering lines on the screen is nice, this post is about how to add interaction to the TextLines that are produced.
TextLines are InteractiveObjects. You can add event listeners directly to them and listen for interaction events. The FTE also gives you the option to associate an individual EventDispatcher instance with a single ContentElement, so that when the user interacts with the data of the ContentElement, the events are cloned to the EventDispatcher instance you specified. As I discuss the details, you’ll see that each approach has its own strengths and weaknesses.
Approach 1: TextLines as InteractiveObjects
Since TextLine is an InteractiveObject, you can simply listen for Mouse and Keyboard events on each TextLine instance. With this approach, you know the TextLine that was interacted with. The main drawback here is that TextLine knows almost nothing about the ContentElement which it is rendering. Multiple ContentElements can be rendered into the same TextLine, and multiple TextLines can render the same (really long) ContentElement.
Interact with the lines in this demo:
Source
The fact that you don’t know about the content of the TextLines is ok though, for some problems that isn’t necessary. For example, you don’t really need to know about the contents of the TextLines to draw decorations, such as underline, strikethrough, or selection.
Select this text:
Source
Approach 2: Working with TextLineMirrorRegions (TLMRs)
The preferred method of managing interaction in the Flash Text Engine is with TextLineMirrorRegions.
If you read my previous post, you’ll remember that to render any text, you have to create instances of any of the Flash Text Engine’s model classes: TextElement, GraphicElement, or GroupElement. When you create an instance of these classes, you can specify an EventDispatcher as the eventMirror for the ContentElement. When the user interacts with the visual representation of this ContentElement via TextLines, the events are re-dispatched to the eventMirror you specified. This allows you to know when a user interacts only with a particular ContentElement.
In this code sample, I create an EventDispatcher to pass in as the eventMirror for the TextElement. Then I add a listener for mouseMove on the eventMirror instance. This will trace out every time you mouse over the TextElement.
var dispatcher:EventDispatcher = new EventDispatcher(); new TextElement('Inspiring quote here.', new ElementFormat( new FontDescription()), dispatcher); var onMouseMove:Function = function(e:MouseEvent):void{ trace('Mouse move on ' + e.target.toString()); } dispatcher.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
These two lines are part of the same TextElement:
Source
How is this different from the previous demo? TextLine has a property called mirrorRegions, a Vector of TextLineMirrorRegion instances. Since multiple ContentElements can be rendered by a single TextLine, TextLine creates TLMR instances for each ContentElement with an eventMirror, then associates the TLMRs with the eventMirrors respectively.
TextLine listens on itself for interaction events. When the events overlap with any of the TLMRs, TextLine notifies the appropriate TLMR of the event. After all normal event processing for the TextLine is done, each TLMR re-dispatches the events it was notified of to its eventMirror instance.
In this example, I added a listener for the “mouseDown” event on both the TextLine and the ContentElement’s eventMirror. Notice that the event dispatched on the eventMirror happens second.
Source
Here’s what the TLMRs look like (I’ve drawn boxes for each boundary of a TextLineMirrorRegion).
Source
Caveats
Of course, this wouldn’t be a Flash Player feature if it didn’t come with caveats ;).
TextLineMirrorRegion simulates the events, it doesn’t re-dispatch the exact instance it received from the TextLine. This is because TLMR isn’t an InteractiveObject itself. If you utilize the eventMirror to listen for MouseEvents, just realize they’re all faked — even though TextLine is the target, they didn’t originate from TextLine, and they don’t have feelings like real player-native events do.
rollOver/rollOut events
This event simulation means that we’re at the mercy of what Adobe chooses to simulate. They didn’t feel the need to simulate the roll events (rollOver/rollOut), so if you try to listen for them on the eventMirror, you won’t get them. Presumably this is because the roll events aren’t needed; since ContentElements don’t have display-list children, the roll events would be exactly the same as mouseOver/mouseOut.
Except the roll events are still very relevent.
It’s true, we’ve shifted from a display-list hierarchical structure (DisplayObjectContainers, etc.) to a ContentElement hierarchical structure. And it’s true, ContentElements don’t have display-list children. But they can have other ContentElement children, which means the roll events are still very relevant.
For example, if you had this XML model to render:
<p> Outside the group. <group> <text color="#44AA00"> First group child. </text> <text color="#AA0044"> Second group child. </text> </group> Outside the group. </p>
You might want to know only when the entire group node is interacted with (just like when you have a DisplayObjectContainer with children).
Here’s the demo of this model. Mouse between the boundary of the first child and the second child, and notice how you get a “mouseOut” and then another “mouseOver” from the group. If this were the roll events, you would only get the “mouseOut” and “mouseOver” from the children, but hear nothing from the group. FYI, “mouseDown” clears the debug lines.
Source
Comparison
So, how do the two techniques match up? The short answer is that each one accomplishes a different task. If you need the very base of interaction capabilities without the context of what you’re messing with (e.g. text selection), adding listeners straight on your TextLines is the way to go. However, if you need the context of which ContentElement the user interacts with (e.g. to mimic an HTML anchor tag), there’s no way around it, you have to use the event mirroring approach.
P.S. Isn’t it freaky interacting with text that you can’t select? Maybe I’m just OCD, but I feel a strong desire to see an I-Beam mouse cursor every time I hover over FTE text. My favorite demo to write was the second one, because not only did I get to come up with a quick selection-drawing method, I added the freakin’ I-Beam cursor. Anyway, hope you enjoyed this and good luck :).
Tags: Flash Text Engine, FTE, fte interaction, tiny tlf, tinytlf



[...] This post was mentioned on Twitter by . said: [...]
I’ve put together my own FTE-based interactive textbox, based on a lot of the ideas you’ve presented here. I’m really happy with the result; I’ll have it online sooner or later.
One big remaining issue is memory usage. Every time my class has to redraw its text contents, I end up scrapping the existing TextLine and making a new one. This stuff really piles up. Have you got any ideas for somehow conserving memory with the FTE classes?
One I idea I have involves making a TextLine for each character in a monospace font, pooling TextLines on a per-character basis, and assembling the text in the display list. Humongous hassle, but for numeric text input it may be worthwhile.
Thank you for the series of insightful post on the internals of FTE.
So, we can add selection operation to FTE w/o relying on Text Layout Framework in a simple way, that’s great!
By doing so, we can save on memory and performance, while supporting all the requested functionality.
Bye-bye TLF in our project, but please welcome FTE.
please tell me what are the advantages of using FTE over a simple TextField with htmlText.
We must write a LOT of code, there’s no selection, IBeam cursor, copy in context menu, our SWF is 200KB for just one line of text! Also you example with selection consumes 20% of my CPU when I’m selecting!
This seems a really nightmare :S