Saturday, May 30, 2009

Making a draggable titlewindow without PopupManager

I wanted to make a application with a Windows-like interface with multiple draggable windows. TitleWindow was the obvious choice for the windows but they are only draggable only when created as a popup by the PopupManager.

I didn't want to use TitleWindow+PopupManager because I wanted to reserve PopupManager to display real popups that will always be on top of other components. Basically I wanted the windows to not be children of the systemManager but of a particular Canvas. This also means that I could have multiple "desktops" in the future.

To do this, first I created an extension of TitleWindow to make it draggable without being a popup.

DragWindow.as
package
{
import flash.events.MouseEvent;
import mx.containers.TitleWindow;

public class DragWindow extends TitleWindow
{
public function DragWindow()
{
super();
}

override protected function createChildren():void
{
super.createChildren();
this.titleBar.addEventListener( MouseEvent.MOUSE_DOWN, startDragging );
}
}
}

Then I created a WindowManager that replicates the behaviour of the PopupManager.

WindowManager.as

package
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.events.MouseEvent;

import mx.containers.Panel;
import mx.containers.TitleWindow;
import mx.core.IFlexDisplayObject;
import mx.events.CloseEvent;

public class WindowManager
{
// array of all windows in any container
public static var windowList:Array = [];

private static var instance:WindowManager;

public function WindowManager():void
{

}

public static function getInstance():WindowManager
{
if(instance == null)
{
WindowManager.instance = new WindowManager;
}
return WindowManager.instance;
}

public static function addWindow(window:IFlexDisplayObject, parent:DisplayObjectContainer):void
{
if(window is TitleWindow)
{
TitleWindow(window).showCloseButton = true;
window.addEventListener(CloseEvent.CLOSE, WindowManager.closeWindow);
}
window.addEventListener(MouseEvent.MOUSE_DOWN, WindowManager.focusWindow);
parent.addChild(DisplayObject(window));
windowList.push(window);
}

// Brings the window to the top of displaylist of the parent
public static function bringToFront(window:DisplayObject):void
{
var parent:DisplayObjectContainer = DisplayObjectContainer(window.parent);
parent.setChildIndex( window, parent.numChildren - 1);
}

public static function createWindow(parent:DisplayObjectContainer, className:Class):IFlexDisplayObject
{
var window:IFlexDisplayObject = new className();
WindowManager.addWindow(window, parent);

return window;
}

public static function removeWindow(window:IFlexDisplayObject):void
{
var parent:DisplayObjectContainer = DisplayObjectContainer(window.parent);
// Remove from display list
parent.removeChild(DisplayObject(window));
// Remove from window list
windowList.splice(windowList.indexOf(window),1);
}

// Brings the window to the top of displaylist of the parent
private static function focusWindow(event:MouseEvent):void
{
var window:DisplayObjectContainer = DisplayObjectContainer(event.currentTarget);
WindowManager.bringToFront(window);
}

// Removes the window from the displaylist
private static function closeWindow(event:CloseEvent):void
{
var window:IFlexDisplayObject = IFlexDisplayObject(event.currentTarget);
WindowManager.removeWindow(window);
}
}
}

asdasd

Saturday, May 16, 2009

Sending and receiving VOs in AMFPHP #1

Last week, I have worked to integrate sending Value Objects via AMFPHP instead of simple arrays in our current project and I would like share my knowledge for people who may have trouble getting started as I have.

I will detail the steps over three posts:

  1. Creating VOs on PHP and AS3

  2. Sending and Receiving VOs through AMFPHP

  3. Sending nested objects in AMFPHP

The first thing you need to do is edit globals.php on the server and edit the $voPath to the correct path for amfphp to find the vo files it receives to map against:

globals.php

$voPath = "path/to/your/vo/";

Then create the actual VO class in that path. Let's say the file is in "path/to/your/vo/users/UserVO.php". Because the vo path is set to "path/to/your/vo", the $_explicitType of the class should be "users.UserVO" because the file is located in "users/UserVO.php" relative to the vo path. This is important because this is the path the server will check for the corresponding VO file to map when it receives this VO from the Flex client.

UserVO.php
<?php
class UserVO
{
public $firstName;
public $lastName;
public $email;

public $_explicitType="users.UserVO";
}
?>

We need to create an AS3 equivalent of the UserVO class on the Flex side, but this time each property is strongly typed to the expected object types:

UserVO.as
package com.flexbreakpoints.vos
{
[RemoteClass(alias="users.UserVO")]
[Bindable]
public class UserVO
{
public var firstName:String;
public var lastName:String;
public var email:String;
}
}

The RemoteClass metatag is the key to identifying the object Flex receives from AMFPHP. The alia string must be identical to the $_explicitType in the PHP class it mirrors. This string is what Flex uses to map the received object to the correct AS3 VO. If no match is found, the object will default to ObjectProxy.

Remember, both the $_explicitType and RemoteClass strings should always be the PHP VO's path relative to the $voPath and not the AS3 VO's package. When you are only receiving the VO the strings only need to MATCH, and not necessarily the PHP VO's path, but it is always good idea to in case you need to send it back to the server.