<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-146151570256196403</id><updated>2011-11-04T07:22:43.290-07:00</updated><category term='Traction'/><category term='troubleshooting'/><category term='logging'/><category term='hack'/><category term='terminal'/><category term='AIR cfunited'/><category term='MySQL'/><category term='lifehack'/><category term='bug'/><category term='custom components'/><category term='AIR'/><category term='skinning'/><category term='degrafa'/><category term='Flex'/><category term='Flex Component Kit'/><category term='Mac OSX'/><category term='Open Source'/><category term='binding'/><title type='text'>Behind the UI</title><subtitle type='html'>Inside the world of RIA development</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.behindtheui.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-8945170049012991130</id><published>2010-03-02T09:50:00.000-08:00</published><updated>2010-03-02T09:58:54.745-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac OSX'/><title type='text'>Mac OSX MySQL installation problem</title><content type='html'>I recently installed MySQL via &lt;a href="http://github.com/mxcl/homebrew"&gt;homebrew&lt;/a&gt; (which I highly recommend). However, when I tried to use the MySQL or MySQLAdmin clients I got an error saying that I couldn't connect to the server through /tmp/mysql.sock. Manually creating the file and running mysqld got me a bit further, but mysqld was failing with another error related to permissions. I finally found an official &lt;a href="http://bugs.mysql.com/bug.php?id=11380"&gt;MySQL bug thread&lt;/a&gt; with the solution. Their problem was related to the upgrade installer distributed by MySQL, not with the homebrew install, but I found that the solution worked anyway -- I just needed to run this command (assuming a homebrew installation for the path): &lt;blockquote&gt;/usr/local/bin/mysql_install_db --rpm&lt;/blockquote&gt;&lt;div&gt;Then I launched mysqld and everything was good to go.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-8945170049012991130?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/8945170049012991130/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=8945170049012991130' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8945170049012991130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8945170049012991130'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2010/03/mac-osx-mysql-installation-problem.html' title='Mac OSX MySQL installation problem'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-2504213965778430014</id><published>2009-08-13T08:55:00.000-07:00</published><updated>2009-08-13T09:03:07.413-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AIR cfunited'/><title type='text'>CFUnited Materials</title><content type='html'>My CFUnited powerpoint slides are &lt;a href="http://dl-client.getdropbox.com/u/19957/cfunited/intro%20to%20air.ppt"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And the application I wrote during my session, along with the necessary helper classes, is &lt;a href="http://dl-client.getdropbox.com/u/19957/cfunited/src.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-2504213965778430014?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/2504213965778430014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=2504213965778430014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2504213965778430014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2504213965778430014'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/08/cfunited-materials.html' title='CFUnited Materials'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-8959502563520713115</id><published>2009-08-02T13:17:00.001-07:00</published><updated>2009-08-02T13:37:25.372-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='binding'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><category scheme='http://www.blogger.com/atom/ns#' term='hack'/><title type='text'>Custom Flex Binding with PropertyChangeEvent</title><content type='html'>In Flex we have a very powerful tool in the [Bindable] metadata tag. However, it can also be a huge source of inefficiency. Basically, when you mark a property as [Bindable] the compiler adds code that dispatches a generic PropertyChangeEvent typed "propertyChange" when the property changes. Whenever this event is detected, the bindings on your property are evaluated. This is all good and fine until you have a bunch of properties that are all marked as [Bindable]. Now the player is watching for the same event type ("propertyChange") for all your bindings for all your properties. So any time one of them changes the player has to look through all the properties in your class to find the one that actually needs to have its bindings reevaluated. Very inefficient.&lt;br /&gt;&lt;br /&gt;Thankfully, the people at Adobe at least gave us a way to work around this CPU deathtrap. You can mark your bindings to evaluate on a custom event type like this: [Bindable('myCustomEventType')]. If you use this wisely then the player will know exactly which property to check out when it detects the event, moving the binding evaluation time back from O(n) to constant time. Much better.&lt;br /&gt;&lt;br /&gt;In past projects I've used this method with generic fash.events.Event objects to great success. However, on my current project &lt;a href="http://jonathanbranam.net/"&gt;Jonathan Branam&lt;/a&gt;, the lead architect, wanted to be more "correct" and use custom PropertyChangeEvents. Okay, makes sense, should be no problem, right? Well, it was a small problem. So, I set up my custom binding like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/**&lt;br /&gt;* The current number of bytes loaded by the server during upload.&lt;br /&gt;*/&lt;br /&gt;protected var _bytesLoaded:int = 0;&lt;br /&gt;[Bindable('bytesLoadedChanged')]&lt;br /&gt;/** @copy #_bytesLoaded **/&lt;br /&gt;public function get bytesLoaded():int{&lt;br /&gt;   return _bytesLoaded;&lt;br /&gt;}&lt;br /&gt;public function set bytesLoaded(value:int):void{&lt;br /&gt;   if(_bytesLoaded != value){ // only set the value if its different&lt;br /&gt;      var oldValue:int = _bytesLoaded;&lt;br /&gt;      _bytesLoaded = value;&lt;br /&gt;      dispatchEvent(new PropertyChangeEvent('bytesLoadedChanged', &lt;br /&gt;                    false, false, PropertyChangeEventKind.UPDATE, &lt;br /&gt;                    _bytesLoaded, oldValue, value, this));&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then I ran my code and the bindings didn't evaluate. It looked right to me, so what was the problem? I looked at the documentation for PropertyChangeEvent (duh, probably should have been a first step) and found this for the "property" property: "A String, QName, or int specifying the property that changed." So, the "property" property of the PropertyChangeEvent should be the NAME of the property that changed, not a reference to that property (follow that?). That amounted to changing three characters, instead of: &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dispatchEvent(new PropertyChangeEvent('bytesLoadedChanged', &lt;br /&gt;    false, false, PropertyChangeEventKind.UPDATE, &lt;br /&gt;    _bytesLoaded, oldValue, value, this));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I now have:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dispatchEvent(new PropertyChangeEvent('bytesLoadedChanged', &lt;br /&gt;    false, false, PropertyChangeEventKind.UPDATE, &lt;br /&gt;    'bytesLoaded', oldValue, value, this));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now my bindings work! For some reason having that property set incorrectly was preventing the player from identifying that I had dispatched a binding event. My guess is that it silently was causing an exception in the PropertyChangeEvent constructor, but I'm not sure. What I do know is that now my bindings work, they're efficient, and they're "correct." And I'm happy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-8959502563520713115?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/8959502563520713115/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=8959502563520713115' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8959502563520713115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8959502563520713115'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/08/custom-flex-binding-with.html' title='Custom Flex Binding with PropertyChangeEvent'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-5579829580708236631</id><published>2009-06-10T14:16:00.000-07:00</published><updated>2009-06-10T14:22:35.992-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lifehack'/><title type='text'>How to make Safari 4 open tabs at startup</title><content type='html'>I decided to give Safari 4 a try and the inability to set it to open the last set of tabs at start up is driving me crazy. I found a solution that, although not ideal, works well enough to get me by until Chrome comes out for Mac. Its an easy enough solution: &lt;br /&gt;&lt;br /&gt;1. Create a new bookmark folder&lt;br /&gt;2. Add the tabs you want to be opened to this folder and order them&lt;br /&gt;3. Goto preferences and set "new windows open with:" to your startup tabs folder&lt;br /&gt;&lt;br /&gt;Now when Safari starts it will load those tabs. Unfortunately, these tabs will also load when you open a new window. And if you happen to have a tab open that you wanted to keep and you close the app its gone. You can go to "History-&gt;Reopen all windows from last session" in desperate situations. This isn't as nice as just remembering the tabs that were open (like every other modern browser), but its passable.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-5579829580708236631?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/5579829580708236631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=5579829580708236631' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/5579829580708236631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/5579829580708236631'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/06/how-to-make-safari-4-open-tabs-at.html' title='How to make Safari 4 open tabs at startup'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-8130371577557743563</id><published>2009-06-09T15:29:00.001-07:00</published><updated>2009-06-09T15:41:53.958-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AIR'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><category scheme='http://www.blogger.com/atom/ns#' term='custom components'/><title type='text'>Caching Images to Disk with AIR</title><content type='html'>In an application I'm currently working on, I create images within the application and want to store them to load later. Because I store the image data in a model object that might be used many places in the running application, I keep the data in memory as a BitmapData object. This turned out to be a problem because you can't write BitmapData objects to disk. Instead you have to write the data to a PNG or JPEG and then save the byte array from that object, and reverse the process to load the data. There may be another way but this is the only solution I could find. Here is my class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;package com.effectiveui.models.widget&lt;br /&gt;{&lt;br /&gt;    import flash.display.Bitmap;&lt;br /&gt;    import flash.display.BitmapData;&lt;br /&gt;    import flash.display.Loader;&lt;br /&gt;    import flash.events.Event;&lt;br /&gt;    import flash.utils.ByteArray;&lt;br /&gt;    import flash.utils.IDataInput;&lt;br /&gt;    import flash.utils.IDataOutput;&lt;br /&gt;    import flash.utils.IExternalizable;&lt;br /&gt;   &lt;br /&gt;    import mx.graphics.codec.PNGEncoder;&lt;br /&gt;   &lt;br /&gt;    [RemoteClass(name="com.effectiveui.models.widget.WidgetCategoryDataModel")]&lt;br /&gt;    public class WidgetCategoryDataModel implements IExternalizable&lt;br /&gt;    {&lt;br /&gt;        public var imageData:BitmapData;&lt;br /&gt;       &lt;br /&gt;        protected var loader:Loader;&lt;br /&gt;&lt;br /&gt;        public function WidgetCategoryDataModel(){&lt;br /&gt;            loader = new Loader();&lt;br /&gt;            loader.contentLoaderInfo.addEventListener(&lt;br /&gt;                   Event.COMPLETE, handleBytesLoaded);           &lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public function handleBytesLoaded(event:Event):void{&lt;br /&gt;            imageData = Bitmap(loader.content).bitmapData;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public function writeExternal(out:IDataOutput):void{&lt;br /&gt;            var encoder:PNGEncoder = new PNGEncoder();&lt;br /&gt;            var bytes:ByteArray = encoder.encode(imageData);&lt;br /&gt;            bytes.position = 0; // may not be necessary&lt;br /&gt;            out.writeDouble(bytes.length);&lt;br /&gt;            out.writeBytes(bytes);           &lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        public function readExternal(input:IDataInput):void{&lt;br /&gt;            var length:Number = input.readDouble();&lt;br /&gt;            var pngData:ByteArray = new ByteArray();&lt;br /&gt;            input.readBytes(pngData,0,length);&lt;br /&gt;           &lt;br /&gt;            loader.loadBytes(pngData);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can download the class &lt;a href="http://dl-client.getdropbox.com/u/19957/WidgetCategoryDataModel.as"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-8130371577557743563?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/8130371577557743563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=8130371577557743563' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8130371577557743563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8130371577557743563'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/06/caching-images-to-disk.html' title='Caching Images to Disk with AIR'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-2462537784331079701</id><published>2009-06-09T15:28:00.001-07:00</published><updated>2009-06-09T15:28:55.150-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AIR'/><category scheme='http://www.blogger.com/atom/ns#' term='Traction'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Traction update</title><content type='html'>I added some &lt;a href="http://traction.effectiveui.com/?p=56"&gt;screen shots&lt;/a&gt; to the Traction website.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-2462537784331079701?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/2462537784331079701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=2462537784331079701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2462537784331079701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2462537784331079701'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/06/traction-update.html' title='Traction update'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-1225995243467178025</id><published>2009-04-08T14:55:00.000-07:00</published><updated>2009-04-08T14:56:25.162-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='skinning'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex Component Kit'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Stateful to Stateless Skinning Script</title><content type='html'>&lt;a href="http://patrickhansen.com/blog/index.php/2009/04/08/stateful-to-stateless-jsfl-flash-command?blog=3"&gt;Patrick Hansen&lt;/a&gt; has a cool solution for converting stateful skins to stateless.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-1225995243467178025?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/1225995243467178025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=1225995243467178025' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/1225995243467178025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/1225995243467178025'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/04/stateful-to-stateless-skinning-script.html' title='Stateful to Stateless Skinning Script'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-2539380561405354586</id><published>2009-03-25T12:07:00.000-07:00</published><updated>2009-03-25T12:18:55.432-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='skinning'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex Component Kit'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Followup on Stateful Skinning</title><content type='html'>We got in touch with Adobe about &lt;a href="http://behindtheui.blogspot.com/2009/03/flex-component-kit-cpu-black-hole.html"&gt;our problems with stateful skinning&lt;/a&gt;. Their response was that all the enter frame handlers are necessary for, "tracking size changes, states, playing transitions, and the general overhead involved with implementing IUIComponent." They gave us a special version of the UIMovieClip class that removed logic for handling size changes since that's not necessary in skins. However, even with this change the state-based skins still took up far too much CPU for us to use them in production. In the end we moved to old stateless skins and now our application idles ~10% CPU usage, as is more typical for AIR applications. The take away message: state-based skins are really cool, but they probably aren't suitable for use in applications that are expected to be left open while the user does anything else on their computer. They probably aren't ever suitable for large applications, either.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-2539380561405354586?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/2539380561405354586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=2539380561405354586' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2539380561405354586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2539380561405354586'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/03/followup-on-stateful-skinning.html' title='Followup on Stateful Skinning'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-2855664442584073228</id><published>2009-03-05T10:13:00.001-08:00</published><updated>2009-03-06T14:23:41.272-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex Component Kit'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Flex Component Kit: CPU black hole</title><content type='html'>On a recent AIR project we tried to use state-based skins using the Flex Component Kit, as recommended by Adobe. This route was much cleaner and faster than the old, stateless method. And everything seemed great at first. However, as the app grew we noticed that our CPU usage while idle was getting ridiculous (70-80%) and made the disturbing discovery that removing our CSS allowed our CPU usage to plunge back to standard AIR levels (5-10%).&lt;br /&gt;&lt;br /&gt;With a little more research we determined that the stateful skins seemed to be the culprit, so we created a simple &lt;a href="http://dl-client.getdropbox.com/u/19957/flex%20component%20kit/SkinningTest.zip"&gt;test program&lt;/a&gt; to test our theory. In this program we have a single MXML component that is a standard WindowedApplication component that contains 500 checkboxes. We put together a simple checkbox skin with the stateful method and another that is stateless.&lt;br /&gt;&lt;br /&gt;To establish a baseline we ran the application without loading either CSS file. The resulting CPU usage was within the typical range for AIR applications (5-10%). The 500 boxes themselves did not seem to add any CPU usage, as expected.&lt;br /&gt;&lt;br /&gt;Then we loaded the stateless skin's CSS file and saw identical CPU usage. Again the behavior was what we expected -- skinning does not add any extra CPU usage. Since there is no activity or animation on our check boxes, having a skin loaded should not require ongoing computation. So far so good.&lt;br /&gt;&lt;br /&gt;Finally, we launched the application loading the stateful CSS and this time our CPU usage was a staggering 50% and it stayed there. We &lt;a href="http://dl-client.getdropbox.com/u/19957/flex%20component%20kit/profiler.png"&gt;ran the profiler&lt;/a&gt; to see what was going on. A few seconds after the application started we saw 377 EnterFrameEvents, which is quite a few but not drastically more than other calls in the application. 40 seconds later the count was 5190. Clearly something in the stateful skinning was causing our application to redraw continuously.&lt;br /&gt;&lt;br /&gt;Now that we had established that the stateful skinning was the problem we wondered whether it was something about our SWC itself or if it was something about each individual skin. To test this we ran the application with different numbers of checkboxes. Running with 100 boxes we saw a CPU usage of 19% -- twice that of a normal application, but still relatively low. 250 boxes used 30% CPU, and, as noted above, 500 boxes used 50%. This seems to indicate that there is some error in each skin. We didn't see our checkboxes animating through states, so the state separation seems to be working correctly. There is something in each state's generated skin that doesn't stop playing once its drawn.&lt;br /&gt;&lt;br /&gt;This discovery explains why a Google search doesn't turn up a myriad of complaints about Adobe's new recommended workflow -- the average application probably doesn't have hundreds of skinned components instantiated at once. Many applications probably have 100 or fewer, meaning that their increased CPU usage may not have set off any red flags. Furthermore, many applications include running animations or additional processing which would take the blame for increased CPU usage. No matter the reason, it appears that this bug has largely gone unnoticed but can have devastating effects on large applications. Hopefully Adobe will have a fix out quickly, otherwise it will be back to stateless skinning misery for us.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://patrickhansen.com/blog/index.php/2009/03/05/flex-stateful-skins-vs-stateless?blog=3"&gt;Read more&lt;/a&gt; about stateful skinning from Patrick Hansen, one of the designers at EffectiveUI who worked on this project with me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-2855664442584073228?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/2855664442584073228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=2855664442584073228' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2855664442584073228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/2855664442584073228'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/03/flex-component-kit-cpu-black-hole.html' title='Flex Component Kit: CPU black hole'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-1748593143458172800</id><published>2009-01-16T16:19:00.001-08:00</published><updated>2009-01-16T16:24:15.792-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Traction'/><title type='text'>Traction: Version 1.0.4</title><content type='html'>&lt;p&gt;Version 1.0.4 of Traction was just released. This version fixes a bug where tickets created in Traction did not have the "Reported By" field filled in. Now Traction automatically sets the logged in user as the reporter when tickets are created. Here's the link to the &lt;a href="http://traction.effectiveui.com/?page_id=3" mce_href="http://traction.effectiveui.com/?page_id=3"&gt;downloads page&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;I was also asked to add support for the Comments section of the Trac interface. Unfortunately, this area is currently not viewed as part of a ticket object and the XML-RPC implementation for Trac only gives access to the Ticket and Wiki areas. If support is ever added for the Comments section I will be happy to add it to Traction.&lt;/p&gt; &lt;p&gt;Version 1.0.4 was created as a direct response to a request by a Traction user. If you use Traction and find bugs or wish it had features it doesn't, please comment &lt;a href="http://traction.effectiveui.com"&gt;here&lt;/a&gt; or &lt;a href="mailto:greg.owen@effectiveui.com"&gt;send me an email&lt;/a&gt;. I'm not actively developing Traction right now, so the only way bugs are going to be fixed or features added is if you make me aware of the need.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-1748593143458172800?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/1748593143458172800/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=1748593143458172800' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/1748593143458172800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/1748593143458172800'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2009/01/traction-version-104.html' title='Traction: Version 1.0.4'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-8192251760210212198</id><published>2008-10-27T10:25:00.000-07:00</published><updated>2008-10-27T10:30:36.713-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='terminal'/><title type='text'>Mac Terminal Themes</title><content type='html'>I recently &lt;a href="http://blog.infinitered.com/entries/show/6"&gt;upgraded the view on my terminal&lt;/a&gt; to be something more readable than anything that comes with OSX. However, I don't really like the red color that comes with that theme -- I feel like its more of a salmon color, and I want something really RED for my red. Anyway, I changed the color using the Terminal preferences, no problem. But every time I restarted the terminal my color scheme would reset to the colors that came with IR_Black. The problem is there is no way to edit a color theme for Terminal, so all changes are temporary unless you create a new color theme. The solution: make all the color changes you want to your theme, then use the options menu to "Duplicate Settings" and make a new color theme. Its kind of a pain to have to create a whole new color theme every time you want to tweak, but it works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-8192251760210212198?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/8192251760210212198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=8192251760210212198' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8192251760210212198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/8192251760210212198'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2008/10/mac-terminal-themes.html' title='Mac Terminal Themes'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-6310670186043914883</id><published>2008-10-10T08:39:00.000-07:00</published><updated>2008-10-10T09:18:29.101-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='degrafa'/><category scheme='http://www.blogger.com/atom/ns#' term='skinning'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Skinning in Degrafa</title><content type='html'>I know, I'm way behind the times, but I had my first experience skinning in &lt;a href="http://www.degrafa.com/"&gt;Degrafa&lt;/a&gt; on a project last week. My perception of Degrafa previously was that it was a nice framework to give you markup for skinning, but doesn't really add anything over the Flex drawing API. This is partially true -- Degrafa doesn't do anything you can't do on your own with the graphics package. However, in true component programming spirit, they've abstracted the graphics package to the point that its almost stupid-easy to use. For very simple tasks it might not be worth bringing in Degrafa. For example, on this project I needed to skin a radio button to be pill shaped. I could have used Degrafa, but with the drawing API this is all the code I needed (in a skin class):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;protected override function updateDisplayList(w:Number, h:Number):void{&lt;br /&gt;   super.updateDisplayList(w, h);&lt;br /&gt;   graphics.clear();&lt;br /&gt;   if(name == "upSkin"){&lt;br /&gt;      graphics.beginFill(UP_COLOR);&lt;br /&gt;      graphics.lineStyle(1,UP_COLOR);&lt;br /&gt;   } else {&lt;br /&gt;      graphics.beginFill(DOWN_COLOR);&lt;br /&gt;      graphics.lineStyle(1,DOWN_COLOR);&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   graphics.drawCircle(w - h/2, h/2, h/2);&lt;br /&gt;   graphics.drawCircle(h/2, h/2, h/2);&lt;br /&gt;   graphics.endFill();&lt;br /&gt;   if(name == "upSkin"){&lt;br /&gt;      graphics.beginFill(UP_COLOR);&lt;br /&gt;      graphics.lineStyle(1,UP_COLOR);&lt;br /&gt;   } else {&lt;br /&gt;      graphics.beginFill(DOWN_COLOR);&lt;br /&gt;      graphics.lineStyle(1,DOWN_COLOR);&lt;br /&gt;   }&lt;br /&gt;   graphics.drawRect(h/2, 0, w - h, h);&lt;br /&gt;   graphics.endFill();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Not bad -- forget the repeated if statement and its only 13 lines of code. But later in the project I needed to put a gradient in the background of several components. I could have used the drawing API and a skin to accomplish this, or I could just add this inside the components MXML:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;degrafa:Surface height=&amp;quot;100%&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt; &amp;lt;degrafa:fills&amp;gt;&lt;br /&gt;  &amp;lt;degrafa:LinearGradientFill id=&amp;quot;backgroundFill&amp;quot; angle=&amp;quot;90&amp;quot;&amp;gt;&lt;br /&gt;   &amp;lt;degrafa:GradientStop alpha=&amp;quot;1&amp;quot; color=&amp;quot;0x555555&amp;quot; ratio=&amp;quot;0&amp;quot;/&amp;gt;        &lt;br /&gt;   &amp;lt;degrafa:GradientStop alpha=&amp;quot;1&amp;quot; color=&amp;quot;0x787878&amp;quot; ratio=&amp;quot;0.2&amp;quot;/&amp;gt;&lt;br /&gt;  &amp;lt;/degrafa:LinearGradientFill&amp;gt;&lt;br /&gt; &amp;lt;/degrafa:fills&amp;gt;&lt;br /&gt; &amp;lt;degrafa:GeometryGroup&amp;gt;&lt;br /&gt;  &amp;lt;degrafa:RegularRectangle height=&amp;quot;{height}&amp;quot; width=&amp;quot;{width}&amp;quot; fill=&amp;quot;{backgroundFill}&amp;quot;/&amp;gt;&lt;br /&gt; &amp;lt;/degrafa:GeometryGroup&amp;gt;&lt;br /&gt;&amp;lt;/degrafa:Surface&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I prefer this way. Not only is it less code, but its easier to think about. And, in my opinion, that's what OO and component programming are all about: tackle a difficult task on the scale where its a simple task and create an abstraction so that the complex task becomes a series of easy tasks. Degrafa went down into the drawing API where each task is (relatively) easy and built up a framework so that we can do complex tasks as a series of completely intuitive easy tasks. Degrafa can also be used to make skin classes (as opposed to putting the Degrafa right in the component layout like I did in this case) and works great with CSS. They have tons of examples with source on their &lt;a href="http://www.degrafa.com"&gt;site&lt;/a&gt; and you can also get the SWC there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-6310670186043914883?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/6310670186043914883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=6310670186043914883' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/6310670186043914883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/6310670186043914883'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2008/10/skinning-in-degrafa.html' title='Skinning in Degrafa'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-4244709191178459491</id><published>2008-09-18T12:03:00.001-07:00</published><updated>2008-09-19T08:20:25.122-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AIR'/><category scheme='http://www.blogger.com/atom/ns#' term='Open Source'/><category scheme='http://www.blogger.com/atom/ns#' term='Traction'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Announcing Traction</title><content type='html'>Over the last several months I've been working on an app called Traction which is now ready for the general public, although it is still in development. Traction is an Open Source (GPL) frontend to the ticket tracking service Trac. Traction is written in AIR and uses Adobe Flex. Using Traction you can interact with your Trac tickets real time — in line edting, tabbing between fields, and filtering. This makes the process of organizing and modifying tickets far easier than the HTML interface offered on the web. Also, Traction caches your tickets locally so you only have to update new tickets every time you login, which makes the experience that much faster. You can also use Traction to create new tickets and track user “scores” which are calculated based on the priority of tickets closed by each user. Visit the &lt;a href="http://traction.effectiveui.com/?page_id=3"&gt;Traction download page&lt;/a&gt; for the repository URL and the latest binary.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-4244709191178459491?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/4244709191178459491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=4244709191178459491' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/4244709191178459491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/4244709191178459491'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2008/09/announcing-traction.html' title='Announcing Traction'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-146151570256196403.post-3062573677970302288</id><published>2008-09-04T13:17:00.000-07:00</published><updated>2008-09-04T13:18:12.044-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='logging'/><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><category scheme='http://www.blogger.com/atom/ns#' term='custom components'/><title type='text'>Adding Exclusion to Flex Logging</title><content type='html'>&lt;p&gt;The built in Flex logging package (mx.logging.*) provides a "filters" property for logging targets that allows developers to dictate which classes' logging messages get logged by each target. However, this is an "inclusive" filter. What I mean by that is that this filter tells you what &lt;em&gt;does&lt;/em&gt; get logged. There is no support for an "exclusive" filter which would allow developers to specify certain classes to ignore logging messages from. I can see the benefits of an inclusive filter, but for a project the size of eBay Desktop we really need an exclusive filter so we can easily see logging messages for the entire app &lt;em&gt;except&lt;/em&gt; for areas we aren't currently interested in. Its sometimes more important to filter out than to filter in.&lt;/p&gt;&lt;p&gt;It turns out that adding an exclusive filter to Flex's logging package is fairly simple. All you need to do is modify the base target interface, ILoggingTarget, to have an additional array of classes to ignore. Then you modify the Log class so that anytime it wants to add a logger to a target it checks to see that the logger's category is both included in the target's filter array and not excluded by the exclusion array. If not then the logger is not added to the target. This amounts to about five lines of code between the addTarget and getLogger functions of Log. &lt;/p&gt;I've subclassed the core logging classes (Log and ILoggingTarget) as well as some of the base target classes (AbstractTarget, LineFormattedTarget, TraceTarget) to include exclusion filtering. You can download a zip &lt;a class="snap_shots" href="http://dl.getdropbox.com/u/19957/exclusion_logging.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/146151570256196403-3062573677970302288?l=www.behindtheui.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.behindtheui.com/feeds/3062573677970302288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=146151570256196403&amp;postID=3062573677970302288' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/3062573677970302288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/146151570256196403/posts/default/3062573677970302288'/><link rel='alternate' type='text/html' href='http://www.behindtheui.com/2008/09/adding-exclusion-to-flex-logging.html' title='Adding Exclusion to Flex Logging'/><author><name>Greg</name><uri>http://www.blogger.com/profile/17458446657879824670</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
