Thursday, September 25, 2008

Screenweaver - an open source alternative to AIR?

I don't have a detailed post this week as I've been working on cleaning up and refactoring my IOC project for launch next week (I hope). However here's a few interesting links:

  • haXe - A multi-platform language with a fairly pleasant syntax based on ECMAScript. You can write code in haXe and compile to SWF (Flash 6 to Flash 9), ActionScript (this isn't too good, I've run into problems), JavaScript, and PHP. It also compiles to NekoVM...
  • Screenweaver Open Source - This is a bridge between Flash and NekoVM, for Windows and Mac OS. You can do all your GUI code in ActionScript, or you can do it all in haXe as it compiles to SWF as well as NekoVM.

I Haven't played with Screenweaver yet, but I plan on it in the near future! And of course when I do I'll be posting my progress here :)

Monday, September 22, 2008

Internet Explorer

Groan. I think the site's working in IE6 now. Debugging HTML on Explorer (without parallels or a Windows box handy) is pain. This is why I do Flex these days.

Thursday, September 18, 2008

Vertical Scrollbars and 100% width content

I've often wished Flex would simply shrink the available width of my containers when their contents grow too tall, but alas, often you'll find yourself with a horizontal scrollbar that only exists to show you the content that's just been hidden by the vertical scrollbar that wasn't there a minute ago!

It's a common question, and the Adobe guys had their reasons for making it how it is. Not much consolation for the rest of us though! So, a (perhaps not the, but a) solution:

package info.joshmcdonald.barra.components
{

Shameless self-promotion: I've created a google code project for code examples and utils from my blog.

    import flash.display.DisplayObject;

    import mx.containers.Canvas;
    import mx.core.UIComponent;

    public class SmartVerticalScroll extends Canvas
    {
        override public function addChild(child : DisplayObject) : DisplayObject
        {
            return addChildAt(child, numChildren);
        }

        override public function addChildAt(child : DisplayObject, index : int) : DisplayObject
        {
            if (numChildren > 0)
                throw new Error("Only one child, stick a canvas or something in here");

            return super.addChildAt(child, index);
        }

Just to keep thing simple, and this example small, we're limiting this to a single child. Not ideal, but we can get away with it by simply using it as a container for a Canvas or a VBox or similar.

        override protected function updateDisplayList(unscaledWidth : Number, unscaledHeight : Number) : void
        {
            var innerWidth : Number = unscaledWidth - borderMetrics.left - borderMetrics.right;

            super.updateDisplayList(unscaledWidth, unscaledHeight);

            if (numChildren == 1)
            {
                var child : DisplayObject = getChildAt(0);
                var uic : UIComponent = child as UIComponent;

                if (uic)
                {
                    if (verticalScrollBar)
                    {
                        var newWidth : Number = Math.max(Math.min(uic.width + uic.x , innerWidth - verticalScrollBar.width), uic.measuredMinWidth);
                        uic.setActualSize(newWidth, uic.height);
                    }
                }
                else
                {
                    child.x = 0;
                    child.y = 0;
                }
            }
        }

Here's where half the magic lies. If we have a vertical scrollbar, we'll shrink our child component to fit within it, unless that would violate its minWidth requirement.

        override public function validateDisplayList() : void
        {
            var before : Boolean = verticalScrollBar == null;
            super.validateDisplayList();
            var after : Boolean = verticalScrollBar == null;

            if (after != before)
            {
                //Scrollbar added or removed - repaint and resize inner component
                invalidateDisplayList();
            }
        }
    }
}

Here's the other half of the magic. When we determine that Flex's built in layout code has decided to add a vertical scrollbar, we make sure to schedule another call to our updateDisplayList() so we can shrink the content down to get out of the way.

Thursday, September 11, 2008

Bindings, Changewatcher, and memory leaks

I'm building some stuff with a serious potential to leak memory (but it's hella cool, watch this space). Specifically, dealing with binding to and from objects without keeping hard references to them, which is easier said than done! I've been going through the source code to the binding system, and all bindings eventually come down to ChangeWatcher.watch().

  1. When you call watch(), you get returned the first of a chain of watchers, one for every host in the chain.
  2. Every watcher in the chain with a non-null host has a hard-reference from the host object to itself.
  3. Every watcher has a hard-ref to the handler function.

This of course leads to the fact that if you ever throw away your watcher reference before calling unWatch(), you're stuck with a zombie reference from the root of your chain to your handler function. Which means in turn that the host object is hard-referenced to anything within the handler function's scope. If the root of your chain happens to be Application, or anything else that lives the life of your app, you're almost certainly leaking memory.

I'd love to be wrong about my conclusions, but I don't think I am. I'll have to create a test some time over the weekend to prove it though, so I can file a bug (and a fix) with Adobe.

So in conclusion: Be awfully careful what you're putting in the handler function for any hand-created ChangeWatcher, or using BindingUtils!

Note that I don't think it's a problem for MXML bindings: Off the top of my head, if the root host of a binding chain defined in MXML is "A" then the handler function is generated as a member function of "A".

Thursday, September 4, 2008

Using the Proxy class (part 2)

In the first part of this article I demonstrated using the basic Proxy functionality. Now we get to the fun stuff, which we need to support for..in and for each..in constructs.

//Gets the next index. When you return 0, for..in and for each..in will quit.
override flash_proxy function nextNameIndex(index : int) : int
{
    if (index < propertyNames.length)
        return index + 1;

    return 0;
}

Here's where the Flex docs get kinda confusing :) The first thing the VM does when you're iterating your Proxy object with for..in or for each..in is call nextNameIndex(0). The docs tell you the input is zero-based, but that's not the case, 0 isn't "first", 1 is. So when flex asks for the "next name from zero" it's actually asking if there's a first name at all. If you've got more properties to iterate, return index + 1. Otherwise, return 0 and the loop will end.

Note that you don't *have* to return index +1. This is how ArrayCollection, ListCollectionView and the like implement filters. But that's another post :)

//Get name[i] where i is 1-based
override flash_proxy function nextName(index : int) : String
{
    return propertyNames[index - 1];
}

//Get value[i] where index is 1-based.
override flash_proxy function nextValue(index : int) : *
{
 return properties[propertyNames[index - 1]];
}

These two functions are the other half of the for..in and for each..in loops. After the VM gets the next index from nextNameIndex() it will call one of these two functions. In the case of for..in it will call nextName(), and for for each..in it will call nextValue(). Because we're not doing anything complicated in this example, we just return "properties[index - 1]" (remember Array is 0-based, the Proxy functions are 1-based). If there were more logic in our getProperty() method from the first part of this article, then we'd want to defer to that method rather than duplicate that login in the nextValue() method.

And that's how you use the Proxy class. Now, go build something bad-ass with it :)

Download source.

Using the Proxy class (part 1)

When I first came across the Proxy class, I got rather excited. Dreams of mock objects, cross-cuts, and various other cglib shenanigans danced in my vision. Sadly, it was not to be, and the VM doesn't let you cast Proxy as another class (or interface). However, it has its uses, and I'd like to explain how to use it.

I know it's been done before, but for this example I'm going to re-invent the "bindable dynamic object". The point is to explain the Proxy class :)

Let's get started:

[Bindable(event="propertyChange")]
public dynamic class BindableDynamic extends Proxy implements IEventDispatcher
{
    //Actual contents
    private var properties : Object = {};

    //Property names
    private var propertyNames : Array = [];

A few things here:

  1. We've annotated the class with [Bindable(event="propertyChange")]. This marks the class as bindable, and because we specify the event name, MXMLC assumes we know what we're doing and doesn't generate any Binding boilerplate for us, which is what we want :)
  2. We've got two private variables here: properties, which holds our actual values, and propertyNames, which we'll use as a list of (guess what?) property names!

The Flex docs don't use the propertyNames variable, and instead use a temporary array when iterating the fields using for..in or for each..in, but I like keeping the list around.

Some more code:

//Simple getProperty by name
override flash_proxy function getProperty(name : *) : *
{
    if (!name)
        return undefined;

    if (!(name in properties))
        return undefined;

    return properties[name];
}

//Do we have property x?
override flash_proxy function hasProperty(name : *) : Boolean
{
    return (name in properties);
}

//Set Property
override flash_proxy function setProperty(name : *, value : *) : void
{
    var oldValue : *;

    //Check for existing value
    if (name in properties)
    {
        //We do have one, we need to retrieve it for comparison and a useful PropertyChangeEvent
        oldValue = properties[name];

        //Check to see if there's no change (in which case we're out like 3 stripes)
        if (oldValue === value)
            return;
    }
    else
    {
        //If we're here, then this is a new property (not an update)
        propertyNames.push(name);
    }

    //Set property
    properties[name] = value;

    //Announce!
    dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, name, oldValue, value));
}

These functions are pretty self-explanatory. A couple of things might jump out at you if you're new(ish) to ActionScript though:

  1. The use of flash_proxy in place of private, public, or internal: In Actionscript, private, public and internal are simply namespaces (but built in and with special meaning). Proxy uses its own namespace so that its methods don't clash with anything you might be trying to intercept. You can also define your own namespaces, and there's an entire blog post (or 8) for me to write about mx_internal and the various fun stuff we can do with it!
  2. The in operator: It's not used so much, but I love its elegance. For our intent at the moment, think of it as an analogue to .hasOwnProperty(). There are differences, but for our current purpose either would do, I just prefer it stylistically. If you'd like to read more, check here and here.
  3. Finally, we dispatch our own PropertyChangeEvent, which is how we let Binding know it's time to do its thing.

…I'll post part 2 tomorrow, where we get to the fun stuff!

Design...

...If this looks borked in IE, email me! I've not had the time to test it since I re-jigged some stuff.

Monday, September 1, 2008

[Mixin]

Trivia: The [Mixin] metadata is great, I use it all the time when doing framework stuff. Check it out if you've never heard of it, or if you're calling static code some other way. But make sure you only annotate public classes with [Mixin], or your SWF will explode on frame1 inside of SystemManager :)

This is

  • Tales of Flex
  • From Brisbane, Australia
  • Opinions on Flex development
  • Tips and FAQs
  • Shameless self-promotion

I am

  • Twitterer
  • Flexcoder
  • Maroon
  • Designer
  • Java lover
  • That loud-mouthed Aussie yob
  • Blogger
  • Problem solver
  • Contributor
  • Cricket Fan
  • Lousy photographer
  • Great cook

I read



Subscribe via RSS to receive updates!