PixLib - Understanding the basis of the framework

readed 11016 times

This tutorial is provided to give you the basis to be able to use the so good PixLib framework made by Francis Bourre. You should know at least what is a MVC pattern and a Command pattern.

Usually, using a MVC, each view will have a controller that will update the model ( if needed ) and any views that need to be changed.
This can be achieved in the PixLib library using the class in com.bourre.mvc. But this tutorial will not deal with this "usual" MVC pattern.

Francis add to his framework something really flexible, called the FrontController. This controller is receiving events from any views and then call the Command associated to it.
You're code is then really clean and pratical: one class = one command so your code for an action can be found really easilly, plus you can see all the actions that could be executed looking at the FrontController code, everything is in there.
You will not look in X classes to try to find the code that do this particular action, you will go straight to the goal.

Here is a basic scheme of it

So enought blabla let's have a look at it. I will explain what is what and what is doing what as we will see the examples. We will try to build a clock with analog and digital display.

Download the last frameword (revision 35)
Sources Sources File and Tweaked class (Size: 28.5 KB)

Pixlib Framework r35 Download the framework (Size: 369.2 KB)

Download the sources for this tutorial.
Sources Download the tutorial sources (Size: 49.8 KB)

This is a template for futur projects you will do.
Pixlib Template Download the template (Size: 22.6 KB)

 

I. The FLA

First check the fla. I have created 3 MovieClips. One for the digital clock, one for the analog and one for the toolbar where the buttons to stop and start the clock are. Those MovieClip will be used as the views ( interfaces )
In the first ( and only ) frame of the fla you have this code

Actionscript:
  1. import net.webbymx.projects.tutorial01.Application;
  2. var apz : Application = new Application( this );

What we are doing here is importing the Application.as wich is the entry point of our code, and creating an instance of it using the _root or _level0. We will see later what is it for.

 

II. The Code

First let see how a project using the PixLix Framework is structured.
To help you, I've done a template for it. You will find the basic files needed to develop an application with the pixlib library using the MVCC ( MVC + COMMAND ) pattern.

So here is the structure
PixLib Project Structure

and the quick explanation

  • commands: contain all your classes implementing the com.bourre.commands.Command interface. Will contain as well the FrontController.
  • events: contain all your classes defining a new type of event ( extending the BasicEvent ). Will contain as well your custom EventBroadcaster class and the EventList class which is only an enumeration of all the events existing.
  • models: contain the model and the ModelList
  • services: will contain every classes that deal with exterior datas ( like server, xml, ... )
  • uis: contain all classes that define your User Interfaces
  • views: all the classes that will extend the MovieClipHelper or ViewHelper classes and that will be link to a MovieClip. Contain as well the ViewList class
  • vos: classes to define custom value object
  • Application.as: this is the main class of your application that will create everything
 

a. Application

So ok, let's have a look now at the Application.as class:

Actionscript:
  1. //  Debug
  2. import com.bourre.log.Logger;
  3. import com.bourre.log.LogLevel;
  4. import com.bourre.utils.LuminicTracer;
  5.  
  6. //  Views
  7. import net.webbymx.projects.tutorial01.views.ViewAnalog;
  8. import net.webbymx.projects.tutorial01.views.ViewDigital;
  9. import net.webbymx.projects.tutorial01.views.ViewTools;
  10.  
  11. //  Controller
  12. import net.webbymx.projects.tutorial01.commands.Controller;
  13.  
  14. //  Models
  15. import net.webbymx.projects.tutorial01.models.ModelClock;
  16.  
  17. //  Personnal EventBroadcaster
  18. import net.webbymx.projects.tutorial01.events.XEventBroadcaster;
  19.  
  20.  
  21. class net.webbymx.projects.tutorial01.Application
  22.     extends MovieClip {
  23. /* ****************************************************************************
  24. * PRIVATE VARIABLES
  25. **************************************************************************** */
  26.    
  27.    
  28. /* ****************************************************************************
  29. * CONSTRUCTOR
  30. **************************************************************************** */
  31.     function Application(container) {
  32.     //  the movieclip is transtyped as a application object
  33.         container.__proto__ = this.__proto__;
  34.         container.__constructor__ = Application;
  35.         this = container;
  36.        
  37.     //  init the object
  38.         _init();
  39.     }
  40.    
  41.  
  42. /* ****************************************************************************
  43. * PRIVATE FUNCTIONS
  44. **************************************************************************** */
  45. /**
  46. * Init the Application
  47. * @param    Void
  48. */
  49.     private function _init( Void ) : Void {
  50.     //  init the debugger
  51.         Logger.getInstance().addLogListener( LuminicTracer.getInstance() );
  52.            
  53.     //  Instanciating the views
  54.         var digital : MovieClip = this.attachMovie( "mc_digital", "mc_digital", 0 );
  55.         var vDigital  : ViewDigital = new ViewDigital ( digital );
  56.        
  57.         var analog : MovieClip = this.attachMovie( "mc_analog", "mc_analog", 1 );
  58.         var vAnalog  : ViewAnalog = new ViewAnalog ( analog );   
  59.  
  60.         var tools : MovieClip = this.attachMovie( "mc_tools", "mc_tools", 2 );
  61.         var vTools  : ViewTools = new ViewTools ( tools );   
  62.        
  63.     //  create the model
  64.         var mClock : ModelClock = new ModelClock();
  65.        
  66.     //  the views are listening to the model
  67.         mClock.addListener( vDigital );
  68.         mClock.addListener( vAnalog );
  69.         mClock.addListener( vTools );
  70.        
  71.     //  init the controller with our custom EventBroadcaster class
  72.         Controller.getInstance( XEventBroadcaster.getInstance() );
  73.     }
  74.  
  75.  
  76. /* ****************************************************************************
  77. * PUBLIC FUNCTIONS
  78. **************************************************************************** */
  79. /* ****************************************************************************
  80. * GETTER & SETTER
  81. **************************************************************************** */
  82. }

Ok what is it doing?

Actionscript:
  1. container.__proto__ = this.__proto__;
  2. container.__constructor__ = Application;
  3. this = container;

Well, first we are transtyping the "container" ( pass as an argument ) which is here ( and in most of the case ) the _root / _level0 of your fla, as an Application object. As you can see our Application.as class is inheriting the MovieClip, so we are keeping the MovieClip properties and methods.

We are then calling the _init() function which will set up everything.

First we are defining the Debugger

Actionscript:
  1. Logger.getInstance().addLogListener( LuminicTracer.getInstance() );

Second we are creating our views from the MovieClip that we have attached on the stage

Actionscript:
  1. var digital : MovieClip = this.attachMovie( "mc_digital", "mc_digital", 0 );
  2. var vDigital  : ViewDigital = new ViewDigital ( digital );
  3.    
  4. var analog : MovieClip = this.attachMovie( "mc_analog", "mc_analog", 1 );
  5. var vAnalog  : ViewAnalog = new ViewAnalog ( analog );   
  6.  
  7. var tools : MovieClip = this.attachMovie( "mc_tools", "mc_tools", 2 );
  8. var vTools  : ViewTools = new ViewTools ( tools );

Third, we are creating the model

Actionscript:
  1. var mClock : ModelClock = new ModelClock();

and setting the views as listener of this model, so then when a model will broadcast an event the views will react to it

Actionscript:
  1. mClock.addListener( vDigital );
  2. mClock.addListener( vAnalog );
  3. mClock.addListener( vTools );

Finally we are creating the FrontController, that will listen to the XEventBroadcaster and call the command associated to the event

Actionscript:
  1. Controller.getInstance( XEventBroadcaster.getInstance() );

Now we will go through each class to see what they are doing.

 

b. The ViewList, the Views and how to use it

First the ViewList. This class is just an enumeration of all the views ( class ) you can have in you application. As flash is not providing enumeration type, this is a way to do it...
Here is the class

Actionscript:
  1. /**
  2. * listing of the views
  3. */
  4. class net.webbymx.projects.tutorial01.views.ViewList {
  5. /* ****************************************************************************
  6. * PUBLIC STATIC VARIABLES
  7. **************************************************************************** */
  8.     public static var VIEW_ANALOG   : String = "view_analog";
  9.     public static var VIEW_DIGITAL  : String = "view_digital";
  10.     public static var VIEW_TOOLS    : String = "view_tools";
  11.    
  12.    
  13. /* ****************************************************************************
  14. * CONSTRUCTOR
  15. **************************************************************************** */
  16.     private function ViewList() {}
  17. }

 

Then, let check the view ViewTool, because in the small application we are doing it's the one using all the basic functionalities.
So first the full code

Actionscript:
  1. //  Debug
  2. import com.bourre.log.Logger;
  3. import com.bourre.log.LogLevel;
  4.  
  5. //  Delegate
  6. import com.bourre.commands.Delegate;
  7.  
  8. //  Event Broadcasting
  9. import com.bourre.events.IEvent;
  10. import com.bourre.events.BasicEvent;
  11. import com.bourre.events.EventType;
  12. import net.webbymx.projects.tutorial01.events.EventList;
  13. import net.webbymx.projects.tutorial01.events.XEventBroadcaster;
  14.  
  15. //  MovieClipHelper
  16. import com.bourre.visual.MovieClipHelper;
  17.  
  18. //  list of Views
  19. import net.webbymx.projects.tutorial01.views.ViewList;
  20.  
  21. class net.webbymx.projects.tutorial01.views.ViewTools
  22.     extends MovieClipHelper {
  23. /* ****************************************************************************
  24. * PRIVATE VARIABLES
  25. **************************************************************************** */
  26. //  Assets
  27.     private var _txtColor   : TextField;
  28.     private var _btnStart   : MovieClip;
  29.     private var _btnStop    : MovieClip;
  30.     private var _btnColor   : MovieClip;
  31.        
  32. /* ****************************************************************************
  33. * CONSTRUCTOR
  34. **************************************************************************** */
  35.     function ViewTools( mc : MovieClip ) {
  36.         super( ViewList.VIEW_TOOLS, mc );
  37.         _init();
  38.     }
  39.    
  40.    
  41. /* ****************************************************************************
  42. * PRIVATE FUNCTIONS
  43. **************************************************************************** */
  44. /**
  45. * Init the view
  46. * @param    Void
  47. */
  48.     private function _init( Void ) : Void {
  49.     //  Position the MovieClip
  50.         view._x = 33;
  51.         view._y = 180;
  52.    
  53.     //  init var, assets, events, ...
  54.         _txtColor = view.txt_color;
  55.         _btnStart = view.btn_start;
  56.         _btnStop = view.btn_stop;
  57.         _btnColor = view.btn_color;
  58.        
  59.     //  Mouse events
  60.         _btnStart.onRelease = Delegate.create( this, _fireEvent, new BasicEvent( EventList.START_CLOCK ) );
  61.         _btnStop.onRelease = Delegate.create( this, _fireEvent, new BasicEvent( EventList.STOP_CLOCK ) );
  62.         _btnColor.onRelease = Delegate.create( this, _fireEvent, new BasicEvent( EventList.CHANGE_CLOCK_COLOR) );
  63.        
  64.         disableStart();
  65.  
  66.     }
  67.  
  68.  
  69. /**
  70. * Broadcast the event
  71. * @usage    _fireEvent( new BasicEvent( EventList.MYTYPE, Object ) );
  72. * @param    e
  73. */
  74.     private function _fireEvent( e : IEvent ) : Void {
  75.         XEventBroadcaster.getInstance().broadcastEvent( e );
  76.     }
  77.  
  78.  
  79. /* ****************************************************************************
  80. * PUBLIC FUNCTIONS
  81. **************************************************************************** */
  82.     public function disableStart( Void ) : Void {
  83.         _btnStop.enabled = true;
  84.         _btnStop._alpha = 100;
  85.         _btnStart.enabled = false;
  86.         _btnStart._alpha = 60;
  87.     }
  88.    
  89.     public function disableStop( Void ) : Void {
  90.         _btnStop.enabled = false;
  91.         _btnStop._alpha = 60;
  92.         _btnStart.enabled = true;
  93.         _btnStart._alpha = 100;
  94.     }
  95.    
  96.    
  97. /* ****************************************************************************
  98. * GETTER & SETTER
  99. **************************************************************************** */
  100.  
  101. }

Now let's see how it works.
A view is an interface. The code in this class only act on the visual aspect. Your view ( class ) is so associated to a visual object ( a MovieClip ). Here, we are not using the inheritance as we have done for the Application, but the composition. Our ViewTool has a property call view where the reference of the MovieClip will be stored.
Looking at the constructor

Actionscript:
  1. function ViewTools( mc : MovieClip ) {
  2.     super( ViewList.VIEW_TOOLS, mc );
  3.     _init();
  4. }

we can see that we are calling the super class ( MovieClipHelper ) constructor. This one is storing the name of your view and the reference of it in a Map ( array associating object ), and the reference of your MovieClip in the property view.

Why that?
The MovieClipHelper class has a static public function called getMovieClipHelper. This function return the view ( class ) wanted. You're using it like that

Actionscript:
  1. ViewTool( MovieClipHelper.getMovieClipHelper( ViewList.ViewTool ) )

This mean that you can access your view from anywhere and thus have control on your MovieClip ( with the view properties or by using public functions )

Ok for this part. Now we are looking at the code in the _init function.
Instead of accessing the assets on the MovieClip by doing

Actionscript:
  1. ViewTool.view.txt_color

I prefer setting up private variables in my class that refer to those assets.

Actionscript:
  1. _txtColor = view.txt_color;
  2. _btnStart = view.btn_start;
  3. _btnStop = view.btn_stop;
  4. _btnColor = view.btn_color;

 

Finally we are setting up the mouse event for the button

Actionscript:
  1. _btnStart.onRelease = Delegate.create( this, _fireEvent, new BasicEvent( EventList.START_CLOCK ) );
  2. _btnStop.onRelease = Delegate.create( this, _fireEvent, new BasicEvent( EventList.STOP_CLOCK ) );
  3. _btnColor.onRelease = Delegate.create( this, _fireEvent, new BasicEvent( EventList.CHANGE_CLOCK_COLOR) );

Each button will dispatch an event ( from the enumeration of the EventList class ).
Here I'm using the Delegate way ( cleaner ) but you could use

Actionscript:
  1. _btnStart.onRelease = function () {
  2.     XEventBroadcaster.getInstance().broadcastEvent( new BasicEvent( EventList.START_CLOCK ) );
  3. }

Then we have 2 public function that a command will call to enable/disable the stop/start buttons

 

c. The ModelList and the ModelClock

The ModelList is like the ViewList. It's just a class listing variables, to emulate the enum ( enumeration ) type that doesn't exist in flash. So in this class we will list all the models name we are using in our application ( here one only ). Yes actually you can create more than one model if you need it ( eg to separate different logic ). Anyway I never done it yet...

The ModelClock is a class storing the code for the logic, everything that's "behind the scene" to make your application working. In our example it will store the code to tick every seconds.

Actionscript:
  1. //  Debug
  2. import com.bourre.log.Logger;
  3. import com.bourre.log.LogLevel;
  4.  
  5. //  Model
  6. import com.bourre.core.Model;
  7.  
  8. //  Import the model list
  9. import net.webbymx.projects.tutorial01.models.ModelList;
  10.  
  11. //  Broadcasting event
  12. import com.bourre.events.EventType;
  13. import com.bourre.events.BasicEvent;
  14.  
  15. class net.webbymx.projects.tutorial01.models.ModelClock
  16.     extends Model {
  17.  
  18. /* ****************************************************************************
  19. * PRIVATE STATIC VAR
  20. **************************************************************************** */
  21.     private static var CLOCK_TICK : EventType =  new EventType( "onClockTicking" );
  22.  
  23.  
  24. /* ****************************************************************************
  25. * PRIVATE VAR
  26. **************************************************************************** */
  27.     private var _siTick : Number;
  28.     private var _dDate  : Date;
  29.    
  30.  
  31. /* ****************************************************************************
  32. * CONSTRUCTOR
  33. **************************************************************************** */
  34.     function ModelClock() {
  35.         super( ModelList.MODEL_CLOCK);
  36.         _startTicking();
  37.     }
  38.  
  39.  
  40.  
  41. /* ****************************************************************************
  42. * PRIVATE FUNCTIONS
  43. **************************************************************************** */
  44.     private function _startTicking( Void ) : Void {
  45.     //  set up the date actual date and time
  46.         _dDate = new Date();
  47.         _tick();
  48.         _siTick = setInterval( this, "_tick", 1000 );
  49.     }
  50.    
  51.     private function _stopTicking( Void ) : Void {
  52.         clearInterval( _siTick );
  53.     }
  54.    
  55.     private