« January 2006 | Main | March 2006 »

February 24, 2006

Evolution of a feature - layout constraints

We spend a lot of time here on the Flex / Flex Builder team arguing over exactly how each piece of the framework should work, and how the syntax should look.

I thought it might be interesting to have a glimpse into how some of these features evolve, and why. I'm picking a particularly controversial example: layout constraints.

---

Layout constraints came about because designers in particular weren't happy using containers for layout. Actually, let me say it a different way. When you drag things around, they generally expect things to stay put, although they are fine with things moving around when it makes sense (like when you drag something into a control bar). And they want pixel level control over the position of every element.

Among coders, feedback on container based layout was mixed. Most of them "got" it, and some of them loved it. But when you step back, it wasn't good for all layouts. Boxy layouts (think forms) were just fine. More freeform layouts (think Flash) were hard to achieve.

There was also a performance issue. Nesting of containers meant nesting of movie clips. Removing the nesting, however, meant that fluid resizing layouts were hard to achieve.

---

For all these reasons, we decided to add a constraint system to Flex. We originally talked about all sorts of more complex ideas -- sibling constraints and other more exotic constraints -- but we settled the more conservative approach of only supporting constraints relative to the parent container.

Let's say that we want to put a button in the top right corner of our app. Our original syntax looked like this:

Attempt 1:
--
<mx:Panel width="100%" height="100%">
    <mx:Canvas width="100%" height="100%">
        <mx:layoutConstraints >
            <mx:EdgeAnchor target="myButton" right="30" />
        </mx:layoutConstraints>
        <mx:Button id="myButton" y="30" />
    </mx:Canvas>
</mx:Panel>

This would allow us to add new types of constraints in the future, such as sibling constraints. The downside, however, lay in the tooling. Every time you add a constraint to a control, you have to add an ID. If the user changes the ID, you have to change it in both places. It's not rocket science, but it's error prone, and it can lead to situations in which the tool is trying to be "too smart".

We moved the layout constraints to be inside the control itself. This fixed the problem of tracking IDs.

Attempt 2:
--
<mx:Panel width="100%" height="100%">
    <mx:Canvas width="100%" height="100%">
        <mx:Button y="30">
            <mx:layoutConstraints>
                <mx:EdgeAnchor right="30" />
            </mx:layoutConstraints>
        </mx:Button>
    </mx:Canvas>
</mx:Panel>

The next issue was that it was confusing to have the canvas inside the panel. People kept accidentally resizing the canvas instead of the panel, or pulling the panel out of the canvas. We also decided that the name "EdgeAnchor" was too verbose.

After much arguing about making things general vs. dealing with the specific needs in the 80% case, we decided to make Panel and Application understand how to do multiple different kinds of layout: absolute, vertical and horizontal.

Attempt 3:
--
<mx:Panel width="100%" height="100%" layout="absolute">
    <mx:Button y="30">
        <mx:layoutConstraints>
            <mx:Anchor right="30" />
        </mx:layoutConstraints>
    </mx:Button>
</mx:Panel>

Right around this time, people were dragging panels into the view, centering them, and expecting them to stay centered no matter what size the browser was. It was especially confusing because Flex Builder has snapping heuristics and the panel would snap into place when it was centered. We dealt with this by adding a centering constraint.


Attempt 4:
--
<mx:Panel width="100%" height="100%" layout="absolute">
    <mx:Button y="30">
        <mx:layoutConstraints>
            <mx:Anchor horizontalCenter="0" />
        </mx:layoutConstraints>
    </mx:Button>
</mx:Panel>

Soon after we made this change, the team was busy doing lots of what we call scenario testing: building applications in the same way our customers would. One thing about layout constraints that stuck out like a sore thumb was its verbosity. Where buttons used to be on a single line, they now occupied 5. We argued about this for a while, and decided to make layout constraints styles, partially because this was consistent with CSS.

We also decided to promote the use of "left" and "top" as opposed to "x" and "y". One might wonder how these are different, but they are. The "left" property is a constraint that specifies the desired position for the left coordinate. The "x" property is the coordinate that an object happens to be at. Thus, if you were to use a move effect to move an object programmtically, its "x" property would change, but its "left" would not.

In any case, the resulting syntax looks like this (not in beta 1, but will be part of beta 2):

Attempt 5:
--
<mx:Panel width="100%" height="100%" layout="absolute">
    <mx:Button top="30" right="30" />
</mx:Panel>

The moral
--
Why did it take so long to arrive at what appears to be an obvious syntax? Perhaps it was too obvious. When we started, we were trying to solve for many different things: forward compatibility, generalizability, and the possibility of many different kinds of constraints. Through trial and error, we arrived at the simplest way to express the basic things we need for this release.

In this case, I am confident we ended up with the right answer, but it took a while. In fact, there are people out there in the Flash community who aren't convinced that layout constraints are a good idea who I think we will turn around based on the new syntax.

Sometimes, these things take time to sort out. The answers always seem obvious in retrospect, but you can't expect to get to the right answer straight away.

Posted by sho at 09:45 PM | Comments (5) | TrackBack

February 15, 2006

MXML text completion control v. 0.5 (aka down with combo boxes!)

I've always been frustrated by the way HTML applications use pull down menus. How many times have you had to pick a country out of a huge pull down menu? Do you use the mouse to scroll down to the country? What about using the keyboard? You have to keep hitting the same key over and over. Neither approach is easy.

Meanwhile, text fields that offer completion hints are starting to become standard for Flex and AJAX applications. Typicaly, these are used to let you quickly pick things that you've already typed into the box. They are not used for picking, say, a country from a list of countries.

I believe text input fields that offer hints for possible completions should be used instead of combo boxes 95% of the time. Down with combo boxes!

1) When you want to use the mouse, it is just as simple.
2) When you want to use the keyboard, it gives you better feedback on what you have typed already.
3) It gives you an obvious affordance to "start over" when you've made a typo.
4) It gives you more immediate feedback.

Here is a relatively simple version of a text completion control for MXML. Unlike most versions of this type of control, this is also optimized for the above case: picking from a list of predefined strings.

Run sample

When you want to pick from a list of predefined strings, just supply the list of all strings as the dataProvider of the control (just like how ComboBox works), and set the "mustPick" flag to true.

Let me know what you think. Are there bugs? Do you think the heuristics are wrong? Is this a good idea in general?

P.S. I've also had to include some other random classes as part of this. I plan on officially distributing these classes and other classes once they are more baked.

Posted by sho at 08:25 PM | Comments (11) | TrackBack

February 02, 2006

10 favorite things about Flex Builder Beta 1

My 10 favorite things about the new Flex/Flex Builder beta

10. Auto-import (use autocomplete on a class name, and the class is imported if needed).
9. Percentage widths now work when changing states.
8. Able to launch multiple debug sessions (handy for testing FES applications that use messaging)
7. Code hints for classes on your class path! (woohoo!)
6. Open type (Ctrl-Shift-T and type the first few characters of your class)
5. Quick outline (Ctrl-O and type the first few characters of your variable/function to jump)
4. Center snapping and center constraints.
3. Goto definition (ctrl-click on classes, variables, etc).
2. Flex Enterprise Services (see http://labs.macromedia.com/wiki/index.php/Flex_Enterprise_Services:Debugging for debugging hints)
1. Startup performance!

Posted by sho at 10:12 AM | Comments (0) | TrackBack