Sunday, December 12, 2010

Flex 4 Custom TabBar

I needed a drag enabled spark TabBar so you could reorder its tabs.  I searched the web and could only find the SuperTabBar component in the FlexLib library.  But that was still based off the old mx components.

So, I bunkered down and created a drag enabled TabBar.  You can reorder and close each tab.  I based this CustomTabBar class off of Saturn Boy's TeffificTabBar component, so I was able to get the closing tabs functionality for free :)

The real challenge was adding in the dragging functionality of the tabs.  Luckily, the spark TabBar is based off the ListBase class.  This means I could just follow along with how the spark List component does its dragEnabled/dropEnabled functionality.

Take a look at the sample below.
View the sample (right click to view source)






-Dan

Tuesday, December 7, 2010

Flex 4 Dashboard

Here is the start of a Flex 4 dashboard application that I put together.  It uses my WindowManager class with five window modifiers.  Let me say right away, I completely ripped off the design from Adobe's Connect Now.  My dashboard is far from being finished and really just more of a shell than anything else.  In future posts, I will go over adding content to it.  As of right now, I was trying to mimic the windowing functionality of Adobe's Connect Now.

View the dashboard (right click to view source)

Below I will discuss how the dashboard application works.

You can now define a window bounds area by using the windowBoundsAreaFunction property on the WindowManager class.  You can use the windowManager.windowBoundsArea property in your window modifier classes.  Take a look at the WindowBoundaryEnforcer class to see how this works.  You don't need to define the windowBoundsAreaFunction.  By default, the bounds will be positioned at 0,0 and height and width of the flex app.  I needed this feature since I didn't want the windows to be dragged over the header and footer parts of the application.

Next up, I am using five window modifiers to mimic the windowing functionality that Adobe's Connect Now has.  I have already talked about the WindowSnapper, WindowBoundaryEnforcer and WindowEffects modifiers in a previous post, so I will only discuss the two new ones I have created for this dashboard.
  • WindowFiller
  • WindowMover
The WindowFiller modifier will find and place a newly added window in the largest amount of space available on the dashboard.  For example, the first window that you add will fill the screen.  If you resize the first window down, the second window you add will fill the newly created space.

The WindowMover modifier is responsible for moving and resizing the windows when the application is resized.  Try it out by adding some windows and resizing your browser window.  Thinking about it now, maybe I should have named this modifier WindowScaler.  I whipped this modifier up in about a hour, so it is not completely pixel perfect, but it gets the job done for now.

I am pretty happy with the way the WindowManager class is turning out.  My main goal has been the ability to just drop it in to my application and get the expected windowing functionality that I needed.  You can see this in action.  Just comment out the WindowManagement class in my dashboard sample and you will see the dashboard is still fully functional but just without the windowing features that you get with the WindowManager and its window modifiers.

I still have plenty of features that I want to work on.  My task list is below:
  • Maximizing of windows
  • Minimizing of windows
  • Saving and restoring windows - this is partially completed
  • Embedded/tabbed windows - this would allow drag and drop of windows into other windows
  • Window layouts
  • Anchoring of windows
Plenty of stuff to do...

-Dan

Saturday, December 4, 2010

Window Manager Update

Update - Take a look at the Flex 4 sample dashboard application using my WindowManager class here.

For the past couple of weeks I have been fooling around with the re-factoring of my WindowManager class.  One thing kept bugging me about the class.  With every new feature I would add, the class got larger and I started to get lost in my own code.  To alleviate these growing pains, I modularized it.  Like in the real world, most managers don't do any of the heavy lifting.  They delegate the work to team members.  The WindowManager class now delegates the work to what I am calling window modifiers.  Window modifiers are the ones responsible for the heavy lifting.

The WindowManager class initially had two features, window snapping and window enforcing of boundaries.  These two features were the first candidates to get split into separate window modifier classes.
  • WindowSnapper
  • WindowBoundaryEnforcer
Not much goes on in the WindowManager class now.  It's pretty simple.  The WindowManager class just passes key window events such as windowAdd, windowMoving, windowMoveEnd and windowResizing to each window modifier that has been registered.  Each window modifier manipulates the window however its been programmed to.  In order to be a window modifier, it needs to implement the IWindowModifier class.  I have made it easy and created a WindowModifier base class that you can just extend and override the methods that will get you your required functionality.

Below is an example of the WindowManager class with the two window modifiers mentioned above.  One major change I should note is that the WindowManager class will now only manage TitleWindows that implement the IManagedWindow interface.  This was a design decision for times where I needed a TitleWindow that I didn't want to be managed by the WindowManager.

View Sample 1 (right click the sample to view source)

The great thing about this modularization is that you only need to include what you need.  For instance, if you have an existing Flex application and all you want is to enforce boundaries on your windows, then all you need to do is declare the WindowManager class somewhere, add the WindowBoundaryEnforcer window modifier to it, and update your existing windows to implement the IManagedWindow interface.

Lets take a look at WindowBoundaryEnforcer window modifier class in detail to get a better idea on what it takes to be a window modifier.
First, the WindowBoundaryEnforcer class extends the base WindowModifier class.  This is the easiest route to take since we are only after a couple of window events.  Next, we override five methods.
  • onWindowAdd(window:TitleWindow):void
  • onWindowMoving(window:TitleWindow, bounds:Rectangle):void
  • onWindowResizing(window:CustomTitleWindow, bounds:Rectangle):void
  • onWindowMoveEnd(window:TitleWindow, bounds:Rectangle):void
  • onModifiersComplete():void
There are more methods a window modifier could override, but in the case of the WindowBoundaryEnforcer, these are the only methods it needs to do its job.  Take a look at the source for the WindowBoundaryEnforcer class to get a better understanding.

Here is another simple sample of a window modifier in action.  In the sample below, I have added a Window Effects window modifier.  It overrides one method.
  • onWindowAdd(window:TitleWindow):void
It is pretty basic, when the window is added, it adds a Rotate3D and Fade effect to the added window.

View Sample 2 (right click the sample to view source)

Here is one last sample showing how window modifiers work.  In the sample below, I have included four window modifiers.  The WindowSnapper, WindowBoundaryEnforcer, WindowEffects and a new WindowSaver class that is responsible for saving the position of the window and re-positioning when the window is added again.  When you view the sample for the first time, the windows will first be positioned at 0,0.  After you move or resize a window, the window position and size will get stored in a shared object.  After positioning and resizing the windows, close the application.  When you view the application again, the windows should be positioned and sized to where you last left them.

View Sample 3 (right click the sample to view source)

In the next post, I will show you some more window modifiers that I have created and also the start of a Flex 4 dashboard entirely based off the WindowManager class and its window modifiers.


Saturday, October 16, 2010

Flex 4 Custom Date Chooser

Here is a pretty simple date chooser you could use to control a calendar.  It has three modes:
  • day
  • week
  • month
It dispatches two events:
  • selectedDateChanged
  • modeChanged
You can skin it to any look you want.  My skins I have provided are just for example purposes and you would probably want to spice it up a bit for use in your application.

View Sample (right click the sample to view source)

Thursday, September 30, 2010

Flex 4 TitleWindow Manager

Update - Take a look at a updated version of the TitleWindowManager class here and a sample dashboard application here.

Here is a TitleWindow manager class that I put together to manage my CustomTitleWindows. It is basically the same as the MDI Manager class in the MDI framework, except my manager class works with the new TitleWindows in Flex 4. Also, my manager class only detects TitleWindows that are created by Flex's PopUpManager class. It has the following features:
  • Enforce Boundaries

    • This feature basically doesn't allow the user to drag the TitleWindow outside the bounds of the application.
    • I also added a new feature that you can turn on called "soft boundaries".  This allows the user to drag the window outside the application bounds, but then snaps back into the application once the window is let go.  The snapping back can be animated if you choose.


  • Snapping

    • This feature is exactly the way it works in the MDI framework, but I just modified it to work with the new Flex 4 TitleWindows.
    • I also added a new feature that you can turn on called "snapToEdges".  This allows the windows to not only snap to other windows, but also to the edges of the application.
I just want to note that you can use this manager class to manage regular old Flex 4 TitleWindows.  You don't need to use my CustomTitleWindow class.  This means that you could just drop in this manager class into your existing Flex application and would have to make no modifications to your existing Flex 4 TitleWindows that were created through the PopUpManager to get the functionality mentioned above.

Enough talking, click the link below to view the sample.

View Sample (right click the sample to view source)

Tuesday, September 21, 2010

Yet Another Resizable TitleWindow in Flex 4

Here is yet another Adobe Flex 4 resizable TitleWindow component I put together. It supports the following properties:
  • enforceBoundaries
  • resizeEnabled
  • moveEnabled
You can also re-size the window from all sides, not just the bottom right corner. I have written the resizing to follow the B features that Adobe has listed out for the Spark TitleWindow.  You can view that spec here.  In the future the resizing could be baked right into the base TitleWindow class.

I took bits of code from previous examples of Flex 3 re-sizable TitleWindows.  Most of the resizing logic came from the MDI components in FlexLib.  I just tweaked some of it to work with Flex 4 and its new skinning features.

Here are some other implementations of re-sizable Flex 4 TitleWindows.
http://flexponential.com/2010/01/10/resizable-titlewindow-in-flex-4/
http://flexdevtips.blogspot.com/2010/06/flex-4-spark-resizable-controls.html

Here's the example. In the sample project below, I have declared the required CSS for the TitleWindow in the defaults.css that get compiled in automatically. Don't forget that CSS when using the TitleWindow in your own projects. (right click to view source):


Tuesday, September 14, 2010

Flex 4 Busy Indicator Component

I wrote up a pretty simple component that you can use to wrap your data displays to indicate data is being loaded/refreshed. The base component extends SkinnableContainer and has only one property named "busy". Set busy to true to show the animation and set to false to hide.
The real magic happens in the Skins. All three Skins that I have provided support two CSS properties, source and scale. Set the source to an SWF and set scale to true or false.
You can easily customize the skins to whatever look and feel you would like. I have provided three. The first one with a simple pulsating red glow on a dark background. The second one has no glow and a light background. The third is pretty much the same as the first but with a green glow.
You could even create a skin that displayed a static image instead of displaying a animated SWF. The BusyIndicator base component doesn't care what gets displayed.

Before I forget I did not create the flash animations. The wonderful person at DesignSwan.com created them. Here is the link to them:
18 Flash Loading Animations
I only parted the animations into separate SWF files and I added a few lines of code so I could stop the animation when it was invisible on the screen. Each one of the fla files is included in the source under the flash files folder.
In the example below I am also using the Flex 4 resize controls that you can find from here on the TitleWindow to demonstrate that the animation can be scaled.

Here's the example. Use the ComboBox control to switch between animations. (right click to view source):


Friday, May 14, 2010

Flex PopUpMenuButton

I was tasked the other day at work to modify the Flex PopUpMenuButton to behave like a single button. The PopUpMenuButton default basically acts like two buttons. The left button executes the first menu item, and the "Arrow" button drops down the menu. The users did not like this default functionality and only wanted one button to drop down the menu. Instead of trying to re-create the PopUpMenuButton with a single button, I instead extended the PopUpMenuButton. With a simple mx_internal override of one function, I was able to get the menu to drop down no matter where you click on the button.

Here's the example. (right click to view source):


Hopefully, this will save someone some time.

Saturday, May 1, 2010

A Smarter Flex ChannelSet

The ChannelSet class in the Adobe Flex Framework is pretty brilliant. For example, lets say we were building an online auction application using LCDS(LiveCycle Data Services) where users can submit bids on items. We would want this application to be Real-Time. Our first choice for how the client connects to the back-end should be the RTMP protocol using the RTMPChannel class. This type of connection beats out any other connections out there. It beats streaming/comet and polling type connections by a long shot. But lets say some of our users are behind strict firewalls that will not allow them to connect using the RTMP protocol. Does this mean these users will not be able to use our application? Nope, this is why the ChannelSet class is so brilliant. We can provide the client with backup/failover channels to connect on. Take a look at the example below.

   
   
   

Notice above I created a ChannelSet with three channels defined. The client will attempt to connect using the RTMP channel first. If the client fails on RTMP, it will attempt on streaming. If the client fails on streaming, it will attempt on the long polling channel. As a developer this is nice as I don't have to code any of this fail over logic. This functionality is baked right into the ChannelSet class. There is only one problem with the ChannelSet class. The problem arises when a client connects, disconnects and then connects again. The client might get disconnected due to loosing their wireless internet. When the client gets disconnected, the ChannelSet class "connection logic" will kick in gear and attempt to connect by looping over the channels until one channel successfully connects. This might be fine for some situations, but let me explain a particular situation where you don't want this to happen.

For example, a user initially connects using RTMP. The user is using the application for awhile, but all of a sudden they lose their internet connection. The ChannelSet class is made aware of the disconnect and its "connection logic" kicks into gear and loops over each channel attempting to connect until one is successful. The users internet connection than comes back online. The user for some reason is now connected using long polling. What! Why the heck is the user connected using the long polling channel when the user initially connected using the RTMP channel. The answer is because the ChannelSet class "connection logic" is just a simple loop over the channels and when the users internet came back, the loop was currently attempting to connect on the long polling channel and it did. That is why the user is now connected over the long polling channel.

So, basically to sum up this problem. The user initially connected over RTMP, gets disconnected, re-connects and connects over long polling that is on the bottom of our channel list. How do we fix this little problem? Simple, we create a smarter ChannelSet class. Take a look at the smarter ChannelSet class that I named "CustomChannelSet" below.
package net.flashdan.util {
 import mx.messaging.Channel;
 import mx.messaging.ChannelSet;
 import mx.messaging.events.ChannelEvent;
 
 public class CustomChannelSet extends ChannelSet {
  
  public function CustomChannelSet(channelIds:Array=null, clusteredWithURLLoadBalancing:Boolean=false) {
   super(channelIds, clusteredWithURLLoadBalancing);
   addEventListener(ChannelEvent.CONNECT, onChannelConnect);
  }
  
  private function onChannelConnect(event:ChannelEvent):void {
   //loop through the channels for this channelset and remove all but the connected one
   for (var i:int=channels.length-1;i>=0;i--) {
    var channel:Channel = channels[i] as Channel;
    if(channel.id != currentChannel.id){
     removeChannel(channel);
    }
   }
  }
 }
}
We would then use our CustomChannelSet like so.

   
   
   

The solution is pretty simple. When the user initially connects over a particular channel, remove all other channels except the currently connected channel. This way when the user initially connects over RTMP, gets disconnected, re-connects, the ChannelSet "connection logic" only attempts to connect over the RTMP channel and no others. Problem solved! I am providing a small sample flex application with the SmarterChannelSet class being used. It's a Flex 4 project. Unzip the file and import the .fxp project in Flash Builder 4.
Download Source

Monday, April 26, 2010

cfObjective 2010 Presentation "Real-Time Death Match"

I presented this year at cfObjective. I had the pleasure to present on the topic of "Real-Time" You can download my slides and code below.
Download Presentation

Monday, April 5, 2010

A Smarter Flex AMFChannel

I use the AMFChannel class probably in all of my Flex projects. Below is a sample of how I use the AMFChannel class to make remote calls from Flex.
   import mx.messaging.ChannelSet;
   import mx.messaging.channels.AMFChannel;
   import mx.rpc.remoting.RemoteObject;

   public function setup():void {
     var remoteChannelSet:ChannelSet = new ChannelSet();
     remoteChannelSet.addChannel(new AMFChannel("my-amf","http://someURL.com/flex2gateway/"));
    
     var service:RemoteObject = new RemoteObject();
     service.channelSet = remoteChannelSet;
    
     //more code goes here...
   }
One thing that I had to tackle early on in my Adobe Flex career was figuring out why my AMF remote calls using the AMFChannel class stopped working when I pushed the code to a secure(https) production environment. Well, it turns out that when you make secure remote calls, you need to use the SecureAMFChannel class. So, instead you would need to replace the code above with something like below.
import mx.messaging.ChannelSet;
import mx.messaging.channels.SecureAMFChannel;
import mx.rpc.remoting.RemoteObject;

public function setup():void {
  var remoteChannelSet:ChannelSet = new ChannelSet();
  remoteChannelSet.addChannel(new SecureAMFChannel("my-secure-amf","https://someURL.com/flex2gateway/secure"));
    
  var service:RemoteObject = new RemoteObject();
  service.channelSet = remoteChannelSet;
    
  //more code goes here...
}
The above code will work in a secure (https) production environment, but now we have hard coded the channel type. It won't work in our development environment anymore. How do we solve this little dilemma? Well, it's pretty easy. Just extend the AMFChannel class and override the getter function for the property "protocol" and write the logic to figure out if the remote calls are going to be secure or not. Below is a smarter AMFChannel class which I just named CustomAMFChannel.
package net.flashdan.utils.channels {
 import mx.messaging.channels.AMFChannel;
 
 public class CustomAMFChannel extends AMFChannel {

   public function CustomAMFChannel(id:String=null, uri:String=null) {
    super(id, uri);
   }
  
   override public function get protocol():String {
    if(this.url.toLowerCase().search("https://") == -1){
     return "http";
    }
    else {
     return "https";
    }
   }
 }
}
You would then use the above class like so.
import mx.messaging.ChannelSet;
import mx.rpc.remoting.RemoteObject;
import net.flashdan.utils.channels.CustomAMFChannel;

 protected function setup():void {
   var remoteChannelSet:ChannelSet = new ChannelSet();
   remoteChannelSet.addChannel(new CustomAMFChannel("my-amf", "https://someURL.com/flex2gateway/secure"));
    
   var service:RemoteObject = new RemoteObject();
   service.channelSet = remoteChannelSet;
    
   //more code goes here...
 }
Hopefully, this will help someone out.
Download Source