Lately I’ve been working on a set of projects (both my own and others) that are built on top of Moccasin, a new open source AS3 framework that I’ve developed for building graphical editors. Moccasin was distilled and refactored out of the first project that used these ideas so that I could use it elsewhere without IP issues, and it lives on Google Code. Moccasin still has a long way to go in terms of overview documentation and I have to ask a certain amount of forgiveness on that subject. I’m perhaps releasing it a bit prematurely, but I regard it as pretty useful in a practical sense since I’m now working on the third substantial application built on top of it. This post is an attempt to bite off a little bit of that overview problem and endow Moccasin with a little more value to other people.
If the current lack of great overview docs is the bad news, well, the good news about Moccasin is that it’s got a complete sample editor application called Simpleworld as part of the distribution, with both AIR and Flex versions. Both the Moccasin framework and the sample application source code are pretty well commented, also. Here’s the Simpleworld application itself, to give a concrete sense of what Moccasin’s all about — it’s an editor for documents that consist of a bunch of squares of different sizes (I don’t call this world “simple” for nothin’):
Here are some of the things you can do, which show off what Moccasin’s about…
That’s the externally visible stuff; what about the internals, though? Moccasin is a framework for building applications, not a single application. Some of the above features are built right into Moccasin (like undo/redo or magnification), while others are implemented on top of it (like square objects, or the ability to resize them with a draggable handle).
Moccasin is not a “thin” framework that encapsulates a set of recommended practices like Cairngorm. It’s more of a “thick” framework. Moccasin is aimed at building a particular type of app: one with interactive graphical views that support a lot of mouse gestures, have some notion of compound or nested views, have some notion of a current selection, a clipboard for cut/copy/paste, need robust undo/redo, and must load and save documents to persist the editor state. The views in question are generally not Flex UIComponents in Canvases or Boxes, but lower-level Flash DisplayObjects that can have very arbitrary shapes and layout geometries. Within this domain, Moccasin very much adheres to an MVCS way of thinking: there is a model representing the edited document’s state, a set of views that adjust to the model’s state via events, a controller which changes the model in response to user actions, and a set of services responsible for talking to the world around and outside the application.
Let me devote the rest of this post to a few observations on Moccasin’s treatment of the Model/View relationship. I’ll try to keep up a series of posts over the coming weeks and, with some luck, accumulate some overview materials in fits and starts. Probably the only way it’s going to happen given my schedule these days.
The way Moccasin treats Models and Views is somewhat unique (where by “unique” I mean, “I never tried it before” — I’m sure someone else has!). As usual, one goal here is to absolutely separate view objects from models: views can reference models, but never the other way around. This not only keeps the architecture clean, but it is absolutely essential to permit multiple views of the same model. A second primary goal is that any change to the model should cause the view to adjust itself accordingly without additional effort by the programmer. This involves more than just data bindings: creating a model should cause the view to be added to the display list, and removing a model from the document must cause the view to vanish.
A common approach to model objects in a graphical editor is to make them inherit from some big old AbstractModel superclass with a boatload of common code in it. This code consists of all the gears and wires to dispatch the right events when the model changes, or when its set of children changes, or when some descendant model changes, or to generate a stable ID for use in document persistence… a lot of junk.
By contrast, here is a SimpleWorld-style class (with comments removed for brevity):
public class Square
public var x:Number;
public var y:Number;
public var size:Number;
Hey, what’s up? This model class has no methods and extends… Object! (Actually, EventDispatcher, due to the Bindable tag, but we’re splitting hairs).
In Moccasin, there are actually two sets of model objects: the domain-specific model (that application programmers work with) and the framework model (that Moccasin deals with). The developer of a Moccasin app only writes code against the domain-specific model, and Moccasin automatically wraps that model object inside another object of type MoccasinModel. MoccasinModel is where the “framework junk” lives, but the application programmer doesn’t see it.
There is also going to be a corresponding View class to draw a Square on the screen — SquareView. Here’s a sense of what a view class looks like:
public class SquareView extends ShapeView
public function SquareView(context:ViewContext, model:MoccasinModel=null)
override protected function updateView():void
var square:Square = model.value as Square;
graphics.drawRect(0, 0, square.size, square.size);
override protected function createFeedbackView():DisplayObject
return new SquareFeedback(context, model);
Because of the strong decoupling between the view, the framework model and the domain-specific model, application programmers can wind up writing very pure code to manipulate the model that doesn’t have to have any dependencies on Moccasin. You can say something like mySquare.x += 100, and this will cause the framework to adjust the corresponding SquareView object to be redrawn in a different position. Changing an ‘x’ property doesn’t seem very exciting, but consider that it’s not SquareView’s ‘x’ property being changed: it’s the Square’s property. In fact, the relationship between model properties and view properties can be quite arbitrary. We could have some other view of our Squares besides a 2D rendering, perhaps a numerical SquareCoordinatesTable with an X column, and a Y column, and Moccasin would manage that model/view relationship in much the same way.
It’s getting near my lame-old-guy bedtime and I’m flagging. I feel better for having squeezed this post out at the end of a long day though; now to keep it going! Next probable topic: Mediators, Contexts and Controllers in Moccasin.