Friday, March 14, 2008

Top Drawer, Part II

Q: Why do graphics geeks write vector art applications? A: We're just drawn to it.

Q: Why couldn't the blank canvas get a date? A: He didn't have any good lines.

Q: Why did the rectangle go to the hospital? A: Because it had a stroke.

Q: Why is TopDrawer sluggish when the canvas is empty? A: Because it's out of Shapes.

Welcome to the second installment of TopDrawer, a series in which I describe how a simple vector-drawing application was implemented in Flex.

Here's the application again, in case you missed it last time:

In Part I, we went through the GUI layer of the application, stepping through most of the code in TopDrawer.mxml. This, time, we'll take a look at the ActionScript3 class ArtCanvas.as, which is where most of the application logic for TopDrawer is found, including the mouse input that handles creating and editing the shapes on the canvas.

ArtCanvas.as

The source code for this file can be found here; you may find it helpful to download it and view it in a separate window, as I'll only be showing snippets from the file below as we walk through it.

ArtCanvas is a custom component, a subclass of UIComponent, which is the base class of all Flex components (and the class which you might usually want to subclass for custom components of your own if you don't need any of the specific capabilities of the existing subclasses). The ArtCanvas component is the visual area in which the shapes are drawn and edited. It handles both the display of the objects (through other classes that we'll see later) as well as the mouse and keyboard events that drive the creation and manipulation of the shapes.

Architecture

Functionality

The functionality of ArtCanvas is fairly simple:

  • Properties: The properties of ArtCanvas determine the type of shape being drawn, the properties of that shape, and the gridSnap properties that affect how drawing occurs.
  • Creation: Through handling mouse events, ArtCanvas creates shapes as the user drags the mouse around on the canvas.
  • Selection: Mouse events also result in selecting and deselecting objects for editing.
  • Editing: This is perhaps an optimistic term for what the user can do with the shapes in this very simplistic application, but selected shapes can be either moved (through mouse dragging) or deleted (through keyboard events or clicking on the Clear button).
  • Rendering: The canvas has a simple white background that ArtCanvas creates with a small display list. All other rendering (of the shapes on the canvas) is handled by the shape objects themselves.

Standard Overrides

A custom component like ArtCanvas that subclasses directly from UIComponent will typically override the following four methods:

        override protected function createChildren():void   
        override protected function commitProperties():void
        override protected function measure():void
        override protected function updateDisplayList(unscaledWidth:Number,
                unscaledHeight:Number):void

These methods are all called during specific phases during the lifecycle of any component and are the appropriate places to perform certain operations for the component:

  • createChildren(): This method is called during the creation phase of the component. If the component is a container for other child components, this is a good place to create them. Note, however, that if children of this component are created dynamically, or otherwise not a permanent feature of this component, then it is not required to create them here. For example, in our case the drawing shapes will be children of the canvas, but none yet exist when the canvas is created, so there is no need to create any children at this stage. So ArtCanvas does not bother to override this method.
  • commitProperties(): This method is called prior to updating the rendering of this component during a frame, if any properties have changed. Imagine a component with several properties, some of which might cause a significant amount of work to update the state of the component. In this case, it might be advantageous to update the state of a component all at once, based on the cumulative effect of all property changes since the last frame. In the case of our simple canvas component there is no need for this, so once again we get away with not needing to override this method.
  • measure(): This method is called prior to rendering the component during a frame if anything has changed that may affect the size of your component, to make sure that Flex knows the preferred size of your component. Some components may base their display size on some internal state, such as the amount of text within a button, or the size of an image, or other factors which the Flex rendering system may not know how to calculate for you. In these cases, your component needs to set some internal UIComponent sizing values. Yet again, we have work here for ArtCanvas; the size of the canvas is completely defined by the container of the canvas (which Flex controls), so we have no preferred size to communicate to Flex and do not need to override this method.
  • updateDisplayList(): This method is called prior to rendering the component during a frame, when the component needs to change the way it is being drawn. Note that because Flash uses a display list to render objects, this method will only be called when the rendering of an object needs to be updated. During normal frame processing, Flash already knows how to draw the component. We actually do have some custom rendering for our component, so we did not escape this time and we do override this method and draw our component accordingly. We'll see this simple code below.

Event Handling

Objects can choose to listen to events that occur in the system, such as mouse and keyboard events on components. For any events that they want to handle, they call addEventListener() and point to a function that will be called when any of those events occur. In the case of ArtCanvas, we handle both mouse events (on the canvas itselft) and keyboard events (in the overall application window). For any event, we can get information from the Event object to find out what we need: x/y information for mouse events, keyboard characters typed for keyboard events, and so on.

The Code

Now that we understand what ArtCanvas is trying to do, let's step through the interesting parts of the code of ArtCanvas.

We Got Style

There are a couple of styles used by ArtCanvas, which are declared outside of the class:

    [Style(name="gridSize", type="int", format="int", inherit="no")]
    [Style(name="gridSnap", type="Boolean", format="Boolean", inherit="no")]

These styles control an invisible grid on the canvas that the cursor will "snap" to during drawing. This feature can be useful for making more exact drawings where objects need to line up and typical mouse movement precision makes that difficult. These metadata tags ("[Style...]") communicate to Flex that this class can be styled in MXML to affect these properties. Now, developers can use CSS style tags to change the values of these grid properties, as we saw previously in the code for TopDrawer.mxml.

We retrieve the values for these properties dynamically as we track the mouse:

        private function getGridSize():int
        {
            return (getStyle("gridSize") == undefined) ? 10 : getStyle("gridSize");
        }   

        private function getGridSnap():Boolean
        {
            return (getStyle("gridSnap") == undefined) ? false : getStyle("gridSnap");
        } 

And we use these values when we call snapPoint(), which is responsible for returning the nearest point on the snap grid to a given (x, y) location (which will typically just be the current mouse location). I won't bother putting the code to snapPoint() here, since it's pretty simple; just trust me that it does what I said, and check out the code in the file for the details.

A: Is it difficult aligning objects in TopDrawer? A: No, it's a snap

Events

We create a custom event in ArtCanvas, and use the following metadata to communicate this to Flex:

        [Event(name="drawingModeChange", type="events.DrawingModeEvent")]

When the internal variable currentShapeMode changes, we will dispatch the drawingModeChange event so that listeners (which are declared in TopDrawer.mxml, as we saw last time) are aware of the change.

Properties

Now, let's see the instance variables that will be used for the canvas:

        // The drawing shapes on the canvas
        private var shapes:ArrayCollection = new ArrayCollection();

        // Point at which a shape starts being drawn - used to track
        // delta movement in mouse drag operations
        private var startPoint:Point;

        // Current shape drawing mode
        private var _currentShapeMode:int = LINE;
   
        // Current shape being drawn
        private var currentShape:ArtShape;
   
        // Currently selected shape. If null, no shape is selected.
        private var selectedShape:ArtShape;
   
        // Shape which renders selection handles for currently selected shape
        private var selectionShape:Shape;
   
        // Color with which following primitives will be created and drawn
        private var _drawingColor:uint;
   
        // Stroke width for future line-drawn primitives
        private var _strokeWidth:int;

Some of these are used to track internal state, such as the startPoint of the current ArtShape. Others are values that are controlled through the GUI, such as the drawingColor, which can be changed by the user via the ColorPicker component. It is helpful, at least if you're just learning ActionScript3, to see the pattern for these externally-modifiable properties in the language. Let's look at currentShapeMode as an example.

Note that our class variable for _currentShapeMode is private. But we want this variable to be settable from outside the class. In addition,we would like to dispatch our custom event drawingModeChanged when this variable changes. A typical pattern in other languages is to have a parallel "setter" method, such as setDrawingColor(), that can be called to affect the value of _currentShapeMode. A similar setter is created in ActionScript3 by the following code:

        public function set currentShapeMode(value:int):void
        {
            _currentShapeMode = value;
            dispatchEvent(new DrawingModeEvent(value));
        }

Note the syntax here; the method is not actually called "setCurrentShapeMode()", but instead uses the keyword "set" to indicate that it is a setter for the class variable currentShapeMode. The cool thing about this approach is that external users of this variable simply reference currentShapeMode directly, instead of calling a method, like this:

        canvas.currentShapeMode = somemode;

For example, the DrawingShapeIcon component defined in TopDrawer.mxml that handles setting the drawing mode to LINE does this by the following click handler:

        click="canvas.currentShapeMode = ArtCanvas.LINE;"

This approach has the terseness of setting a field from the caller's standpoint, but actually calls your set method, where you can perform more than a simple assignment of the variable. In this case, we need to both set the value of the variable and dispatch an event; we do both of these in our setter.

Shapes

Finally, we're onto the heart of this class, and the entire application: creating and manipulating shapes. This is done mainly through mouse handling; mouse clicks allow selection and mouse drags enable either creation (if nothing is selected) or movement of a selected shape.

Since we will need to handle mouse events, we need to inform Flex that we want to listen for these events. We do this in our constructor:

        public function ArtCanvas()
        {
            super();
            addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
            addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
        } 

Of course, these events will only give us move up/down information. In order to handle mouse drag events we also need to track mouse movement. But since we only care about mouse movement while the mouse button is down (that is, mouse drags, not just mouse moves), we will only add a listener for mouse movement when there is a mouse down event. We'll see how that is done that later in the handleMouseDown() method.

First, a utility method. There are two different places where an object may be selected: when an existing shape is clicked on by the user and when the user finishes creating a shape. To avoid duplicating the code, there is a selectShape() method. This method registers that a shape is selected, creates a new selectionShape, which is basically a set of selection handles (filled rectangles), the position of which is determined by the shape being selected, and adds the selectionShape to the children of the canvas so that it is displayed appropriately:

        private function selectShape(shape:ArtShape):void
        {
            selectedShape = shape;
            selectionShape = selectedShape.getSelectionShape();
            addChild(selectionShape);
        }

Here is our handler for mouse down events. startPoint gets the point where the mouse was clicked on the canvas or, if grid snapping is enabled, the nearest point on the grid:

        private function handleMouseDown(event:MouseEvent):void
        {
            startPoint = snapPoint(event.localX, event.localY);

Next, we get the global mouse location relative to the Flex window (otherwise known as the Flash "stage"), which we will use in testing for object selection. Note that we do not use a grid-snapped point for selection because we want to test against the actual pixels of a shape, and many or most of those pixels will actually not be on the grid (picture a diagonal line, for example, most of whose pixels lie between, not on, grid points):

            var selectPoint:Point = localToGlobal(new Point(event.localX, event.localY));

Next, we see whether there is already a currently-selected shape. If so, we see whether we hit that shape with this mouse-down operation. If not, we deselect the shape (which includes removing the transient selectionShape from the children displayed by the canvas):

            if (selectedShape) {
                if (!selectedShape.hitTestPoint(selectPoint.x, selectPoint.y, true)) {
                    removeChild(selectionShape);
                    selectedShape = null;
                }
            }

If there is no shape selected (or if we deselected a previously selected shape because the current mouse location missed it), then see whether we should select a shape, based on the global mouse position. Note that the true parameter in the hitTestPoint() call tells the method to base hits only on actual shape pixels not the simple bounding box of the shape:

            if (!selectedShape) {
                for each (var shape:ArtShape in shapes)
                {
                    if (shape.hitTestPoint(selectPoint.x, selectPoint.y, true))
                    {
                        selectShape(shape);
                        break;
                    }
                }
            }

If we still do not have a selected shape, then the mouse truly didn't hit any of the existing shapes. So it's time to create a new a new shape. This is done by instantiating one of the handful of specific Shape subclasses, according to the currentShapeMode variable (the value of which is determined by which icon the user selected in the TopDrawer.mxml UI), sending in this initial point to the new shape, and adding that shape to the display list of the canvas:

            if (!selectedShape) {
                switch (currentShapeMode) {
                    case LINE:
                        currentShape = new Line(strokeWidth, drawingColor);
                        break;
                    case ELLIPSE:
                        currentShape = new Ellipse(strokeWidth, drawingColor, false);
                        break;
                    // and so on: other cases deleted for brevity
                }
                currentShape.start(startPoint);
                addChild(currentShape);
            }

Finally, we now need to track further mouse-move events, which will be used to either move a selected shape or continue creating the new one:

            addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
        }

As long as the mouse button is held down, we will receive mouse movement events, which will be handled as drag operations. If there is no currently selected object, then each drag operation sends another point into the in-creation shape. Otherwise, each drag moves the currently selected object (and its selectionShape) according to how much the mouse moved since the last mouse event:

        private function handleMouseMove(event:MouseEvent):void
        {
            var location:Point = snapPoint(event.localX, event.localY);
            if (!selectedShape) {
                currentShape.drag(location);
            } else {
                var deltaX:int = location.x - startPoint.x;
                var deltaY:int = location.y - startPoint.y;
                selectedShape.x += deltaX;
                selectedShape.y += deltaY;
                selectionShape.x += deltaX;
                selectionShape.y += deltaY;
                startPoint = location;
            }
        }

When the mouse button is released, we will receive that event in our handleMouseUp() method. In this method, we will only process position information for objects being created (selected objects being moved do not need a final operation to complete; they will be handled just by the previous drag events). We add the final point to the created shape, then test whether it is valid; this prevents spurious null objects where the first/last/intermediate points are all the same. If the shape is valid, we select it and add it to the current list of shapes for the canvas. Finally, we remove our mouse movement listener since we only care about movement for dragging between mouse up and down events:

        private function handleMouseUp(event:MouseEvent):void
        {
            if (!selectedShape) {
                if (currentShape.addPoint(snapPoint(event.localX, event.localY))) {
                    if (currentShape.validShape()) {
                        shapes.addItem(currentShape);
                        selectShape(currentShape);
                    } else {
                        removeChild(currentShape);
                    }
                    // done creating current shape
                    currentShape = null;
                }
            }
            removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);           
        }

There is one more event that we track, which is the key-down event from the keyboard. We do this just to allow easy deletion of the currently-selected object. Our handler simply deletes the current object from the list of shapes, removes it from the children of the canvas (which removes it from the objects being displayed by Flex), and removes the current selectionShape as well:

        public function handleKeyDown(event:KeyboardEvent):void
        {
            if (event.keyCode == Keyboard.DELETE) {
                if (selectedShape) {
                    var itemIndex:int = shapes.getItemIndex(selectedShape);
                    if (itemIndex >= 0)
                    {
                        shapes.removeItemAt(itemIndex);
                        removeChild(selectedShape);
                        selectedShape = null;
                        removeChild(selectionShape);
                        selectionShape = null;
                    }
                }
            }
        }

Similarly, clicking on the "clear" button in the UI will cause all objects in the list to be deleted by a call to the clear() method:

        public function clear():void
        {
            for each (var shape:Shape in shapes) {
                removeChild(shape);
            }
            shapes.removeAll();
            if (selectedShape) {
                selectedShape = null;
                removeChild(selectionShape);
            }
        }

Display

There's one final method that is interesting to look at: updateDisplayList(). We saw this method earlier in our discussion of the typical four overrides from UIComponent, where this is the only method that we actually need to override in ArtCanvas. In our case, all we have to do here is draw our canvas to look like what we want. Here, we set the fill to be solid white and fill a rectangle the size of our component:

        override protected function updateDisplayList(unscaledWidth:Number,
                unscaledHeight:Number):void
        {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            graphics.clear();
            graphics.beginFill(0xffffff, 1);
            graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
        }

Note that all of the interesting rendering, that of the shapes that have been and are being created, is handled by the shapes themselves; when they are added as children of ArtCanvas, Flex automatically works with those shapes directly to get their display lists. So all we need to do here was handle the rendering for the canvas itself.

goto end;

That's it for ArtCanvas, which has most of the logic in the entire TopDrawer application. I'm tempted to put the rest of the code here, but I'd like to keep each blog somewhat shorter than way too long, so I'll defer it to my next entry. In that next TopDrawer article (ooooh, I can feel the suspense building...), I'll go over the code in the Shape classes and other minor classes. I'll also post the full source tree so that you can build and play with it yourself.

Wednesday, March 12, 2008

Filthy Flex Teaser

I spent last week at the Java Posse Roundup conference, where I gave a 5-minute "lighting talk" entitled "Filthy Rich [Flex] Clients." The video is posted on YouTube, along with other videos of lighting talks from the conference, but I'll embed it here as well:
The quality isn't really up to the task of showing how the code actually works (I'll blithely blame the video quality, and hope that the problem is not with the speaker). But I thought I'd post the video as a teaser for future articles. These demos are nice byte-sized techniques that you can use in Flex and Flash to get some nice graphical effects for rich applications.

Monday, March 3, 2008

Finally, Some Code! (Top Drawer, Part I)

As part of my learning the Flex platform, I plan to write some sample Flex applications to see how various things work in the system. In particular, I want to play around with MXML, ActionScript3, Flex components, Flash APIs, and general UI and graphics functionality in the platform. And I definitely want to play around with filter effects, states, and transitions, of course; I find them very moving.

To begin with, I wrote a simple vector drawing application. There's not enough of them out there, right? I remember a decent one on the original Macintosh in the mid 80's, but who's got one of those machines lying around these days? It's way easier to write a new version in Flex than to search around EBay for an old Mac and then find some place to store the thing when you're not using the drawing application.

TopDrawer: The Application

Here's the application, hopefully running here inline in your browser window.

(Apologies if it doesn't work for you; I think I've used some very basic tags to embed it in the page, and it may not work correctly if you aren't running the right version of the Flash player (version 9) that it requires.)

The cool thing about the application (apart from the amazingly beautiful UI, of course. Check out that gradient!) is that the basic GUI and the functionality of creating some simple shapes from mouse gestures was actually up and running within about an hour of my first attack on it. That seemed pretty good, for someone like me that's very new to the platform. Basic UI elements, simple mouse-handling, and simple manipulation of Flash Shape objects was quite straightforward. Finishing up the application to its current state too a bit longer, of course, as I made the UI more complex, added features, allowed selection and editing, and other nifty features. But I was heartened by my initial productivity on the application. My experience so far, albeit quite limited, is that Flex allows you to get up and running with a decent looking UI in pretty short order.

TopDrawer is pretty much like what you'd expect from a vector drawing application, although arguably a bit skimpy on features (like no Open/Save, for example, and only 6 drawing primitives, and limited editing capability, and, well, pretty big limitations overall). You select a drawing primitive from the panel, click and drag on the canvas to create it, select an object by clicking on it, and move it around with the mouse. There are some drawing attributes that you can change, such as the current color and stroke width. You can also change canvas attributes like whether drawing snaps to a grid, and what size the grid has.

Underneath, TopDrawer consists of a Flex Application component to hold the main GUI, a custom component for the canvas (a subclass of UIComponent), subclasses of Shape for the various geometric primitives, and various small helper classes. There are some particular aspects of the Flex stack that I wanted to learn more about, like custom components, styles, and events, so I made efforts to integrate some of those elements into the application.

A reasonable way to approach a blog like this where an application is discussed is to perfect the application first, and then present the finished code. This shows the correct way to go about this type of application and avoids any embarrassment over code that the author would later regret.

But where's the fun in that?

I figure the whole point of my writing this blog, especially at this point in my career at Adobe when I'm completely new to the Flex development platform, is that I can try to teach while I learn. Like if your Biology teacher didn't show up for class one day and one of the students got up and winged it instead. (Okay, hopefully I'll do a tad better than that, or at least better than I would trying to teach Biology). Because I think it's instructive to see not only what the finished code should be like, but why it's better than some other approaches that seemed like a good idea at the time.

So what I want to do instead is to show the application as I initially wrote it; not a finished product, but one which I was reasonably happy with for a first attack at the problem. I'll go over the various aspects of the code and application so that you can see how things fit together and make the application work. Hopefully, along the way, folks new to Flex, Flash, and ActionScript will learn a bit about coding in this new environment.

Then when I'm done, I'll make some changes to it and show why the changes make the code and the application better.

In case you're thinking “why should I spend the time looking at the wrong code?” I'll tip my hand and say that the initial version of the application is not actually bad; there's a lot in it that is perfectly valid for a Flex application. It's just that the later version of the application is better in some respects. So hopefully there is plenty to learn from both. When I reach a piece of code that will be improved upon later, [I'll note it like this] so that you can view it with the right degree of skepticism and an appropriately cocked eyebrow.

One more note: an entire walkthrough of the application, even this relatively small application, would take too much space for a single blog entry. So I'll stretch it over the next few entries, going over one or more files in each one. When I'm done, I'll post the entire source tree (what, you think I want to post it now and risk you skipping off before the stunning conclusion?)

Architecture

Before I dive into an actual code walkthrough, I wanted to explain how things work together at a high level.

The main application is driven from the file TopDrawer.mxml (which I'll be going over in this article, below). This file sets up the main GUI of the application, including the drawing canvas (our custom component, ArtCanvas), the drawing primitive palette (instances of a custom component that displays an icon, DrawingShapeIcon), and the drawing attribute selection items (a color chooser, a combobox for the line width, and a checkbox/textfield for the grid snap).

Most of the interaction of the application is handled in ArtCanvas.as, which tracks mouse drags and performs object creation/movement appropriately. Objects are created with subclasses of the Flash Shape object, which store the rendering operations necessary for each shape.

That's pretty much it (I told you it was a simple application). There are some other classes and pieces of functionality that I'll discuss in the context of the code walkthrough.

The Code

TopDrawer.mxml

You can view or download the full source for this file here; I'd suggest you bring up the file in a different window so that you can see the snippets I'll go over below in the larger context of the entire file.

This file sets up the main Application window. The logic in this file is mostly declarative, setting up the look of the GUI and hooking up events statically in MXML, although there is some dynamic logic specified in the file as well.

Let's take a look at the code:

    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
           layout="absolute"  xmlns:comps="components.*"
           creationComplete="windowCreated();"
           keyDown="canvas.handleKeyDown(event)"> 
Some interesting thing to note here include:
  • Xmlns:comps=”components.*”: This is an xml-ism that says “I have some custom components in my own package that I will be referring to in here”. Everything prefaced by “mx” (such as the Application component itself) is a core Flex component. Components prefaced with ‘comps” (such as ArtCanvas, which we'll see soon) are my subclasses.
  • creationComplete=”windowCreated();”: This hooks out the creationComplete event on the window and asks Flex to call the windowCreated() function, where we'll run some additional tasks that are appropriate to perform at that time.
  • keyDown=”canvas.handleKeyDown(event)”: This hooks out the keyDown event on the application and calls a handler function to process the event. This is done in order to process delete-key events to remove the currently-selected object. As we'll see later, there's a better way to do this.
      <mx:Style>
           <!-- initial values for the snap grid - these can be changed in the GUI -->
           ArtCanvas {
             gridSize: 20;
             gridSnap: false;
           }
      </mx:Style> 

I declared a couple of custom styles in the ArtCanvas class, just for the heck of it (no, really: my goal in writing this application was to learn about various Flex capabilities). This section sets those styles, which control whether drawing is ‘snapped' to an invisible grid of the specified size, to some default values. Note that these values are also settable in the GUI itself (probably not the most obvious use of Styles ever created, but it was an excuse to play with Styles…).

      <mx:Script>
           <![CDATA[
             /**
              * Some  things need to happen at window creation time, such as adding
              * listeners  for a custom event to change the drawing mode
              */
             private function windowCreated():void
             {
                   trace("windowCreated");
                   // setFocus()  allows key events (like hitting the delete key) to be
                   // processed  automatically, without having to click in a component first
                   setFocus();
                   // These event  listeners are set up to be called when the canvas mode
                   // changes.  This allows all drawing-mode icons to highlight/de-highlight
                   // themselves  appropriately when the mode changes.
                   canvas.addEventListener("drawingModeChange",  lineIcon.modeChanged);
                   canvas.addEventListener("drawingModeChange",  scribbleIcon.modeChanged);
                   canvas.addEventListener("drawingModeChange",  rectangleIcon.modeChanged);
                   canvas.addEventListener("drawingModeChange",  rectangleFilledIcon.modeChanged);
                   canvas.addEventListener("drawingModeChange",  ellipseIcon.modeChanged);
                   canvas.addEventListener("drawingModeChange", ellipseFilledIcon.modeChanged);
                   // Setting the  initial mode here, after window creation, allows the appropriate
                   // mode icon  to be highlighted through the event listener mechanism above
                   canvas.currentShapeMode  = ArtCanvas.LINE;
             }
           ]]>
         </mx:Script> 

The Script/CDATA tags are the way that MXML files set apart a section of ActionScript code. You can write the code in a separate file and then simply include that file here, but it's sometimes convenient to write small functions in the same file.

In this case, I wrote a handler for the creationComplete event on the application window, and use that opportunity to set some properties. First, I call setFocus(), which sets input focus to the application window to begin with. This works in conjunction with the keyDown handler that I set up earlier so that delete-key events can automatically be processed. Without the setFocus() call, the window would not have input focus and the keyDown even would not be received.

[This setFocus() approach is not the best way to go about what I want. We'll find a better approach later]

There are then several calls to add eventListeners to the drawing primitive icons. This is done so that when the drawing mode is changed, which is propagated through the custom event drawingModeChange, all icons get the message and highlight or de-highlight themselves appropriately.

[Although this message-sending approach is better than simply having the icons peek into the inner workings of each other and the drawing canvas, it still creates a coupling between the canvas state and the icon state. We'll see a better way to handle this later.]

Finally, now that our event listeners are set up, we set the value of the canvas' current drawing mode, currentShapeMode, which will cause the events to get sent around and the appropriate icon to get highlighted in the palette.

That's all of the ActionScript code in the application file; now back to the GUI. First, we see the creation of our drawing canvas here:

      <comps:ArtCanvas id="canvas"  drawingColor="{drawingColorChooser.selectedColor}"
           strokeWidth="{lineWidth.selectedItem.width}"
           left="105"  top="10" right="10" bottom="10"/> 

This tag creates our custom component, ArtCanvas, and sets some initial properties on it. Note that we're setting the dimension properties directly because the toplevel window, our Application component, has an absolute layout. This would look different if we were using a layout manager here. Also, note that the basic GUI was created in the FlexBuilder3 design tool (a nifty WYSIWYG GUI builder), so the hard-coded values here actually came from that tool, based on where I placed things with the mouse.

The other interesting thing to note here is that we are creating a binding between the canvas' drawingColor and strokeWidth variables with the appropriate GUI items that control those values. So when the user changes the values in the color chooser or the line-width combobox, those values will automatically be changed in the canvas. Databinding rocks; no more setting up listeners to detect these changes and then doing boilerplate property setting appropriately.

[While the databinding aspect of this of this is very cool, this approach does set up a coupling between the canvas itself with the other GUI components. We'll see a better approach later]

    <mx:HBox x="10" y="10" width="87" height="108" horizontalAlign="center">
        <mx:VBox height="100%">
            <comps:DrawingShapeIcon id="lineIcon"
                source="@Embed(source='assets/icons/line.png')"
                mode="{ArtCanvas.LINE}"
                click="canvas.currentShapeMode = ArtCanvas.LINE;"
                width="32" height="32"/>

This next section sets up the palette of drawing modes. The first two tags are simple container/layout objects, where HBox lays out its children horizontally and VBox lays out its children vertically. The overall palette consists of the one HBox with 2 VBox children, where each VBox has three palette item. Each palette item is managed by a custom Image subclass, DrawingShapeIcon. All of the icons are set up similarly; I'll just go through this single “lineIcon” and assume that you get the idea.

We set up an id for each icon, which we refer to elsewhere when we set up the event listeners; in this case, the id is lineIcon. The source item is the image file that will be displayed in this Image subclass. Note that I chose to embed the icon image here. The Flex Image component allows you to either dynamically fetch the image at runtime, which allows you to deploy a smaller SWF file, or to embed the image in the SWF file, which enables faster loading of the image. In this case, the icon images are quite small, so I opted for embedding since the footprint was negligible.

The mode value is a property of DrawingShapeIcon and determines the drawing mode that will be selected when the user clicks on that icon. Note that mode is databound to the constant value LINE in ArtCanvas.

[This data-binding of constant values is the most obvious and terse way of initializing the mode value in mxml. But it's not the most efficient; we'll see a better approach later]

Speaking of clicking, we hook out the click event to perform a single line of ActionScript code which sets the value of the shape mode on the canvas. You can see from this that execution of ActionScript code is not relegated to <mx:Script> sections or entire other ActionScript files and classes; you can call ActionScript code directly in event handlers if you need to. Typically, you would only do this for very brief amounts of code, otherwise things could get unreadable pretty quickly.

      <mx:ColorPicker id="drawingColorChooser" x="10" y="126"  width="87" height="33"/> 

Next up is a simple color-chooser component. Note that we don't specify anything meaningful in this mxml code apart from the id and placement, but that an earlier component in GUI used the id of this item to data-bind between the currently chosen color and the current drawing color on the canvas.

    <mx:ComboBox id="lineWidth" x="10" y="167"  width="87"
            itemRenderer="components.LineWidthRenderer"  editable="false">
        <mx:ArrayCollection>
            <mx:Object width="1"  label="width 1"/>
            <mx:Object width="2"  label="width 2"/>
            <mx:Object width="3"  label="width 3"/>
            <mx:Object width="4"  label="width 4"/>
            <mx:Object width="5"  label="width 5"/>
        </mx:ArrayCollection>
    </mx:ComboBox> 

The ComboBox component allows a WYSIWIG way of setting the stroke width. Instead of just specifying the width with some numeric value, you can see the line width in the popup item directly and choose the appropriate one.

There are a couple of interesting things about the attributes for this component. One thing is the itemRenderer attribute; this specifies a custom component which will be called whenever the combobox needs to render one of the items in the popup. We'll see the code later, but this component basically gets the data value for the item (the width and label values above) and draws a line appropriately.

The other interesting thing about this component is the ArrayCollection item; this item is used as the "dataProvider" for ComboBox. It basically populates the popup menu with the Objects listed inside the ArrayCollection. These items are used later to determine how to render any particular item in the popup (by the itemRenderer, discussed above).

[Although this line-width component does most of what I wanted, you'll notice that the default display of the combobox (when the user is not clicking on it) displays a text-only representation of the value of the currently-selected item (which takes the value of the label field from the dataProvider). We'll see if we can get a more interesting graphical display of the selected item in a later attempt.]

    <mx:CheckBox id="snapCheckbox" x="10" y="197"  label="snap"
            change="{canvas.setStyle('gridSnap',  snapCheckbox.selected)}"
            selected="{canvas.getStyle('gridSnap')}"/>
        <mx:TextInput id="gridSizeField" x="70" y="197"  width="27"
               enabled="{snapCheckbox.selected}"
               text="{canvas.getStyle('gridSize')}"
               change="{canvas.setStyle('gridSize', gridSizeField.text)}"/> 

Lastly, we see the two items that control the grid styles. The CheckBox controls whether the snapping grid is active and the TextInput field controls the size of the grid.

Because the grid parameters were specified by Styles, versus properties, the code to change them is a bit more verbose than it would be otherwise. So, for example, when the checkbox component issues a change event, we need to send a new style value to the drawing canvas. Similarly, to have the checkbox reflect the value of the current selection value, we need to get the current style (which we then databind to, to automatically reflect changes in the style).

The text field is similar to the checkbox in its use of styles to send the values back and forth to the canvas. One interesting point is that the text field's enabled property is databound to the value of the snapCheckbox item's selected field. So the text field will only be editable if gridSnap is actually true; otherwise, the field is disabled and appears grayed-out to the user. It's a nice bit of UI completeness which really should be there in a real app, but tends to be tedious to wire up and therefore not necessarily implemented as a first-order item.

To Be Continued...

We're obviously not done; I've only shown the MXML code for the application window GUI. I also want to walk through the code for ArtCanvas, which handles the mouse interaction to create and edit the shapes, and some of the other ActionScript classes that store and render the shapes. Walking through these other files will show us more about ActionScript, Flex, and Flash programming than we have seen so far.

So look soon for the next in the series; it'll be absolutely Top Drawer.

Wednesday, February 27, 2008

Moment of Reflexion

A comment on my previous posting asked for some explanation of what Flex is and how it relates to Flash.

I'm still intending on posting some technical content here and going over some sample code, but hey - I'm Flexible.

I was sort of assuming that everyone had heard of Flex and knew something about it. But since I'm new to the platform myself, and since I intend this blog to be for relative newcomers to the platform (at least for now, since that's where I'm at), it seems reasonable to define some terms. Here is my understanding of how these various Adobe products and platforms relate to each other. If I get whacked from someone in marketing, I'll replace them with something more correct, but I think this will do for now.

ActionScript

ActionScript is the programming language used to program Flash applications. The current version of the language is ActionScript3, which is based on ECMAScript (read: JavaScript), and should be quite familiar to most web programmers because of its similarity to JavaScript. ActionScript is more type-safe that JavaScript in general (or at least that's my impression from using it in the Flex environment); you need to declare most variables as having specific types at compile time. The base syntax is actually not too unlike Java. I've found the transition easy overall, although I still write my variable declarations backwards out of habit and then go back and fix them when the compiler pukes on my code. For example, the following declaration in Java:

    Object blah;
would be writtin in ActionScript as:
    var blah:Object;

I should write a macro in the IDE to do the switch for me, since I seem to have a hard time twisting my brain around after all this time to do it correctly.

Flash

Flash is a graphical library and runtime that enables animated graphics applications. There is an authoring tool for creating Flash content, a language for programming Flash applications (ActionScript3), a class library of graphics and other useful objects, and a VM that enables Flash applications to perform well. From the start, Flash was about creating animations, and the tools and libraries for Flash all reflect that time-based model; the Stage, MovieClip objects, and frame rates are all part of a normal Flash application.

The rendering in Flash takes place via a "display list" (also known as a scene graph, or retained-mode graphics). Instead of issuing direct rendering calls in your code which get executed immediately (like they do in immediate-mode APIs such as Java 2D), you place rendering requests on a display list which gets processed and displayed which the next frame is rendered. These requests then stay on that display list until removed.

The use of a scene graph is a subtle point, and perhaps one that most application designers couldn't frankly give a toss about. But it ends up being important to Flash and Flex developers, as it's important to understand what is being put on the display list and when that display list needs to be cleared and repopulated with other rendering requests instead.

For example, if you put the following rendering calls into the display list of a Flash object (where graphics is the graphics context of a Flash display object):

    graphics.moveTo(0, 0);
    graphics.lineTo(100, 100);
then you will get a line on the screen from (wait for it...) (0, 0) to (100, 100). Suppose you later want to move the line to be from (50, 50) to (150, 100). If you made these calls to the graphics object:
    graphics.moveTo(50, 50);
    graphics.lineTo(150, 100);
and ran your application, you would see two lines on the screen. You didn't actually change the line's position; instead, you simply added another line to the display list. The typical way to get what you want is to recreate the display list from scratch by clearing it first, and then add in the rendering calls you want:
    graphics.clear();
    graphics.moveTo(50, 50);
    graphics.lineTo(150, 100);

Flex

Flex is a GUI toolkit library that sits on top of the Flash library and runtime, much like Swing sits on top of Java 2D (for the Java folks in the audience). The addition of Flex did a few things to improve (at least for folks like me) the programming experience of Flash applications:

  • GUI components: Prior to Flex, most Flash applications brewed their own components (buttons, etc.), which meant more work for the developer, less consistency between applications, and potentially buggy or unpredictable behavior. Who here has run across bizarre implementations of scrollbars in Flash applications that didn't look or behave at all like the scroll bars we know and love? Flex brings uniformity and functionality to this previously, er, more chaotic space.
  • Programming model: As I alluded to before, the programming model of Flash is, well, different than most GUI developers might expect. Thinking about an enterprise UI in terms of MovieClips, frames, and display lists is, well, different. Flex abstracts all of that away and allows developers to write GUI applications for Flash in a much more traditional way. A button control is a button control, with events, skins, states, and so on; it's not a MovieClip added to a Stage or anything so unusual (at least to me, but maybe I just have Stage fright).
  • Declarative programming: Flex also introduced the MXML language for declarative programming of applications. This XML-based language allows programmers (and design tools) to create static code that tells Flex how the UI should be layed out, and saves the actual dynamic programming logic via ActionScript for things that cannot be declared statically. There are various advantages to this model (from what I've seen so far): clear XML code that sets up the GUI briefly (as opposed to lots of code to do the same task), a logical hierarchy of XML tags that reflect the parentage of GUI containers and components, and interaction with design tools for ongoing creation and editing of the GUI in a WYSIWYG builder (such as the one in FlexBuilder). It's pretty cool to be able to go in and out of design mode, hand-coding stuff where appropriate, and dragging and dropping other things when possible. The fact that the GUI layout is persisted in XML means that the builder tool can handle edits much better than approaches that use actual code behind the scenes (typically, the code is not allowed to be edited or if you do edit it you probably cannot get the tool to read it in again).

Given all of these advantages and advances of Flex, GUI programmers should now find it much easier to write real, full, robust GUI applications that run on the Flash platform.

The output of a Flex build is simply a SWF file (the type of file that the Flash player will run); it ends up just being another Flash application. But inside of that application is the set of Flex components and capabilities used in your application, which then translate into Flash rendering.

The Flex SDK is free and open sourced; you can write Flex applications, compile them, and ship them all for free.

FlexBuilder

This IDE (a plugin for the Eclipse platform) is optional - you can use the Flex SDK for free as a command-line compiler, using whatever IDEs and code editors you want. The FlexBuilder tool does cost money, but gives you some advantages over hand-coded Flex applications, such as the GUI builder I mentioned above as well as all of the code editing features you would expect for both ActionScript and MXML (code hinting, and so on).

AIR

Think of Adobe AIR as the packaging of both web applications (HTML/JavaScript) and Flex/Flash applications for the desktop. Flash was written originally for the browser, and Flash content is predominantly browser-based today. Although you can run SWF files in a standalone SWF player, traditional Flash applications are really intended for use in web browsers. The same goes for Flex applications, since they are essentially Flash applications with more built-in capabilities.

But what if your users want to run your application offline? Or what if they want access to their local filesystem, a feature that browser applications typically don't allow because of security restrictions?

AIR enables all of this; it allows applications to be installed on the local operating system and accessed from the desktop just like the other applications that the desktop user runs. AIR applications can still access online information, in fact that is still an important model for these rich applications, but they can also run offline when appropriate or necessary.

AIR also bundles in more capabilities that are not currently a part of the Flash or Flex platform, such as a full HTML/JavaScript rendering engine. So even if you're a web developer, just writing AJAX applications with HTML and JavaScript, you can use AIR to deploy these applications to your user's local desktop.

BlazeDS, LiveCycle, etc.

I think I'll leave these product terms for another day. There's a host of Flex-related products and capabilities for the back-end. But frankly, I'm still figuring out the stuff on the client, and I'd really rather stick to what I know on this blog....

Tuesday, February 26, 2008

Know Free Launch

Adobe released a bunch of good stuff yesterday. It's pretty huge. Not mentioning it would be like forgetting to tell your wife you like her new haircut; tactless and just plain dumb.
  • Adobe AIR 1.0: Bringing Flex, web programming, and Rich Internet Applications in general to the desktop.
  • Flex 3: The latest release of the Flex platform, a powerful GUI toolkit sitting on top of the rich Flash rendering engine.
  • The open-sourcing of both the Flex SDK and BlazeDS (back end services for Flex and AIR applications)

Meanwhile, I'm still putting together technical pieces on a Flex app I've written; expect that to start dribbling out here soon. Like the crumbs that multiply on my shirt as I eat my breakfast; at first it's just a couple of crumbs, but pretty soon you've got a whole scone there in its component parts.

Eventually, I want to be able to write pieces that help describe the work we’ve done on Flex, the features we’re working on, the future plans, and the inner details that help people understand how to use it most effectively.

But hey, I just got here; I don’t know any of that stuff. I’ve been developing graphics software for my entire career and hacking the internals of desktop Java for the last several years, and I am very familiar with desktop client technology in general, but the whole stack offered by Flex is completely new to me. I will, I’m sure, eventually learn all of the fun internals, and when I do I’ll execute on the plan above.

But until then, I’ll be coming up to speed on Flex, Flash, AIR, ActionScript3, and everything else I need to be able to help out on the Flex team. And as I do, I thought it would be useful to post interesting things that I discover, applications that I’m working on, and random thoughts on Flex development to hopefully help others on a similar learning curve.

Maybe you’re a Java developer, trying to learn about Flex. Or maybe you’re a Flash developer, interested in Flex’s GUI toolkit. Or maybe you’re new to desktop development and are checking out the various technologies available. Or maybe you did a Google search on “acid reflux”, ended up here and haven’t a clue what I’m talking about.

In any case, stay tuned for future postings here where I’ll talk about some of the things I’m working on and interesting things I find out about the platform as I cruise along.

Thursday, February 21, 2008

What the Hello World

I know what the world needs - another weblog! There's surely not enough of them out there already...

Welcome to my new weblog, Codedependent (a term which comes from this joke on my humor blog). I intend to use this blog for random discussions of graphics software; demos, tutorials, how-tos, snippets, announcements, algorithms, whatever. I'm also not above the odd humor piece or two (which can be even funnier when people take it seriously, despite my best efforts to be completely ridiculous).

I've recently joined Adobe Systems to work on the Flex SDK. Much of the material I write will be about Flex, Flash, AIR, ActionScript, and related technologies, although I also intend to have non-technology-specific pieces as interesting graphics software topics arise.

I feel the need to offer some kind of explanation to justify my writing this blog. Given some of the stuff out there in the blogosphere, all that bloggers really seem to need is an internet connection and a vague sense of where the keyboard is. But I’m after a higher goal here – I actually want people to read the articles, enjoy them, interact with me, and maybe even learn a thing or two as I, too, will be learning from working on the material.

Anyway, here’s me in brief (not "me in briefs"; this is not that kind of blog): I’ve been developing graphics software for my entire career, after a short-lived stint doing Network Communications, where I realized just how dull that work was. I work on anything that puts the pixels on the screen. I’ve worked on graphics technology at the application level, at the API level, in library development, and even down to the drivers for graphics chips. I was recently at Sun Microsystems, where I was an architect in the desktop client group for the last several years. I worked with all of the technologies in desktop Java, such as the Swing GUI toolkit and the Java 2D rendering layer. Some of my efforts there included hardware-acceleration for Java 2D rendering and, more recently, animation and whizzy graphical effects for Swing. On the topic of nifty graphical effects, I published a book last year with Romain Guy, entitled Filthy Rich Clients.

One of the things that attracted me to Flex, and to Adobe, was a client platform that enables very rich user experiences; transitions, animations, filters, and just darned good-looking UIs are all pretty exciting to this graphics geek. I hope to be able to help make Flex an even richer platform going forward.

As part of my recent stint at Sun, I kept a weblog on java.net; you can check out that blog if you want an idea of the kinds of things that interest me. That blog's postings are pretty Java-specific, since that was the intent, so I probably won't be adding to it in my newfangled life here at Adobe. Instead, I’ve started this new blog to be able to talk about Flex and, well, anything else that seems appropriate.

This should be fun - I hope you can join me. I promise to get more technical in future pieces. I just needed to get this "Who the heck are you?" piece out of the way so that I could get to more interesting material. For example, I've just finished writing a simple vector-drawing application as a way to teach myself about various elements of Flex and Flash. I'll be posting that application and going over the code in upcoming articles for anyone that's interested in learning along with me.

By the way, I also have a humor weblog that I post to when I want to write stuff that has nothing whatsoever to do with software (although I have been known to post jokes of a geeky nature there). If it’s technical, look here. If it’s not, look there. And if it's complete junk, look somewhere else.