English Text Counter (for Schematic)

June 12th, 2010 by Steven Sacks

So, there's this digital agency called Schematic. I applied for a job there a couple of years ago and, despite employees giving me personal recommendations, I never heard back from their hiring manager. Oh well. I heard from a colleague of mine that interviewed with them that they give interviewees a series of coding tests to determine their prowess with Flash. They allow you to take the tests home and work on them.

One of these tests is to create a counter that counts from 0 to 1,000,000 in English, e.g one hundred twenty-six, three hundred thousand six hundred seventy-two, etc. From what I've heard, very few people are able to write this without timing out the player, and the ones that are able to pull it off bring the player to its knees.

I like a challenge, so I decided to give it a go and spent an evening working on it. The result is below. I ran some metrics and my code runs a for loop from 0 to 1,000,000 in 4400ms. It's so fast that I created controls to adjust the speed and a slider to adjust the magnitudes. I suggest you try to code this yourself. It was a fun exercise, to be sure. On Windows, this swf doesn't even register on the CPU.

If anyone wants the source (maybe because you're interviewing at Schematic) feel free to contact me and I'll give it to you. I guess they're going to have to take this test out of rotation. Got anything more interesting, Schematic?

Posted in Awesome, Flash, Technology having 6 comments »

It's time for NaN to throw errors

June 11th, 2010 by Steven Sacks

We've all experienced it. You're trying to position or size something and it draws all wacky. You're not sure why, but eventually you figure out that one of the values is NaN. The Flash runtime does absolutely nothing to help you solve this problem, but it really should.

By contrast, if you try to run operations on something null, you get a #1099 that tells you you're attempting to run an operation on something you cannot run operations on.

Performing calculations on NaN results in unpredictable behavior. There is absolutely nothing you can do with NaN mathematically that doesn't result in NaN, and there's no value for doing so. I think it's high time that we get a runtime error when attempting to perform math on NaN.

Nobody ever wants to run operations on NaN. It's always an accident or a bug in the code. If I try to set a Sprite's x to NaN, the runtime should throw an error that NaN is not a valid value for x, not draw the Sprite somewhere off screen. If I try to add, subtract, multiply, divide or any other mathematic operation on NaN, the runtime should throw an error that you cannot run mathematical operations on NaN.

Think about it this way. Being able to let the runtime do operations on NaN is AS1 style coding. It's not strict and the runtime doesn't let you know why something didn't work as expected. You have to go digging to figure it out. This is one of the biggest reasons to use AS3. Throwing runtime errors when running operations on NaN is AS3 style coding. It's strict and the runtime will let you know if you are trying to do something that will always be a bad result (such as trying to access a null).

Who's with me? Can I get some love for this suggestion?

Posted in AS3, Actionscript, Flash, Flex, Rants, Technology having 10 comments »

Flash CS5 TLF Engine Causes Errors With Loaded SWFs

May 28th, 2010 by Steven Sacks

I have discovered a nasty bug with Flash CS5 and a TLF TextField in a loaded swf.

If you use TLF Engine in a loaded swf, you cannot call a public function of a document class (runtime error function doesn't exist), you cannot cast the swf as its document class or cast it as an interface the document class implements (both cause a coercion error).

It's an extremely common use case. I need one swf to load another swf and I need that other swf to have TLF TextFields in it, and I also need to call public functions on my swf, or cast the loader.content as the proper class or interface.

You can download a trivial example here:
TLF_Fail.zip

In the zip, there are two .fla files, main.fla and other.fla, each with a document class. The other.fla has a TLF TextField on its stage and its document class has a public function customFunc() that traces that it was called. The Main document class creates a loader, loads the other.fla and when the COMPLETE event fires, it tries to call the public function or tries to cast the document class as the document class or cast the document class as its interface. Each of these causes an error.

Main.as

package
{
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.net.URLRequest;

	public class Main extends MovieClip
	{
		private var loader:Loader;

		public function Main()
		{
			super();
			init();
		}
		private function init():void
		{
			addChild(loader = new Loader());
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
			loader.load(new URLRequest("other.swf"));
		}
		private function onLoadComplete(event:Event):void
		{
			// this says that there's no such function on the __Preloader__
			MovieClip(loader.content).customFunc();

			// this causes a coercion error
			//Other(loader.content);

			// this causes a coercion error
			//ICustom(loader.content).customFunc();
		}
	}
}



Other.as

package
{
	import flash.display.MovieClip;
	import fl.text.TLFTextField;

	public class Other extends MovieClip implements ICustom
	{
		public var TLF_Foo:TLFTextField;

		public function Other()
		{
			super();
		}
		public function customFunc():void
		{
			trace("custom func called");
		}
	}
}



ICustom.as

package
{
	public interface ICustom
	{
		function customFunc():void;
	}
}



Here are the error messages:

MovieClip(loader.content).customFunc();

ReferenceError: Error #1069: Property customFunc not found on Other__Preloader__ and there is no default value.
	at Main/onLoadComplete()



Other(loader.content);

TypeError: Error #1034: Type Coercion failed: cannot convert Other__Preloader__@23c88121 to Other.
	at Main/onLoadComplete()
ReferenceError: Error #1056: Cannot create property __rslLoaders on Other.
	at fl.rsl::RSLPreloader/contentComplete()



ICustom(loader.content);

TypeError: Error #1034: Type Coercion failed: cannot convert Other__Preloader__@23c98121 to ICustom.
	at Main/onLoadComplete()


There are tens of thousands of developers worldwide who have a big stake in this issue being resolved because the Gaia Framework casts all of its pages as interfaces and calls functions on them and this bug makes it so developers cannot use the Gaia Framework with the TLF Engine.

Adobe, please fix this issue.

UPDATE 1
I decided to run a test to see how long before the loader.content is my swf and not Adobe's TLF Preloader. It never happens.

private var count:int;

private function onLoadComplete(event:Event):void
{
	addEventListener(Event.ENTER_FRAME, onFrame);
}
private function onFrame(event:Event):void
{
	trace(++count + ": " + loader.content);
}


Here's the output:

1: [object Other__Preloader__]
2: [object Other__Preloader__]
3: [object Other__Preloader__]
4: [object Other__Preloader__]
5: [object Other__Preloader__]
...
89: [object Other__Preloader__]
90: [object Other__Preloader__]
91: [object Other__Preloader__]



When you load a swf that has TLF inside it, the loader.content is never your swf.



UPDATE 2
If you go into the Actionscript settings of the swf using the TLF Engine and changed the Runtime Shared Library Settings Default linkage to "Merged into code" the errors go away. But, the problem with this workaround is that the swf increases in size by 125k.

This means that for enterprise sites that use multiple swfs, every swf that uses TLF has to increase by 125k. I can imagine that this will dissuade enterprise customers like Disney, HBO, Ford and other big companies who serve millions of visitors daily from using the TLF engine on their sites.



UPDATE 3
I've done some more investigating. I trace the parent stack when ADDED_TO_STAGE is fired inside the loaded swf. The ADDED_TO_STAGE event fires twice. Here's what happens.

When you use Loader to load a swf that has an RSL (which, in this case, is the TLF engine), and you load that swf, Adobe's Preloader is what it actually loads. Then, Adobe's Preloader creates a Loader and adds that Loader to its display stack, alongside a Shape that is the visual part of their preloader. Adobe's Preloader then tells its Loader to load your swf.

Once your swf loads, it's already on the stage, so ADDED_TO_STAGE is fired. Then, Adobe's Preloader calls removeChild() on the Loader and the Shape, and then calls addChild(loader.content), which triggers another ADDED_TO_STAGE event. After that, your loaded swf's parent is their Preloader, not their Preloader's Loader.

Now here's where it gets worse. The Preloader attempts to add a property called __rslLoaders to your document class. If you reference the document class of the loaded swf in your Main document class before you load your swf (such as you write code that casts the loaded swf as your document class, i.e. Other(loader.content) you get a runtime error:
ReferenceError: Error #1056: Cannot create property __rslLoaders on Other.
at fl.rsl::RSLPreloader/contentComplete()

The Loader you use to load your swf fires its COMPLETE event before Adobe's Preloader is finished loading your swf. You cannot listen to Adobe's preloader to know when your swf has actually been loaded and is available, and, as far as I can tell, Adobe's Preloader provides no way to target your swf.

By building it this way, the author of Adobe's preloader has decided for all of us that nobody who uses TLF should need to target the timeline of a loaded swf without jumping through some major hoops. Here are the hoops.

Because you cannot bubble events from within the other.swf through the Loader that loaded it to the Main class, your loading class has no way of knowing when your loaded swf is actually available unless you do an ENTER_FRAME listener and loop through the children of MovieClip(loader.content) and look for your interface class (as mentioned above, you cannot cast as the document class or you get a runtime reference error, and you cannot change the ApplicationDomain or the SecurityDomain of Adobe's Preloader's Loader). Then, you need to store a reference to your swf to be able to access it, which makes unloading it more of a chore (I actually haven't tested to see if Flash truly lets go of the loaded tlf swf).

private var otherSWF:ICustom;

private function onLoadComplete(event:Event):void
{
	addEventListener(Event.ENTER_FRAME, onFrame);
}
private function onFrame(event:Event):void
{
	var i:int = MovieClip(loader.content).numChildren;
	while (i--)
	{
		if (MovieClip(loader.content).getChildAt(i) as ICustom != null)
		{
			otherSWF = MovieClip(loader.content).getChildAt(i) as ICustom;
			removeEventListener(Event.ENTER_FRAME, onFrame);
			break;
		}
	}
}



In my trivial example running on my local machine, it takes 5 frames before my loaded swf is available.

Posted in AS3, Adobe, Bugs, Flash, Flash CS5 having 14 comments »

AIR NativeDragManager is BROKEN with IE 8

May 7th, 2010 by Steven Sacks

The AIR NativeDragManager is broken when interacting with IE 8 on any operating system. This is true in both AIR 1.5.x and the latest AIR 2 Beta 2.

The clipboard has null data when you drag and drop from IE 8. This does not happen with any other browser.

When you drag and drop links from any browser besides IE 8, the event.clipboard.formats has four formats, HTML_FORMAT, FILE_PROMISE_LIST_FORMAT, URL_FORMAT and TEXT_FORMAT. Getting the URL_FORMAT returns the expected url.

When you drag and drop the same links from IE 8, the clipboard.formats has two values, FILE_PROMISE_LIST_FORMAT and URL_FORMAT, but the promise list is empty and the url is null.

In AIR 1.5.x, there is no FILE_PROMISE_LIST_FORMAT (because that's a 10.1 feature), but the url is still null when coming from IE 8.

This is trivial to test.

addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onNativeDragEnter);

private function onNativeDragEnter(event:NativeDragEvent):void
{
	trace(event.clipboard.formats);
	if (event.clipboard.hasFormat(ClipboardFormats.URL_FORMAT))
	{
		trace("url = " + event.clipboard.getData(ClipboardFormats.URL_FORMAT));
	}
}

Then, go to any webpage in IE 8 and attempt to drag and drop url links from the html page on to AIR. If you go to the same page and drag the same links in any other browser, it works as expected.

The worst part is, this is a critical bug for a company that's been working on an AIR app for over a year because their AIR app is entirely based on this feature working. I just found out about this bug and, after thorough testing, have determined that the fate of this company is in the hands of the Adobe AIR team.

Help us Adobe AIR team. You're our only hope.

UPDATE
I've realized this issue is not just with AIR, it looks like Microsoft broke drag-and-drop with IE8. You can't even drag and drop links into text fields in notepad. A quick google search demonstrates the problem is widespread. Microsoft, you suck.

Posted in AIR, Adobe, Bugs having 1 comment »

Apple's Behavior Shouldn't Be A Surprise To Anyone…

May 3rd, 2010 by Steven Sacks

Read the following and consider its accuracy at analyzing the recent actions and tone Apple has taken.

Apple has always insisted on having a hardware monopoly, except for a brief period in the mid-1990s when they allowed clone-makers to compete with them, before subsequently putting them out of business. Macintosh hardware was, consequently, expensive. You didn't open it up and fool around with it because doing so would void the warranty. In fact the first Mac was specifically designed to be difficult to open–you needed a kit of exotic tools, which you could buy through little ads that began to appear in the back pages of magazines a few months after the Mac came out on the market. These ads always had a certain disreputable air about them, like pitches for lock-picking tools in the backs of lurid detective magazines.

This monopolistic policy can be explained in at least three different ways.

THE CHARITABLE EXPLANATION is that the hardware monopoly policy reflected a drive on Apple's part to provide a seamless, unified blending of hardware, operating system, and software. There is something to this. It is hard enough to make an OS that works well on one specific piece of hardware, designed and tested by engineers who work down the hallway from you, in the same company. Making an OS to work on arbitrary pieces of hardware, cranked out by rabidly entrepeneurial clonemakers on the other side of the International Date Line, is very difficult, and accounts for much of the troubles people have using Windows.

THE FINANCIAL EXPLANATION is that Apple, unlike Microsoft, is and always has been a hardware company. It simply depends on revenue from selling hardware, and cannot exist without it.

THE NOT-SO-CHARITABLE EXPLANATION has to do with Apple's corporate culture, which is rooted in Bay Area Baby Boomdom.

Now, since I'm going to talk for a moment about culture, full disclosure is probably in order, to protect myself against allegations of conflict of interest and ethical turpitude: (1) Geographically I am a Seattleite, of a Saturnine temperament, and inclined to take a sour view of the Dionysian Bay Area, just as they tend to be annoyed and appalled by us. (2) Chronologically I am a post-Baby Boomer. I feel that way, at least, because I never experienced the fun and exciting parts of the whole Boomer scene–just spent a lot of time dutifully chuckling at Boomers' maddeningly pointless anecdotes about just how stoned they got on various occasions, and politely fielding their assertions about how great their music was. But even from this remove it was possible to glean certain patterns, and one that recurred as regularly as an urban legend was the one about how someone would move into a commune populated by sandal-wearing, peace-sign flashing flower children, and eventually discover that, underneath this facade, the guys who ran it were actually control freaks; and that, as living in a commune, where much lip service was paid to ideals of peace, love and harmony, had deprived them of normal, socially approved outlets for their control-freakdom, it tended to come out in other, invariably more sinister, ways.

Applying this to the case of Apple Computer will be left as an exercise for the reader, and not a very difficult exercise.

It is a bit unsettling, at first, to think of Apple as a control freak, because it is completely at odds with their corporate image. Weren't these the guys who aired the famous Super Bowl ads showing suited, blindfolded executives marching like lemmings off a cliff? Isn't this the company that even now runs ads picturing the Dalai Lama (except in Hong Kong) and Einstein and other offbeat rebels?

It is indeed the same company, and the fact that they have been able to plant this image of themselves as creative and rebellious free-thinkers in the minds of so many intelligent and media-hardened skeptics really gives one pause. It is testimony to the insidious power of expensive slick ad campaigns and, perhaps, to a certain amount of wishful thinking in the minds of people who fall for them. It also raises the question of why Microsoft is so bad at PR, when the history of Apple demonstrates that, by writing large checks to good ad agencies, you can plant a corporate image in the minds of intelligent people that is completely at odds with reality. (The answer, for people who don't like Damoclean questions, is that since Microsoft has won the hearts and minds of the silent majority–the bourgeoisie–they don't give a damn about having a slick image, any more then Dick Nixon did. "I want to believe,"–the mantra that Fox Mulder has pinned to his office wall in The X-Files–applies in different ways to these two companies; Mac partisans want to believe in the image of Apple purveyed in those ads, and in the notion that Macs are somehow fundamentally different from other computers, while Windows people want to believe that they are getting something for their money, engaging in a respectable business transaction).

All done? This is an unmodified excerpt from Neal Stephenson's essay "In the Beginning…Was the Command Line". It was written in 1999. Before OSX.

Surprised?

You can read the entirety here. It's a fantastic read, even today.

Posted in Apple, Technology having no comments »

Setting up FDT to look and behave like FlashDevelop

April 30th, 2010 by Steven Sacks

If you are moving to FDT from FlashDevelop and want FDT to look and behave more closely to FlashDevelop, here's what you need to do.

Open Window > Preferences.

Under FDT > Editor > Code Assist copy and paste this into Auto activation triggers for AS, and set the delay to 0ms.

abcdefghijklmnopqrstuvwxyz_. :



Under Editor: Colors and Editor > > Semantic Highlight you're going to set the syntax colors. This is a bit involved. What you should do on Windows is create a custom color in the palette for each of these.

Here are the RGB values for each of the items you need to change under Colors. You should turn off Bold/Italic for all these items if you want to match FlashDevelop's defaults.

Keywords and all Keyword 'types': 21, 24, 255
Base Types 'Void': 0, 128, 128
Core Types from MM Classes Root: 0, 128, 128
Constants: 0, 0, 153
Single/Multi Line Comment: 0, 128, 0
SingleMulti Line Todo: 0, 128, 0
JavaDoc: 128, 0, 0
Strings/Characters: 163, 21, 21

And here are the RGB values for each of the items you need to change under Semantic Highlight (Click on the AS3 tab).

Class: 0, 128, 128
Field: 0, 0, 155
Function: 0, 0, 0
Getter: 0, 0, 0
Regular expression: 255, 0, 255
Setter: 0, 0, 0
Static field: 0, 0, 0
Static function: 0, 0, 0
Static getter: 0, 0, 0
Static getter: 0, 0, 0
Top level function: 0, 128, 128
Top level namespace: 0, 128, 128
Top level variable: 0, 128, 128



Under Problems > AS3 Problems:
Unresolvable > Unresolvable variable reference in E4X and Unresolvable member reference in dynamic object should both be set to Disabled.

If you're on Mac you can skip this step. Windows users should definitely do this. Under General > Keys find Quick Fix (type it into the top input box), click on it (should be bound to CTRL+1). You can add CTRL+SHIFT+1 if you want to make it like FlashDevelop, but I would leave it alone. However, I highly recommend adding ALT+1 or ALT+2. It's a more natural position for your hand to be in.

Apply all your changes and return to the FDT IDE.

On the left you should see the Flash Explorer panel. Drag that entire block, not just the panel tab but the entire block, to the right edge of the application. This will mimic the Project tab's position in FlashDevelop. I would take the Outline and ANT tabs from the other block and add them to the same block as Flash Explorer. I closed the ANT tab since I don't use it often enough to warrant it being always visible.

That's basically it. The rest is all up to personal style.

Posted in AS3, FlashDevelop, Tips/Tricks, Workflow having 1 comment »

Socket.timeout property doesn't work

March 27th, 2010 by Steven Sacks

AS3's Socket class has a public property called timeout, and it doesn't work.

The default value of timeout is 20000 milliseconds. Setting timeout to any other value doesn't change the 20000 millisecond timeout.

You can confirm this easily by attempting to connect to a Socket that doesn't respond. It will always take 20000 milliseconds for it to timeout, even if you set the timeout to a much smaller value, say 1000 or 5000.

There is a workaround.

1. Make your own Timer and listen for TimerEvent.TIMER_COMPLETE.
2. Start the timer as soon as you call connect().
3. In the Event.CONNECT listener, reset the timer.
4. If the TIMER_COMPLETE event fires first, close the socket.

Posted in AS3, Bugs having no comments »

Adobe AIR Non-Transparent Window Bugs Part 3: Native Controls Showing Up

February 9th, 2010 by Steven Sacks

In my previous post, I showed how sometimes Windows would draw native controls on top of your non-transparent windows, as pictured here:

Windows XP

However, I could not figure out a sure way to reproduce it. Now I have.

Here's how you do it:

1. Make a new AIR application in Flex Builder.

2. In your-app.xml, set systemChrome to none and visible to true.

3. Prevent MINIMIZE and MAXIMIZE in your WindowedApplication:

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
    <mx:Script>
        <![CDATA[
            private function init():void
            {
             nativeWindow.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGING, onDisplayStateChanging);
            }
            private function onDisplayStateChanging(event:NativeWindowDisplayStateEvent):void
            {
                if (event.afterDisplayState == NativeWindowDisplayState.MINIMIZED || event.afterDisplayState == NativeWindowDisplayState.MAXIMIZED)
                {
                    event.preventDefault();
                }
            }
        ]]>
    </mx:Script>
</mx:WindowedApplication>

4. Launch the app.

5. Click twice on the taskbar button (should do nothing).

6. Right-click on the taskbar button. Windows native system controls appear.

This is just a reliable way to make it happen every time. You don't have to do this exact thing to make them show up, they show up on their own randomly. I hope this example makes it easy for the AIR team to debug this issue.

I've exported the sample project with the above code. Click here to download.

Posted in AIR, Adobe, Bugs having 3 comments »

Adobe AIR Non-Transparent Window Bugs Part 2: Maximize

February 4th, 2010 by Steven Sacks

Maximized AIR windows on Windows XP are positioned at -4,-4 and the width and height add 8 pixels to compensate. On Windows 7, this is doubled, so the position is -8,-8 and the width and height add 16 pixels to compensate. This works fine with transparent AIR windows.

Note: This behavior is not documented, and I hope they add it to the AIR documentation.

However, when you maximize a non-transparent non-system chrome window in AIR, the window is drawn 1 pixel in on all edges, so you can see the desktop as a 1 pixel border around the window. Additionally, the window is positioned at 1,1 instead of -4,-4 in Windows XP and -8,-8 in Windows 7, and the window is cropped to 1 pixel less than the visible bounds on width and height, which means 10 pixels are being cropped in Windows XP and 18 pixels are being cropped in Windows 7.

Here's a screenshot of what the window top and bottom look like when normal:
Normal AIR Window
Normal AIR Window Bottom

And this is what it looks like when it's maximized (I cropped out the middle to fit the blog):
Maximized AIR Window
Maximized AIR Window Bottom

As you can see from the maximized image, the window is incorrectly rendered. There are two primary issues.

The NativeWindowBoundsEvent says it's positioned at -4,-4 / -8,-8, but it's actually positioned at 1,1.

The NativeWindowBoundsEvent width and height are correct at +8 for Windows XP and +16 for Windows 7. But, because the window is cropped at 2 pixels less width and height than the window is drawn at, on Windows XP the window is cropped by 10 pixels, and on Windows 7 the window is cropped by 18 pixels.

Here are screenshots where you can see the 1 pixel border on Windows XP and Windows 7. I made the desktop bright red (#FF0000) so you can clearly see the borders. There's also another bug with Windows XP that I'll go into below.

Windows XP
Windows XP

Windows 7
Windows 7

If you look at the Windows XP one, you can see that native window controls are being drawn (this happens in Windows 7, as well). This happens sometimes when you maximize from a normal state on a non-transparent window. There's nothing reliable that causes it, but it does happen sometimes. This is the first time I've been able to take a screenshot of it. What's weird is rolling over the graphics in the window hides the controls but you can still click on them. What's weirder is that if you click on the native control to restore the window (go back to normal), AIR doesn't fire a state change event!

There's one more issue with non-transparent AIR windows. It's the same bug that the CS4 suite of software has when the windows are maximized. When you have two monitors and your CS4 application is maximized (like Flash or Photoshop) and you drag a window across the right edge of the application, you see ugly ghosting of the window you're dragging.

Windows XP Edge Bug

See all that ugly blue? That was me dragging the FlexBuilder window around. The blue is the color of the title bar of the native window. Any window will cause that, though. On AIR, the problem is even worse because that 1 pixel border on the top and bottom also suffer from the ghosting.

I don't know why, but CS4 and AIR both have the same exact issue.

Here's what it looks like in Flash CS4 and Photoshop CS4:

Flash CS4
Windows XP Edge Bug

Photoshop CS4
Windows XP Edge Bug

The ghosting goes away if you change state, like minimizing then restoring or clicking the restore button. This only happens when the window is maximized.

Please, please, please fix this in AIR (and CS5)!

Added to Adobe Bugs on Feb 8th

Update: Workaround

While there's no way to deal with the 1 pixel border or the ugly ghosting that happens, I have figured out a straightforward workaround for the resize. Because you cannot move Mac windows to negative y positions (AFAIK), this if statement will only happen on windows.

private function onResize(event:NativeWindowBoundsEvent):void
{
	var offset:int;
	if ((event.afterBounds.x == -4 && event.afterBounds.y == -4) || (event.afterBounds.x == -8 && event.afterBounds.y == -8)) offset = event.afterBounds.x * -2;
	width = event.afterBounds.width - offset;
	height = event.afterBounds.height - offset;
}

Posted in AIR, Adobe, Bugs having 7 comments »

Adobe AIR has bad redraw bugs on Windows

January 27th, 2010 by Steven Sacks

In Adobe AIR, I recently had to change from using a transparent window to a non-transparent one, and I've never really worked with non-transparent windows without system chrome. Doing so has created a very painful situation due to AIR's redraw bugs on Windows when using non-transparent windows.

Transparency performance on Windows is bad, but it's not Adobe's fault. Windows has bad transparent window performance. However, on Windows, there are major redraw issues with AIR on a non-transparent window, and none of these issues are present in non-AIR applications.

When you minimize a window in AIR, it resizes the application to 160×31, and moves it to -32000, -32000. I can understand the move, but the resize doesn't make sense to me. Especially considering the minSize of the Window is much larger than that.

Because of the resize to 160×31, when you restore the app from a minimized state it resizes the application from that small size back to the size it was before you minimized. On transparent windows it resizes before you can see it, which is why I never saw this before. But, on a non-transparent window, you can actually see your application redraw from 160×31 to the size it was before you minimized. This looks terrible.

What happens whenever you change the window state from Minimized to Normal or from Normal to Maximized, or Maximized to Normal, the move happens first, then, about 100ms later (though getTimer() claims they happen in the same millisecond), the resize happens. This is an clearly visible to the user, and really doesn't look good.

Additionally, on this specific project, when AIR resizes my window to 160×31, it causes all kinds of negative issues with elements that are not meant to draw to that small of a dimension (I set the minSize of the window for a reason). Because of the delay in resizing after the move as outlined above, these issues are visible for a moment when you restore from a minimized state, but sometimes, elements inside an HTMLLoader do not resize correctly unless I resize the application by 1 pixel after restoring.

The best I can do is in the RESIZE listeners check to see if the afterBounds is 160×31 and ignore it if so. So, I do the check for 160×31 in my onResize listener, ignoring the resize if those are the afterBounds values, and that mitigates the issue except for a couple important exceptions.

First, you still see the two step redraw occur, except that since the application isn't resized smaller, it just looks like the application is masked for that split second. The second issue is that I have an HTMLLoader playing a swf with wmode="opaque" inside it and what happens on redraw is that even though the application is "masked" by the smaller window size (since I'm ignoring the resize), the swf in the HTMLLoader is visible beyond the bounds of the NativeWindow 160×31 size until the resize occurs after the move. This isn't the worst thing in the world, but it's not that great either. It's definitely better than the visual consequences of redrawing the application to 160×31.

Another example of this move-wait-resize behavior is when you maximize a non-transparent AIR window. First, it moves the window to -4,-4 (-8,-8 on Windows 7) and then resizes the window to fill the screen. Because of the delay, you see maximize happen in two steps. You see the window jump up to the top left corner first and then you see it resize to fill the screen. This doesn't happen with transparent windows and it doesn't happen with any other application on Windows.

It's really disappointing that something so basic is so buggy. I don't like that AIR resizes the window to 160×31 when you minimize, and I really wish they could move and resize non-transparent windows at the same time since other applications on Windows work that way.

As it is, I'm stuck trying to work around these issues and mitigate them as much as possible. I'm still hoping our commercial application will be a shining example of the kinds of things you can make with AIR, despite AIR's flaws. Most of the bad rap AIR gets is undeserved since most of the time, it is developers not knowing how to manage memory and other such nonsense. But in cases like this, it's something fundamental and specific to how the window draws on the most common target platform, Windows, and is completely out of my control.

All I can do is mitigate the issues as best I can, and write blog posts outlining the problems in the hops that the AIR engineering team will fix these issues in a future release.

Added to Adobe Bugs on Feb 8th

Posted in AIR, Bugs having 6 comments »

About Steven Sacks

I am a professional Flash developer with over 13 years of programming experience. I have consulted for high-profile agencies and companies in San Francisco, Los Angeles, Atlanta and New York, and developed numerous award-winning websites and rich internet applications for clients including Adobe, Fox Sports, FX Networks, Anheuser-Busch, GE, DirecTV, ESPN, The Weather Channel, Home Depot, and Coca-Cola.

I am the author of the open-source Gaia Framework for Adobe Flash, which dramatically reduces development time and makes developing Flash sites much easier.