Moccasin: a Flex framework for graphical editing apps
September 30, 2008 on 5:41 pm | In Flex, Programming | 14 CommentsLately 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…
- load and save XML documents on the server. View the one that’s loaded by the above example here.
- Click any square to select it singly, Ctrl/Command-click squares to make discontinuous selections
- Click on the background to add a new square to the world
- Drag any selected square to move all selected squares together
- Drag the mouse across the background to select all squares intersecting that rectangle
- Delete, Cut, Copy or Paste any selected objects
- Resize any individual square by dragging its resize handle (generalizes easily to multiple object handles)
- Undo/redo the last N editing actions
- View the graphical document at any desired magnification using the zoom slider
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):
[RemoteClass]
[Bindable]
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)
{
super(context, model);
initialize();
}
override protected function updateView():void
{
super.updateView();
graphics.beginFill(0);
var square:Square = model.value as Square;
graphics.drawRect(0, 0, square.size, square.size);
graphics.endFill();
}
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.
14 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a comment
Entries and comments feeds.
Valid XHTML and CSS.
All content copyright (c) 2006-2007 Joseph Berkovitz. All Rights Reserved.
That’s really interesting and I didn’t know about the project until now. I’m looking forward to seeing more about Moccasin, as it might be a good basis for (the graphing part of) an app I’m just starting. Cheers!
Comment by Josh McDonald — September 30, 2008 #
Hey Joe,
This is great – just diving into the source now, to see if it might integrate with a framework of my own, but I’m very excited about the possibilities.
Thanks!
Ian
Comment by Ian McDonald — October 2, 2008 #
hey Joe,
You probably won’t believe this, but I started working on something similar like a couple weeks ago, basically trying to figure out a framework suitable for graphical editing, with MDI, document management, undo/redo, and of course, the editor itself.
I’m having a look at the source right now, just want to say a big “thank you” for sharing this :)
Jeremy.
Comment by Jeremy Lu — October 6, 2008 #
[...] Moccasin: a Flex framework for graphical editing apps (from joeberkovitz.com) [...]
Pingback by Flex Monkey Patches » Blog Archive » Rubbernecker’s Review - Week 15 — October 7, 2008 #
Joe,
Very nicely done and useful framework. A question for you – I’ve been working on a graphical editing app and was using ObjectHandles framework to enable handles on objects and subsequnt dragging, sizing, rotation. The OH framework goes a little further on manipulation of object properties but it utilizes a wrapper type approach, much like Moccasin. I’m not sure if you looked at OH but do you have any suggestions on an approach to use both frameworks to complement each other? As it is, OH wants to be the outermost object wrapper so the graphical handles can match the extents of the underlying shape (usually a flex container holding whatever) but I don’t know how I can wrap a moccasin model, or for example a collection of objects selected by a drag selection and wrap with OH to allow the property mods. Is this viable or would you suggest extending moccasin to allow rotation handles and rotation mouse event handling and further extend it to allow those functions for multiple selected graphical objects?
thanks for any suggestions on this,
Danny
Comment by Danny — October 13, 2008 #
Danny, it’s great that you bring this up — Marc Hughes and I were talking about how OH and Moccasin could fit together a month ago, when he did a presentation on his stuff at the Boston Flex User Group. I love what he’s done with OH but at the end of the day I think it is hard to fit the models together, so I have gone in the direction of adding OH-like functionality to Moccasin. In fact, if you look in Moccasin’s view package you will find SelectionHandle which is a basic selection-handle class. The SimpleWorld example uses SelectionHandle so you can see how to use a DragMediator to make SelectionHandle do something useful. I’ve also added rotation handles and edge/corner handles to an app I built on top of Moccasin, but unfortunately that code isn’t open source so I need to redo it.
What I would really like to do (and what Marc has done a wonderful job of) is have a cool hookup between Moccasin and Degrafa. There’s probably a good way to do that without disrupting anything but I haven’t had time to check it out.
Comment by joe — October 13, 2008 #
Hey Joe,
I’m really impressed with your framework. I’ve managed to figure out quite a bit. One thing I’m working on at the moment is a graphical presentation editor. I can’t seem to figure out how to make an app that goes one model deeper i.e.
-> Moccasin -> Presentation -> Slide. I can get the editor functionality working on the PresentationView but I’d ultimately like to extend it to the SlideView. Problem is when I create a new Presentation Model it needs to automatically create a new slide model and attach it.
Hopefully all of that makes sense? Essentially all I want to do is take your simpleworld example and add another layer. e.g.
Moccasin -> Universe -> World -> Square
Cheers,
Rob
Comment by Rob Morgan — November 14, 2008 #
To get this to work, you need to do a couple of things:
1. your Presentation object needs to have a “children” ArrayCollection property, each of which is a Slide. (If you don’t call the property “children”, you need to supply a MOCCASIN_CHILDREN_PROPERTY with the name you used.)
2. override PresentationView’s createChildView() method to create a SlideView() for a new Slide child model.
3. when you create your new Presentation, add a Slide to it. This should cause the SlideView to be created.
Note that if you’re trying to be like PowerPoint or Keynote, you might not actually want your presentation views to be in the MoccasinView along with the slides. In that case you might want to make the SlideView the root model object of your MoccasinView and show the Presentation views somewhere else, like as items in an mx:List component.
Comment by joe — November 14, 2008 #
[...] from this description by the author of moccasin, Joe [...]
Pingback by moccasin – flex graphical framework « Fiji Ecuador Seattle Greece Montana — May 12, 2009 #
Hey Joe,
I’ve been using the framework for a week now and I like it a lot (although not at first because of no docs haha). Anyways I was thinking about implementing grouping of different view into one view object so I could manipulate that one object (just like photoshop does), but before I get into it, I just wanted to know if that functionality is already in the framework?
Best Regards,
Able
Comment by Able — July 22, 2009 #
Joe,
first of all thanks for the great work! I’ve been playing with moccasin for a while and it looks really well thought.
Is there any simple way I can specify that some of the shapes are not even selectable? In my current scenario, I need to “reopen” a document and make sure that the pre-existing shapes are not modified, so that, for example, I can add a new shape, edit it, but not delete or move or change in any way the original ones.
Unless I’m missing something really obvious, what is the best way to handle this in moccasin?
Thanks!
Comment by Luca — August 20, 2009 #
Probably the best way is to put the SelectionMediator attachment in SelectableView.initialize() inside an if() statement that depends on some property of the model.
Comment by joe — August 20, 2009 #
Thanks, Joe, it worked! :)
Comment by Luca — August 21, 2009 #
Thanks for the talk @ FITC… really inspiring stuff and helpful.
Comment by cm — September 21, 2009 #