Tree menu with ActionScript AS3

##An Example of How to Customize a Tree Component##

I came across some open source code developed by Yahoo! called the Yahoo Atra components. VERY HARD TO CUSTOMIZE! I found the documentation to be particularly inadequate for the degree that I wanted to customize their components. But it is going to be much easier for you because I am am going to provide you with a template class to help you customize to your heart's content, or just simply use what I have provided because my version of the tree component meets all your needs. You may like the Astra Tree as it is but WARNING! The Astra Tree component's horizontal scroll bar does not work. This is particularly problematic if you are trying to do a resizable UI window where you cause the scroll bars to appear when the window is too small for the contents of the window. I spent days coding a custom calculateContentWidth function that would activate the horizontal scroll bar. This way, my code will always accommodate the contents of the tree window. To view a preview of the Tree scroll down:



If this is what you are looking for then proceed with the following directions:

go to http://developer.yahoo.com/flash/astra-flash/ . Here you will see more about the Tree component on which my component is based. It will provide you with ASDoc information for all the Astra Tree APIs which believe me will come in handy.

To get a hold of the components download the http://p.yimg.com/c/ycs/ydn/flash/pkg/astra-flash-1.4.zip file by clicking the large download button on the Astra page or by just clicking this link.

Unzip the entire .zip file

Install an MXP file using Adobe Extension Manager found under the build directory in the .zip file

Once this is done you can use the component panel and drag the tree component onto the stage or into the library of your FLA

If you dragged it on stage it is now in the library so you can delete it from the stage

Download my CustomTree.zip file and unzip the example files. You will find all the custom tree classes under the folder "classes\"

drag the classes into your project and make sure all class paths have been defined for it and you are using the correct packages. Also, remember the Astra classes rely on flashes 'fl' package. Some times the fl package from Adobe needs to be added as a class path for this to work (see this link about the fl package for more info on that)

For reference check out the CustomTreeExample.fla in the zip file to see how I have set up my file.

In your FLA or a class that has access to the stage you can use the following code whenever you want to call the tree up out of the Library:


import TreeCustom;var mytree:TreeCustom = new TreeCustom();
mytree.setSize(500,200);
this.addChild(my tree);
mytree.init();

you will find Component Assets/TreeCellRenderSkins in my FLA's library. I have customized this component in a variety of ways by editing those assets. If they do not work for your purposes,
I encourage you to experiment with customizing them as you see fit. Remember this is just an example of how to customize the Yahoo! Astra Tree component to help get you started.

My implementation of the tree has a few functions that will demonstrate how to add a node to the tree and place more nodes under that node.

Here is an example of the code:

//Astra depends on some component libraries.
//For instructions on how to set up go to the following URL
//http://www.actionscript-flash-guru.com/blog/19-tree-menu-with-actionscript-as3.php
//set up the tree
import TreeCustom;var mytree:TreeCustom = new TreeCustom();
mytree.setSize(500,200);
this.addChild(my tree);
mytree.init();
//multi purpose add a node to the tree function
//param 1 - this is main text of the tree node
//param 2 - this is the class name of the symbol in the library to use as an icon _iconFunctionCustom in CustomTree is what determines the icon for the list element in the Tree if you use nothing it resumes to default operation
//param 3 - an array of lines that you might want to include under the item
//param 4 - text that right justifies its self to the end of the tree item it is a clickable link and will trigger the link in the following:
//textFieldRight.htmlText = "<a href='test.html'> "+_data.data.endText+" </a> ";
//in TreeCellRenderCustom.as
mytree.addCustomItem( "This is example 1 of an item in the list","ThisIsASymbolInTheLibrary",["this is sub node 1","this is sub node 2"],"this is text on the end");
//this creates a closed parent node or branch node that
//encapsulates any subsequent nodes added under this
node.mytree.group("click to open group");
//this adds a leaf node or a node with
leafsmytree.addCustomItem("This is example 2 of an item in the list","",["this is sub node 1","this is sub node 2"]);
//this close up the node "click to open group" and any subsequent
//additions are added at the same level as the parent node "click to open group"
mytree.groupEnd();
mytree.addCustomItem("This is example 3 of an item in the list", "", ["this is sub node 1","this is sub node 2"]);
mytree.addLeaf("new item","");
/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package {
import com.yahoo.astra.fl.controls.Tree;
import com.yahoo.astra.fl.controls.treeClasses.TreeDataProvider;
import com.yahoo.astra.fl.controls.treeClasses.TNode;
import com.yahoo.astra.fl.controls.treeClasses.LeafNode;
import com.yahoo.astra.fl.controls.treeClasses.BranchNode;
import com.yahoo.astra.fl.controls.treeClasses.RootNode;
import fl.controls.ButtonLabelPlacement;
import fl.controls.listClasses.ListData;
import fl.controls.listClasses.ICellRenderer;
//import com.yahoo.astra.fl.controls.treeClasses.LabelButton;
import fl.controls.LabelButton;
import fl.events.ComponentEvent;
import fl.core.UIComponent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.StyleSheet;
import fl.core.InvalidationType;
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.display.MovieClip;
//--------------------------------------
// Styles
//--------------------------------------
/**
* Name of the class to use as the skin for the icon associated
* with a closed branch of the tree.
*
* @default TreeCellRenderer_closedBranchIcon
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="closedBranchIcon", type="Class")]
/**
* Name of the class to use as the skin for the icon associated
* with an open branch of the tree.
*
* @default TreeCellRenderer_openBranchIcon
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="openBranchIcon", type="Class")]
/**
* Name of the class to use as the skin for the icon associated
* with a leaf node of the tree.
*
* @default TreeCellRenderer_leafIcon
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="leafIcon", type="Class")]
/**
* @copy fl.controls.LabelButton#style:upSkin
*
* @default CellRenderer_upSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="upSkin", type="Class")]
/**
* @copy fl.controls.LabelButton#style:downSkin
*
* @default CellRenderer_downSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="downSkin", type="Class")]
/**
* @copy fl.controls.LabelButton#style:overSkin
*
* @default CellRenderer_overSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="overSkin", type="Class")]
/**
* @copy fl.controls.LabelButton#style:disabledSkin
*
* @default CellRenderer_disabledSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="disabledSkin", type="Class")]
/**
* @copy fl.controls.LabelButton#style:selectedDisabledSkin
*
* @default CellRenderer_selectedDisabledSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="selectedDisabledSkin", type="Class")]
/**
* @copy fl.controls.LabelButton#style:selectedUpSkin
*
* @default CellRenderer_selectedUpSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="selectedUpSkin", type="Class")]
/**
* @copy fl.controls.LabelButton#style:selectedDownSkin
*
* @default CellRenderer_selectedDownSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="selectedDownSkin", type="Class")]
/**
* @copy fl.controls.LabelButton#style:selectedOverSkin
*
* @default CellRenderer_selectedOverSkin
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="selectedOverSkin", type="Class")]
/**
* @copy fl.core.UIComponent#style:textFormat
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="textFormat", type="flash.text.TextFormat")]
/**
* @copy fl.core.UIComponent#style:disabledTextFormat
*
* @default null
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="disabledTextFormat", type="flash.text.TextFormat")]
/**
* @copy fl.controls.LabelButton#style:textPadding
*
* @default 5
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="textPadding", type="Number", format="Length")]
/**
* Number of pixels to use as offset when rendering nested tree nodes.
*
* @default 5
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="nodeIndent", type="Number", format="Length")]
/**
* Left margin width in pixels
*
* @default 5
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
[Style(name="leftMargin", type="Number", format="Length")]
//--------------------------------------
// Class description
//--------------------------------------
/**
* The TreeCellRenderer class defines methods and properties for
* Tree component to manipulate and display custom
* cell content in each of its rows. TreeCellRenderer relies on
* properties contained in the TreeDataProvider objects to set
* appropriate icons and offsets for individual cells.
* The TreeCellRenderer implements ICellRenderer and extends
* the LabelButton.
*
* @see com.yahoo.astra.fl.controls.treeClasses.TreeDataProvider
* @see ICellRenderer
* @see fl.controls.LabelButton
*
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public class TreeCellRendererCustom extends LabelButton implements ICellRenderer {
/**
* @private (protected)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
protected var _listData:ListData;
/**
* @private (protected)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
protected var _data:Object;
/**
* @private (protected)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
protected static var _widthOverride:Number = 0;
/**
* @private (protected)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
protected static var _heightOverride:Number = 0;
protected var textFieldRight:TextField;
public static const NODE_INDENT:uint = 20;
/**
* Creates a new TreeCellRenderer instance.
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public function TreeCellRendererCustom():void {
super();
toggle = true;
focusEnabled = false;
this.addEventListener(MouseEvent.CLICK, handleClickEvent, false, 0, true);
}
/*public function get iconGet():DisplayObject{
return icon;
}*/
public function destroy():void{
if (this.hasEventListener(MouseEvent.CLICK)){
this.removeEventListener(MouseEvent.CLICK, handleClickEvent);
} else {
trace("yo it don have da listener");
}
}
private function handleClickEvent(evt:MouseEvent) : void {
var currentNode:TNode = data as TNode;
if (this.icon != null && currentNode is BranchNode &&
this.icon.x <= this.mouseX &&
this.mouseX <= (this.icon.x + this.icon.width) &&
this.icon.y <= this.mouseY &&
this.mouseY <= (this.icon.y + this.icon.height)) {
evt.stopImmediatePropagation();
if (currentNode.isOpen()) {
currentNode.closeNode();
} else {
currentNode.openNode();
}
} else if (this.mouseX >= (width-textFieldRight.width-30) &&
0 <= this.mouseY &&
this.mouseY <= (0 + this.height) &&
this._data.data.endText != null &&
this._data.data.endText.length > 0){
trace("you clicked on "+this._data.data.endText);
evt.stopImmediatePropagation();
}
}
/**
* @private
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
private static var defaultStyles:Object = {upSkin:"TreeCellRenderer_upSkin",downSkin:"TreeCellRenderer_downSkin",overSkin:"TreeCellRenderer_overSkin",
disabledSkin:"TreeCellRenderer_disabledSkin",
selectedDisabledSkin:"TreeCellRenderer_selectedDisabledSkin",
selectedUpSkin:"TreeCellRenderer_selectedUpSkin",selectedDownSkin:"TreeCellRenderer_selectedDownSkin",selectedOverSkin:"TreeCellRenderer_selectedOverSkin",
closedBranchIcon:"TreeCellRenderer_closedBranchIcon",
openBranchIcon:"TreeCellRenderer_openBranchIcon",
leafIcon:"TreeCellRenderer_leafIcon",
textFormat:null,
disabledTextFormat:null,
embedFonts:null,
textPadding:5,
nodeIndent:5,
leftMargin:5};
/**
* @copy fl.core.UIComponent#getStyleDefinition()
*
* @includeExample ../../core/examples/UIComponent.getStyleDefinition.1.as -noswf
*
* @see fl.core.UIComponent#getStyle()
* @see fl.core.UIComponent#setStyle()
* @see fl.managers.StyleManager
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public static function getStyleDefinition():Object {
return mergeStyles(defaultStyles, LabelButton.getStyleDefinition());
}
public static function setOverrideSize(width:Number, height:Number):void {
_widthOverride = width;
_heightOverride = height;
}
/**
* Specifies the dimensions at which the data should be rendered.
* These dimensions affect both the data and the cell that contains it;
* the cell renderer uses them to ensure that the data fits the cell and
* does not bleed into adjacent cells.
*
* @param width The width of the object, in pixels.
*
* @param height The height of the object, in pixels.
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
override public function setSize(width:Number, height:Number):void {
if (_widthOverride != 0){
width = _widthOverride;
}
if (_heightOverride != 0){
height = _heightOverride;
}
super.setSize(width, height);
}
/**
* @copy fl.controls.listClasses.ICellRenderer#listData
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public function get listData():ListData {
return _listData;
}
/**
* @private (setter)
* When listData is set, we determine the appropriate icon to use
* with the particular type of Tree node (open branch, closed branch,
* or leaf).
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public function set listData(value:ListData):void {
_listData = value;
label = _listData.label;
var parentTree:Tree = _listData.owner as Tree;
if (data.nodeType == TreeDataProvider.BRANCH_NODE) {
if (data.nodeState == TreeDataProvider.OPEN_NODE) {
if (parentTree.iconFunction != null) {
setStyle("icon", parentTree.iconFunction(data));
} else if (parentTree.openBranchIconField != null && data[parentTree.openBranchIconField] != null) {
setStyle("icon", data[parentTree.openBranchIconField]);
} else {
setStyle("icon", getStyleValue("openBranchIcon"));
}
} else {
if (parentTree.iconFunction != null) {
setStyle("icon", parentTree.iconFunction(data));
} else if (parentTree.closedBranchIconField != null && data[parentTree.closedBranchIconField] != null) {
setStyle("icon", data[parentTree.closedBranchIconField]);
} else {
setStyle("icon", getStyleValue("closedBranchIcon"));
}
}
}
else {
if (parentTree.iconFunction != null) {
setStyle("icon", parentTree.iconFunction(data));
} else if (parentTree.openBranchIconField != null && data[parentTree.leafIconField] != null) {
setStyle("icon", data[parentTree.leafIconField]);
} else {
setStyle("icon", getStyleValue("leafIcon"));
}
}
}
override protected function drawIcon():void {
var oldIcon:DisplayObject = icon;
var styleName:String = (enabled) ? mouseState : "disabled";
if (selected) {
styleName = "selected"+styleName.substr(0,1).toUpperCase()+styleName.substr(1);
}
styleName += "Icon";
var iconStyle:Object = getStyleValue(styleName);
if (iconStyle == null) {
// try the default icon:
iconStyle = getStyleValue("icon");
}
if (iconStyle != null) {
icon = getDisplayObjectInstance(iconStyle);
}
if (icon != null) {
addChildAt(icon,1);
}
if (oldIcon != null && oldIcon != icon) {
removeChild(oldIcon);
}
}
override protected function draw():void {
if (textField.htmlText != _label) {
label = _label;
}
if (isInvalid(InvalidationType.STYLES,InvalidationType.STATE)) {
drawBackground();
drawIcon();
drawTextFormat();
invalidate(InvalidationType.SIZE,false);
}
if (isInvalid(InvalidationType.SIZE)) {
drawLayout();
}
if (isInvalid(InvalidationType.SIZE,InvalidationType.STYLES)) {
if (isFocused && focusManager.showFocusIndicator) { drawFocus(true); }
}
validate(); // because we're not calling super.draw
}
override protected function drawBackground():void {
var styleName:String = (enabled) ? mouseState : "disabled";
var bgParentMc:TreeCellRendererCustom;
if (selected) { styleName = "selected"+styleName.substr(0,1).toUpperCase()+styleName.substr(1); }
styleName += "Skin";
var bg:DisplayObject = background;
background = getDisplayObjectInstance(getStyleValue(styleName));
addChildAt(background, 0);
if (bg != null && bg != background) {
bgParentMc = bg.parent as TreeCellRendererCustom;
bgParentMc.graphics.clear();
removeChild(bg);
}
}
override public function set label(value:String):void {
_label = value;
if (textField.htmlText != _label) {
textField.htmlText = _label;
dispatchEvent(new ComponentEvent(ComponentEvent.LABEL_CHANGE));
}
invalidate(InvalidationType.SIZE);
invalidate(InvalidationType.STYLES);
}
override protected function configUI():void {
super.configUI();
//WHERE YOU LEFT OFF: add a text field to the far right
textFieldRight = new TextField();
textFieldRight.autoSize = TextFieldAutoSize.RIGHT;
textFieldRight.type = TextFieldType.DYNAMIC;
textFieldRight.selectable = true;
var style:StyleSheet = new StyleSheet();
var hover:Object = new Object();
//hover.fontWeight = "bold";
hover.color = "#0000FF";
var link:Object = new Object();
link.textDecoration= "underline";
link.color = "#FF0000";
var active:Object = new Object();
//active.fontWeight = "bold";
active.color = "#FF0000";
var visited:Object = new Object();
visited.color = "#cc0099";
visited.textDecoration= "underline";
style.setStyle("a:link", link);
style.setStyle("a:hover", hover);
style.setStyle("a:active", active);
style.setStyle(".visited", visited);
textFieldRight.styleSheet = style;
addChild(textFieldRight);
//
textField.styleSheet = new StyleSheet();
textField.autoSize = TextFieldAutoSize.LEFT;
}
override protected function drawTextFormat():void {
// Apply a default textformat
/*
var uiStyles:Object = UIComponent.getStyleDefinition();
var defaultTF:TextFormat = enabled ? uiStyles.defaultTextFormat as TextFormat : uiStyles.defaultDisabledTextFormat as TextFormat;
//textField.setTextFormat(defaultTF);
var tf:TextFormat = getStyleValue(enabled?"textFormat":"disabledTextFormat") as TextFormat;
if (tf != null) {
//textField.setTextFormat(tf);
} else {
tf = defaultTF;
}
//textField.defaultTextFormat = tf;
setEmbedFont();
*/
}
/**
* @copy fl.controls.listClasses.ICellRenderer#data
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public function get data():Object {
return _data;
}
/**
* @private (setter)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public function set data(value:Object):void {
_data = value;
}
/**
* @copy fl.controls.listClasses.ICellRenderer#selected
*
* @default false
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
override public function get selected():Boolean {
return super.selected;
}
/**
* @private (setter)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
override public function set selected(value:Boolean):void {
super.selected = value;
}
/**
* @private (protected)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
override protected function toggleSelected(event:MouseEvent):void {
// don't set selected or dispatch change event.
}
/**
* @private (protected)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
override protected function drawLayout():void {
var textPadding:Number = Number(getStyleValue("textPadding"));
var nodeIndent:Number = Number(getStyleValue("nodeIndent"));
var leftMargin:Number = Number(getStyleValue("leftMargin"));
var indentation:int = (_data.nodeLevel*NODE_INDENT);
var textFieldX:Number = 0;
var renderBoxesLimit:int = 0;
var bgMc:MovieClip;
var bgParentMc:TreeCellRendererCustom;
// Align icon and add the indent derived from node's level
if (icon != null) {
if (_data.data.type == "MoreInfo" || _data.data.type == "MoreDoneInfo"){
icon.x = (leftMargin + data.nodeLevel * nodeIndent)-NODE_INDENT;
icon.visible = false;
} else {
icon.x = leftMargin + data.nodeLevel * nodeIndent;
}
icon.y = Math.round( (height-icon.height)>>1 );
textFieldX = icon.x + icon.width + textPadding;
}
// Align text and add the indent derived from node's level
if (label.length > 0) {
textField.visible = true;
var textWidth:Number = Math.max(0, width - textFieldX - textPadding*2);
textField.width = textWidth;
textField.height = textField.textHeight + 4;
textField.x = textFieldX;
textField.y = Math.round((height-textField.height)>>1);
} else {
textField.visible = false;
}
//set background state
bgMc = background as MovieClip;
bgParentMc = bgMc.parent as TreeCellRendererCustom;
if (_data.data.type == "Group" && _data.nodeType == TreeDataProvider.BRANCH_NODE){
bgMc.gotoAndStop("branch");
}
if (_data.data.type == "Done"){
textField.x = indentation-NODE_INDENT+5;
background.width = width-indentation+NODE_INDENT;
background.x = indentation-NODE_INDENT;
bgMc.visible = false;
bgParentMc.graphics.lineStyle(1, 0xD7D7D7);
bgParentMc.graphics.moveTo(background.x, 0); ///This is where we start drawing
bgParentMc.graphics.lineTo(background.x, height);
bgParentMc.graphics.lineTo(width, height);
renderBoxesLimit = 1;
} else if (_data.data.type == "DoneInfo"){
bgParentMc.graphics.lineStyle(1, 0xD7D7D7);
bgParentMc.graphics.moveTo(indentation-NODE_INDENT, 0); ///This is where we start drawing
bgParentMc.graphics.lineTo(indentation-NODE_INDENT, height);
bgParentMc.graphics.lineTo(width, height);
textField.selectable = true;
if (_data.nodeType == TreeDataProvider.LEAF_NODE){
background.visible = false;
if (_data.nodeLevel == 1){
renderBoxesLimit = 1;
}
}
} else if (_data.data.type == "MoreDoneInfo"){
bgParentMc.graphics.lineStyle(1, 0xD7D7D7);
bgParentMc.graphics.moveTo(indentation, 0); ///This is where we start drawing
bgParentMc.graphics.lineTo(indentation, height);
bgParentMc.graphics.lineTo(width, height);
textField.selectable = true;
if (_data.nodeType == TreeDataProvider.LEAF_NODE){
background.visible = false;
if (_data.nodeLevel == 1){
renderBoxesLimit = 1;
}
}
} else if (_data.data.type == "MoreInfo"){
bgParentMc.graphics.lineStyle(1, 0xD7D7D7);
bgParentMc.graphics.moveTo(indentation, 0); ///This is where we start drawing
bgParentMc.graphics.lineTo(indentation, height);
textField.selectable = true;
//bgParentMc.graphics.lineTo(width, height);
if (_data.nodeType == TreeDataProvider.LEAF_NODE){
background.visible = false;
if (_data.nodeLevel == 1){
renderBoxesLimit = 1;
}
}
} else if (_data.data.type == "SquareNoClick"){
bgParentMc.graphics.lineStyle(1, 0xD7D7D7);
bgParentMc.graphics.moveTo(indentation, 0); ///This is where we start drawing
bgParentMc.graphics.lineTo(indentation, height);
bgParentMc.graphics.lineTo(width, height);
bgParentMc.graphics.lineTo(width, 0);
bgParentMc.graphics.lineTo(indentation, 0);
textField.selectable = true;
//bgParentMc.graphics.lineTo(width, height);
if (_data.nodeType == TreeDataProvider.LEAF_NODE){
background.visible = false;
if (_data.nodeLevel == 1){
renderBoxesLimit = 1;
}
}
} else {
// Size background
//background.width = textField.width+textFieldX+leftMargin+( data.nodeLevel * nodeIndent);
background.width = width-indentation;
background.x = indentation;
if (_data.nodeType == TreeDataProvider.LEAF_NODE){
background.visible = false;
if (_data.nodeLevel == 1){
renderBoxesLimit = 1;
}
}
}
background.height = height;
//draw box lines
if (_data.nodeLevel > 0 && _data.data.type != "Empty"){
bgParentMc.graphics.lineStyle(1, 0xD7D7D7);
//TEST: uncomment when you want to see what the lines that make the boxes look like
//bgParentMc.graphics.lineStyle(1, 0x000000);
for (var j:int = 0; j < _data.nodeLevel-renderBoxesLimit; j++){
if (_data.label == "line0" && _data.nodeLevel == 1){
var yourmom:int = 30;
}
bgParentMc.graphics.moveTo(j*NODE_INDENT,0); ///This is where we start drawing
bgParentMc.graphics.lineTo(j*NODE_INDENT, height);
}
}
if (_data.data.endText != null && _data.data.endText.length > 0){
textFieldRight.visible = true;
textFieldRight.htmlText = "<a href='test.html'>"+_data.data.endText+"</a>";
textFieldRight.x = width-textFieldRight.width-20;
} else {
textFieldRight.visible = false;
}
}
}
}
package {
//astra yahoo libs
import TreeCellRendererCustom;
import com.yahoo.astra.fl.controls.treeClasses.TreeDataProvider;
import com.yahoo.astra.fl.controls.Tree;
import com.yahoo.astra.fl.controls.treeClasses.TNode;
import com.yahoo.astra.fl.controls.treeClasses.LeafNode;
import com.yahoo.astra.fl.controls.treeClasses.BranchNode;
import com.yahoo.astra.fl.controls.treeClasses.RootNode;
//adobe libs
import fl.core.InvalidationType;
import fl.controls.ScrollPolicy;
import fl.data.DataProvider;
import fl.controls.listClasses.CellRenderer;
import fl.controls.listClasses.ICellRenderer;
import fl.controls.listClasses.ListData;
import fl.controls.ScrollPolicy;
import fl.controls.SelectableList;
import fl.core.InvalidationType;
import fl.core.UIComponent;
import fl.managers.IFocusManagerComponent;
import fl.events.DataChangeType;
import fl.events.DataChangeEvent;
import fl.events.ListEvent;
import fl.events.ScrollEvent;
//native libs
import flash.xml.XMLNode;
import flash.xml.XMLNodeType;
import flash.text.TextFormat;
import flash.text.TextField;
import flash.text.TextLineMetrics;
import flash.text.TextFieldAutoSize;
import flash.text.StyleSheet;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.ui.Keyboard;
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
import flash.geom.Rectangle;
import flash.external.ExternalInterface;
public class TreeCustom extends Tree {
private var _isScrollSizeChanged:Boolean;
private var _scrollMaxWidthSize:Number;
private var _currentNumberOfGroups:uint;
private var _lastGroupNodeList:Array = new Array();
private var _placeHolderNode:LeafNode;
private var _placeHolderNode1:LeafNode;
private var _cellRendererList:Array = new Array();
public var isScrollToBottom:Boolean = true;
private static const NO_DATA:String = "no data";
public function TreeCustom() {
//starting structure for the data provider
var dp:XML = <node label="Root node"></node>;
super();
//uncomment this if you want to use calculate content width
this.useFixedHorizontalScrolling = false;
//use custom cell renderer
this.setStyle('cellRenderer', TreeCellRendererCustom);
this.setRendererStyle("nodeIndent", TreeCellRendererCustom.NODE_INDENT);
//this.setRendererStyle("nodeIndent", 20);
this.setRendererStyle("textPadding", 5);
this.enabled = true;
this.horizontalScrollPolicy = ScrollPolicy.AUTO;
this.horizontalLineScrollSize = 5;
this.allowMultipleSelection = true;
this.iconFunction = _iconFunctionCustom;
this.labelFunction = _labelFunctionCustom;
this.maxHorizontalScrollPosition = 0;
_scrollMaxWidthSize = 0;
this.dataProvider = new TreeDataProvider(dp);
}
public function init():void{
var parentDp:TreeDataProvider = this.dataProvider as TreeDataProvider;
var rn:RootNode;
rn = parentDp.rootNode as RootNode;
_placeHolderNode1 = new LeafNode(parentDp);
_placeHolderNode1.label = "-";
_placeHolderNode1.data = {type:"Empty"};
rn.addChildNodeAt(_placeHolderNode1, rn.children.length);
_placeHolderNode = new LeafNode(parentDp);
_placeHolderNode.label = "";
_placeHolderNode.data = {type:"Empty"};
rn.addChildNodeAt(_placeHolderNode,rn.children.length);
}
public function addCustomItem(item:String, type:String, lines:Array = null, endTextStr:String = "", endLink:Object = null):void{
var parentDp:TreeDataProvider = this.dataProvider as TreeDataProvider;
var parentGroupNode:BranchNode;
var bn:BranchNode = new BranchNode(parentDp);
var rn:RootNode;
var ln:LeafNode;
var i:uint;
var j:uint;
if (lines == null){
lines = [];
}
var linesLength:uint;
var linesLength2:uint;
var linesArray:Array = limitStringLength(item);
//addChildNode
bn.label = linesArray.shift() as String;
if (endTextStr == NO_DATA+"."+NO_DATA){
bn.data = {type:type};
} else {
bn.data = {type:type, endText:endTextStr};
}
if (_currentNumberOfGroups == 0){
rn = parentDp.rootNode as RootNode;
rn.addChildNodeAt(bn, rn.children.length-2);
//rn.addChildNode(bn);
} else {
rn = parentDp.rootNode as RootNode;
parentGroupNode = _lastGroupNodeList[_lastGroupNodeList.length-1] as BranchNode;
//parentGroupNode.addChildNodeAt(bn, rn.children.length-2);
parentGroupNode.addChildNode(bn);
}
//bn.openNode();
linesLength = linesArray.length;
for (i = 0; i < linesLength; i++){
ln = new LeafNode(parentDp);
ln.label = linesArray[i];
if (i == linesLength-1){
ln.data = {type:"MoreDoneInfo"};
} else {
ln.data = {type:"MoreInfo"};
}
bn.addChildNode(ln);
}
linesLength = lines.length;
for (i = 0; i < linesLength; i++){
ln = new LeafNode(parentDp);
linesArray = limitStringLength(lines[i]);
ln.label = linesArray.shift();
linesLength2 = linesArray.length;
ln.data = {type:"SquareNoClick"};
bn.addChildNode(ln);
for (j = 0; j<linesLength2; j++){
ln = new LeafNode(parentDp);
ln.label = " "+linesArray[j];
if (j == linesLength2-1){
ln.data = {type:"MoreDoneInfo"};
} else {
ln.data = {type:"MoreInfo"};
}
bn.addChildNode(ln);
}
}
if (dataProvider != null && isScrollToBottom){
scrollToIndex(dataProvider.length);
}
}
override public function removeAll():void{
super.removeAll();
this.dataProvider.removeAll();
//starting structure for the data provider
var dp:XML = <node label="Root node"></node>;
this.dataProvider = new TreeDataProvider(dp);
var parentDp:TreeDataProvider = this.dataProvider as TreeDataProvider;
var rn:RootNode;
rn = parentDp.rootNode as RootNode;
_placeHolderNode1 = new LeafNode(parentDp);
_placeHolderNode1.label = "-";
_placeHolderNode1.data = {type:"Empty"};
rn.addChildNodeAt(_placeHolderNode1, rn.children.length);
_placeHolderNode = new LeafNode(parentDp);
_placeHolderNode.label = "";
_placeHolderNode.data = {type:"Empty"};
rn.addChildNodeAt(_placeHolderNode,rn.children.length);
}
public function group(groupTitle:String):void{
var parentDp:TreeDataProvider = this.dataProvider as TreeDataProvider;
var bn:BranchNode = new BranchNode(parentDp);
var parentGroupNode:BranchNode;
var rn:RootNode;
bn.label = groupTitle;
bn.data = {type:"Group"};
if (_currentNumberOfGroups == 0){
rn = parentDp.rootNode as RootNode;
rn.addChildNodeAt(bn, rn.children.length-2);
//rn.addChildNode(bn);
} else {
parentGroupNode = _lastGroupNodeList[_lastGroupNodeList.length-1] as BranchNode;
parentGroupNode.addChildNode(bn);
}
_currentNumberOfGroups++;
_lastGroupNodeList.push(bn);
}
public function groupEnd():void{
var parentDp:TreeDataProvider = this.dataProvider as TreeDataProvider;
var bn:BranchNode;
var ln:LeafNode;
if (_currentNumberOfGroups != 0){
_currentNumberOfGroups--;
bn = _lastGroupNodeList.pop() as BranchNode;
ln = new LeafNode(parentDp);
ln.label = "done";
ln.data = {type:"Done"};
bn.addChildNode(ln);
}
}
function limitStringLength(s:String):Array{
var textString:String = s;
var dummyTxt:TextField = new TextField();
var linesOnEachArray:Array = new Array();
dummyTxt.styleSheet = new StyleSheet();
dummyTxt.autoSize = TextFieldAutoSize.NONE;
dummyTxt.width = 800;
dummyTxt.multiline = true;
dummyTxt.wordWrap = true;
dummyTxt.htmlText = textString;
for (var i:uint; i < dummyTxt.numLines; i++){
linesOnEachArray.push( dummyTxt.getLineText(i) );
}
return linesOnEachArray;
}
public function addLeaf(item:String, type:String):void{
var parentDp:TreeDataProvider = this.dataProvider as TreeDataProvider;
var parentGroupNode:BranchNode;
var rn:RootNode;
var ln:LeafNode = new LeafNode(parentDp);
var i:uint = 0;
ln.label = item;
ln.data = {type:""+type};
if (_currentNumberOfGroups == 0){
rn = parentDp.rootNode as RootNode;
rn.addChildNodeAt(ln, rn.children.length-2);
} else {
rn = parentDp.rootNode as RootNode;
parentGroupNode = _lastGroupNodeList[_lastGroupNodeList.length-1] as BranchNode;
parentGroupNode.addChildNode(ln);
}
}
private function _iconFunctionCustom(data:Object):String {
if (data.data.type == "ThisIsASymbolInTheLibrary") {
return ("ThisIsASymbolInTheLibrary");
} if (data.data.type == "Empty") {
return "";
} else if (data.data.type == "Done") {
return "";
}else if (data.nodeType == TreeDataProvider.LEAF_NODE) {
return ("TreeCellRenderer_leafIcon");
} else if (data.nodeType == TreeDataProvider.BRANCH_NODE) {
if (data.nodeState == TreeDataProvider.OPEN_NODE) {
return "TreeCellRenderer_openBranchIcon";
}
else {
return "TreeCellRenderer_closedBranchIcon";
}
}
return "";
}
private function _labelFunctionCustom(data:Object):String{
var textString:String = data.label;
var maxHorizontalPixles:Number;
var textMetrics:TextLineMetrics;
var dummyTxt:TextField = new TextField();
dummyTxt.styleSheet = new StyleSheet();
dummyTxt.htmlText = "";
dummyTxt.htmlText = textString;
textMetrics = dummyTxt.getLineMetrics(0);
maxHorizontalPixles = textMetrics.width+(TreeCellRendererCustom.NODE_INDENT*data.nodeLevel)+60;
if (data.data.endText != null){
dummyTxt.htmlText = "";
dummyTxt.htmlText = data.data.endText;
textMetrics = dummyTxt.getLineMetrics(0);
maxHorizontalPixles += textMetrics.width;
}
if (_scrollMaxWidthSize < maxHorizontalPixles){
_isScrollSizeChanged = true;
_scrollMaxWidthSize = maxHorizontalPixles;
}
return textString;
}
override protected function validate():void {
var i:int = 40;
super.validate();
if (_isScrollSizeChanged){
drawLayout();
//drawList();
//updateChildren();
}
_isScrollSizeChanged = false;
}
override protected function calculateContentWidth():void {
var textString:String;
var maxHorizontalPixles:Number;
var textMetrics:TextLineMetrics;
var dummyTxt:TextField;
var item:Object;
if (_isScrollSizeChanged){
contentWidth = _scrollMaxWidthSize;
if (width < contentWidth){
TreeCellRendererCustom.setOverrideSize(contentWidth, 0);
super.setSize(width, height);
} else {
TreeCellRendererCustom.setOverrideSize(width, 0);
super.setSize(width, height);
}
//_scrollMaxWidthSize = 0;
} else {
// figure out what we have to check:
var startIndex:uint = Math.floor(this._verticalScrollPosition/this.rowHeight);
var endIndex:uint = Math.min(this.length,startIndex + this.rowCount+1);
for (var i:uint = startIndex; i<endIndex; i++) {
item = _dataProvider.getItemAt(i);
textString = item.label;
dummyTxt = new TextField();
dummyTxt.styleSheet = new StyleSheet();
dummyTxt.htmlText = "";
dummyTxt.htmlText = textString;
textMetrics = dummyTxt.getLineMetrics(0);
maxHorizontalPixles = textMetrics.width+60;
if (item.data.endText != null){
dummyTxt.htmlText = "";
dummyTxt.htmlText = item.data.endText;
textMetrics = dummyTxt.getLineMetrics(0);
maxHorizontalPixles += textMetrics.width;
}
if (contentWidth < maxHorizontalPixles){
//_isScrollSizeChanged = true;
contentWidth = maxHorizontalPixles;
if (width < contentWidth){
TreeCellRendererCustom.setOverrideSize(contentWidth, 0);
super.setSize(width, height);
} else {
TreeCellRendererCustom.setOverrideSize(width, 0);
super.setSize(width, height);
}
trace("contentWidth : "+contentWidth);
}
}
}
}
override protected function drawList():void {
// List is very environmentally friendly, it reuses existing
// renderers for old data, and recycles old renderers for new data.
// set horizontal scroll:
listHolder.x = listHolder.y = contentPadding;
//var rect:Rectangle = listHolder.scrollRect;
//rect.x = _horizontalScrollPosition;
// set pixel scroll:
//rect.y = Math.floor(_verticalScrollPosition)%rowHeight;
//listHolder.scrollRect = rect;
listHolder.cacheAsBitmap = useBitmapScrolling;
// figure out what we have to render:
var startIndex:uint = Math.floor(_verticalScrollPosition/rowHeight);
var endIndex:uint = Math.min(length,startIndex + rowCount+1);
// these vars get reused in different loops:
var i:uint;
var item:Object;
var renderer:ICellRenderer;
// create a dictionary for looking up the new "displayed" items:
var itemHash:Dictionary = renderedItems = new Dictionary(true);
for (i=startIndex; i<endIndex; i++) {
itemHash[_dataProvider.getItemAt(i)] = true;
}
// find cell renderers that are still active, and make those that aren't active available:
var itemToRendererHash:Dictionary = new Dictionary(true);
while (activeCellRenderers.length > 0) {
renderer = activeCellRenderers.pop() as ICellRenderer;
item = renderer.data;
if (itemHash[item] == null || invalidItems[item] == true) {
availableCellRenderers.push(renderer);
} else {
itemToRendererHash[item] = renderer;
// prevent problems with duplicate objects:
invalidItems[item] = true;
}
list.removeChild(renderer as DisplayObject);
}
invalidItems = new Dictionary(true);
// draw cell renderers:
for (i=startIndex; i<endIndex; i++) {
var reused:Boolean = false;
item = _dataProvider.getItemAt(i);
if (itemToRendererHash[item] != null) {
// existing renderer for this item we can reuse:
reused = true;
renderer = itemToRendererHash[item];
delete(itemToRendererHash[item]);
} else if (availableCellRenderers.length > 0) {
// recycle an old renderer:
renderer = availableCellRenderers.pop() as ICellRenderer;
} else {
// out of renderers, create a new one:
renderer = getDisplayObjectInstance(getStyleValue("cellRenderer")) as ICellRenderer;
var rendererSprite:Sprite = renderer as Sprite;
if (rendererSprite != null) {
rendererSprite.addEventListener(MouseEvent.CLICK,handleCellRendererClick,false,0,true);
_cellRendererList.push({obj:rendererSprite, eventStr: MouseEvent.CLICK, func:handleCellRendererClick});
rendererSprite.addEventListener(MouseEvent.ROLL_OVER,handleCellRendererMouseEvent,false,0,true);
_cellRendererList.push({obj:rendererSprite, eventStr: MouseEvent.ROLL_OVER, func:handleCellRendererMouseEvent});
rendererSprite.addEventListener(MouseEvent.ROLL_OUT,handleCellRendererMouseEvent,false,0,true);
_cellRendererList.push({obj:rendererSprite, eventStr: MouseEvent.ROLL_OUT, func:handleCellRendererMouseEvent});
rendererSprite.addEventListener(Event.CHANGE,handleCellRendererChange,false,0,true);
_cellRendererList.push({obj:rendererSprite, eventStr: Event.CHANGE, func:handleCellRendererChange});
rendererSprite.doubleClickEnabled = true;
rendererSprite.addEventListener(MouseEvent.DOUBLE_CLICK, handleCellRendererDoubleClick, false, 0, true);
_cellRendererList.push({obj:rendererSprite, eventStr: MouseEvent.DOUBLE_CLICK, func:handleCellRendererDoubleClick});
//rendererSprite = renderer as Sprite;
if (rendererSprite != null && rendererSprite["setStyle"] != null) {
for (var n:String in rendererStyles) {
rendererSprite["setStyle"](n, rendererStyles[n])
}
}
}
}
list.addChild(renderer as Sprite);
activeCellRenderers.push(renderer);
renderer.y = rowHeight*(i-startIndex);
//renderer.setSize(availableWidth+_maxHorizontalScrollPosition,rowHeight);
if (width < contentWidth){
renderer.setSize(contentWidth, rowHeight);
} else {
renderer.setSize(width, rowHeight);
}
var label:String = itemToLabel(item);
var icon:Object = null;
if (_iconFunction != null) {
icon = _iconFunction(item);
} else if (_iconField != null) {
icon = item[_iconField];
}
if (!reused) {
renderer.data = item;
}
renderer.listData = new ListData(label,icon,this,i,i,0);
renderer.selected = (_selectedIndices.indexOf(i) != -1);
// force an immediate draw (because render event will not be called on the renderer):
if (renderer is UIComponent) {
(renderer as UIComponent).drawNow();
}
}
}
/**
* Sets the component to the specified width and height.
*
* @param width The width of the component, in pixels.
*
* @param height The height of the component, in pixels.
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
override public function setSize(width:Number, height:Number):void {
super.setSize(width, height);
if (width < contentWidth){
TreeCellRendererCustom.setOverrideSize(contentWidth, 0);
super.setSize(width, height);
} else {
TreeCellRendererCustom.setOverrideSize(width, 0);
super.setSize(width, height);
}
}
}
}
view raw TreeCustom.as hosted with ❤ by GitHub