In my previous post I posed the problem of using the new Text Layout Framework in an application where you already have an Undo stack/mechanism, for example in PureMVC with the Undo utility (CommandHistoryProxy).

The challenge arises because the TLF already has an UndoManager, so the solution must hook into that. Added to that, in an application you’re likely to need to serialize your TextFlow to disk/server, which means de-serializing it, which means you’re storing your data outside of the TextFlow instance and needing to keep this store up to date as the user interacts with the TextFlow via the EditManager provided by the TLF.

I was getting hung up on my application using just 3 (undoable) commands such as SetPropertyCommand and was hoping I could fit the TLF editor in with this because these commands had coped well with ALL other user operations so far.

To give some background to those unfamiliar with PureMVC, my code path is typically:

  1. User interacts with something inside an editor view e.g. moves an image.
  2. View dispatches a SetPropertyEvent (contains target, prop, newValue, oldValue).
  3. EditorView Mediator picks up the event and sends a SET_PROPERTY notification.
  4. Facade hears that notification and executes the SetPropertyCommand which is undoable and so automatically finds its way to the top of the Undo stack so that the user can hit Undo from anywhere.

So that worked fine for everything from moving, scaling, picking colours and files for image or video controls, even adding and removing new things to the stage, you can undo/redo hundreds of these in any order. Basically most user operations can be summarised as setting a property on a target entity. But it won’t work with the Text Layout Framework for the reasons mentioned in my previous post, you can’t just set properties, you must use the methods provided.

The solution is to add another event/command pair similar to the SetProperty combination, this one is called ApplyTextFlowOperation(Event/Command) and crucially, it uses the FlowOperation class provided by the TLF.

The command looks like this:


public class ApplyTextFlowOperationCommand extends UndoableCommandBase
{
	public function ApplyTextFlowOperationCommand()
	{
		// if you prefer to have a separate undo class (not required)
		// registerUndoCommand(UndoApplyTextFlowOperationCommand);
	}
		
	override public function executeCommand():void
	{
		var op:FlowOperation = getNote().getBody() as FlowOperation;
		var em:EditManager = (op.textFlow.interactionManager as EditManager);
		em.doOperation(op);
	}

	override public function undo():void
	{
		var op:FlowOperation = getNote().getBody() as FlowOperation;
		var em:EditManager = (op.textFlow.interactionManager as EditManager);
		em.undo();
	}
}

Slot this into the PureMVC architecture the same as you would any undoable command and all works fine, you can apply TextFlow operations and undo them via your regular undo button (for example hooked up to an UndoLastCommand command).

The missing piece is when to write raw XML markup from the TextFlow to your “real” model, ready for serialization. I do this automatically by detecting changes to the TextFlow inside my model (in my case a TextElement class that represents any “text fields” the user has created on screen, which stores other properties like alpha, creation date for offline-syncing and so on).

You can listen for changes to a TextFlow via:

FlowOperationEvent – dispatched when the user typed/edits a TextFlow.
CompositionCompletionEvent – dispatched when you apply a FlowOperation, such as the ApplyFormatOperation in the example above.