flex 4: window system development ideas - actionscript-3

My application will need some windows but I can't use the popUpManager to handle custom components so I'm thinking to implement a new window system.
But I still don't know how to merge it with my custom components. Any ideas?
My code:
My login skin:
<s:Skin(...)>
<s:Group>
(...login components...)
</s:Group>
</s:Skin>
package com.totty.app.components.login {
import com.totty.app.TottysBrain;
import com.totty.app.components.window.Window;
import com.totty.app.events.LoginSuccessEvent;
import com.totty.tottysBrain.components.BrainyDynComponent;
import com.totty.tottysBrain.components.BrainyWindow;
import flash.events.Event;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.managers.PopUpManager;
import spark.components.Button;
import spark.components.CheckBox;
import spark.components.Group;
import spark.components.Label;
import spark.components.TextInput;
import spark.components.supportClasses.SkinnableComponent;
[Event(name="loginSuccess", type="com.totty.app.events.LoginSuccessEvent")]
public class Login extends BrainyDynComponent {
[SkinPart(required="true")]
public var email:TextInput;
[SkinPart(required="true")]
public var password:TextInput = new TextInput();
[SkinPart(required="false")]
public var rememberMe:CheckBox;
[SkinPart(required="false")]
public var notification:Label;
[SkinPart(required="true")]
public var submit:Button = new Button();
private var _currentState:uint = 0;
private var _states:Array = ['default', 'logging', 'loginFailure', 'loginSuccess'];
private var _default:Boolean = false;
private var _logging:Boolean = false;
private var _loggingSuccess:Boolean = false;
private var _loggingFailure:Boolean = false;
public function Login() {
super();
defaultCommand = 'users.login';
defaultSkin = LoginSkin;
_setCurrentState(0);
onPartAdded('submit', function():void {
submit.addEventListener(MouseEvent.CLICK, _submitLoginButton_click);
});
onPartRemoved('submit', function():void {
submit.removeEventListener(MouseEvent.CLICK, _submitLoginButton_click);
});
onPartAdded('window', function():void{
//window.addEventListener('close', _window_close);
//window.title = 'Login';
});
}
private function _window_close(evt:Event):void{
dispatchEvent(evt);
var parentAsGroup:Group = parent as Group
parentAsGroup.removeElement(this);
}
protected function _submitLogin():void {
_setCurrentState(1);
submitData({email:email.text, password:password.text, rememberMe:rememberMe.selected});
}
override protected function _onSuccess(result:*):void {
Alert.show(result);
_setCurrentState(3);
if(result) {
dispatchEvent(new LoginSuccessEvent(email.text, rememberMe.selected));
_loggingSuccess = true;
}else{
_setCurrentState(4);
_loggingSuccess = false;
}
}
override protected function _onFailure(result:*):void {
Alert.show(result);
_setCurrentState(4);
_loggingSuccess = false;
}
private function _submitLoginButton_click(evt:MouseEvent):void {
_submitLogin();
}
private function _setCurrentState(n:uint):void{
if(_currentState == n) return;
_currentState = n;
invalidateProperties();
}
override protected function commitProperties():void{
super.commitProperties();
switch(_currentState){
case 0:
// default
//window.footerText = '';
break;
case 1:
// logging
//window.footerText = 'logging';
break;
case 2:
// loginFailure
//window.footerText = 'loginFailure';
break;
case 3:
// loginSuccess
//window.footerText = 'loginSuccess';
//parentBrain.removeElement(this);
//var parentAsGroup:Group = parent as Group
//PopUpManager.removePopUp(this);
//parentAsGroup.removeElement(this);
break;
}
}
}
}`
Now I have the main application to add this component in a window. I would prefer to be able to be able to see the window while I'm designing the interface. That means that the window component must be added to the component. Not to use the but something like to contain all the contents of the component.

Maybe you already saw this, but its a good place to start, anyway :)
http://code.google.com/p/flexmdi/

Related

Receiving a stream of a NetGroup

I'm trying to receive a stream of a NetGroup (television) but it fails at some point. Here is the code (reduced to a testcase):
package lib
{
import flash.events.NetStatusEvent;
import flash.external.ExternalInterface;
import flash.media.SoundTransform;
import flash.media.Video;
import flash.net.GroupSpecifier;
import flash.net.NetConnection;
import flash.net.NetGroup;
import flash.net.NetStream;
import flash.net.NetStreamPlayOptions;
import mx.core.FlexGlobals;
public class player
{
private var connection:NetConnection;
private var group:NetGroup;
private var group_option:GroupSpecifier;
private var self:Object;
private var stream:NetStream;
private var stream_option:NetStreamPlayOptions;
private var video:Video;
private function _connect():void
{
self.connection = new NetConnection();
self.connection.addEventListener(NetStatusEvent.NET_STATUS, self._event_net_status);
self.connection.connect('rtmfp://p2p.peer-stream.com');
}
private function _event_net_status(event:NetStatusEvent):void
{
if(event.info.code === '')
return;
ExternalInterface.call('echo', 'status', event.info.code, false);
switch(event.info.code)
{
case 'NetConnection.Connect.Success':
self._p2p_start();
break;
case 'NetGroup.Connect.Success':
self._stream_init();
break;
case 'NetStream.Connect.Success':
self._stream_start();
break;
}
}
private function _p2p_start():void
{
self.group_option = new GroupSpecifier('G:0101010c0b0e70726f2073696562656e00');
self.group_option.ipMulticastMemberUpdatesEnabled = true;
self.group_option.multicastEnabled = true;
self.group_option.objectReplicationEnabled = true;
self.group_option.postingEnabled = true;
self.group_option.routingEnabled = true;
self.group_option.serverChannelEnabled = true;
self.group = new NetGroup(self.connection, self.group_option.groupspecWithAuthorizations());
self.group.addEventListener(NetStatusEvent.NET_STATUS, self._event_net_status);
}
private function _stream_init():void
{
self.stream = new NetStream(self.connection, self.group_option.groupspecWithAuthorizations());
self.stream.addEventListener(NetStatusEvent.NET_STATUS, self._event_net_status);
}
private function _stream_start():void
{
self.video.attachNetStream(self.stream);
self.stream.soundTransform = new SoundTransform();
self.stream_option = new NetStreamPlayOptions();
self.stream_option.streamName = 'G:0101010c0b0e70726f2073696562656e00';
self.stream.play2(self.stream_option);
}
public function player():void
{
super();
FlexGlobals.topLevelApplication.stage.quality = 'BEST';
self = this;
self.video = new Video(640, 480);
FlexGlobals.topLevelApplication.video_box.addChild(self.video);
self.video.smoothing = true;
self._connect();
}
}
}
The ExternalInterface call there shows me just the passed info in a textfield (I'm testing this in a browser). Here is the result of this output:
NetConnection.Connect.Success
NetGroup.Connect.Success
NetStream.Connect.Success
NetStream.Play.Reset
NetStream.Play.Start
But nothing happens on the player. Here are my thoughts:
I'm using the same string as GroupSpecifier and streamName but I'm assuming this is wrong. If so how can I get the stream name of the NetGroup?
I'm enabling everything from the GroupSpecifier as I don't know what are the minimum requirements to receive a stream and pass it then to another clients. Maybe enabling all makes some troubles here.
Maybe somebody can tell me how I can solve this problem to finally see a stream.
I was able to solve the problem: The streamName was indeed wrong and I could manually figure out the correct name. Also instead of using new GroupSpecifier() I must pass the known group-string directly to the NetGroup and NetStream object.

What is the proper way to add a SharedObject to a .AS file?

Having trouble adding a SharedObject to a .AS file, I am trying to add the users name to the .flv file in red 5 like "username_myfile" I am not sure I am adding the code thats saves the name in the correct part of the class. is there a proper way to add a SharedObject to a .AS file?
--
added:
import flash.net.SharedObject;
package app
{
import flash.display.BitmapData;
import flash.display.Stage;
import flash.external.ExternalInterface;
**import flash.net.SharedObject;**
public class DataHolder
{
private static var dataHolder:DataHolder;
public var savedstuff:SharedObject = SharedObject.getLocal("myStuff2","/");
if (savedstuff.size > 0)
{
public var username = savedstuff.data.username;
}
public var filename:String = (String(username) + "_myfile");
public var rtmpPath:String = "rtmp://numbershere.compute-1.amazonaws.com/oflaDemo/";
public var recorder:Recorder;
public var navigation:Navigation;
public var cameraFPS:Number = 30;
public var cameraWidth:Number = 640;
public var cameraHeight:Number = 480;
public var cameraQuality:Number = 88;// camera quality
public var timelimit:Number = 25;// limit count of seconds
public var stage:Stage;
public function DataHolder()
{
if (dataHolder)
{
throw new Error("Only one DataHolder instance should be instantiated");
}
}
public static function getInstance():DataHolder
{
if (dataHolder == null)
{
dataHolder = new DataHolder();
}
return dataHolder;
}
}
}
Remove the conditional declaration of your shared object and declare as usual, ie:
private var savedStuff:SharedObject;
Then, in your constructor:
try {
savedStuff = SharedObject.getLocal("savedData");
} catch (error) {
cookiesActive = false;
};
Then check cookiesActive==true to save data to the SharedObject, eg:
if (cookiesActive) {
savedStuff.data.levelsFinished = 1;
}

Record Sound Using AS3 Worker

Hello I am trying to record the user sound using AS3 Worker everything works well when it comes to communication between the worker and the main scene but I think the record functionnality have and issue here I am using the Record Clash from bytearray.org (http://www.bytearray.org/?p=1858)
and here's is my code am I missing something :
package objects
{
import flash.events.Event;
import flash.system.MessageChannel;
import flash.system.Worker;
import flash.system.WorkerDomain;
import flash.utils.ByteArray;
import starling.display.Button;
import starling.display.Image;
import starling.display.Sprite;
import starling.events.Event;
public class RecordComponent extends Sprite
{
private var audioContainer:Image;
private var recButton:Button;
private var playButton:Button;
private var stopButton:Button;
//background thread for recording
private var worker:Worker;
private var wtm:MessageChannel;
private var mtw:MessageChannel;
private var output:ByteArray;
public function RecordComponent()
{
super();
worker = WorkerDomain.current.createWorker(Workers.RecordWorker);
wtm = worker.createMessageChannel(Worker.current);
mtw = Worker.current.createMessageChannel(worker);
worker.setSharedProperty("wtm",wtm);
worker.setSharedProperty("mtw",mtw);
worker.start();
wtm.addEventListener(flash.events.Event.CHANNEL_MESSAGE,onChannelMessage);
this.addEventListener(starling.events.Event.ADDED_TO_STAGE, onAddedToStage);
}
protected function onChannelMessage(event:flash.events.Event):void
{
output = wtm.receive();
}
protected function onAddedToStage(event:starling.events.Event):void
{
this.removeEventListener(starling.events.Event.ADDED_TO_STAGE, onAddedToStage);
drawScreen();
}
private function drawScreen():void
{
audioContainer = new Image( Assets.getAtlas().getTexture("audioContainer") );
addChild(audioContainer);
//record button
recButton = new Button( Assets.getAtlas().getTexture("recordButton") );
recButton.x=Math.ceil(194/2 - recButton.width/2);
recButton.y=5;
addChild(recButton);
//stop button
stopButton = new Button( Assets.getAtlas().getTexture("stopButton") );
stopButton.x=recButton.x+10;
stopButton.y=10;
stopButton.visible = false;
addChild(stopButton);
//play button
playButton = new Button( Assets.getAtlas().getTexture("playButton") );
playButton.x=Math.ceil(194 - playButton.width)-25;
playButton.y=10;
playButton.visible = false;
addChild(playButton);
this.addEventListener(starling.events.Event.TRIGGERED, buttons_triggeredHandler);
}
private function buttons_triggeredHandler(event:starling.events.Event):void
{
var currentButton:Button = event.target as Button;
switch(currentButton)
{
case recButton:
startRecording();
break;
case stopButton:
stopRecording();
break;
case playButton:
playRecording();
break;
}
}
private function playRecording():void
{
mtw.send("RECORD_PLAY");
}
private function stopRecording():void
{
stopButton.visible = false;
recButton.x = 25;
playButton.visible = true;
recButton.visible = true;
mtw.send("RECORD_STOP");
//
}
private function startRecording():void
{
recButton.visible = false;
stopButton.visible = true;
playButton.visible = false;
mtw.send("RECORD_START");
}
}
}
RecordWorker
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Microphone;
import flash.system.MessageChannel;
import flash.system.Worker;
import org.as3wavsound.WavSound;
import org.bytearray.micrecorder.MicRecorder;
import org.bytearray.micrecorder.encoder.WaveEncoder;
import org.bytearray.micrecorder.events.RecordingEvent;
public class RecordWorker extends Sprite
{
private var wtm:MessageChannel;
private var mtw:MessageChannel;
// volume in the final WAV file will be downsampled to 50%
private var volume:Number = .75;
// we create the WAV encoder to be used by MicRecorder
private var wavEncoder:WaveEncoder = new WaveEncoder( volume );
// we create the MicRecorder object which does the job
private var recorder:MicRecorder = new MicRecorder( wavEncoder,Microphone.getEnhancedMicrophone() );
public function RecordWorker()
{
wtm = Worker.current.getSharedProperty("wtm");
mtw = Worker.current.getSharedProperty("mtw");
mtw.addEventListener(Event.CHANNEL_MESSAGE,command_Handler);
}
//message received from the main component
protected function command_Handler(event:Event):void
{
switch(mtw.receive())
{
case "RECORD_START":
trace("recording.....");
recorder.record();
recorder.addEventListener(RecordingEvent.RECORDING, onRecording);
recorder.addEventListener(flash.events.Event.COMPLETE, onRecordComplete);
break;
case "RECORD_STOP":
trace("record stopped....");
recorder.stop();
break;
case "RECORD_PLAY":
if(recorder.output.length>0)
{
var player:WavSound = new WavSound(recorder.output);
player.play();
}
break;
}
}
private function onRecording(event:RecordingEvent):void
{
trace ( event.time );
}
private function onRecordComplete(event:flash.events.Event):void
{
}
}
}
anny help will be appriciated
Thanks,
Khaled
Looking at this write-up on Adobe's AS3 reference, it seems that because each created worker is "a virtual instance of the Flash runtime", it has no direct connection to any of the core runtime's input or output channels. The Microphone class is one such cut-off interface, which is why it can't reach the system mic inside a worker.

Flex 4.5 Not listening to custom event dispatched in custom AS class

I have the following situation:
I have an event handler, that displays small messages in my application's statusbar.
These messages get passes through by dispatching events from custom components.
A simple message could be like "HTTP Error" or so.
Now, the main event listener, in the main application file, listens to the event dispatched by any custom component, but seems to refuse listening to events dispatched by custom AS classes.
Here is my code for the custom event:
package main.events
{
import flash.events.Event;
public class ShowNoticeEvent extends Event
{
public var message:String;
public static const SHOW_NOTICE:String = "showNotice";
public function ShowNoticeEvent(type:String, msg:String, bubbles:Boolean = false, cancelable:Boolean = false)
{
super(type, bubbles, cancelable);
this.message = msg;
}
override public function clone():Event
{
return new ShowNoticeEvent(type, message);
}
}
}
This is the event listener in the main application file:
addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
And this is the custom AS class that dispatches the custom event. I pasted all the code, so you could see the whole part of it.
package components.dashboard
{
import components.dashboard.models.*;
/* Event imports */
import flash.events.*;
import main.events.*;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
[Event(name="showNotice", type="main.events.ShowNoticeEvent")]
public class Controller extends UIComponent
{
private var baseUrl:String;
public function Controller(baseUrl:String)
{
this.baseUrl = baseUrl;
}
public function getRunningQuotations():void
{
var runningQuotationsList:RunningQuotationsList = RunningQuotationsList.getInstance();
execService("index.php?a=1", runningQuotationsList.updateList, "pnlRunningQuotations");
}
public function getRecentProjects():void
{
var recentProjectsList:RecentProjectsList = RecentProjectsList.getInstance();
execService("index.php?a=2", recentProjectsList.updateList, "pnlRecentProjects");
}
public function getLatestCustomers():void
{
var latestCustomersList:LatestCustomersList = LatestCustomersList.getInstance();
execService("index.php?a=3", latestCustomersList.updateList, "pnlLatestCustomers");
}
private function execService(url:String, listener:Function, component:String):void
{
var basicService:HTTPService = new HTTPService(baseUrl);
basicService.showBusyCursor = true;
basicService.addEventListener(FaultEvent.FAULT, function(e:FaultEvent):void{httpFault(e, component)});
basicService.method = "POST";
basicService.resultFormat = "text";
basicService.url = url;
basicService.addEventListener(ResultEvent.RESULT, listener);
basicService.send();
}
private function httpFault(event:FaultEvent, component:String = null):void {
var faultstring:String = event.fault.faultString;
var eventObj:ShowNoticeEvent = new ShowNoticeEvent(ShowNoticeEvent.SHOW_NOTICE, faultstring, true);
dispatchEvent(eventObj);
trace(faultstring);
}
}
}
So to sum it all up:
- The event listener listens to the custom event dispatched by any custom component.
- The event listener does not listen to the custom event duspatched by an AS class.
Those who wonder, the event really gets dispatched, that's why I added a trace call.
The instance of Controller Class would have to be added to stage for that to work.
by doing
addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
in the main file you are adding the listener to the stage.
So basically you are doing.
stage.addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
If controler instance is not on stage you won't see the event.
You might want to look into a Singleton type pattern for your data management as that would fit this setup pretty good.
Main:
Controller.getLastInstance().addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true)
.
package components.dashboard
{
import components.dashboard.models.*;
/* Event imports */
import flash.events.*;
import main.events.*;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
[Event(name="showNotice", type="main.events.ShowNoticeEvent")]
public class Controller extends UIComponent
{
static public function getLastInstance():Controller { return _instance; }
static private var _instance:Controller;
private var baseUrl:String;
public function Controller(baseUrl:String)
{
_instance = this;
this.baseUrl = baseUrl;
}
public function getRunningQuotations():void
{
var runningQuotationsList:RunningQuotationsList = RunningQuotationsList.getInstance();
execService("index.php?a=1", runningQuotationsList.updateList, "pnlRunningQuotations");
}
public function getRecentProjects():void
{
var recentProjectsList:RecentProjectsList = RecentProjectsList.getInstance();
execService("index.php?a=2", recentProjectsList.updateList, "pnlRecentProjects");
}
public function getLatestCustomers():void
{
var latestCustomersList:LatestCustomersList = LatestCustomersList.getInstance();
execService("index.php?a=3", latestCustomersList.updateList, "pnlLatestCustomers");
}
private function execService(url:String, listener:Function, component:String):void
{
var basicService:HTTPService = new HTTPService(baseUrl);
basicService.showBusyCursor = true;
basicService.addEventListener(FaultEvent.FAULT, function(e:FaultEvent):void{httpFault(e, component)});
basicService.method = "POST";
basicService.resultFormat = "text";
basicService.url = url;
basicService.addEventListener(ResultEvent.RESULT, listener);
basicService.send();
}
private function httpFault(event:FaultEvent, component:String = null):void {
var faultstring:String = event.fault.faultString;
var eventObj:ShowNoticeEvent = new ShowNoticeEvent(ShowNoticeEvent.SHOW_NOTICE, faultstring, true);
dispatchEvent(eventObj);
trace(faultstring);
}
}
}
Not Ideal since you could only ever have 1 of them.
But I think better than having to turn a simple EventDispatcher into DisplayObject and add it to stage just to Simply bubble.

ActionScript - Access List/DataProvider From Custom CellRenderer

the code below sets up a List object in the main controller class that uses a custom cell renderer (CustomListCell class). the CustomListCell class creates a Button object for the cell that will be used to delete itself from the List's DataProvider.
how can i properly access the parent List object from its custom cell renderer?
//Controller Class
private function createList():void
{
provider = new DataProvider(data);
list = new List();
list.width = 200;
list.height = 400;
list.rowHeight = 50;
list.dataProvider = provider;
list.setStyle("cellRenderer", CustomListCell);
}
-----
//CustomListCell Class
import fl.controls.Button;
public class CustomListCell extends Sprite implements ICellRenderer
{
public function CustomListCell()
{
var button:Button = new Button();
button.label = "Delete Cell";
button.addEventListener(MouseEvent_MOUSE_DOWN, deleteCellHandler);
addChild(button);
}
private function deleteCellHandler(evt:MouseEvent):void
{
//Access List/DataProvider Here
}
//required implemented ICellRenderer functions follow
}
UPDATE
the following is my working custom renderer that implements ICellRenderer with Flash v3 List component. the List's dataProvider consists of 2 elements for each cell: randomColor and randomNumber.
package
{
//Imports
import fl.controls.Button;
import fl.controls.List;
import fl.controls.listClasses.ICellRenderer;
import fl.controls.listClasses.ListData;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.geom.ColorTransform;
//Class
public class TouchListRenderer extends Sprite implements ICellRenderer
{
//Properties
private var cellWidthProperty:Number;
private var cellHeightProperty:Number;
private var dataProperty:Object;
private var listDataProperty:ListData;
private var selectedProperty:Boolean;
//Cell Display Objects
private var backgroundCanvas:MySprite = new MySprite();
private var numberTextField:TextField = new TextField();
private var button:Button = new Button();
//Constructor
public function TouchListRenderer()
{
}
//Size Setter (Getter Functions Intentionally Omitted)
public function setSize(width:Number, height:Number):void
{
cellWidthProperty = width;
cellHeightProperty = height;
}
//Data Setter
public function set data(value:Object):void
{
dataProperty = value;
}
//Data Getter
public function get data():Object
{
return dataProperty;
}
//List Data Setter
public function set listData(value:ListData):void
{
listDataProperty = value;
}
//List Data Getter
public function get listData():ListData
{
return listDataProperty;
}
//Selected Setter
public function set selected(value:Boolean):void
{
selectedProperty = value;
layout();
}
//Selected Getter
public function get selected():Boolean
{
return selectedProperty;
}
//Size And Layout
private function layout():void
{
var newColor:ColorTransform = new ColorTransform();
newColor.color = dataProperty.randomColor;
backgroundCanvas.transform.colorTransform = newColor;
backgroundCanvas.scaleX = cellWidthProperty / backgroundCanvas.width;
backgroundCanvas.scaleY = cellHeightProperty / backgroundCanvas.height;
numberTextField.text = dataProperty.randomNumber;
numberTextField.autoSize = TextFieldAutoSize.LEFT;
numberTextField.textColor = 0xFFFFFF;
numberTextField.x = 50;
numberTextField.y = cellHeightProperty / 2 - numberTextField.height / 2;
numberTextField.border = true;
numberTextField.selectable = false;
button.label = "Delete";
button.x = cellWidthProperty - button.width - 50;
button.y = cellHeightProperty / 2 - button.height / 2;
button.drawNow();
button.addEventListener(MouseEvent.MOUSE_DOWN, buttonClickEventHandler);
addChild(backgroundCanvas);
addChild(numberTextField);
addChild(button);
}
//Button Click Event Handler
private function buttonClickEventHandler(evt:MouseEvent):void
{
List(listDataProperty.owner).removeItemAt(listDataProperty.index);
}
//Style Setter
public function setStyle(style:String, value:Object):void
{
}
//Mouse State Setter
public function setMouseState(state:String):void
{
}
}
}
package
{
import flash.display.Sprite;
public class MySprite extends Sprite
{
public function MySprite()
{
graphics.beginFill(0xFF0000);
graphics.drawRect(0, 0, 10, 10);
graphics.endFill();
}
}
}
ugh! the answer was in front of me the whole time! next time remind me to check the docs:
List(listData.owner)
fl.controls.listClasses.ListData.owner
There are multiple ways to do this.
Here is a very hacky solution: Use an icon, and have that icon dispatch a close event.
The idea is you'll place a custom MovieClip in each list cell as icon. That icon will dispatch an event with the index of the cell clicked so you can remove it.
1st step: Make a basic custom event to pass cell index through:
package{
import flash.events.Event;
public class CloseEvent extends Event{
public static const CLOSE:String = 'close';
public var index:int;
public function CloseEvent(type:String,bubbles:Boolean = true,cancelable:Boolean=true){
super(type,bubbles,cancelable);
}
}
}
2nd step:: Draw a close icon or something, convert it to MovieClip and Export for Actionscript
3rd step: Add the event listener to dispatch the custom event when the close icon is clicked.
Inside the close icon Movie Clip I've placed the following actions:
import fl.controls.listClasses.CellRenderer;
//setup click
buttonMode = true;
if(parent) parent.mouseChildren = true;
addEventListener(MouseEvent.MOUSE_DOWN,dispatchClose);
//setup event
var closeEvent:CloseEvent = new CloseEvent(CloseEvent.CLOSE,true);
if(parent) closeEvent.index = CellRenderer(parent).listData.index;
//listen to click and pass on
function dispatchClose(event:MouseEvent):void {
dispatchEvent(closeEvent);
}
Very basic stuff, listen for mouse down, create an event and set the index and dispatch that event on click. The icon is added to a cell renderer, therefor the cell render is it's parent which it has a listData property among others, which holds the index of the cell.
So here's how the test snippet looks:
import fl.data.DataProvider;
var dp:DataProvider = new DataProvider();
for(var i:int = 0 ; i < 30 ; i++) dp.addItem({label:'item'+(i+1),icon:Close});
ls.dataProvider = dp;
addEventListener(CloseEvent.CLOSE,deleteItem);
function deleteItem(event:CloseEvent):void {
ls.removeItemAt(event.index);
}
Since the CloseEvent bubbles, we can catch it from outside the cell renderer's icon and tell the list to remove the item at that index. It's possible to do that within the icon, but it will be necessary to 'climb' up the hierarchy all the way to the list, and it's pretty hacky already.
I did this because, I was probably as lazy as #TheDarkIn1978 :P to implement the ICellRenderer functions. Then I looked at question code again and didn't understand why the custom cell extends a Sprite, when CellRenderer already implements the ICellRenderer functions already.
So here is my attempt to do it in a less hacky manner:
package{
import fl.controls.*;
import fl.controls.listClasses.*;
import fl.data.*;
import flash.events.*;
public class SCListCell extends CellRenderer implements ICellRenderer{
protected var closeButton:Button;
protected var closeEvent:CloseEvent;
override protected function configUI():void {
super.configUI();
closeButton = new Button();
closeButton.label = 'x';
closeButton.buttonMode = true;
closeButton.setSize(30,20);
closeButton.drawNow();
closeButton.addEventListener(MouseEvent.CLICK,close);
addChild(closeButton);
closeEvent = new CloseEvent(CloseEvent.CLOSE);
}
private function close(event:MouseEvent):void{
closeEvent.index = listData.index;
dispatchEvent(closeEvent);
}
override protected function drawLayout():void{
mouseChildren = true;
closeButton.x = width-closeButton.width;
}
}
}
Used the same CloseEvent to pass the index, and the custom cell has direct access to the listData object to fetch the index, so the sample snippet looks like this:
import fl.data.DataProvider;
var dp:DataProvider = new DataProvider();
for(var i:int = 0 ; i < 30 ; i++) dp.addItem({label:'item'+(i+1)});
ls.dataProvider = dp;
addEventListener(CloseEvent.CLOSE,deleteItem);
function deleteItem(event:CloseEvent):void {
ls.removeItemAt(event.index);
}
ls.setStyle('cellRenderer',SCListCell);
So to answer your question:
how can i properly access the parent
List object from its custom cell
renderer?
You can use the listData property of the cell renderer.
You can if you want to, but it means going up a few levels:
package{
import fl.controls.*;
import fl.controls.listClasses.*;
import fl.data.*;
import flash.events.*;
public class SCListCell extends CellRenderer implements ICellRenderer{
protected var closeButton:Button;
override protected function configUI():void {
super.configUI();
closeButton = new Button();
closeButton.label = 'x';
closeButton.buttonMode = true;
closeButton.setSize(30,20);
closeButton.drawNow();
closeButton.addEventListener(MouseEvent.CLICK,close);
addChild(closeButton);
}
private function close(event:MouseEvent):void{
List(this.parent.parent.parent).removeItemAt(listData.index);
}
override protected function drawLayout():void{
mouseChildren = true;
closeButton.x = width-closeButton.width;
}
}
}
Which leaves the list creation part as simple as:
import fl.data.DataProvider;
var dp:DataProvider = new DataProvider();
for(var i:int = 0 ; i < 30 ; i++) dp.addItem({label:'item'+(i+1)});
ls.dataProvider = dp;
ls.setStyle('cellRenderer',SCListCell);
CloseEvent isn't needed in this case.
HTH