Converting MX Component to Spark Component ( Part 2 – a )
In the previous post we began building our custom Spark colorPicker by creating the preview class and it's corresponding default skin.
In this post we will begin by creating a KeyboardManager class that will allow us to calculate the new index in the dataProvider after a Up/Down/Left/Right keyboard input.
This class can become quite complicated because we want the following behaviour from our keyboard manager.
The selected index should be able to move up past the top of the dataProvider and move to the bottom row and vice versa, in addition we should be able to go off the left hand side of the columns and appear on the right hand side and vice versa.
For example if the dataProvider has 25 items and each row contains 5 columns.
Up / Down Examples.
If we are at column 1 row 1 ( box 1 ) and we press up we should go to column 1 row 5( box 2 ) and then for every key press we should move up the column until we hit the row 1 column 1 index again. However if we press down then we should go to column 1 row 2 and so forth until we hit column 1 row 5 ( box 2 ). Then when we press down again we should move to the top of the column again row 1 column 1 ( box 1 ) and this should be the case for each column no matter how many rows it has.
Box 1 Box 2


Left / Right Examples
Again if we start in column 1 row 1 ( box 1 ) and we press left then we should end up in column 5 row 1 ( box 3 ) and each subsequent left keyboard event should decrement the column number until it reaches 1 again.
If we press the opposite right key then we should move right along the columns until we hit column 5 row 1 ( box 3 ) then we should loop back to column 1 row 1 ( box 1 ).
Box 1 Box 3


This is all quite simple however what should happen if we have an odd number in our dataProvider? For example a 5*5 grid with a 19 color dataprovider? like follows.

We need to make sure that pressing left and right still stay on the same row no matter how many items are in that row for example if we move from column 1 row 5 ( box 4 ) left we should end up in column 4 row 5 ( box 5 ) and vice versa if we press right from box 5 we should end up in box 4.
Box 4 Box 5


Also if we are in box 5 and we press down we should still go to the first item in the column. Furthermore if we are in position column 5 row 4 ( box 6 ) and we press down we should still go to position column 5 row 1 ( box 3 ). And if we press right then we should go to column 1 row 4 ( box 7 ) and vice versa.
Box 6 Box 7


Ok now we can actually get started writing some code. To begin we will need to know two things
1. The length of the dataProvider and 2. the number of columns to split it into. Next we need to know the current index and the keyboard input the user pressed. Knowing this lets create the following Interface.
package co.uk.betadesigns.components.colorpicker.interfaces { import flash.events.KeyboardEvent; public interface ISparkColorPickerKeyboardManager { /** * Set the number of columns that the ColorPicker has this * is used in calculating the new index after keyboard events * are pressed. The number of rows is also automatically calculated * from dataProviderLength / numberOfColumns. * * @param value int representing the number of columns. * @return int representing the number of columns. * */ function set numberOfColumns( value : int ) : void; function get numberOfColumns() : int; /** * Set the length that the ColorPicker dataProvider has. * This is used in calculating the new index after keyboard events * are pressed. The number of rows is also automatically calculated * from dataProviderLength / numberOfColumns. * * @param value int representing the number of columns. * @return int representing the number of columns. * */ function set dataProviderLength( value : int ) : void; function get dataProviderLength() : int; /** * The KeyDown method takes a keyboard event and the current * index in the dataprovider it then calculates the new index * based on the keyboard button pressed UP/DOWN/LEFT/RIGHT * It uses the numberOfColumns and numberOfRows in order to * calculate the the new index. * * @param event the keyboard event. * @param index the current index in the dataProvider. * * @return returns the new index in the dataProvider. * */ function keyDown( event : KeyboardEvent, index : int ) : int; } }
Next we need to create two classes the first is our empty SparkColorPickerKeyboardManager and the second is the TestCase to go along with it.
package co.uk.betadesigns.components.colorpicker { import co.uk.betadesigns.components.colorpicker.interfaces.ISparkColorPickerKeyboardManager; import flash.events.KeyboardEvent; import flash.ui.Keyboard; public class SparkColorPickerKeyboardManager implements ISparkColorPickerKeyboardManager { private var _numberOfColumns : int = 1; public function get numberOfColumns():int { return _numberOfColumns; } public function set numberOfColumns(value:int):void { if( value < 1 ) value = 1; _numberOfColumns = value; } private var _dataProviderLength : int = 0; public function get dataProviderLength():int { return _dataProviderLength; } public function set dataProviderLength(value:int):void { if( value < 1 ) value = 1; _dataProviderLength = value; } public function keyDown( event : KeyboardEvent, index : int ) : int { return index; } } }
As you can see from the source above we have put in some simple checks on the setters to make sure the numberOfColumns and the dataProviderLength value can never be set to below 1 this will stop us from returning NaN and Infinity values.
For our TestCase we won't bother testing those two setters as they are quite simple so just setup your empty SparkColorPickerKeyboardMangerTests.as with 4 failing tests for our events Up/Down/Left/Right
package flexUnitTests.co.uk.betadesigns.components.colorpicker { import co.uk.betadesigns.components.colorpicker.SparkColorPickerKeyboardManager; import flash.events.KeyboardEvent; import flash.ui.Keyboard; import flexunit.framework.Assert; public class SparkColorPickerKeyboardManagerTests { private var _class : SparkColorPickerKeyboardManager; [Before] public function setUp():void { _class = new SparkColorPickerKeyboardManager(); } [After] public function tearDown():void { _class = null; } [Test] public function testKeyUp():void { Assert.fail( "Test Not implemented yet." ); } [Test] public function testKeyDown():void { Assert.fail( "Test Not implemented yet." ); } [Test] public function testKeyLeft():void { Assert.fail( "Test Not implemented yet." ); } [Test] public function testKeyRight():void { Assert.fail( "Test Not implemented yet." ); } private function up() : KeyboardEvent { return new KeyboardEvent( KeyboardEvent.KEY_DOWN, true, false, 0, Keyboard.UP ); } private function down() : KeyboardEvent { return new KeyboardEvent( KeyboardEvent.KEY_DOWN, true, false, 0, Keyboard.DOWN ); } private function left() : KeyboardEvent { return new KeyboardEvent( KeyboardEvent.KEY_DOWN, true, false, 0, Keyboard.LEFT ); } private function right() : KeyboardEvent { return new KeyboardEvent( KeyboardEvent.KEY_DOWN, true, false, 0, Keyboard.RIGHT ); } } }
You will notice that I have also included four helper methods at the bottom of the class to simulate keypress' for our tests.
I have also added Assert.fail() to each test so that we can make sure they are working. I always do this just as a sanity check.
We need to test all the boundaries of our keyboard manager so we should test negative numbers as well as numbers that go beyond our dataProvider length to ensure they return expected values. In Addition we need to make sure our grids from above return the expected results.
We will begin fleshing out our tests in part 2-b




