Crazy code

Actionscript, Flex No Comments »

Sometimes you have to write a method for something crazy complicated. In this snippet I had a container with a load of TextFlow text in it and I needed to zoom into a specific link within the flow while also scaling the container and positioning it within a Scroller viewport without the container going out of the bounds of the Viewport when the link is centered within it.

This is meaningless to anyone but me but sometimes you just have to share the pain.

Zoom in to a link in a TextFlow within a scaled container in a Scroller viewport

  1. private function moveAndZoomToLink(link:LinkElement,p:Person):void
  2.                         {
  3.                                 var linkStart:int = link.getAbsoluteStart();
  4.                                 var textFlowLine:TextFlowLine = textFlow.flowComposer.findLineAtPosition(linkStart);
  5.                                 var linkPositionOnLine:int = linkStart-textFlowLine.absoluteStart;
  6.                                        
  7.                                 var controllerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(linkStart);
  8.                                 var controller:ContainerController = textFlow.flowComposer.getControllerAt(controllerIndex);
  9.                                 var containerSprite:Sprite = controller.container;
  10.                                
  11.                                 var rect:Rectangle = textFlowLine.getBounds();
  12.                                 var atomRect:Rectangle = textFlowLine.getTextLine().getAtomBounds(linkPositionOnLine+1);
  13.                                 var nameWidth:Number = p.firstName.length*atomRect.width;
  14.                                
  15.                                 var globalAtomRect:Rectangle = new Rectangle(rect.x+(atomRect.x-atomRect.width),rect.y,nameWidth,rect.height);
  16.                                
  17.                                 var box:Shape = new Shape();
  18.                                 box.graphics.beginFill(0xff0000,0.3);
  19.                                 box.graphics.drawRect(globalAtomRect.x,globalAtomRect.y,globalAtomRect.width,globalAtomRect.height);
  20.                                 containerSprite.addChild(box);
  21.                                
  22.                                 var localPosition:Point = new Point();
  23.                                 localPosition.x = containerSprite.x+rect.x+globalAtomRect.x;
  24.                                 localPosition.y = containerSprite.y+rect.y;
  25.                                
  26.                                 /*
  27.                                         We're zooming in so scale the local position
  28.                                 */
  29.                                 var scaledX:Number = localPosition.x*Config.SHIRT_ZOOM_SCALE;
  30.                                 var scaledY:Number = localPosition.y*Config.SHIRT_ZOOM_SCALE;
  31.                                
  32.                                 /*
  33.                                         We don't want the shirt to move so it is out of the viewport so calculate what this range is ...
  34.                                 */
  35.                                
  36.                                 var maxMoveX:Number = (Config.SHIRT_WIDTH*Config.SHIRT_ZOOM_SCALE)-Config.VIEWPORT_WIDTH;
  37.                                 var maxMoveY:Number = (Config.SHIRT_HEIGHT*Config.SHIRT_ZOOM_SCALE)-Config.VIEWPORT_HEIGHT;
  38.                                
  39.                                 var newX:Number = 0-scaledX;
  40.                                 var newY:Number = 0-scaledY;
  41.                                
  42.                                 var centeredX:Number = newX+Config.VIEWPORT_CENTER_X;
  43.                                 var centeredY:Number = newY+Config.VIEWPORT_CENTER_Y;
  44.                                
  45.                                 /*
  46.                                         Check the shirt hasn't moved out of bounds
  47.                                 */
  48.                                
  49.                                 var offsetX:Number = centeredX>0? centeredX*-1 : centeredX<0-maxMoveX? (centeredX*-1)-maxMoveX : 0;
  50.                                 var offsetY:Number = centeredY>0? centeredY*-1 : centeredY<0-maxMoveY? (centeredY*-1)-maxMoveY : 0;
  51.                                
  52.                                 /*
  53.                                         Finally, the new position is nudged so it is at the centre of the viewport space or as near as can be.
  54.                                 */
  55.                                 moveToPointX = centeredX>0? 0 : centeredX<0-maxMoveX? centeredX+((centeredX*-1)-maxMoveX) : centeredX;
  56.                                 moveToPointY = centeredY>0? 0 : centeredY<0-maxMoveY? centeredY+((centeredY*-1)-maxMoveY) : centeredY;
  57.                                
  58.                                 var viewportX:Number = Config.VIEWPORT_CENTER_X+offsetX;
  59.                                 var viewportY:Number = Config.VIEWPORT_CENTER_Y+offsetY;
  60.                                
  61.                                 shirtMoveTo.play();
  62.                                
  63.                                 var evt:PersonSelectionEvent = new PersonSelectionEvent(PersonSelectionEvent.PERSON_SELECTION,true);
  64.                                 evt.viewportPoint = new Point(viewportX,viewportY);
  65.                                 evt.person = p;
  66.                                
  67.                                 dispatchEvent(evt);
  68.                         }

How to install the AIR 2 Beta SDK into FlashBuilder 4 and build a cool multi-touch app for your MacBook

Actionscript, Flex No Comments »

Phew … that was a long blog post title! I have to be very specific about this little demo though because if you aren’t on a MacBook with a multi-touch trackpad this demo will not do anything and then you’ll be severely pissed! I’ve not done any tests with Windows so I’m not sure about touch screens or other AIR enabled devices. More information on this here.

Firstly, go and download the latest AIR 2 SDK Beta from here.

The first thing you need to do is duplicate your FB4 SDK folder which you will find in the Application/Adobe Flash Builder 4/sdks/ folder. Then copy the tbz2 download into that folder.

4.0.0 AIR 2 Beta
Uploaded with plasq‘s Skitch!

Now you need to uncompress the tbz2 (zip) to the duplicated SDK folder you created. You’ll need to do this in the Terminal app so open it and type ‘cd’ to change the directory and then drag the folder into the terminal to add the path. Then hit return to jump into that folder and enter this text to unzip the package …

tar jxvf AIR20_mac_sdk_*****.tbz2

… replace the asterisks with the version number of the beta you downloaded. You’ll have something like this in your terminal window :

Terminal 2014 bash 2014 80ղ4
Uploaded with plasq‘s Skitch!

Then hit return and you should see all the files being installed into the appropriate folders.

Now open Flash Builder 4 and go to the preferences to change the SDK. Choose the new SDK folder you duplicated which has now got the new AIR 2 packages installed.

Preferences
Uploaded with plasq‘s Skitch!

Now create a new Flex AIR project and go to the project preferences to choose your new compiler SDK:

Properties for MultiTouchTest_html
Uploaded with plasq‘s Skitch!

Now you are ready to build a new AIR 2 project with cool multi-touch events! And here’s one I made earlier which demos four of the new TransformGestureEvent Events that you can play with just using the MacBook multi-touch trackpad. It’s basically a small little app of a Monkey that you can scale with a pinch and rotate with a two finger twist. You can also drag him around with a two-finger pan and also make him spring up, down, left and right by swiping on the track pad with three fingers.

Let’s jump straight into the code. I’ve built this in Flex so I use MXML to generate my application window and add a simple Sprite object to the stage using SpriteVisualElement. There’s a simple SWC that contains a Sprite that I created in Flash and exported as ‘Monkey’. This can then be added to the SpriteVisualElement object as a child.

Here’s the full Application class :

View the full source and download the project here.

Grab the AIR file and play on your desktop here. You’ll need the AIR 2 runtime that you can grab here.

If you want to install your tests on an iPhone that’s a blog post for the near future … you can’t do it from within FB4 at the moment and I aint got CS5. My attempts at doing it from the command line failed but I think that’s because it doesn’t like the SWF that FB4 produced. Will look into it.

Hope you enjoy touching my monkey you dirty boys and girls!

Multiple links in a TextFlow object in Flex 4

Actionscript, Flex No Comments »

Flex 4 includes the new Adobe Text Layout Framework (TLF) which is a fantastic new addition that allows greater control of text rendering and the flow of text through containers. You can view the demo material here.

I recently had to use the TLF to generate a whole bunch of links in a stream that flowed through several columns within a composition. The first problem I noticed was that all of the links when chained together were converted to one link. I lost track of how this was fixed but I think it had to do with the links not having href attributes. Anyway, that wasn’t the main problem I encountered. I managed to stream my links together into one long chain and had them nicely justified to the columns :

Screen shot 2010-03-19 at 11.06.55
Uploaded with plasq‘s Skitch!

As you can see I was trying to create a nice wall of links. Eventually these will be names and the user will be able to click on a link for more information etc. Nice! So all it needs is a nice hover state on each link, right? So I add a hover format to the TextFlow and then fell off my chair and sobbed.

Check out the proof-of-concept I created here : http://lyraspace.com/transfer/tlf/TextFlow_POC.html and then read through the posts I wrote on the official forum :

http://forums.adobe.com/message/2682942#2682942
and
http://forums.adobe.com/thread/598789?tstart=0

As you can see, there’s a massive performance issue here with formatting links within a paragraph. It needs to re-draw the paragraph to format the link and with 500 links in the paragraph it’s going to get very sluggish.

The answer then is to create a paragraph for each link? No, this will create a new line for each link.

So I had to work out when a new link would sit on a new line and then create a new paragraph from that link.

And here’s how I did it …

programmatically creating a TextFlow stream of Link Elements

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <s:Application
  3.        
  4.         xmlns:fx="http://ns.adobe.com/mxml/2009"
  5.         xmlns:s="library://ns.adobe.com/flex/spark"
  6.         xmlns:mx="library://ns.adobe.com/flex/mx"
  7.         xmlns:maps="com.cravens.nr.maps.*"
  8.        
  9.         minWidth="955" minHeight="600"
  10.        
  11.         viewSourceURL="srcview/index.html"
  12.         >
  13.        
  14.         <fx:Declarations>
  15.                 <maps:MainEventMap id="eventMap" />
  16.                 <maps:ModelMap id="modelMap" />
  17.         </fx:Declarations>
  18.        
  19.         <fx:Script>
  20.                 <![CDATA[
  21.                         import com.cravens.nr.model.FontsManager;
  22.                        
  23.                         import flash.text.engine.BreakOpportunity;
  24.                         import flash.text.engine.FontLookup;
  25.                         import flash.text.engine.RenderingMode;
  26.                         import flash.text.engine.TypographicCase;
  27.                        
  28.                         import flashx.textLayout.compose.TextFlowLine;
  29.                         import flashx.textLayout.conversion.TextConverter;
  30.                         import flashx.textLayout.elements.Configuration;
  31.                         import flashx.textLayout.elements.LinkElement;
  32.                         import flashx.textLayout.elements.OverflowPolicy;
  33.                         import flashx.textLayout.elements.ParagraphElement;
  34.                         import flashx.textLayout.elements.SpanElement;
  35.                         import flashx.textLayout.elements.SubParagraphGroupElement;
  36.                         import flashx.textLayout.elements.TextFlow;
  37.                         import flashx.textLayout.events.CompositionCompleteEvent;
  38.                         import flashx.textLayout.events.DamageEvent;
  39.                         import flashx.textLayout.events.FlowOperationEvent;
  40.                         import flashx.textLayout.events.UpdateCompleteEvent;
  41.                         import flashx.textLayout.formats.TextAlign;
  42.                         import flashx.textLayout.formats.TextLayoutFormat;
  43.                        
  44.                         import mx.collections.ArrayCollection;
  45.                         import mx.events.FlexEvent;
  46.                         import mx.utils.ObjectUtil;
  47.                        
  48.                         protected var config:Configuration;
  49.                        
  50.                         protected var paragraphText:String = "<p>";
  51.                        
  52.                         [Bindable]
  53.                         protected var textFlow:TextFlow;
  54.                        
  55.                         [Bindable]
  56.                         protected var namesArr:ArrayCollection;
  57.                        
  58.                         protected var linkCount:int = 0;
  59.                        
  60.                         protected var para:ParagraphElement;
  61.                         protected var link:LinkElement;
  62.                         protected var space:SpanElement;
  63.                        
  64.                         protected var updateDelayTimer:Timer;
  65.                        
  66.                         //---------------------------------------------------------------------------
  67.                         public function handleResult(e:Object):void
  68.                         {
  69.                                 trace("Result:"+ObjectUtil.toString(e));
  70.                                
  71.                                 /*
  72.                                         The XML structure returned from the server maps to an ArrayCollection
  73.                                 */
  74.                                 namesArr = e.signatures.signature as ArrayCollection;
  75.                                 startTextFlowCreation();
  76.                         }
  77.                         //---------------------------------------------------------
  78.                         public function handleFault(e:Object):void
  79.                         {
  80.                                 trace("Fault:"+ObjectUtil.toString(e));
  81.                         }
  82.                         //---------------------------------------------------------------------------
  83.                         protected function startTextFlowCreation():void
  84.                         {
  85.                                 FontsManager.getInstance();
  86.                                
  87.                                 config = Configuration(TextFlow.defaultConfiguration).clone();
  88.                                
  89.                                 var linkNormalFormat:TextLayoutFormat = new TextLayoutFormat();
  90.                                 linkNormalFormat.color = 0x000000;
  91.                                 var linkHoverFormat:TextLayoutFormat = new TextLayoutFormat();
  92.                                 linkHoverFormat.color = 0x999999;
  93.                                
  94.                                 var defaultFormat:TextLayoutFormat = new TextLayoutFormat();
  95.                                 defaultFormat.paddingTop = 6;
  96.                                 defaultFormat.color = 0x333333;
  97.                                 defaultFormat.textAlign = TextAlign.JUSTIFY;
  98.                                 defaultFormat.textAlignLast = TextAlign.JUSTIFY;
  99.                                 defaultFormat.fontSize = 10;
  100.                                 defaultFormat.lineHeight = 12;
  101.                                 defaultFormat.fontLookup = FontLookup.EMBEDDED_CFF;
  102.                                 defaultFormat.typographicCase = TypographicCase.UPPERCASE;
  103.                                 defaultFormat.renderingMode = RenderingMode.CFF;
  104.                                 defaultFormat.fontFamily = FontsManager.NOVAMONO;
  105.                                
  106.                                 config.textFlowInitialFormat = defaultFormat;
  107.                                 config.defaultLinkNormalFormat = linkNormalFormat;
  108.                                 config.defaultLinkHoverFormat = linkHoverFormat;
  109.                                 config.overflowPolicy = OverflowPolicy.FIT_DESCENDERS;
  110.                                
  111.                                 textFlow = new TextFlow(config);
  112.                                 textFlow.columnCount = 4;
  113.                                
  114.                                 para = new ParagraphElement();
  115.                                 textFlow.addChild(para);
  116.                                
  117.                                 updateDelayTimer = new Timer(50,1);
  118.                                 updateDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE,checkLink);
  119.                                 addLinkElement();
  120.                         }
  121.                         //---------------------------------------------------------
  122.                         protected function addLinkElement():void
  123.                         {
  124.                                 var o:Object = namesArr[linkCount];
  125.                                 var firstName:String = o.first_name;
  126.                                 var lastName:String = o.last_name;
  127.                                
  128.                                 link = new LinkElement();
  129.                                 link.href = "event:"+o.id;
  130.                                 var linkText:SpanElement = new SpanElement();
  131.                                 linkText.text = o.first_name+" "+o.last_name;
  132.                                 space = new SpanElement();
  133.                                 space.text = " ";
  134.                                 link.addChild(linkText);
  135.                                
  136.                                 para.addChild(link);
  137.                                 para.addChild(space);
  138.                                
  139.                                 textFlow.flowComposer.composeToPosition(textFlow.textLength);
  140.                                
  141.                                 updateDelayTimer.start();
  142.                         }
  143.                         //---------------------------------------------------------
  144.                         protected function checkLink(event:TimerEvent = null):void
  145.                         {
  146.                                 updateDelayTimer.reset();
  147.                                
  148.                                 if(link)
  149.                                 {
  150.                                         var linkStart:int = link.getAbsoluteStart();
  151.                                         var linkEnd:int = linkStart+link.textLength;
  152.                                        
  153.                                         var textFlowLine:TextFlowLine = textFlow.flowComposer.findLineAtPosition(linkStart);
  154.                                         var lineStart:int = textFlowLine.absoluteStart;
  155.                                         var isStartOfLine:Boolean = linkStart==lineStart;
  156.                                        
  157.                                         if(isStartOfLine && linkStart!=0)
  158.                                         {
  159.                                                 para.removeChild(link);
  160.                                                 para.removeChild(space);
  161.                                                
  162.                                                 para = new ParagraphElement();
  163.                                                 textFlow.addChild(para);
  164.                                                
  165.                                                 para.addChild(link);
  166.                                                 para.addChild(space);
  167.                                         }
  168.                                        
  169.                                         linkCount++;
  170.                                         if(linkCount == namesArr.length) textFlowComplete();
  171.                                         else addLinkElement();
  172.                                 }
  173.                         }
  174.                         //---------------------------------------------------------
  175.                         protected function textFlowComplete():void
  176.                         {
  177.                                 updateDelayTimer.removeEventListener(TimerEvent.TIMER_COMPLETE,checkLink);
  178.                                 updateDelayTimer = null;
  179.                         }
  180.  
  181.                 ]]>
  182.         </fx:Script>
  183.        
  184.         <s:VGroup width="100%" height="100%">
  185.                 <s:TextArea
  186.                        
  187.                         id="textArea"
  188.                         width="100%"
  189.                         height="100%"
  190.                         textFlow="{textFlow}"
  191.                         editable="false"
  192.                         selectable="false"
  193.                         />
  194.                
  195.         </s:VGroup>
  196.        
  197.        
  198. </s:Application>

In the example above you have to assume that the names_arr ArrayCollection is loaded in via the Mate framework eventMap and that this collection is made up of objects with a firstName and lastName property that I can use to create the links.

The formatting of the TextFlow can be created in advance by setting up a Configuration object and adding it as an argument while instantiating the TextFlow.

Creating a TextFlow Configuration object

  1. config = Configuration(TextFlow.defaultConfiguration).clone();
  2.                                
  3.                                 var linkNormalFormat:TextLayoutFormat = new TextLayoutFormat();
  4.                                 linkNormalFormat.color = 0x000000;
  5.                                 var linkHoverFormat:TextLayoutFormat = new TextLayoutFormat();
  6.                                 linkHoverFormat.color = 0x999999;
  7.                                
  8.                                 var defaultFormat:TextLayoutFormat = new TextLayoutFormat();
  9.                                 defaultFormat.paddingTop = 6;
  10.                                 defaultFormat.color = 0x333333;
  11.                                 defaultFormat.textAlign = TextAlign.JUSTIFY;
  12.                                 defaultFormat.textAlignLast = TextAlign.JUSTIFY;
  13.                                 defaultFormat.fontSize = 10;
  14.                                 defaultFormat.lineHeight = 12;
  15.                                 defaultFormat.fontLookup = FontLookup.EMBEDDED_CFF;
  16.                                 defaultFormat.typographicCase = TypographicCase.UPPERCASE;
  17.                                 defaultFormat.renderingMode = RenderingMode.CFF;
  18.                                 defaultFormat.fontFamily = FontsManager.NOVAMONO;
  19.                                
  20.                                 config.textFlowInitialFormat = defaultFormat;
  21.                                 config.defaultLinkNormalFormat = linkNormalFormat;
  22.                                 config.defaultLinkHoverFormat = linkHoverFormat;
  23.                                 config.overflowPolicy = OverflowPolicy.FIT_DESCENDERS;
  24.                                
  25.                                 textFlow = new TextFlow(config);

… and then to find the position of the element on the line I used this code :

Finding the position of an element within a TextFlow

  1. var linkStart:int = link.getAbsoluteStart();
  2.                                         var linkEnd:int = linkStart+link.textLength;
  3.                                        
  4.                                         var textFlowLine:TextFlowLine = textFlow.flowComposer.findLineAtPosition(linkStart);
  5.                                         var lineStart:int = textFlowLine.absoluteStart;
  6.                                         var isStartOfLine:Boolean = linkStart==lineStart;

As you can see I re-parent the element to a new ParagraphElement object if the LinkElement is at the start of the line.

The Text Layout Framework is still new and there are bugs, this highlights just how erratic the performance can be with it at the moment so handle cautiously and be sure to post your problems on the forum. The guys at Adobe were very helpful and as with all problems like this, you end up learning a hell of a lot about the framework when solving them!

Hey, you might have noticed I’m now using Snipplr to embed my code. I hope it doesn’t disappear any time soon.

Logging Flash with Google Chrome Developer Tools console

Actionscript, Flex 1 Comment »

Google Chrome’s new Developer Tools panel is really great but now it’s even better now that I’ve managed to log directly into it from Actionscript.

And it’s all thanks to this blog post : http://aut0poietic.us/2008/10/22/using-the-flex-3-logging-framework

All I had to do was create the ExternalConsoleTarget class from his example and then grab the code for the Console Logging wrapper. Then in my project import the Console class and log like this …

Console.debug("Hello Chrome");

It worked for me straight out of the box. Wonderful.

And because I’m a bloody nice bloke here’s my test project which includes a SWC I made so you can use the two classes in your own projects: http://lyraspace.com/tools/ConsoleLogger/Main.html

New touch/gesture events in Actionscript 3 for Flash Player 10.1

Actionscript, Flash, Flex 1 Comment »

This video from a MAX session has an informative demonstration of the new touch/gesture events in 10.1. Also loads of really cool new features in the AIR 2.0 runtime.

What’s coming in Adobe AIR 2.0

Here’s a great article outlining all of the new events, classes and information on how to manage the input.

I’ve scraped the code off this article for my own reference below. Please do read the article though.


flash.ui.Multitouch
[static] inputMode:String
[static] maxTouchPoints:int
[static] supportedGestures:Vector
[static] supportsGestureEvents:Boolean
[static] supportsTouchEvents:Boolean

Touch Input mode

flash.events.MultitouchInputMode
Multitouch.inputMode
MultitouchInputMode.TOUCH_POINT
MultitouchInputMode.NONE
MultitouchInputMode.GESTURE

Mouse

MouseEvent.CLICK
MouseEvent.DOUBLE_CLICK
MouseEvent.MOUSE_DOWN
MouseEvent.MOUSE_UP
MouseEvent.MOUSE_MOVE
MouseEvent.MOUSE_OUT
MouseEvent.MOUSE_OVER
MouseEvent.ROLL_OUT
MouseEvent.ROLL_OVER

Touch

TouchEvent.TOUCH_BEGIN
TouchEvent.TOUCH_MOVE
TouchEvent.TOUCH_END
TouchEvent.TOUCH_TAP
TouchEvent.TOUCH_OUT
TouchEvent.TOUCH_OVER
TouchEvent.TOUCH_ROLL_OUT
TouchEvent.TOUCH_ROLL_OVER
TouchEvent.TOUCH_TAP

Touch management

Many multi-touch developers like to use an Object or Array of points to keep track of touch inputs (aka blobs) as well as an ID, and X/Y coordinatees for each point. It looks like Flash Player 10.1 will help you conform to these conventions, ActionScript-style.


flash.utils.Dictionary /* Used to track Arrays of Points */
flash.geom.Point /* One contact point */
TouchEvent.touchPointID /* Unique ID for each touch contact point */
TouchEvent.stageX
TouchEvent.stageY
TouchEvent.isPrimaryTouchPoint

Gestures

flash.events.GestureEvent;
flash.events.GesturePhase;
flash.events.GesturePhaseEvent;
flash.events.TransformGestureEvent;

/* GestureEvent */
GestureEvent.GESTURE_TWO_FINGER_TAP
GestureEvent.phase: String
GestureEvent.localX:Number
GestureEvent.localY:Number
GestureEvent.stageX:Number
GestureEvent.stageY:Number

/* GesturePhase */
GesturePhase.BEGIN
GesturePhase.END
GesturePhase.UPDATE

/* GesturePhaseEvent */
GesturePhaseEvent.GESTURE_PAN
GesturePhaseEvent.GESTURE_PRESS_AND_TAP
GesturePhaseEvent.GESTURE_ROTATE
GesturePhaseEvent.GESTURE_ZOOM //Is the same thing as 'pinch'
GesturePhaseEvent.offsetX:Number
GesturePhaseEvent.offsetY:Number
GesturePhaseEvent.rotation:Number
GesturePhaseEvent.scaleX:Number
GesturePhaseEvent.scaleY:Number

Orientation recognition

flash.display.StageOrientation;
/* e.g. StageOrientation.DEFAULT */

flash.display.StageDisplayState;
/* e.g. stage.displayState = FULL_SCREEN_INTERACTIVE; */

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in