How can I hide the TabNavigator gap in Flex 4.6? - actionscript-3

I tried extending TabNavigator and overriding contentY, but it references private tabBarHeight, so to get around that I simply copied the getter contents into my override and subtracted the unwanted 12 pixels, but that made absolutely zero difference.
Anyone have any ideas (Aside from switching to a viewstack!)?
public class Tabber extends TabNavigator
{
public function Tabber(){
super();
}
override protected function get contentY():Number {
var paddingTop:Number = getStyle("paddingTop");
if (isNaN(paddingTop))
paddingTop = 0;
var tabHeight:Number = getStyle("tabHeight");
if (isNaN(tabHeight))
tabHeight = tabBar.getExplicitOrMeasuredHeight();
var tabBarHeight:Number = tabHeight - borderMetrics.top;
return tabBarHeight + paddingTop - 12;
}
}

Have you tried setting tabHeight (with setStyle method)?
Because TabNavigator uses it:
private function get tabBarHeight():Number
{
var tabHeight:Number = getStyle("tabHeight");
if (isNaN(tabHeight))
tabHeight = tabBar.getExplicitOrMeasuredHeight();
return tabHeight - borderMetrics.top;
}
Or maybe changing the verticalGap style...

Related

A-star implementation, not finding shortest path issue

I have code that is supposed to find the shortest path from point A to point B. To do this i am using a A-star variation. I am using a 2d array to represent a 2d grid but my path does not take diagonal shortcuts, only left, right, up, and down. So far everything works fine except it does not always find the shortest path possible. I want to know what is going wrong, why it is going wrong, and how I can fix it. Thank you in advance.
Here is a picture to illustrate what exactly is happening:
and here is my code (path finding class first, then its helper class):
BTW: Math vector is nothing more than just a geometric point class, and both playerTileLocation and enemyTileLocation are just points that correspond to the start and end nodes on the grid. Also i use the class AStarNode as the nodes for all the tiles on the map, instead of a regular object.
package {
import src.Characters.Character;
import src.InGame.Map;
import src.Maths.MathVector;
public final class BaseAI {
// REPRESENTS UP, DOWN, RIGHT, AND LEFT OF ANY ONE NODE
private static const bordersOfNode:Array = new Array(
new MathVector( -1, 0), new MathVector(1, 0), new MathVector(0, -1), new MathVector(0, 1));
private var _player:Character;
private var map:Map;
private var playerTileLocation:MathVector;
private var openList:Array;
private var closedList:Array;
// 2D ARRAY OF MAP TILES (I DON'T USE HERE, BUT I PLAN TO IN FUTURE)
private var mapArray:Array;
private var originNode:AStarNode;
private var complete:Boolean;
public function BaseAI(_player:Character,map:Map):void {
this._player = _player;
this.map = map;
openList = new Array();
closedList = new Array();
mapArray = map.tiles;
}
public function get player():Character {
return this._player;
}
public function calculatePlayerTileLocation():void {
playerTileLocation = map.worldToTilePoint(player.groundPosition);
}
//WILL EVENTUAL RETURN A DIRECTION FOR THE ENEMY TO TAKE THAT ITERATION (EVERY 1-2 SECONDS)
public function getDirection(enemy:Character):String {
var enemyTileLocation:MathVector = map.worldToTilePoint(enemy.groundPosition);
originNode = new AStarNode(enemyTileLocation, playerTileLocation);
originNode.setAsOrigin();
openList = [originNode];
closedList = [];
complete = false;
var currentNode:AStarNode;
var examiningNode:AStarNode;
while (!complete) {
openList.sortOn("F", Array.NUMERIC);
currentNode = openList[0];
closedList.push(currentNode);
openList.splice(0, 1);
for (var i in bordersOfNode) {
examiningNode = new AStarNode(new MathVector(currentNode.X + bordersOfNode[i].x, currentNode.Y + bordersOfNode[i].y),playerTileLocation);
if (map.isOpenTile(map.getTile(examiningNode.X, examiningNode.Y)) && !examiningNode.isThisInArray(closedList)) {
if (!examiningNode.isThisInArray(openList)) {
openList.push(examiningNode);
examiningNode.parentNode = currentNode;
}else {
}
if (examiningNode.X == playerTileLocation.x && examiningNode.Y == playerTileLocation.y) {
complete = true;
var done:Boolean = false;
var thisNode:AStarNode;
thisNode = examiningNode;
while (!done) {
if (thisNode.checkIfOrigin()) {
done = true;
}else {
thisNode = thisNode.parentNode;
}
}
}
}
}
}
}
}
}
package {
import src.Maths.MathVector;
internal final class AStarNode {
private var _X:int;
private var _Y:int;
private var _G:int;
private var _H:int;
private var _F:int;
private var _parentNode:AStarNode;
private var _isOrigin:Boolean;
public static const VERTICAL:uint = 10;
public function AStarNode(thisNodeLocation:MathVector, targetNodeLocation:MathVector) {
X = thisNodeLocation.x;
Y = thisNodeLocation.y;
H = Math.abs(X - targetNodeLocation.x) + Math.abs(Y - targetNodeLocation.y);
G = 0;
F = H + G;
}
public function set X(newX:int):void {
this._X = newX;
}
public function get X():int {
return this._X;
}
public function set Y(newY:int):void {
this._Y = newY;
}
public function get Y():int {
return this._Y;
}
public function set G(newG:int):void {
this._G = newG;
}
public function get G():int {
return this._G;
}
public function set H(newH:int):void {
this._H = newH;
}
public function get H():int {
return this._H;
}
public function set F(newF:int):void {
this._F = newF;
}
public function get F():int {
return this._F;
}
public function set parentNode(newParentNode:AStarNode):void {
this._parentNode = newParentNode;
}
public function get parentNode():AStarNode {
return this._parentNode;
}
public function setAsOrigin():void {
_isOrigin = true;
}
public function checkIfOrigin():Boolean {
return _isOrigin;
}
public function isThisInArray(arrayToCheck:Array):Boolean {
for (var i in arrayToCheck) {
if (arrayToCheck[i].X == this.X && arrayToCheck[i].Y == this.Y) {
return true
}
}
return false
}
}
enter code here
}
A quick glance through your code raises the idea of wrong heuristics. Your G value is always 0 in a node, at lease I do not see where it could change. However, in A-star algorithm for your task (finding the shortest path with obstacles) it should represent the number of steps already made to reach the cell. That would allow the algorithm to replace the long path with a shorter one.
The one time I coded an A star 'algorithm' I used a 2-dimensional Array for the grid (as you have). At the start of the search each grid location's 'searched' property was set to false. Each grid location would also have an Array of connecting directions; options that the player could choose to move in - some might be open, some might be blocked and inaccessible.
I would start the search by checking the starting grid position for how many direction options it had. For each option I would push a 'path' Array into a _paths Array. Each 'path' Array would end up containing a sequence of 'moves' (0 for up, 1 for right, 2 for down and 3 for left). So for each initial path, I would push in the corresponding starting move. I would also set the grid position's 'searched' property to true.
I would then iterate through each path, running through that sequence of moves to get to the most recently added location. I would check if that location was the target location. If not I would mark that location as searched then check which directions were available, ignoring locations that had already been searched. If non were available, the path would be closed and 'spliced' from the Array of paths.
Otherwise ByteArray 'deep copies' of the current path Array were made for each available move option, in excess of the first move option. A move in one direction was added to the current path and the new paths, in their respective directions.
If the number of paths ever reaches 0, there is not a path between the 2 locations.
I think that was about it. I hope that's helpful.
Note that the search does not need to be 'directed' toward the target; what I've suggested searches all possible paths and just 'happens' to find the most direct route by killing paths that try to check locations that have already been searched (meaning some other path has got there first and is therefore shorter).

List Component - Change gap size between cells

I want to increase the gap size between cells in my list so they won't be on top of each other. I know there is m_list.setStyle("contentPadding", 5) but that only adjusts the gap from the edges of the list and not the individual cells. Also adjusting the rowHeight on the list does not effect the gap between adjacent cells. I can't seem to find any resources on how this is done.
Thanks in advance.
It's not support with api. You may create own List extends fl.controls.List and use trick with rowHeight, see down:
package
{
import fl.controls.List;
import fl.controls.listClasses.CellRenderer;
public class MyList extends List
{
public var gap:Number = 5;
public function MyList()
{
super();
rowHeight += gap;
}
override protected function draw():void
{
super.draw();
updateRenderer();
}
protected function updateRenderer():void
{
var renderers:Array = availableCellRenderers.concat(activeCellRenderers);
var l:uint = renderers.length;
for (var i:uint=0; i<l; i++)
{
var renderer:CellRenderer = renderers[i];
if (i > 0)
{
renderer.y += gap;
}
renderer.setSize(availableWidth+_maxHorizontalScrollPosition,rowHeight - gap);
renderer.drawNow();
}
}
}
}

How to handle event for nonvisual objects in Flex

I am trying to perform two way binding e.g I have a button (out of many controls), on its selection, I am showing the values of its diff properties(like height, width etc) in some textinput. This one way process works fine.
But the reverse process doesn't work. i.e When I select some button, and try to change its dimension by entering some value in height, width textinputs, the dimension are not changed.
How to know which button was selected by me? How events needs to be handled here ?
private void Form1_Load(object sender, System.EventArgs e)
{
//Create some data and bind it to the grid
dt1 = GetData(1000, 3);
this.UltraGrid1.DataSource = dt1;
//Set the grid's CreationFilter to a new instance of the NumbersInRowSelectors class.
this.UltraGrid1.CreationFilter = new NumbersInRowSelectors();
}
private void UltraGrid1_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayoutEventArgs e)
{
//Hide the default images that are drawn in the RowSelectors, like the pencil and asterisk, etc.
e.Layout.Override.RowSelectorAppearance.ImageAlpha = Infragistics.Win.Alpha.Transparent;
//Center the text in the RowSelectors.
e.Layout.Override.RowSelectorAppearance.TextHAlign = Infragistics.Win.HAlign.Center;
e.Layout.Override.RowSelectorAppearance.TextVAlign = Infragistics.Win.VAlign.Middle;
//There is no wy to change the width of the RowSelectors.
//Use a smaller font, so that 3-digit numbers will fit.
e.Layout.Override.RowSelectorAppearance.FontData.Name = "Small Fonts";
e.Layout.Override.RowSelectorAppearance.FontData.SizeInPoints = 6;
}
//The NumbersInRowSelectors class. This class Implements a CreationFilter and
//adds a TextUIElement to each RowSelector which displays the row number of
//the row.
public class NumbersInRowSelectors:Infragistics.Win.IUIElementCreationFilter
{
#region Implementation of IUIElementCreationFilter
public void AfterCreateChildElements(Infragistics.Win.UIElement parent)
{
//Don't need to do anything here
}
public bool BeforeCreateChildElements(Infragistics.Win.UIElement parent)
{
//Declare some variables
Infragistics.Win.TextUIElement objTextUIElement;
Infragistics.Win.UltraWinGrid.RowSelectorUIElement objRowSelectorUIElement;
Infragistics.Win.UltraWinGrid.UltraGridRow objRow;
int RowNumber;
//Check to see if the parent is a RowSelectorUIElement. If not,
//we don't need to do anything
if (parent is Infragistics.Win.UltraWinGrid.RowSelectorUIElement)
{
//Get the Row from the RowSelectorsUIElement
objRowSelectorUIElement = (Infragistics.Win.UltraWinGrid.RowSelectorUIElement)parent;
objRow = (Infragistics.Win.UltraWinGrid.UltraGridRow)objRowSelectorUIElement.GetContext(typeof(Infragistics.Win.UltraWinGrid.UltraGridRow));
//Get the Index of the Row, so we can use it as a row number.
RowNumber = objRow.Index;
//Check to see if the TextUIElement is already created. Since
//The RowSelectorsUIElement never has children by default, we
//can just check the count.
if (parent.ChildElements.Count == 0)
{
//Create a new TextUIElement and parent it to the RowSelectorUIElement
objTextUIElement = new Infragistics.Win.TextUIElement(parent, RowNumber.ToString());
parent.ChildElements.Add(objTextUIElement);
}
else
{
//There's already a TextUIElement here, so just set the Text
objTextUIElement = (Infragistics.Win.TextUIElement)parent.ChildElements[0];
objTextUIElement.Text = RowNumber.ToString();
}
//Position the TextUIElement into the RowSelectorUIElement
objTextUIElement.Rect = parent.RectInsideBorders;
//Return True let the grid know we handled this event.
//This doesn't really do anything, since the grid
//does not create any child elements for this object, anyway.
return true;
}
//Return false to let the grid know we did not handle the event.
//This doesn't really do anything, since the grid
//does not create any child elements for this object, anyway.
return false;
}
#endregion
}
}
Create a "currently selected item" member in the class where the button and the text edit are declared.
In the button selection event listener assign the event target to this member. Then use it in the text edit event listener.
For example:
// It's a declaration of the member variable
private var m_current_btn:Button = null;
// It's an event listener for your button
private function on_selection_change(event:Event):void
{
m_current_btn = event.target as Button;
// button_x and button_y are two text edits
button_x.text = m_current_button.x.toString();
button_y.text = m_current_button.y.toString();
}
// Event listener to track changes in the coordinate text inputs
private function on_coordinate_textedit_change(event:Event):void
{
if (m_current_btn != null)
{
m_current_btn.x = parseInt(button_x.text);
m_current_btn.y = parseInt(button_y.text);
}
}

How do you make a custom auto complete component in Flex?

I need to make an auto complete component in flex that fetches the auto complete results from a remote database using a webservice. I have the webservice and querying part worked out. I've already made custom components in action script by extending VBoxes. However I cannot figure out how to generate the popup window that is supposed to show under the text input in my auto complete text box.
Currently I am using something like
PopUpManager.addPopUp(popup, parentComponent);
My popup class extends VBox and it extends the createChildren method as follows
protected override function createChildren():void
{
for (var i:int = 0; i < results.length; i++) {
var itemC:UIComponent =
factory.getComponent(results[i]);
addChild(itemC);
itemC.addEventListener(MouseEvent.CLICK,
getClickFunction(i));
}
private function getClickFunction(index:int):Function {
return function (event:MouseEvent):void
{
selectedIndex = index;
};
}
Unfortunately when the webservice retrieves its results and addPopUp is called, nothing shows up.
Currently the factory.getComponent method is executing this code
public function getComponent(user:Object):UIComponent
{
var email:Label = new Label();
email.text = user.email;
var name:Label = new Label();
name.text = user.displayName;
var vbox:VBox = new VBox();
vbox.addChild(name);
vbox.addChild(email);
return vbox;
}
I think you ought to look for someone who has already implemented this. While your issue is probably related to not positioning and sizing the component before calling addPopup() even if we helped you solve that you still have a lot more work todo. (BTW call super.createChildren in your override or else bad things will happen). Anyway, check this out:
http://www.adobe.com/cfusion/exchange/index.cfm?event=extensionDetail&extid=1047291
Finally I figured out how to use the List control and I stopped using a factory to generate components, instead I use the itemRenderer feature in the list control. I also used that to replace the custom popup class, and I added a positioning function to be called later. By combining these things I was able to get the drop down to display as expected. It appears that some components do not work well as pop ups.
Regardless, the working pop up code is
Inside my autocomplete component which extends HBox
dropDownList = new List();
dropDownList.itemRenderer = itemRenderer;
dropDownList.dataProvider = results;
dropDownList.labelFunction = labelFunction;
dropDownList.rowCount = results.length;
dropDownList.labelFunction = labelFunction==null ?
defaultLabelFunction : labelFunction;
dropDownList.tabFocusEnabled = false;
dropDownList.owner = this;
PopUpManager.addPopUp(IFlexDisplayObject(dropDownList), DisplayObject(this));
callLater(positionDropDownList);
Method in the autocomplete component (textInput is my text field)
public function positionDropDownList():void {
var localPoint:Point = new Point(0, textInput.y);
var globalPoint:Point = localToGlobal(localPoint);
dropDownList.x = globalPoint.x;
var fitsBelow:Boolean = parentApplication.height - globalPoint.y - textInput.height > dropDownList.height;
var fitsAbove:Boolean = globalPoint.y > dropDownList.height;
if (fitsBelow || !fitsAbove) {
dropDownList.y = globalPoint.y + textInput.measuredHeight;
} else {
dropDownList.y = globalPoint.y - dropDownList.height;
}
}
The position function was code that I borrowed from http://hillelcoren.com/flex-autocomplete/

Displaying HTML node in tree - height issue

Grrr so close yet still failing...
I display this tree, in Flex, which contains two nodes types:
Regular nodes, which are rendered normally like text (because they are!)
Rich (HTML) nodes - that's where things get twisted
Note that my issue is when I dynamically add a new (HTML) node to my tree.
So... How do I display HTML nodes?
I subclass TreeItemRenderer
In that subclass, I override set data() and add a text child to my renderer
Therefore I now have:
[icon] [label]
[text component]
Why?
The default label is a pure text component, not HTML-capable, hence the extra component: I want to display the new guy and forget the default label.
(continued) I override updateDisplayList() and, if the node is a rich one, I set label's height to zero, set my component's x and y to label'x and and y.
So...what am I missing? Ah, yes: I need to set my node's height since HTML text can be bigger or smaller than its text counterpart.
(continued) I override measure()
If my node is not a rich one, I simply invoke super.measure() and return
If it is a rich one, I give my html component a width (htmlComponent.width = explicitWidth - super.label.x;) and its height should be automatically computed.
This gives me a fairly reliably unreliable result!
When I fold/unfold my tree, every other time, I seem to get a correct height for my HTML node. The other time I get a height of '4' which happens to be the HTML component's padding alone, without content.
I know that I must be doing something fairly stupid here...but I am not sure what. I will post my code if my rambling is too incoherent to make any sense of...
**** EDIT: here is the source code for my renderer
As you can see, only 'notes' nodes use HTML.
I add a 'htmlComponent' child that will display the rich text while the default label is zero-sized and disappears.
It's definitely very raw code, as it's in progress!
package com.voilaweb.tfd
{
import mx.collections.*;
import mx.controls.Text;
import mx.controls.treeClasses.*;
import mx.core.UITextField;
import mx.core.UIComponent;
import flash.text.TextLineMetrics;
public class OutlinerRenderer extends TreeItemRenderer
{
private function get is_note():Boolean
{
return ('outlinerNodeNote' == XML(super.data).name().localName);
}
override public function set data(value:Object):void
{
super.data = value;
var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
if(!htmlComponent)
{
htmlComponent = new Text();
htmlComponent.name = "htmlComponent";
addChild(htmlComponent);
}
if(is_note)
htmlComponent.htmlText = XML(super.data).attribute('nodeText');
else
htmlComponent.htmlText = null;
setStyle('verticalAlign', 'top');
}
/*
* Today we've learnt a valuable lesson: there is no guarantee of when createChildren() will be invoked.
* Better be dirty and add children in set data()
override protected function createChildren():void
{
super.createChildren();
var htmlComponent:Text = new Text();
htmlComponent.name = "htmlComponent";
addChild(htmlComponent);
}
*/
override protected function measure():void
{
if(is_note)
{
super.measure();
var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
//Setting the width of the description field
//causes the height calculation to happen
htmlComponent.width = explicitWidth - super.label.x;
//We add the measuredHeight to the renderers measured height
//measuredHeight += (htmlComponent.measuredHeight - label.measuredHeight);
// Note the silly trick here...hopefully in the future I figure out how to avoid it
//
// Here is what happens: we check if measuredHeight is equal to decoration such as margin, insets...rather than that + some height
// If so, then we need to come up with an actual height which we do by adding textHeight to this height
// Note that I care about text being equal to margin etc but do not have proper access to these
// For instance UITextField.TEXT_HEIGHT_PADDING == 4 but is not accessible
// I am going to check if "<10" that will cover this case...
trace("For text " + htmlComponent.htmlText);
trace("width = " + htmlComponent.getExplicitOrMeasuredWidth()+" x height = " + htmlComponent.getExplicitOrMeasuredHeight());
var m:TextLineMetrics = htmlComponent.measureHTMLText(htmlComponent.htmlText);
//if(10 > htmlComponent.measuredHeight && !isNaN(htmlComponent.explicitHeight))
//htmlComponent.explicitHeight = m.height + htmlComponent.measuredHeight;
//if(htmlComponent.measuredHeight < 10) htmlComponent.explicitHeight = 50;
//measuredHeight += (htmlComponent.getExplicitOrMeasuredHeight() - super.label.getExplicitOrMeasuredHeight());
measuredHeight += (htmlComponent.getExplicitOrMeasuredHeight() - label.getExplicitOrMeasuredHeight());
trace("m:"+m.height+" Height: " + htmlComponent.getExplicitOrMeasuredHeight());
}
else
{
super.measure();
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
label.height = label.getExplicitOrMeasuredHeight(); // If you tell me my height, then I shall use my variable height!
graphics.clear();
if(is_note)
{
label.height = 0;
var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
htmlComponent.x = label.x;
htmlComponent.y = label.y;
htmlComponent.height = htmlComponent.getExplicitOrMeasuredHeight();
graphics.beginFill(0x555555);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.endFill();
}
var complete:XMLList = XML(super.data).attribute('complete');
if(complete.length() > 0 && true == complete[0])
{
var startx:Number = data ? TreeListData(listData).indent : 0;
if(disclosureIcon)
startx += disclosureIcon.measuredWidth;
if(icon)
startx += icon.measuredWidth;
graphics.lineStyle(3, getStyle("color"));
var y:Number = label.y + label.getExplicitOrMeasuredHeight() / 2;
graphics.moveTo(startx, y);
graphics.lineTo(startx + label.getExplicitOrMeasuredWidth(), y);
}
}
}
}
You made false assumption about label component in default renderer - it is capable of displaying html content. This renderer works for me:
public class HtmlTreeItemRenderer extends TreeItemRenderer {
override protected function commitProperties():void {
super.commitProperties();
label.htmlText = data ? listData.label : "";
invalidateDisplayList();
}
}
It would certainly help if you could post some code.
I was wondering though why you are using a custom html renderer. Is it because you want to display an icon next to the label since you mention [icon] [label]? If so, you're probably better off using an iconField or iconFunction.
Another thing that comes to mind is the variableRowHeight property. You might need to set this if your nodes have different heights.
Try it.
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%" verticalScrollPolicy="off"
horizontalScrollPolicy="off">
<mx:Script>
<![CDATA[
import mx.core.UITextField;
private var texto:UITextField;
override protected function createChildren():void
{
super.createChildren();
texto = new UITextField();
texto.setColor(0xFFFFFF);
texto.multiline = true;
texto.wordWrap = true;
texto.autoSize = TextFieldAutoSize.LEFT;
this.addChild(texto);
//texto.text = data.title;
}
override public function set data(value:Object):void
{
super.data = value;
if (value)
{
texto.htmlText = value.title;
this.invalidateDisplayList();
}
}
override protected function measure():void
{
super.measure();
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (texto)
texto.width = this.width;
}
]]>
</mx:Script>
</mx:Canvas>