Archive for May, 2009
Flex Menu Monkey Patch: Allowing a Branch to be Selected
Posted by: | CommentsSo the interaction designers decided that the branches of a menu needed to be selectable (picture a warped checkbox tree, only it’s a menu… and there aren’t any checkboxes). The default behavior of a Menu in Flex can be described as follows:
1. If you click on a leaf, the leaf is selected.
2. If you click on a branch, the sub menu opens up.
3. If you hover over a branch, the sub menu opens up.
I needed to change #2 to select the branch.
The first thing that I did was sublcass Menu (we’ll call it NewMenu), and overrode (with copies) two obvious methods on the Menu class:
override protected function mouseUpHandler(event : MouseEvent) : void override protected function mouseDownHandler(event : MouseEvent) : void
In those two methods you’ll need to remove any expressions containing something similar to the following:
_dataDescriptor.isBranch(item)
This will allow the branches of your sub class to be treated as leaves. The problem is that Menu class contains this line:
private var subMenu:Menu;
which forces all sub menus to be of type Menu– the only NewMenu will be at the very top! We need all sub menus to be NewMenus. So this is where the monkey patching comes in. You have to change subMenu to a protected variable. Then in your NewMenu class, you can override the function:
mx_internal override function openSubMenu(row : IListItemRenderer) : void
And change the line:
menu = new Menu();
to
menu = new NewMenu();
And you’re done! The reason you need subMenu to be protected is because later on in the method you assign menu to subMenu, and you couldn’t do that from a subclass when it was private. Voila. Interaction designers satisfied.
Responding to FocusIn and focusOut Events
Posted by: | CommentsI recently had some problems responding to focusIn and focusOut events, trying to handle them from my MXML code. After some experimentation and poking around, I discovered the solution was to override the focusInHandler and focusOutHandler methods. These are protected methods inherited mx.core.UIComponent class.
My particular application was to stop a timer that was triggering events when the control had focus, and to restart the timer when focus was lost. Since you’ll be overriding the method, be sure to call super.focusInHandler(event) to ensure the default behavior is still maintained (unless your reason is to override rather than extend the default behavior). Here are the method signatures:
override protected function focusInHandler(event : FocusEvent) : void override protected function focusOutHandler(event : FocusEvent) : void
Autoscrolling a Flex TextArea
Posted by: | CommentsIn the little suite of console widgets I created, I was having more difficulty than necessary getting my console to scroll to the bottom as text was added. On account of (what I suspect were) timing issues, when I tried executing console.verticalScrollPosition = console.maxVerticalScrollPosition; the maxVerticalScrollPosition has not been updated to reflect the text that had been added.
I was not adding the text directly to the text property of the TextArea, rather it was bound to a String in my model. None of the obvious events fired by TextArea seemed to be doing the trick, the maxVerticalScrollPosition was always what it was previously. However, on the next update it would change to reflect what I wanted it to become after the previous update (in other words it was a step behind).
I solved the problem by adding a binding to console’s text property, like this:
BindingUtils.bindSetter(scroll, this.console, "text");
And the scroll method contained the boilerplate console.verticalScrollPosition = console.maxVerticalScrollPosition; statement. That seemed to do the trick. What I was going to try next was a little more extreme:
BindingUtils.bindSetter(scroll, this.console, "maxVerticalScrollPosition");
but fortunately I didn’t have to.
Data Model Injector (poke)
Posted by: | CommentsI needed to build a simple command line interpreter for an application I am building, well, I didn’t need to but I wanted something inconspicuous that the testers (and interaction designers) wouldn’t see and complain about. So I threw something together, it was fun.
Two basic operations I was constantly needing to do was to get a value and/or modify a value. I modified my peek method to return the object in question. And then I also wrote a poke method that allows me to set a value specified by a dot notation.
Here’s the code to the modified peek:
private function peek(prop : String) : * {
try {
var a : Array = prop.split('.');
var x : * = model;
for (var i : int = 0; i < a.length; i++) {
x = x[ a[i] ];
}
return x;
}
catch (error : Error) { return "undefined"; }
}
You can see it’s just a one line change from my previous implementation. And here is poke:
private function poke(prop : String, val : *) : void {
try {
var a : Array = prop.split('.');
var x : * = model;
for (var i : int = 0; i < a.length-1; i++) {
x = x[ a[i] ];
}
x[ a[a.length-1] ] = val;
}
catch (error : Error) { trace(error.message); }
}
In this method I had to iterate one less time to get the parent of the object I wanted to assign to. And then on the final line I manually grabbed it to perform the assignment.
My initial wrong assumption was that I could do this in my loop:
for (var i : int = 0; i < a.length; i++) {
x = x[ a[i] ];
}
x = val;
Take a look at it, or step through it in a debugger to see why that doesn’t work. It was pretty enlightening.

