Renju – AS3 board game built with the PureMVC framework.
This is awesome. Here is a polished board game with nice graphics and sounds - full AI - and best of all, the complete source. This is a great example of PureMVC in action. All of the projects on Actionscript Notes utilize PureMVC, and the owner seems to be generous with the source. Many thanks for that.
Christian Cantrell's ever-growing selection of useful Actionscript 3/AIR libraries.
Here's the full list of what he has on Google Code.
The CoreLib is essential, but he's been busy writing useful libraries for AIR. I've used the notification library, which has some great features and makes notifications on the desktop sensible. The exchange library is interesting, for sure. They are all worth checking out.
There are also a number of interesting example applications using AIR, including a motion detection application, an Amazon S3 interface, a screen saver, and an MS Exchange calendar.
I had an epiphany as to what polymorphism is last night.
Yes, I know I am late to the party. I can't even estimate how many times I've read a definition of an Interface over the last year. It has been a concept that totally baffled me for a long time, then I sorta understood, and last night I actually implemented an Interface intentionally and correctly. The same is true for the term polymorphism. I can read definitions until my eyes hurt, but until I actually use it, the words are just abstractions. These two concepts are synonymous, as Interfaces in Actionscript 3 are a convenience for type-checking polymorphism in APIs.
'Ah-ha!'
In this case, I had a Flex container that I wanted to pass to a method. I didn't know what the container would be, but I knew that it would be a container. IContainer? Yup. Now the method couldn't care less what class is being passed its way, as long as it has the appropriate methods that match the IContainer signature.
Now it makes sense. This is the reason I keep reading that I need to use implicit getters and setters with private class properties instead of public properties. Public properties are not allowed in an interface, so you aren't able to abstract these properties with an interface. Getters and setters ARE allowed in an interface, so now you can expose these properties and ensure that methods expecting the interface get what they are looking for. As it turns out, people weren't recommending this simply to get me to write extra lines of code
The pieces are starting to come together. Abstract classes, design patterns, polymorphism, interfaces... PureMVC has been a help. The framework pushes me towards proper OOP and the use of design patterns. Stacking knowledge, one block at a time.
Flex component transformation with ObjectHandles
Rogue Development's ObjectHandles is a great component. It is simple to use and provides all of the functionality that I was looking for in a transformation modifier. Prior to implementing this, I was using the TransformTool from senocular.com's library. This is also a great utility, but it wants to scale my content, and that was a deal breaker in my current project. ObjectHandles does NOT resize the child components. There are use cases for both approaches, but ObjectHandles allows me to decide when to scale.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <?xml version="1.0" encoding="utf-8"?> <objectHandles xmlns="com.roguedevelopment.objecthandles.*" xmlns:mx="http://www.adobe.com/2006/mxml" allowRotate="false" objectMovedEvent="objectUpdatedHandler( event )" width="{image.width}" height="{image.height}" > <mx:Script> <![CDATA[ import com.roguedevelopment.objecthandles.ObjectHandlesMouseCursors; public static const CHANGE:String = "imageBoxChanged"; private function objectUpdatedHandler( event:Event ):void { dispatchEvent( new Event( CHANGE, true ) ); } ]]> </mx:Script> <mx:Image id="image" source="@Embed(source='/assets/cower.png')" /> </objectHandles> |
Animated collision of two crane trucks.
I spend a lot of my time animating horrific accidents. It's a great way to make a living - once you get past the original reference pictures. The above is from a case that recently cleared. Watch your speakers, it has audio.
Accessing a List from its itemRenderer
This was really throwing me off tonight. I could not figure out how to get at the List from its itemRenderer. this.parent... no this.owner... no
So I started The Search™, trying to hunt down The Answer™. This is one of those things that doesn't just jump out of the manual at you. Luckily, Peter Ent is running an extremely informative series on itemRenders. He says to get at the List, we need to simply access the listData property. But there's a catch:
Most controls such as Text, Label, Button, CheckBox, and so forth, implement IDropInListItemRenderer. Most containers, such as HBox, Canvas, etc. do not implement that interface. If you want to use listData in an itemRenderer that extends a Container you will have to implement IDropInListItemRenderer yourself - I'll cover that in the next article.
Luckily for us, it is a simple process:
1 | <mx:hbox xmlns:mx="http://www.adobe.com/2006/mxml" implements="mx.controls.listClasses.IDropInListItemRenderer"></mx:hbox> |
We will need to implement the appropriate methods in the itemRenderer's Script tag also:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import mx.controls.listClasses.BaseListData; private var _listData:BaseListData; private var list:List; public function get listData() : BaseListData { return _listData; } public function set listData( value:BaseListData ) : void { _listData = value; list = listData.owner as List list.addEventListener( ListEvent.CHANGE, onListChange, false, 0, true ) } |
In my case, I wanted to listen for changes so that I could set one of the itemRenderer sub-component's properties whenever the list had changed. Now to access the List:
if ( list.selectedItem == data ) doSomeStuff();
Easy as that.
PureMVC Python port has been released
I am very excited about the potential of this port. As far as I can tell, there isn't a whole lot out there for MVC and Python. There are a handful of server/web MVC frameworks. Django is the one that I have experience with, and it is a fantastic framework. As is true when comparing PureMVC to most frameworks, however, the PureMVC for Python is dead simple. It is a straight port, implementing PureMVC in the same fashion as we see in the original AS3 version. Structurally it differs from Actionscript - it is Python after all - but fundamentally it is the same.
Many thanks to Toby de Havilland for overcoming some of the initial issues with the port, and getting it up and running. Thanks also to Nathan Levesque for running point on the project and getting the ball rolling with the port
Here it is running the Employee Admin demo on XP and OSX:
5 reasons PureMVC kicks ass
PureMVC is a lightweight MVC framework originally written for Actionscript 3, but which has subsequently been ported to a host of other platforms. I'm a PureMVC fan boy. There is no denying it. Here are few reasons why:
Simplicity
There are a few MVC frameworks available for Actionscript 3. None of them are, well... as pure. PureMVC is simple by design. It doesn't provide you with a lot of bells and whistles. What it does is provide a solid foundation upon which to add your own functionality in limitless ways. The framework itself is simple. 'Make it as simple as possible, but no simpler,' said Albert Einstein. This is one of the core goals of PureMVC.
Community
The PureMVC community is growing. The Architect's Lounge has high signal to noise ratio. Cliff makes a super human effort to personally answer every question that is posted. Among the helpful community members is Chandima Cumaranatunge, the co-author of a book that anybody interested in OO Actionscript should own, Actionscript 3 Design Patterns.
Cliff has created the PureMVC Manifold Project, which provides a well crafted central location for official PureMVC releases, and a host of ports, demos, and utilities developed by the community. This wealth of information provides a high quality code base to use and learn from.
Reusability
As you can see by poking around the Manifold Project, there are a lot of utilities and modules already available. The basic structure of a PureMVC project naturally lends itself to the creation of common classes that can be reused across many projects. Major components don't care who is sitting beside them. They happily wait for their notifications and respond without considering their neighbors. In some circles this would be considered rude, but in a PureMVC application this gives a wonderful, loosely couple application that can be refactored and recycled.
Portability
The port to Python is done! PureMVC is on Flex, Flash, AIR, FlashLite, Python, .NET, Windows Mobile, Silverlight, J2ME, SE, EE, JavaFX, PHP and ColdFusion. This speaks to the simplicity of the framework. It is so fundamental, that most of the ports were just straight translation.
Cliff
Cliff is samurai. He is the benevolent, tireless dictator that any flowering project needs. He is actively engaged with the community. On a daily basis he is on the forums answering questions. The structuring of the ever-expanding pile of resources and information into something useful is daunting task. There it is with the Manifold Project. It is inspiring to see someone so passionate about what they do, and channeling that passion back at anyone interested in learning.
Thanks Cliff, your efforts are greatly appreciated.
Complex DataGrid filterFunction in Flex/Air
So I need to add robust user controlled filtering options in the application I am working on. This problem has dogged me for a month or two, with my co-workers politely insisting on more and better ways to filter their data. This is completely understandable. When you are staring down a datagrid that is virtually 12 feet long, getting to what you actually want to see is important.
This 2006 article from Bruce Phillips was a big help in the basic concept of multiple input filtering. As is his normal style, it is littered with good references to the source material from his research. This is only considering two filter parameters, however, and I need to consider n parameters, that will probably shift as the needs of the end user grow.
The problem is that each new filtering parameter exponentially increases the complexity of the filterFunction. I can see the pattern, but I haven't quite reached the epiphany that I need to break it down into one of those tight little functions I see real programmers creating (unfortunately, I haven't seen any that address my specific problem).
I thought I'd share my naive, and perhaps confusing filterFunction. It would be great to hear how others approach this problem, and come up with a concise solution.
This function utilizes a TextInput to get text search input from the user. It uses
a series of CheckBoxes for various status condition to generate an array [ 1, 0, 1, 1 ]
to represent their on/off state. It also uses two combo boxes to filter by staff members
as well as clients.
filterFunction:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | private function filterCases(item:CaseVO):Boolean { //is the item visible or hidden var result:Boolean = false; //array of CheckBox.selected property values // in handy ones and zeroes. This is returned from a function // that takes the actual CheckBoxes as input and gives me this. var case_mask:Array = [ 1, 0, 1, 1 ]; //a couple VOs to verify the selectedItem in the ComboBoxes. var client:PersonVO; var expert:PersonVO; //array iteration variable var i:int; var dateFormat:DateFormatter = new DateFormatter( ) dateFormat.formatString = 'MM/DD/YY' //text search if ( caseGrid.filter.caseNumInput.text.length > 0 ) { var searchResult:Boolean = ( ( String( '0' + item.case_id.toString() ).search( caseGrid.filter.caseNumInput.text ) >= 0 ) || ( item.style.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 ) ); if( item.description ) { searchResult = searchResult || ( item.description.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 ) } if ( item.client_id ) { searchResult = searchResult || ( personProxy.personFromId( item.client_id ).fullName.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 ) } if ( item.date_of_accident ) { searchResult = searchResult || ( dateFormat.format(item.date_of_accident).search( caseGrid.filter.caseNumInput.text ) >= 0 ) } for ( i = 0; i < case_mask.length; i++ ) { if ( caseGrid.filter.client.selectedItem is PersonVO ) { client = caseGrid.filter.client.selectedItem //is an expert selected for filtering also? if ( caseGrid.filter.expert.selectedItem is PersonVO && searchResult) { expert = caseGrid.filter.expert.selectedItem; //filter for client, expert, and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id && item.expert_id == expert.id && searchResult ) result = true; } else { //filter for client and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id && searchResult ) result = true; } } else if ( caseGrid.filter.expert.selectedItem is PersonVO ) { expert = caseGrid.filter.expert.selectedItem; for ( i = 0; i < case_mask.length; i++ ) { //filter for expert and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.expert_id == expert.id && searchResult ) result = true } } else { //filter for client and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && searchResult ) result = true; } } } //see if a client is selected for filtering else if ( caseGrid.filter.client.selectedItem is PersonVO ) { client = caseGrid.filter.client.selectedItem for ( i = 0; i < case_mask.length; i++ ) { //is an expert selected for filtering also? if ( caseGrid.filter.expert.selectedItem is PersonVO ) { expert = caseGrid.filter.expert.selectedItem; //filter for client, expert, and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id && item.expert_id == expert.id ) result = true; } else { //filter for client and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id ) result = true; } } } //client isn't selected, check the expert filter. else if ( caseGrid.filter.expert.selectedItem is PersonVO ) { expert = caseGrid.filter.expert.selectedItem; for ( i = 0; i < case_mask.length; i++ ) { //filter for expert and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.expert_id == expert.id ) result = true } } //finally, filter for the status else { for ( i = 0; i < case_mask.length; i++ ) { //filter for status if ( case_mask[i] && item.status == String( i + 1 ) ) result = true; } } return result } |
You can see that I am repeating myself from the top down to the final else statement, cutting the filter parameters down like a layer cake. It works, and is relatively quick on a grid with 1500 or so items. This just screams "REFACTOR ME" every time I see it.

















