Text Layout Framework Follow Up PureMVC Solution

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 a…

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.

3 thoughts on “Text Layout Framework Follow Up PureMVC Solution”

  1. Another possibility is to implement IUndoManager yourself and supply that impl to the EditManager constructor. Your IUndoManager would then called directly by TLF and can add TLF operations into a mixed undo stack.

    That’s how we intended it to work.

  2. Thanks for that Richard, I hadn’t considered that I could slot another IUndoManager in (I guess I still have to work with TLF a lot more before I’m familiar with all the interfaces/classes.)

    That would provide us a with way of using an intermediary IUndoManager to wrap TLF’s Operations into Commands that the mixed undo-stack can work with, in that case I think I’d still end up with an ApplyFlowOperationCommand so it ends a up fairly similar solution, but perhaps that’s a cleaner route overall.

  3. In the example code I’m executing the FlowOperation (stored in the Notification body) via the EditManager. You *must* execute any TextFlow operations (formatting or otherwise) via the EditManager to preserve undo inside the Text Layout Framework.

    However, I didn’t actually need to apply the FlowOperation inside my undoable Command itself…

    In fact now I use the EditManager directly in my application… anything from simply editManager.applyFormat(…) calls, to editManager.begin/endCompositeOperation() for complex groups of operations that I want to treat as 1 single edit.

    I then listen for FlowOperationEvents which are dispatched each time you use the EditManager on the TextFlow. When I hear these, I simply dispatch an event to execute my ApplyFlowOperationCommand via PureMVC, which stores the FlowOperation in the notification body (as per this code), but the difference this time is the command has nothing inside its execute method because the EditManager has already performed the action. But the undo() method remains the same.

    Two ways to skin this cat, but I found this second method to be easier to read as your EditManager operations all take place in situ.

Comments are closed.